@@ -26,33 +26,12 @@
Max Combined Charge Current:
-
- @if (chargeAmpereValues == null || inProgressSetting == Setting.CombinedChargeCurrent)
- {
-
- }
- else
- {
-
-
- @Sanitize(settings.MaxCombinedChargeCurrent) A
-
- @if (chargeAmpereValues != null)
- {
-
- }
-
- }
+
+
SetSetting(Setting.CombinedChargeCurrent,settings.MaxCombinedChargeCurrent)">
+
+ Save
+
@@ -61,33 +40,11 @@
Max Grid Charge Current:
-
- @if (chargeAmpereValues == null || inProgressSetting == Setting.UtilityChargeCurrent)
- {
-
- }
- else
- {
-
-
- @Sanitize(settings.MaxACChargeCurrent) A
-
-
-
- }
+
+
SetSetting(Setting.UtilityChargeCurrent,settings.MaxACChargeCurrent)">
+
+ Save
+
@@ -96,21 +53,21 @@
Output Source Priority:
- SetOutputPriority(OutputPriority.SolarFirst)">
+ SetOutputPriority(OutputPriority.SolarFirst)">
Solar First
- SetOutputPriority(OutputPriority.SolarBatteryUtility)">
+ SetOutputPriority(OutputPriority.SolarBatteryUtility)">
Solar > Battery > Utility
- SetOutputPriority(OutputPriority.UtilityFirst)">
+ SetOutputPriority(OutputPriority.UtilityFirst)">
Utility First
-
+
@@ -119,22 +76,22 @@
Battery Charging Priority:
- SetChargePriority(ChargePriority.OnlySolar)">
+ SetChargePriority(ChargePriority.OnlySolar)">
Solar Only
- SetChargePriority(ChargePriority.SolarFirst)">
+ SetChargePriority(ChargePriority.SolarFirst)">
Solar First
- SetChargePriority(ChargePriority.SolarAndUtility)">
+ SetChargePriority(ChargePriority.SolarAndUtility)">
Solar & Utility
- SetChargePriority(ChargePriority.UtilityFirst)">
+ SetChargePriority(ChargePriority.UtilityFirst)">
Utility First
@@ -142,7 +99,7 @@
-
+
@@ -151,7 +108,7 @@
-
+
SetVoltage(Setting.BulkVoltage)">
Save
@@ -183,7 +140,8 @@
-
+
SetVoltage(Setting.DischargeCutOff)">
Save
@@ -215,7 +173,8 @@
-
+
@@ -236,7 +195,7 @@
-
Watts
+
Watts
@@ -296,8 +255,8 @@
- Update
-
+ Update
+
@@ -305,49 +264,14 @@
}
@code{
- private static ChargeAmpereValues? chargeAmpereValues;
- private bool isLoadingChargeValues = false;
private CurrentSettings? settings;
private Button currentButton = Button.None;
private bool isSuccess;
- private string inProgressSetting = "";
protected override async Task OnInitializedAsync()
{
settings = await Http.GetFromJsonAsync
("api/settings/get-setting-values");
StateHasChanged();
-
- _ = Task.Run(async () =>
- {
- if (chargeAmpereValues is null)
- {
- isLoadingChargeValues = true;
- StateHasChanged();
-
- using var client = new HttpClient
- {
- BaseAddress = new Uri(Http.BaseAddress?.ToString() ?? "/"),
- Timeout = TimeSpan.FromSeconds(10)
- };
-
- chargeAmpereValues = await client.GetFromJsonAsync("api/settings/get-charge-ampere-values");
-
- // some inverters only seem to support one of the two commands over usb
- if (chargeAmpereValues?.CombinedAmpereValues.Any() is true &&
- chargeAmpereValues?.UtilityAmpereValues.Any() is false)
- {
- chargeAmpereValues.UtilityAmpereValues = chargeAmpereValues.CombinedAmpereValues;
- }
- if (chargeAmpereValues?.CombinedAmpereValues.Any() is false &&
- chargeAmpereValues?.UtilityAmpereValues.Any() is true)
- {
- chargeAmpereValues.CombinedAmpereValues = chargeAmpereValues.UtilityAmpereValues;
- }
-
- isLoadingChargeValues = false;
- StateHasChanged();
- }
- });
}
private async Task SetChargePriority(string priority)
@@ -358,23 +282,29 @@
{
case ChargePriority.OnlySolar:
currentButton = Button.ChOnlySolar;
+
break;
case ChargePriority.SolarFirst:
currentButton = Button.ChSolarFirst;
+
break;
case ChargePriority.SolarAndUtility:
currentButton = Button.ChSolarAndUtility;
+
break;
case ChargePriority.UtilityFirst:
currentButton = Button.ChUtilityFirst;
+
break;
default:
currentButton = Button.None;
+
break;
- };
+ }
+ ;
if (await Http.GetStringAsync($"api/settings/set-setting/{Setting.ChargePriority}/{priority}") == "true")
- {
+ {
isSuccess = true;
UpdateLocalSetting(Setting.ChargePriority, priority);
}
@@ -384,30 +314,22 @@
{
isSuccess = false;
- switch (priority)
+ currentButton = priority switch
{
- case OutputPriority.SolarFirst:
- currentButton = Button.OpSolarFirst;
- break;
- case OutputPriority.SolarBatteryUtility:
- currentButton = Button.OpSolarBatteryUtility;
- break;
- case OutputPriority.UtilityFirst:
- currentButton = Button.OpUtilityFirst;
- break;
- default:
- currentButton = Button.None;
- break;
+ OutputPriority.SolarFirst => Button.OpSolarFirst,
+ OutputPriority.SolarBatteryUtility => Button.OpSolarBatteryUtility,
+ OutputPriority.UtilityFirst => Button.OpUtilityFirst,
+ _ => Button.None
};
if (await Http.GetStringAsync($"api/settings/set-setting/{Setting.OutputPriority}/{priority}") == "true")
- {
+ {
isSuccess = true;
- UpdateLocalSetting(Setting.OutputPriority,priority);
+ UpdateLocalSetting(Setting.OutputPriority, priority);
}
}
- private async Task SetVoltage(string setting)
+ private async Task SetVoltage(Setting setting)
{
isSuccess = false;
decimal value = 0;
@@ -417,27 +339,34 @@
case Setting.BulkVoltage:
currentButton = Button.BulkVoltage;
value = settings!.BulkChargeVoltage;
+
break;
case Setting.FloatVoltage:
currentButton = Button.FloatVoltage;
value = settings!.FloatChargeVoltage;
+
break;
case Setting.DischargeCutOff:
currentButton = Button.DischargeCutOff;
value = settings!.DischargeCuttOffVoltage;
+
break;
case Setting.BackToGrid:
currentButton = Button.BackToGridVoltage;
value = settings!.BackToGridVoltage;
+
break;
case Setting.BackToBattery:
currentButton = Button.BackToBattery;
value = settings!.BackToBatteryVoltage;
+
break;
default:
currentButton = Button.None;
+
break;
- };
+ }
+ ;
if (await Http.GetStringAsync($"api/settings/set-setting/{setting}/{value:00.0}") == "true")
{
@@ -445,33 +374,33 @@
}
}
- private async Task SetSetting(string settingName, string value)
+ private async Task SetSetting(Setting settingName, string value)
{
- inProgressSetting = settingName;
if (await Http.GetStringAsync($"api/settings/set-setting/{settingName}/{value}") == "true")
{
UpdateLocalSetting(settingName, value);
- inProgressSetting = "";
}
}
- private void UpdateLocalSetting(string settingName, string value)
+ private void UpdateLocalSetting(Setting settingName, string value)
{
switch (settingName)
{
case Setting.OutputPriority:
settings!.OutputPriority = value;
+
break;
case Setting.ChargePriority:
settings!.ChargePriority = value;
+
break;
case Setting.CombinedChargeCurrent:
settings!.MaxCombinedChargeCurrent = value;
+
break;
case Setting.UtilityChargeCurrent:
settings!.MaxACChargeCurrent = value;
- break;
- default:
+
break;
}
}
@@ -486,22 +415,22 @@
}
private string Spinner(Button button)
- => currentButton == button && !isSuccess
- ? "spinner-border"
- : "";
+ => currentButton == button && !isSuccess
+ ? "spinner-border"
+ : "";
- private string Hidden(Button button)
- => currentButton == button && !isSuccess
- ? "visually-hidden"
- : "";
+ private string Hidden(Button button)
+ => currentButton == button && !isSuccess
+ ? "visually-hidden"
+ : "";
- private string Success(Button button, string currentValue, string settingValue)
- => (currentButton == button && isSuccess) || currentValue == settingValue
- ? "oi oi-circle-check text-success"
- : "";
+ private string Success(Button button, string currentValue, string settingValue)
+ => (currentButton == button && isSuccess) || currentValue == settingValue
+ ? "oi oi-circle-check text-success"
+ : "";
private string Sanitize(string value)
- => value.StartsWith("0") ? value[1..] : value;
+ => value.StartsWith("0") ? value[1..] : value;
private enum Button
{
@@ -515,23 +444,12 @@
OpSolarBatteryUtility = 7,
UpdateUserSettings = 8,
BackToGridVoltage = 9,
- BackToBattery= 10,
+ BackToBattery = 10,
DischargeCutOff = 11,
BulkVoltage = 12,
- FloatVoltage = 13
+ FloatVoltage = 13,
+ MaxCombinedChargeCurrent = 14,
+ MaxUtilityChargeCurrent = 15
}
- private static class Setting
- {
- public const string ChargePriority = "PCP";
- public const string OutputPriority = "POP";
- public const string CombinedChargeCurrent = "MNCHGC";
- public const string UtilityChargeCurrent = "MUCHGC";
-
- public const string BulkVoltage = "PCVV";
- public const string FloatVoltage = "PBFT";
- public const string DischargeCutOff = "PSDV";
- public const string BackToGrid = "PBCV";
- public const string BackToBattery = "PBDV";
- }
}
\ No newline at end of file
diff --git a/src/Server/BatteryService/JK-BMS-RS485-Service.cs b/src/Server/BatteryService/JK-BMS-RS485-Service.cs
index bc45774..d9a3797 100644
--- a/src/Server/BatteryService/JK-BMS-RS485-Service.cs
+++ b/src/Server/BatteryService/JK-BMS-RS485-Service.cs
@@ -1,4 +1,4 @@
-using InverterMon.Server.Persistance.Settings;
+using InverterMon.Server.Persistence.Settings;
using InverterMon.Shared.Models;
using SerialPortLib;
diff --git a/src/Server/Endpoints/GetStatus/Endpoint.cs b/src/Server/Endpoints/GetStatus/Endpoint.cs
index 274cafd..8c77358 100644
--- a/src/Server/Endpoints/GetStatus/Endpoint.cs
+++ b/src/Server/Endpoints/GetStatus/Endpoint.cs
@@ -1,12 +1,12 @@
-using InverterMon.Server.InverterService;
-using InverterMon.Shared.Models;
+using InverterMon.Shared.Models;
using System.Runtime.CompilerServices;
+using InverterMon.Server.InverterService;
namespace InverterMon.Server.Endpoints.GetStatus;
public class Endpoint : EndpointWithoutRequest
{
- public CommandQueue Queue { get; set; }
+ public CurrentStatus CurrentStatus { get; set; }
public override void Configure()
{
@@ -34,7 +34,7 @@ public class Endpoint : EndpointWithoutRequest
{
if (Env.IsDevelopment())
{
- var status = Queue.StatusCommand.Result;
+ var status = CurrentStatus.Result;
status.OutputVoltage = Random.Shared.Next(240);
status.LoadWatts = Random.Shared.Next(3500);
status.LoadPercentage = Random.Shared.Next(100);
@@ -51,11 +51,8 @@ public class Endpoint : EndpointWithoutRequest
yield return status;
}
else
- {
- yield return Queue.IsAcceptingCommands
- ? Queue.StatusCommand.Result
- : blank;
- }
+ yield return CurrentStatus.Result;
+
await Task.Delay(1000, c);
}
}
diff --git a/src/Server/Endpoints/PVLog/GetPVForDay/Endpoint.cs b/src/Server/Endpoints/PVLog/GetPVForDay/Endpoint.cs
index 38fb5d4..e24b94f 100644
--- a/src/Server/Endpoints/PVLog/GetPVForDay/Endpoint.cs
+++ b/src/Server/Endpoints/PVLog/GetPVForDay/Endpoint.cs
@@ -1,5 +1,5 @@
-using InverterMon.Server.Persistance;
-using InverterMon.Server.Persistance.Settings;
+using InverterMon.Server.Persistence;
+using InverterMon.Server.Persistence.Settings;
using InverterMon.Shared.Models;
namespace InverterMon.Server.Endpoints.PVLog.GetPVForDay;
diff --git a/src/Server/Endpoints/Settings/GetChargeAmpereValues/Endpoint.cs b/src/Server/Endpoints/Settings/GetChargeAmpereValues/Endpoint.cs
deleted file mode 100644
index edaf450..0000000
--- a/src/Server/Endpoints/Settings/GetChargeAmpereValues/Endpoint.cs
+++ /dev/null
@@ -1,53 +0,0 @@
-using InverterMon.Server.InverterService;
-using InverterMon.Server.Persistance.Settings;
-using InverterMon.Shared.Models;
-
-namespace InverterMon.Server.Endpoints.Settings.GetChargeAmpereValues;
-
-public class Endpoint : EndpointWithoutRequest
-{
- public CommandQueue Queue { get; set; }
- public UserSettings UserSettings { get; set; }
-
- static ChargeAmpereValues? _ampereValues;
-
- public override void Configure()
- {
- Get("settings/get-charge-ampere-values");
- AllowAnonymous();
- }
-
- public override async Task HandleAsync(CancellationToken c)
- {
- if (Env.IsDevelopment())
- {
- await SendAsync(
- new()
- {
- CombinedAmpereValues = new[] { "010", "020", "030" },
- UtilityAmpereValues = new[] { "04", "10", "20" }
- });
-
- return;
- }
-
- if (_ampereValues is null)
- {
- var cmd1 = new InverterService.Commands.GetChargeAmpereValues(false);
- var cmd2 = new InverterService.Commands.GetChargeAmpereValues(true);
- Queue.AddCommands(cmd1, cmd2);
-
- await Task.WhenAll(
- cmd1.WhileProcessing(c, 5000),
- cmd2.WhileProcessing(c, 5000));
-
- _ampereValues = new()
- {
- CombinedAmpereValues = cmd1.Result,
- UtilityAmpereValues = cmd2.Result
- };
- }
-
- await SendAsync(_ampereValues);
- }
-}
\ No newline at end of file
diff --git a/src/Server/Endpoints/Settings/GetSettingValues/Endpoint.cs b/src/Server/Endpoints/Settings/GetSettingValues/Endpoint.cs
index 4f5365b..0a1171f 100644
--- a/src/Server/Endpoints/Settings/GetSettingValues/Endpoint.cs
+++ b/src/Server/Endpoints/Settings/GetSettingValues/Endpoint.cs
@@ -1,13 +1,10 @@
-using InverterMon.Server.InverterService;
-using InverterMon.Server.InverterService.Commands;
-using InverterMon.Server.Persistance.Settings;
+using InverterMon.Server.Persistence.Settings;
using InverterMon.Shared.Models;
namespace InverterMon.Server.Endpoints.Settings.GetSettingValues;
public class Endpoint : EndpointWithoutRequest
{
- public CommandQueue Queue { get; set; }
public UserSettings UserSettings { get; set; }
public override void Configure()
@@ -18,27 +15,29 @@ public class Endpoint : EndpointWithoutRequest
public override async Task HandleAsync(CancellationToken c)
{
- var cmd = new GetSettings();
- cmd.Result.SystemSpec = UserSettings.ToSystemSpec();
+ //todo: get values from inverter and send to client
- if (Env.IsDevelopment())
- {
- cmd.Result.ChargePriority = "03";
- cmd.Result.MaxACChargeCurrent = "10";
- cmd.Result.MaxCombinedChargeCurrent = "020";
- cmd.Result.OutputPriority = "02";
- cmd.Result.BulkChargeVoltage = 27.1m;
- await SendAsync(cmd.Result);
- return;
- }
-
- Queue.AddCommands(cmd);
-
- await cmd.WhileProcessing(c);
-
- if (cmd.IsComplete)
- await SendAsync(cmd.Result);
- else
- ThrowError("Unable to read settings in a timely manner!");
+ // var cmd = new GetSettings();
+ // cmd.Result.SystemSpec = UserSettings.ToSystemSpec();
+ //
+ // if (Env.IsDevelopment())
+ // {
+ // cmd.Result.ChargePriority = "03";
+ // cmd.Result.MaxACChargeCurrent = "10";
+ // cmd.Result.MaxCombinedChargeCurrent = "020";
+ // cmd.Result.OutputPriority = "02";
+ // cmd.Result.BulkChargeVoltage = 27.1m;
+ // await SendAsync(cmd.Result);
+ // return;
+ // }
+ //
+ // Queue.AddCommands(cmd);
+ //
+ // await cmd.WhileProcessing(c);
+ //
+ // if (cmd.IsComplete)
+ // await SendAsync(cmd.Result);
+ // else
+ // ThrowError("Unable to read settings in a timely manner!");
}
}
\ No newline at end of file
diff --git a/src/Server/Endpoints/Settings/SetSettingValue/Endpoint.cs b/src/Server/Endpoints/Settings/SetSettingValue/Endpoint.cs
index 0f845da..7a941d9 100644
--- a/src/Server/Endpoints/Settings/SetSettingValue/Endpoint.cs
+++ b/src/Server/Endpoints/Settings/SetSettingValue/Endpoint.cs
@@ -1,11 +1,7 @@
-using InverterMon.Server.InverterService;
-
-namespace InverterMon.Server.Endpoints.Settings.SetSettingValue;
+namespace InverterMon.Server.Endpoints.Settings.SetSettingValue;
public class Endpoint : Endpoint
{
- public CommandQueue Queue { get; set; }
-
public override void Configure()
{
Get("settings/set-setting/{Command}/{Value}");
@@ -14,9 +10,11 @@ public class Endpoint : Endpoint
public override async Task HandleAsync(Shared.Models.SetSetting r, CancellationToken c)
{
- var cmd = new InverterService.Commands.SetSetting(r.Command, r.Value);
- Queue.AddCommands(cmd);
- await cmd.WhileProcessing(c);
- await SendAsync(cmd.Result);
+ //todo: set settings using inveter
+
+ // var cmd = new InverterService.Commands.SetSetting(r.Command, r.Value);
+ // Queue.AddCommands(cmd);
+ // await cmd.WhileProcessing(c);
+ // await SendAsync(cmd.Result);
}
}
\ No newline at end of file
diff --git a/src/Server/Endpoints/Settings/SetSystemSpec/Endpoint.cs b/src/Server/Endpoints/Settings/SetSystemSpec/Endpoint.cs
index 7b2bf73..9e27e69 100644
--- a/src/Server/Endpoints/Settings/SetSystemSpec/Endpoint.cs
+++ b/src/Server/Endpoints/Settings/SetSystemSpec/Endpoint.cs
@@ -1,5 +1,5 @@
-using InverterMon.Server.Persistance;
-using InverterMon.Server.Persistance.Settings;
+using InverterMon.Server.Persistence;
+using InverterMon.Server.Persistence.Settings;
namespace InverterMon.Server.Endpoints.Settings.SetSystemSpec;
diff --git a/src/Server/InverterService/CommandExecutor.cs b/src/Server/InverterService/CommandExecutor.cs
deleted file mode 100644
index 841a1e7..0000000
--- a/src/Server/InverterService/CommandExecutor.cs
+++ /dev/null
@@ -1,102 +0,0 @@
-using System.Diagnostics;
-using ICommand = InverterMon.Server.InverterService.Commands.ICommand;
-
-namespace InverterMon.Server.InverterService;
-
-class CommandExecutor : BackgroundService
-{
- readonly CommandQueue queue;
- readonly ILogger logger;
- readonly string _devPath = "/dev/hidraw0";
- readonly bool _isTroubleMode;
- readonly string _mppPath = "/usr/local/bin/mpp-solar";
-
- public CommandExecutor(CommandQueue queue, IConfiguration config, ILogger log)
- {
- this.queue = queue;
- logger = log;
- _devPath = config["LaunchSettings:DeviceAddress"] ?? _devPath;
- _isTroubleMode = config["LaunchSettings:TroubleMode"] == "yes";
- _mppPath = config["LaunchSettings:MppSolarPath"] ?? _mppPath;
-
- log.LogInformation("connecting to the inverter...");
-
- var sw = new Stopwatch();
- sw.Start();
-
- while (!Connect() && sw.Elapsed.TotalMinutes <= 5)
- Thread.Sleep(10000);
-
- if (sw.Elapsed.TotalMinutes >= 5)
- log.LogInformation("inverter connecting timed out!");
- }
-
- bool Connect()
- {
- if (!Inverter.Connect(_devPath, logger))
- return false;
-
- logger.LogInformation("connected to inverter at: [{adr}]", _devPath);
-
- return true;
- }
-
- protected override async Task ExecuteAsync(CancellationToken ct)
- {
- var delay = 0;
- var timeout = TimeSpan.FromMinutes(5);
-
- while (!ct.IsCancellationRequested && delay <= timeout.TotalMilliseconds)
- {
- var cmd = queue.GetCommand();
-
- if (cmd is not null)
- {
- try
- {
- await ExecuteCommand(cmd, ct);
- queue.IsAcceptingCommands = true;
- delay = 0;
- queue.RemoveCommand();
- }
- catch (Exception x)
- {
- queue.IsAcceptingCommands = false;
- logger.LogError("command [{cmd}] failed with reason [{msg}]", cmd.CommandString, x.Message);
- await Task.Delay(delay += 1000);
- }
- }
- else
- await Task.Delay(500, ct);
- }
- logger.LogError("command execution halted due to excessive failures!");
- }
-
- async Task ExecuteCommand(ICommand command, CancellationToken ct)
- {
- if (_isTroubleMode && command.IsTroublesomeCmd)
- {
- Inverter.Disconnect();
- using var process = new Process();
- process.StartInfo.FileName = _mppPath;
- process.StartInfo.Arguments = $"-p {_devPath} -o raw -c {command.CommandString}";
- process.StartInfo.UseShellExecute = false;
- process.StartInfo.RedirectStandardOutput = true;
- process.Start();
- command.Start();
- var output = await process.StandardOutput.ReadToEndAsync(ct);
- var result = output.ParseCli()[1..^1];
- command.Parse(result);
- command.End();
- await process.WaitForExitAsync(ct);
- Inverter.Connect(_devPath, logger);
- }
- else
- {
- command.Start();
- await Inverter.Write(command.CommandString, ct);
- command.Parse(await Inverter.Read(ct));
- command.End();
- }
- }
-}
\ No newline at end of file
diff --git a/src/Server/InverterService/CommandQueue.cs b/src/Server/InverterService/CommandQueue.cs
deleted file mode 100644
index 97a6db1..0000000
--- a/src/Server/InverterService/CommandQueue.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using System.Collections.Concurrent;
-using InverterMon.Server.InverterService.Commands;
-using ICommand = InverterMon.Server.InverterService.Commands.ICommand;
-
-namespace InverterMon.Server.InverterService;
-
-public class CommandQueue
-{
- public bool IsAcceptingCommands { get; set; } = true;
- public GetStatus StatusCommand { get; } = new();
-
- readonly ConcurrentQueue _toProcess = new();
-
- public bool AddCommands(params ICommand[] commands)
- {
- if (IsAcceptingCommands)
- {
- foreach (var cmd in commands)
- _toProcess.Enqueue(cmd);
-
- return true;
- }
-
- return false;
- }
-
- public ICommand? GetCommand()
- => _toProcess.TryPeek(out var command) ? command : null;
-
- public void RemoveCommand()
- => _toProcess.TryDequeue(out _);
-}
\ No newline at end of file
diff --git a/src/Server/InverterService/Commands/Command.cs b/src/Server/InverterService/Commands/Command.cs
deleted file mode 100644
index 0bf03ae..0000000
--- a/src/Server/InverterService/Commands/Command.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-// ReSharper disable UnassignedGetOnlyAutoProperty
-
-namespace InverterMon.Server.InverterService.Commands;
-
-public interface ICommand
-{
- string CommandString { get; set; }
- bool IsTroublesomeCmd { get; }
- void Parse(string rawResponse);
- void Start();
- void End();
-}
-
-public abstract class Command : ICommand where TResponseDto : new()
-{
- public abstract string CommandString { get; set; }
- public virtual bool IsTroublesomeCmd { get; }
- public TResponseDto Result { get; protected set; } = new();
- public bool IsComplete { get; private set; }
-
- public abstract void Parse(string responseFromInverter);
-
- protected DateTime startTime = DateTime.Now;
-
- public void Start()
- {
- startTime = DateTime.Now;
- IsComplete = false;
- }
-
- public void End()
- => IsComplete = true;
-
- public async Task WhileProcessing(CancellationToken c, int timeoutMillis = Constants.StatusPollingFrequencyMillis)
- {
- while (!c.IsCancellationRequested && !IsComplete && DateTime.Now.Subtract(startTime).TotalMilliseconds <= timeoutMillis)
- await Task.Delay(500, c);
- }
-}
\ No newline at end of file
diff --git a/src/Server/InverterService/Commands/GetChargeAmpereValues.cs b/src/Server/InverterService/Commands/GetChargeAmpereValues.cs
deleted file mode 100644
index 18cd72f..0000000
--- a/src/Server/InverterService/Commands/GetChargeAmpereValues.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-// ReSharper disable VirtualMemberCallInConstructor
-
-namespace InverterMon.Server.InverterService.Commands;
-
-class GetChargeAmpereValues : Command>
-{
- public override string CommandString { get; set; } = "QMCHGCR";
- public override bool IsTroublesomeCmd { get; } = true;
-
- public GetChargeAmpereValues(bool getUtilityValues)
- {
- Result.AddRange(new[] { "000" });
-
- if (getUtilityValues)
- CommandString = "QMUCHGCR";
- }
-
- public override void Parse(string responseFromInverter)
- {
- if (responseFromInverter.StartsWith("(NAK"))
- return;
-
- var parts = responseFromInverter[1..]
- .Split(' ', StringSplitOptions.RemoveEmptyEntries)
- .Select(x => x[..3]);
-
- if (parts.Any())
- Result.Clear(); //remove default values
-
- Result.AddRange(parts);
- }
-}
\ No newline at end of file
diff --git a/src/Server/InverterService/Commands/GetSettings.cs b/src/Server/InverterService/Commands/GetSettings.cs
deleted file mode 100644
index 16bdd34..0000000
--- a/src/Server/InverterService/Commands/GetSettings.cs
+++ /dev/null
@@ -1,52 +0,0 @@
-using InverterMon.Shared.Models;
-
-namespace InverterMon.Server.InverterService.Commands;
-
-class GetSettings : Command
-{
- public override string CommandString { get; set; } = "QPIRI";
-
- public override void Parse(string responseFromInverter)
- {
- // 1) 230.0 - grid rating voltage
- // 2) 15.2 - grid rating current
- // 3) 230.0 - ac output rating voltage
- // 4) 50.0 - ac output rating frequency
- // 5) 15.2 - ac output rating current
- // 6) 3500 - ac output rating apparant power
- // 7) 3500 - ac output rating active power
- // 8) 24.0 - batt rating voltage
- // 9) 23.5 - batt back to grid voltage
- // 10) 23.4 - batt discharge cut off voltage
- // 11) 28.8 - batt bulk charging voltage
- // 12) 27.0 - batt float charging voltage
- // 13) 2 - battery type (0:agm / 1:flooded / 2: user)
- // 14) 10 - max ac charging current
- // 15) 020 - max combined charging current
- // 16) 1 - input voltage range (0:appliance / 1:ups)
- // 17) 1 - output source priority (0:utility first / 1:solar first / 2:solar>battery>utility)
- // 18) 3 - charge priority (0:utility first /1:solar first / 2:solar & utility / 3:only solar)
- // 19) 1 - parallel max number
- // 20) 01 - machine type
- // 21) 0 - topology
- // 22) 0 - output mode
- // 23) 28.5 - back to battery use voltage
- // 24) 0 - pv ok for parallel
- // 25) 1 - pv power balance
-
- if (responseFromInverter.StartsWith("(NAK"))
- return;
-
- var parts = responseFromInverter[1..].Split(' ', StringSplitOptions.RemoveEmptyEntries);
-
- Result.BackToGridVoltage = decimal.Parse(parts[9 - 1]);
- Result.DischargeCuttOffVoltage = decimal.Parse(parts[10 - 1]);
- Result.BulkChargeVoltage = decimal.Parse(parts[11 - 1]);
- Result.FloatChargeVoltage = decimal.Parse(parts[12 - 1]);
- Result.MaxACChargeCurrent = parts[14 - 1];
- Result.MaxCombinedChargeCurrent = parts[15 - 1];
- Result.OutputPriority = $"0{parts[17 - 1]}";
- Result.ChargePriority = $"0{parts[18 - 1]}";
- Result.BackToBatteryVoltage = decimal.Parse(parts[23 - 1]);
- }
-}
\ No newline at end of file
diff --git a/src/Server/InverterService/Commands/GetStatus.cs b/src/Server/InverterService/Commands/GetStatus.cs
deleted file mode 100644
index 7e1553b..0000000
--- a/src/Server/InverterService/Commands/GetStatus.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using InverterMon.Shared.Models;
-
-namespace InverterMon.Server.InverterService.Commands;
-
-public class GetStatus : Command
-{
- public override string CommandString { get; set; } = "QPIGS";
-
- public override void Parse(string responseFromInverter)
- {
- //(232.0 50.1 232.0 50.1 0000 0000 000 476 27.02 000 100 0553 0000 000.0 27.00 00000 10011101 03 04 00000 101a\xc8\r
- //(000.0 00.0 229.8 50.0 0851 0701 023 355 26.20 000 050 0041 00.0 058.5 00.00 00031 00010000 00 00 00000 010 0 01 0000
-
- if (responseFromInverter.StartsWith("(NAK"))
- return;
-
- var parts = responseFromInverter[1..].Split(' ', StringSplitOptions.RemoveEmptyEntries);
-
- Result.GridVoltage = decimal.Parse(parts[0]);
- Result.OutputVoltage = decimal.Parse(parts[2]);
- Result.LoadWatts = int.Parse(parts[5]);
- Result.LoadPercentage = decimal.Parse(parts[6]);
- Result.BatteryVoltage = decimal.Parse(parts[8]);
- Result.BatteryChargeCurrent = int.Parse(parts[9]);
- Result.HeatSinkTemperature = int.Parse(parts[11]);
- Result.PVInputCurrent = decimal.Parse(parts[12]);
- Result.PVInputVoltage = decimal.Parse(parts[13]);
- Result.BatteryDischargeCurrent = int.Parse(parts[15]);
- Result.PVInputWatt = Result.PVInputVoltage == 00 ? 0 : Convert.ToInt32(int.Parse(parts[19]));
- }
-}
\ No newline at end of file
diff --git a/src/Server/InverterService/Commands/SetSetting.cs b/src/Server/InverterService/Commands/SetSetting.cs
deleted file mode 100644
index 8e05725..0000000
--- a/src/Server/InverterService/Commands/SetSetting.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-// ReSharper disable VirtualMemberCallInConstructor
-
-namespace InverterMon.Server.InverterService.Commands;
-
-class SetSetting : Command
-{
- public override string CommandString { get; set; }
- public override bool IsTroublesomeCmd { get; } = true;
-
- public SetSetting(string settingName, string settingValue)
- {
- CommandString = settingName + settingValue;
- }
-
- public override void Parse(string responseFromInverter)
- {
- Result = responseFromInverter[1..4] == "ACK";
- }
-}
\ No newline at end of file
diff --git a/src/Server/InverterService/Constants.cs b/src/Server/InverterService/Constants.cs
deleted file mode 100644
index 72a4f30..0000000
--- a/src/Server/InverterService/Constants.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace InverterMon.Server.InverterService;
-
-public static class Constants
-{
- public const int StatusPollingFrequencyMillis = 2000;
-}
\ No newline at end of file
diff --git a/src/Server/InverterService/CurrentStatus.cs b/src/Server/InverterService/CurrentStatus.cs
new file mode 100644
index 0000000..7909c54
--- /dev/null
+++ b/src/Server/InverterService/CurrentStatus.cs
@@ -0,0 +1,8 @@
+using InverterMon.Shared.Models;
+
+namespace InverterMon.Server.InverterService;
+
+public class CurrentStatus
+{
+ public InverterStatus Result { get; set; }
+}
\ No newline at end of file
diff --git a/src/Server/InverterService/Extensions/ResponseSanitizer.cs b/src/Server/InverterService/Extensions/ResponseSanitizer.cs
deleted file mode 100644
index f00c559..0000000
--- a/src/Server/InverterService/Extensions/ResponseSanitizer.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-using System.Text.RegularExpressions;
-
-namespace InverterMon.Server.InverterService;
-
-public static partial class Extensions
-{
- [GeneratedRegex(@"[^\u0009\u000A\u000D\u0020-\u007E]")]
- private static partial Regex StringSanitizer();
-
- static readonly Regex _sanRx = StringSanitizer();
-
- public static string Sanitize(this string input)
- => _sanRx.Replace(input, "");
-
- [GeneratedRegex(@"'\((.*?)\\")]
- private static partial Regex CLIParser();
-
- static readonly Regex _cliRx = CLIParser();
-
- public static string ParseCli(this string input)
- {
- var match = _cliRx.Match(input);
-
- return match.Success
- ? match.Groups[0].Value
- : "`(NAK\\";
- }
-}
\ No newline at end of file
diff --git a/src/Server/InverterService/Inverter.cs b/src/Server/InverterService/Inverter.cs
deleted file mode 100644
index b353fd2..0000000
--- a/src/Server/InverterService/Inverter.cs
+++ /dev/null
@@ -1,143 +0,0 @@
-using System.IO.Ports;
-using System.Text;
-
-namespace InverterMon.Server.InverterService;
-
-public static class Inverter
-{
- static SerialPort? _serialPort;
- static FileStream? _fileStream;
-
- public static bool Connect(string devicePath, ILogger logger)
- {
- try
- {
- if (devicePath.Contains("/hidraw", StringComparison.OrdinalIgnoreCase))
- {
- _fileStream = new(devicePath, FileMode.Open, FileAccess.ReadWrite);
-
- return true;
- }
-
- if (devicePath.Contains("/ttyUSB", StringComparison.OrdinalIgnoreCase) || devicePath.Contains("COM", StringComparison.OrdinalIgnoreCase))
- {
- _serialPort = new(devicePath)
- {
- BaudRate = 2400,
- Parity = Parity.None,
- DataBits = 8,
- StopBits = StopBits.One,
- Handshake = Handshake.None
- };
- _serialPort.Open();
-
- return true;
- }
- logger.LogError("device path [{path}] is not acceptable!", devicePath);
- }
- catch (Exception x)
- {
- logger.LogError("connection error at [{path}]. reason: [{reason}]", devicePath, x.Message);
- }
-
- return false;
- }
-
- public static void Disconnect()
- {
- _serialPort?.Close();
- _serialPort?.Dispose();
- _fileStream?.Close();
- _fileStream?.Dispose();
- }
-
- static readonly byte[] _writeBuffer = new byte[512];
-
- public static Task Write(string command, CancellationToken ct)
- {
- var cmdBytes = Encoding.ASCII.GetBytes(command);
- var crc = CalculateXmodemCrc16(command);
-
- Buffer.BlockCopy(cmdBytes, 0, _writeBuffer, 0, cmdBytes.Length);
- _writeBuffer[cmdBytes.Length] = (byte)(crc >> 8);
- _writeBuffer[cmdBytes.Length + 1] = (byte)(crc & 0xff);
- _writeBuffer[cmdBytes.Length + 2] = 0x0d;
-
- if (_fileStream != null)
- return _fileStream.WriteAsync(_writeBuffer, 0, cmdBytes.Length + 3, ct);
-
- return _serialPort != null
- ? _serialPort.BaseStream.WriteAsync(_writeBuffer, 0, cmdBytes.Length + 3, ct)
- : Task.CompletedTask;
- }
-
- static readonly byte[] _readBuffer = new byte[1024];
-
- public static async Task Read(CancellationToken ct)
- {
- var pos = 0;
- const byte eol = 0x0d;
-
- if (_fileStream != null)
- {
- do
- {
- var readCount = await _fileStream.ReadAsync(_readBuffer.AsMemory(pos, _readBuffer.Length - pos), ct);
-
- if (readCount > 0)
- {
- pos += readCount;
-
- for (var i = pos - readCount; i < pos; i++)
- {
- if (_readBuffer[i] == eol)
- return Encoding.ASCII.GetString(_readBuffer, 0, i - 2).Sanitize();
- }
- }
- } while (pos < _readBuffer.Length);
- }
- else if (_serialPort != null)
- {
- do
- {
- var readCount = await _serialPort.BaseStream.ReadAsync(_readBuffer.AsMemory(pos, _readBuffer.Length - pos), ct);
-
- if (readCount > 0)
- {
- pos += readCount;
-
- for (var i = pos - readCount; i < pos; i++)
- {
- if (_readBuffer[i] == eol)
- return Encoding.ASCII.GetString(_readBuffer, 0, i - 2).Sanitize();
- }
- }
- } while (pos < _readBuffer.Length);
- }
- else
- throw new InvalidOperationException("inverter not connected.");
-
- throw new InvalidOperationException("buffer overflow.");
- }
-
- static ushort CalculateXmodemCrc16(string data)
- {
- ushort crc = 0;
- var length = data.Length;
-
- for (var i = 0; i < length; i++)
- {
- crc ^= (ushort)(data[i] << 8);
-
- for (var j = 0; j < 8; j++)
- {
- if ((crc & 0x8000) != 0)
- crc = (ushort)((crc << 1) ^ 0x1021);
- else
- crc <<= 1;
- }
- }
-
- return crc;
- }
-}
\ No newline at end of file
diff --git a/src/Server/InverterService/StatusRetriever.cs b/src/Server/InverterService/StatusRetriever.cs
index 237b330..8de9d1c 100644
--- a/src/Server/InverterService/StatusRetriever.cs
+++ b/src/Server/InverterService/StatusRetriever.cs
@@ -1,26 +1,22 @@
-using InverterMon.Server.Persistance;
-using InverterMon.Server.Persistance.Settings;
+using InverterMon.Server.Persistence;
+using InverterMon.Server.Persistence.Settings;
namespace InverterMon.Server.InverterService;
-class StatusRetriever(CommandQueue queue, Database db, UserSettings userSettings) : BackgroundService
+class StatusRetriever(Database db, CurrentStatus currentStatus, UserSettings userSettings) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken c)
{
- var cmd = queue.StatusCommand;
-
while (!c.IsCancellationRequested)
{
- if (queue.IsAcceptingCommands)
- {
- //feels hacky. find a better solution.
- cmd.Result.BatteryCapacity = userSettings.BatteryCapacity;
- cmd.Result.PV_MaxCapacity = userSettings.PV_MaxCapacity;
+ currentStatus.Result.BatteryCapacity = userSettings.BatteryCapacity;
+ currentStatus.Result.PV_MaxCapacity = userSettings.PV_MaxCapacity;
- queue.AddCommands(cmd);
- _ = db.UpdateTodaysPvGeneration(cmd, c);
- }
- await Task.Delay(Constants.StatusPollingFrequencyMillis);
+ //todo: get data from inverter and map to CurrentStatus.Result
+
+ _ = db.UpdateTodaysPvGeneration(currentStatus, c);
+
+ await Task.Delay(2000);
}
}
}
\ No newline at end of file
diff --git a/src/Server/InverterService/protocol docs/voltronic-inverter-protocol.pdf b/src/Server/InverterService/protocol docs/voltronic-inverter-protocol.pdf
deleted file mode 100644
index a4d5c9b..0000000
Binary files a/src/Server/InverterService/protocol docs/voltronic-inverter-protocol.pdf and /dev/null differ
diff --git a/src/Server/Persistance/Database.cs b/src/Server/Persistance/Database.cs
index 226bd63..2e86e8d 100644
--- a/src/Server/Persistance/Database.cs
+++ b/src/Server/Persistance/Database.cs
@@ -1,23 +1,22 @@
using InverterMon.Server.InverterService;
-using InverterMon.Server.InverterService.Commands;
-using InverterMon.Server.Persistance.PVGen;
-using InverterMon.Server.Persistance.Settings;
+using InverterMon.Server.Persistence.PVGen;
+using InverterMon.Server.Persistence.Settings;
using LiteDB;
-namespace InverterMon.Server.Persistance;
+namespace InverterMon.Server.Persistence;
public class Database
{
readonly LiteDatabase _db;
- readonly CommandQueue _queue;
+ readonly CurrentStatus _currentStatus;
readonly UserSettings _settings;
readonly ILiteCollection _pvGenCollection;
readonly ILiteCollection _usrSettingsCollection;
PVGeneration? _today;
- public Database(IHostApplicationLifetime lifetime, CommandQueue queue, UserSettings settings)
+ public Database(IHostApplicationLifetime lifetime, CurrentStatus status, UserSettings settings)
{
- _queue = queue;
+ _currentStatus = status;
_settings = settings;
_db = new("InverterMon.db") { CheckpointSize = 0 };
lifetime.ApplicationStopping.Register(() => _db?.Dispose());
@@ -27,8 +26,6 @@ public class Database
RestoreUserSettings();
}
- //todo: break apart this class and put seperated logic in each vertical slice
-
public void RestoreTodaysPvWattHours()
{
var todayDayNumber = DateOnly.FromDateTime(DateTime.Now).DayNumber;
@@ -39,26 +36,24 @@ public class Database
.SingleOrDefault();
if (_today is not null)
- _queue.StatusCommand.Result.RestorePVWattHours(_today.TotalWattHours);
+ _currentStatus.Result.RestorePVWattHours(_today.TotalWattHours);
else
{
_today = new() { Id = todayDayNumber };
_today.SetTotalWattHours(0);
- _queue.StatusCommand.Result.RestorePVWattHours(0);
+ _currentStatus.Result.RestorePVWattHours(0);
_pvGenCollection.Insert(_today);
_db.Checkpoint();
}
}
- public async Task UpdateTodaysPvGeneration(GetStatus cmd, CancellationToken c)
+ public async Task UpdateTodaysPvGeneration(CurrentStatus cmd, CancellationToken c)
{
var hourNow = DateTime.Now.Hour;
if (hourNow < _settings.SunlightStartHour || hourNow >= _settings.SunlightEndHour)
return;
- await cmd.WhileProcessing(c);
-
var todayDayNumber = DateOnly.FromDateTime(DateTime.Now).DayNumber;
if (_today?.Id == todayDayNumber)
diff --git a/src/Server/Persistance/PVGen/PVGenExtensions.cs b/src/Server/Persistance/PVGen/PVGenExtensions.cs
index 374a2e6..a64a530 100644
--- a/src/Server/Persistance/PVGen/PVGenExtensions.cs
+++ b/src/Server/Persistance/PVGen/PVGenExtensions.cs
@@ -1,4 +1,4 @@
-namespace InverterMon.Server.Persistance.PVGen;
+namespace InverterMon.Server.Persistence.PVGen;
public static class PVGenExtensions
{
diff --git a/src/Server/Persistance/PVGen/PVGeneration.cs b/src/Server/Persistance/PVGen/PVGeneration.cs
index fea1d0a..dfaf137 100644
--- a/src/Server/Persistance/PVGen/PVGeneration.cs
+++ b/src/Server/Persistance/PVGen/PVGeneration.cs
@@ -1,4 +1,4 @@
-namespace InverterMon.Server.Persistance.PVGen;
+namespace InverterMon.Server.Persistence.PVGen;
public class PVGeneration
{
diff --git a/src/Server/Persistance/Settings/UserSettings.cs b/src/Server/Persistance/Settings/UserSettings.cs
index c6fb42d..3f8c0a7 100644
--- a/src/Server/Persistance/Settings/UserSettings.cs
+++ b/src/Server/Persistance/Settings/UserSettings.cs
@@ -1,7 +1,7 @@
-using InverterMon.Server.Persistance.PVGen;
+using InverterMon.Server.Persistence.PVGen;
using InverterMon.Shared.Models;
-namespace InverterMon.Server.Persistance.Settings;
+namespace InverterMon.Server.Persistence.Settings;
public class UserSettings
{
diff --git a/src/Server/Program.cs b/src/Server/Program.cs
index d18b8cb..d000ab0 100644
--- a/src/Server/Program.cs
+++ b/src/Server/Program.cs
@@ -4,8 +4,8 @@ using System.Net;
using InverterMon.Server;
using InverterMon.Server.BatteryService;
using InverterMon.Server.InverterService;
-using InverterMon.Server.Persistance;
-using InverterMon.Server.Persistance.Settings;
+using InverterMon.Server.Persistence;
+using InverterMon.Server.Persistence.Settings;
//avoid parsing issues with non-english cultures
var cultureInfo = new CultureInfo("en-US");
@@ -18,15 +18,14 @@ _ = int.TryParse(bld.Configuration["LaunchSettings:WebPort"] ?? "80", out var po
bld.WebHost.ConfigureKestrel(o => o.Listen(IPAddress.Any, port));
bld.Services
+ .AddSingleton()
.AddSingleton()
- .AddSingleton()
.AddSingleton()
.AddSingleton();
if (!bld.Environment.IsDevelopment())
{
bld.Services
- .AddHostedService()
.AddHostedService();
}
diff --git a/src/Shared/Models/ChargeAmpereValues.cs b/src/Shared/Models/ChargeAmpereValues.cs
deleted file mode 100644
index c70f112..0000000
--- a/src/Shared/Models/ChargeAmpereValues.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace InverterMon.Shared.Models;
-
-public class ChargeAmpereValues
-{
- public IEnumerable CombinedAmpereValues { get; set; }
- public IEnumerable UtilityAmpereValues { get; set; }
-}
\ No newline at end of file
diff --git a/src/Shared/Models/ChargePriority.cs b/src/Shared/Models/ChargePriority.cs
index 736e897..af10e82 100644
--- a/src/Shared/Models/ChargePriority.cs
+++ b/src/Shared/Models/ChargePriority.cs
@@ -2,8 +2,8 @@
public static class ChargePriority
{
- public const string SolarFirst = "01";
- public const string SolarAndUtility = "02";
- public const string OnlySolar = "03";
- public const string UtilityFirst = "00";
-}
+ public const string SolarFirst = "1";
+ public const string SolarAndUtility = "2";
+ public const string OnlySolar = "3";
+ public const string UtilityFirst = "0";
+}
\ No newline at end of file
diff --git a/src/Shared/Models/InverterSetting.cs b/src/Shared/Models/InverterSetting.cs
new file mode 100644
index 0000000..18be280
--- /dev/null
+++ b/src/Shared/Models/InverterSetting.cs
@@ -0,0 +1,14 @@
+namespace InverterMon.Shared.Models;
+
+public enum Setting
+{
+ ChargePriority = 1,
+ OutputPriority = 2,
+ CombinedChargeCurrent = 3,
+ UtilityChargeCurrent = 4,
+ BulkVoltage = 5,
+ FloatVoltage = 6,
+ DischargeCutOff = 7,
+ BackToGrid = 8,
+ BackToBattery = 9
+}
\ No newline at end of file
diff --git a/src/Shared/Models/OutputPriority.cs b/src/Shared/Models/OutputPriority.cs
index 9d6c012..677db81 100644
--- a/src/Shared/Models/OutputPriority.cs
+++ b/src/Shared/Models/OutputPriority.cs
@@ -2,7 +2,7 @@
public static class OutputPriority
{
- public const string SolarFirst = "01";
- public const string SolarBatteryUtility = "02";
- public const string UtilityFirst = "00";
+ public const string SolarFirst = "1";
+ public const string SolarBatteryUtility = "2";
+ public const string UtilityFirst = "0";
}
\ No newline at end of file