set settings

This commit is contained in:
djnitehawk 2025-03-12 21:21:13 +05:30 committed by Dĵ ΝιΓΞΗΛψΚ
parent b642498a60
commit ef7fff7302
15 changed files with 226 additions and 174 deletions

View File

@ -7,7 +7,7 @@
<PageTitle>JK BMS Status</PageTitle> <PageTitle>JK BMS Status</PageTitle>
<Loader Enabled=@(status is null) /> <Loader Enabled=@(status is null)/>
@if (status is not null) @if (status is not null)
{ {
@ -50,7 +50,7 @@
@if (status.AvgCurrentAmps == 0) @if (status.AvgCurrentAmps == 0)
{ {
<div class="fs-5 m-0 p-0 text-muted"> <div class="fs-5 m-0 p-0 text-muted">
Holding<br />Voltage Holding<br/>Voltage
</div> </div>
} }
@if (status.IsWarning) @if (status.IsWarning)
@ -134,13 +134,14 @@
protected override void OnInitialized() protected override void OnInitialized()
{ {
onStatusUpdated += UpdateState; onStatusUpdated += UpdateState;
onStatusRetrievalError += NullifyStatus; onStatusRetrievalError += NullifyStatus;
} }
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
var st = await Js.LoadStateAsync<ClientSettings>(); var st = await Js.LoadStateAsync<ClientSettings>();
if(st is null)
if (st is null)
{ {
await Js.SaveStateAsync(state); await Js.SaveStateAsync(state);
} }
@ -168,6 +169,7 @@
{ {
return status!.GetTimeString(); return status!.GetTimeString();
} }
return $"{status!.TimeHrs} Hrs {status.TimeMins} Mins"; return $"{status!.TimeHrs} Hrs {status.TimeMins} Mins";
} }
@ -175,10 +177,12 @@
{ {
if (state.ShowCapacityKwh) if (state.ShowCapacityKwh)
{ {
var avlCap = Math.Round((status!.AvailableCapacity * status.PackNominalVoltage) / 1000, 1); var avlCap = Math.Round(status!.AvailableCapacity * status.PackNominalVoltage / 1000, 1);
var packCap = Math.Round((status!.PackCapacity * status.PackNominalVoltage) / 1000, 1); var packCap = Math.Round(status!.PackCapacity * status.PackNominalVoltage / 1000, 1);
return $"{avlCap} kWh / {packCap} kWh"; return $"{avlCap} kWh / {packCap} kWh";
} }
return $"{Math.Round(status!.AvailableCapacity, 1)} Ah / {status!.PackCapacity} Ah"; return $"{Math.Round(status!.AvailableCapacity, 1)} Ah / {status!.PackCapacity} Ah";
} }
@ -207,11 +211,9 @@
// and it leads to a new stream download being created everytime a page is initialized. // and it leads to a new stream download being created everytime a page is initialized.
// which leads to a memory leak/ connection exhaustion. // which leads to a memory leak/ connection exhaustion.
using var client = new HttpClient using var client = new HttpClient();
{ client.BaseAddress = new(basePath);
BaseAddress = new(basePath), client.Timeout = TimeSpan.FromSeconds(5);
Timeout = TimeSpan.FromSeconds(5)
};
var retryDelay = 1000; var retryDelay = 1000;
@ -231,10 +233,10 @@
JsonSerializer.DeserializeAsyncEnumerable<BMSStatus>( JsonSerializer.DeserializeAsyncEnumerable<BMSStatus>(
stream, stream,
new JsonSerializerOptions new JsonSerializerOptions
{ {
PropertyNameCaseInsensitive = true, PropertyNameCaseInsensitive = true,
DefaultBufferSize = 64 DefaultBufferSize = 64
})) }))
{ {
onStatusUpdated?.Invoke(s); onStatusUpdated?.Invoke(s);
retryDelay = 1000; retryDelay = 1000;

View File

@ -265,7 +265,7 @@
@code{ @code{
private CurrentSettings? settings; private CurrentSettings? settings;
private Button currentButton = Button.None; private Button? currentButton;
private bool isSuccess; private bool isSuccess;
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
@ -274,7 +274,7 @@
StateHasChanged(); StateHasChanged();
} }
private async Task SetChargePriority(string priority) private async Task SetChargePriority(byte priority)
{ {
isSuccess = false; isSuccess = false;
@ -297,7 +297,7 @@
break; break;
default: default:
currentButton = Button.None; currentButton = null;
break; break;
} }
@ -310,7 +310,7 @@
} }
} }
private async Task SetOutputPriority(string priority) private async Task SetOutputPriority(byte priority)
{ {
isSuccess = false; isSuccess = false;
@ -319,7 +319,7 @@
OutputPriority.SolarFirst => Button.OpSolarFirst, OutputPriority.SolarFirst => Button.OpSolarFirst,
OutputPriority.SolarBatteryUtility => Button.OpSolarBatteryUtility, OutputPriority.SolarBatteryUtility => Button.OpSolarBatteryUtility,
OutputPriority.UtilityFirst => Button.OpUtilityFirst, OutputPriority.UtilityFirst => Button.OpUtilityFirst,
_ => Button.None _ => null
}; };
if (await Http.GetStringAsync($"api/settings/set-setting/{Setting.OutputPriority}/{priority}") == "true") if (await Http.GetStringAsync($"api/settings/set-setting/{Setting.OutputPriority}/{priority}") == "true")
@ -332,7 +332,7 @@
private async Task SetVoltage(Setting setting) private async Task SetVoltage(Setting setting)
{ {
isSuccess = false; isSuccess = false;
decimal value = 0; double value = 0;
switch (setting) switch (setting)
{ {
@ -362,19 +362,19 @@
break; break;
default: default:
currentButton = Button.None; currentButton = null;
break; break;
} }
; ;
if (await Http.GetStringAsync($"api/settings/set-setting/{setting}/{value:00.0}") == "true") if (await Http.GetStringAsync($"api/settings/set-setting/{setting}/{value}") == "true")
{ {
isSuccess = true; isSuccess = true;
} }
} }
private async Task SetSetting(Setting settingName, string value) private async Task SetSetting(Setting settingName, byte value)
{ {
if (await Http.GetStringAsync($"api/settings/set-setting/{settingName}/{value}") == "true") if (await Http.GetStringAsync($"api/settings/set-setting/{settingName}/{value}") == "true")
{ {
@ -382,7 +382,7 @@
} }
} }
private void UpdateLocalSetting(Setting settingName, string value) private void UpdateLocalSetting(Setting settingName, byte value)
{ {
switch (settingName) switch (settingName)
{ {
@ -411,7 +411,7 @@
isSuccess = false; isSuccess = false;
await Http.PostAsJsonAsync("api/settings/set-system-spec", settings!.SystemSpec); await Http.PostAsJsonAsync("api/settings/set-system-spec", settings!.SystemSpec);
isSuccess = true; isSuccess = true;
currentButton = Button.None; currentButton = null;
} }
private string Spinner(Button button) private string Spinner(Button button)
@ -424,7 +424,7 @@
? "visually-hidden" ? "visually-hidden"
: ""; : "";
private string Success(Button button, string currentValue, string settingValue) private string Success(Button button, byte currentValue, byte settingValue)
=> (currentButton == button && isSuccess) || currentValue == settingValue => (currentButton == button && isSuccess) || currentValue == settingValue
? "oi oi-circle-check text-success" ? "oi oi-circle-check text-success"
: ""; : "";
@ -432,16 +432,15 @@
private string Sanitize(string value) private string Sanitize(string value)
=> value.StartsWith("0") ? value[1..] : value; => value.StartsWith("0") ? value[1..] : value;
private enum Button private enum Button : byte
{ {
None = 0, OpUtilityFirst = 1, //never change this value
ChOnlySolar = 1, OpSolarFirst = 2, //never change this value
ChSolarFirst = 2, OpSolarBatteryUtility = 3, //never change this value
ChSolarAndUtility = 3, ChOnlySolar = 4,
ChUtilityFirst = 4, ChSolarFirst = 5,
OpUtilityFirst = 5, ChSolarAndUtility = 6,
OpSolarFirst = 6, ChUtilityFirst = 7,
OpSolarBatteryUtility = 7,
UpdateUserSettings = 8, UpdateUserSettings = 8,
BackToGridVoltage = 9, BackToGridVoltage = 9,
BackToBattery = 10, BackToBattery = 10,

View File

@ -6,7 +6,7 @@ namespace InverterMon.Server.Endpoints.GetStatus;
public class Endpoint : EndpointWithoutRequest<object> public class Endpoint : EndpointWithoutRequest<object>
{ {
public FelicitySolarInverter Inverter { get; set; } public FelicitySolarInverter Inverter { get; set; } = null!;
public override void Configure() public override void Configure()
{ {
@ -28,25 +28,25 @@ public class Endpoint : EndpointWithoutRequest<object>
async IAsyncEnumerable<InverterStatus> GetDataStream([EnumeratorCancellation] CancellationToken c) async IAsyncEnumerable<InverterStatus> GetDataStream([EnumeratorCancellation] CancellationToken c)
{ {
var blank = new InverterStatus();
while (!c.IsCancellationRequested) while (!c.IsCancellationRequested)
{ {
if (Env.IsDevelopment()) if (Env.IsDevelopment())
{ {
var status = new InverterStatus(); var status = new InverterStatus
status.OutputVoltage = Random.Shared.Next(240); {
status.LoadWatts = Random.Shared.Next(3500); OutputVoltage = Random.Shared.Next(240),
status.LoadPercentage = Random.Shared.Next(100); LoadWatts = Random.Shared.Next(3500),
status.BatteryVoltage = Random.Shared.Next(24); LoadPercentage = Random.Shared.Next(100),
status.BatteryChargeCurrent = Random.Shared.Next(20); BatteryVoltage = Random.Shared.Next(24),
status.BatteryDischargeCurrent = Random.Shared.Next(300); BatteryChargeCurrent = Random.Shared.Next(20),
status.HeatSinkTemperature = Random.Shared.Next(300); BatteryDischargeCurrent = Random.Shared.Next(300),
status.PVInputCurrent = Random.Shared.Next(300); HeatSinkTemperature = Random.Shared.Next(300),
status.PVInputVoltage = Random.Shared.Next(300); PVInputCurrent = Random.Shared.Next(300),
status.PVInputWatt = Random.Shared.Next(1000); PVInputVoltage = Random.Shared.Next(300),
status.PV_MaxCapacity = 1000; PVInputWatt = Random.Shared.Next(1000),
status.BatteryCapacity = 100; PV_MaxCapacity = 1000,
BatteryCapacity = 100
};
yield return status; yield return status;
} }

View File

@ -4,6 +4,11 @@ using InverterMon.Shared.Models;
namespace InverterMon.Server.Endpoints.PVLog.GetPVForDay; namespace InverterMon.Server.Endpoints.PVLog.GetPVForDay;
public class Request
{
public int DayNumber { get; set; }
}
public class Endpoint : Endpoint<Request, PVDay> public class Endpoint : Endpoint<Request, PVDay>
{ {
public Database Db { get; set; } public Database Db { get; set; }
@ -21,7 +26,7 @@ public class Endpoint : Endpoint<Request, PVDay>
if (pvDay is null) if (pvDay is null)
{ {
await SendNotFoundAsync(); await SendNotFoundAsync(c);
return; return;
} }
@ -34,8 +39,6 @@ public class Endpoint : Endpoint<Request, PVDay>
TotalWattHours = Random.Shared.Next(3000) TotalWattHours = Random.Shared.Next(3000)
}; };
//pvDay.AllocateBuckets(6, 18);
for (var i = 0; i < 97; i++) for (var i = 0; i < 97; i++)
pvDay.WattPeaks.Add(i.ToString(), Random.Shared.Next(2000)); pvDay.WattPeaks.Add(i.ToString(), Random.Shared.Next(2000));
} }

View File

@ -1,6 +0,0 @@
namespace InverterMon.Server.Endpoints.PVLog.GetPVForDay;
public class Request
{
public int DayNumber { get; set; }
}

View File

@ -1,4 +1,5 @@
using InverterMon.Server.Persistence.Settings; using InverterMon.Server.InverterService;
using InverterMon.Server.Persistence.Settings;
using InverterMon.Shared.Models; using InverterMon.Shared.Models;
namespace InverterMon.Server.Endpoints.Settings.GetSettingValues; namespace InverterMon.Server.Endpoints.Settings.GetSettingValues;
@ -6,6 +7,7 @@ namespace InverterMon.Server.Endpoints.Settings.GetSettingValues;
public class Endpoint : EndpointWithoutRequest<CurrentSettings> public class Endpoint : EndpointWithoutRequest<CurrentSettings>
{ {
public UserSettings UserSettings { get; set; } public UserSettings UserSettings { get; set; }
public FelicitySolarInverter Inverter { get; set; }
public override void Configure() public override void Configure()
{ {
@ -15,29 +17,48 @@ public class Endpoint : EndpointWithoutRequest<CurrentSettings>
public override async Task HandleAsync(CancellationToken c) public override async Task HandleAsync(CancellationToken c)
{ {
//todo: get values from inverter and send to client if (Env.IsDevelopment())
{
var res = new CurrentSettings
{
BackToBatteryVoltage = 48.1,
BackToGridVoltage = 48.2,
FloatChargeVoltage = 48.3,
ChargePriority = ChargePriority.OnlySolar,
DischargeCuttOffVoltage = 48.4,
BulkChargeVoltage = 48.5,
MaxACChargeCurrent = 10,
MaxCombinedChargeCurrent = 20,
OutputPriority = OutputPriority.SolarFirst,
SystemSpec = UserSettings.ToSystemSpec()
};
await SendAsync(res, cancellation: c);
// var cmd = new GetSettings(); return;
// cmd.Result.SystemSpec = UserSettings.ToSystemSpec(); }
//
// if (Env.IsDevelopment()) try
// { {
// cmd.Result.ChargePriority = "03"; var data = Inverter.ReadSettings();
// cmd.Result.MaxACChargeCurrent = "10"; var res = new CurrentSettings
// cmd.Result.MaxCombinedChargeCurrent = "020"; {
// cmd.Result.OutputPriority = "02"; BackToBatteryVoltage = data.BatteryBackToDischargeVoltage,
// cmd.Result.BulkChargeVoltage = 27.1m; BackToGridVoltage = data.BatteryBackToChargeVoltage,
// await SendAsync(cmd.Result); BulkChargeVoltage = data.BatteryCvChargingVoltage,
// return; ChargePriority = data.ChargingSourcePriority,
// } DischargeCuttOffVoltage = data.BatteryCutOffVoltage,
// FloatChargeVoltage = data.BatteryFloatingChargingVoltage,
// Queue.AddCommands(cmd); MaxACChargeCurrent = data.MaxAcChargingCurrent,
// MaxCombinedChargeCurrent = data.MaxChargingCurrent,
// await cmd.WhileProcessing(c); OutputPriority = data.OutputSourcePriority,
// SystemSpec = UserSettings.ToSystemSpec()
// if (cmd.IsComplete) };
// await SendAsync(cmd.Result); await SendAsync(res, cancellation: c);
// else }
// ThrowError("Unable to read settings in a timely manner!"); catch (Exception e)
{
Logger.LogError("Unable to read settings from inverter. Details: [{msg}]", e.Message);
ThrowError("Unable to read settings from inverter!");
}
} }
} }

View File

@ -1,20 +1,33 @@
namespace InverterMon.Server.Endpoints.Settings.SetSettingValue; using InverterMon.Server.InverterService;
namespace InverterMon.Server.Endpoints.Settings.SetSettingValue;
public class Endpoint : Endpoint<Shared.Models.SetSetting, bool> public class Endpoint : Endpoint<Shared.Models.SetSetting, bool>
{ {
public FelicitySolarInverter Inverter { get; set; }
public override void Configure() public override void Configure()
{ {
Get("settings/set-setting/{Command}/{Value}"); Get("settings/set-setting/{Setting}/{Value}");
AllowAnonymous(); AllowAnonymous();
} }
public override async Task HandleAsync(Shared.Models.SetSetting r, CancellationToken c) public override async Task HandleAsync(Shared.Models.SetSetting r, CancellationToken c)
{ {
//todo: set settings using inveter if (Env.IsDevelopment())
{
await SendAsync(true, cancellation: c);
// var cmd = new InverterService.Commands.SetSetting(r.Command, r.Value); return;
// Queue.AddCommands(cmd); }
// await cmd.WhileProcessing(c);
// await SendAsync(cmd.Result); try
{
Inverter.SetSetting(r.Setting, r.Value);
}
catch
{
await SendAsync(false, cancellation: c);
}
} }
} }

View File

@ -1,8 +0,0 @@
// using InverterMon.Shared.Models;
//
// namespace InverterMon.Server.InverterService;
//
// public class CurrentStatus
// {
// public InverterStatus Result { get; set; }
// }

View File

@ -5,20 +5,6 @@ using InverterMon.Shared.Models;
namespace InverterMon.Server.InverterService; namespace InverterMon.Server.InverterService;
// sealed class StatusData
// {
// public int WorkingMode { get; set; }
// public int BatteryChargingStage { get; set; }
// public double BatteryVoltage { get; set; }
// public int BatteryCurrent { get; set; }
// public int BatteryPower { get; set; }
// public double AcOutputVoltage { get; set; }
// public int AcOutputActivePower { get; set; }
// public int LoadPercentage { get; set; }
// public double PvInputVoltage { get; set; }
// public int PvInputPower { get; set; }
// }
sealed class SettingsData sealed class SettingsData
{ {
public double BatteryCutOffVoltage { get; set; } public double BatteryCutOffVoltage { get; set; }
@ -32,21 +18,32 @@ sealed class SettingsData
public byte MaxAcChargingCurrent { get; set; } public byte MaxAcChargingCurrent { get; set; }
} }
[SuppressMessage("Performance", "CA1822:Mark members as static")] [SuppressMessage("Performance", "CA1822:Mark members as static"),
SuppressMessage("ReSharper", "MemberCanBeMadeStatic.Global")]
public sealed class FelicitySolarInverter public sealed class FelicitySolarInverter
{ {
public InverterStatus Status { get; private set; } public InverterStatus Status { get; } = new();
const byte SlaveAddress = 0x01; const byte SlaveAddress = 0x01;
static SerialPort _serialPort = null!; static SerialPort _serialPort = null!;
internal void Connect(string portName) internal bool Connect(string portName)
{ {
lock (_lock) lock (_lock)
{ {
_serialPort = new(portName, 2400, Parity.None, 8, StopBits.One); _serialPort = new(portName, 2400, Parity.None, 8, StopBits.One);
_serialPort.Open();
try
{
_serialPort.Open();
}
catch
{
return false;
}
} }
return true;
} }
static byte[]? _cachedStatusFrame; static byte[]? _cachedStatusFrame;
@ -60,11 +57,8 @@ public sealed class FelicitySolarInverter
{ {
var regs = ReadRegisters(StatusStartAddress, StatusRegisterCount); var regs = ReadRegisters(StatusStartAddress, StatusRegisterCount);
// var status = new StatusData
// {
// WorkingMode = regs[0], // 0x1101: Working mode (offset 0) // WorkingMode = regs[0], // 0x1101: Working mode (offset 0)
// BatteryChargingStage = regs[1], // 0x1102: Battery charging stage (offset 1) // BatteryChargingStage = regs[1], // 0x1102: Battery charging stage (offset 1)
// };
Status.BatteryVoltage = regs[7] / 100.0; // 0x1108: Battery voltage (offset 0x1108 - 0x1101 = 7) Status.BatteryVoltage = regs[7] / 100.0; // 0x1108: Battery voltage (offset 0x1108 - 0x1101 = 7)
Status.BatteryDischargeCurrent = regs[8]; // 0x1109: Battery current (offset 8) -- signed value Status.BatteryDischargeCurrent = regs[8]; // 0x1109: Battery current (offset 8) -- signed value
@ -103,7 +97,7 @@ public sealed class FelicitySolarInverter
return settings; return settings;
} }
internal void SetSetting(Setting setting, float value) internal void SetSetting(Setting setting, double value)
{ {
ushort registerAddress; ushort registerAddress;
@ -154,7 +148,23 @@ public sealed class FelicitySolarInverter
throw new ArgumentException("Invalid setting!"); throw new ArgumentException("Invalid setting!");
} }
WriteSingleRegister(registerAddress, (ushort)value); var settingValue = (ushort)value;
// Build request frame:
// [Slave Address][Function Code 0x06][Register Address Hi][Register Address Lo]
// [Value Hi][Value Lo][CRC Lo][CRC Hi]
var frame = new byte[8];
frame[0] = SlaveAddress;
frame[1] = 0x06;
frame[2] = (byte)(registerAddress >> 8);
frame[3] = (byte)(registerAddress & 0xFF);
frame[4] = (byte)(settingValue >> 8);
frame[5] = (byte)(settingValue & 0xFF);
var crc = CalculateCrc(frame, 6);
frame[6] = (byte)(crc & 0xFF);
frame[7] = (byte)(crc >> 8);
SendModbusRequest(frame);
} }
internal void Close() internal void Close()
@ -212,25 +222,6 @@ public sealed class FelicitySolarInverter
return registers; return registers;
} }
static void WriteSingleRegister(ushort registerAddress, ushort value)
{
// Build request frame:
// [Slave Address][Function Code 0x06][Register Address Hi][Register Address Lo]
// [Value Hi][Value Lo][CRC Lo][CRC Hi]
var frame = new byte[8];
frame[0] = SlaveAddress;
frame[1] = 0x06;
frame[2] = (byte)(registerAddress >> 8);
frame[3] = (byte)(registerAddress & 0xFF);
frame[4] = (byte)(value >> 8);
frame[5] = (byte)(value & 0xFF);
var crc = CalculateCrc(frame, 6);
frame[6] = (byte)(crc & 0xFF);
frame[7] = (byte)(crc >> 8);
SendModbusRequest(frame);
}
static readonly object _lock = new(); static readonly object _lock = new();
static byte[] SendModbusRequest(byte[] request) static byte[] SendModbusRequest(byte[] request)

View File

@ -3,16 +3,35 @@ using InverterMon.Server.Persistence.Settings;
namespace InverterMon.Server.InverterService; namespace InverterMon.Server.InverterService;
class StatusRetriever(Database db, FelicitySolarInverter inverter, UserSettings userSettings) : BackgroundService class StatusRetriever(Database db, FelicitySolarInverter inverter, UserSettings userSettings, IConfiguration config, ILogger<StatusRetriever> log)
: BackgroundService
{ {
protected override async Task ExecuteAsync(CancellationToken c) protected override async Task ExecuteAsync(CancellationToken c)
{ {
var port = config["LaunchSettings:DeviceAddress"] ?? throw new ArgumentException("Device address not specified in appsettings.json file!");
while (!inverter.Connect(port))
{
log.LogCritical("Unable to connect to the inverter at [{port}]. Retrying in 5 seconds...", port);
await Task.Delay(5000);
}
while (!c.IsCancellationRequested) while (!c.IsCancellationRequested)
{ {
inverter.Status.BatteryCapacity = userSettings.BatteryCapacity; inverter.Status.BatteryCapacity = userSettings.BatteryCapacity;
inverter.Status.PV_MaxCapacity = userSettings.PV_MaxCapacity; inverter.Status.PV_MaxCapacity = userSettings.PV_MaxCapacity;
//todo: get data from inverter and map to CurrentStatus.Result try
{
inverter.UpdateStatus();
}
catch (Exception e)
{
log.LogError("Error while reading inverter status data! Details: [{msg}]", e.Message);
await Task.Delay(2000);
continue;
}
db.UpdateTodaysPvGeneration(c); db.UpdateTodaysPvGeneration(c);

View File

@ -20,6 +20,7 @@ bld.WebHost.ConfigureKestrel(o => o.Listen(IPAddress.Any, port));
bld.Services bld.Services
.AddSingleton<UserSettings>() .AddSingleton<UserSettings>()
.AddSingleton<Database>() .AddSingleton<Database>()
.AddSingleton<FelicitySolarInverter>()
.AddSingleton<JkBms>(); .AddSingleton<JkBms>();
if (!bld.Environment.IsDevelopment()) if (!bld.Environment.IsDevelopment())

View File

@ -2,8 +2,8 @@
public static class ChargePriority public static class ChargePriority
{ {
public const string SolarFirst = "1"; public const byte SolarFirst = 1;
public const string SolarAndUtility = "2"; public const byte SolarAndUtility = 2;
public const string OnlySolar = "3"; public const byte OnlySolar = 3;
public const string UtilityFirst = "0"; public const byte UtilityFirst = 0;
} }

View File

@ -2,39 +2,56 @@
public class CurrentSettings public class CurrentSettings
{ {
public string ChargePriority { get; set; } = "000"; public byte ChargePriority { get; set; }
public string OutputPriority { get; set; } = "000"; public byte OutputPriority { get; set; }
public string MaxACChargeCurrent { get; set; } = "000"; public byte MaxACChargeCurrent { get; set; }
public string MaxCombinedChargeCurrent { get; set; } = "000"; public byte MaxCombinedChargeCurrent { get; set; }
decimal _backToGrid; double _backToGrid;
public decimal BackToGridVoltage { get => _backToGrid; set => _backToGrid = RoundToHalfPoints(value); }
decimal _dischargeCuttOff; public double BackToGridVoltage
public decimal DischargeCuttOffVoltage { get => _dischargeCuttOff; set => _dischargeCuttOff = RoundToOneDecimalPoint(value); } {
get => _backToGrid;
set => _backToGrid = value; //RoundToHalfPoints(value);
}
decimal _bulkVoltage; double _dischargeCuttOff;
public decimal BulkChargeVoltage
public double DischargeCuttOffVoltage
{
get => _dischargeCuttOff;
set => _dischargeCuttOff = value; // RoundToOneDecimalPoint(value);
}
double _bulkVoltage;
public double BulkChargeVoltage
{ {
get => _bulkVoltage; get => _bulkVoltage;
set => _bulkVoltage = RoundToOneDecimalPoint(value < _floatVoltage ? _floatVoltage : value); set => _bulkVoltage = value; // RoundToOneDecimalPoint(value < _floatVoltage ? _floatVoltage : value);
} }
decimal _floatVoltage; double _floatVoltage;
public decimal FloatChargeVoltage
public double FloatChargeVoltage
{ {
get => _floatVoltage; get => _floatVoltage;
set => _floatVoltage = RoundToOneDecimalPoint(value > _bulkVoltage ? _bulkVoltage : value); set => _floatVoltage = value; // RoundToOneDecimalPoint(value > _bulkVoltage ? _bulkVoltage : value);
} }
decimal _backToBattery; double _backToBattery;
public decimal BackToBatteryVoltage { get => _backToBattery; set => _backToBattery = RoundToHalfPoints(value); }
public double BackToBatteryVoltage
{
get => _backToBattery;
set => _backToBattery = value; //RoundToHalfPoints(value);
}
public SystemSpec SystemSpec { get; set; } = new(); public SystemSpec SystemSpec { get; set; } = new();
static decimal RoundToHalfPoints(decimal value) // static double RoundToHalfPoints(double value)
=> Math.Round(value * 2, MidpointRounding.AwayFromZero) / 2; // => Math.Round(value * 2, MidpointRounding.AwayFromZero) / 2;
static decimal RoundToOneDecimalPoint(decimal value) // static double RoundToOneDecimalPoint(double value)
=> Math.Round(value, 1, MidpointRounding.AwayFromZero); // => Math.Round(value, 1, MidpointRounding.AwayFromZero);
} }

View File

@ -2,7 +2,7 @@
public static class OutputPriority public static class OutputPriority
{ {
public const string SolarFirst = "1"; public const byte SolarFirst = 1;
public const string SolarBatteryUtility = "2"; public const byte SolarBatteryUtility = 2;
public const string UtilityFirst = "0"; public const byte UtilityFirst = 0;
} }

View File

@ -2,6 +2,6 @@
public class SetSetting public class SetSetting
{ {
public string Command { get; set; } public Setting Setting { get; set; }
public string Value { get; set; } public double Value { get; set; }
} }