From bbca604ce9c8a268367853cf81275823f5e77133 Mon Sep 17 00:00:00 2001 From: atef Date: Mon, 5 May 2025 11:59:20 +0200 Subject: [PATCH] Add Calibration charge mode and clean code --- csharp/App/SodiStoreMax/src/Ess/Controller.cs | 69 ++++-- csharp/App/SodiStoreMax/src/Program.cs | 220 ++++++------------ 2 files changed, 120 insertions(+), 169 deletions(-) diff --git a/csharp/App/SodiStoreMax/src/Ess/Controller.cs b/csharp/App/SodiStoreMax/src/Ess/Controller.cs index 98d34b3cb..e81c75607 100644 --- a/csharp/App/SodiStoreMax/src/Ess/Controller.cs +++ b/csharp/App/SodiStoreMax/src/Ess/Controller.cs @@ -11,12 +11,16 @@ public static class Controller private static readonly Double MaxDischargePower = -8000; // By battery TODO: move to config private static readonly Double MaxChargePower = 6000; // By battery TODO: move to config + private static Boolean _hasRepetitiveCalibrationChargeChecked = false; + private static DateTime _nextDayAt10Am = DateTime.Now; + public static EssMode SelectControlMode(this StatusRecord s) { //return EssMode.OptimizeSelfConsumption; return s.StateMachine.State != 23 ? EssMode.Off : s.MustReachMinSoc() ? EssMode.ReachMinSoc + : s.MustDoCalibrationCharge() ? EssMode.CalibrationCharge : s.GridMeter is null ? EssMode.NoGridMeter : EssMode.OptimizeSelfConsumption; } @@ -147,26 +151,15 @@ public static class Controller EssMode.Off => 0, EssMode.OffGrid => 0, EssMode.NoGridMeter => 0, + EssMode.CalibrationCharge => s.ControlInverterPower(chargePower), _ => throw new ArgumentException(null, nameof(mode)) }; } - // private static Boolean MustHeatBatteries(this StatusRecord s) - // { - // var batteries = s.GetBatteries(); -// - // if (batteries.Count <= 0) - // return true; // batteries might be there but BMS is without power -// - // return batteries - // .Select(b => b.Temperatures.State) - // .Contains(TemperatureState.Cold); - // } - private static Double MaxBatteryChargePower(this StatusRecord s) { - // This introduces a limit when we don't have communication with batteries - // Otherwise the limit will be 0 and the batteries will be not heated + // This introduces a limit when we don't have communication with batteries, + // Otherwise the limit will be 0, and the batteries will be not heated var batteries = s.GetBatteries(); @@ -206,6 +199,54 @@ public static class Controller return s.Battery?.Devices ?? Array.Empty(); } + private static Boolean MustDoCalibrationCharge(this StatusRecord statusRecord) + { + var calibrationChargeForced = statusRecord.Config.ForceCalibrationChargeState; + var additionalCalibrationRequired = AdditionalCalibrationDateHasBeenPassed(statusRecord.Config.DayAndTimeForAdditionalCalibration); + var repetitiveCalibrationRequired = RepetitiveCalibrationDateHasBeenPassed(statusRecord.Config.DayAndTimeForRepetitiveCalibration); + + var mustDoCalibrationCharge = calibrationChargeForced == CalibrationChargeType.ChargePermanently || + (calibrationChargeForced == CalibrationChargeType.AdditionallyOnce && additionalCalibrationRequired) || + (calibrationChargeForced == CalibrationChargeType.RepetitivelyEvery && repetitiveCalibrationRequired); + + Console.WriteLine("Next Repetitive calibration charge date is "+ statusRecord.Config.DayAndTimeForRepetitiveCalibration); + Console.WriteLine("Next Additional calibration charge date is "+ statusRecord.Config.DayAndTimeForAdditionalCalibration); + + if (statusRecord.Battery is not null) + { + if (calibrationChargeForced == CalibrationChargeType.AdditionallyOnce && statusRecord.Battery.Eoc ) + { + statusRecord.Config.ForceCalibrationChargeState = CalibrationChargeType.RepetitivelyEvery; + + } + else if (calibrationChargeForced == CalibrationChargeType.RepetitivelyEvery && statusRecord.Battery.Eoc && _hasRepetitiveCalibrationChargeChecked) + { + statusRecord.Config.DayAndTimeForRepetitiveCalibration = statusRecord.Config.DayAndTimeForRepetitiveCalibration.AddDays(7); + _hasRepetitiveCalibrationChargeChecked = false; + } + } + + return mustDoCalibrationCharge; + } + + private static Boolean RepetitiveCalibrationDateHasBeenPassed(DateTime calibrationChargeDate) + { + if (DateTime.Now >= calibrationChargeDate ) + { + _hasRepetitiveCalibrationChargeChecked = true; + return true; + } + return false; + } + private static Boolean AdditionalCalibrationDateHasBeenPassed(DateTime calibrationChargeDate) + { + if (DateTime.Now >= calibrationChargeDate ) + { + return true; + } + return false; + } + private static Double ControlGridPower(this StatusRecord status, Double targetPower) { return ControlPower diff --git a/csharp/App/SodiStoreMax/src/Program.cs b/csharp/App/SodiStoreMax/src/Program.cs index 29e2a9a4d..1429c279b 100644 --- a/csharp/App/SodiStoreMax/src/Program.cs +++ b/csharp/App/SodiStoreMax/src/Program.cs @@ -68,8 +68,7 @@ internal static class Program private const UInt16 NbrOfFileToConcatenate = 30; // add this to config file private static UInt16 _fileCounter = 0; private static SalimaxAlarmState _salimaxAlarmState = SalimaxAlarmState.Green; - private const String Port = "/dev/ttyUSB0"; - private static Boolean _oneTimeFlag = false; + private const String Port = "/dev/ttyUSB0"; // move to a config file static Program() { @@ -314,108 +313,28 @@ internal static class Program var bAlarmList = new List(); var bWarningList = new List(); - if (record.Battery != null) - { - //_oneTimeFlag.WriteLine(" onetime flag"); - if (record.Battery?.MonomerHighVoltageAlarm == true /*&& !_oneTimeFlag*/) //this should be checked: the number 0 - { - //_oneTimeFlag = true; - warningList.Add(new AlarmOrWarning - { - Date = DateTime.Now.ToString("yyyy-MM-dd"), - Time = DateTime.Now.ToString("HH:mm:ss"), - CreatedBy = "Charging Battery System", - Description = "dynCCL Active: Max Battery Charging is 10 * N" - }); - } - - /* if (record.Battery?.MonomerLowVoltageAlarm == true && !_oneTimeFlag) //this should be checked: the number 10 - { - _oneTimeFlag = true; - warningList.Add(new AlarmOrWarning - { - Date = DateTime.Now.ToString("yyyy-MM-dd"), - Time = DateTime.Now.ToString("HH:mm:ss"), - CreatedBy = "Battery System", - Description = "dynDCL Active: Max Battery Discharging is 10 * N" - }); - } - else - { - _oneTimeFlag = false; - }*/ + if (record.Battery is { MonomerHighVoltageAlarm: true }) + { + warningList.Add(new AlarmOrWarning + { + Date = DateTime.Now.ToString("yyyy-MM-dd"), + Time = DateTime.Now.ToString("HH:mm:ss"), + CreatedBy = "Charging Battery System", + Description = "dynCCL Active: Max Battery Charging is 10 * N" + }); + } + if (record.Battery is { MonomerLowVoltageAlarm: true }) + { + warningList.Add(new AlarmOrWarning + { + Date = DateTime.Now.ToString("yyyy-MM-dd"), + Time = DateTime.Now.ToString("HH:mm:ss"), + CreatedBy = "Battery System", + Description = "dynDCL Active: Max Battery Discharging is 10 * N" + }); } - /* - if (record.Battery != null) - { - var i = 0; - - foreach (var battery in record.Battery.Devices) - { - var devicesBatteryNode = record.Config.Devices.BatteryNodes[i]; - - if (battery.LimpBitMap == 0) - { - // "All String are Active".WriteLine(); - } - else if (IsPowerOfTwo(battery.LimpBitMap)) - { - "1 String is disabled".WriteLine(); - Console.WriteLine(" ****************** "); - warningList.Add(new AlarmOrWarning - { - Date = DateTime.Now.ToString("yyyy-MM-dd"), - Time = DateTime.Now.ToString("HH:mm:ss"), - CreatedBy = "Battery node" + devicesBatteryNode, - Description = "1 String is disabled" - }); - - bWarningList.Add("/"+i+1 + "/1 String is disabled"); // battery id instead ( i +1 ) of node id: requested from the frontend - } - else - { - "2 or more string are disabled".WriteLine(); - Console.WriteLine(" ****************** "); - - alarmList.Add(new AlarmOrWarning - { - Date = DateTime.Now.ToString("yyyy-MM-dd"), - Time = DateTime.Now.ToString("HH:mm:ss"), - CreatedBy = "Battery node" + devicesBatteryNode, - Description = "2 or more string are disabled" - }); - bAlarmList.Add(i +";2 or more string are disabled"); - } - - foreach (var warning in record.Battery.Warnings) - { - warningList.Add(new AlarmOrWarning - { - Date = DateTime.Now.ToString("yyyy-MM-dd"), - Time = DateTime.Now.ToString("HH:mm:ss"), - CreatedBy = "Battery node" + devicesBatteryNode, - Description = warning - }); - bWarningList.Add(i +";" + warning); - } - - foreach (var alarm in battery.Alarms) - { - alarmList.Add(new AlarmOrWarning - { - Date = DateTime.Now.ToString("yyyy-MM-dd"), - Time = DateTime.Now.ToString("HH:mm:ss"), - CreatedBy = "Battery node" + devicesBatteryNode, - Description = alarm - }); - bWarningList.Add(i +";" + alarm); - } - i++; - } - }*/ - - if (alarmCondition is not null) + if (alarmCondition is not null) { alarmCondition.WriteLine(); @@ -428,49 +347,33 @@ internal static class Program }); } - foreach (var alarm in record.AcDc.Alarms) - { - alarmList.Add(new AlarmOrWarning - { - Date = DateTime.Now.ToString("yyyy-MM-dd"), - Time = DateTime.Now.ToString("HH:mm:ss"), - CreatedBy = "AcDc", - Description = alarm.ToString() - }); - } + alarmList.AddRange(record.AcDc.Alarms + .Select(alarm => new AlarmOrWarning + { Date = DateTime.Now.ToString("yyyy-MM-dd"), + Time = DateTime.Now.ToString("HH:mm:ss"), + CreatedBy = "AcDc", + Description = alarm.ToString() })); - foreach (var alarm in record.DcDc.Alarms) - { - alarmList.Add(new AlarmOrWarning - { - Date = DateTime.Now.ToString("yyyy-MM-dd"), - Time = DateTime.Now.ToString("HH:mm:ss"), - CreatedBy = "DcDc", - Description = alarm.ToString() - }); - } + alarmList.AddRange(record.DcDc.Alarms + .Select(alarm => new AlarmOrWarning + { Date = DateTime.Now.ToString("yyyy-MM-dd"), + Time = DateTime.Now.ToString("HH:mm:ss"), + CreatedBy = "DcDc", + Description = alarm.ToString() })); - foreach (var warning in record.AcDc.Warnings) - { - warningList.Add(new AlarmOrWarning - { - Date = DateTime.Now.ToString("yyyy-MM-dd"), - Time = DateTime.Now.ToString("HH:mm:ss"), - CreatedBy = "AcDc", - Description = warning.ToString() - }); - } + warningList.AddRange(record.AcDc.Warnings + .Select(warning => new AlarmOrWarning + { Date = DateTime.Now.ToString("yyyy-MM-dd"), + Time = DateTime.Now.ToString("HH:mm:ss"), + CreatedBy = "AcDc", + Description = warning.ToString() })); - foreach (var warning in record.DcDc.Warnings) - { - warningList.Add(new AlarmOrWarning - { - Date = DateTime.Now.ToString("yyyy-MM-dd"), - Time = DateTime.Now.ToString("HH:mm:ss"), - CreatedBy = "DcDc", - Description = warning.ToString() - }); - } + warningList.AddRange(record.DcDc.Warnings + .Select(warning => new AlarmOrWarning + { Date = DateTime.Now.ToString("yyyy-MM-dd"), + Time = DateTime.Now.ToString("HH:mm:ss"), + CreatedBy = "DcDc", + Description = warning.ToString() })); _salimaxAlarmState = warningList.Any() ? SalimaxAlarmState.Orange @@ -480,7 +383,7 @@ internal static class Program ? SalimaxAlarmState.Red : _salimaxAlarmState; // this will be replaced by LedState - TryParse(s3Bucket?.Split("-")[0], out var installationId); + var installationId = GetInstallationId(s3Bucket ?? string.Empty); var returnedStatus = new StatusMessage { @@ -494,7 +397,13 @@ internal static class Program return returnedStatus; } - + + private static Int32 GetInstallationId(String s3Bucket) + { + var part = s3Bucket.Split('-').FirstOrDefault(); + return TryParse(part, out var id) ? id : 0; // is 0 a default safe value? check with Marios + } + private static String? DetectAlarmStates(this StatusRecord r) => r.Relays switch { { K2ConnectIslandBusToGridBus: false, K2IslandBusIsConnectedToGridBus: true } => " Contradiction: R0 is opening the K2 but the K2 is still close ", @@ -553,7 +462,7 @@ internal static class Program } else { - maxBatteryChargingCurrentLiveByDcDc = 0; + maxBatteryChargingCurrentLiveByDcDc = 0; maxBatteryDischargingCurrentLivebyDcDc = 0; } inverters.ForEach(d => d.Control.Dc.MaxVoltage = devicesConfig.AcDc.MaxDcLinkVoltage); @@ -1001,15 +910,16 @@ internal static class Program status.Config.MinSoc = config.MinimumSoC; status.Config.GridSetPoint = config.GridSetPoint * 1000; // converted from kW to W - // status.Config.ForceCalibrationChargeState = config.CalibrationChargeState; - // - // if (config.CalibrationChargeState == CalibrationChargeType.RepetitivelyEvery) - // { - // status.Config.DayAndTimeForRepetitiveCalibration = config.CalibrationChargeDate; - // } - // else if (config.CalibrationChargeState == CalibrationChargeType.AdditionallyOnce) - // { - // status.Config.DayAndTimeForAdditionalCalibration = config.CalibrationChargeDate; - // } + + status.Config.ForceCalibrationChargeState = config.CalibrationChargeState; + + if (config.CalibrationChargeState == CalibrationChargeType.RepetitivelyEvery) + { + status.Config.DayAndTimeForRepetitiveCalibration = config.CalibrationChargeDate; + } + else if (config.CalibrationChargeState == CalibrationChargeType.AdditionallyOnce) + { + status.Config.DayAndTimeForAdditionalCalibration = config.CalibrationChargeDate; + } } } \ No newline at end of file