diff --git a/csharp/App/GrowattCommunication/MiddlewareClasses/MiddlewareAgent.cs b/csharp/App/GrowattCommunication/MiddlewareClasses/MiddlewareAgent.cs index 279e39052..84a6ef8fc 100644 --- a/csharp/App/GrowattCommunication/MiddlewareClasses/MiddlewareAgent.cs +++ b/csharp/App/GrowattCommunication/MiddlewareClasses/MiddlewareAgent.cs @@ -9,24 +9,41 @@ 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() + public static bool InitializeCommunicationToMiddleware() { - _controllerIpAddress = FindVpnIp(); - if (Equals(IPAddress.None, _controllerIpAddress)) + try { - Console.WriteLine("There is no VPN interface, exiting..."); + _controllerIpAddress = FindVpnIp(); + if (Equals(IPAddress.None, _controllerIpAddress)) + { + Console.WriteLine("There is no VPN interface."); + _udpListener = null; + return false; + } + + const int udpPort = 9000; + _endPoint = new IPEndPoint(_controllerIpAddress, udpPort); + + _udpListener?.Close(); + _udpListener?.Dispose(); + + _udpListener = new UdpClient(); + _udpListener.Client.Blocking = false; + _udpListener.Client.Bind(_endPoint); + + Console.WriteLine($"UDP listener bound to {_endPoint}"); + return true; + } + catch (Exception ex) + { + Console.WriteLine($"Failed to initialize middleware communication: {ex}"); + _udpListener = null; + return false; } - - const Int32 udpPort = 9000; - _endPoint = new IPEndPoint(_controllerIpAddress, udpPort); - - _udpListener = new UdpClient(); - _udpListener.Client.Blocking = false; - _udpListener.Client.Bind(_endPoint); } private static IPAddress FindVpnIp() @@ -50,40 +67,92 @@ public static class MiddlewareAgent return IPAddress.None; } - public static Configuration? SetConfigurationFile() { - if (_udpListener.Available > 0) + try { + // Ensure listener is initialized + if (_udpListener == null) + { + Console.WriteLine("UDP listener not initialized, trying to initialize..."); + InitializeCommunicationToMiddleware(); + + if (_udpListener == null) + { + Console.WriteLine("Failed to initialize UDP listener."); + return null; + } + } + + // Check if data is available + if (_udpListener.Available <= 0) + return null; + IPEndPoint? serverEndpoint = null; - - var replyMessage = "ACK"; - var replyData = Encoding.UTF8.GetBytes(replyMessage); - + var udpMessage = _udpListener.Receive(ref serverEndpoint); - var message = Encoding.UTF8.GetString(udpMessage); - + var message = Encoding.UTF8.GetString(udpMessage); + + Console.WriteLine($"Received raw UDP message from {serverEndpoint}: {message}"); + var config = JsonSerializer.Deserialize(message); - + if (config != null) { - Console.WriteLine($"Received a configuration message: " + - "MinimumSoC is " + config.MinimumSoC + " and operating priorty is " +config.OperatingPriority + "Number of batteries is " + config.BatteriesCount - + "MaximumChargingCurrent is " + config.MaximumChargingCurrent + "MaximumDischargingCurrent " + config.MaximumDischargingCurrent + " Control permission is" + config.ControlPermission ); - - // Send the reply to the sender's endpoint + Console.WriteLine( + $"Received a configuration message:\n" + + $"MinimumSoC: {config.MinimumSoC}\n" + + $"OperatingPriority: {config.OperatingPriority}\n" + + $"Number of batteries: {config.BatteriesCount}\n" + + $"Maximum Charging current: {config.MaximumChargingCurrent}\n" + + $"Maximum Discharging current: {config.MaximumDischargingCurrent}\n" + + $"ControlPermission: {config.ControlPermission}" + ); + + // Send ACK + var replyMessage = "ACK"; + var replyData = Encoding.UTF8.GetBytes(replyMessage); + _udpListener.Send(replyData, replyData.Length, serverEndpoint); Console.WriteLine($"Replied to {serverEndpoint}: {replyMessage}"); + return config; } + else + { + Console.WriteLine("Received UDP message but failed to deserialize Configuration."); + return null; + } } - - if (_endPoint != null && !_endPoint.Equals(_udpListener.Client.LocalEndPoint as IPEndPoint)) + catch (SocketException ex) { - Console.WriteLine("UDP address has changed, rebinding..."); - InitializeCommunicationToMiddleware(); - } - return null; - } + Console.WriteLine($"Socket error in SetConfigurationFile: {ex}"); + + // Recover by reinitializing + try + { + _udpListener?.Close(); + _udpListener?.Dispose(); + } + catch + { + // ignored + } + _udpListener = null; + InitializeCommunicationToMiddleware(); + + return null; + } + catch (JsonException ex) + { + Console.WriteLine($"JSON deserialization error: {ex}"); + return null; + } + catch (Exception ex) + { + Console.WriteLine($"Unexpected error in SetConfigurationFile: {ex}"); + return null; + } + } } \ No newline at end of file diff --git a/csharp/App/GrowattCommunication/deploy.sh b/csharp/App/GrowattCommunication/deploy.sh index a919ab29c..05bd7293a 100755 --- a/csharp/App/GrowattCommunication/deploy.sh +++ b/csharp/App/GrowattCommunication/deploy.sh @@ -6,12 +6,13 @@ username='inesco' root_password='Sodistore0918425' release_flag_file="./bin/Release/$dotnet_version/linux-arm64/publish/.release.flag" +DOTNET="/snap/dotnet-sdk_60/current/dotnet" set -e echo -e "\n============================ Build ============================\n" -dotnet publish \ +"$DOTNET" publish \ ./GrowattCommunication.csproj \ -p:PublishTrimmed=false \ -c Release \ diff --git a/csharp/Lib/Devices/WITGrowatt4-15K/WITGrowatRecord.Api.cs b/csharp/Lib/Devices/WITGrowatt4-15K/WITGrowatRecord.Api.cs index 4d29fc83d..b5c1e7b0a 100644 --- a/csharp/Lib/Devices/WITGrowatt4-15K/WITGrowatRecord.Api.cs +++ b/csharp/Lib/Devices/WITGrowatt4-15K/WITGrowatRecord.Api.cs @@ -141,6 +141,20 @@ public partial class WITGrowatRecord //set => _PowerFactor = value; } + + + public UInt16 ExportLimitationEnabled + { + get => _ExportLimitationEnabled; + set => _ExportLimitationEnabled = value; + } + + + public Int16 ExportLimitationPowerRate + { + get => _ExportLimitationPowerRate; + set => _ExportLimitationPowerRate = value; + } public UInt16 EmsCommunicationFailureTime { diff --git a/csharp/Lib/Devices/WITGrowatt4-15K/WITGrowatRecord.Modbus.cs b/csharp/Lib/Devices/WITGrowatt4-15K/WITGrowatRecord.Modbus.cs index b31a63090..02f4f4329 100644 --- a/csharp/Lib/Devices/WITGrowatt4-15K/WITGrowatRecord.Modbus.cs +++ b/csharp/Lib/Devices/WITGrowatt4-15K/WITGrowatRecord.Modbus.cs @@ -165,7 +165,11 @@ 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(30200, writable : true)] private UInt16 _ExportLimitationEnabled; // // 0: not enabled // 1: single machine Export Limitation enable + [HoldingRegister(30201, writable : true)] private Int16 _ExportLimitationPowerRate; // [-100,100] // Default value: 0 Positive value is backflow, negative value is fair current + [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 not enable this the naming is not correct [HoldingRegister(30300)] private UInt16 _BatteryClusterIndex; // [0..3]