From 80caa1c6313406560ba6fb567da5b1b11f4feed7 Mon Sep 17 00:00:00 2001 From: djnitehawk Date: Thu, 13 Mar 2025 10:12:31 +0530 Subject: [PATCH] inverter testing passed --- src/Client/Pages/Index.razor | 14 +----- src/Client/Pages/Settings.razor | 10 ++-- src/Server/Endpoints/GetStatus/Endpoint.cs | 49 ++++++++++--------- .../Settings/GetSettingValues/Endpoint.cs | 38 +++++++------- .../Settings/SetSettingValue/Endpoint.cs | 13 ++--- .../InverterService/FelicityInverter.cs | 48 +++++++++++++----- src/Server/InverterService/StatusRetriever.cs | 15 ++++-- .../Persistance/Settings/UserSettings.cs | 2 +- src/Server/Program.cs | 11 +++-- src/Server/Properties/launchSettings.json | 4 +- src/Server/appsettings.json | 7 +-- src/Shared/Models/BMSStatus.cs | 2 +- src/Shared/Models/InverterSetting.cs | 11 +++++ src/Shared/Models/InverterStatus.cs | 6 +-- src/Shared/Models/SystemSpec.cs | 2 +- 15 files changed, 135 insertions(+), 97 deletions(-) diff --git a/src/Client/Pages/Index.razor b/src/Client/Pages/Index.razor index e4ecc56..8bd48b7 100644 --- a/src/Client/Pages/Index.razor +++ b/src/Client/Pages/Index.razor @@ -62,8 +62,8 @@
- - @(status?.HeatSinkTemperature) C° + + @(status?.WorkingMode)
@@ -205,16 +205,6 @@ private static double RoundToOneDecimal(double? val) => Math.Round(val ?? 0, 1); - private static string TemperatureCss() - { - return status?.HeatSinkTemperature switch - { - >= 55 and < 65 => "text-danger", - >= 65 => "text-danger fw-bolder blinktext", - _ => "text-muted" - }; - } - public static async Task StartStatusStreaming(string basePath) { //note: only reason we have a full-time stream download is because there's a bug in diff --git a/src/Client/Pages/Settings.razor b/src/Client/Pages/Settings.razor index 1ce309f..89114cb 100644 --- a/src/Client/Pages/Settings.razor +++ b/src/Client/Pages/Settings.razor @@ -375,11 +375,11 @@ } } - private async Task SetChargeCurrent(Setting settingName, byte value) + private async Task SetChargeCurrent(Setting setting, byte value) { isSuccess = false; - switch (settingName) + switch (setting) { case Setting.CombinedChargeCurrent: currentButton = Button.MaxCombinedChargeCurrent; @@ -392,10 +392,12 @@ break; } - if (await Http.GetStringAsync($"api/settings/set-setting/{settingName}/{value}") == "true") + var xxx = $"api/settings/set-setting/{setting}/{value}"; + + if (await Http.GetStringAsync($"api/settings/set-setting/{setting}/{value}") == "true") { isSuccess = true; - UpdateLocalSetting(settingName, value); + UpdateLocalSetting(setting, value); } } diff --git a/src/Server/Endpoints/GetStatus/Endpoint.cs b/src/Server/Endpoints/GetStatus/Endpoint.cs index f9a0ff2..13a30ba 100644 --- a/src/Server/Endpoints/GetStatus/Endpoint.cs +++ b/src/Server/Endpoints/GetStatus/Endpoint.cs @@ -7,6 +7,7 @@ namespace InverterMon.Server.Endpoints.GetStatus; public class Endpoint : EndpointWithoutRequest { public FelicitySolarInverter Inverter { get; set; } = null!; + public IHostApplicationLifetime AppLife { get; set; } = null!; public override void Configure() { @@ -28,32 +29,32 @@ public class Endpoint : EndpointWithoutRequest async IAsyncEnumerable GetDataStream([EnumeratorCancellation] CancellationToken c) { - while (!c.IsCancellationRequested) + while (!c.IsCancellationRequested && !AppLife.ApplicationStopping.IsCancellationRequested) { - if (Env.IsDevelopment()) - { - var status = new InverterStatus - { - OutputVoltage = Random.Shared.Next(240), - LoadWatts = Random.Shared.Next(3500), - LoadPercentage = Random.Shared.Next(100), - BatteryVoltage = Random.Shared.Next(24), - BatteryChargeCurrent = Random.Shared.Next(20), - BatteryDischargeCurrent = Random.Shared.Next(300), - HeatSinkTemperature = Random.Shared.Next(300), - PVInputCurrent = Random.Shared.Next(300), - PVInputVoltage = Random.Shared.Next(300), - PVInputWatt = Random.Shared.Next(1000), - PV_MaxCapacity = 1000, - BatteryCapacity = 100 - }; + // if (Env.IsDevelopment()) + // { + // var status = new InverterStatus + // { + // OutputVoltage = Random.Shared.Next(240), + // LoadWatts = Random.Shared.Next(3500), + // LoadPercentage = Random.Shared.Next(100), + // BatteryVoltage = Random.Shared.Next(24), + // BatteryChargeCurrent = Random.Shared.Next(20), + // BatteryDischargeCurrent = Random.Shared.Next(300), + // HeatSinkTemperature = Random.Shared.Next(300), + // PVInputCurrent = Random.Shared.Next(300), + // PVInputVoltage = Random.Shared.Next(300), + // PVInputWatt = Random.Shared.Next(1000), + // PV_MaxCapacity = 1000, + // BatteryCapacity = 100 + // }; + // + // yield return status; + // } + // else + yield return Inverter.Status; - yield return status; - } - else - yield return Inverter.Status; - - await Task.Delay(1000, c); + await Task.Delay(2000, c); } } } \ 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 dbfa4c0..c64c14a 100644 --- a/src/Server/Endpoints/Settings/GetSettingValues/Endpoint.cs +++ b/src/Server/Endpoints/Settings/GetSettingValues/Endpoint.cs @@ -17,25 +17,25 @@ public class Endpoint : EndpointWithoutRequest public override async Task HandleAsync(CancellationToken c) { - 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); - - return; - } + // 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); + // + // return; + // } try { diff --git a/src/Server/Endpoints/Settings/SetSettingValue/Endpoint.cs b/src/Server/Endpoints/Settings/SetSettingValue/Endpoint.cs index 2f113b2..c28f671 100644 --- a/src/Server/Endpoints/Settings/SetSettingValue/Endpoint.cs +++ b/src/Server/Endpoints/Settings/SetSettingValue/Endpoint.cs @@ -14,16 +14,17 @@ public class Endpoint : Endpoint public override async Task HandleAsync(Shared.Models.SetSetting r, CancellationToken c) { - if (Env.IsDevelopment()) - { - await SendAsync(true, cancellation: c); - - return; - } + // if (Env.IsDevelopment()) + // { + // await SendAsync(true, cancellation: c); + // + // return; + // } try { Inverter.SetSetting(r.Setting, r.Value); + await SendAsync(true, cancellation: c); } catch { diff --git a/src/Server/InverterService/FelicityInverter.cs b/src/Server/InverterService/FelicityInverter.cs index e3f192c..9133b41 100644 --- a/src/Server/InverterService/FelicityInverter.cs +++ b/src/Server/InverterService/FelicityInverter.cs @@ -57,19 +57,39 @@ public sealed class FelicitySolarInverter { var regs = ReadRegisters(StatusStartAddress, StatusRegisterCount); - // WorkingMode = regs[0], // 0x1101: Working mode (offset 0) + lock (_lock) + { + if (!_serialPort.IsOpen) + return; + } + // BatteryChargingStage = regs[1], // 0x1102: Battery charging stage (offset 1) - 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.BatteryChargeCurrent = regs[8]; // 0x1109: Battery current (offset 8) -- signed value - Status.BatteryDischargeWatts = regs[9]; // 0x110A: Battery power (offset 9) -- signed value - Status.BatteryChargeWatts = regs[9]; // 0x110A: Battery power (offset 9) -- signed value - Status.OutputVoltage = regs[16] / 10.0; // 0x1111: AC output voltage (offset 0x1111 - 0x1101 = 16) - Status.LoadWatts = regs[29]; // 0x111E: AC output active power (offset 0x111E - 0x1101 = 29) - Status.LoadPercentage = regs[31]; // 0x1120: Load percentage (offset 0x1120 - 0x1101 = 31) - Status.PVInputVoltage = regs[37] / 10.0; // 0x1126: PV input voltage (offset 0x1126 - 0x1101 = 37) - Status.PVInputWatt = regs[41]; // 0x112A: PV input power (offset 0x112A - 0x1101 = 41) -- signed value + Status.WorkingMode = (WorkingMode)regs[0]; // 0x1101: Working mode (offset 0) + Status.BatteryVoltage = regs[7] / 100.0; // 0x1108: Battery voltage (offset 0x1108 - 0x1101 = 7) + + var disCur = ChargeStatus(regs[8]); // 0x1109: Battery current (offset 8) -- signed value + Status.BatteryDischargeCurrent = disCur.IsDischarge ? disCur.PositiveValue : 0; + Status.BatteryChargeCurrent = disCur.IsDischarge is false ? disCur.PositiveValue : 0; + + var disPow = ChargeStatus(regs[9]); // 0x110A: Battery power (offset 9) -- signed value + Status.BatteryDischargeWatts = disPow.IsDischarge ? disPow.PositiveValue : 0; + Status.BatteryChargeWatts = disPow.IsDischarge is false ? disPow.PositiveValue : 0; + + Status.OutputVoltage = regs[16] / 10.0; // 0x1111: AC output voltage (offset 0x1111 - 0x1101 = 16) + Status.GridVoltage = regs[22] / 10.0; // 0x1111: AC output voltage (offset 0x1117 - 0x1101 = 22) + Status.LoadWatts = regs[29]; // 0x111E: AC output active power (offset 0x111E - 0x1101 = 29) + Status.LoadPercentage = regs[31]; // 0x1120: Load percentage (offset 0x1120 - 0x1101 = 31) + Status.PVInputVoltage = regs[37] / 10.0; // 0x1126: PV input voltage (offset 0x1126 - 0x1101 = 37) + Status.PVInputWatt = regs[41]; // 0x112A: PV input power (offset 0x112A - 0x1101 = 41) -- signed value + + static (bool IsDischarge, short PositiveValue) ChargeStatus(short value) + { + var isNegative = value < 0; + var positiveValue = isNegative ? (short)-value : value; + + return (isNegative, positiveValue); + } } // The settings registers we need are located between 0x211F and 0x2159. @@ -206,6 +226,9 @@ public sealed class FelicitySolarInverter var response = SendModbusRequest(frame); + if (response.Length == 0) + return []; + // Expected response structure: // [Slave Address][Function Code][Byte Count][Data...][CRC Lo][CRC Hi] @@ -228,6 +251,9 @@ public sealed class FelicitySolarInverter { lock (_lock) //prevent concurrent access { + if (!_serialPort.IsOpen) + return []; + _serialPort.DiscardInBuffer(); _serialPort.DiscardOutBuffer(); diff --git a/src/Server/InverterService/StatusRetriever.cs b/src/Server/InverterService/StatusRetriever.cs index 1102994..7895d72 100644 --- a/src/Server/InverterService/StatusRetriever.cs +++ b/src/Server/InverterService/StatusRetriever.cs @@ -22,9 +22,11 @@ class StatusRetriever( await Task.Delay(5000); } + log.LogInformation("Connected to inverter at device address: [{port}]", port); + appLife.ApplicationStopping.Register(inverter.Close); - while (!c.IsCancellationRequested) + while (!c.IsCancellationRequested && !appLife.ApplicationStopping.IsCancellationRequested) { inverter.Status.BatteryCapacity = userSettings.BatteryCapacity; inverter.Status.PV_MaxCapacity = userSettings.PV_MaxCapacity; @@ -41,9 +43,16 @@ class StatusRetriever( continue; } - db.UpdateTodaysPvGeneration(c); + try + { + db.UpdateTodaysPvGeneration(c); + } + catch + { + //do nothing + } - await Task.Delay(2000); + await Task.Delay(2000, CancellationToken.None); } } } \ No newline at end of file diff --git a/src/Server/Persistance/Settings/UserSettings.cs b/src/Server/Persistance/Settings/UserSettings.cs index 3f8c0a7..3e1e09d 100644 --- a/src/Server/Persistance/Settings/UserSettings.cs +++ b/src/Server/Persistance/Settings/UserSettings.cs @@ -8,7 +8,7 @@ public class UserSettings public int Id { get; set; } = 1; public int PV_MaxCapacity { get; set; } = 1000; public int BatteryCapacity { get; set; } = 100; - public float BatteryNominalVoltage { get; set; } = 25.6f; + public double BatteryNominalVoltage { get; set; } = 25.6f; public int SunlightStartHour { get; set; } = 6; public int SunlightEndHour { get; set; } = 18; public int[] PVGraphRange => new[] { 0, (SunlightEndHour - SunlightStartHour) * 60 }; diff --git a/src/Server/Program.cs b/src/Server/Program.cs index 50a79d5..03e2b64 100644 --- a/src/Server/Program.cs +++ b/src/Server/Program.cs @@ -23,11 +23,12 @@ bld.Services .AddSingleton() .AddSingleton(); -if (!bld.Environment.IsDevelopment()) -{ - bld.Services - .AddHostedService(); -} +// if (!bld.Environment.IsDevelopment()) +// { +bld.Services + .AddHostedService(); + +// } bld.Services.AddFastEndpoints(o => o.SourceGeneratorDiscoveredTypes = DiscoveredTypes.All); diff --git a/src/Server/Properties/launchSettings.json b/src/Server/Properties/launchSettings.json index d4a1adf..a91018f 100644 --- a/src/Server/Properties/launchSettings.json +++ b/src/Server/Properties/launchSettings.json @@ -5,10 +5,10 @@ "dotnetRunMessages": true, "launchBrowser": true, "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", - "applicationUrl": "http://localhost:80;https://localhost:443", + "applicationUrl": "http://localhost:80", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } } } -} +} \ No newline at end of file diff --git a/src/Server/appsettings.json b/src/Server/appsettings.json index 3e4674a..0bd4571 100644 --- a/src/Server/appsettings.json +++ b/src/Server/appsettings.json @@ -1,15 +1,12 @@ { "LaunchSettings": { - "DeviceAddress": "/dev/ttyUSB1", + "DeviceAddress": "COM3", "JkBmsAddress": "/dev/ttyUSB0", "WebPort": 80 }, "Logging": { "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Debug", - "Microsoft.Hosting.Lifetime": "Debug", - "FastEndpoints.StartupTimer": "Debug" + "Default": "Information" } } } \ No newline at end of file diff --git a/src/Shared/Models/BMSStatus.cs b/src/Shared/Models/BMSStatus.cs index 9f23715..16f880a 100644 --- a/src/Shared/Models/BMSStatus.cs +++ b/src/Shared/Models/BMSStatus.cs @@ -65,7 +65,7 @@ public class BMSStatus public double AvgPowerWatts => Math.Round(AvgCurrentAmps * PackVoltage, 0, MidpointRounding.AwayFromZero); [JsonPropertyName("u")] - public float PackNominalVoltage { get; set; } + public double PackNominalVoltage { get; set; } public string GetTimeString() { diff --git a/src/Shared/Models/InverterSetting.cs b/src/Shared/Models/InverterSetting.cs index 18be280..04e8247 100644 --- a/src/Shared/Models/InverterSetting.cs +++ b/src/Shared/Models/InverterSetting.cs @@ -11,4 +11,15 @@ public enum Setting DischargeCutOff = 7, BackToGrid = 8, BackToBattery = 9 +} + +public enum WorkingMode +{ + POWER = 0, + STANDBY = 1, + BYPASS = 2, + BATTERY = 3, + FAULT = 4, + LINE = 5, + CHARGING = 6 } \ No newline at end of file diff --git a/src/Shared/Models/InverterStatus.cs b/src/Shared/Models/InverterStatus.cs index d35b429..b5c5d28 100644 --- a/src/Shared/Models/InverterStatus.cs +++ b/src/Shared/Models/InverterStatus.cs @@ -38,7 +38,7 @@ public class InverterStatus public double GridVoltage { get; set; } [JsonPropertyName("l")] - public int HeatSinkTemperature { get; set; } + public WorkingMode WorkingMode { get; set; } [JsonPropertyName("m")] public double LoadCurrent => LoadWatts == 0 ? 0 : LoadWatts / OutputVoltage; @@ -69,7 +69,7 @@ public class InverterStatus pvInputWatt = value; var interval = (DateTime.Now - pvInputWattHourLastComputed).TotalSeconds; - PVInputWattHour += value / (3600 / Convert.ToDouble(interval)); + PVInputWattHour += value / (3600 / interval); pvInputWattHourLastComputed = DateTime.Now; } } @@ -81,7 +81,7 @@ public class InverterStatus public int PV_MaxCapacity { get; set; } [JsonPropertyName("v")] - public int PVPotential => PVInputWatt > 0 ? Convert.ToInt32(Convert.ToDouble(PVInputWatt) / PV_MaxCapacity * 100) : 0; + public int PVPotential => PVInputWatt > 0 ? Convert.ToInt32(PVInputWatt / PV_MaxCapacity * 100) : 0; int pvInputWatt; DateTime pvInputWattHourLastComputed; diff --git a/src/Shared/Models/SystemSpec.cs b/src/Shared/Models/SystemSpec.cs index 276c326..6f72b10 100644 --- a/src/Shared/Models/SystemSpec.cs +++ b/src/Shared/Models/SystemSpec.cs @@ -4,7 +4,7 @@ public class SystemSpec { public int PV_MaxCapacity { get; set; } = 1000; public int BatteryCapacity { get; set; } = 100; - public float BatteryNominalVoltage { get; set; } = 25.6f; + public double BatteryNominalVoltage { get; set; } = 25.6f; public int SunlightStartHour { get; set; } = 6; public int SunlightEndHour { get; set; } = 18; } \ No newline at end of file