From 8b4d40f85ee65460b54d349315c02c1e39e4c6f8 Mon Sep 17 00:00:00 2001 From: atef Date: Wed, 12 Nov 2025 15:52:01 +0100 Subject: [PATCH 01/23] Update the aggregator path for PV. --- csharp/App/SodiStoreMax/src/AggregationService/Aggregator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/csharp/App/SodiStoreMax/src/AggregationService/Aggregator.cs b/csharp/App/SodiStoreMax/src/AggregationService/Aggregator.cs index f68d0a1c8..19bb623a5 100644 --- a/csharp/App/SodiStoreMax/src/AggregationService/Aggregator.cs +++ b/csharp/App/SodiStoreMax/src/AggregationService/Aggregator.cs @@ -137,9 +137,9 @@ public static class Aggregator { batterySoc.Add((double)jsonObject["Battery"]["Soc"]); } - if (jsonObject["PvOnDc"] != null && jsonObject["PvOnDc"]["DcWh"] != null) + if (jsonObject["PvOnDc"] != null && jsonObject["PvOnDc"]["1"]["DcWh"] != null) { - pvPowerSum.Add((double)jsonObject["PvOnDc"]["DcWh"]); + pvPowerSum.Add((double)jsonObject["PvOnDc"]["1"]["DcWh"]); } if (jsonObject["Battery"] != null && jsonObject["Battery"]["Power"] != null) { From c496f9b3982e6ccd1f228e5c7f44336f5a2b46b9 Mon Sep 17 00:00:00 2001 From: atef Date: Wed, 12 Nov 2025 15:52:29 +0100 Subject: [PATCH 02/23] Update the config file for Sinexcel project --- .../SinexcelCommunication/SystemConfig/Config.cs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/csharp/App/SinexcelCommunication/SystemConfig/Config.cs b/csharp/App/SinexcelCommunication/SystemConfig/Config.cs index db1d6d9c6..b13920d0b 100644 --- a/csharp/App/SinexcelCommunication/SystemConfig/Config.cs +++ b/csharp/App/SinexcelCommunication/SystemConfig/Config.cs @@ -1,5 +1,6 @@ using System.Diagnostics.CodeAnalysis; using System.Text.Json; +using InnovEnergy.Lib.Devices.Sinexcel_12K_TL.DataType; using InnovEnergy.Lib.Utils; using static System.Text.Json.JsonSerializer; @@ -17,16 +18,25 @@ public class Config public required Double MinSoc { get; set; } public required Double GridSetPoint { get; set; } - + public required Double MaximumDischargingCurrent { get; set; } + public required Double MaximumChargingCurrent { get; set; } + public required WorkingMode OperatingPriority { get; set; } + public required Int16 BatteriesCount { get; set; } + public required Double ModbusProtcolNumber { get; set; } + public required S3Config? S3 { get; set; } - private static String? LastSavedData { get; set; } public static Config Default => new() { MinSoc = 20, GridSetPoint = 0, + MaximumChargingCurrent = 180, + MaximumDischargingCurrent = 180, + OperatingPriority = WorkingMode.TimeChargeDischarge, + BatteriesCount = 0, + ModbusProtcolNumber = 1.2, S3 = new() { Bucket = "1-3e5b3069-214a-43ee-8d85-57d72000c19d", From 44f5841bd4a538253b9d8d6d4225aad350d336bb Mon Sep 17 00:00:00 2001 From: atef Date: Wed, 12 Nov 2025 15:52:53 +0100 Subject: [PATCH 03/23] Update the Configuration for RabbitMQ manager. for sinexcel project --- .../SinexcelCommunication/DataTypes/Configuration.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/csharp/App/SinexcelCommunication/DataTypes/Configuration.cs b/csharp/App/SinexcelCommunication/DataTypes/Configuration.cs index 24af7e5fc..705698450 100644 --- a/csharp/App/SinexcelCommunication/DataTypes/Configuration.cs +++ b/csharp/App/SinexcelCommunication/DataTypes/Configuration.cs @@ -1,12 +1,13 @@ +using InnovEnergy.Lib.Devices.Sinexcel_12K_TL.DataType; + namespace InnovEnergy.App.SinexcelCommunication.DataTypes; public class Configuration { public Double MinimumSoC { get; set; } - public Double GridSetPoint { get; set; } - // public Double MaximumDischargingCurrent { get; set; } - // public Double MaximumChargingCurrent { get; set; } - // public EssMode OperatingPriority { get; set; } - // public required Int16 BatteriesCount { get; set; } + public Double MaximumDischargingCurrent { get; set; } + public Double MaximumChargingCurrent { get; set; } + public WorkingMode OperatingPriority { get; set; } + public Int16 BatteriesCount { get; set; } } From 7d6bf1b9910461eb76a2fa1d2e99bb14e6690eaa Mon Sep 17 00:00:00 2001 From: atef Date: Wed, 12 Nov 2025 15:53:15 +0100 Subject: [PATCH 04/23] Update the configuration for rowatt project --- csharp/App/GrowattCommunication/DataTypes/Configuration.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/csharp/App/GrowattCommunication/DataTypes/Configuration.cs b/csharp/App/GrowattCommunication/DataTypes/Configuration.cs index ebda4ee93..c0222e448 100644 --- a/csharp/App/GrowattCommunication/DataTypes/Configuration.cs +++ b/csharp/App/GrowattCommunication/DataTypes/Configuration.cs @@ -6,11 +6,10 @@ namespace InnovEnergy.App.GrowattCommunication.DataTypes; public class Configuration { public Double MinimumSoC { get; set; } - public Double GridSetPoint { get; set; } public Double MaximumDischargingCurrent { get; set; } public Double MaximumChargingCurrent { get; set; } public EssMode OperatingPriority { get; set; } - public required Int16 BatteriesCount { get; set; } + public Int16 BatteriesCount { get; set; } } From abf1825457970176232aab22502b30629f0833ce Mon Sep 17 00:00:00 2001 From: atef Date: Wed, 12 Nov 2025 15:53:45 +0100 Subject: [PATCH 05/23] moved the data collector web app under APP --- csharp/DataCollectorWebApp/Controller.cs | 272 ------------------ .../DataCollectorWebApp.csproj | 14 - 2 files changed, 286 deletions(-) delete mode 100644 csharp/DataCollectorWebApp/Controller.cs delete mode 100644 csharp/DataCollectorWebApp/DataCollectorWebApp.csproj diff --git a/csharp/DataCollectorWebApp/Controller.cs b/csharp/DataCollectorWebApp/Controller.cs deleted file mode 100644 index c58c739fe..000000000 --- a/csharp/DataCollectorWebApp/Controller.cs +++ /dev/null @@ -1,272 +0,0 @@ -using System.Text; -using Microsoft.AspNetCore.Mvc; - -namespace DataCollectorWebApp; - -public class LoginResponse -{ - public string Token { get; set; } - public object User { get; set; } // or a User class if needed - public bool AccessToSalimax { get; set; } - public bool AccessToSalidomo { get; set; } - public bool AccessToSodiohome { get; set; } - public bool AccessToSodistoreMax { get; set; } -} - - -public class Installation -{ - //Each installation has 2 roles, a read role and a write role. - //There are 2 keys per role a public key and a secret - //Product can be 0 or 1, 0 for Salimax, 1 for Salidomo - public String Name { get; set; } - public String Location { get; set; } - public String Region { get; set; } = ""; - public String Country { get; set; } = ""; - public String VpnIp { get; set; } = ""; - public String InstallationName { get; set; } = ""; - - public String S3Region { get; set; } = "sos-ch-dk-2"; - public String S3Provider { get; set; } = "exo.io"; - public String S3WriteKey { get; set; } = ""; - public String S3Key { get; set; } = ""; - public String S3WriteSecret { get; set; } = ""; - public String S3Secret { get; set; } = ""; - public int S3BucketId { get; set; } = 0; - public String ReadRoleId { get; set; } = ""; - public String WriteRoleId { get; set; } = ""; - public Boolean TestingMode { get; set; } = false; - public int Status { get; set; } = -1; - public int Product { get; set; } = 0; - public int Device { get; set; } = 0; - public string SerialNumber { get; set; } = ""; - public string InverterSN { get; set; } = ""; - public string DataloggerSN { get; set; } = ""; - public String OrderNumbers { get; set; } - public String VrmLink { get; set; } = ""; -} - -[Controller] -public class InstallationsController : Controller -{ - - - -[HttpGet] -[Route("/Installations")] -[Produces("text/html")] -public async Task Index() -{ - const string HtmlHeader = @" - - - Inesco Energy Installations Overview - - - -

Installation Overview

- - - - - - - - - - - - -"; - - const string HtmlFooter = @" - -
NameProductLocationVPN IPStatus
- - -"; - - - - string GetProductName(int productId) - { - return productId switch - { - 0 => "Salimax", - 1 => "Salidomo", - 2 => "SodioHome", - 3 => "SodistoreMax", - }; - } - - string GetStatusHtml(int status) - { - if (status == -1) - { - return "×"; - } - - - var statusClass = $"status-{status}"; - var title = status switch - { - 0 => "Online", - 1 => "Warning", - 2 => "Error", - _ => "Unknown" - }; - - return $""; - } - - - string BuildRowHtml(Installation i) => $@" - - {i.Name} - {GetProductName(i.Product)} - {i.Location} - {i.VpnIp} - {GetStatusHtml(i.Status)} - -"; - - var installations = await FetchInstallationsFromApi(); - - var sb = new StringBuilder(); - sb.Append(HtmlHeader); - - foreach (var i in installations) - { - sb.Append(BuildRowHtml(i)); - } - - sb.Append(HtmlFooter); - return Content(sb.ToString(), "text/html"); -} - - - public async Task?> FetchInstallationsFromApi() - { - - var username = "baumgartner@innov.energy"; - var password = "1234"; - - using var http = new HttpClient { BaseAddress = new Uri("https://monitor.inesco.energy/api/") }; - - // Step 1: Login - var loginResponse = await http.PostAsync($"Login?username={username}&password={password}", null); - if (!loginResponse.IsSuccessStatusCode) - { - Console.WriteLine("Login failed with status code {StatusCode}", loginResponse.StatusCode); - return null; - } - - var loginData = await loginResponse.Content.ReadFromJsonAsync(); - if (loginData?.Token is null) - { - Console.WriteLine("Login succeeded but token was missing"); - return null; - } - - var token = loginData.Token; - Console.WriteLine($"Token: {token}"); - var installations = new List(); - - var getInstallationsRequestResponse = await http.GetAsync($"GetAllInstallationsFromProduct?product=0&authToken={token}"); - - var newInstallations= await getInstallationsRequestResponse.Content.ReadFromJsonAsync>(); - if (newInstallations != null) - { - installations.AddRange(newInstallations); - } - - getInstallationsRequestResponse = await http.GetAsync($"GetAllInstallationsFromProduct?product=1&authToken={token}"); - newInstallations= await getInstallationsRequestResponse.Content.ReadFromJsonAsync>(); - if (newInstallations != null) - { - installations.AddRange(newInstallations); - } - - getInstallationsRequestResponse = await http.GetAsync($"GetAllInstallationsFromProduct?product=2&authToken={token}"); - newInstallations= await getInstallationsRequestResponse.Content.ReadFromJsonAsync>(); - if (newInstallations != null) - { - installations.AddRange(newInstallations); - } - - getInstallationsRequestResponse = await http.GetAsync($"GetAllInstallationsFromProduct?product=3&authToken={token}"); - newInstallations= await getInstallationsRequestResponse.Content.ReadFromJsonAsync>(); - if (newInstallations != null) - { - installations.AddRange(newInstallations); - } - - - //Console.WriteLine("Installations retrieved ",installations); - return installations; - } - -} \ No newline at end of file diff --git a/csharp/DataCollectorWebApp/DataCollectorWebApp.csproj b/csharp/DataCollectorWebApp/DataCollectorWebApp.csproj deleted file mode 100644 index 3bde501d5..000000000 --- a/csharp/DataCollectorWebApp/DataCollectorWebApp.csproj +++ /dev/null @@ -1,14 +0,0 @@ - - - - net7.0 - enable - enable - - - - - - - - From 3f354ed2bbe1e7549ce607d62b78fa61fab77b7b Mon Sep 17 00:00:00 2001 From: atef Date: Wed, 12 Nov 2025 15:54:22 +0100 Subject: [PATCH 06/23] Update the message for of middleware agent for growatt project --- .../MiddlewareClasses/MiddlewareAgent.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/csharp/App/GrowattCommunication/MiddlewareClasses/MiddlewareAgent.cs b/csharp/App/GrowattCommunication/MiddlewareClasses/MiddlewareAgent.cs index e7d47b40f..c044bb1b8 100644 --- a/csharp/App/GrowattCommunication/MiddlewareClasses/MiddlewareAgent.cs +++ b/csharp/App/GrowattCommunication/MiddlewareClasses/MiddlewareAgent.cs @@ -9,9 +9,9 @@ namespace InnovEnergy.App.GrowattCommunication.MiddlewareClasses; public static class MiddlewareAgent { - private static UdpClient _udpListener = null!; + private static UdpClient _udpListener = null!; private static IPAddress? _controllerIpAddress; - private static EndPoint? _endPoint; + private static EndPoint? _endPoint; public static void InitializeCommunicationToMiddleware() { @@ -67,8 +67,8 @@ public static class MiddlewareAgent if (config != null) { - Console.WriteLine($"Received a configuration message: GridSetPoint is " + config.GridSetPoint + - ", MinimumSoC is " + config.MinimumSoC + " and operating priorty is " +config.OperatingPriority + "Number of batteries is " + config.BatteriesCount ); + Console.WriteLine($"Received a configuration message: " + + "MinimumSoC is " + config.MinimumSoC + " and operating priorty is " +config.OperatingPriority + "Number of batteries is " + config.BatteriesCount + config.MaximumChargingCurrent + config.MaximumDischargingCurrent); // Send the reply to the sender's endpoint _udpListener.Send(replyData, replyData.Length, serverEndpoint); From 92b9265a671bfe25ae99caf2807f1bf776d6efed Mon Sep 17 00:00:00 2001 From: atef Date: Wed, 12 Nov 2025 15:54:41 +0100 Subject: [PATCH 07/23] update the mideelwareagent message for sunexcel project --- .../MiddlewareClasses/MiddlewareAgent.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/csharp/App/SinexcelCommunication/MiddlewareClasses/MiddlewareAgent.cs b/csharp/App/SinexcelCommunication/MiddlewareClasses/MiddlewareAgent.cs index 4fa8a114a..f9faf5b43 100644 --- a/csharp/App/SinexcelCommunication/MiddlewareClasses/MiddlewareAgent.cs +++ b/csharp/App/SinexcelCommunication/MiddlewareClasses/MiddlewareAgent.cs @@ -67,8 +67,8 @@ public static class MiddlewareAgent if (config != null) { - Console.WriteLine($"Received a configuration message: GridSetPoint is " + config.GridSetPoint + - ", MinimumSoC is " + config.MinimumSoC + " and operating priorty is " ); + Console.WriteLine($"Received a configuration message: " + + "MinimumSoC is " + config.MinimumSoC + " and operating priorty is " +config.OperatingPriority + "Number of batteries is " + config.BatteriesCount + config.MaximumChargingCurrent + config.MaximumDischargingCurrent); // Send the reply to the sender's endpoint _udpListener.Send(replyData, replyData.Length, serverEndpoint); From ce62bb56875873822ab8cc67a59631ae8099ab4f Mon Sep 17 00:00:00 2001 From: atef Date: Wed, 12 Nov 2025 15:55:31 +0100 Subject: [PATCH 08/23] delete device state for sinexcel project. not needed --- csharp/App/SinexcelCommunication/DeviceState.cs | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 csharp/App/SinexcelCommunication/DeviceState.cs diff --git a/csharp/App/SinexcelCommunication/DeviceState.cs b/csharp/App/SinexcelCommunication/DeviceState.cs deleted file mode 100644 index 839b21bb8..000000000 --- a/csharp/App/SinexcelCommunication/DeviceState.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace InnovEnergy.App.SinexcelCommunication; - -public enum DeviceState -{ - Disabled, - Measured, - Computed -} \ No newline at end of file From b122177e61d57c59ea7d6ce5c2c8f7eb5a234941 Mon Sep 17 00:00:00 2001 From: atef Date: Wed, 12 Nov 2025 15:58:37 +0100 Subject: [PATCH 09/23] Update the growattACDC to inverter Record --- csharp/App/GrowattCommunication/Program.cs | 325 +++++++++++---------- 1 file changed, 165 insertions(+), 160 deletions(-) diff --git a/csharp/App/GrowattCommunication/Program.cs b/csharp/App/GrowattCommunication/Program.cs index 2b6692711..7292eb38d 100644 --- a/csharp/App/GrowattCommunication/Program.cs +++ b/csharp/App/GrowattCommunication/Program.cs @@ -96,8 +96,8 @@ public static class Program return new StatusRecord { - AcDcGrowatt = growattRecord, - Config = config // load from disk every iteration, so config can be changed while running + InverterRecord = growattRecord, + Config = config // load from disk every iteration, so config can be changed while running }; } @@ -133,38 +133,38 @@ public static class Program //await SaveModbusTcpFile(statusrecord); // save the JSON file for modbuscTCP Debug.Assert(statusrecord != null, nameof(statusrecord) + " != null"); - statusrecord.AcDcGrowatt.RemotePowerControl.WriteLine(" = RemotePowerControl"); - statusrecord.AcDcGrowatt.EnableEmsCommunicationFailureTime.WriteLine(" = EnableEmsCommunicationFailureTime"); + statusrecord.InverterRecord.RemotePowerControl.WriteLine(" = RemotePowerControl"); + statusrecord.InverterRecord.EnableEmsCommunicationFailureTime.WriteLine(" = EnableEmsCommunicationFailureTime"); - statusrecord.AcDcGrowatt.EnableCommand.WriteLine(" = EnableCommand"); - statusrecord.AcDcGrowatt.ControlPermession.WriteLine(" = ControlPermession"); - statusrecord.AcDcGrowatt.MeterPower.WriteLine(" MeterPower"); - statusrecord.AcDcGrowatt.ConsumptionPower.WriteLine(" ConsumptionPower"); - statusrecord.AcDcGrowatt.ExportedPowerToGridMeter.WriteLine(" ExportedPowerToGrid"); - statusrecord.AcDcGrowatt.ImportedPowerFromGrid.WriteLine(" ImportedPowerFromGrid"); - statusrecord.AcDcGrowatt.InverterActivePower.WriteLine(" InverterActivePower"); - statusrecord.AcDcGrowatt.BatteryChargeCutoffVoltage.WriteLine(" BatteryChargeCutoffVoltage "); //30409 we set power here - statusrecord.AcDcGrowatt.BatteryDischargeCutoffVoltage.WriteLine(" BatteryDishargeCutoffVoltage "); //30409 we set power here - statusrecord.AcDcGrowatt.PowerFactor.WriteLine(" = PowerFactor"); - statusrecord.AcDcGrowatt.Batteries[0].Soc.WriteLine(" SOC"); - statusrecord.AcDcGrowatt.Batteries[0].Power.WriteLine(" Battery Power"); - statusrecord.AcDcGrowatt.Batteries[0].Current.WriteLine(" Battery Current"); - statusrecord.AcDcGrowatt.Batteries[0].Voltage.WriteLine(" Battery Voltage"); - statusrecord.AcDcGrowatt.BatteryMaxChargeCurrent.WriteLine(" BatteryMaxChargeCurrent "); //30409 we set power here - statusrecord.AcDcGrowatt.BatteryMaxdischargeCurrent.WriteLine(" BatteryMaxDischargeCurrent "); //30409 we set power here - statusrecord.AcDcGrowatt.DischargeCutoffSoc.WriteLine(" DischargeCutoffSoc "); //30409 we set power here - statusrecord.AcDcGrowatt.ChargeCutoffSoc.WriteLine(" ChargeCutoffSoc "); //30409 we set power here + statusrecord.InverterRecord.EnableCommand.WriteLine(" = EnableCommand"); + statusrecord.InverterRecord.ControlPermession.WriteLine(" = ControlPermession"); + statusrecord.InverterRecord.GridPower.WriteLine(" MeterPower"); + statusrecord.InverterRecord.ConsumptionPower.WriteLine(" ConsumptionPower"); + statusrecord.InverterRecord.ExportedPowerToGridMeter.WriteLine(" ExportedPowerToGrid"); + statusrecord.InverterRecord.ImportedPowerFromGrid.WriteLine(" ImportedPowerFromGrid"); + statusrecord.InverterRecord.InverterActivePower.WriteLine(" InverterActivePower"); + statusrecord.InverterRecord.MaxSoc.WriteLine(" BatteryChargeCutoffVoltage "); //30409 we set power here + statusrecord.InverterRecord.MinSoc.WriteLine(" BatteryDishargeCutoffVoltage "); //30409 we set power here + statusrecord.InverterRecord.PowerFactor.WriteLine(" = PowerFactor"); + statusrecord.InverterRecord.Battery1Soc.WriteLine(" SOC"); + statusrecord.InverterRecord.Battery1Power.WriteLine(" Battery Power"); + statusrecord.InverterRecord.Battery1Current.WriteLine(" Battery Current"); + statusrecord.InverterRecord.Battery1Voltage.WriteLine(" Battery Voltage"); + statusrecord.InverterRecord.BatteryMaxChargingCurrent.WriteLine(" BatteryMaxChargeCurrent "); //30409 we set power here + statusrecord.InverterRecord.BatteryMaxDischargingCurrent.WriteLine(" BatteryMaxDischargeCurrent "); //30409 we set power here + statusrecord.InverterRecord.MinSoc.WriteLine(" MinSoc "); //30409 we set power here + statusrecord.InverterRecord.ChargeCutoffSocVoltage.WriteLine(" ChargeCutoffSoc "); //30409 we set power here - statusrecord.AcDcGrowatt.TotalPvPower.WriteLine(" Pv Power "); //30409 we set power here + statusrecord.InverterRecord.PvPower.WriteLine(" Pv Power "); //30409 we set power here - statusrecord.AcDcGrowatt.SystemOperatingMode.WriteLine(" = SystemOperatingMode"); - statusrecord.AcDcGrowatt.BatteryOperatingMode.WriteLine(" = BatteryOperatingMode"); - statusrecord.AcDcGrowatt.OperatingPriority.WriteLine(" = OperatingPriority"); // 30408 this the duration + statusrecord.InverterRecord.SystemOperatingMode.WriteLine(" = SystemOperatingMode"); + statusrecord.InverterRecord.BatteryOperatingMode.WriteLine(" = BatteryOperatingMode"); + statusrecord.InverterRecord.OperatingMode.WriteLine(" = OperatingPriority"); // 30408 this the duration - statusrecord.AcDcGrowatt.FaultMainCode.WriteLine(" = FaultMainCode"); // 30408 this the duration - statusrecord.AcDcGrowatt.FaultSubCode.WriteLine(" = FaultSubCode"); // 30408 this the duration - statusrecord.AcDcGrowatt.WarningMainCode.WriteLine(" = WarningMainCode"); // 30408 this the duration - statusrecord.AcDcGrowatt.WarningSubCode.WriteLine(" = WarningSubCode"); // 30408 this the duration + statusrecord.InverterRecord.FaultMainCode.WriteLine(" = FaultMainCode"); // 30408 this the duration + statusrecord.InverterRecord.FaultSubCode.WriteLine(" = FaultSubCode"); // 30408 this the duration + statusrecord.InverterRecord.WarningMainCode.WriteLine(" = WarningMainCode"); // 30408 this the duration + statusrecord.InverterRecord.WarningSubCode.WriteLine(" = WarningSubCode"); // 30408 this the duration @@ -173,10 +173,13 @@ public static class Program statusrecord.ApplyDefaultSettings(); Console.WriteLine( " ************************************ We are writing ************************************"); - statusrecord.Config.Save(); // save the config file - growattDeviceT415K.Write(statusrecord.AcDcGrowatt); - var stopTime = DateTime.Now; - Console.WriteLine(stopTime.ToString("HH:mm:ss.fff")); + if (statusrecord != null) + { + statusrecord.Config.Save(); // save the config file + growattDeviceT415K.Write(statusrecord.InverterRecord); + Console.WriteLine(DateTime.Now.ToString("HH:mm:ss.fff")); + + } return statusrecord; } catch (Exception e) @@ -185,7 +188,6 @@ public static class Program Console.WriteLine(e ); return null; } - } } @@ -195,7 +197,6 @@ public static class Program if (status == null) return; status.Config.MinSoc = config.MinimumSoC; - status.Config.GridSetPoint = config.GridSetPoint * 1000; // converted from kW to W status.Config.MaximumChargingCurrent = config.MaximumChargingCurrent; status.Config.MaximumDischargingCurrent = config.MaximumDischargingCurrent; status.Config.OperatingPriority = config.OperatingPriority; @@ -211,23 +212,23 @@ public static class Program case EssMode.Off: return "no mode"; case EssMode.GridPriority: - statusrecord.AcDcGrowatt.RemotePowerControl = true; - statusrecord.AcDcGrowatt.RemotePowerControlChargeDuration = 0; // 30408 this the duration + statusrecord.InverterRecord.RemotePowerControl = true; + statusrecord.InverterRecord.RemotePowerControlChargeDuration = 0; // 30408 this the duration //statusrecord.AcDcGrowatt.ActivePowerPercent = 50; // 30408 this the duration //statusrecord.AcDcGrowatt.ActivePowerPercentDerating = 50; // 30408 this the duration - statusrecord.AcDcGrowatt.RemoteChargDischargePower = - 100; //30409 we set power here // for grid priority from 0 to -100 - statusrecord.AcDcGrowatt.ActualChargeDischargePowerControlValue.WriteLine(" register 30474");; // this to check what was set + statusrecord.InverterRecord.RemoteChargDischargePower = - 100; //30409 we set power here // for grid priority from 0 to -100 + statusrecord.InverterRecord.ActualChargeDischargePowerControlValue.WriteLine(" register 30474");; // this to check what was set return "Grid priority mode active"; case EssMode.BatteryPriority: - statusrecord.AcDcGrowatt.RemotePowerControl = true; - statusrecord.AcDcGrowatt.RemotePowerControlChargeDuration = 0; // 30408 this the duration + statusrecord.InverterRecord.RemotePowerControl = true; + statusrecord.InverterRecord.RemotePowerControlChargeDuration = 0; // 30408 this the duration - statusrecord.AcDcGrowatt.RemoteChargDischargePower = 100; //30409 we set power here // for battery priority from 0 to 100 - statusrecord.AcDcGrowatt.ActualChargeDischargePowerControlValue.WriteLine(" register 30474");; // this to check what was set + statusrecord.InverterRecord.RemoteChargDischargePower = 100; //30409 we set power here // for battery priority from 0 to 100 + statusrecord.InverterRecord.ActualChargeDischargePowerControlValue.WriteLine(" register 30474");; // this to check what was set return "Battery priority mode active"; case EssMode.LoadPriority: - statusrecord.AcDcGrowatt.RemotePowerControl = false; + statusrecord.InverterRecord.RemotePowerControl = false; return "Load priority mode active"; default: throw new ArgumentOutOfRangeException(nameof(mode), mode, null); @@ -236,41 +237,41 @@ public static class Program private static StatusMessage GetSodiHomeStateAlarm(StatusRecord? record) { - var s3Bucket = Config.Load().S3?.Bucket; + var s3Bucket = Config.Load().S3?.Bucket; - var alarmList = new List(); - var warningList = new List(); + var alarmList = new List(); + var warningList = new List(); - if (record.AcDcGrowatt.SystemOperatingMode == GrowattSystemStatus.Fault) - { - if (record.AcDcGrowatt.FaultMainCode != 0) - { - alarmList.Add(new AlarmOrWarning - { - Date = DateTime.Now.ToString("yyyy-MM-dd"), - Time = DateTime.Now.ToString("HH:mm:ss"), - CreatedBy = "Growatt Inverter", - Description = record.AcDcGrowatt.WarningMainCode.ToString(), // to add the sub code - }); - } + if (record.InverterRecord.SystemOperatingMode == GrowattSystemStatus.Fault) + { + if (record.InverterRecord.FaultMainCode != 0) + { + alarmList.Add(new AlarmOrWarning + { + Date = DateTime.Now.ToString("yyyy-MM-dd"), + Time = DateTime.Now.ToString("HH:mm:ss"), + CreatedBy = "Growatt Inverter", + Description = record.InverterRecord.WarningMainCode.ToString(), // to add the sub code + }); + } - if (record.AcDcGrowatt.WarningMainCode != 0) - { - warningList.Add(new AlarmOrWarning - { - Date = DateTime.Now.ToString("yyyy-MM-dd"), - Time = DateTime.Now.ToString("HH:mm:ss"), - CreatedBy = "Growatt inverter", - Description = record.AcDcGrowatt.FaultMainCode.ToString(), //to add the sub code - }); - } - } - - _sodiohomeAlarmState = warningList.Any() + if (record.InverterRecord.WarningMainCode != 0) + { + warningList.Add(new AlarmOrWarning + { + Date = DateTime.Now.ToString("yyyy-MM-dd"), + Time = DateTime.Now.ToString("HH:mm:ss"), + CreatedBy = "Growatt inverter", + Description = record.InverterRecord.FaultMainCode.ToString(), //to add the sub code + }); + } + } + + _sodiohomeAlarmState = warningList.Any() ? SodistoreAlarmState.Orange : SodistoreAlarmState.Green; // this will be replaced by LedState - _sodiohomeAlarmState = alarmList.Any() + _sodiohomeAlarmState = alarmList.Any() ? SodistoreAlarmState.Red : _sodiohomeAlarmState; // this will be replaced by LedState @@ -329,22 +330,23 @@ public static class Program { if (st is null) return; - - st.AcDcGrowatt.BatteryMaxChargeCurrent = st.Config.MaximumChargingCurrent; - st.AcDcGrowatt.BatteryMaxdischargeCurrent = st.Config.MaximumDischargingCurrent ; - st.AcDcGrowatt.DischargeCutoffSoc = st.Config.MinSoc; - st.AcDcGrowatt.EmsCommunicationFailureTime = 20; // 20 sec - st.AcDcGrowatt.EnableEmsCommunicationFailureTime = false; - st.AcDcGrowatt.EnableCommand = true; - st.AcDcGrowatt.ControlPermession = true; - st.AcDcGrowatt.BatteryChargeCutoffVoltage = 60; //st.Config.BatteryChargeCutoffVoltage; - st.AcDcGrowatt.BatteryDischargeCutoffVoltage = 25; //st.Config.BatteryDischargeCutoffVoltage; - } - - private static Dictionary ConvertToModbusRegisters(Object value, String outputType, Int32 startingAddress) + + st.InverterRecord.BatteryMaxChargingCurrent = st.Config.MaximumChargingCurrent; + st.InverterRecord.BatteryMaxDischargingCurrent = st.Config.MaximumDischargingCurrent; + st.InverterRecord.MinSoc = (UInt16)st.Config.MinSoc; + st.InverterRecord.EmsCommunicationFailureTime = 20; // 20 sec + st.InverterRecord.EnableEmsCommunicationFailureTime = false; + st.InverterRecord.EnableCommand = true; + st.InverterRecord.ControlPermession = true; + st.InverterRecord.MaxSoc = 60; //st.Config.BatteryChargeCutoffVoltage; + st.InverterRecord.MinSoc = 25; //st.Config.BatteryDischargeCutoffVoltage; + } + + private static Dictionary ConvertToModbusRegisters(Object value, String outputType, + Int32 startingAddress) { var registers = new Dictionary(); - + switch (outputType) { case "UInt16": @@ -379,43 +381,45 @@ public static class Program private static async Task SaveModbusTcpFile(StatusRecord status) { var modbusData = new Dictionary(); - - // SYSTEM DATA - var result1 = ConvertToModbusRegisters((status.Config.ModbusProtcolNumber * 10), "UInt16", 30001); // this to be updated to modbusTCP version - var result2 = ConvertToModbusRegisters(status.AcDcGrowatt.SystemDateTime.ToUnixTime(), "UInt32", 30002); - var result3 = ConvertToModbusRegisters(status.AcDcGrowatt.SystemOperatingMode, "UInt16", 30004); - var result17 = ConvertToModbusRegisters(status.AcDcGrowatt.OperatingPriority, "UInt16", 30005); + + // SYSTEM DATA + var result1 = + ConvertToModbusRegisters((status.Config.ModbusProtcolNumber * 10), "UInt16", + 30001); // this to be updated to modbusTCP version + var result2 = ConvertToModbusRegisters(status.InverterRecord.SystemDateTime.ToUnixTime(), "UInt32", 30002); + var result3 = ConvertToModbusRegisters(status.InverterRecord.SystemOperatingMode, "UInt16", 30004); + var result17 = ConvertToModbusRegisters(status.InverterRecord.OperatingMode, "UInt16", 30005); // BATTERY SUMMARY (assuming single battery [0]) // this to be improved - var battery = status.AcDcGrowatt.BatteriesRecords!.Batteries[0]; - var result4 = ConvertToModbusRegisters((status.Config.BatteriesCount ), "UInt16", 31000); - var result8 = ConvertToModbusRegisters((status.AcDcGrowatt.BatteryOperatingMode), "UInt16", 31001); - var result12 = ConvertToModbusRegisters((battery.Voltage.Value * 10), "Int16", 31002); - var result13 = ConvertToModbusRegisters((battery.Current.Value * 10), "Int32", 31003); - var result14 = ConvertToModbusRegisters((battery.Soc.Value * 100), "UInt16", 31005); - var result5 = ConvertToModbusRegisters((battery.Power.Value * 10), "Int32", 31006); + var result4 = ConvertToModbusRegisters((status.Config.BatteriesCount), "UInt16", 31000); + var result8 = ConvertToModbusRegisters((status.InverterRecord.BatteryOperatingMode), "UInt16", 31001); + var result12 = ConvertToModbusRegisters((status.InverterRecord.Battery1Voltage.Value * 10), "Int16", 31002); + var result13 = ConvertToModbusRegisters((status.InverterRecord.Battery1Current.Value * 10), "Int32", 31003); + var result14 = ConvertToModbusRegisters((status.InverterRecord.Battery1Soc.Value * 100), "UInt16", 31005); + var result5 = ConvertToModbusRegisters((status.InverterRecord.Battery1Power.Value * 10), "Int32", 31006); - var result7 = ConvertToModbusRegisters((status.AcDcGrowatt.DischargeCutoffSoc * 100), "UInt16", 31008); - var result20 = ConvertToModbusRegisters((status.AcDcGrowatt.ChargeCutoffSoc * 100), "UInt16", 31009); - var result15 = ConvertToModbusRegisters((status.AcDcGrowatt.BatteriesRecords!.AverageSoh * 100), "UInt16", 310010); - var result16 = ConvertToModbusRegisters((battery.BatteryAmbientTemperature.Value * 100), "UInt16", 31011); - var result21 = ConvertToModbusRegisters((status.AcDcGrowatt.BatteryMaxChargeCurrent * 10), "UInt16", 31012); - var result22 = ConvertToModbusRegisters((status.AcDcGrowatt.BatteryMaxdischargeCurrent * 10), "UInt16", 31013); - var result23 = ConvertToModbusRegisters((status.AcDcGrowatt.BatteryChargeCutoffVoltage * 10), "UInt16", 31014); + var result7 = ConvertToModbusRegisters((status.InverterRecord.MinSoc * 100), "UInt16", 31008); + var result20 = ConvertToModbusRegisters((status.InverterRecord.ChargeCutoffSocVoltage * 100), "UInt16", 31009); + var result15 = + ConvertToModbusRegisters((status.InverterRecord.Battery1Soh * 100), "UInt16", 310010); + var result16 = ConvertToModbusRegisters((status.InverterRecord.Battery1AmbientTemperature.Value * 100), "UInt16", 31011); + var result21 = ConvertToModbusRegisters((status.InverterRecord.BatteryMaxChargingCurrent * 10), "UInt16", 31012); + var result22 = ConvertToModbusRegisters((status.InverterRecord.BatteryMaxDischargingCurrent * 10), "UInt16", 31013); + var result23 = ConvertToModbusRegisters((status.InverterRecord.MaxSoc * 10), "UInt16", 31014); + + var result18 = ConvertToModbusRegisters((status.InverterRecord.PvPower.Value * 10), "UInt32", 32000); + var result19 = ConvertToModbusRegisters((status.InverterRecord.GridPower * 10), "Int32", 33000); - var result18 = ConvertToModbusRegisters((status.AcDcGrowatt.TotalPvPower.Value * 10), "UInt32", 32000); - var result19 = ConvertToModbusRegisters((status.AcDcGrowatt.MeterPower * 10), "Int32", 33000); - // Merge all results into one dictionary var allResults = new[] { - result1,result2, result3, result17, result4, result5, result7, result8, - result12, result13, result14, result15, result16, result18, result19,result20, + result1, result2, result3, result17, result4, result5, result7, result8, + result12, result13, result14, result15, result16, result18, result19, result20, result21, result22, result23 }; - + foreach (var result in allResults) { foreach (var entry in result) @@ -548,67 +552,68 @@ public static class Program private static async Task ConcatinatingAndCompressingFiles(Int64 timeStamp, S3Config s3Config) { - if (_fileCounter >= NbrOfFileToConcatenate) - { - _fileCounter = 0; + if (_fileCounter >= NbrOfFileToConcatenate) + { + _fileCounter = 0; - var logFileConcatenator = new LogFileConcatenator(); - var jsontoSend = logFileConcatenator.ConcatenateFiles(NbrOfFileToConcatenate); + var logFileConcatenator = new LogFileConcatenator(); + var jsontoSend = logFileConcatenator.ConcatenateFiles(NbrOfFileToConcatenate); - var fileNameWithoutExtension = timeStamp.ToString(); // used for both S3 and local - var s3Path = fileNameWithoutExtension + ".json"; + var fileNameWithoutExtension = timeStamp.ToString(); // used for both S3 and local + var s3Path = fileNameWithoutExtension + ".json"; - var request = s3Config.CreatePutRequest(s3Path); + var request = s3Config.CreatePutRequest(s3Path); - var compressedBytes = CompresseBytes(jsontoSend); - var base64String = Convert.ToBase64String(compressedBytes); - var stringContent = new StringContent(base64String, Encoding.UTF8, "application/base64"); + var compressedBytes = CompresseBytes(jsontoSend); + var base64String = Convert.ToBase64String(compressedBytes); + var stringContent = new StringContent(base64String, Encoding.UTF8, "application/base64"); - var uploadSucceeded = false; + var uploadSucceeded = false; - try - { - var response = await request.PutAsync(stringContent); + try + { + var response = await request.PutAsync(stringContent); - if (response.StatusCode != 200) - { - Console.WriteLine("ERROR: PUT"); - var error = await response.GetStringAsync(); - Console.WriteLine(error); + if (response.StatusCode != 200) + { + Console.WriteLine("ERROR: PUT"); + var error = await response.GetStringAsync(); + Console.WriteLine(error); - await SaveToLocalCompressedFallback(compressedBytes, fileNameWithoutExtension); - Heartbit(); - return false; - } + await SaveToLocalCompressedFallback(compressedBytes, fileNameWithoutExtension); + Heartbit(); + return false; + } - uploadSucceeded = true; - Console.WriteLine("✅ File uploaded to S3 successfully."); + uploadSucceeded = true; + Console.WriteLine("✅ File uploaded to S3 successfully."); - Console.WriteLine("---------------------------------------- Resending FailedUploadedFiles----------------------------------------"); - Heartbit(); + Console.WriteLine( + "---------------------------------------- Resending FailedUploadedFiles----------------------------------------"); + Heartbit(); - await ResendLocalFailedFilesAsync(s3Config); // retry any pending failed files - } - catch (Exception ex) - { - Console.WriteLine("Upload exception: " + ex.Message); + await ResendLocalFailedFilesAsync(s3Config); // retry any pending failed files + } + catch (Exception ex) + { + Console.WriteLine("Upload exception: " + ex.Message); - if (!uploadSucceeded) - { - await SaveToLocalCompressedFallback(compressedBytes, fileNameWithoutExtension); - } + if (!uploadSucceeded) + { + await SaveToLocalCompressedFallback(compressedBytes, fileNameWithoutExtension); + } - Heartbit(); - return false; - } - } + Heartbit(); + return false; + } + } - _fileCounter++; - return true; + _fileCounter++; + return true; } - + private static void Heartbit() - { + { var s3Bucket = Config.Load().S3?.Bucket; var tryParse = int.TryParse(s3Bucket?.Split("-")[0], out var installationId); @@ -684,7 +689,7 @@ public static class Program } } } - + private static Byte[] CompresseBytes(String jsonToSend) { //Compress JSON data to a byte array From 231069768ee6b88843056c9e4c9148fd5669fcda Mon Sep 17 00:00:00 2001 From: atef Date: Wed, 12 Nov 2025 15:59:12 +0100 Subject: [PATCH 10/23] Naming Sinexcel inverter to recordinverter --- csharp/App/SinexcelCommunication/Program.cs | 127 +++++++++++--------- 1 file changed, 72 insertions(+), 55 deletions(-) diff --git a/csharp/App/SinexcelCommunication/Program.cs b/csharp/App/SinexcelCommunication/Program.cs index 483b6a95b..3bfb7cca0 100644 --- a/csharp/App/SinexcelCommunication/Program.cs +++ b/csharp/App/SinexcelCommunication/Program.cs @@ -31,7 +31,7 @@ namespace InnovEnergy.App.SinexcelCommunication; [SuppressMessage("Trimming", "IL2026:Members annotated with \'RequiresUnreferencedCodeAttribute\' require dynamic access otherwise can break functionality when trimming application code")] internal static class Program { - private static readonly TimeSpan UpdateInterval = TimeSpan.FromSeconds(4); + private static readonly TimeSpan UpdateInterval = TimeSpan.FromSeconds(5); private const UInt16 NbrOfFileToConcatenate = 15; // add this to config file private static UInt16 _fileCounter = 0; private static Channel _sinexcelChannel; @@ -54,7 +54,7 @@ internal static class Program public static async Task Main(String[] args) { _sinexcelChannel = new SerialPortChannel(Port, BaudRate, Parity, DataBits, StopBits); - + InitializeCommunicationToMiddleware(); while (true) { try @@ -85,7 +85,7 @@ internal static class Program return new StatusRecord { - SinexcelRecord = sinexcelRecord, + InverterRecord = sinexcelRecord, Config = config // load from disk every iteration, so config can be changed while running }; } @@ -111,61 +111,22 @@ internal static class Program var startTime = DateTime.Now; Console.WriteLine("***************************** Reading Battery Data *********************************************"); - Console.WriteLine(startTime.ToString("HH:mm:ss.fff")); + Console.WriteLine(startTime.ToString("HH:mm:ss.fff ")+ "Start Reading"); // the order matter of the next three lines var statusrecord = ReadStatus(); if (statusrecord == null) return null; - - statusrecord.SinexcelRecord.GridAPhaseVoltage.WriteLine(" = Grid A PhaseVoltage"); - statusrecord.SinexcelRecord.GridBPhaseVoltage.WriteLine(" = Grid B PhaseVoltage"); - statusrecord.SinexcelRecord.GridCPhaseVoltage.WriteLine(" = Grid C PhaseVoltage"); + SendSalimaxStateAlarm(GetSodiHomeStateAlarm(statusrecord),statusrecord); - statusrecord.SinexcelRecord.GridAPhaseCurrent.WriteLine(" = Grid A PhaseCurrent"); - statusrecord.SinexcelRecord.GridBPhaseCurrent.WriteLine(" = Grid B PhaseCurrent"); - statusrecord.SinexcelRecord.GridCPhaseCurrent.WriteLine(" = Grid C PhaseCurrent"); - - - statusrecord.SinexcelRecord.GridAPhaseActivePower.WriteLine(" = A Grid Active Power"); - statusrecord.SinexcelRecord.GridBPhaseActivePower.WriteLine(" = B Grid Active Power"); - statusrecord.SinexcelRecord.GridCPhaseActivePower.WriteLine(" = C Grid Active Power"); - - statusrecord.SinexcelRecord.GridVoltageFrequency.WriteLine(" = Frequency"); - - statusrecord.SinexcelRecord.ElectricMeterCPhaseActivePower.WriteLine(" Meter Phase C Power"); - statusrecord.SinexcelRecord.ElectricMeterBPhaseActivePower.WriteLine(" Meter Phase B Power"); - statusrecord.SinexcelRecord.ElectricMeterAPhaseActivePower.WriteLine(" Meter Phase A Power"); - - statusrecord.SinexcelRecord.ElectricMeterAPhaseCurrent.WriteLine(" Meter Phase C Current "); - statusrecord.SinexcelRecord.ElectricMeterBPhaseCurrent.WriteLine(" Meter Phase B Current "); - statusrecord.SinexcelRecord.ElectricMeterBPhaseCurrent.WriteLine(" Meter Phase A Current "); - statusrecord.SinexcelRecord.WorkingMode.WriteLine(" workingmode"); - - statusrecord.SinexcelRecord.BatteryVoltage.WriteLine(" BatteryVoltage"); - statusrecord.SinexcelRecord.BatteryVoltage1.WriteLine(" BatteryVoltage1"); - statusrecord.SinexcelRecord.BatteryVoltage2.WriteLine(" BatteryVoltage2"); - - statusrecord.SinexcelRecord.BatteryPower1.WriteLine(" BatteryPower1"); - statusrecord.SinexcelRecord.BatteryPower2.WriteLine(" BatteryPower2"); - statusrecord.SinexcelRecord.TotalBatteryPower.WriteLine(" TotalBatteryPower"); - - statusrecord.SinexcelRecord.EnableBattery1.WriteLine(" EnableBattery1"); - statusrecord.SinexcelRecord.EnableBattery2.WriteLine(" EnableBattery2"); - - statusrecord.SinexcelRecord.MaxChargingCurrentBattery1.WriteLine(" MaxChargingCurrentBattery1"); - statusrecord.SinexcelRecord.MaxChargingCurrentBattery2.WriteLine(" MaxChargingCurrentBattery2"); - - statusrecord.SinexcelRecord.MaxDischargingCurrentBattery1.WriteLine(" MaxChargingCurrentBattery1"); - statusrecord.SinexcelRecord.MaxDischargingCurrentBattery2.WriteLine(" MaxChargingCurrentBattery2"); - - //EssModeControl(statusrecord); - // statusrecord.ApplyDefaultSettings(); Console.WriteLine( " ************************************ We are writing ************************************"); + var startWritingTime = DateTime.Now; + Console.WriteLine(startWritingTime.ToString("HH:mm:ss.fff ") +"start Writing"); statusrecord?.Config.Save(); // save the config file - //sinexcelDevice.Write(statusrecord?.SinexcelRecord); + //if (statusrecord?.InverterRecord != null) sinexcelDevice.Write(statusrecord.InverterRecord); + var stopTime = DateTime.Now; - Console.WriteLine(stopTime.ToString("HH:mm:ss.fff")); + Console.WriteLine(stopTime.ToString("HH:mm:ss.fff ")+ "run iteration time finish"); return statusrecord; } catch (Exception e) @@ -178,6 +139,61 @@ internal static class Program } } + private static StatusMessage GetSodiHomeStateAlarm(StatusRecord? record) + { + var s3Bucket = Config.Load().S3?.Bucket; + + var alarmList = new List(); + var warningList = new List(); +/* + if (record.SinexcelRecord.WorkingMode == GrowattSystemStatus.Fault) + { + if (record.AcDcGrowatt.FaultMainCode != 0) + { + alarmList.Add(new AlarmOrWarning + { + Date = DateTime.Now.ToString("yyyy-MM-dd"), + Time = DateTime.Now.ToString("HH:mm:ss"), + CreatedBy = "Growatt Inverter", + Description = record.AcDcGrowatt.WarningMainCode.ToString(), // to add the sub code + }); + } + + if (record.AcDcGrowatt.WarningMainCode != 0) + { + warningList.Add(new AlarmOrWarning + { + Date = DateTime.Now.ToString("yyyy-MM-dd"), + Time = DateTime.Now.ToString("HH:mm:ss"), + CreatedBy = "Growatt inverter", + Description = record.AcDcGrowatt.FaultMainCode.ToString(), //to add the sub code + }); + } + }*/ + + _sodiohomeAlarmState = warningList.Any() + ? SodistoreAlarmState.Orange + : SodistoreAlarmState.Green; // this will be replaced by LedState + + _sodiohomeAlarmState = alarmList.Any() + ? SodistoreAlarmState.Red + : _sodiohomeAlarmState; // this will be replaced by LedState + + var installationId = GetInstallationId(s3Bucket ?? string.Empty); + + var returnedStatus = new StatusMessage + { + InstallationId = installationId, + Product = 2, + Status = _sodiohomeAlarmState, + Type = MessageType.AlarmOrWarning, + Alarms = alarmList, + Warnings = warningList + }; + + return returnedStatus; + } + private static Int32 GetInstallationId(String s3Bucket) { var part = s3Bucket.Split('-').FirstOrDefault(); @@ -220,11 +236,10 @@ internal static class Program if (status == null) return; status.Config.MinSoc = config.MinimumSoC; - status.Config.GridSetPoint = config.GridSetPoint * 1000; // converted from kW to W - //status.Config.MaximumChargingCurrent = config.MaximumChargingCurrent; - //status.Config.MaximumDischargingCurrent = config.MaximumDischargingCurrent; - //status.Config.OperatingPriority = config.OperatingPriority; - //status.Config.BatteriesCount = config.BatteriesCount; + status.Config.MaximumChargingCurrent = config.MaximumChargingCurrent; + status.Config.MaximumDischargingCurrent = config.MaximumDischargingCurrent; + status.Config.OperatingPriority = config.OperatingPriority; + status.Config.BatteriesCount = config.BatteriesCount; } private static async Task SaveModbusTcpFile(StatusRecord status) @@ -233,7 +248,7 @@ internal static class Program // SYSTEM DATA var result1 = ConvertToModbusRegisters((status.Config.MinSoc * 10), "UInt16", 30001); // this to be updated to modbusTCP version - var result2 = ConvertToModbusRegisters(status.SinexcelRecord.GridAPhaseVoltage, "UInt32", 30002); + var result2 = ConvertToModbusRegisters(status.InverterRecord.GridAPhaseVoltage, "UInt32", 30002); // Merge all results into one dictionary var allResults = new[] @@ -254,6 +269,8 @@ internal static class Program //Console.WriteLine("JSON file written successfully."); //Console.WriteLine(json); + var stopTime = DateTime.Now; + Console.WriteLine(stopTime.ToString("HH:mm:ss.fff" )+ " Finish the loop"); return true; } From c94d9ecf31e4be41d4a3a2e62d2097af996a9980 Mon Sep 17 00:00:00 2001 From: atef Date: Wed, 12 Nov 2025 15:59:58 +0100 Subject: [PATCH 11/23] move the Data collector web app under APP folder --- csharp/DataCollectorWebApp/Program.cs | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 csharp/DataCollectorWebApp/Program.cs diff --git a/csharp/DataCollectorWebApp/Program.cs b/csharp/DataCollectorWebApp/Program.cs deleted file mode 100644 index 6420e3bf6..000000000 --- a/csharp/DataCollectorWebApp/Program.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Microsoft.AspNetCore.Builder; - -namespace InnovEnergy.App.DataCollectorWebApp; - -public static class Program -{ - public static async Task Main(string[] args) - { - Console.WriteLine("Starting DataCollectorWebApp"); - - var builder = WebApplication.CreateBuilder(args); - - builder.Services.AddControllers(); - var app = builder.Build(); - - app.MapControllers(); - await app.RunAsync(); - } -} \ No newline at end of file From f40f0be0565b00055b9ba0dc611aa427b766b264 Mon Sep 17 00:00:00 2001 From: atef Date: Wed, 12 Nov 2025 16:00:26 +0100 Subject: [PATCH 12/23] Delete salimax device not needed --- csharp/App/SinexcelCommunication/SalimaxDevice.cs | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 csharp/App/SinexcelCommunication/SalimaxDevice.cs diff --git a/csharp/App/SinexcelCommunication/SalimaxDevice.cs b/csharp/App/SinexcelCommunication/SalimaxDevice.cs deleted file mode 100644 index f019ab5e6..000000000 --- a/csharp/App/SinexcelCommunication/SalimaxDevice.cs +++ /dev/null @@ -1,13 +0,0 @@ -using InnovEnergy.Lib.Utils.Net; - -namespace InnovEnergy.App.SinexcelCommunication; - -public class SalimaxDevice : Ip4Address -{ - public required DeviceState DeviceState { get; init; } - - public override String ToString() => $"{base.ToString()} ({DeviceState})"; - - public override int GetHashCode() => HashCode.Combine(base.GetHashCode(), DeviceState); - -} \ No newline at end of file From ed5609689b28e94bf77398af6517b96e8c8e720d Mon Sep 17 00:00:00 2001 From: atef Date: Wed, 12 Nov 2025 16:07:47 +0100 Subject: [PATCH 13/23] Update the sinexcel recor api with new reading writing attribute --- csharp/Sinexcel 12K TL/SinexcelRecord.Api.cs | 267 +++++++++++++------ 1 file changed, 183 insertions(+), 84 deletions(-) diff --git a/csharp/Sinexcel 12K TL/SinexcelRecord.Api.cs b/csharp/Sinexcel 12K TL/SinexcelRecord.Api.cs index 7d6909d9e..50905a5b7 100644 --- a/csharp/Sinexcel 12K TL/SinexcelRecord.Api.cs +++ b/csharp/Sinexcel 12K TL/SinexcelRecord.Api.cs @@ -1,5 +1,3 @@ -using System.Diagnostics.CodeAnalysis; -using System.Globalization; using InnovEnergy.Lib.Devices.Sinexcel_12K_TL.DataType; using InnovEnergy.Lib.Units; using InnovEnergy.Lib.Units.Power; @@ -9,6 +7,7 @@ namespace InnovEnergy.Lib.Devices.Sinexcel_12K_TL; public partial class SinexcelRecord { + private Int16 _factorFromKwtoW = 1000; // ─────────────────────────────────────────────── // Public API — Decoded Float Values // ─────────────────────────────────────────────── @@ -30,17 +29,17 @@ public partial class SinexcelRecord public Current GridCPhaseCurrent => UIntToFloat(_grid_C_Phase_Current); public Current GridNWireCurrent => UIntToFloat(_grid_N_Wire_Current); - public ApparentPower GridAPhaseApparentPower => UIntToFloat(_gridAPhaseApparentPower); - public ApparentPower GridBPhaseApparentPower => UIntToFloat(_gridBPhaseApparentPower); - public ApparentPower GridCPhaseApparentPower => UIntToFloat(_gridCPhaseApparentPower); + public ApparentPower GridAPhaseApparentPower => UIntToFloat(_gridAPhaseApparentPower) * _factorFromKwtoW; + public ApparentPower GridBPhaseApparentPower => UIntToFloat(_gridBPhaseApparentPower) * _factorFromKwtoW; + public ApparentPower GridCPhaseApparentPower => UIntToFloat(_gridCPhaseApparentPower) * _factorFromKwtoW; - public ActivePower GridAPhaseActivePower => UIntToFloat(_gridAPhaseActivePower); - public ActivePower GridBPhaseActivePower => UIntToFloat(_gridBPhaseActivePower); - public ActivePower GridCPhaseActivePower => UIntToFloat(_gridCPhaseActivePower); + public ActivePower GridAPhaseActivePower => UIntToFloat(_gridAPhaseActivePower) * _factorFromKwtoW; + public ActivePower GridBPhaseActivePower => UIntToFloat(_gridBPhaseActivePower) * _factorFromKwtoW; + public ActivePower GridCPhaseActivePower => UIntToFloat(_gridCPhaseActivePower) * _factorFromKwtoW; - public ReactivePower GridAPhaseReactivePower => UIntToFloat(_gridAPhaseReactivePower); - public ReactivePower GridBPhaseReactivePower => UIntToFloat(_gridBPhaseReactivePower); - public ReactivePower GridCPhaseReactivePower => UIntToFloat(_gridCPhaseReactivePower); + public ReactivePower GridAPhaseReactivePower => UIntToFloat(_gridAPhaseReactivePower) * _factorFromKwtoW; + public ReactivePower GridBPhaseReactivePower => UIntToFloat(_gridBPhaseReactivePower) * _factorFromKwtoW; + public ReactivePower GridCPhaseReactivePower => UIntToFloat(_gridCPhaseReactivePower) * _factorFromKwtoW; // ─────────────────────────────────────────────── // Load Measurements @@ -60,17 +59,19 @@ public partial class SinexcelRecord public Current LoadCPhaseCurrent => UIntToFloat(_loadCPhaseCurrent); public Current LoadNWireCurrent => UIntToFloat(_loadNWireCurrent); - public ApparentPower LoadAPhaseApparentPower => UIntToFloat(_loadAPhaseApparentPower); - public ApparentPower LoadBPhaseApparentPower => UIntToFloat(_loadBPhaseApparentPower); - public ApparentPower LoadCPhaseApparentPower => UIntToFloat(_loadCPhaseApparentPower); + public ApparentPower LoadAPhaseApparentPower => UIntToFloat(_loadAPhaseApparentPower) * _factorFromKwtoW; + public ApparentPower LoadBPhaseApparentPower => UIntToFloat(_loadBPhaseApparentPower) * _factorFromKwtoW; + public ApparentPower LoadCPhaseApparentPower => UIntToFloat(_loadCPhaseApparentPower) * _factorFromKwtoW; - public ActivePower LoadAPhaseActivePower => UIntToFloat(_loadAPhaseActivePower); - public ActivePower LoadBPhaseActivePower => UIntToFloat(_loadBPhaseActivePower); - public ActivePower LoadCPhaseActivePower => UIntToFloat(_loadCPhaseActivePower); + public ActivePower LoadAPhaseActivePower => UIntToFloat(_loadAPhaseActivePower) * _factorFromKwtoW; + public ActivePower LoadBPhaseActivePower => UIntToFloat(_loadBPhaseActivePower) * _factorFromKwtoW; + public ActivePower LoadCPhaseActivePower => UIntToFloat(_loadCPhaseActivePower) * _factorFromKwtoW; + + public ActivePower ConsumptionPower => TotalLoadPower; - public ReactivePower LoadAPhaseReactivePower => UIntToFloat(_loadAPhaseReactivePower); - public ReactivePower LoadBPhaseReactivePower => UIntToFloat(_loadBPhaseReactivePower); - public ReactivePower LoadCPhaseReactivePower => UIntToFloat(_loadCPhaseReactivePower); + public ReactivePower LoadAPhaseReactivePower => UIntToFloat(_loadAPhaseReactivePower) * _factorFromKwtoW; + public ReactivePower LoadBPhaseReactivePower => UIntToFloat(_loadBPhaseReactivePower) * _factorFromKwtoW; + public ReactivePower LoadCPhaseReactivePower => UIntToFloat(_loadCPhaseReactivePower) * _factorFromKwtoW; // ─────────────────────────────────────────────── // Inverter Measurements @@ -83,10 +84,12 @@ public partial class SinexcelRecord public Voltage InverterBcWireVoltage => UIntToFloat(_inverterBCWireVoltage); public Voltage InverterCaWireVoltage => UIntToFloat(_inverterCAWireVoltage); - public ActivePower InverterAPhaseActivePower => UIntToFloat(_inverterAPhaseActivePower); - public ActivePower InverterBPhaseActivePower => UIntToFloat(_inverterBPhaseActivePower); - public ActivePower InverterCPhaseActivePower => UIntToFloat(_inverterCPhaseActivePower); + public ActivePower InverterAPhaseActivePower => UIntToFloat(_inverterAPhaseActivePower) * _factorFromKwtoW; + public ActivePower InverterBPhaseActivePower => UIntToFloat(_inverterBPhaseActivePower) * _factorFromKwtoW; + public ActivePower InverterCPhaseActivePower => UIntToFloat(_inverterCPhaseActivePower) * _factorFromKwtoW; + public ActivePower InverterActivePower => InverterAPhaseActivePower + InverterBPhaseActivePower + InverterCPhaseActivePower; + // ─────────────────────────────────────────────── // DC/AC Temperature Sensors (°C) // ─────────────────────────────────────────────── @@ -99,22 +102,24 @@ public partial class SinexcelRecord // ─────────────────────────────────────────────── // Date / Time Information // ─────────────────────────────────────────────── - public Single Year => UIntToFloat(_year); - public Single Month => UIntToFloat(_month); - public Single Day => UIntToFloat(_day); - public Single Hour => UIntToFloat(_hour); - public Single Minute => UIntToFloat(_minute); - public Single Second => UIntToFloat(_second); - + public UInt16 Year => (UInt16) ConvertBitPatternToFloat(_year); + public UInt16 Month => (UInt16) ConvertBitPatternToFloat(_month); + public UInt16 Day => (UInt16) ConvertBitPatternToFloat(_day); + public UInt16 Hour => (UInt16) ConvertBitPatternToFloat(_hour); + public UInt16 Minute => (UInt16) ConvertBitPatternToFloat(_minute); + public UInt16 Second => (UInt16) ConvertBitPatternToFloat(_second); + + public DateTime SystemDateTime => new(Year, Month, Day, Hour, Minute, Second); + // ─────────────────────────────────────────────── // Diesel Generator Measurements // ─────────────────────────────────────────────── public Voltage DieselGenAPhaseVoltage => ConvertBitPatternToFloat(_dieselGenAPhaseVoltage); public Voltage DieselGenBPhaseVoltage => ConvertBitPatternToFloat(_dieselGenBPhaseVoltage); public Voltage DieselGenCPhaseVoltage => ConvertBitPatternToFloat(_dieselGenCPhaseVoltage); - public Voltage DieselGenABWireVoltage => ConvertBitPatternToFloat(_dieselGenABWireVoltage); - public Voltage DieselGenBCWireVoltage => ConvertBitPatternToFloat(_dieselGenBCWireVoltage); - public Voltage DieselGenCAWireVoltage => ConvertBitPatternToFloat(_dieselGenCAWireVoltage); + public Voltage DieselGenAbWireVoltage => ConvertBitPatternToFloat(_dieselGenABWireVoltage); + public Voltage DieselGenBcWireVoltage => ConvertBitPatternToFloat(_dieselGenBCWireVoltage); + public Voltage DieselGenCaWireVoltage => ConvertBitPatternToFloat(_dieselGenCAWireVoltage); public Frequency DieselGenVoltageFrequency => ConvertBitPatternToFloat(_dieselGenVoltageFrequency); @@ -123,38 +128,39 @@ public partial class SinexcelRecord public Current DieselGenCPhaseCurrent => ConvertBitPatternToFloat(_dieselGenCPhaseCurrent); public Current DieselGenNWireCurrent => ConvertBitPatternToFloat(_dieselGenNWireCurrent); - public ApparentPower DieselGenAPhaseApparentPower => ConvertBitPatternToFloat(_dieselGenAPhaseApparentPower); - public ApparentPower DieselGenBPhaseApparentPower => ConvertBitPatternToFloat(_dieselGenBPhaseApparentPower); - public ApparentPower DieselGenCPhaseApparentPower => ConvertBitPatternToFloat(_dieselGenCPhaseApparentPower); + public ApparentPower DieselGenAPhaseApparentPower => ConvertBitPatternToFloat(_dieselGenAPhaseApparentPower) * _factorFromKwtoW; + public ApparentPower DieselGenBPhaseApparentPower => ConvertBitPatternToFloat(_dieselGenBPhaseApparentPower) * _factorFromKwtoW; + public ApparentPower DieselGenCPhaseApparentPower => ConvertBitPatternToFloat(_dieselGenCPhaseApparentPower) * _factorFromKwtoW; - public ActivePower DieselGenAPhaseActivePower => ConvertBitPatternToFloat(_dieselGenAPhaseActivePower); - public ActivePower DieselGenBPhaseActivePower => ConvertBitPatternToFloat(_dieselGenBPhaseActivePower); - public ActivePower DieselGenCPhaseActivePower => ConvertBitPatternToFloat(_dieselGenCPhaseActivePower); + public ActivePower DieselGenAPhaseActivePower => ConvertBitPatternToFloat(_dieselGenAPhaseActivePower) * _factorFromKwtoW; + public ActivePower DieselGenBPhaseActivePower => ConvertBitPatternToFloat(_dieselGenBPhaseActivePower) * _factorFromKwtoW; + public ActivePower DieselGenCPhaseActivePower => ConvertBitPatternToFloat(_dieselGenCPhaseActivePower) * _factorFromKwtoW; - public ReactivePower DieselGenAPhaseReactivePower => ConvertBitPatternToFloat(_dieselGenAPhaseReactivePower); - public ReactivePower DieselGenBPhaseReactivePower => ConvertBitPatternToFloat(_dieselGenBPhaseReactivePower); - public ReactivePower DieselGenCPhaseReactivePower => ConvertBitPatternToFloat(_dieselGenCPhaseReactivePower); + public ReactivePower DieselGenAPhaseReactivePower => ConvertBitPatternToFloat(_dieselGenAPhaseReactivePower) * _factorFromKwtoW; + public ReactivePower DieselGenBPhaseReactivePower => ConvertBitPatternToFloat(_dieselGenBPhaseReactivePower) * _factorFromKwtoW; + public ReactivePower DieselGenCPhaseReactivePower => ConvertBitPatternToFloat(_dieselGenCPhaseReactivePower) * _factorFromKwtoW; // ─────────────────────────────────────────────── // Photovoltaic and Battery Measurements // ─────────────────────────────────────────────── public Voltage PvVoltage1 => ConvertBitPatternToFloat(_pvVoltage1); public Current PvCurrent1 => ConvertBitPatternToFloat(_pvCurrent1); - public ActivePower PvPower1 => ConvertBitPatternToFloat(_pvPower1); + public ActivePower PvPower1 => ConvertBitPatternToFloat(_pvPower1) *_factorFromKwtoW; public Voltage PvVoltage2 => ConvertBitPatternToFloat(_pvVoltage2); public Current PvCurrent2 => ConvertBitPatternToFloat(_pvCurrent2); - public ActivePower PvPower2 => ConvertBitPatternToFloat(_pvPower2); + public ActivePower PvPower2 => ConvertBitPatternToFloat(_pvPower2) * _factorFromKwtoW; + - public Voltage BatteryVoltage1 => ConvertBitPatternToFloat(_batteryVoltage1); - public Current BatteryCurrent1 => ConvertBitPatternToFloat(_batteryCurrent1); - public ActivePower BatteryPower1 => ConvertBitPatternToFloat(_batteryPower1); - public Percent BatterySoc1 => ConvertBitPatternToFloat(_batterySoc1); - public float BatteryFullLoadDuration1 => ConvertBitPatternToFloat(_batteryFullLoadDuration1); + public Voltage Battery1Voltage => ConvertBitPatternToFloat(_batteryVoltage1); + public Current Battery1Current => ConvertBitPatternToFloat(_batteryCurrent1); + public ActivePower Battery1Power => ConvertBitPatternToFloat(_batteryPower1) * _factorFromKwtoW; + public Percent Battery1Soc => ConvertBitPatternToFloat(_batterySoc1); + public float BatteryFullLoadDuration1 => ConvertBitPatternToFloat(_batteryFullLoadDuration1); // this is in hour - public Voltage BatteryVoltage2 => ConvertBitPatternToFloat(_batteryVoltage2); - public Current BatteryCurrent2 => ConvertBitPatternToFloat(_batteryCurrent2); - public ActivePower BatteryPower2 => ConvertBitPatternToFloat(_batteryPower2); - public Percent BatterySoc2 => ConvertBitPatternToFloat(_batterySoc2); + public Voltage Battery2Voltage => ConvertBitPatternToFloat(_batteryVoltage2); + public Current Battery2Current => ConvertBitPatternToFloat(_batteryCurrent2); + public ActivePower Battery2Power => ConvertBitPatternToFloat(_batteryPower2) * _factorFromKwtoW; + public Percent Battery2Soc => ConvertBitPatternToFloat(_batterySoc2); public float BatteryFullLoadDuration2 => ConvertBitPatternToFloat(_batteryFullLoadDuration2); public Temperature DcdcTemperature1 => ConvertBitPatternToFloat(_dcdcTemperature1); @@ -166,8 +172,8 @@ public partial class SinexcelRecord // ─────────────────────────────────────────────── // Energy and Power Summary // ─────────────────────────────────────────────── - public Energy ElectricityPurchased => ConvertBitPatternToFloat(_electricityPurchased); - public Energy ElectricityFed => ConvertBitPatternToFloat(_electricityFed); + public Energy TotalEnergyToUser => ConvertBitPatternToFloat(_electricityPurchased); + public Energy TotalEnergyToGrid => ConvertBitPatternToFloat(_electricityFed); public Energy SelfGeneratedElectricity => ConvertBitPatternToFloat(_selfGeneratedElectricity); public Energy BatteryCharge => ConvertBitPatternToFloat(_batteryCharge); public Energy BatteryDischarge => ConvertBitPatternToFloat(_batteryDischarge); @@ -175,21 +181,21 @@ public partial class SinexcelRecord public Energy DailySelfGeneratedElectricity => ConvertBitPatternToFloat(_dailySelfGeneratedElectricity); public Energy DailyElectricityPurchased => ConvertBitPatternToFloat(_dailyElectricityPurchased); public Energy DailyElectricityFed => ConvertBitPatternToFloat(_dailyElectricityFed); - public Energy DailyBatteryCharge => ConvertBitPatternToFloat(_dailyBatteryCharge); - public Energy DailyBatteryDischarge => ConvertBitPatternToFloat(_dailyBatteryDischarge); + public Energy BatteryDailyChargeEnergy => ConvertBitPatternToFloat(_dailyBatteryCharge); + public Energy BatteryDailyDischargeEnergy => ConvertBitPatternToFloat(_dailyBatteryDischarge); public Energy DailyLoadPowerConsumption => ConvertBitPatternToFloat(_dailyLoadPowerConsumption); - public ActivePower TotalPhotovoltaicPower => ConvertBitPatternToFloat(_totalPhotovoltaicPower); - public ActivePower TotalBatteryPower => ConvertBitPatternToFloat(_totalBatteryPower); - public ActivePower TotalLoadPower => ConvertBitPatternToFloat(_totalLoadPower); - public ActivePower TotalGridPower => ConvertBitPatternToFloat(_totalGridPower); - public ActivePower ImportantLoadTotalPower => ConvertBitPatternToFloat(_importantLoadTotalPower); - public ActivePower GeneralLoadTotalPower => ConvertBitPatternToFloat(_generalLoadTotalPower); + public ActivePower TotalPhotovoltaicPower => ConvertBitPatternToFloat(_totalPhotovoltaicPower) * _factorFromKwtoW; + public ActivePower TotalBatteryPower => ConvertBitPatternToFloat(_totalBatteryPower) * _factorFromKwtoW; + public ActivePower TotalLoadPower => ConvertBitPatternToFloat(_totalLoadPower) * _factorFromKwtoW ; + public ActivePower TotalGridPower => ConvertBitPatternToFloat(_totalGridPower) * _factorFromKwtoW ; + public ActivePower ImportantLoadTotalPower => ConvertBitPatternToFloat(_importantLoadTotalPower)* _factorFromKwtoW; + public ActivePower GeneralLoadTotalPower => ConvertBitPatternToFloat(_generalLoadTotalPower)* _factorFromKwtoW; public Voltage Pv3Voltage => ConvertBitPatternToFloat(_pv3Voltage); public Current Pv3Current => ConvertBitPatternToFloat(_pv3Current); - public ActivePower Pv3Power => ConvertBitPatternToFloat(_pv3Power); + public ActivePower Pv3Power => ConvertBitPatternToFloat(_pv3Power) * _factorFromKwtoW; public Voltage Pv4Voltage => ConvertBitPatternToFloat(_pv4Voltage); public Current Pv4Current => ConvertBitPatternToFloat(_pv4Current); - public ActivePower Pv4Power => ConvertBitPatternToFloat(_pv4Power); + public ActivePower Pv4Power => ConvertBitPatternToFloat(_pv4Power) * _factorFromKwtoW; public ActivePower GeneratorTotalPower => ConvertBitPatternToFloat(_generatorTotalPower); // ─────────────────────────────────────────────── @@ -318,13 +324,13 @@ public partial class SinexcelRecord set => _batteryCapacity1 = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); } - public float MaxChargingCurrentBattery1 + public float Battery1MaxChargingCurrent { get => BitConverter.Int32BitsToSingle(unchecked((Int32)_maxChargingCurrentBattery1)); set => _maxChargingCurrentBattery1 = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); } - public float MaxDischargingCurrentBattery1 + public float Battery1MaxDischargingCurrent { get => BitConverter.Int32BitsToSingle(unchecked((Int32)_maxDischargingCurrentBattery1)); set => _maxDischargingCurrentBattery1 = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); @@ -336,7 +342,7 @@ public partial class SinexcelRecord set => _ratedBatteryVoltage1 = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); } - public float MinSocBattery1 + public float Battery1MinSoc { get => BitConverter.Int32BitsToSingle(unchecked((Int32)_minSocBattery1)); set => _minSocBattery1 = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); @@ -356,8 +362,8 @@ public partial class SinexcelRecord public float ActiveChargeDischargePower { - get => ConvertBitPatternToFloat(_activeChargeDischargePower); - set => _activeChargeDischargePower = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); + get => ConvertBitPatternToFloat(_activeChargeDischargePower) * _factorFromKwtoW; + set => _activeChargeDischargePower = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); // we should check this may need to convert from W to KW } // ─────────────────────────────────────────────── @@ -405,13 +411,13 @@ public partial class SinexcelRecord set => _battery2Capacity = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); } - public float MaxChargingCurrentBattery2 + public float Battery2MaxChargingCurrent { get => BitConverter.Int32BitsToSingle(unchecked((int)_maxChargingCurrentBattery2)); set => _maxChargingCurrentBattery2 = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); } - public float MaxDischargingCurrentBattery2 + public float Battery2MaxDischargingCurrent { get => BitConverter.Int32BitsToSingle(unchecked((int)_maxDischargingCurrentBattery2)); set => _maxDischargingCurrentBattery2 = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); @@ -498,16 +504,10 @@ public partial class SinexcelRecord set => _meterReverseManualDetection = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); } - public Voltage BatteryVoltage + public SinexcelBatteryRating BatteryVoltageRating { - get => (Voltage)ConvertBitPatternToFloat(_batteryVoltage); - set => _batteryVoltage = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); - } - - public SinexcelBatteryRating BatteryRating - { - get => (SinexcelBatteryRating)ConvertBitPatternToFloat(_batteryRating); - set => _batteryRating = (UInt32)value; + get => (SinexcelBatteryRating)ConvertBitPatternToFloat(_batteryVoltageRating); + set => _batteryVoltageRating = (UInt32)value; } public float Battery1Activation @@ -571,23 +571,122 @@ public partial class SinexcelRecord // ─────────────────────────────────────────────── public ActivePower ElectricMeterAPhaseActivePower { - get => BitConverter.Int32BitsToSingle(unchecked((Int32)_electricMeterAPhaseActivePower)); + get => BitConverter.Int32BitsToSingle(unchecked((Int32)_electricMeterAPhaseActivePower)) * _factorFromKwtoW; } public ActivePower ElectricMeterBPhaseActivePower { - get => BitConverter.Int32BitsToSingle(unchecked((Int32)_electricMeterBPhaseActivePower)); + get => BitConverter.Int32BitsToSingle(unchecked((Int32)_electricMeterBPhaseActivePower)) * _factorFromKwtoW; } public ActivePower ElectricMeterCPhaseActivePower { - get => BitConverter.Int32BitsToSingle(unchecked((Int32)_electricMeterCPhaseActivePower)); + get => BitConverter.Int32BitsToSingle(unchecked((Int32)_electricMeterCPhaseActivePower)) * _factorFromKwtoW; } + public ActivePower GridPower + { + get => ElectricMeterAPhaseActivePower + ElectricMeterBPhaseActivePower + ElectricMeterCPhaseActivePower; + } + + /* + // Energy (kW·h) + public Energy Battery1TotalChargingEnergy => BitConverter.Int32BitsToSingle(unchecked((Int32)_batteryCab1TotalChargingEnergy)); // 0xB0FC + public Energy Battery1TotalDischargedEnergy => BitConverter.Int32BitsToSingle(unchecked((Int32)_batteryCab1TotalDischargedEnergy)); // 0xB0FE + + // Pack totals and status + public Voltage Battery1PackTotalVoltage => BitConverter.Int32BitsToSingle(unchecked((Int32)_batteryCab1PackTotalVoltage)); // 0xB100 (0.01 V resolution per spec) + public Current Battery1PackTotalCurrent => BitConverter.Int32BitsToSingle(unchecked((Int32)_batteryCab1PackTotalCurrent)); // 0xB102 (0.01 A resolution per spec) + public Temperature Battery1Temperature => BitConverter.Int32BitsToSingle(unchecked((Int32)_batteryCab1Temperature)); // 0xB104 (0.01 °C resolution per spec) + public Percent Battery1SocSecondvalue => BitConverter.Int32BitsToSingle(unchecked((Int32)_batteryCab1Soc)); // 0xB106 % + public Percent Battery1Soh => BitConverter.Int32BitsToSingle(unchecked((Int32)_batteryCab1Soh)); // 0xB108 % + // Energy (kW·h) + public float Battery2TotalChargingEnergy => BitConverter.Int32BitsToSingle(unchecked((Int32)_batteryCab2TotalChargingEnergy)); // 0xB1FC + public float Battery2TotalDischargedEnergy => BitConverter.Int32BitsToSingle(unchecked((Int32)_batteryCab2TotalDischargedEnergy)); // 0xB1FE + +// Pack Voltage / Current / Temperature + public Voltage Battery2PackTotalVoltage => BitConverter.Int32BitsToSingle(unchecked((Int32)_batteryCab2PackTotalVoltage)); // 0xB200 (0.01 V resolution) + public Current Battery2PackTotalCurrent => BitConverter.Int32BitsToSingle(unchecked((Int32)_batteryCab2PackTotalCurrent)); // 0xB202 (0.01 A resolution) + public Temperature Battery2Socsecondvalue => BitConverter.Int32BitsToSingle(unchecked((Int32)_batteryCab2Soc)); // 0xB206 % + public Percent Battery2Soh => BitConverter.Int32BitsToSingle(unchecked((Int32)_batteryCab2Soh)); // 0xB208 % + + // Repetitive-week mask (bit-mapped 0–6 = Sun–Sat) + public SinexcelWeekDays RepetitiveWeeks + { + get => (SinexcelWeekDays)(_repetitiveWeeks & 0x7F); // only 7 bits used + set => _repetitiveWeeks = (UInt32)value; + } + + // Effective start / end as UNIX timestamps + public DateTime EffectiveStartDate + { + get => DateTimeOffset.FromUnixTimeSeconds(_effectiveStartDate).DateTime; + set => _effectiveStartDate = (UInt32)new DateTimeOffset(value).ToUnixTimeSeconds(); + } + + public DateTime EffectiveEndDate + { + get => DateTimeOffset.FromUnixTimeSeconds(_effectiveEndDate).DateTime; + set => _effectiveEndDate = (UInt32)new DateTimeOffset(value).ToUnixTimeSeconds(); + } + + // Charging power during time period 1 (kW) + public float ChargingPowerPeriod1 + { + get => BitConverter.Int32BitsToSingle(unchecked((Int32)_chargingPowerPeriod1)); + set => _chargingPowerPeriod1 = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); + } + + // ─────────────────────────────────────────────── + // Charging period 1 + // ─────────────────────────────────────────────── + public TimeSpan ChargeStartTimePeriod1 + { + get => ConvertToTimeSpan(_chargeStartTimePeriod1); + set => _chargeStartTimePeriod1 = ConvertFromTimeSpan(value); + } + + public TimeSpan ChargeEndTimePeriod1 + { + get => ConvertToTimeSpan(_chargeEndTimePeriod1); + set => _chargeEndTimePeriod1 = ConvertFromTimeSpan(value); + } + + // ─────────────────────────────────────────────── + // Discharging period 1 + // ─────────────────────────────────────────────── + public TimeSpan DischargeStartTimePeriod1 + { + get => ConvertToTimeSpan(_dischargeStartTimePeriod1); + set => _dischargeStartTimePeriod1 = ConvertFromTimeSpan(value); + } + + public TimeSpan DischargeEndTimePeriod1 + { + get => ConvertToTimeSpan(_dischargeEndTimePeriod1); + set => _dischargeEndTimePeriod1 = ConvertFromTimeSpan(value); + }*/ + + // ─────────────────────────────────────────────── // Conversion methodes // ─────────────────────────────────────────────── + + private static TimeSpan ConvertToTimeSpan(UInt32 value) + { + int hhmm = (int)value; + int hours = hhmm / 100; + int minutes = hhmm % 100; + return new TimeSpan(hours, minutes, 0); + } + + private static UInt32 ConvertFromTimeSpan(TimeSpan time) + { + int hhmm = time.Hours * 100 + time.Minutes; + return (UInt32)hhmm; + } + private static Single IntToFloat(Int32 intValue) { byte[] intBytes = BitConverter.GetBytes(intValue); From 09bd11740f2a84ac69522812ae8fbcae73cb00f7 Mon Sep 17 00:00:00 2001 From: atef Date: Wed, 12 Nov 2025 16:08:40 +0100 Subject: [PATCH 14/23] Update SinexcelRecord modbus with new registers reading and writing --- .../Sinexcel 12K TL/SinexcelRecord.Modbus.cs | 43 ++++++++++++++++++- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/csharp/Sinexcel 12K TL/SinexcelRecord.Modbus.cs b/csharp/Sinexcel 12K TL/SinexcelRecord.Modbus.cs index 2fe68ae1d..09f952c25 100644 --- a/csharp/Sinexcel 12K TL/SinexcelRecord.Modbus.cs +++ b/csharp/Sinexcel 12K TL/SinexcelRecord.Modbus.cs @@ -287,8 +287,7 @@ public partial class SinexcelRecord [HoldingRegister(15428, writable: true)] private UInt32 _powerOff; // 0x3C44 [HoldingRegister(15430, writable: true)] private UInt32 _faultClearing; // 0x3C46 [HoldingRegister(15518, writable: true)] private UInt32 _meterReverseManualDetection; // 0x3C9E - [HoldingRegister(15520, writable: true)] private UInt32 _batteryVoltage; // 0x3CA0 - [HoldingRegister(15522, writable: true)] private UInt32 _batteryRating; // 0x3CA2 + [HoldingRegister(15520, writable: true)] private UInt32 _batteryVoltageRating; // 0x3CA0 [HoldingRegister(15524, writable: true)] private UInt32 _battery1Activation; // 0x3CA4 [HoldingRegister(15526, writable: true)] private UInt32 _battery2Activation; // 0x3CA6 @@ -309,5 +308,45 @@ public partial class SinexcelRecord [HoldingRegister(41234)] private UInt32 _electricMeterAPhaseActivePower; // 0xA112 [HoldingRegister(41236)] private UInt32 _electricMeterBPhaseActivePower; // 0xA114 [HoldingRegister(41238)] private UInt32 _electricMeterCPhaseActivePower; // 0xA116 + /* + // ─────────────────────────────────────────────── + // Battery Cabinet 1 — Energy / Pack measurements (read-only) + // ─────────────────────────────────────────────── + [HoldingRegister(45308)] private UInt32 _batteryCab1TotalChargingEnergy; // 0xB0FC kW·h + [HoldingRegister(45310)] private UInt32 _batteryCab1TotalDischargedEnergy; // 0xB0FE kW·h + [HoldingRegister(45312)] private UInt32 _batteryCab1PackTotalVoltage; // 0xB100 (resolution 0.01 V) + [HoldingRegister(45314)] private UInt32 _batteryCab1PackTotalCurrent; // 0xB102 (resolution 0.01 A) + [HoldingRegister(45316)] private UInt32 _batteryCab1Temperature; // 0xB104 (resolution 0.01 °C) + [HoldingRegister(45318)] private UInt32 _batteryCab1Soc; // 0xB106 % + [HoldingRegister(45320)] private UInt32 _batteryCab1Soh; // 0xB108 % + + // ─────────────────────────────────────────────── + // Battery Cabinet 2 — Energy / Pack Measurements (Read-Only) + // ─────────────────────────────────────────────── + [HoldingRegister(45564)] private UInt32 _batteryCab2TotalChargingEnergy; // 0xB1FC kW·h + [HoldingRegister(45566)] private UInt32 _batteryCab2TotalDischargedEnergy; // 0xB1FE kW·h + + [HoldingRegister(45568)] private UInt32 _batteryCab2PackTotalVoltage; // 0xB200 (0.01 V) + [HoldingRegister(45570)] private UInt32 _batteryCab2PackTotalCurrent; // 0xB202 (0.01 A) + [HoldingRegister(45572)] private UInt32 _batteryCab2Temperature; // 0xB204 (0.01 °C) + [HoldingRegister(45574)] private UInt32 _batteryCab2Soc; // 0xB206 % + [HoldingRegister(45576)] private UInt32 _batteryCab2Soh; // 0xB208 % + + // ─────────────────────────────────────────────── + // Time-based scheduling (period 1) configuration + // ─────────────────────────────────────────────── + [HoldingRegister(49332, writable: true)] private UInt32 _repetitiveWeeks; // 0xC0B4, bit flags Sun–Sat + [HoldingRegister(49334, writable: true)] private UInt32 _effectiveStartDate; // 0xC0B6, UNIX timestamp (UTC+offset) + [HoldingRegister(49336, writable: true)] private UInt32 _effectiveEndDate; // 0xC0B8, UNIX timestamp (UTC+offset) + [HoldingRegister(49338, writable: true)] private UInt32 _chargingPowerPeriod1; // 0xC0BA, float [0–50] kW, default 5 + + // ─────────────────────────────────────────────── + // Time-based charge/discharge schedule period 1 + // ─────────────────────────────────────────────── + [HoldingRegister(49172, writable: true)] private UInt32 _chargeStartTimePeriod1; // 0xC014 [0-2359] (start) + [HoldingRegister(49174, writable: true)] private UInt32 _chargeEndTimePeriod1; // 0xC016 [0-2359] (end) + [HoldingRegister(49176, writable: true)] private UInt32 _dischargeStartTimePeriod1; // 0xC018 [0-2359] (start) + [HoldingRegister(49178, writable: true)] private UInt32 _dischargeEndTimePeriod1; // 0xC01A [0-2359] (end) +*/ } \ No newline at end of file From a2b70086ea3541e6689510892b44b1564a4c3021 Mon Sep 17 00:00:00 2001 From: atef Date: Wed, 12 Nov 2025 16:08:57 +0100 Subject: [PATCH 15/23] Create an enum for SinexcelWeekdays --- .../Sinexcel 12K TL/DataType/SinexcelWeekDays.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 csharp/Sinexcel 12K TL/DataType/SinexcelWeekDays.cs diff --git a/csharp/Sinexcel 12K TL/DataType/SinexcelWeekDays.cs b/csharp/Sinexcel 12K TL/DataType/SinexcelWeekDays.cs new file mode 100644 index 000000000..5882c86ae --- /dev/null +++ b/csharp/Sinexcel 12K TL/DataType/SinexcelWeekDays.cs @@ -0,0 +1,14 @@ +namespace InnovEnergy.Lib.Devices.Sinexcel_12K_TL.DataType; + +public enum SinexcelWeekDays : byte +{ + None = 0b00000000, + Sunday = 0b00000001, + Monday = 0b00000010, + Tuesday = 0b00000100, + Wednesday= 0b00001000, + Thursday = 0b00010000, + Friday = 0b00100000, + Saturday = 0b01000000, + All = 0b01111111 +} \ No newline at end of file From d067c5a57d91af71c74a84e9303c5fcf34a98871 Mon Sep 17 00:00:00 2001 From: atef Date: Wed, 12 Nov 2025 16:09:22 +0100 Subject: [PATCH 16/23] Update the AcDCgrowaat and sinexcle with common name for the front end : InverterRecord --- csharp/App/GrowattCommunication/ESS/StatusRecord.cs | 2 +- csharp/App/SinexcelCommunication/ESS/StatusRecord.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/csharp/App/GrowattCommunication/ESS/StatusRecord.cs b/csharp/App/GrowattCommunication/ESS/StatusRecord.cs index 8b34c42ff..8b0e94e81 100644 --- a/csharp/App/GrowattCommunication/ESS/StatusRecord.cs +++ b/csharp/App/GrowattCommunication/ESS/StatusRecord.cs @@ -5,6 +5,6 @@ namespace InnovEnergy.App.GrowattCommunication.ESS; public record StatusRecord { - public required WITGrowatRecord AcDcGrowatt { get; set; } + public required WITGrowatRecord InverterRecord { get; set; } public required Config Config { get; set; } } \ No newline at end of file diff --git a/csharp/App/SinexcelCommunication/ESS/StatusRecord.cs b/csharp/App/SinexcelCommunication/ESS/StatusRecord.cs index 5941bf755..88a9a04b3 100644 --- a/csharp/App/SinexcelCommunication/ESS/StatusRecord.cs +++ b/csharp/App/SinexcelCommunication/ESS/StatusRecord.cs @@ -5,6 +5,6 @@ namespace InnovEnergy.App.SinexcelCommunication.ESS; public record StatusRecord { - public required SinexcelRecord SinexcelRecord { get; set; } + public required SinexcelRecord InverterRecord { get; set; } public required Config Config { get; set; } } \ No newline at end of file From 8e029a1afdad2ff7f5b8f5c5504eab5f739fd1fc Mon Sep 17 00:00:00 2001 From: atef Date: Wed, 12 Nov 2025 16:11:19 +0100 Subject: [PATCH 17/23] Update the Growatt API and modbus --- .../WITGrowatt4-15K/WITGrowatRecord.Api.cs | 190 +++++++----------- .../WITGrowatt4-15K/WITGrowatRecord.Modbus.cs | 78 +++---- 2 files changed, 115 insertions(+), 153 deletions(-) diff --git a/csharp/Lib/Devices/WITGrowatt4-15K/WITGrowatRecord.Api.cs b/csharp/Lib/Devices/WITGrowatt4-15K/WITGrowatRecord.Api.cs index 748ca514a..4fd1ab729 100644 --- a/csharp/Lib/Devices/WITGrowatt4-15K/WITGrowatRecord.Api.cs +++ b/csharp/Lib/Devices/WITGrowatt4-15K/WITGrowatRecord.Api.cs @@ -15,10 +15,10 @@ public partial class WITGrowatRecord //public BatteriesRecord? BatteriesRecords => BatteriesRecord.FromBatteries(Batteries); - public ActivePower ConsumptionPower => MeterPower + InverterActivePower ; - public ActivePower InverterActivePower => _ActivePower; - public ReactivePower InverterReactivePower => _ReactivePower; - public Frequency Frequency => _Frequency; + public ActivePower ConsumptionPower => GridPower + InverterActivePower ; + public ActivePower InverterActivePower => _activePower; + public ReactivePower InverterReactivePower => _reactivePower; + public Frequency Frequency => _frequency; public VoltageRms GridAbLineVoltage => _GridAbLineVoltage; public VoltageRms GridBcLineVoltage => _GridBcLineVoltage; @@ -27,7 +27,7 @@ public partial class WITGrowatRecord public CurrentRms PhaseBCurrent => _PhaseBCurrent; public CurrentRms PhaseCCurrent => _PhaseCCurrent; - public ActivePower MeterPower => _importedPowerFromGrid - _exportedPowerToGrid; + public ActivePower GridPower => _importedPowerFromGrid - _exportedPowerToGrid; public ActivePower ExportedPowerToGridMeter => _exportedPowerToGrid; public ActivePower ImportedPowerFromGrid => _importedPowerFromGrid; @@ -38,34 +38,33 @@ public partial class WITGrowatRecord public Energy EnergyToGrid => _EnergyToGrid; public Energy TotalEnergyToGrid => _TotalEnergyToGrid; - public GrowattSystemStatus SystemOperatingMode => (GrowattSystemStatus)_SystemOperatingMode; - public BatteryoperatinStatus BatteryOperatingMode => (BatteryoperatinStatus) _BatteryOperatingMode; - public OperatingPriority OperatingPriority => (OperatingPriority)_OperatingPriority; + public GrowattSystemStatus SystemOperatingMode => (GrowattSystemStatus)_systemOperatingMode; + public BatteryoperatinStatus BatteryOperatingMode => (BatteryoperatinStatus) _batteryOperatingMode; + public OperatingPriority OperatingMode => (OperatingPriority)_operatingPriority; - public UInt16 FaultMainCode => _FaultMainCode; // need to pre proceesed - public UInt16 FaultSubCode => _FaultSubCode; // need to pre proceesed - public UInt16 WarningMainCode => _WarningMainCode; // need to pre proceesed - public UInt16 WarningSubCode => _WarningSubCode; // need to pre proceesed + public UInt16 FaultMainCode => _faultMainCode; // need to pre proceesed + public UInt16 FaultSubCode => _faultSubCode; // need to pre proceesed + public UInt16 WarningMainCode => _warningMainCode; // need to pre proceesed + public UInt16 WarningSubCode => _warningSubCode; // need to pre proceesed - public Voltage Pv1Voltage => _Pv1Voltage; - public Current Pv1Current => _Pv1Current; - public Voltage Pv2Voltage => _Pv2Voltage; - public Current Pv2Current => _Pv2Current; - public Voltage Pv3Voltage => _Pv3Voltage; - public Current Pv3Current => _Pv3Current; - public Voltage Pv4Voltage => _Pv4Voltage; - public Current Pv4Current => _Pv4Current; - public Voltage Pv5Voltage => _Pv5Voltage; - public Current Pv5Current => _Pv5Current; - public Voltage Pv6Voltage => _Pv6Voltage; - public Current Pv6Current => _Pv6Current; + public Voltage Pv1Voltage => _pv1Voltage; + public Current Pv1Current => _pv1Current; + public Voltage Pv2Voltage => _pv2Voltage; + public Current Pv2Current => _pv2Current; + public Voltage Pv3Voltage => _pv3Voltage; + public Current Pv3Current => _pv3Current; + public Voltage Pv4Voltage => _pv4Voltage; + public Current Pv4Current => _pv4Current; + public Voltage Pv5Voltage => _pv5Voltage; + public Current Pv5Current => _pv5Current; + public Voltage Pv6Voltage => _pv6Voltage; + public Current Pv6Current => _pv6Current; - public DcPower Pv1InpuPower => _Pv1InpuPower; - + public DcPower Pv1InpuPower => _pv1InpuPower; - public DcPower TotalPvPower => (Pv1Voltage * Pv1Current) + (Pv2Voltage * Pv2Current) + + public DcPower PvPower => (Pv1Voltage * Pv1Current) + (Pv2Voltage * Pv2Current) + (Pv3Voltage * Pv3Current) + (Pv4Voltage * Pv4Current) + (Pv5Voltage * Pv5Current) + (Pv6Voltage * Pv6Current); // ********************************** Holding Registers (Control) ************************************************************* @@ -173,16 +172,16 @@ public partial class WITGrowatRecord set => _BatteryMaxDischargePower = value; } - public Percent ChargeCutoffSoc + public Percent ChargeCutoffSocVoltage { - get => _ChargeCutoffSoc; - set => _ChargeCutoffSoc = (UInt16)value; + get => _BatteryChargeCutoffVoltage; + set => _BatteryChargeCutoffVoltage = (UInt16)value; } - public Percent DischargeCutoffSoc + public Percent DischargeCutoffVoltage { - get => _DischargeCutoffSoc; - set => _DischargeCutoffSoc = (UInt16)value; + get => _BatteryDischargeCutoffVoltage; + set => _BatteryDischargeCutoffVoltage = (UInt16)value; } public Percent LoadPriorityDischargeCutoffSoc @@ -226,98 +225,61 @@ public partial class WITGrowatRecord set => _OffGridDischargeCutoffSoc = (UInt16)(value); } - public UInt16 BatteryChargeCutoffVoltage + public UInt16 MaxSoc { - get => _BatteryChargeCutoffVoltage; - set => _BatteryChargeCutoffVoltage = value; + get => _maxSoc; + set => _maxSoc = value; } - public UInt16 BatteryDischargeCutoffVoltage + public UInt16 MinSoc { - get => _BatteryDischargeCutoffVoltage; - set => _BatteryDischargeCutoffVoltage = value; + get => _minSoc; + set => _minSoc = value; } - public Double BatteryMaxChargeCurrent + public Double BatteryMaxChargingCurrent { - get => _BatteryMaxChargeCurrent; - set => _BatteryMaxChargeCurrent = (UInt16)value; + get => _batteryMaxChargingCurrent; + set => _batteryMaxChargingCurrent = (UInt16)value; } - public Double BatteryMaxdischargeCurrent + public Double BatteryMaxDischargingCurrent { - get => _BatteryMaxdischargeCurrent; - set => _BatteryMaxdischargeCurrent = (UInt16) value; + get => _batteryMaxDischargingCurrent; + set => _batteryMaxDischargingCurrent = (UInt16) value; } - - - - public IReadOnlyList Batteries => new List - { - new BatteryRecord - { - Soc = _BatterySoc1, - Soh = _BatterySoh1, - //ClusterTotalNumber = _ClusterTotalNumber1, - Current = _BatteryCurrent1, - Voltage = _BatteryVoltage1, - Power = _BatteryPower1, - MaxAllowableChargePower = _BatteryMaxAllowableChargePower1, - MaxAllowableDischargePower = _BatteryMaxAllowableDischargePower1, - DailyChargeEnergy = _DailyChargeEnergy1, - DailyDischargeEnergy = _DailyDischargeEnergy1, - AccumulatedChargeEnergy = _AccumulatedChargeEnergy1, - AccumulatedDischargeEnergy = _AccumulatedDishargeEnergy1, - BatteryAmbientTemperature = _BatteryAmbientTemperature1 - - } - /* - new BatteryRecord - { - Soc = _BatterySoc2, - Soh = _BatterySoh2, - // ClusterTotalNumber = _ClusterTotalNumber2, - Current = _BatteryCurrent2, - Voltage = _BatteryVoltage2, - Power = _BatteryPower2, - MaxAllowableChargePower = _BatteryMaxAllowableChargePower2, - MaxAllowableDischargePower = _BatteryMaxAllowableDischargePower2, - DailyChargeEnergy = _DailyChargeEnergy2, - DailyDischargeEnergy = _DailyDischargeEnergy2, - AccumulatedChargeEnergy = _AccumulatedChargeEnergy2, - AccumulatedDischargeEnergy = _AccumulatedDischargeEnergy2, - BatteryAmbientTemperature = _BatteryAmbientTemperature2 + public Double Battery1MaxDischargingCurrent => _batteryMaxDischargingCurrent; + + public Double Battery1MaxChargingCurrent => _batteryMaxChargingCurrent; + + public Percent Battery1Soc => _BatterySoc1; + public Percent Battery1Soh => _BatterySoh1; + public Current Battery1Current => _BatteryCurrent1; + public Voltage Battery1Voltage => _BatteryVoltage1; + public ActivePower Battery1Power => _BatteryPower1; + public ActivePower Battery1MaxAllowableChargePower => _BatteryMaxAllowableChargePower1; + public ActivePower Battery1MaxAllowableDischargePower => _BatteryMaxAllowableDischargePower1; + public Energy Battery1DailyChargeEnergy => _DailyChargeEnergy1; + public Energy Battery1DailyDischargeEnergy => _DailyDischargeEnergy1; + public Energy Battery1AccumulatedChargeEnergy => _AccumulatedChargeEnergy1; + public Energy Battery1AccumulatedDischargeEnergy => _AccumulatedDishargeEnergy1; + public Temperature Battery1AmbientTemperature => _BatteryAmbientTemperature1; + + public Percent Battery2Soc => _BatterySoc2; + public Percent Battery2Soh => _BatterySoh2; + public Current Battery2Current => _BatteryCurrent2; + public Voltage Battery2Voltage => _BatteryVoltage2; + public ActivePower Battery2Power => _batteryPower2; + public ActivePower Battery2MaxAllowableChargePower => _BatteryMaxAllowableChargePower2; + public ActivePower Battery2MaxAllowableDischargePower => _BatteryMaxAllowableDischargePower2; + public Energy Battery2DailyChargeEnergy => _DailyChargeEnergy2; + public Energy Battery2DailyDischargeEnergy => _DailyDischargeEnergy2; + public Energy Battery2AccumulatedChargeEnergy => _AccumulatedChargeEnergy2; + public Energy Battery2AccumulatedDischargeEnergy => _AccumulatedDischargeEnergy2; + public Temperature Battery2AmbientTemperature => _BatteryAmbientTemperature2; + + public Energy BatteryDailyChargeEnergy => _DailyChargeEnergy1 + _DailyChargeEnergy2; + public Energy BatteryDailyDischargeEnergy => _DailyDischargeEnergy1 + _DailyDischargeEnergy2; - },*/ - /* - new BatteryRecord - { - Soc = _BatterySoc3, - Soh = _BatterySoh3, - ClusterTotalNumber = _ClusterTotalNumber3, - Current = _BatteryCurrent3, - Voltage = _BatteryVoltage3, - Power = _BatteryPower3, - MaxAllowableChargePower = _BatteryMaxAllowableChargePower3, - MaxAllowableDischargePower = _BatteryMaxAllowableDischargePower3, - DailyChargeEnergy = _DailyChargeEnergy3, - DailyDischargeEnergy = _DailyDischargeEnergy3 - }, - - new BatteryRecord - { - Soc = _BatterySoc4, - Soh = _BatterySoh4, - ClusterTotalNumber = _ClusterTotalNumber4, - Current = _BatteryCurrent4, - Voltage = _BatteryVoltage4, - Power = _BatteryPower4, - MaxAllowableChargePower = _BatteryMaxAllowableChargePower4, - MaxAllowableDischargePower = _BatteryMaxAllowableDischargePower4, - DailyChargeEnergy = _DailyChargeEnergy4, - DailyDischargeEnergy = _DailyDischargeEnergy4 - }*/ - }; - public BatteriesRecord? BatteriesRecords => BatteriesRecord.FromBatteries(Batteries); } diff --git a/csharp/Lib/Devices/WITGrowatt4-15K/WITGrowatRecord.Modbus.cs b/csharp/Lib/Devices/WITGrowatt4-15K/WITGrowatRecord.Modbus.cs index a1e19f588..63ad4b751 100644 --- a/csharp/Lib/Devices/WITGrowatt4-15K/WITGrowatRecord.Modbus.cs +++ b/csharp/Lib/Devices/WITGrowatt4-15K/WITGrowatRecord.Modbus.cs @@ -14,36 +14,36 @@ public partial class WITGrowatRecord [InputRegister(8083, Scale = 0.1)] private Int32 _exportedPowerToGrid; // 31000–31009 — Operating Status Info - [InputRegister(31000)] private UInt16 _SystemOperatingMode; - [InputRegister(31001)] private UInt16 _BatteryOperatingMode; - [InputRegister(31002)] private UInt16 _OperatingPriority; - [InputRegister(31003)] private UInt16 _Reserved1; - [InputRegister(31004)] private UInt16 _Reserved2; - [InputRegister(31005)] private UInt16 _FaultMainCode;// Can we change this to warning? - [InputRegister(31006)] private UInt16 _FaultSubCode; // Can we change this to warning? - [InputRegister(31007)] private UInt16 _WarningMainCode; - [InputRegister(31008)] private UInt16 _WarningSubCode; - [InputRegister(31009)] private UInt16 _Reserved3; + [InputRegister(31000)] private UInt16 _systemOperatingMode; + [InputRegister(31001)] private UInt16 _batteryOperatingMode; + [InputRegister(31002)] private UInt16 _operatingPriority; + [InputRegister(31003)] private UInt16 _reserved1; + [InputRegister(31004)] private UInt16 _reserved2; + [InputRegister(31005)] private UInt16 _faultMainCode;// Can we change this to warning? + [InputRegister(31006)] private UInt16 _faultSubCode; // Can we change this to warning? + [InputRegister(31007)] private UInt16 _warningMainCode; + [InputRegister(31008)] private UInt16 _warningSubCode; + [InputRegister(31009)] private UInt16 _reserved3; // 31010–31099 — PV Parameters - [InputRegister(31010,Scale = 0.1)] private Int16 _Pv1Voltage; - [InputRegister(31011,Scale = 0.1)] private Int16 _Pv1Current; - [InputRegister(31012,Scale = 0.1)] private Int16 _Pv2Voltage; - [InputRegister(31013,Scale = 0.1)] private Int16 _Pv2Current; - [InputRegister(31014,Scale = 0.1)] private Int16 _Pv3Voltage; - [InputRegister(31015,Scale = 0.1)] private Int16 _Pv3Current; - [InputRegister(31016,Scale = 0.1)] private Int16 _Pv4Voltage; - [InputRegister(31017,Scale = 0.1)] private Int16 _Pv4Current; - [InputRegister(31018,Scale = 0.1)] private Int16 _Pv5Voltage; - [InputRegister(31019,Scale = 0.1)] private Int16 _Pv5Current; - [InputRegister(31020,Scale = 0.1)] private Int16 _Pv6Voltage; - [InputRegister(31021,Scale = 0.1)] private Int16 _Pv6Current; - [InputRegister(31058)] private Int16 _Pv1InpuPower; + [InputRegister(31010,Scale = 0.1)] private Int16 _pv1Voltage; + [InputRegister(31011,Scale = 0.1)] private Int16 _pv1Current; + [InputRegister(31012,Scale = 0.1)] private Int16 _pv2Voltage; + [InputRegister(31013,Scale = 0.1)] private Int16 _pv2Current; + [InputRegister(31014,Scale = 0.1)] private Int16 _pv3Voltage; + [InputRegister(31015,Scale = 0.1)] private Int16 _pv3Current; + [InputRegister(31016,Scale = 0.1)] private Int16 _pv4Voltage; + [InputRegister(31017,Scale = 0.1)] private Int16 _pv4Current; + [InputRegister(31018,Scale = 0.1)] private Int16 _pv5Voltage; + [InputRegister(31019,Scale = 0.1)] private Int16 _pv5Current; + [InputRegister(31020,Scale = 0.1)] private Int16 _pv6Voltage; + [InputRegister(31021,Scale = 0.1)] private Int16 _pv6Current; + [InputRegister(31058)] private Int16 _pv1InpuPower; //— AC Side 31100 - 31199 - [InputRegister(31100, Scale = 0.1)] private Int32 _ActivePower; // Positive: feed to grid, Negative: draw from the grid - [InputRegister(31102, Scale = 0.1)] private Int32 _ReactivePower; // Positive: capacitive, Negative: Inductive - [InputRegister(31104, Scale = 0.1)] private Int16 _Reserved4; - [InputRegister(31105, Scale = 0.01)] private UInt16 _Frequency; + [InputRegister(31100, Scale = 0.1)] private Int32 _activePower; // Positive: feed to grid, Negative: draw from the grid + [InputRegister(31102, Scale = 0.1)] private Int32 _reactivePower; // Positive: capacitive, Negative: Inductive + [InputRegister(31104, Scale = 0.1)] private Int16 _reserved4; + [InputRegister(31105, Scale = 0.01)] private UInt16 _frequency; [InputRegister(31106, Scale = 0.1)] private UInt16 _GridAbLineVoltage; [InputRegister(31107, Scale = 0.1)] private UInt16 _GridBcLineVoltage; @@ -88,7 +88,7 @@ public partial class WITGrowatRecord // 31300–31399 — Second Battery Cluster Info (incl. BDC and BMS) - /* [InputRegister(31300)] private Int32 _BatteryPower2; // positive Charge, Negative Discharge + [InputRegister(31300)] private Int32 _batteryPower2; // positive Charge, Negative Discharge [InputRegister(31302)] private UInt32 _DailyChargeEnergy2; [InputRegister(31304)] private UInt32 _AccumulatedChargeEnergy2; [InputRegister(31206)] private UInt32 _DailyDischargeEnergy2; //0.1kw @@ -164,26 +164,26 @@ public partial class WITGrowatRecord //[HoldingRegister(30152, writable: true)] private UInt16 _Reserved11; // [HoldingRegister(30154, writable: true)] private UInt16 _ActivePowerPercent; // Limit percentage: [0, 100]; Default: 100; takes the smaller value of 30151 and 30154 as actual active limit; Not stored - [HoldingRegister(30162)] private UInt16 _PowerFactor; // [0, 2000] ∪ [18000, 20000]; Default: 20000; Actual PF = (Register Value - 10000) + [HoldingRegister(30162)] private UInt16 _PowerFactor; // [0, 2000] ∪ [18000, 20000]; Default: 20000; Actual PF = (Register Value - 10000) [HoldingRegister(30203, writable : true)] private UInt16 _EmsCommunicationFailureTime; // [1,300] TODO to 30 [HoldingRegister(30204, writable : true)] private Boolean _EnableEmsCommunicationFailureTime; // 0: disabled, 1 = enabled we should enable this TODO - [HoldingRegister(30300)] private UInt16 _BatteryClusterIndex; // [0..3] + [HoldingRegister(30300)] private UInt16 _BatteryClusterIndex; // [0..3] [HoldingRegister(30400 , writable: true)] private UInt32 _BatteryMaxChargePower; // [HoldingRegister(30402 , writable: true)] private UInt32 _BatteryMaxDischargePower; // - [HoldingRegister(30404, writable: true)] private UInt16 _ChargeCutoffSoc; // - [HoldingRegister(30405, writable: true)] private UInt16 _DischargeCutoffSoc; // - [HoldingRegister(30406, writable: true)] private UInt16 _LoadPriorityDischargeCutoffSoc; // - [HoldingRegister(30407, writable: true)] private Boolean _RemotePowerControl; // - [HoldingRegister(30408, writable: true)] private UInt16 _RemotePowerControlChargeDuration; // - [HoldingRegister(30409, writable: true)] private Int16 _RemoteChargDischargePower; // - [HoldingRegister(30410, writable: true)] private Boolean _AcChargeEnable; // + [HoldingRegister(30404, writable: true)] private UInt16 _maxSoc; // + [HoldingRegister(30405, writable: true)] private UInt16 _minSoc; // + [HoldingRegister(30406, writable: true)] private UInt16 _LoadPriorityDischargeCutoffSoc; // + [HoldingRegister(30407, writable: true)] private Boolean _RemotePowerControl; // + [HoldingRegister(30408, writable: true)] private UInt16 _RemotePowerControlChargeDuration; // + [HoldingRegister(30409, writable: true)] private Int16 _RemoteChargDischargePower; // + [HoldingRegister(30410, writable: true)] private Boolean _AcChargeEnable; // [HoldingRegister(30474)] private UInt16 _ActualChargeDischargePowerControlValue; // [HoldingRegister(30475)] private UInt16 _OffGridDischargeCutoffSoc; // [HoldingRegister(30496, writable: true, Scale = 0.1)] private UInt16 _BatteryChargeCutoffVoltage; // [HoldingRegister(30497, writable: true, Scale = 0.1)] private UInt16 _BatteryDischargeCutoffVoltage; // - [HoldingRegister(30498, writable: true, Scale = 0.1)] private UInt16 _BatteryMaxChargeCurrent; // - [HoldingRegister(30499, writable: true, Scale = 0.1)] private UInt16 _BatteryMaxdischargeCurrent; //*/ + [HoldingRegister(30498, writable: true, Scale = 0.1)] private UInt16 _batteryMaxChargingCurrent; // + [HoldingRegister(30499, writable: true, Scale = 0.1)] private UInt16 _batteryMaxDischargingCurrent; //*/ } \ No newline at end of file From ca08d8d6deb7f515213d798f3e3e695adaa1d4cf Mon Sep 17 00:00:00 2001 From: atef Date: Tue, 18 Nov 2025 02:29:51 +0100 Subject: [PATCH 18/23] update to max pv production --- csharp/App/SodiStoreMax/src/AggregationService/Aggregator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/csharp/App/SodiStoreMax/src/AggregationService/Aggregator.cs b/csharp/App/SodiStoreMax/src/AggregationService/Aggregator.cs index 19bb623a5..0699adcb1 100644 --- a/csharp/App/SodiStoreMax/src/AggregationService/Aggregator.cs +++ b/csharp/App/SodiStoreMax/src/AggregationService/Aggregator.cs @@ -295,7 +295,7 @@ public static class Aggregator ChargingBatteryPower = batteryChargePower.Any() ? batteryChargePower.Average() : 0.0, GridExportPower = gridPowerExport.Any() ? gridPowerExport.Sum() : 0.0, GridImportPower = gridPowerImport.Any() ? gridPowerImport.Sum() : 0.0, - PvPower = pvPower.Any() ? pvPower.Last() : 0.0, + PvPower = pvPower.Any() ? pvPower.Max() : 0.0, HeatingPower = heatingPowerAvg.Any() ? heatingPowerAvg.Average() : 0.0, }; From 29166e1adae07e95662d39558eb193ecf898d96a Mon Sep 17 00:00:00 2001 From: atef Date: Tue, 18 Nov 2025 02:30:03 +0100 Subject: [PATCH 19/23] delete unecessary lines --- csharp/App/GrowattCommunication/Program.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/csharp/App/GrowattCommunication/Program.cs b/csharp/App/GrowattCommunication/Program.cs index 7292eb38d..62c6cffc6 100644 --- a/csharp/App/GrowattCommunication/Program.cs +++ b/csharp/App/GrowattCommunication/Program.cs @@ -129,9 +129,6 @@ public static class Program SendSalimaxStateAlarm(GetSodiHomeStateAlarm(statusrecord),statusrecord); - //await DataLogging(statusrecord, timestamp); // save a csv file locally - //await SaveModbusTcpFile(statusrecord); // save the JSON file for modbuscTCP - Debug.Assert(statusrecord != null, nameof(statusrecord) + " != null"); statusrecord.InverterRecord.RemotePowerControl.WriteLine(" = RemotePowerControl"); statusrecord.InverterRecord.EnableEmsCommunicationFailureTime.WriteLine(" = EnableEmsCommunicationFailureTime"); From 5db53006c3039e01b0f9a6b86d3e8cba7ca683c7 Mon Sep 17 00:00:00 2001 From: atef Date: Tue, 18 Nov 2025 02:30:28 +0100 Subject: [PATCH 20/23] fix working mode and all specfic data --- csharp/Sinexcel 12K TL/SinexcelRecord.Api.cs | 650 +++++++++--------- .../Sinexcel 12K TL/SinexcelRecord.Modbus.cs | 101 +-- 2 files changed, 392 insertions(+), 359 deletions(-) diff --git a/csharp/Sinexcel 12K TL/SinexcelRecord.Api.cs b/csharp/Sinexcel 12K TL/SinexcelRecord.Api.cs index 50905a5b7..9e97fa23c 100644 --- a/csharp/Sinexcel 12K TL/SinexcelRecord.Api.cs +++ b/csharp/Sinexcel 12K TL/SinexcelRecord.Api.cs @@ -215,268 +215,269 @@ public partial class SinexcelRecord public ApparentPower RatedPowerKva => _ratedPower / 100f; // 0x2008 (value / 100) public SinexcelModel Model => (SinexcelModel)_model; // 0x200D - public ThreePhaseWireSystem ThreePhaseWireSystem - { - get => (ThreePhaseWireSystem)_threePhaseWireSystem; - set => _threePhaseWireSystem = (UInt32)value; - } - - public InputFrequency InputFrequencyClass - { - get => (InputFrequency)_inputFrequencyClass; - set => _inputFrequencyClass = (UInt32)value; - } - + //public ThreePhaseWireSystem ThreePhaseWireSystem + //{ + // get => (ThreePhaseWireSystem)_threePhaseWireSystem; + // //set => _threePhaseWireSystem = (UInt32)value; + //} +// + //public InputFrequency InputFrequencyClass + //{ + // get => (InputFrequency)_inputFrequencyClass; + // //set => _inputFrequencyClass = (UInt32)value; + //} +// public WorkingMode WorkingMode { - get => (WorkingMode)ConvertBitPatternToFloat(_workingMode); - set => _workingMode = (UInt32)value; + get => (WorkingMode)(int)BitConverter.Int32BitsToSingle(unchecked((int)_workingMode)); + set => _workingMode = BitConverter.ToUInt32(BitConverter.GetBytes((float)value), 0); } - public GridSwitchMethod GridSwitchMethod - { - get => (GridSwitchMethod)_methodSwitchMode; - set => _methodSwitchMode = (UInt32)value; - } - - // ─────────────────────────────────────────────── - // Inverter Control Configuration - // ─────────────────────────────────────────────── - public SinexcelUnbalanceCompensation EnableOnGridUnbalanceCompensation - { - get => (SinexcelUnbalanceCompensation)_enableOnGridUnbalanceCompensation; - set => _enableOnGridUnbalanceCompensation = (UInt32)value; - } - - public SinexcelTemperatureDrop TemperatureDrop - { - get => (SinexcelTemperatureDrop)ConvertBitPatternToFloat(_temperatureDrop); - set => _temperatureDrop = (UInt32)value; - } - - public SinexcelHvrt Hvrt - { - get => (SinexcelHvrt)_hvrt; - set => _hvrt = (UInt32)value; - } - - public SinexcelLvrt Lvrt - { - get => (SinexcelLvrt)_lvrt; - set => _lvrt = (UInt32)value; - } - -// Fan Gear — numeric [0.5 ~ 1.0], default 1.0 - public float FanGear - { - get => BitConverter.Int32BitsToSingle(unchecked((Int32)_fanGear)); - set => _fanGear = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); - } - - // ─────────────────────────────────────────────── - // Battery Configuration - // ─────────────────────────────────────────────── - public SinexcelBatteryAccessMethod BatteryAccessMethod - { - get => (SinexcelBatteryAccessMethod)_batteryAccessMethod; - set => _batteryAccessMethod = (UInt32)value; - } - - public SinexcelMeterAccessEnable MeterAccessEnable - { - get => (SinexcelMeterAccessEnable)ConvertBitPatternToFloat(_meterAccessEnable); - set => _meterAccessEnable = (UInt32)value; - } - - public SinexcelBatteryEnable EnableBattery1 - { - get => (SinexcelBatteryEnable)ConvertBitPatternToFloat(_enableBattery1); - set => _enableBattery1 = (UInt32)value; - } - - public SinexcelBatteryEnable EnableBattery2 - { - get => (SinexcelBatteryEnable)ConvertBitPatternToFloat(_enableBattery2); - set => _enableBattery2 = (UInt32)value; - } - - public SinexcelPvEnable EnablePv1 - { - get => (SinexcelPvEnable)ConvertBitPatternToFloat(_enablePv1); - set => _enablePv1 = (UInt32)value; - } - - public SinexcelPvEnable EnablePv2 - { - get => (SinexcelPvEnable)ConvertBitPatternToFloat(_enablePv2); - set => _enablePv2 = (UInt32)value; - } - - public SinexcelBatteryType BatteryType - { - get => (SinexcelBatteryType)_batteryType; - set => _batteryType = (UInt32)value; - } - - public float BatteryCapacity1 - { - get => BitConverter.Int32BitsToSingle(unchecked((Int32)_batteryCapacity1)); - set => _batteryCapacity1 = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); - } - - public float Battery1MaxChargingCurrent - { - get => BitConverter.Int32BitsToSingle(unchecked((Int32)_maxChargingCurrentBattery1)); - set => _maxChargingCurrentBattery1 = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); - } - - public float Battery1MaxDischargingCurrent - { - get => BitConverter.Int32BitsToSingle(unchecked((Int32)_maxDischargingCurrentBattery1)); - set => _maxDischargingCurrentBattery1 = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); - } - - public float RatedBatteryVoltage1 - { - get => BitConverter.Int32BitsToSingle(unchecked((Int32)_ratedBatteryVoltage1)); - set => _ratedBatteryVoltage1 = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); - } - - public float Battery1MinSoc - { - get => BitConverter.Int32BitsToSingle(unchecked((Int32)_minSocBattery1)); - set => _minSocBattery1 = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); - } - - public float SetValueBattery1 - { - get => BitConverter.Int32BitsToSingle(unchecked((Int32)_setValueBattery1)); - set => _setValueBattery1 = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); - } - - public SinexcelActiveChargeDischarge ActiveChargeDischarge - { - get => (SinexcelActiveChargeDischarge)ConvertBitPatternToFloat(_activeChargeDischarge); - set => _activeChargeDischarge = (UInt32)value; - } - - public float ActiveChargeDischargePower - { - get => ConvertBitPatternToFloat(_activeChargeDischargePower) * _factorFromKwtoW; - set => _activeChargeDischargePower = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); // we should check this may need to convert from W to KW - } - - // ─────────────────────────────────────────────── - // Protection & PV Mode - // ─────────────────────────────────────────────── - public SinexcelIslandProtection EnableIslandProtection - { - get => (SinexcelIslandProtection)_enableIslandProtection; - set => _enableIslandProtection = (UInt32)value; - } - - public SinexcelPvAccessMode PvAccessMode - { - get => (SinexcelPvAccessMode)_pvAccessMode; - set => _pvAccessMode = (UInt32)value; - } - - // ─────────────────────────────────────────────── - // System-Level Parameters - // ─────────────────────────────────────────────── - public float OutputVoltageAdjustmentFactor - { - get => BitConverter.Int32BitsToSingle(unchecked((int)_outputVoltageAdjustmentFactor)); - set => _outputVoltageAdjustmentFactor = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); - } - - public float SetValueBatteryUndervoltage1 - { - get => BitConverter.Int32BitsToSingle(unchecked((int)_setValueBatteryUndervoltage1)); - set => _setValueBatteryUndervoltage1 = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); - } - - public float InverterPowerLimit - { - get => BitConverter.Int32BitsToSingle(unchecked((int)_inverterPowerLimit)); - set => _inverterPowerLimit = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); - } - - // ─────────────────────────────────────────────── - // Battery 2 Parameters - // ─────────────────────────────────────────────── - public float Battery2Capacity - { - get => BitConverter.Int32BitsToSingle(unchecked((int)_battery2Capacity)); - set => _battery2Capacity = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); - } - - public float Battery2MaxChargingCurrent - { - get => BitConverter.Int32BitsToSingle(unchecked((int)_maxChargingCurrentBattery2)); - set => _maxChargingCurrentBattery2 = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); - } - - public float Battery2MaxDischargingCurrent - { - get => BitConverter.Int32BitsToSingle(unchecked((int)_maxDischargingCurrentBattery2)); - set => _maxDischargingCurrentBattery2 = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); - } - - public float Battery2RatedVoltage - { - get => BitConverter.Int32BitsToSingle(unchecked((int)_battery2RatedVoltage)); - set => _battery2RatedVoltage = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); - } - - public float Battery2MinSoc - { - get => BitConverter.Int32BitsToSingle(unchecked((int)_battery2MinSoc)); - set => _battery2MinSoc = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); - } - - public float Battery2OverVoltageSetting - { - get => BitConverter.Int32BitsToSingle(unchecked((int)_battery2OverVoltageSetting)); - set => _battery2OverVoltageSetting = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); - } - - public float Battery2UnderVoltageSetpoint - { - get => BitConverter.Int32BitsToSingle(unchecked((int)_battery2UnderVoltageSetpoint)); - set => _battery2UnderVoltageSetpoint = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); - } - - // ─────────────────────────────────────────────── - // Parallel / System Settings - // ─────────────────────────────────────────────── - public SinexcelMachineMode MachineMode - { - get => (SinexcelMachineMode)ConvertBitPatternToFloat(_singleOrParallelMachine); - set => _singleOrParallelMachine = (UInt32)value; - } - - public UInt32 NumberOfSystemModules - { - get => (UInt32)ConvertBitPatternToFloat(_numberOfSystemModules); - set => _numberOfSystemModules = value; - } - - public UInt32 ParallelModuleMachineNumber - { - get => (UInt32)ConvertBitPatternToFloat(_parallelModuleMachineNumber); - set => _parallelModuleMachineNumber = value; - } - - public AccreditedCountry AccreditedCountry - { - get => (AccreditedCountry)ConvertBitPatternToFloat(_accreditedCountries); - set => _accreditedCountries = (UInt32)value; - } - - // ─────────────────────────────────────────────── - // Control Commands - // ─────────────────────────────────────────────── +// + //public GridSwitchMethod GridSwitchMethod + //{ + // get => (GridSwitchMethod)_methodSwitchMode; + // //set => _methodSwitchMode = (UInt32)value; + //} +// + //// ─────────────────────────────────────────────── + //// Inverter Control Configuration + //// ─────────────────────────────────────────────── + //public SinexcelUnbalanceCompensation EnableOnGridUnbalanceCompensation + //{ + // get => (SinexcelUnbalanceCompensation)_enableOnGridUnbalanceCompensation; + // //set => _enableOnGridUnbalanceCompensation = (UInt32)value; + //} +// + //public SinexcelTemperatureDrop TemperatureDrop + //{ + // get => (SinexcelTemperatureDrop)ConvertBitPatternToFloat(_temperatureDrop); + // //set => _temperatureDrop = (UInt32)value; + //} +// + //public SinexcelHvrt Hvrt + //{ + // get => (SinexcelHvrt)_hvrt; + // //set => _hvrt = (UInt32)value; + //} +// + //public SinexcelLvrt Lvrt + //{ + // get => (SinexcelLvrt)_lvrt; + // //set => _lvrt = (UInt32)value; + //} +// +// F//an Gear — numeric [0.5 ~ 1.0], default 1.0 + //public float FanGear + //{ + // get => BitConverter.Int32BitsToSingle(unchecked((Int32)_fanGear)); + // //set => _fanGear = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); + //} +// + //// ─────────────────────────────────────────────── + //// Battery Configuration + //// ─────────────────────────────────────────────── + //public SinexcelBatteryAccessMethod BatteryAccessMethod + //{ + // get => (SinexcelBatteryAccessMethod)_batteryAccessMethod; + // //set => _batteryAccessMethod = (UInt32)value; + //} + // + //public SinexcelMeterAccessEnable MeterAccessEnable + //{ + // get => (SinexcelMeterAccessEnable)ConvertBitPatternToFloat(_meterAccessEnable); + // //set => _meterAccessEnable = (UInt32)value; + //} + // + //public SinexcelBatteryEnable EnableBattery1 + //{ + // get => (SinexcelBatteryEnable)ConvertBitPatternToFloat(_enableBattery1); + // //set => _enableBattery1 = (UInt32)value; + //} + // + //public SinexcelBatteryEnable EnableBattery2 + //{ + // get => (SinexcelBatteryEnable)ConvertBitPatternToFloat(_enableBattery2); + // //set => _enableBattery2 = (UInt32)value; + //} + // + //public SinexcelPvEnable EnablePv1 + //{ + // get => (SinexcelPvEnable)ConvertBitPatternToFloat(_enablePv1); + // //set => _enablePv1 = (UInt32)value; + //} + // + //public SinexcelPvEnable EnablePv2 + //{ + // get => (SinexcelPvEnable)ConvertBitPatternToFloat(_enablePv2); + // //set => _enablePv2 = (UInt32)value; + //} + // + //public SinexcelBatteryType BatteryType + //{ + // get => (SinexcelBatteryType)_batteryType; + // //set => _batteryType = (UInt32)value; + //} + // + //public float BatteryCapacity1 + //{ + // get => BitConverter.Int32BitsToSingle(unchecked((Int32)_batteryCapacity1)); + // //set => _batteryCapacity1 = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); + //} + // + //public float Battery1MaxChargingCurrent + //{ + // get => BitConverter.Int32BitsToSingle(unchecked((Int32)_maxChargingCurrentBattery1)); + // //set => _maxChargingCurrentBattery1 = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); + //} + // + //public float Battery1MaxDischargingCurrent + //{ + // get => BitConverter.Int32BitsToSingle(unchecked((Int32)_maxDischargingCurrentBattery1)); + // //set => _maxDischargingCurrentBattery1 = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); + //} + // + //public float RatedBatteryVoltage1 + //{ + // get => BitConverter.Int32BitsToSingle(unchecked((Int32)_ratedBatteryVoltage1)); + // //set => _ratedBatteryVoltage1 = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); + //} + // + //public float Battery1MinSoc + //{ + // get => BitConverter.Int32BitsToSingle(unchecked((Int32)_minSocBattery1)); + // //set => _minSocBattery1 = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); + //} + // + //public float SetValueBattery1 + //{ + // get => BitConverter.Int32BitsToSingle(unchecked((Int32)_setValueBattery1)); + // //set => _setValueBattery1 = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); + //} + // + //public SinexcelActiveChargeDischarge ActiveChargeDischarge + //{ + // get => (SinexcelActiveChargeDischarge)ConvertBitPatternToFloat(_activeChargeDischarge); + // //set => _activeChargeDischarge = (UInt32)value; + //} + // + //public float ActiveChargeDischargePower + //{ + // get => ConvertBitPatternToFloat(_activeChargeDischargePower) * _factorFromKwtoW; + // //set => _activeChargeDischargePower = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); // we should check this may need to convert from W to KW + //} + // + //// ─────────────────────────────────────────────── + //// Protection & PV Mode + //// ─────────────────────────────────────────────── + //public SinexcelIslandProtection EnableIslandProtection + //{ + // get => (SinexcelIslandProtection)_enableIslandProtection; + // //set => _enableIslandProtection = (UInt32)value; + //} + // + //public SinexcelPvAccessMode PvAccessMode + //{ + // get => (SinexcelPvAccessMode)_pvAccessMode; + // //set => _pvAccessMode = (UInt32)value; + //} + // + //// ─────────────────────────────────────────────── + //// System-Level Parameters + //// ─────────────────────────────────────────────── + //public float OutputVoltageAdjustmentFactor + //{ + // get => BitConverter.Int32BitsToSingle(unchecked((int)_outputVoltageAdjustmentFactor)); + // //set => _outputVoltageAdjustmentFactor = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); + //} + // + //public float SetValueBatteryUndervoltage1 + //{ + // get => BitConverter.Int32BitsToSingle(unchecked((int)_setValueBatteryUndervoltage1)); + // //set => _setValueBatteryUndervoltage1 = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); + //} + // + //public float InverterPowerLimit + //{ + // get => BitConverter.Int32BitsToSingle(unchecked((int)_inverterPowerLimit)); + // //set => _inverterPowerLimit = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); + //} + // + //// ─────────────────────────────────────────────── + //// Battery 2 Parameters + //// ─────────────────────────────────────────────── + //public float Battery2Capacity + //{ + // get => BitConverter.Int32BitsToSingle(unchecked((int)_battery2Capacity)); + // //set => _battery2Capacity = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); + //} +// + //public float Battery2MaxChargingCurrent + //{ + // get => BitConverter.Int32BitsToSingle(unchecked((int)_maxChargingCurrentBattery2)); + // //set => _maxChargingCurrentBattery2 = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); + //} + // + //public float Battery2MaxDischargingCurrent + //{ + // get => BitConverter.Int32BitsToSingle(unchecked((int)_maxDischargingCurrentBattery2)); + // //set => _maxDischargingCurrentBattery2 = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); + //} + // + //public float Battery2RatedVoltage + //{ + // get => BitConverter.Int32BitsToSingle(unchecked((int)_battery2RatedVoltage)); + // //set => _battery2RatedVoltage = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); + //} + // + //public float Battery2MinSoc + //{ + // get => BitConverter.Int32BitsToSingle(unchecked((int)_battery2MinSoc)); + // //set => _battery2MinSoc = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); + //} + // + //public float Battery2OverVoltageSetting + //{ + // get => BitConverter.Int32BitsToSingle(unchecked((int)_battery2OverVoltageSetting)); + // //set => _battery2OverVoltageSetting = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); + //} + // + //public float Battery2UnderVoltageSetpoint + //{ + // get => BitConverter.Int32BitsToSingle(unchecked((int)_battery2UnderVoltageSetpoint)); + // //set => _battery2UnderVoltageSetpoint = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); + //} + // + //// ─────────────────────────────────────────────── + //// Parallel / System Settings + //// ─────────────────────────────────────────────── + //public SinexcelMachineMode MachineMode + //{ + // get => (SinexcelMachineMode)ConvertBitPatternToFloat(_singleOrParallelMachine); + // //set => _singleOrParallelMachine = (UInt32)value; + //} + // + //public UInt32 NumberOfSystemModules + //{ + // get => (UInt32)ConvertBitPatternToFloat(_numberOfSystemModules); + // //set => _numberOfSystemModules = value; + //} + // + //public UInt32 ParallelModuleMachineNumber + //{ + // get => (UInt32)ConvertBitPatternToFloat(_parallelModuleMachineNumber); + // //set => _parallelModuleMachineNumber = value; + //} + // + //public AccreditedCountry AccreditedCountry + //{ + // get => (AccreditedCountry)ConvertBitPatternToFloat(_accreditedCountries); + // //set => _accreditedCountries = (UInt32)value; + //} + // + //// ─────────────────────────────────────────────── + //// Control Commands + //// ─────────────────────────────────────────────── public float PowerOn { get => BitConverter.Int32BitsToSingle(unchecked((Int32)_powerOn)); @@ -488,39 +489,39 @@ public partial class SinexcelRecord get => BitConverter.Int32BitsToSingle(unchecked((Int32)_powerOff)); set => _powerOff = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); } - - public float FaultClearing - { - get => BitConverter.Int32BitsToSingle(unchecked((Int32)_faultClearing)); - set => _faultClearing = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); - } - - // ─────────────────────────────────────────────── - // Meter & Battery Control - // ─────────────────────────────────────────────── - public float MeterReverseManualDetection - { - get => BitConverter.Int32BitsToSingle(unchecked((Int32)_meterReverseManualDetection)); - set => _meterReverseManualDetection = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); - } - - public SinexcelBatteryRating BatteryVoltageRating - { - get => (SinexcelBatteryRating)ConvertBitPatternToFloat(_batteryVoltageRating); - set => _batteryVoltageRating = (UInt32)value; - } - - public float Battery1Activation - { - get => BitConverter.Int32BitsToSingle(unchecked((Int32)_battery1Activation)); - set => _battery1Activation = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); - } - - public float Battery2Activation - { - get => BitConverter.Int32BitsToSingle(unchecked((Int32)_battery2Activation)); - set => _battery2Activation = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); - } +// + //public float FaultClearingf + //{ + // get => BitConverter.Int32BitsToSingle(unchecked((Int32)_faultClearing)); + // // //set => _faultClearing = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); + //} +// + //// ─────────────────────────────────────────────── + //// Meter & Battery Control + //// ─────────────────────────────────────────────── + //public float MeterReverseManualDetection + //{ + // get => BitConverter.Int32BitsToSingle(unchecked((Int32)_meterReverseManualDetection)); + // //set => _meterReverseManualDetection = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); + //} +// + //public SinexcelBatteryRating BatteryVoltageRating + //{ + // get => (SinexcelBatteryRating)ConvertBitPatternToFloat(_batteryVoltageRating); + // //set => _batteryVoltageRating = (UInt32)value; + //} +// + //public float Battery1Activation + //{ + // get => BitConverter.Int32BitsToSingle(unchecked((Int32)_battery1Activation)); + // //set => _battery1Activation = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); + //} +// + //public float Battery2Activation + //{ + // get => BitConverter.Int32BitsToSingle(unchecked((Int32)_battery2Activation)); + // //set => _battery2Activation = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); + //} // ─────────────────────────────────────────────── // Electric Meter Operating State @@ -589,7 +590,7 @@ public partial class SinexcelRecord get => ElectricMeterAPhaseActivePower + ElectricMeterBPhaseActivePower + ElectricMeterCPhaseActivePower; } - /* + // Energy (kW·h) public Energy Battery1TotalChargingEnergy => BitConverter.Int32BitsToSingle(unchecked((Int32)_batteryCab1TotalChargingEnergy)); // 0xB0FC public Energy Battery1TotalDischargedEnergy => BitConverter.Int32BitsToSingle(unchecked((Int32)_batteryCab1TotalDischargedEnergy)); // 0xB0FE @@ -611,46 +612,66 @@ public partial class SinexcelRecord public Temperature Battery2Socsecondvalue => BitConverter.Int32BitsToSingle(unchecked((Int32)_batteryCab2Soc)); // 0xB206 % public Percent Battery2Soh => BitConverter.Int32BitsToSingle(unchecked((Int32)_batteryCab2Soh)); // 0xB208 % + // Repetitive-week mask (bit-mapped 0–6 = Sun–Sat) + //public float TimedChargeAndDischargeOff + //{ + // get => BitConverter.Int32BitsToSingle(unchecked((Int32)_Timed_Charge_and_Discharge_Off)) ; // only 7 bits used + // //set => _Timed_Charge_and_Discharge_Off = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); + //} + // Repetitive-week mask (bit-mapped 0–6 = Sun–Sat) + /* public float OtherTimePeriodMode + { + get => _Other_time_period_mode ; // only 7 bits used + //set => _Other_time_period_mode = (UInt32)value; + }*/ + + + // Repetitive-week mask (bit-mapped 0–6 = Sun–Sat) public SinexcelWeekDays RepetitiveWeeks { get => (SinexcelWeekDays)(_repetitiveWeeks & 0x7F); // only 7 bits used - set => _repetitiveWeeks = (UInt32)value; + set => _repetitiveWeeks = (UInt32)value; } - +// // Effective start / end as UNIX timestamps public DateTime EffectiveStartDate { get => DateTimeOffset.FromUnixTimeSeconds(_effectiveStartDate).DateTime; - set => _effectiveStartDate = (UInt32)new DateTimeOffset(value).ToUnixTimeSeconds(); + set => _effectiveStartDate = (UInt32)new DateTimeOffset(value).ToUnixTimeSeconds(); } - +// public DateTime EffectiveEndDate { get => DateTimeOffset.FromUnixTimeSeconds(_effectiveEndDate).DateTime; - set => _effectiveEndDate = (UInt32)new DateTimeOffset(value).ToUnixTimeSeconds(); + set => _effectiveEndDate = (UInt32)new DateTimeOffset(value).ToUnixTimeSeconds(); } - - // Charging power during time period 1 (kW) +// // Charging power during time period 1 (kW) public float ChargingPowerPeriod1 { get => BitConverter.Int32BitsToSingle(unchecked((Int32)_chargingPowerPeriod1)); - set => _chargingPowerPeriod1 = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); + set => _chargingPowerPeriod1 = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); } + public float DishargingPowerPeriod1 + { + get => BitConverter.Int32BitsToSingle(unchecked((Int32)_dischargingPowerPeriod1)); + set => _dischargingPowerPeriod1 = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); + } + + // ─────────────────────────────────────────────── // Charging period 1 // ─────────────────────────────────────────────── public TimeSpan ChargeStartTimePeriod1 { get => ConvertToTimeSpan(_chargeStartTimePeriod1); - set => _chargeStartTimePeriod1 = ConvertFromTimeSpan(value); + set => _chargeStartTimePeriod1 = ConvertFromTimeSpan(value); } - public TimeSpan ChargeEndTimePeriod1 { get => ConvertToTimeSpan(_chargeEndTimePeriod1); - set => _chargeEndTimePeriod1 = ConvertFromTimeSpan(value); + set => _chargeEndTimePeriod1 = ConvertFromTimeSpan(value); } // ─────────────────────────────────────────────── @@ -659,34 +680,41 @@ public partial class SinexcelRecord public TimeSpan DischargeStartTimePeriod1 { get => ConvertToTimeSpan(_dischargeStartTimePeriod1); - set => _dischargeStartTimePeriod1 = ConvertFromTimeSpan(value); + set => _dischargeStartTimePeriod1 = ConvertFromTimeSpan(value); } - +// public TimeSpan DischargeEndTimePeriod1 { get => ConvertToTimeSpan(_dischargeEndTimePeriod1); - set => _dischargeEndTimePeriod1 = ConvertFromTimeSpan(value); - }*/ + set => _dischargeEndTimePeriod1 = ConvertFromTimeSpan(value); + } // ─────────────────────────────────────────────── // Conversion methodes // ─────────────────────────────────────────────── - private static TimeSpan ConvertToTimeSpan(UInt32 value) + private static TimeSpan ConvertToTimeSpan(UInt32 raw) { - int hhmm = (int)value; + // raw is IEEE754 float bits, e.g. 0x44640000 for 912.0 + float f = BitConverter.Int32BitsToSingle(unchecked((int)raw)); + int hhmm = (int)Math.Round(f); + int hours = hhmm / 100; int minutes = hhmm % 100; + return new TimeSpan(hours, minutes, 0); } private static UInt32 ConvertFromTimeSpan(TimeSpan time) { - int hhmm = time.Hours * 100 + time.Minutes; - return (UInt32)hhmm; + int hhmm = time.Hours * 100 + time.Minutes; // e.g. 9*100+12 = 912 + float f = (float)hhmm; // 912.0f + // convert float to raw IEEE754 bits + return BitConverter.ToUInt32(BitConverter.GetBytes(f), 0); // e.g. 0x44640000 } + private static Single IntToFloat(Int32 intValue) { byte[] intBytes = BitConverter.GetBytes(intValue); diff --git a/csharp/Sinexcel 12K TL/SinexcelRecord.Modbus.cs b/csharp/Sinexcel 12K TL/SinexcelRecord.Modbus.cs index 09f952c25..39197e941 100644 --- a/csharp/Sinexcel 12K TL/SinexcelRecord.Modbus.cs +++ b/csharp/Sinexcel 12K TL/SinexcelRecord.Modbus.cs @@ -226,70 +226,70 @@ public partial class SinexcelRecord // ─────────────────────────────────────────────── // System configuration / operation registers // ─────────────────────────────────────────────── - [HoldingRegister(12294, writable: true)] private UInt32 _threePhaseWireSystem; // 0x3006 - [HoldingRegister(12296, writable: true)] private UInt32 _remainnotused; // 0x3008 - [HoldingRegister(12298, writable: true)] private UInt32 _inputFrequencyClass; // 0x300A + //[HoldingRegister(12294, writable: true)] private UInt32 _threePhaseWireSystem; // 0x3006 + //[HoldingRegister(12296, writable: true)] private UInt32 _remainnotused; // 0x3008 + //[HoldingRegister(12298, writable: true)] private UInt32 _inputFrequencyClass; // 0x300A [HoldingRegister(12300, writable: true)] private UInt32 _workingMode; // 0x300C - [HoldingRegister(12302, writable: true)] private UInt32 _methodSwitchMode; // 0x300E + //[HoldingRegister(12302, writable: true)] private UInt32 _methodSwitchMode; // 0x300E // ─────────────────────────────────────────────── // Inverter Control and Protection Configuration // ─────────────────────────────────────────────── - [HoldingRegister(12304, writable: true)] private UInt32 _enableOnGridUnbalanceCompensation; // 0x3010 - [HoldingRegister(12306, writable: true)] private UInt32 _temperatureDrop; // 0x3012 - [HoldingRegister(12308, writable: true)] private UInt32 _hvrt; // 0x3014 - [HoldingRegister(12310, writable: true)] private UInt32 _lvrt; // 0x3016 - [HoldingRegister(12312, writable: true)] private UInt32 _fanGear; // 0x3018 + //[HoldingRegister(12304, writable: true)] private UInt32 _enableOnGridUnbalanceCompensation; // 0x3010 + //[HoldingRegister(12306, writable: true)] private UInt32 _temperatureDrop; // 0x3012 + //[HoldingRegister(12308, writable: true)] private UInt32 _hvrt; // 0x3014 + //[HoldingRegister(12310, writable: true)] private UInt32 _lvrt; // 0x3016 + //[HoldingRegister(12312, writable: true)] private UInt32 _fanGear; // 0x3018 // ─────────────────────────────────────────────── // Battery & PV Configuration // ─────────────────────────────────────────────── - [HoldingRegister(12336, writable: true)] private UInt32 _batteryAccessMethod; // 0x3030 - [HoldingRegister(12338, writable: true)] private UInt32 _meterAccessEnable; // 0x3032 - [HoldingRegister(12340, writable: true)] private UInt32 _enableBattery1; // 0x3034 - [HoldingRegister(12342, writable: true)] private UInt32 _enableBattery2; // 0x3036 - [HoldingRegister(12344, writable: true)] private UInt32 _enablePv1; // 0x3038 - [HoldingRegister(12346, writable: true)] private UInt32 _enablePv2; // 0x303A - [HoldingRegister(12348, writable: true)] private UInt32 _batteryType; // 0x303C - [HoldingRegister(12350, writable: true)] private UInt32 _batteryCapacity1; // 0x303E - [HoldingRegister(12352, writable: true)] private UInt32 _maxChargingCurrentBattery1; // 0x3040 - [HoldingRegister(12354, writable: true)] private UInt32 _maxDischargingCurrentBattery1; // 0x3042 - [HoldingRegister(12362, writable: true)] private UInt32 _ratedBatteryVoltage1; // 0x304A - [HoldingRegister(12358, writable: true)] private UInt32 _minSocBattery1; // 0x3046 - [HoldingRegister(12360, writable: true)] private UInt32 _setValueBattery1; // 0x3048 - [HoldingRegister(12364, writable: true)] private UInt32 _activeChargeDischarge; // 0x304A - [HoldingRegister(12366, writable: true)] private UInt32 _activeChargeDischargePower; // 0x304C - [HoldingRegister(12380, writable: true)] private UInt32 _enableIslandProtection; // 0x305C - [HoldingRegister(12382, writable: true)] private UInt32 _pvAccessMode; // 0x305E + //[HoldingRegister(12336, writable: true)] private UInt32 _batteryAccessMethod; // 0x3030 + //[HoldingRegister(12338, writable: true)] private UInt32 _meterAccessEnable; // 0x3032 + //[HoldingRegister(12340, writable: true)] private UInt32 _enableBattery1; // 0x3034 + //[HoldingRegister(12342, writable: true)] private UInt32 _enableBattery2; // 0x3036 + //[HoldingRegister(12344, writable: true)] private UInt32 _enablePv1; // 0x3038 + //[HoldingRegister(12346, writable: true)] private UInt32 _enablePv2; // 0x303A + //[HoldingRegister(12348, writable: true)] private UInt32 _batteryType; // 0x303C + //[HoldingRegister(12350, writable: true)] private UInt32 _batteryCapacity1; // 0x303E + //[HoldingRegister(12352, writable: true)] private UInt32 _maxChargingCurrentBattery1; // 0x3040 + //[HoldingRegister(12354, writable: true)] private UInt32 _maxDischargingCurrentBattery1; // 0x3042 + //[HoldingRegister(12362, writable: true)] private UInt32 _ratedBatteryVoltage1; // 0x304A + //[HoldingRegister(12358, writable: true)] private UInt32 _minSocBattery1; // 0x3046 + //[HoldingRegister(12360, writable: true)] private UInt32 _setValueBattery1; // 0x3048 + //[HoldingRegister(12364, writable: true)] private UInt32 _activeChargeDischarge; // 0x304A + //[HoldingRegister(12366, writable: true)] private UInt32 _activeChargeDischargePower; // 0x304C + //[HoldingRegister(12380, writable: true)] private UInt32 _enableIslandProtection; // 0x305C + //[HoldingRegister(12382, writable: true)] private UInt32 _pvAccessMode; // 0x305E // ─────────────────────────────────────────────── // System & Battery-2 Configuration // ─────────────────────────────────────────────── - [HoldingRegister(12384, writable: true)] private UInt32 _outputVoltageAdjustmentFactor; // 0x3060 - [HoldingRegister(12386, writable: true)] private UInt32 _setValueBatteryUndervoltage1; // 0x3062 - [HoldingRegister(12388, writable: true)] private UInt32 _inverterPowerLimit; // 0x3064 - [HoldingRegister(12400, writable: true)] private UInt32 _battery2Capacity; // 0x30B0 - [HoldingRegister(12402, writable: true)] private UInt32 _maxChargingCurrentBattery2; // 0x30B2 - [HoldingRegister(12404, writable: true)] private UInt32 _maxDischargingCurrentBattery2; // 0x30B4 - [HoldingRegister(12406, writable: true)] private UInt32 _battery2RatedVoltage; // 0x30B6 - [HoldingRegister(12408, writable: true)] private UInt32 _battery2MinSoc; // 0x30B8 - [HoldingRegister(12410, writable: true)] private UInt32 _battery2OverVoltageSetting; // 0x30BA - [HoldingRegister(12412, writable: true)] private UInt32 _battery2UnderVoltageSetpoint; // 0x30BC - - [HoldingRegister(12414, writable: true)] private UInt32 _singleOrParallelMachine; // 0x30BE - [HoldingRegister(12416, writable: true)] private UInt32 _numberOfSystemModules; // 0x30C0 - [HoldingRegister(12418, writable: true)] private UInt32 _parallelModuleMachineNumber; // 0x30C2 - [HoldingRegister(12420, writable: true)] private UInt32 _accreditedCountries; // 0x30C4 + //[HoldingRegister(12384, writable: true)] private UInt32 _outputVoltageAdjustmentFactor; // 0x3060 + //[HoldingRegister(12386, writable: true)] private UInt32 _setValueBatteryUndervoltage1; // 0x3062 + //[HoldingRegister(12388, writable: true)] private UInt32 _inverterPowerLimit; // 0x3064 + //[HoldingRegister(12400, writable: true)] private UInt32 _battery2Capacity; // 0x30B0 + //[HoldingRegister(12402, writable: true)] private UInt32 _maxChargingCurrentBattery2; // 0x30B2 + //[HoldingRegister(12404, writable: true)] private UInt32 _maxDischargingCurrentBattery2; // 0x30B4 + //[HoldingRegister(12406, writable: true)] private UInt32 _battery2RatedVoltage; // 0x30B6 + //[HoldingRegister(12408, writable: true)] private UInt32 _battery2MinSoc; // 0x30B8 + //[HoldingRegister(12410, writable: true)] private UInt32 _battery2OverVoltageSetting; // 0x30BA + //[HoldingRegister(12412, writable: true)] private UInt32 _battery2UnderVoltageSetpoint; // 0x30BC +// + //[HoldingRegister(12414, writable: true)] private UInt32 _singleOrParallelMachine; // 0x30BE + //[HoldingRegister(12416, writable: true)] private UInt32 _numberOfSystemModules; // 0x30C0 + //[HoldingRegister(12418, writable: true)] private UInt32 _parallelModuleMachineNumber; // 0x30C2 + //[HoldingRegister(12420, writable: true)] private UInt32 _accreditedCountries; // 0x30C4 // ─────────────────────────────────────────────── // System Control & Diagnostic Registers // ─────────────────────────────────────────────── [HoldingRegister(15426, writable: true)] private UInt32 _powerOn; // 0x3C42 [HoldingRegister(15428, writable: true)] private UInt32 _powerOff; // 0x3C44 - [HoldingRegister(15430, writable: true)] private UInt32 _faultClearing; // 0x3C46 - [HoldingRegister(15518, writable: true)] private UInt32 _meterReverseManualDetection; // 0x3C9E - [HoldingRegister(15520, writable: true)] private UInt32 _batteryVoltageRating; // 0x3CA0 - [HoldingRegister(15524, writable: true)] private UInt32 _battery1Activation; // 0x3CA4 - [HoldingRegister(15526, writable: true)] private UInt32 _battery2Activation; // 0x3CA6 + //[HoldingRegister(15430, writable: true)] private UInt32 _faultClearing; // 0x3C46 + //[HoldingRegister(15518, writable: true)] private UInt32 _meterReverseManualDetection; // 0x3C9E + //[HoldingRegister(15520, writable: true)] private UInt32 _batteryVoltageRating; // 0x3CA0 + //[HoldingRegister(15524, writable: true)] private UInt32 _battery1Activation; // 0x3CA4 + //[HoldingRegister(15526, writable: true)] private UInt32 _battery2Activation; // 0x3CA6 // ─────────────────────────────────────────────── @@ -308,7 +308,7 @@ public partial class SinexcelRecord [HoldingRegister(41234)] private UInt32 _electricMeterAPhaseActivePower; // 0xA112 [HoldingRegister(41236)] private UInt32 _electricMeterBPhaseActivePower; // 0xA114 [HoldingRegister(41238)] private UInt32 _electricMeterCPhaseActivePower; // 0xA116 - /* + // ─────────────────────────────────────────────── // Battery Cabinet 1 — Energy / Pack measurements (read-only) // ─────────────────────────────────────────────── @@ -336,10 +336,15 @@ public partial class SinexcelRecord // ─────────────────────────────────────────────── // Time-based scheduling (period 1) configuration // ─────────────────────────────────────────────── + + //[HoldingRegister(49328, writable: true)] private UInt32 _Timed_Charge_and_Discharge_Off; // 0xC0B4, bit flags Sun–Sat + //[HoldingRegister(49330, writable: true)] private UInt32 _Other_time_period_mode; // 0xC0B4, bit flags Sun–Sat [HoldingRegister(49332, writable: true)] private UInt32 _repetitiveWeeks; // 0xC0B4, bit flags Sun–Sat [HoldingRegister(49334, writable: true)] private UInt32 _effectiveStartDate; // 0xC0B6, UNIX timestamp (UTC+offset) [HoldingRegister(49336, writable: true)] private UInt32 _effectiveEndDate; // 0xC0B8, UNIX timestamp (UTC+offset) [HoldingRegister(49338, writable: true)] private UInt32 _chargingPowerPeriod1; // 0xC0BA, float [0–50] kW, default 5 + [HoldingRegister(49340, writable: true)] private UInt32 _dischargingPowerPeriod1; // 0xC0BA, float [0–50] kW, default 5 + // ─────────────────────────────────────────────── // Time-based charge/discharge schedule period 1 @@ -348,5 +353,5 @@ public partial class SinexcelRecord [HoldingRegister(49174, writable: true)] private UInt32 _chargeEndTimePeriod1; // 0xC016 [0-2359] (end) [HoldingRegister(49176, writable: true)] private UInt32 _dischargeStartTimePeriod1; // 0xC018 [0-2359] (start) [HoldingRegister(49178, writable: true)] private UInt32 _dischargeEndTimePeriod1; // 0xC01A [0-2359] (end) -*/ + } \ No newline at end of file From 79b75634019b70d6b56781b4899944a03c0dcfa4 Mon Sep 17 00:00:00 2001 From: atef Date: Tue, 18 Nov 2025 02:30:49 +0100 Subject: [PATCH 21/23] update program.cs to debug --- csharp/App/SinexcelCommunication/Program.cs | 99 +++++++++++++++++---- 1 file changed, 82 insertions(+), 17 deletions(-) diff --git a/csharp/App/SinexcelCommunication/Program.cs b/csharp/App/SinexcelCommunication/Program.cs index 3bfb7cca0..26b210dd4 100644 --- a/csharp/App/SinexcelCommunication/Program.cs +++ b/csharp/App/SinexcelCommunication/Program.cs @@ -24,6 +24,8 @@ using Formatting = Newtonsoft.Json.Formatting; using JsonSerializer = System.Text.Json.JsonSerializer; using static InnovEnergy.App.SinexcelCommunication.MiddlewareClasses.MiddlewareAgent; using System.Diagnostics.CodeAnalysis; +using InnovEnergy.Lib.Devices.Sinexcel_12K_TL.DataType; + #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. namespace InnovEnergy.App.SinexcelCommunication; @@ -117,13 +119,48 @@ internal static class Program var statusrecord = ReadStatus(); if (statusrecord == null) return null; + + // Console.WriteLine( statusrecord.InverterRecord.TimedChargeAndDischargeOff + " TimedChargeAndDischargeOff "); + Console.WriteLine( statusrecord.InverterRecord.Battery1Power + " Battery1Power "); + Console.WriteLine( statusrecord.InverterRecord.Battery1Soc + " Battery1Soc "); + Console.WriteLine( statusrecord.InverterRecord.RepetitiveWeeks + " RepetitiveWeeks "); + Console.WriteLine( statusrecord.InverterRecord.EffectiveStartDate + " EffectiveStartDate "); + Console.WriteLine( statusrecord.InverterRecord.EffectiveEndDate + " EffectiveEndDate "); + Console.WriteLine( statusrecord.InverterRecord.ChargingPowerPeriod1 + " ChargingPowerPeriod1 "); + Console.WriteLine( statusrecord.InverterRecord.ChargeStartTimePeriod1 + " ChargeStartTimePeriod1 "); + Console.WriteLine( statusrecord.InverterRecord.ChargeEndTimePeriod1 + " ChargeEndTimePeriod1 "); + + Console.WriteLine( statusrecord.InverterRecord.DischargeStartTimePeriod1 + " DischargeStartTimePeriod1 "); + Console.WriteLine( statusrecord.InverterRecord.DischargeEndTimePeriod1 + " DischargeEndTimePeriod1 "); + + Console.WriteLine( statusrecord.InverterRecord.PowerOn + " PowerOn "); + Console.WriteLine( statusrecord.InverterRecord.PowerOff + " PowerOff "); + Console.WriteLine( statusrecord.InverterRecord.WorkingMode + " WorkingMode "); + // Console.WriteLine( statusrecord.InverterRecord.FaultClearing + " FaultClearing "); SendSalimaxStateAlarm(GetSodiHomeStateAlarm(statusrecord),statusrecord); Console.WriteLine( " ************************************ We are writing ************************************"); var startWritingTime = DateTime.Now; Console.WriteLine(startWritingTime.ToString("HH:mm:ss.fff ") +"start Writing"); statusrecord?.Config.Save(); // save the config file - //if (statusrecord?.InverterRecord != null) sinexcelDevice.Write(statusrecord.InverterRecord); + + if (statusrecord != null) + { + statusrecord.InverterRecord.WorkingMode = WorkingMode.TimeChargeDischarge; + + //statusrecord.InverterRecord.TimedChargeAndDischargeOff = 0; + statusrecord.InverterRecord.RepetitiveWeeks = SinexcelWeekDays.All; + statusrecord.InverterRecord.EffectiveStartDate = DateTime.Today.AddDays(-1); + statusrecord.InverterRecord.EffectiveEndDate = DateTime.Today.AddDays(2); + statusrecord.InverterRecord.ChargingPowerPeriod1 = 3; + statusrecord.InverterRecord.DishargingPowerPeriod1 = 3; + statusrecord.InverterRecord.DischargeStartTimePeriod1 = new TimeSpan(16, 00, 0); + statusrecord.InverterRecord.DischargeEndTimePeriod1 = new TimeSpan(17, 03, 0); + statusrecord.InverterRecord.PowerOn = 1; + statusrecord.InverterRecord.PowerOff = 0; + //statusrecord.InverterRecord.FaultClearing = 1; + } + if (statusrecord?.InverterRecord != null) sinexcelDevice.Write(statusrecord.InverterRecord); var stopTime = DateTime.Now; Console.WriteLine(stopTime.ToString("HH:mm:ss.fff ")+ "run iteration time finish"); @@ -247,10 +284,11 @@ internal static class Program var modbusData = new Dictionary(); // SYSTEM DATA - var result1 = ConvertToModbusRegisters((status.Config.MinSoc * 10), "UInt16", 30001); // this to be updated to modbusTCP version - var result2 = ConvertToModbusRegisters(status.InverterRecord.GridAPhaseVoltage, "UInt32", 30002); + var result1 = ConvertToModbusRegisters((status.Config.ModbusProtcolNumber * 10), "UInt16", 30001); // this to be updated to modbusTCP version + var result2 = ConvertToModbusRegisters(status.InverterRecord.SystemDateTime.ToUnixTime(), "UInt32", 30002); // Merge all results into one dictionary + var allResults = new[] { result1,result2 @@ -274,39 +312,66 @@ internal static class Program return true; } - private static Dictionary ConvertToModbusRegisters(Object value, String outputType, Int32 startingAddress) + private static Dictionary ConvertToModbusRegisters(object value, string outputType, int startingAddress) { - var registers = new Dictionary(); + var regs = new Dictionary(capacity: 2); switch (outputType) { case "UInt16": - registers[startingAddress.ToString()] = Convert.ToUInt16(value); + { + regs[$"{startingAddress}"] = Convert.ToUInt16(value); break; + } case "Int16": - var int16Val = Convert.ToInt16(value); - registers[startingAddress.ToString()] = (UInt16)int16Val; // reinterpret signed as ushort + { + short v = Convert.ToInt16(value); + regs[$"{startingAddress}"] = unchecked((ushort)v); // reinterpret break; + } case "UInt32": - var uint32Val = Convert.ToUInt32(value); - registers[startingAddress.ToString()] = (UInt16)(uint32Val & 0xFFFF); // Low word - registers[(startingAddress + 1).ToString()] = (UInt16)(uint32Val >> 16); // High word + { + uint v = Convert.ToUInt32(value); + ushort hi = (ushort)(v >> 16); + ushort lo = (ushort)(v & 0xFFFF); + regs[$"{startingAddress}"] = hi; // HIGH word first (Modbus standard) + regs[$"{startingAddress + 1}"] = lo; // then LOW word break; + } case "Int32": - var int32Val = Convert.ToInt32(value); - var raw = unchecked((UInt32)int32Val); // reinterprets signed int as unsigned - registers[startingAddress.ToString()] = (UInt16)(raw & 0xFFFF); - registers[(startingAddress + 1).ToString()] = (UInt16)(raw >> 16); + { + int v = Convert.ToInt32(value); + uint raw = unchecked((uint)v); // bit-reinterpret + ushort hi = (ushort)(raw >> 16); + ushort lo = (ushort)(raw & 0xFFFF); + regs[$"{startingAddress}"] = hi; // HIGH word + regs[$"{startingAddress + 1}"] = lo; // LOW word break; + } + + case "Float": // IEEE-754 single + { + float f = Convert.ToSingle(value); + // Convert to bytes, then to two big-endian 16-bit words + var bytes = BitConverter.GetBytes(f); // little-endian on most platforms + Array.Reverse(bytes); // to big-endian byte order + ushort hi = (ushort)((bytes[0] << 8) | bytes[1]); + ushort lo = (ushort)((bytes[2] << 8) | bytes[3]); + regs[$"{startingAddress}"] = hi; + regs[$"{startingAddress + 1}"] = lo; + break; + } default: - throw new ArgumentException("Unsupported output type: " + outputType); + throw new ArgumentException($"Unsupported output type: {outputType}"); } - return registers; + + return regs; } + private static async Task DataLogging(StatusRecord status, DateTime timeStamp) { From c1674f352ca1b8f797d5d6af46ed88faa1feb797 Mon Sep 17 00:00:00 2001 From: atef Date: Tue, 18 Nov 2025 02:31:09 +0100 Subject: [PATCH 22/23] delete the sleeping thread --- csharp/Lib/Protocols/Modbus/Slaves/ModbusDevice.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/csharp/Lib/Protocols/Modbus/Slaves/ModbusDevice.cs b/csharp/Lib/Protocols/Modbus/Slaves/ModbusDevice.cs index 327eede23..b8c332943 100644 --- a/csharp/Lib/Protocols/Modbus/Slaves/ModbusDevice.cs +++ b/csharp/Lib/Protocols/Modbus/Slaves/ModbusDevice.cs @@ -41,7 +41,7 @@ public class ModbusDevice<[DynamicallyAccessedMembers(All)] R> where R : notnull foreach (var batch in _Batches) { batch.Read(record); - Thread.Sleep(30); // this added mainly for Growatt reading + //Thread.Sleep(30); // this added mainly for Growatt reading } return record; } @@ -51,7 +51,7 @@ public class ModbusDevice<[DynamicallyAccessedMembers(All)] R> where R : notnull foreach (var batch in _Batches) { batch.Write(record); - Thread.Sleep(50); // this added mainly for Growatt reading + //Thread.Sleep(50); // this added mainly for Growatt reading } } } \ No newline at end of file From 615c1939a227c742ef04e1c37e5ec1ec538c3575 Mon Sep 17 00:00:00 2001 From: atef Date: Tue, 18 Nov 2025 02:37:37 +0100 Subject: [PATCH 23/23] update aggregator on sodistormax and salimax --- csharp/App/SaliMax/src/AggregationService/Aggregator.cs | 6 +++--- .../App/SodiStoreMax/src/AggregationService/Aggregator.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/csharp/App/SaliMax/src/AggregationService/Aggregator.cs b/csharp/App/SaliMax/src/AggregationService/Aggregator.cs index e430bb351..d73ad572f 100644 --- a/csharp/App/SaliMax/src/AggregationService/Aggregator.cs +++ b/csharp/App/SaliMax/src/AggregationService/Aggregator.cs @@ -142,9 +142,9 @@ public static class Aggregator { batterySoc.Add((double)jsonObject["Battery"]["Soc"]); } - if (jsonObject["PvOnDc"] != null && jsonObject["PvOnDc"]["DcWh"] != null) + if (jsonObject["PvOnDc"] != null && jsonObject["PvOnDc"]["1"]["DcWh"] != null) // todo update to all pv { - pvPowerSum.Add((double)jsonObject["PvOnDc"]["DcWh"]); + pvPowerSum.Add((double)jsonObject["PvOnDc"]["1"]["DcWh"]); } if (jsonObject["Battery"] != null && jsonObject["Battery"]["Dc"]["Power"] != null) { @@ -316,7 +316,7 @@ public static class Aggregator ChargingBatteryPower = batteryChargePower.Any() ? batteryChargePower.Average() : 0.0, GridExportPower = gridPowerExport.Any() ? gridPowerExport.Sum() : 0.0, GridImportPower = gridPowerImport.Any() ? gridPowerImport.Sum() : 0.0, - PvPower = pvPower.Any() ? pvPower.Last() : 0.0, + PvPower = pvPower.Any() ? pvPower.Max() : 0.0, HeatingPower = heatingPowerAvg.Any() ? heatingPowerAvg.Average() : 0.0, }; diff --git a/csharp/App/SodiStoreMax/src/AggregationService/Aggregator.cs b/csharp/App/SodiStoreMax/src/AggregationService/Aggregator.cs index 0699adcb1..b857fb6ca 100644 --- a/csharp/App/SodiStoreMax/src/AggregationService/Aggregator.cs +++ b/csharp/App/SodiStoreMax/src/AggregationService/Aggregator.cs @@ -137,7 +137,7 @@ public static class Aggregator { batterySoc.Add((double)jsonObject["Battery"]["Soc"]); } - if (jsonObject["PvOnDc"] != null && jsonObject["PvOnDc"]["1"]["DcWh"] != null) + if (jsonObject["PvOnDc"] != null && jsonObject["PvOnDc"]["1"]["DcWh"] != null) // todo update to all pv { pvPowerSum.Add((double)jsonObject["PvOnDc"]["1"]["DcWh"]); }