diff --git a/csharp/App/GrowattCommunication/Program.cs b/csharp/App/GrowattCommunication/Program.cs index 62c6cffc6..939373298 100644 --- a/csharp/App/GrowattCommunication/Program.cs +++ b/csharp/App/GrowattCommunication/Program.cs @@ -46,6 +46,14 @@ public static class Program private static SodistoreAlarmState _prevSodiohomeAlarmState = SodistoreAlarmState.Green; private static SodistoreAlarmState _sodiohomeAlarmState = SodistoreAlarmState.Green; + // Tracking for error/warning content changes + private static List _prevErrorCodes = new List(); + private static List _prevWarningCodes = new List(); + + // Heartbeat tracking + private static DateTime _lastHeartbeatTime = DateTime.MinValue; + private const Int32 HeartbeatIntervalSeconds = 60; + // move all this to config file private const String Port = "/dev/ttyUSB0"; @@ -292,7 +300,44 @@ public static class Program var part = s3Bucket.Split('-').FirstOrDefault(); return int.TryParse(part, out var id) ? id : 0; // is 0 a default safe value? check with Marios } - + + /// + /// Checks if the error or warning content has changed compared to the previous state. + /// This allows detection of new/cleared errors even when the overall alarm state (Red/Orange/Green) remains the same. + /// + private static Boolean HasErrorsOrWarningsChanged(StatusMessage currentState) + { + // Get current error codes (descriptions) + var currentErrors = currentState.Alarms? + .Select(a => a.Description ?? String.Empty) + .OrderBy(d => d) // Sort for consistent comparison + .ToList() ?? new List(); + + // Get current warning codes (descriptions) + var currentWarnings = currentState.Warnings? + .Select(w => w.Description ?? String.Empty) + .OrderBy(d => d) // Sort for consistent comparison + .ToList() ?? new List(); + + // Check if lists have changed (new items added or existing items removed) + var errorsChanged = !currentErrors.SequenceEqual(_prevErrorCodes); + var warningsChanged = !currentWarnings.SequenceEqual(_prevWarningCodes); + + // Update tracking if changes detected + if (errorsChanged || warningsChanged) + { + Console.WriteLine($"Error/Warning content changed:"); + Console.WriteLine($" Errors: {String.Join(", ", currentErrors)} (was: {String.Join(", ", _prevErrorCodes)})"); + Console.WriteLine($" Warnings: {String.Join(", ", currentWarnings)} (was: {String.Join(", ", _prevWarningCodes)})"); + + _prevErrorCodes = currentErrors; + _prevWarningCodes = currentWarnings; + return true; + } + + return false; + } + private static void SendSalimaxStateAlarm(StatusMessage currentSalimaxState, StatusRecord? record) { var s3Bucket = Config.Load().S3?.Bucket; @@ -305,14 +350,36 @@ public static class Program _subscribeToQueueForTheFirstTime = true; _prevSodiohomeAlarmState = currentSalimaxState.Status; _subscribedToQueue = RabbitMqManager.SubscribeToQueue(currentSalimaxState, s3Bucket, VpnServerIp); + _lastHeartbeatTime = DateTime.Now; // Initialize heartbeat timer } - //If already subscribed to the queue and the status has been changed, update the queue - if (!subscribedNow && _subscribedToQueue && currentSalimaxState.Status != _prevSodiohomeAlarmState) + // Check if we should send a message + var stateChanged = currentSalimaxState.Status != _prevSodiohomeAlarmState; + var contentChanged = HasErrorsOrWarningsChanged(currentSalimaxState); + var needsHeartbeat = (DateTime.Now - _lastHeartbeatTime).TotalSeconds >= HeartbeatIntervalSeconds; + + // Send message if: state changed, content changed, OR heartbeat needed + if (!subscribedNow && _subscribedToQueue && (stateChanged || contentChanged || needsHeartbeat)) { _prevSodiohomeAlarmState = currentSalimaxState.Status; + + // Set appropriate message type + if (stateChanged || contentChanged) + { + currentSalimaxState.Type = MessageType.AlarmOrWarning; + Console.WriteLine($"Sending AlarmOrWarning message - StateChanged: {stateChanged}, ContentChanged: {contentChanged}"); + } + else if (needsHeartbeat) + { + currentSalimaxState.Type = MessageType.Heartbit; + Console.WriteLine($"Sending Heartbeat message - {HeartbeatIntervalSeconds}s interval reached"); + _lastHeartbeatTime = DateTime.Now; + } + if (s3Bucket != null) + { RabbitMqManager.InformMiddleware(currentSalimaxState); + } } //If there is an available message from the RabbitMQ Broker, apply the configuration file