diff --git a/csharp/App/BmsTunnel/BmsTunnel.cs b/csharp/App/BmsTunnel/BmsTunnel.cs
index bb3999280..b01632292 100644
--- a/csharp/App/BmsTunnel/BmsTunnel.cs
+++ b/csharp/App/BmsTunnel/BmsTunnel.cs
@@ -102,7 +102,7 @@ public class BmsTunnel : IDisposable
{
// TODO: this should go into outer loop instead of returning magic value CrcError
- Console.WriteLine(BitConverter.ToString(response).Replace("-", " "));
+ //Console.WriteLine(BitConverter.ToString(response).Replace("-", " "));
return CrcError;
}
diff --git a/csharp/App/SaliMax/SaliMax.csproj b/csharp/App/SaliMax/SaliMax.csproj
index ba80db36b..8c66b470f 100644
--- a/csharp/App/SaliMax/SaliMax.csproj
+++ b/csharp/App/SaliMax/SaliMax.csproj
@@ -6,24 +6,24 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/csharp/App/SaliMax/run (BeagleBone Meiringen).sh b/csharp/App/SaliMax/run (BeagleBone Meiringen).sh
deleted file mode 100755
index e98675532..000000000
--- a/csharp/App/SaliMax/run (BeagleBone Meiringen).sh
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/bin/bash
-
-
-dotnet_version='net6.0'
-
-set -e
-
-echo -e "\n============================ Build ============================\n"
-
-dotnet publish \
- ./SaliMax.csproj \
- -c Release \
- -r linux-x64
-
-echo -e "\n============================ Deploy ============================\n"
-
-rsync -v \
- ./bin/Release/$dotnet_version/linux-x64/publish/* \
- ie-entwicklung@10.2.3.49:~/salimax
-
-
- # debian@10.2.1.87:~/salimax
-
-echo -e "\n============================ Restart Salimax sevice ============================\n"
-
-ssh -tt \
- ie-entwicklung@10.2.3.49 \
- sudo systemctl restart salimax.service
-
-
-echo -e "\n============================ Print service output ============================\n"
-
-ssh -tt \
- ie-entwicklung@10.2.3.49 \
- journalctl -f -u salimax.service
-
-
diff --git a/csharp/App/SaliMax/src/AsciiArt.cs b/csharp/App/SaliMax/src/AsciiArt.cs
index 9e0b47545..8896d0487 100644
--- a/csharp/App/SaliMax/src/AsciiArt.cs
+++ b/csharp/App/SaliMax/src/AsciiArt.cs
@@ -1,79 +1,77 @@
-using InnovEnergy.Lib.Units;
-using InnovEnergy.Lib.Utils;
-
-namespace InnovEnergy.App.SaliMax;
-
-public static class AsciiArt
-{
-
- public static String CreateBox(params Object[] elements)
- {
- var aligned = elements
- .Select(e => e.ToString()!)
- .JoinLines()
- .AlignLeft();
-
- var w = aligned.Width();
-
- var line = "".PadRight(w + 2, '─');
- var top = "┌" + line + "┐";
- var bottom = "└" + line + "┘";
-
- return aligned
- .SplitLines()
- .Select(l => l.SurroundWith(" "))
- .Select(l => l.SurroundWith("│"))
- .Prepend(top)
- .Append(bottom)
- .JoinLines();
- }
-
- public static String CreateHorizontalArrow(Decimal value, String separator)
- {
- var valueToString = " " + value.W();
-
- if (value == 0)
- {
- valueToString = "";
- }
-
- var contentWidth = separator.Length;
-
- var horizontal = "".PadRight(contentWidth, ' ');
-
- var v = valueToString.PadRight(contentWidth);
- var s = separator.PadRight(contentWidth);
-
- return StringUtils.JoinLines(
- horizontal,
- v,
- s,
- horizontal
- );
- }
-
- public static String CreateTransitionPadLeft(String value, String separator)
- {
- var contentWidth = separator.Length + 2;
-
- var horizontal = "".PadLeft(contentWidth, ' ');
-
- var v = value.PadLeft(contentWidth);
- var s = separator.PadLeft(contentWidth);
-
- return StringUtils.JoinLines(
- horizontal,
- v,
- s,
- horizontal
- );
- }
-
- public static String CreateVerticalArrow(Decimal power, Int32 width = 0)
- {
- var flow = "V".NewLine() + "V".NewLine() + power.W().NewLine() + "V".NewLine() + "V";
-
- return flow.AlignCenterHorizontal(width);
- }
-
-}
\ No newline at end of file
+// using InnovEnergy.Lib.Utils;
+//
+// namespace InnovEnergy.App.SaliMax;
+//
+// public static class AsciiArt
+// {
+//
+// public static String CreateBox(params Object[] elements)
+// {
+// var aligned = elements
+// .Select(e => e.ToString()!)
+// .JoinLines()
+// .AlignLeft();
+//
+// var w = aligned.Width();
+//
+// var line = "".PadRight(w + 2, '─');
+// var top = "┌" + line + "┐";
+// var bottom = "└" + line + "┘";
+//
+// return aligned
+// .SplitLines()
+// .Select(l => l.SurroundWith(" "))
+// .Select(l => l.SurroundWith("│"))
+// .Prepend(top)
+// .Append(bottom)
+// .JoinLines();
+// }
+//
+// public static String CreateHorizontalArrow(Decimal value, String separator)
+// {
+// var valueToString = " " + value.W();
+//
+// var contentWidth = separator.Length;
+//
+// var horizontal = "".PadRight(contentWidth, ' ');
+//
+// var v = valueToString.PadRight(contentWidth);
+// var s = separator.PadRight(contentWidth);
+//
+// return StringUtils.JoinLines(
+// horizontal,
+// horizontal,
+// horizontal,
+// v,
+// s,
+// horizontal,
+// horizontal,
+// horizontal
+// );
+// }
+//
+// public static String CreateTransitionPadLeft(String value, String separator)
+// {
+// var contentWidth = separator.Length + 2;
+//
+// var horizontal = "".PadLeft(contentWidth, ' ');
+//
+// var v = value.PadLeft(contentWidth);
+// var s = separator.PadLeft(contentWidth);
+//
+// return StringUtils.JoinLines(
+// horizontal,
+// v,
+// s,
+// horizontal
+// );
+// }
+//
+// public static String CreateVerticalArrow(Decimal power, Int32 width = 0)
+// {
+// var flow = "V".NewLine() + "V".NewLine() + power.W().NewLine() + "V".NewLine() + "V";
+//
+// return flow.AlignCenterHorizontal(width);
+// }
+//
+// }
\ No newline at end of file
diff --git a/csharp/App/SaliMax/src/Controller/AvgBatteriesStatus.cs b/csharp/App/SaliMax/src/Controller/AvgBatteriesStatus.cs
deleted file mode 100644
index 151c0c5af..000000000
--- a/csharp/App/SaliMax/src/Controller/AvgBatteriesStatus.cs
+++ /dev/null
@@ -1,56 +0,0 @@
-using InnovEnergy.Lib.Devices.Battery48TL;
-using InnovEnergy.Lib.StatusApi;
-using InnovEnergy.Lib.Units;
-using InnovEnergy.Lib.Units.Composite;
-using static InnovEnergy.Lib.Devices.Battery48TL.TemperatureState;
-
-namespace InnovEnergy.App.SaliMax.Controller;
-
-public static class AvgBatteriesStatus
-{
- public static CombinedStatus? Combine(this IReadOnlyList stati)
- {
- var combined = stati.Count == 0
- ? null
- : new Battery48TLStatus
- {
- Soc = stati.Min(b => b.Soc),
- Temperature = stati.Average(b => b.Temperature),
- Dc = new DcBus
- {
- Voltage = stati.Average(b => b.Dc.Voltage),
- Current = stati.Sum(b => b.Dc.Current),
- },
-
- Alarms = stati.SelectMany(b => b.Alarms).Distinct().ToList(),
- Warnings = stati.SelectMany(b => b.Warnings).Distinct().ToList(),
-
- MaxChargingPower = stati.Sum(b => b.MaxChargingPower),
- MaxDischargingPower = stati.Sum(b => b.MaxDischargingPower),
-
- Heating = stati.Any(b => b.Heating),
-
- AmberLed = LedState.Off, // not used for combined battery
- BlueLed = LedState.Off,
- RedLed = LedState.Off,
- GreenLed = LedState.Off,
-
- CellsVoltage = stati.Average(b => b.CellsVoltage),
- ConnectedToDc = stati.Any(b => b.ConnectedToDc),
-
- TemperatureState = stati.Any(b => b.TemperatureState == OperatingTemperature) // TODO: revisit when we have the overheated state
- ? OperatingTemperature
- : Cold,
-
- TotalCurrent = stati.Average(b => b.TotalCurrent),
-
- EocReached = stati.All(b => b.EocReached),
- };
-
- return new CombinedStatus
- {
- Combined = combined!,
- Children = stati
- };
- }
-}
\ No newline at end of file
diff --git a/csharp/App/SaliMax/src/Controller/Control.cs b/csharp/App/SaliMax/src/Controller/Control.cs
deleted file mode 100644
index 7564a5f81..000000000
--- a/csharp/App/SaliMax/src/Controller/Control.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-namespace InnovEnergy.App.SaliMax.Controller;
-
-public static class Control
-{
-
- public static Decimal ControlGridPower(this StatusRecord status, Decimal targetPower)
- {
- return ControlPower(status.GridMeterStatus!.Ac.ActivePower, targetPower, status.SalimaxConfig!.PConstant);
- }
-
- public static Decimal ControlInverterPower(this StatusRecord status, Decimal targetInverterPower)
- {
- var s = status.InverterStatus!;
- var totalInverterAcPower = s.Ac.ActivePower;
-
- return ControlPower(totalInverterAcPower, targetInverterPower,status.SalimaxConfig!.PConstant);
- }
-
- public static Decimal ControlBatteryPower(this StatusRecord status, Decimal targetBatteryPower, UInt16 i = 0) //this will use the avg batteries
- {
- return ControlPower(status.BatteriesStatus!.Combined.Dc.Power, targetBatteryPower, status.SalimaxConfig!.PConstant);
- }
-
- public static Decimal ControlLowBatterySoc(this StatusRecord status)
- {
- return ControlBatteryPower(status, HoldMinSocCurve(status));
- }
-
- public static Decimal LowerLimit(params Decimal[] deltas) => deltas.Max();
- public static Decimal UpperLimit(params Decimal[] deltas) => deltas.Min();
-
- private static Decimal HoldMinSocCurve(StatusRecord s)
- {
- // TODO: explain LowSOC curve
-
- var a = -2 * s.SalimaxConfig!.SelfDischargePower / s.SalimaxConfig.HoldSocZone;
- var b = -a * (s.SalimaxConfig.MinSoc + s.SalimaxConfig.HoldSocZone);
-
- return s.BatteriesStatus!.Combined.Soc * a + b; //this will use the avg batteries
- }
-
- private static Decimal ControlPower(Decimal measurement, Decimal target, Decimal p)
- {
- var error = target - measurement;
- return error * p;
- }
-}
\ No newline at end of file
diff --git a/csharp/App/SaliMax/src/Controller/ControlRecord.cs b/csharp/App/SaliMax/src/Controller/ControlRecord.cs
deleted file mode 100644
index ce7e4fc8f..000000000
--- a/csharp/App/SaliMax/src/Controller/ControlRecord.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using InnovEnergy.App.SaliMax.SaliMaxRelays;
-using InnovEnergy.App.SaliMax.SystemConfig;
-using InnovEnergy.Lib.Devices.Trumpf.TruConvertAc;
-using InnovEnergy.Lib.Devices.Trumpf.TruConvertDc;
-
-namespace InnovEnergy.App.SaliMax.Controller;
-
-public class ControlRecord
-{
- public TruConvertAcControl? AcControlRecord { get; init; }
- public TruConvertDcControl? DcControlRecord { get; init; }
- public SalimaxConfig? SalimaxConfig { get; init; } // we may have to create record of each
- public SaliMaxRelayStatus? SalimaxRelays { get; init; } // we may have to create record of each
-}
-
-
-
diff --git a/csharp/App/SaliMax/src/Controller/ControlTarget.cs b/csharp/App/SaliMax/src/Controller/ControlTarget.cs
deleted file mode 100644
index 1e279f8d3..000000000
--- a/csharp/App/SaliMax/src/Controller/ControlTarget.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-namespace InnovEnergy.App.SaliMax.Controller;
-
-public enum ControlTarget // TODO to delete
-{
- None = 0,
- GridAc = 1,
- BatteryDc = 2,
- InverterAc = 3,
- InverterDc = 4,
-}
\ No newline at end of file
diff --git a/csharp/App/SaliMax/src/Controller/Controller.cs b/csharp/App/SaliMax/src/Controller/Controller.cs
deleted file mode 100644
index 5638da544..000000000
--- a/csharp/App/SaliMax/src/Controller/Controller.cs
+++ /dev/null
@@ -1,514 +0,0 @@
-using InnovEnergy.App.SaliMax.SaliMaxRelays;
-using InnovEnergy.App.SaliMax.SystemConfig;
-using InnovEnergy.Lib.Devices.Trumpf.TruConvert;
-using InnovEnergy.Lib.Devices.Trumpf.TruConvertAc;
-using InnovEnergy.Lib.Devices.Trumpf.TruConvertDc;
-using InnovEnergy.Lib.Time.Unix;
-using InnovEnergy.Lib.Utils;
-
-using InnovEnergy.Lib.Devices.Trumpf.TruConvertAc.Enums;
-
-using static InnovEnergy.App.SaliMax.SaliMaxRelays.RelayState;
-
-
-namespace InnovEnergy.App.SaliMax.Controller;
-
-public static class Controller
-{
- private static readonly UnixTimeSpan MaxTimeWithoutEoc = UnixTimeSpan.FromDays(7);
-
- private static Boolean _mustChargeFlag = false;
-
- private static readonly TimeSpan CommunicationTimeout = TimeSpan.FromSeconds(10);
-
- public static readonly Int16 MaxmimumAllowedBatteryTemp = 315;
-
- private static UInt16 _numberOfInverters;
-
- private static UInt16 GetSaliMaxState(StatusRecord statusRecord)
- {
- if (statusRecord.SaliMaxRelayStatus is null)
- throw new ArgumentNullException(nameof(SaliMaxRelayStatus) + " is not available"); //TODO
-
- if (statusRecord.InverterStatus is null)
- throw new ArgumentNullException(nameof(statusRecord.InverterStatus) + " is not available"); //TODO
-
- return statusRecord switch
- {
- {
- SaliMaxRelayStatus.K1: Open, SaliMaxRelayStatus.K2: Open, SaliMaxRelayStatus.K3: Open,
- InverterStatus.MainState: not MainState.Operation
- } => 1,
- {
- SaliMaxRelayStatus.K1: Open, SaliMaxRelayStatus.K2: Open, SaliMaxRelayStatus.K3: Closed,
- InverterStatus.MainState: not MainState.Operation
- } => 2,
- {
- SaliMaxRelayStatus.K1: Open, SaliMaxRelayStatus.K2: Closed, SaliMaxRelayStatus.K3: Open,
- InverterStatus.MainState: not MainState.Operation
- } => 3,
- {
- SaliMaxRelayStatus.K1: Open, SaliMaxRelayStatus.K2: Closed, SaliMaxRelayStatus.K3: Closed,
- InverterStatus.MainState: not MainState.Operation
- } => 4,
- {
- SaliMaxRelayStatus.K1: Closed, SaliMaxRelayStatus.K2: Open, SaliMaxRelayStatus.K3: Open,
- InverterStatus.MainState: not MainState.Operation
- } => 5,
- {
- SaliMaxRelayStatus.K1: Closed, SaliMaxRelayStatus.K2: Open, SaliMaxRelayStatus.K3: Closed,
- InverterStatus.MainState: not MainState.Operation
- } => 6,
- {
- SaliMaxRelayStatus.K1: Closed, SaliMaxRelayStatus.K2: Closed, SaliMaxRelayStatus.K3: Open,
- InverterStatus.MainState: not MainState.Operation
- } => 7,
- {
- SaliMaxRelayStatus.K1: Closed, SaliMaxRelayStatus.K2: Closed, SaliMaxRelayStatus.K3: Closed,
- InverterStatus.MainState: not MainState.Operation
- } => 8,
-
- //Grid-Tied 400V/50 Hz
- {
- SaliMaxRelayStatus.K1: Open, SaliMaxRelayStatus.K2: Open, SaliMaxRelayStatus.K3: Open,
- InverterStatus.GridType: AcDcGridType.GridTied400V50Hz
- } => 9,
- {
- SaliMaxRelayStatus.K1: Closed, SaliMaxRelayStatus.K2: Open, SaliMaxRelayStatus.K3: Open,
- InverterStatus.GridType: AcDcGridType.GridTied400V50Hz
- } => 10,
- {
- SaliMaxRelayStatus.K1: Open, SaliMaxRelayStatus.K2: Closed, SaliMaxRelayStatus.K3: Open,
- InverterStatus.GridType: AcDcGridType.GridTied400V50Hz
- } => 11,
- {
- SaliMaxRelayStatus.K1: Closed, SaliMaxRelayStatus.K2: Closed, SaliMaxRelayStatus.K3: Open,
- InverterStatus.GridType: AcDcGridType.GridTied400V50Hz
- } => 12,
- {
- SaliMaxRelayStatus.K1: Open, SaliMaxRelayStatus.K2: Open, SaliMaxRelayStatus.K3: Closed,
- InverterStatus.GridType: AcDcGridType.GridTied400V50Hz
- } => 13,
- {
- SaliMaxRelayStatus.K1: Closed, SaliMaxRelayStatus.K2: Open, SaliMaxRelayStatus.K3: Closed,
- InverterStatus.GridType: AcDcGridType.GridTied400V50Hz
- } => 14,
- {
- SaliMaxRelayStatus.K1: Open, SaliMaxRelayStatus.K2: Closed, SaliMaxRelayStatus.K3: Closed,
- InverterStatus.GridType: AcDcGridType.GridTied400V50Hz
- } => 15,
- {
- SaliMaxRelayStatus.K1: Closed, SaliMaxRelayStatus.K2: Closed, SaliMaxRelayStatus.K3: Closed,
- InverterStatus.GridType: AcDcGridType.GridTied400V50Hz
- } => 16,
-
- //Island 400V / 50Hz
- {
- SaliMaxRelayStatus.K1: Open, SaliMaxRelayStatus.K2: Open, SaliMaxRelayStatus.K3: Open,
- InverterStatus.GridType: AcDcGridType.Island400V50Hz
- } => 17,
- {
- SaliMaxRelayStatus.K1: Closed, SaliMaxRelayStatus.K2: Open, SaliMaxRelayStatus.K3: Open,
- InverterStatus.GridType: AcDcGridType.Island400V50Hz
- } => 18,
- {
- SaliMaxRelayStatus.K1: Open, SaliMaxRelayStatus.K2: Closed, SaliMaxRelayStatus.K3: Open,
- InverterStatus.GridType: AcDcGridType.Island400V50Hz
- } => 19,
- {
- SaliMaxRelayStatus.K1: Closed, SaliMaxRelayStatus.K2: Closed, SaliMaxRelayStatus.K3: Open,
- InverterStatus.GridType: AcDcGridType.Island400V50Hz
- } => 20,
- {
- SaliMaxRelayStatus.K1: Open, SaliMaxRelayStatus.K2: Open, SaliMaxRelayStatus.K3: Closed, //this is wrong
- InverterStatus.GridType: AcDcGridType.Island400V50Hz
- } => 21,
- {
- SaliMaxRelayStatus.K1: Closed, SaliMaxRelayStatus.K2: Open, SaliMaxRelayStatus.K3: Closed,
- InverterStatus.GridType: AcDcGridType.Island400V50Hz
- } => 22,
- {
- SaliMaxRelayStatus.K1: Open, SaliMaxRelayStatus.K2: Closed, SaliMaxRelayStatus.K3: Closed,
- InverterStatus.GridType: AcDcGridType.Island400V50Hz
- } => 23,
- {
- SaliMaxRelayStatus.K1: Closed, SaliMaxRelayStatus.K2: Closed, SaliMaxRelayStatus.K3: Closed,
- InverterStatus.GridType: AcDcGridType.Island400V50Hz
- } => 24,
-
-
- _ => throw new ArgumentOutOfRangeException(nameof(statusRecord), statusRecord, null)
- };
- }
-
- public static ControlRecord SaliMaxControl(StatusRecord statusRecord)
- {
- var currentSaliMaxState = GetSaliMaxState(statusRecord);
-
- UInt16 acSlaveId = 1;
- var resetInverterAlarm = CheckInverterAlarms(statusRecord, currentSaliMaxState).WriteLine(" reset Alarm");
- var resetDcAlarm = CheckDcDcAlarms(statusRecord);
-
- var lastEocTime = GetLastEocTime(statusRecord);
- var timeSinceLastEoc = UnixTime.Now - lastEocTime;
-
- _numberOfInverters = (UInt16)statusRecord.InverterStatus!.NumberOfConnectedSlaves ;
- _mustChargeFlag = timeSinceLastEoc > MaxTimeWithoutEoc;
-
- var noGridMeter = statusRecord.GridMeterStatus == null;
- var saliMaxConfig = statusRecord.SalimaxConfig with { LastEoc = lastEocTime };
-
- ExplainState(currentSaliMaxState);
-
- const RelayState k2Relay = Closed;
-
- var acPowerStageEnable = StateConfig.AcPowerStageEnableStates.Contains(currentSaliMaxState); //this is logical incorrect, find better way
-
- var dcPowerStageEnable = statusRecord.BatteriesStatus is not null; // TODO this is to check, Can be the batteries Status be null?
-
- var salimaxRelay = statusRecord.SaliMaxRelayStatus! with { K2 = k2Relay }; // to check // this is must be control
-
- if (resetInverterAlarm)
- {
- acPowerStageEnable = !resetInverterAlarm ;
- acSlaveId = 0;
- }
-
- if (resetDcAlarm)
- {
- dcPowerStageEnable = !resetDcAlarm ;
- }
-
- acSlaveId.WriteLine(" AcSlave @");
-
- if (statusRecord.BatteriesStatus == null)
- {
- Console.WriteLine(" No batteries");
- return new ControlRecord
- {
- AcControlRecord = Defaults.TruConvertAcControl with
- {
- SignedPowerNominalValue = 0,
- PowerStageEnable = acPowerStageEnable,
- CommunicationTimeout = CommunicationTimeout,
- SlaveAddress = acSlaveId,
- ResetsAlarmAndWarning = resetInverterAlarm
- },
- DcControlRecord = Defaults.TruConvertDcControl with
- {
- PowerStageEnable = dcPowerStageEnable,
- ResetsAlarmAndWarning = resetDcAlarm,
- TimeoutForCommunication = CommunicationTimeout
-
- },
- SalimaxConfig = saliMaxConfig, // must create a control of each
- SalimaxRelays = salimaxRelay, // must create a control of each
- };
- }
-
- if (noGridMeter)
- {
- // Blackout ( no grid meter and K1 opened automatically
- if (statusRecord.SaliMaxRelayStatus?.K1 == Open)
- {
- Console.WriteLine("Blackout occured");
- //FromGridTieToIsland();
- }
- // Grid meter not detected ( broken)
- else
- {
- Console.WriteLine("Grid meter not detected");
- }
-
- throw new NotImplementedException();
- }
-
- var newPowerSetPoint = CalculateNewPowerSetPoint(statusRecord);
-
- ////////////////////////// Control Record //////////////////////////
-
- var acControlRecord = Defaults.TruConvertAcControl with
- {
- PowerStageEnable = acPowerStageEnable,
- CommunicationTimeout = CommunicationTimeout,
- SignedPowerNominalValue = newPowerSetPoint,
- SlaveAddress = acSlaveId,
- ResetsAlarmAndWarning = resetInverterAlarm
- };
-
- var dcControlRecord = Defaults.TruConvertDcControl with
- {
- PowerStageEnable = dcPowerStageEnable,
- ResetsAlarmAndWarning = resetDcAlarm,
- TimeoutForCommunication = CommunicationTimeout
- };
-
-
- return new ControlRecord
- {
- AcControlRecord = acControlRecord,
- DcControlRecord = dcControlRecord,
- SalimaxConfig = saliMaxConfig,
- SalimaxRelays = salimaxRelay
- };
- }
-
- private static Decimal CalculateNewPowerSetPoint(StatusRecord statusRecord)
- {
- var currentPowerSetPoint = statusRecord.InverterStatus!.AcSignedPowerValue;
-
- var limitReason = "no limit";
- var goal = "no goal";
- var delta = 0m;
-
- if (_mustChargeFlag)
- {
- goal = "Calibration Charge";
- delta = statusRecord.ControlInverterPower(statusRecord.SalimaxConfig.MaxInverterPower);
- }
- else if (statusRecord.BatteriesStatus!.Combined.Soc < statusRecord.SalimaxConfig.MinSoc) // TODO
- {
- goal = $"reach min SOC (Min soc: {statusRecord.SalimaxConfig.MinSoc})";
- delta = statusRecord.ControlInverterPower(statusRecord.SalimaxConfig
- .MaxInverterPower); // this the new mustChargeToMinSoc
- }
- else
- {
- goal = $"optimize self consumption (Grid set point: {statusRecord.SalimaxConfig.GridSetPoint})";
- delta = statusRecord.ControlGridPower(statusRecord.SalimaxConfig.GridSetPoint);
- }
-
- ////////////////////////// Upper Limits //////////////////////////
-
- var inverterAc2DcLimitPower = statusRecord.ControlInverterPower(statusRecord.SalimaxConfig.MaxInverterPower);
-
- if (delta > inverterAc2DcLimitPower)
- {
- limitReason = "limited by max inverter Ac to Dc power";
- delta = inverterAc2DcLimitPower;
- }
-
- var batteryChargingLimitPower = statusRecord.ControlBatteryPower(statusRecord.BatteriesStatus!.Combined.MaxChargingPower);
-
- if (delta > batteryChargingLimitPower)
- {
- limitReason = "limited by max battery charging power";
- delta = batteryChargingLimitPower;
- }
-
- ////////////////////////// Lower Limits //////////////////////////
-
- var inverterDc2AcLimitPower = statusRecord.ControlInverterPower(-statusRecord.SalimaxConfig.MaxInverterPower);
-
- if (delta < inverterDc2AcLimitPower)
- {
- limitReason = $"limited by max inverter Dc to Ac power: {-statusRecord.SalimaxConfig.MaxInverterPower}W";
- delta = inverterDc2AcLimitPower;
- }
-
- var batteryDischargingLimitPower =
- statusRecord.ControlBatteryPower(statusRecord.BatteriesStatus.Combined.MaxDischargingPower); // TODO change to avg battery
-
- if (delta < batteryDischargingLimitPower)
- {
- limitReason =
- $"limited by max battery discharging power: {statusRecord.BatteriesStatus.Combined.MaxDischargingPower}";// TODO change to avg battery
-
- delta = batteryDischargingLimitPower;
- }
-
- var keepMinSocLimitDelta = statusRecord.ControlLowBatterySoc();
- if (delta < keepMinSocLimitDelta)
- {
- limitReason =
- $"limiting discharging power in order to stay above min SOC: {statusRecord.SalimaxConfig.MinSoc}%";
- delta = keepMinSocLimitDelta;
- }
-
- // if (statusRecord.BatteriesStatus[0]!.Temperature >= 300) //must not reduce the delta
- // {
- // var softLandingFactor = (MaxmimumAllowedBatteryTemp - statusRecord.BatteriesStatus[0]!.Temperature) / 15; //starting softlanding from 300 degree
- // limitReason =
- // $"limiting discharging power in order to stay keep the battery temp below 315°: {statusRecord.BatteriesStatus[0]!.Temperature}°" + " Softlanding factor: " + softLandingFactor;
- // delta *= softLandingFactor;
- // }
-
- var newPowerSetPoint =
- DistributePower(currentPowerSetPoint + delta, statusRecord.SalimaxConfig.MaxInverterPower);
-
- ////////////////////// Print Data for Debug purpose //////////////////////////
-
- //
- goal.WriteLine();
- limitReason.WriteLine(" Limit reason");
- delta.WriteLine(" Delta");
- // "============".WriteLine();
- return newPowerSetPoint;
- }
-
- private static State TargetState(State currentState)
- {
- return currentState switch
- {
- State.State1 => State.State17,
- State.State17 => State.State21,
- State.State21 => State.State22,
- State.State22 => State.State6,
- State.State6 => State.State2,
- State.State2 => State.State4,
- State.State4 => State.State12,
- State.State12 => State.State16,
- State.State16 => State.State15,
- State.State15 => State.State13,
- State.State13 => State.State9,
- State.State9 => State.State1,
- _ => throw new Exception("Unknown State!") // maybe not throwing an exception, instead write on the log file
- };
- }
-
-
- private static void ExplainState(UInt16 s)
- {
- Console.WriteLine("State: " + s);
- switch (s)
- {
- case 1:
- Console.WriteLine(" Inverter is Off");
- Console.WriteLine(" Turning on power stage of inverter");
- Console.WriteLine(" grid type = island 400V / 50Hz");
- break;
- case 17:
- Console.WriteLine(" Waiting for K3 to close");
- break;
- case 21:
- Console.WriteLine(" Inverter is in Island Mode");
- Console.WriteLine(" Waiting for K1 to close to leave it");
- break;
- case 22:
- Console.WriteLine(" K1 is closed");
- Console.WriteLine(" Turning off power stage of inverter");
- break;
- case 6:
- Console.WriteLine(" Waiting for K3 to open");
- break;
- case 2:
- Console.WriteLine(" K3 is open");
- Console.WriteLine(" Closing the K2");
- break;
- case 4:
- Console.WriteLine(" K2 is Closed");
- Console.WriteLine(" Turning on power stage of inverter");
- Console.WriteLine(" grid type = grid-tied 400V / 50Hz");
- break;
- case 12:
- Console.WriteLine(" Waiting for K3 to close");
- break;
- case 16:
- Console.WriteLine(" Inverter is in grid-tie");
- Console.WriteLine(" Waiting for K1 to open to leave it");
- break;
- case 15:
- Console.WriteLine(" K1 is open");
- Console.WriteLine(" Opening the K2");
- break;
- case 13:
- Console.WriteLine(" K2 is open");
- Console.WriteLine(" Waiting for K3 to open");
- break;
- case 9:
- Console.WriteLine(" K3 is open");
- Console.WriteLine(" Turning off power stage of inverter");
- break;
- default:
- Console.WriteLine("Unknown State!");
- File.AppendAllTextAsync(Config.LogSalimaxLog, String.Join(Environment.NewLine, UnixTime.Now + "Unknown State!"));
- break;
- }
- }
-
-
- public static void WriteControlRecord(ControlRecord controlRecord,
- TruConvertAcDevice acDevice,
- TruConvertDcDevice dcDevice,
- SaliMaxRelaysDevice saliMaxRelaysDevice)
- {
- controlRecord.SalimaxConfig?.Save();
-
- var acControlRecord = controlRecord.AcControlRecord;
- var dcControlRecord = controlRecord.DcControlRecord;
-
- if (acControlRecord != null && dcControlRecord != null)
- {
- acDevice.WriteControl(acControlRecord);
-
- dcDevice.WriteControl(dcControlRecord);
- }
- else
- {
- Console.WriteLine("AcControl and DcControl record is empty");
- File.AppendAllTextAsync(Config.LogSalimaxLog, String.Join(Environment.NewLine, UnixTime.Now + "AcControl and DcControl record is empty!"));
- }
- }
-
- private static UnixTime GetLastEocTime(StatusRecord statusRecord)
- { if (statusRecord.BatteriesStatus != null)
- {
- if (statusRecord.BatteriesStatus!.Combined.EocReached)
- {
- Console.WriteLine("battery has reached EOC");
- File.AppendAllTextAsync(Config.LogSalimaxLog,
- String.Join(Environment.NewLine,
- UnixTime.Now + "battery has reached EOC"));
- return UnixTime.Now;
- }
- }
- else
- {
- Console.WriteLine("No battery Detected");
- }
- return statusRecord.SalimaxConfig.LastEoc;
- }
-
- private static Decimal DistributePower(Decimal powerSetPoint, Int32 maximumPowerSetPoint)
- {
- var inverterPowerSetPoint = powerSetPoint / _numberOfInverters;
- return inverterPowerSetPoint.Clamp(-maximumPowerSetPoint, maximumPowerSetPoint);
- ;
- }
-
- private static Boolean CheckDcDcAlarms(StatusRecord s)
- {
- s.DcDcStatus?.Alarms.Count.WriteLine(" Dc Alarm count");
- if ( s.DcDcStatus?.Alarms.Count > 0 &&
- s.DcDcStatus?.PowerOperation == false)
- {
- File.AppendAllTextAsync(Config.LogSalimaxLog, UnixTime.Now + " " + s.DcDcStatus.Alarms);
- return true;
- }
-
- return false;
- }
-
- private static Boolean CheckInverterAlarms(StatusRecord s, UInt16 state)
- {
- s.InverterStatus?.Alarms.Count.WriteLine(" Ac Alarm count");
- if ( s.InverterStatus?.Alarms.Count > 0 )
- {
- File.AppendAllTextAsync(Config.LogSalimaxLog, UnixTime.Now + " " + s.InverterStatus.Alarms[0]); // Todo write every alarm in he alarm list
- return true;
- }
-
- return false;
- }
-
- private static Boolean FromGridTieToIsland(StatusRecord s) //this is must be called when the K1 open
- {
- //check again the K1 is open
- //s.sal = true;
-
-
- return true;
- }
-
- //TODO: Include number of connected batteries
-}
\ No newline at end of file
diff --git a/csharp/App/SaliMax/src/Controller/SaliMaxState.cs b/csharp/App/SaliMax/src/Controller/SaliMaxState.cs
deleted file mode 100644
index be7470fae..000000000
--- a/csharp/App/SaliMax/src/Controller/SaliMaxState.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-namespace InnovEnergy.App.SaliMax.Controller;
-
-public struct SaliMaxState
-{
- private Int32 State { get; }
-
- public SaliMaxState(Int32 state)
- {
- if (state < 1 || state >24)
- throw new ArgumentOutOfRangeException(nameof(state));
-
- State = state;
- }
-}
\ No newline at end of file
diff --git a/csharp/App/SaliMax/src/Controller/State.cs b/csharp/App/SaliMax/src/Controller/State.cs
deleted file mode 100644
index 7634d1d66..000000000
--- a/csharp/App/SaliMax/src/Controller/State.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-
-namespace InnovEnergy.App.SaliMax.Controller;
-
-public enum State : Int16
-{
- State1 = 1,
- State2 = 2,
- State3 = 3,
- State4 = 4,
- State5 = 5,
- State6 = 6,
- State7 = 7,
- State8 = 8,
- State9 = 9,
- State10 = 10,
- State11 = 11,
- State12 = 12,
- State13 = 13,
- State14 = 14,
- State15 = 15,
- State16 = 16,
- State17 = 17,
- State18 = 18,
- State19 = 19,
- State20 = 20,
- State21 = 21,
- State22 = 22,
- State23 = 23,
- State24 = 24
-}
\ No newline at end of file
diff --git a/csharp/App/SaliMax/src/Controller/StateConfig.cs b/csharp/App/SaliMax/src/Controller/StateConfig.cs
deleted file mode 100644
index ce356d311..000000000
--- a/csharp/App/SaliMax/src/Controller/StateConfig.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-
-namespace InnovEnergy.App.SaliMax.Controller;
-
-public static class StateConfig
-{
- public static readonly IReadOnlyList AcPowerStageEnableStates = new[] {8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 };
-}
\ No newline at end of file
diff --git a/csharp/App/SaliMax/src/Controller/StatusRecord.cs b/csharp/App/SaliMax/src/Controller/StatusRecord.cs
deleted file mode 100644
index feebca76a..000000000
--- a/csharp/App/SaliMax/src/Controller/StatusRecord.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-using InnovEnergy.App.SaliMax.SaliMaxRelays;
-using InnovEnergy.App.SaliMax.SystemConfig;
-using InnovEnergy.Lib.Devices.AMPT;
-using InnovEnergy.Lib.Devices.Battery48TL;
-using InnovEnergy.Lib.Devices.EmuMeter;
-using InnovEnergy.Lib.Devices.Trumpf.TruConvertAc;
-using InnovEnergy.Lib.Devices.Trumpf.TruConvertDc;
-using InnovEnergy.Lib.StatusApi;
-
-namespace InnovEnergy.App.SaliMax.Controller;
-
-public record StatusRecord
-{
- public TruConvertAcStatus? InverterStatus { get; init; }
- public TruConvertDcStatus? DcDcStatus { get; init; }
- public CombinedStatus? BatteriesStatus { get; init; }
-
- public EmuMeterStatus? GridMeterStatus { get; init; }
- public SaliMaxRelayStatus? SaliMaxRelayStatus { get; init; }
- public AmptCommunicationUnitStatus? AmptStatus { get; init; }
- public EmuMeterStatus? AcInToAcOutMeterStatus { get; init; }
- public SalimaxConfig SalimaxConfig { get; init; } = null!;
-}
\ No newline at end of file
diff --git a/csharp/App/SaliMax/src/Ess/ControlRecord.cs b/csharp/App/SaliMax/src/Ess/ControlRecord.cs
new file mode 100644
index 000000000..76c5afcff
--- /dev/null
+++ b/csharp/App/SaliMax/src/Ess/ControlRecord.cs
@@ -0,0 +1,15 @@
+// using InnovEnergy.App.SaliMax.SaliMaxRelays;
+// using InnovEnergy.App.SaliMax.SystemConfig;
+//
+// namespace InnovEnergy.App.SaliMax.Controller;
+//
+// public class ControlRecord
+// {
+// public TruConvertAcControl? AcControlRecord { get; init; }
+// public TruConvertDcControl? DcControlRecord { get; init; }
+// public Config? SalimaxConfig { get; init; } // we may have to create record of each
+// public SaliMaxRelayControl? SalimaxRelays { get; init; } // we may have to create record of each
+// }
+//
+//
+//
diff --git a/csharp/App/SaliMax/src/Ess/Controller.cs b/csharp/App/SaliMax/src/Ess/Controller.cs
new file mode 100644
index 000000000..e13c6667d
--- /dev/null
+++ b/csharp/App/SaliMax/src/Ess/Controller.cs
@@ -0,0 +1,242 @@
+using InnovEnergy.Lib.Devices.Battery48TL.DataTypes;
+using InnovEnergy.Lib.Devices.Trumpf.TruConvertAc;
+using InnovEnergy.Lib.Devices.Trumpf.TruConvertAc.DataTypes;
+using InnovEnergy.Lib.Time.Unix;
+using InnovEnergy.Lib.Utils;
+
+
+namespace InnovEnergy.App.SaliMax.Ess;
+
+public static class Controller
+{
+ private static readonly UnixTimeSpan MaxTimeWithoutEoc = UnixTimeSpan.FromDays(7); // TODO: move to config
+ private static readonly TimeSpan CommunicationTimeout = TimeSpan.FromSeconds(10);
+
+
+
+
+ public static EssMode SelectControlMode(this StatusRecord s)
+ {
+ return EssMode.OptimizeSelfConsumption;
+
+ // return s.SystemState.Id != 16 ? EssMode.Off
+ // : s.MustHeatBatteries() ? EssMode.HeatBatteries
+ // : s.MustDoCalibrationCharge() ? EssMode.CalibrationCharge
+ // : s.MustReachMinSoc() ? EssMode.ReachMinSoc
+ // : s.GridMeter is null ? EssMode.NoGridMeter
+ // : EssMode.OptimizeSelfConsumption;
+ }
+
+
+ public static EssControl ControlEss(this StatusRecord s)
+ {
+ // var hasPreChargeAlarm = s.HasPreChargeAlarm();
+ //
+ // if (hasPreChargeAlarm)
+ // "PreChargeAlarm".Log();
+
+ var mode = s.SelectControlMode();
+
+ if (mode is EssMode.Off or EssMode.NoGridMeter)
+ return new EssControl(mode, EssLimit.NoLimit, PowerCorrection: 0, PowerSetpoint: 0);
+
+ var essDelta = s.ComputePowerDelta(mode);
+
+ var unlimitedControl = new EssControl(mode, EssLimit.NoLimit, essDelta, 0);
+ var limitedControl = unlimitedControl
+ .LimitChargePower(s)
+ .LimitDischargePower(s);
+
+ var currentPowerSetPoint = s.CurrentPowerSetPoint();
+ var setpoint = currentPowerSetPoint + limitedControl.PowerCorrection;
+ //var setpoint = -11000;
+
+ return limitedControl with { PowerSetpoint = setpoint };
+ }
+
+ private static EssControl LimitChargePower(this EssControl control, StatusRecord s)
+ {
+ var maxInverterChargePower = s.ControlInverterPower(s.Config.MaxInverterPower);
+ var maxBatteryChargePower = s.MaxBatteryChargePower();
+
+ return control
+ .LimitChargePower(maxInverterChargePower, EssLimit.ChargeLimitedByInverterPower)
+ .LimitChargePower(maxBatteryChargePower, EssLimit.ChargeLimitedByBatteryPower);
+ }
+
+ private static EssControl LimitChargePower(this EssControl control, Double controlDelta, EssLimit reason)
+ {
+ return control.PowerCorrection > controlDelta
+ ? control with { LimitedBy = reason, PowerCorrection = controlDelta }
+ : control;
+ }
+
+
+ private static EssControl LimitDischargePower(this EssControl control, StatusRecord s)
+ {
+ var maxInverterDischargeDelta = s.ControlInverterPower(-s.Config.MaxInverterPower);
+ var maxBatteryDischargeDelta = s.Battery.Devices.Sum(b => b.MaxDischargePower);
+ var keepMinSocLimitDelta = s.ControlBatteryPower(s.HoldMinSocPower());
+
+ return control
+ .LimitDischargePower(maxInverterDischargeDelta, EssLimit.DischargeLimitedByInverterPower)
+ .LimitDischargePower(maxBatteryDischargeDelta , EssLimit.DischargeLimitedByBatteryPower)
+ .LimitDischargePower(keepMinSocLimitDelta , EssLimit.DischargeLimitedByMinSoc);
+ }
+
+
+ private static EssControl LimitDischargePower(this EssControl control, Double controlDelta, EssLimit reason)
+ {
+ return control.PowerCorrection < controlDelta
+ ? control with { LimitedBy = reason, PowerCorrection = controlDelta }
+ : control;
+ }
+
+
+ private static Double ComputePowerDelta(this StatusRecord s, EssMode mode) => mode switch
+ {
+ EssMode.HeatBatteries => s.ControlInverterPower(s.Config.MaxInverterPower),
+ EssMode.CalibrationCharge => s.ControlInverterPower(s.Config.MaxInverterPower),
+ EssMode.ReachMinSoc => s.ControlInverterPower(s.Config.MaxInverterPower),
+ EssMode.OptimizeSelfConsumption => s.ControlGridPower(s.Config.GridSetPoint),
+ _ => throw new ArgumentException(null, nameof(mode))
+ };
+
+
+
+ private static Boolean HasPreChargeAlarm(this StatusRecord statusRecord)
+ {
+ return statusRecord.DcDc.Alarms.Contains(Lib.Devices.Trumpf.TruConvertDc.Status.AlarmMessage.DcDcPrecharge);
+ }
+
+ private static Boolean MustHeatBatteries(this StatusRecord s)
+ {
+ var batteries = s.Battery.Devices;
+
+ 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)
+ {
+ return s.Battery.Devices.Sum(b => b.MaxChargePower);
+ }
+
+ private static Double CurrentPowerSetPoint(this StatusRecord s)
+ {
+ return s
+ .AcDc
+ .Devices
+ .Select(d =>
+ {
+ var acPowerControl = d.Control.Ac.Power;
+
+ return acPowerControl.L1.Active
+ + acPowerControl.L2.Active
+ + acPowerControl.L3.Active;
+ })
+ .Sum(p => p);
+ }
+
+ private static Boolean MustReachMinSoc(this StatusRecord s)
+ {
+ var batteries = s.Battery.Devices;
+
+ return batteries.Count > 0
+ && batteries.Any(b => b.Soc < s.Config.MinSoc);
+ }
+
+
+ private static Boolean MustDoCalibrationCharge(this StatusRecord statusRecord)
+ {
+ var config = statusRecord.Config;
+
+ if (statusRecord.Battery.Eoc)
+ {
+ "Batteries have reached EOC".Log();
+ config.LastEoc = UnixTime.Now;
+ return false;
+ }
+
+ return UnixTime.Now - statusRecord.Config.LastEoc > MaxTimeWithoutEoc;
+ }
+
+ private static Double DistributePower(this StatusRecord s, Double powerSetPoint)
+ {
+ var inverterPowerSetPoint = powerSetPoint / s.AcDc.Devices.Count;
+ return inverterPowerSetPoint.Clamp(-s.Config.MaxInverterPower, s.Config.MaxInverterPower);
+ }
+
+
+ public static Double ControlGridPower(this StatusRecord status, Double targetPower)
+ {
+ return ControlPower
+ (
+ measurement: status.GridMeter!.Ac.Power.Active,
+ target: targetPower,
+ pConstant: status.Config.PConstant
+ );
+ }
+
+ public static Double ControlInverterPower(this StatusRecord status, Double targetInverterPower)
+ {
+ return ControlPower
+ (
+ measurement: status.AcDc.Ac.Power.Active,
+ target: targetInverterPower,
+ pConstant: status.Config.PConstant
+ );
+ }
+
+ public static Double ControlBatteryPower(this StatusRecord status, Double targetBatteryPower)
+ {
+ return ControlPower
+ (
+ measurement: status.Battery.Devices.Sum(b => b.Dc.Power),
+ target: targetBatteryPower,
+ pConstant: status.Config.PConstant
+ );
+ }
+
+ private static Double HoldMinSocPower(this StatusRecord s)
+ {
+ // TODO: explain LowSOC curve
+
+ var batteries = s.Battery.Devices;
+
+ if (batteries.Count == 0)
+ return Double.NegativeInfinity;
+
+ var a = -2 * s.Config.SelfDischargePower * batteries.Count / s.Config.HoldSocZone;
+ var b = -a * (s.Config.MinSoc + s.Config.HoldSocZone);
+
+ return batteries.Min(d => d.Soc.Value) * a + b;
+ }
+
+ private static Double ControlPower(Double measurement, Double target, Double pConstant)
+ {
+ var error = target - measurement;
+ return error * pConstant;
+ }
+
+ private static Double ControlPowerWithIntegral(Double measurement, Double target, Double p, Double i)
+ {
+ var errorSum = 0; // this is must be sum of error
+ var error = target - measurement;
+ var kp = p * error;
+ var ki = i * errorSum;
+ return ki + kp;
+ }
+
+ private static IEnumerable InverterStates(this AcDcDevicesRecord acDcStatus)
+ {
+ return acDcStatus
+ .Devices
+ .Select(d => d.Status.InverterState.Current);
+ }
+
+}
\ No newline at end of file
diff --git a/csharp/App/SaliMax/src/Ess/EssControl.cs b/csharp/App/SaliMax/src/Ess/EssControl.cs
new file mode 100644
index 000000000..f2d9c9f19
--- /dev/null
+++ b/csharp/App/SaliMax/src/Ess/EssControl.cs
@@ -0,0 +1,14 @@
+using InnovEnergy.Lib.Units.Power;
+
+namespace InnovEnergy.App.SaliMax.Ess;
+
+public record EssControl
+(
+ EssMode Mode,
+ EssLimit LimitedBy,
+ ActivePower PowerCorrection,
+ ActivePower PowerSetpoint
+);
+
+
+
diff --git a/csharp/App/SaliMax/src/Ess/EssLimit.cs b/csharp/App/SaliMax/src/Ess/EssLimit.cs
new file mode 100644
index 000000000..8e2a3a582
--- /dev/null
+++ b/csharp/App/SaliMax/src/Ess/EssLimit.cs
@@ -0,0 +1,18 @@
+namespace InnovEnergy.App.SaliMax.Ess;
+
+public enum EssLimit
+{
+ NoLimit,
+ DischargeLimitedByMinSoc,
+ DischargeLimitedByBatteryPower,
+ DischargeLimitedByInverterPower,
+ ChargeLimitedByInverterPower,
+ ChargeLimitedByBatteryPower,
+}
+
+
+// limitedBy = $"limiting discharging power in order to stay above min SOC: {s.Config.MinSoc}%";
+// limitedBy = $"limited by max battery discharging power: {maxDischargePower}";
+// limitedBy = $"limited by max inverter Dc to Ac power: {-s.Config.MaxInverterPower}W";
+// limitedBy = $"limited by max battery charging power: {maxChargePower}";
+// limitedBy = "limited by max inverter Ac to Dc power";
\ No newline at end of file
diff --git a/csharp/App/SaliMax/src/Ess/EssMode.cs b/csharp/App/SaliMax/src/Ess/EssMode.cs
new file mode 100644
index 000000000..da0f71012
--- /dev/null
+++ b/csharp/App/SaliMax/src/Ess/EssMode.cs
@@ -0,0 +1,11 @@
+namespace InnovEnergy.App.SaliMax.Ess;
+
+public enum EssMode
+{
+ Off,
+ HeatBatteries,
+ CalibrationCharge,
+ ReachMinSoc,
+ NoGridMeter,
+ OptimizeSelfConsumption
+}
\ No newline at end of file
diff --git a/csharp/App/SaliMax/src/Ess/StatusRecord.cs b/csharp/App/SaliMax/src/Ess/StatusRecord.cs
new file mode 100644
index 000000000..7a1175483
--- /dev/null
+++ b/csharp/App/SaliMax/src/Ess/StatusRecord.cs
@@ -0,0 +1,24 @@
+using InnovEnergy.App.SaliMax.SaliMaxRelays;
+using InnovEnergy.App.SaliMax.System;
+using InnovEnergy.App.SaliMax.SystemConfig;
+using InnovEnergy.Lib.Devices.AMPT;
+using InnovEnergy.Lib.Devices.Battery48TL;
+using InnovEnergy.Lib.Devices.EmuMeter;
+using InnovEnergy.Lib.Devices.Trumpf.TruConvertAc;
+using InnovEnergy.Lib.Devices.Trumpf.TruConvertDc;
+
+namespace InnovEnergy.App.SaliMax.Ess;
+
+public record StatusRecord
+{
+ public AcDcDevicesRecord AcDc { get; init; } = null!;
+ public DcDcDevicesRecord DcDc { get; init; } = null!;
+ public Battery48TlRecords Battery { get; init; } = null!;
+ public EmuMeterRegisters? GridMeter { get; init; }
+ public EmuMeterRegisters? CriticalLoad { get; init; }
+ public RelaysRecord? Relays { get; init; }
+ public AmptStatus Mppt { get; init; } = null!;
+ public Config Config { get; init; } = null!;
+ public SystemState SystemState { get; } = new SystemState();
+ public EssControl Ess { get; set; } = null!;
+}
\ No newline at end of file
diff --git a/csharp/App/SaliMax/src/Logger.cs b/csharp/App/SaliMax/src/Logger.cs
new file mode 100644
index 000000000..56414bf66
--- /dev/null
+++ b/csharp/App/SaliMax/src/Logger.cs
@@ -0,0 +1,20 @@
+using Microsoft.Extensions.Logging;
+
+namespace InnovEnergy.App.SaliMax;
+
+public static class Logger
+{
+ // Specify the maximum log file size in bytes (e.g., 1 MB)
+
+ private const Int32 MaxFileSizeBytes = 1024 * 1024; // TODO: move to settings
+ private const Int32 MaxLogFileCount = 1000; // TODO: move to settings
+ private const String LogFilePath = "LogDirectory/log.txt"; // TODO: move to settings
+
+ private static readonly ILogger _logger = new CustomLogger(LogFilePath, MaxFileSizeBytes, MaxLogFileCount);
+
+ public static T Log(this T t) where T : notnull
+ {
+ // _logger.LogInformation(t.ToString()); // TODO: check warning
+ return t;
+ }
+}
\ No newline at end of file
diff --git a/csharp/App/SaliMax/src/Program.cs b/csharp/App/SaliMax/src/Program.cs
index 84245bb28..b3f7ee55a 100644
--- a/csharp/App/SaliMax/src/Program.cs
+++ b/csharp/App/SaliMax/src/Program.cs
@@ -1,196 +1,247 @@
using System.Diagnostics;
+using System.Runtime.InteropServices;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
using Flurl.Http;
-using InnovEnergy.App.SaliMax.Controller;
+using InnovEnergy.App.SaliMax.Ess;
using InnovEnergy.App.SaliMax.SaliMaxRelays;
+using InnovEnergy.App.SaliMax.System;
using InnovEnergy.App.SaliMax.SystemConfig;
using InnovEnergy.Lib.Devices.AMPT;
using InnovEnergy.Lib.Devices.Battery48TL;
using InnovEnergy.Lib.Devices.EmuMeter;
+using InnovEnergy.Lib.Devices.Trumpf.SystemControl;
+using InnovEnergy.Lib.Devices.Trumpf.SystemControl.DataTypes;
using InnovEnergy.Lib.Devices.Trumpf.TruConvertAc;
+using InnovEnergy.Lib.Devices.Trumpf.TruConvertAc.DataTypes;
using InnovEnergy.Lib.Devices.Trumpf.TruConvertDc;
+using InnovEnergy.Lib.Protocols.Modbus.Channels;
using InnovEnergy.Lib.Time.Unix;
+using InnovEnergy.Lib.Units;
+using InnovEnergy.Lib.Units.Composite;
using InnovEnergy.Lib.Utils;
+using static InnovEnergy.Lib.Devices.Trumpf.SystemControl.DataTypes.SystemConfig;
+using Exception = System.Exception;
#pragma warning disable IL2026
-
namespace InnovEnergy.App.SaliMax;
internal static class Program
{
+ [DllImport("libsystemd.so.0")]
+ private static extern Int32 sd_notify(Int32 unsetEnvironment, String state);
+
private const UInt32 UpdateIntervalSeconds = 2;
+
+ private static readonly Byte[] BatteryNodes = { 2, 3, 4, 5, 6 };
+ private const String BatteryTty = "/dev/ttyUSB0";
+
+ // private const String RelaysIp = "10.0.1.1"; // "192.168.1.242";
+ // private const String TruConvertAcIp = "10.0.2.1"; // "192.168.1.2";
+ // private const String TruConvertDcIp = "10.0.3.1"; // "192.168.1.3";
+ // private const String GridMeterIp = "10.0.4.1"; // "192.168.1.241";
+ // private const String InternalMeter = "10.0.4.2"; // "192.168.1.241";
+ // private const String AmptIp = "10.0.5.1"; // "192.168.1.249";
+
+
+ private static readonly TcpChannel TruConvertAcChannel = new TcpChannel("localhost", 5001);
+ private static readonly TcpChannel TruConvertDcChannel = new TcpChannel("localhost", 5002);
+ private static readonly TcpChannel GridMeterChannel = new TcpChannel("localhost", 5003);
+ private static readonly TcpChannel AcOutLoadChannel = new TcpChannel("localhost", 5004);
+ private static readonly TcpChannel AmptChannel = new TcpChannel("localhost", 5005);
+ private static readonly TcpChannel RelaysChannel = new TcpChannel("localhost", 5006);
+ private static readonly TcpChannel BatteriesChannel = new TcpChannel("localhost", 5007);
+
+ private static readonly S3Config S3Config = new S3Config
+ {
+ Bucket = "saliomameiringen",
+ Region = "sos-ch-dk-2",
+ Provider = "exo.io",
+ ContentType = "text/plain; charset=utf-8",
+ Key = "EXO2bf0cbd97fbfa75aa36ed46f",
+ Secret = "Bn1CDPqOG-XpDSbYjfIJxojcHTm391vZTc8z8l_fEPs"
+ };
+
public static async Task Main(String[] args)
{
- try
+ while (true)
{
- await Run();
- }
- catch (Exception e)
- {
- await File.AppendAllTextAsync(Config.LogSalimaxLog,
- String.Join(Environment.NewLine, UnixTime.Now + " \n" + e));
- throw;
+ try
+ {
+ await Run();
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine(e);
+ }
}
+
}
private static async Task Run()
{
Console.WriteLine("Starting SaliMax");
-
- var batteryNodes = new Byte[] { 2, 3 };
-
- var batteryTty = "/dev/ttyUSB0";
- var relaysIp = "10.0.1.1";
- var truConvertAcIp = "10.0.2.1";
- var truConvertDcIp = "10.0.3.1";
- var gridMeterIp = "10.0.4.1";
- var internalMeter = "10.0.4.2";
- var amptIp = "10.0.5.1";
+ // Send the initial "service started" message to systemd
+ var sdNotifyReturn = sd_notify(0, "READY=1");
+ var battery48TlDevices = BatteryNodes
+ .Select(n => new Battery48TlDevice(BatteriesChannel, n))
+ .ToList();
+
+ var batteryDevices = new Battery48TlDevices(battery48TlDevices);
+ var acDcDevices = new TruConvertAcDcDevices(TruConvertAcChannel);
+ var dcDcDevices = new TruConvertDcDcDevices(TruConvertDcChannel);
+ var gridMeterDevice = new EmuMeterDevice(GridMeterChannel);
+ var criticalLoadMeterDevice = new EmuMeterDevice(AcOutLoadChannel);
+ var amptDevice = new AmptDevices(AmptChannel);
+ var saliMaxRelaysDevice = new RelaysDevice(RelaysChannel);
- var s3Config = new S3Config
+ StatusRecord ReadStatus() => new()
{
- Bucket = "saliomameiringen",
- Region = "sos-ch-dk-2",
- Provider = "exo.io",
- ContentType = "text/plain; charset=utf-8",
- Key = "EXO2bf0cbd97fbfa75aa36ed46f",
- Secret = "Bn1CDPqOG-XpDSbYjfIJxojcHTm391vZTc8z8l_fEPs"
+ AcDc = acDcDevices.Read(),
+ DcDc = dcDcDevices.Read(),
+ Battery = batteryDevices.Read(),
+ Relays = saliMaxRelaysDevice.Read(),
+ CriticalLoad = criticalLoadMeterDevice.Read(),
+ GridMeter = gridMeterDevice.Read(),
+ Mppt = amptDevice.Read(),
+ Config = Config.Load() // load from disk every iteration, so config can be changed while running
};
-
-#if DEBUG
- var inverterDevice = new TruConvertAcDevice("127.0.0.1", 5001);
- var dcDcDevice = new TruConvertDcDevice("127.0.0.1", 5002);
- var gridMeterDevice = new EmuMeterDevice("127.0.0.1", 5003);
- var saliMaxRelaysDevice = new SaliMaxRelaysDevice("127.0.0.1", 5004);
- var amptDevice = new AmptCommunicationUnit("127.0.0.1", 5005);
- var acInToAcOutMeterDevice = new EmuMeterDevice("127.0.0.1", 5003); // TODO: use real device
- var secondBattery48TlDevice = Battery48TlDevice.Fake();
- var firstBattery48TlDevice =Battery48TlDevice.Fake();;
- var salimaxConfig = new SalimaxConfig();
-#else
- var batteries = batteryNodes.Select(n => new Battery48TlDevice(batteryTty, n)).ToList();
-
-
- var inverterDevice = new TruConvertAcDevice(truConvertAcIp);
- var dcDcDevice = new TruConvertDcDevice(truConvertDcIp);
-
- var gridMeterDevice = new EmuMeterDevice(gridMeterIp);
- var acInToAcOutMeterDevice = new EmuMeterDevice(internalMeter); // TODO: use real device
-
- var amptDevice = new AmptCommunicationUnit(amptIp);
-
- var saliMaxRelaysDevice = new SaliMaxRelaysDevice(relaysIp);
- var salimaxConfig = new SalimaxConfig();
-#endif
- // This is will be always add manually ? or do we need to read devices automatically in a range of IP @
-
-
-
- StatusRecord ReadStatus()
+ void WriteControl(StatusRecord r)
{
- var combinedBatteryStatus = batteries
- .Select(b => b.ReadStatus())
- .NotNull()
- .ToList()
- .Combine();
+ if (r.Relays is not null)
+ saliMaxRelaysDevice.Write(r.Relays);
- // var dcDcStatusArray = dcDcDevices.Select(b => b.ReadStatus()).NotNull().ToArray();
- // var inverterStatusArray = inverterDevices.Select(b => b.ReadStatus()).NotNull().ToArray();
-
- return new StatusRecord
- {
- InverterStatus = inverterDevice.ReadStatus(),
- DcDcStatus = dcDcDevice.ReadStatus(),
- BatteriesStatus = combinedBatteryStatus,
- AcInToAcOutMeterStatus = acInToAcOutMeterDevice.ReadStatus(),
- GridMeterStatus = gridMeterDevice.ReadStatus(),
- SaliMaxRelayStatus = saliMaxRelaysDevice.ReadStatus(),
- AmptStatus = amptDevice.ReadStatus(),
- SalimaxConfig = salimaxConfig.Load().Result,
- };
+ acDcDevices.Write(r.AcDc);
+ dcDcDevices.Write(r.DcDc);
}
- var startTime = UnixTime.Now;
- const Int32 delayTime = 10;
-
Console.WriteLine("press ctrl-C to stop");
-
-
+
while (true)
{
- var t = UnixTime.Now;
- while (t.Ticks % UpdateIntervalSeconds != 0)
- {
- await Task.Delay(delayTime);
- t = UnixTime.Now;
- }
-
- var status = ReadStatus();
-#if BatteriesAllowed
+ sd_notify(0, "WATCHDOG=1");
- var jsonLog = status.ToLog(t);
- await UploadTimeSeries(s3Config, jsonLog, t);
- var controlRecord = Controller.Controller.SaliMaxControl(status);
- Controller.Controller.WriteControlRecord(controlRecord, inverterDevice, dcDcDevice, saliMaxRelaysDevice);
+ var t = UnixTime.FromTicks(UnixTime.Now.Ticks / 2 * 2);
+
+ t.ToUtcDateTime().WriteLine();
+
+ var record = ReadStatus();
+
+ record.AcDc.ResetAlarms();
+ record.DcDc.ResetAlarms();
- //JsonSerializer.Serialize(jsonLog, JsonOptions).WriteLine(ConsoleColor.DarkBlue);
-#endif
- Topology.Print(status);
+ record.ControlSystemState();
- while (UnixTime.Now == t)
- await Task.Delay(delayTime);
+ var essControl = record.ControlEss();
+
+ record.Ess = essControl;
+
+ record.AcDc.SystemControl.ApplyDefaultSettings();
+ record.DcDc.SystemControl.ApplyDefaultSettings();
+
+ DistributePower(record, essControl);
+
+ "===========================================".WriteLine();
+
+ WriteControl(record);
+
+ await UploadCsv(record, t);
+
+ var emuMeterRegisters = record.GridMeter;
+ if (emuMeterRegisters is not null)
+ {
+ emuMeterRegisters.Ac.Power.Active.WriteLine();
+ emuMeterRegisters.Ac.Power.Reactive.WriteLine();
+ }
}
// ReSharper disable once FunctionNeverReturns
}
-
-
-
- // to delete not used anymore
- [Conditional("RELEASE")]
- private static void ReleaseWriteLog(JsonObject jsonLog, UnixTime timestamp)
+ private static void DistributePower(StatusRecord record, EssControl essControl)
{
- // WriteToFile(jsonLog, "/home/debian/DataSaliMax/" + timestamp); // this is was for beaglebone TODO
+ var nInverters = record.AcDc.Devices.Count;
+
+ var powerPerInverterPhase = nInverters > 0
+ ? AcPower.FromActiveReactive(essControl.PowerSetpoint / nInverters / 3, 0)
+ : AcPower.Null;
+
+ //var powerPerInverterPhase = AcPower.Null;
+
+ powerPerInverterPhase.WriteLine("powerPerInverterPhase");
+
+ record.AcDc.Devices.ForEach(d =>
+ {
+ d.Control.Ac.PhaseControl = PhaseControl.Asymmetric;
+ d.Control.Ac.Power.L1 = powerPerInverterPhase;
+ d.Control.Ac.Power.L2 = powerPerInverterPhase;
+ d.Control.Ac.Power.L3 = powerPerInverterPhase;
+ });
}
-
- [Conditional("DEBUG")]
- private static void DebugWriteLog(JsonObject jsonLog, UnixTime timestamp)
+ private static void ApplyDefaultSettings(this SystemControlRegisters? sc)
{
- WriteToFile(jsonLog, "/home/atef/JsonData/" + timestamp);
+ if (sc is null)
+ return;
+
+ sc.ReferenceFrame = ReferenceFrame.Consumer;
+ sc.SystemConfig = AcDcAndDcDc;
+
+ #if DEBUG
+ sc.CommunicationTimeout = TimeSpan.FromMinutes(10);
+ #else
+ sc.CommunicationTimeout = TimeSpan.FromSeconds(10);
+ #endif
+
+ sc.PowerSetPointActivation = PowerSetPointActivation.Immediate;
+ sc.UseSlaveIdForAddressing = true;
+ sc.SlaveErrorHandling = SlaveErrorHandling.Relaxed;
+ sc.SubSlaveErrorHandling = SubSlaveErrorHandling.Off;
+ sc.ResetAlarmsAndWarnings = true;
}
- private static void WriteToFile(Object obj, String fileName)
+ private static DcDcDevicesRecord ResetAlarms(this DcDcDevicesRecord dcDcStatus)
{
- var jsonString = JsonSerializer.Serialize(obj, JsonOptions);
- File.WriteAllText(fileName, jsonString);
+ var sc = dcDcStatus.SystemControl;
+
+ if (sc is not null)
+ sc.ResetAlarmsAndWarnings = sc.Alarms.Any();
+
+ foreach (var d in dcDcStatus.Devices)
+ d.Control.ResetAlarmsAndWarnings = d.Status.Alarms.Any() || d.Status.Warnings.Any();
+
+ return dcDcStatus;
}
- private static readonly JsonSerializerOptions JsonOptions = new()
+ private static AcDcDevicesRecord ResetAlarms(this AcDcDevicesRecord acDcRecord)
{
- WriteIndented = true,
- IgnoreReadOnlyProperties = false,
- Converters = { new JsonStringEnumConverter() },
- NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals,
- //TODO
- };
+ var sc = acDcRecord.SystemControl;
+ if (sc is not null)
+ sc.ResetAlarmsAndWarnings = sc.Alarms.Any() || sc.Warnings.Any();
- private static async Task UploadTimeSeries(S3Config config, JsonObject json, UnixTime unixTime)
+ foreach (var d in acDcRecord.Devices)
+ d.Control.ResetAlarmsAndWarnings = d.Status.Alarms.Any() || d.Status.Warnings.Any();
+
+ return acDcRecord;
+ }
+
+ private static async Task UploadCsv(StatusRecord status, UnixTime timeStamp)
{
- var payload = JsonSerializer.Serialize(json, JsonOptions);
- var s3Path = unixTime.Ticks + ".json";
- var request = config.CreatePutRequest(s3Path);
- var response = await request.PutAsync(new StringContent(payload));
+ var csv = status.ToCsv();
+ var s3Path = timeStamp + ".csv";
+ var request = S3Config.CreatePutRequest(s3Path);
+ var response = await request.PutAsync(new StringContent(csv));
+ csv.WriteLine();
+ timeStamp.Ticks.WriteLine();
+
if (response.StatusCode != 200)
{
Console.WriteLine("ERROR: PUT");
@@ -199,18 +250,5 @@ internal static class Program
}
}
- private static async Task UploadTopology(S3Config config, JsonObject json, UnixTime unixTime)
- {
- var payload = JsonSerializer.Serialize(json, JsonOptions);
- var s3Path = "topology" + unixTime.Ticks + ".json";
- var request = config.CreatePutRequest(s3Path);
- var response = await request.PutAsync(new StringContent(payload));
+}
- if (response.StatusCode != 200)
- {
- Console.WriteLine("ERROR: PUT");
- var error = response.GetStringAsync();
- Console.WriteLine(error);
- }
- }
-}
\ No newline at end of file
diff --git a/csharp/App/SaliMax/src/SaliMaxRelays/RelayMapBoolean.cs b/csharp/App/SaliMax/src/SaliMaxRelays/RelayMapBoolean.cs
deleted file mode 100644
index bafd76793..000000000
--- a/csharp/App/SaliMax/src/SaliMaxRelays/RelayMapBoolean.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace InnovEnergy.App.SaliMax.SaliMaxRelays;
-
-public enum RelayState
-{
- Open = 0,
- Closed = 1
-}
\ No newline at end of file
diff --git a/csharp/App/SaliMax/src/SaliMaxRelays/RelaysDevice.cs b/csharp/App/SaliMax/src/SaliMaxRelays/RelaysDevice.cs
new file mode 100644
index 000000000..e58e5ac26
--- /dev/null
+++ b/csharp/App/SaliMax/src/SaliMaxRelays/RelaysDevice.cs
@@ -0,0 +1,41 @@
+using InnovEnergy.Lib.Devices.Adam6360D;
+using InnovEnergy.Lib.Protocols.Modbus.Channels;
+
+namespace InnovEnergy.App.SaliMax.SaliMaxRelays;
+
+public class RelaysDevice
+{
+ private Adam6360DDevice AdamDevice { get; }
+
+ public RelaysDevice(String hostname) => AdamDevice = new Adam6360DDevice(hostname, 2);
+ public RelaysDevice(Channel channel) => AdamDevice = new Adam6360DDevice(channel, 2);
+
+ public RelaysRecord? Read()
+ {
+ try
+ {
+ return AdamDevice.Read();
+ }
+ catch (Exception e)
+ {
+ $"Failed to read from {nameof(RelaysDevice)}\n{e}".Log();
+
+ // TODO: log
+ return null;
+ }
+ }
+
+ public void Write(RelaysRecord r)
+ {
+ try
+ {
+ AdamDevice.Write(r);
+ }
+ catch (Exception e)
+ {
+ $"Failed to write to {nameof(RelaysDevice)}\n{e}".Log();
+ }
+ }
+}
+
+
diff --git a/csharp/App/SaliMax/src/SaliMaxRelays/RelaysRecord.cs b/csharp/App/SaliMax/src/SaliMaxRelays/RelaysRecord.cs
new file mode 100644
index 000000000..092c7cb8f
--- /dev/null
+++ b/csharp/App/SaliMax/src/SaliMaxRelays/RelaysRecord.cs
@@ -0,0 +1,54 @@
+using InnovEnergy.Lib.Devices.Adam6360D;
+
+namespace InnovEnergy.App.SaliMax.SaliMaxRelays;
+
+public enum InvertersAreConnectedToAc
+{
+ None,
+ Some,
+ All
+}
+
+public class RelaysRecord
+{
+ private readonly Adam6360DRegisters _Regs;
+
+ public RelaysRecord(Adam6360DRegisters regs) => _Regs = regs;
+
+
+ public Boolean K1GridBusIsConnectedToGrid => _Regs.DigitalInput6;
+ public Boolean K2IslandBusIsConnectedToGridBus => !_Regs.DigitalInput4;
+
+
+ public IEnumerable K3InverterIsConnectedToIslandBus
+ {
+ get
+ {
+ yield return K3Inverter1IsConnectedToIslandBus;
+ yield return K3Inverter2IsConnectedToIslandBus;
+ yield return K3Inverter3IsConnectedToIslandBus;
+ yield return K3Inverter4IsConnectedToIslandBus;
+ }
+ }
+
+ public Boolean K3Inverter1IsConnectedToIslandBus => !_Regs.DigitalInput0;
+ public Boolean K3Inverter2IsConnectedToIslandBus => !_Regs.DigitalInput1;
+ public Boolean K3Inverter3IsConnectedToIslandBus => !_Regs.DigitalInput2;
+ public Boolean K3Inverter4IsConnectedToIslandBus => !_Regs.DigitalInput3;
+
+ public Boolean FiWarning => !_Regs.DigitalInput5;
+ public Boolean FiError => !_Regs.DigitalInput7;
+
+ public Boolean K2ConnectIslandBusToGridBus { get => _Regs.Relay0; set => _Regs.Relay0 = value;}
+
+ public static implicit operator Adam6360DRegisters(RelaysRecord d) => d._Regs;
+ public static implicit operator RelaysRecord(Adam6360DRegisters d) => new RelaysRecord(d);
+
+ //
+ // public HighActivePinState F1Inverter1 => _Regs.DigitalInput8.ConvertTo(); // 1 = Closed , 0 = open
+ // public HighActivePinState F2Inverter2 => _Regs.DigitalInput9.ConvertTo(); // 1 = Closed , 0 = open
+ // public HighActivePinState F3Inverter3 => _Regs.DigitalInput10.ConvertTo(); // 1 = Closed , 0 = open
+ // public HighActivePinState F4Inverter4 => _Regs.DigitalInput11.ConvertTo(); // 1 = Closed , 0 = open
+ //
+ // public HighActivePinState Di12 => _Regs.DigitalInput12.ConvertTo();
+}
diff --git a/csharp/App/SaliMax/src/SaliMaxRelays/SaliMaxRelaysDevice.cs b/csharp/App/SaliMax/src/SaliMaxRelays/SaliMaxRelaysDevice.cs
deleted file mode 100644
index 4b5db1e06..000000000
--- a/csharp/App/SaliMax/src/SaliMaxRelays/SaliMaxRelaysDevice.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-using InnovEnergy.Lib.Devices.Adam6060;
-using InnovEnergy.Lib.Utils;
-
-namespace InnovEnergy.App.SaliMax.SaliMaxRelays;
-
-public class SaliMaxRelaysDevice
-{
- private Adam6060Device AdamDevice { get; }
-
- public SaliMaxRelaysDevice (String hostname, UInt16 port = 502)//TODO
- {
- AdamDevice = new Adam6060Device(hostname, port);
- }
-
-
- public SaliMaxRelayStatus? ReadStatus()
- {
- // Console.WriteLine("Reading Relay Status");
-
- var adamStatus = AdamDevice.ReadStatus();
-
- if (adamStatus is null)
- return null;
-
- return new SaliMaxRelayStatus
- {
- K1 = adamStatus.DigitalInput0.ConvertTo(),
- K2 = adamStatus.DigitalInput1.ConvertTo(),
- K3 = adamStatus.DigitalInput2.ConvertTo()
- };
- }
-
- public void WriteControl(Boolean k2State) //this to improve
- {
- Console.WriteLine("Writing Relay Status");
-
- var relayControlStatus = new Adam6060Control
- {
- Relay2 = k2State
- };
-
- AdamDevice.WriteControl(relayControlStatus);
- }
-}
-
-
diff --git a/csharp/App/SaliMax/src/SaliMaxRelays/SaliMaxRelaysStatus.cs b/csharp/App/SaliMax/src/SaliMaxRelays/SaliMaxRelaysStatus.cs
deleted file mode 100644
index 915896d54..000000000
--- a/csharp/App/SaliMax/src/SaliMaxRelays/SaliMaxRelaysStatus.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace InnovEnergy.App.SaliMax.SaliMaxRelays;
-
-public record SaliMaxRelayStatus
-{
- public RelayState K1 { get; init; } = RelayState.Closed; // Address on Adam(0X) 00002
- public RelayState K2 { get; init; } = RelayState.Closed; // Address on Adam(0X) 00003
- public RelayState K3 { get; init; } = RelayState.Closed; // Address on Adam(0X) 00004
-}
\ No newline at end of file
diff --git a/csharp/App/SaliMax/src/System/Controller.cs b/csharp/App/SaliMax/src/System/Controller.cs
new file mode 100644
index 000000000..8e1dc2a34
--- /dev/null
+++ b/csharp/App/SaliMax/src/System/Controller.cs
@@ -0,0 +1,470 @@
+using InnovEnergy.App.SaliMax.Ess;
+using InnovEnergy.App.SaliMax.SaliMaxRelays;
+using InnovEnergy.Lib.Devices.Battery48TL;
+using InnovEnergy.Lib.Devices.Trumpf.TruConvertAc;
+using InnovEnergy.Lib.Devices.Trumpf.TruConvertDc;
+using static InnovEnergy.Lib.Devices.Trumpf.SystemControl.DataTypes.GridType;
+
+namespace InnovEnergy.App.SaliMax.System;
+
+public static class Controller
+{
+ private static Int32 GetSystemState(this StatusRecord r)
+ {
+ var relays = r.Relays;
+
+ if (relays is null)
+ return 101; // Message = "Panic: relay device is not available!",
+
+ var acDcs = r.AcDc;
+
+ if (acDcs.NotAvailable())
+ return 102;
+
+ var k4 = acDcs.AllDisabled() ? 0
+ : acDcs.AllGridTied() ? 1
+ : acDcs.AllIsland() ? 2
+ : 4;
+
+ if (k4 == 4)
+ return 103; //Message = "Panic: ACDCs have unequal grid types",
+
+ var nInverters = r.AcDc.Devices.Count;
+
+ var k1 = relays.K1GridBusIsConnectedToGrid ? 1 : 0;
+ var k2 = relays.K2IslandBusIsConnectedToGridBus ? 1 : 0;
+ var k3 = relays.K3InverterIsConnectedToIslandBus.Take(nInverters).Any(c => c) ? 1 : 0;
+
+ // states as defined in states excel sheet
+ return 1
+ + 1*k1
+ + 2*k2
+ + 4*k3
+ + 8*k4;
+ }
+
+ public static Boolean ControlSystemState(this StatusRecord s)
+ {
+ s.SystemState.Id = s.GetSystemState();
+
+ return s.SystemState.Id switch
+ {
+ 1 => State1(s),
+ 2 => State2(s),
+ 4 => State4(s),
+ 6 => State6(s),
+ 9 => State9(s),
+ //10 => State10(s),
+ 12 => State12(s),
+ 13 => State13(s),
+ 15 => State15(s),
+ 16 => State16(s),
+ 17 => State17(s),
+ 18 => State18(s),
+ 21 => State21(s),
+
+ 101 => State101(s),
+ 102 => State102(s),
+ 103 => State103(s),
+ _ => UnknownState(s)
+ };
+
+ }
+
+ private static Boolean NotAvailable(this AcDcDevicesRecord acDcs)
+ {
+ return acDcs.SystemControl == null || acDcs.Devices.Count == 0;
+ }
+
+ private static Boolean NotAvailable(this DcDcDevicesRecord dcDcs)
+ {
+ return dcDcs.SystemControl == null || dcDcs.Devices.Count == 0;
+ }
+
+
+ private static Boolean NotAvailable(this Battery48TlRecords batteries)
+ {
+ return batteries.Devices.Count <= 0;
+ }
+
+ private static Boolean State1(StatusRecord s)
+ {
+ s.SystemState.Message = "Inverters are off. Switching to Island Mode.";
+
+ s.DcDc.Enable();
+ s.AcDc.Enable();
+ s.AcDc.EnableIslandMode();
+ s.Relays.DisconnectIslandBusFromGrid();
+
+ return false;
+
+ // => 17
+ }
+
+ private static Boolean State2(StatusRecord s)
+ {
+ s.SystemState.Message = "Inverters are disconnected from Island Bus. Switching to GridTie Mode. C";
+
+ s.DcDc.Disable();
+ s.AcDc.Disable();
+ s.AcDc.EnableGridTieMode();
+ s.Relays.ConnectIslandBusToGrid();
+
+ return false;
+
+ // => 10
+ }
+
+ private static Boolean State4(StatusRecord s)
+ {
+ s.SystemState.Message = "Turning on Inverters";
+
+ s.DcDc.Enable();
+ s.AcDc.Enable();
+ s.AcDc.EnableGridTieMode();
+ s.Relays.ConnectIslandBusToGrid();
+
+ return false;
+
+ // => 12
+ }
+
+
+ private static Boolean State6(StatusRecord s)
+ {
+ s.SystemState.Message = "Inverters are off. Waiting for them to disconnect from Island Bus.";
+
+ s.DcDc.Disable();
+ s.AcDc.Disable();
+ s.AcDc.EnableIslandMode();
+ s.Relays.DisconnectIslandBusFromGrid();
+
+ return true;
+
+ // => 2
+ }
+
+
+ private static Boolean State9(StatusRecord s)
+ {
+
+ s.SystemState.Message = "Inverters have disconnected from Island Bus. Turning them off.";
+
+ s.DcDc.Disable(); // TODO: leave enabled?
+ s.AcDc.Disable();
+ s.AcDc.EnableGridTieMode();
+ s.Relays.DisconnectIslandBusFromGrid();
+
+ return true;
+
+ // => 1
+ }
+
+
+ //
+ // private static Boolean State10(StatusRecord s)
+ // {
+ //
+ // s.SystemState.Message = "Inverters have disconnected from AcOut. Turning them off.";
+ //
+ // s.DcDc.Disable(); // TODO: leave enabled?
+ // s.AcDc.Disable();
+ // s.AcDc.EnableGridTieMode();
+ // s.Relays.DisconnectIslandBusFromGrid();
+ //
+ // return true;
+ //
+ // // => 12
+ // }
+
+ private static Boolean State12(StatusRecord s)
+ {
+ s.SystemState.Message = "Waiting for Inverters to connect to Island Bus";
+
+ s.DcDc.Enable();
+ s.AcDc.Enable();
+ s.AcDc.EnableGridTieMode();
+ s.Relays.ConnectIslandBusToGrid();
+
+ return true;
+
+ // => 16
+ }
+
+
+
+ private static Boolean State13(StatusRecord s)
+ {
+ s.SystemState.Message = "Disconnected from AcIn (K2), awaiting inverters to disconnect from AcOut (K3)";
+
+ s.DcDc.Enable();
+ s.AcDc.Enable();
+ s.AcDc.EnableGridTieMode();
+ s.Relays.DisconnectIslandBusFromGrid();
+
+ return true;
+
+ // => 9
+ }
+
+ private static Boolean State15(StatusRecord s)
+ {
+ s.SystemState.Message = "Grid has been lost, disconnecting AcIn from AcOut (K2)";
+
+ s.DcDc.Enable();
+ s.AcDc.Enable();
+ s.AcDc.EnableGridTieMode();
+ s.Relays.DisconnectIslandBusFromGrid();
+
+ return true;
+
+ // => 13
+ }
+
+ private static Boolean State16(StatusRecord s)
+ {
+ // return new
+ // (
+ // " Inverter is in grid-tie\n Waiting for K1AcInIsConnectedToGrid to open to leave it",
+ // AcPowerStageEnable: true,
+ // DcPowerStageEnable: true,
+ // GridType.GridTied400V50Hz,
+ // HighActivePinState.Closed
+ // );
+
+ s.SystemState.Message = "ESS";
+
+ s.DcDc.Enable();
+ s.AcDc.Enable();
+ s.AcDc.EnableGridTieMode();
+ s.Relays.ConnectIslandBusToGrid();
+
+ return true;
+
+ // => 15
+ }
+
+
+ private static Boolean State17(StatusRecord s)
+ {
+ s.SystemState.Message = "Inverters are in Island Mode. Waiting for them to connect to AcIn.";
+
+ s.DcDc.Enable();
+ s.AcDc.Enable();
+ s.AcDc.EnableIslandMode();
+ s.Relays.DisconnectIslandBusFromGrid();
+
+ return true;
+
+ // => 21
+ }
+
+
+
+ private static Boolean State18(StatusRecord s)
+ {
+ // return new
+ // (
+ // " Didn't succeed to go to Island mode and K1AcInIsConnectedToGrid close\n Turning off power stage of inverter\n Moving to Grid Tie",
+ // AcPowerStageEnable: false,
+ // DcPowerStageEnable: false,
+ // GridType.GridTied400V50Hz,
+ // HighActivePinState.Open
+ // );
+
+ s.DcDc.Disable();
+ s.AcDc.Disable();
+ s.AcDc.EnableIslandMode();
+ s.Relays.DisconnectIslandBusFromGrid();
+
+ return true;
+ }
+
+ private static Boolean State21(StatusRecord s)
+ {
+ s.SystemState.Message = "Island Mode";
+
+ s.DcDc.Enable();
+ s.AcDc.Enable();
+ s.AcDc.EnableIslandMode();
+ s.Relays.DisconnectIslandBusFromGrid();
+
+ return false;
+
+ // => 22
+ }
+
+ private static Boolean State22(StatusRecord s)
+ {
+ s.SystemState.Message = "Grid became available (K1). Turning off inverters.";
+
+ s.DcDc.Disable();
+ s.AcDc.Disable();
+ s.AcDc.EnableIslandMode();
+ s.Relays.DisconnectIslandBusFromGrid();
+
+ return false;
+
+ // => 6
+ }
+
+
+
+
+
+ private static Boolean State101(StatusRecord s)
+ {
+ s.SystemState.Message = "Relay device is not available";
+ return s.EnableSafeDefaults();
+ }
+
+ private static Boolean State102(StatusRecord s)
+ {
+ s.SystemState.Message = "ACDCs not available";
+ return s.EnableSafeDefaults();
+ }
+
+
+ private static Boolean State103(StatusRecord s)
+ {
+ s.SystemState.Message = "Panic: ACDCs have unequal grid types";
+ return s.EnableSafeDefaults();
+ }
+
+ private static Boolean State104(StatusRecord s)
+ {
+ s.SystemState.Message = "Panic: DCDCs not available";
+ return s.EnableSafeDefaults();
+ }
+
+
+ private static Boolean UnknownState(StatusRecord s)
+ {
+ // "Unknown System State"
+
+ return s.EnableSafeDefaults();
+ }
+
+
+
+ private static Boolean AllDisabled(this AcDcDevicesRecord acDcs)
+ {
+ return acDcs.Devices.All(d => !d.Control.PowerStageEnable);
+ }
+
+ private static Boolean AllGridTied(this AcDcDevicesRecord acDcs)
+ {
+ return acDcs.Devices.All(d => d.Status.ActiveGridType is GridTied380V60Hz)
+ || acDcs.Devices.All(d => d.Status.ActiveGridType is GridTied400V50Hz)
+ || acDcs.Devices.All(d => d.Status.ActiveGridType is GridTied480V60Hz);
+ }
+
+ private static Boolean AllIsland(this AcDcDevicesRecord acDcs)
+ {
+ return acDcs.Devices.All(d => d.Status.ActiveGridType is Island400V50Hz)
+ || acDcs.Devices.All(d => d.Status.ActiveGridType is Island480V60Hz);
+ }
+
+ private static void ForAll(this IEnumerable ts, Action action)
+ {
+ foreach (var t in ts)
+ action(t);
+ }
+
+ private static void Disable(this AcDcDevicesRecord acDc)
+ {
+ acDc.Devices
+ .Select(d => d.Control)
+ .ForAll(c => c.PowerStageEnable = false);
+ }
+
+
+ private static void Disable(this DcDcDevicesRecord dcDc)
+ {
+ dcDc.Devices
+ .Select(d => d.Control)
+ .ForAll(c => c.PowerStageEnable = false);
+ }
+
+ private static void Enable(this AcDcDevicesRecord acDc)
+ {
+ acDc.Devices
+ .Select(d => d.Control)
+ .ForAll(c => c.PowerStageEnable = true);
+ }
+
+
+ private static void Enable(this DcDcDevicesRecord dcDc)
+ {
+ dcDc.Devices
+ .Select(d => d.Control)
+ .ForAll(c => c.PowerStageEnable = true);
+ }
+
+
+
+ private static void EnableGridTieMode(this AcDcDevicesRecord acDc)
+ {
+ acDc.Devices
+ .Select(d => d.Control)
+ .ForAll(c => c.Ac.GridType = GridTied400V50Hz); // TODO: config grid type
+ }
+
+
+ private static void EnableIslandMode(this AcDcDevicesRecord acDc)
+ {
+ acDc.Devices
+ .Select(d => d.Control)
+ .ForAll(c => c.Ac.GridType = Island400V50Hz); // TODO: config grid type
+ }
+
+ private static void DisconnectIslandBusFromGrid(this RelaysRecord? relays)
+ {
+ if (relays is not null)
+ relays.K2ConnectIslandBusToGridBus = false;
+ }
+
+ private static void ConnectIslandBusToGrid(this RelaysRecord? relays)
+ {
+ if (relays is not null)
+ relays.K2ConnectIslandBusToGridBus = true;
+ }
+
+
+ private static Boolean EnableSafeDefaults(this StatusRecord s)
+ {
+ s.DcDc.Disable();
+ s.AcDc.Disable();
+ s.AcDc.EnableGridTieMode();
+ s.Relays.DisconnectIslandBusFromGrid();
+
+ return false;
+ }
+
+ private static DcDcDevicesRecord ResetAlarms(this DcDcDevicesRecord dcDcStatus)
+ {
+ var sc = dcDcStatus.SystemControl;
+
+ if (sc is not null)
+ sc.ResetAlarmsAndWarnings = sc.Alarms.Any();
+
+ foreach (var d in dcDcStatus.Devices)
+ d.Control.ResetAlarmsAndWarnings = d.Status.Alarms.Any() || d.Status.Warnings.Any();
+
+ return dcDcStatus;
+ }
+
+ private static AcDcDevicesRecord ResetAlarms(this AcDcDevicesRecord acDcStatus)
+ {
+ var sc = acDcStatus.SystemControl;
+
+ if (sc is not null)
+ sc.ResetAlarmsAndWarnings = sc.Alarms.Any() || sc.Warnings.Any();
+
+ foreach (var d in acDcStatus.Devices)
+ d.Control.ResetAlarmsAndWarnings = d.Status.Alarms.Any() || d.Status.Warnings.Any();
+
+ return acDcStatus;
+ }
+
+}
\ No newline at end of file
diff --git a/csharp/App/SaliMax/src/System/SystemState.cs b/csharp/App/SaliMax/src/System/SystemState.cs
new file mode 100644
index 000000000..062a03193
--- /dev/null
+++ b/csharp/App/SaliMax/src/System/SystemState.cs
@@ -0,0 +1,7 @@
+namespace InnovEnergy.App.SaliMax.System;
+
+public class SystemState
+{
+ public String Message { get; set; } = "Panic: Unknown State!";
+ public Int32 Id { get; set; } = 100;
+}
\ No newline at end of file
diff --git a/csharp/App/SaliMax/src/SystemConfig/Config.cs b/csharp/App/SaliMax/src/SystemConfig/Config.cs
index 73e42897a..c156269ff 100644
--- a/csharp/App/SaliMax/src/SystemConfig/Config.cs
+++ b/csharp/App/SaliMax/src/SystemConfig/Config.cs
@@ -1,6 +1,91 @@
+using System.Text.Json;
+using InnovEnergy.Lib.Time.Unix;
+using InnovEnergy.Lib.Utils;
+using static System.Text.Json.JsonSerializer;
+
namespace InnovEnergy.App.SaliMax.SystemConfig;
-public static class Config
+// shut up trim warnings
+#pragma warning disable IL2026
+
+public class Config //TODO: let IE choose from config files (Json) and connect to GUI
{
- public const String LogSalimaxLog = "/home/ie-entwicklung/Salimax.log"; // todo remove ie-entwicklung
+ private static String DefaultConfigFilePath => Path.Combine(Environment.CurrentDirectory, "config.json");
+
+ private static readonly JsonSerializerOptions JsonOptions = new() { WriteIndented = true };
+
+ public Double MinSoc { get; set; }
+ public UnixTime LastEoc { get; set; }
+ public Double PConstant { get; set; }
+ public Double ForceChargePower { get; set; }
+ public Double ForceDischargePower { get; set; }
+ public Double MaxInverterPower { get; set; }
+ public Double GridSetPoint { get; set; }
+ public Double SelfDischargePower { get; set; }
+ public Double HoldSocZone { get; set; }
+ public Double ControllerPConstant { get; set; }
+
+ public static Config Default => new()
+ {
+ MinSoc = 20,
+ LastEoc = UnixTime.Epoch,
+ PConstant = .5,
+ ForceChargePower = 1_000_000,
+ ForceDischargePower = -1_000_000,
+ MaxInverterPower = 32_000,
+ GridSetPoint = 0.0,
+ SelfDischargePower = 200, // TODO: multiple batteries
+ HoldSocZone = 1, // TODO: find better name,
+ ControllerPConstant = 0.5
+ };
+
+
+ public void Save(String? path = null)
+ {
+ var configFilePath = path ?? DefaultConfigFilePath;
+
+ try
+ {
+ var jsonString = Serialize(this, JsonOptions);
+ File.WriteAllText(configFilePath, jsonString);
+ }
+ catch (Exception e)
+ {
+ $"Failed to write config file {configFilePath}\n{e}".Log();
+ throw;
+ }
+ }
+
+
+ public static Config Load(String? path = null)
+ {
+ var configFilePath = path ?? DefaultConfigFilePath;
+ try
+ {
+ var jsonString = File.ReadAllText(configFilePath);
+ return Deserialize(jsonString)!;
+ }
+ catch (Exception e)
+ {
+ $"Failed to read config file {configFilePath}, using default config\n{e}".Log();
+ return Default;
+ }
+ }
+
+
+ public static async Task LoadAsync(String? path = null)
+ {
+ var configFilePath = path ?? DefaultConfigFilePath;
+ try
+ {
+ var jsonString = await File.ReadAllTextAsync(configFilePath);
+ return Deserialize(jsonString)!;
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine($"Couldn't read config file {configFilePath}, using default config");
+ e.Message.WriteLine();
+ return Default;
+ }
+ }
}
\ No newline at end of file
diff --git a/csharp/App/SaliMax/src/SystemConfig/Defaults.cs b/csharp/App/SaliMax/src/SystemConfig/Defaults.cs
index 1fcb95d25..1d74cef46 100644
--- a/csharp/App/SaliMax/src/SystemConfig/Defaults.cs
+++ b/csharp/App/SaliMax/src/SystemConfig/Defaults.cs
@@ -1,120 +1,123 @@
-using InnovEnergy.Lib.Devices.Trumpf.TruConvertAc;
-using InnovEnergy.Lib.Devices.Trumpf.TruConvertDc;
-
-namespace InnovEnergy.App.SaliMax.SystemConfig;
-
-public static class Defaults
-{
- public static readonly TruConvertAcControl TruConvertAcControl = new()
- {
- Date = default, // TODO
- Time = default, // TODO,
- IpAddress = default, // 0x C0A80102;
- Subnet = default, //= 0x FFFFFF00;
- Gateway = default, //= 0x C0A80102;
- ResetParamToDefault = false, // Coil
- CommunicationTimeout = TimeSpan.FromSeconds(10),
- FactoryResetParameters = false,
- ConnectedSystemConfig = Lib.Devices.Trumpf.TruConvert.SystemConfig.AcDcAndDcDc,
- UpdateSwTrigger = 0,
- AutomaticSwUpdate = 0,
- CustomerValuesSaveReset = 0,
- // SerialNumberSystemControl = 0,
- // SerialNumberAcDc = 0,
- IntegrationLevel = 16,
- // IlBuildnumber = 0,
- PowerStageEnable = true,
- SetValueConfig = SymmetricAcOperationMode.Symmetric, // Asymmetric = 0, // this is can not be seen in UI
- ResetsAlarmAndWarning = true,
- PreChargeDcLinkConfig = PreChargeDcLinkConfig.Internal, // 1 = internal
- PowerFactorConvention = PowerFactorConvention.Producer, // 0 = producer
- SlaveAddress = 1,
- ErrorHandlingPolicy = AcErrorPolicy.Relaxed, // 0 = relaxed
- GridType = AcDcGridType.GridTied400V50Hz,
- // SubSlaveAddress = 0, // Broadcast
- UseModbusSlaveIdForAddressing = false,
- SubSlaveErrorPolicy = 0,
- SignedPowerNominalValue = 0, //signedPowerValue
- SignedPowerSetValueL1 = 0,
- SignedPowerSetValueL2 = 0,
- SignedPowerSetValueL3 = 0,
- // PowerSetValue = 0,
- // PowerSetValueL1 = 0,
- // PowerSetValueL2 = 0,
- // PowerSetValueL3 = 0,
- MaximumGridCurrentRmsL1 = 15,
- MaximumGridCurrentRmsL2 = 15,
- MaximumGridCurrentRmsL3 = 15,
- CosPhiSetValueL1 = 0,
- CosPhiSetValueL2 = 0,
- CosPhiSetValueL3 = 0,
- PhaseL1IsCapacitive = false,
- PhaseL2IsCapacitive = false,
- PhaseL3IsCapacitive = false,
- PhasesAreCapacitive = false,
- SetPointCosPhi = 0,
- SetPointSinPhi = 0,
- SetPointSinPhiL1 = 0,
- SetPointSinPhiL2 = 0,
- SetPointSinPhiL3 = 0,
- FrequencyOffsetIm = 0,
- VoltageAdjustmentFactorIm = 0,
- PreChargeDcLinkVoltage = 10,
- MaxPeakCurrentVoltageControlL1 = 0,
- MaxPeakCurrentVoltageControlL2 = 0,
- MaxPeakCurrentVoltageControlL3 = 0,
- GridFormingMode = 0, // 0 = not grid-forming (grid-tied) ,1 = grid-forming TODO enum
-
- //remove DC stuff from AC
- DcLinkRefVoltage = 800,
- DcLinkMinVoltage = 780,
- DcLinkMaxVoltage = 820,
- DcVoltageRefUs = 900,
- DcMinVoltageUs = 880,
- DcMaxVoltageUs = 920,
- AcDcGcBypassMode = 0,
- AcDcGcPMaxThresholdPercent = 150,
- AcDcGcStartupRampEnable = 0,
- DcConfigModule = DcStageConfiguration.Off,
- DcDcPowerDistribution = 100,
- AcDcDistributionMode = AcDcDistributionMode.Auto,
- };
-
- public static readonly TruConvertDcControl TruConvertDcControl = new()
- {
- Date = default,
- Time = default,
- IpAddress = default,
- Subnet = default,
- Gateway = default,
- ResetParamToDefault = false ,
- TimeoutForCommunication = TimeSpan.FromSeconds(10) ,
- RestartFlag = false ,
- ConnectedSystemConfig = Lib.Devices.Trumpf.TruConvert.SystemConfig.DcDcOnly,
- UpdateSwTrigger = 0,
- AutomaticSwUpdate = 0,
- CustomerValuesSaveReset = 0,
- SerialNumberSystemControl = 0,
- SerialNumberDcDc = 0,
- MaterialNumberDcDc = 0,
- PowerStageEnable = true,
- ResetsAlarmAndWarning = false,
- SlaveAddress = 1,
- SubSlaveAddress = 0,
- ModbusSlaveId = false,
- MaximumBatteryVoltage = 56,
- MinimumBatteryVoltage = 42,
- MaximumBatteryChargingCurrent = 208,
- MaximumBatteryDischargingCurrent = 208,
- MaximumVoltageAlarmThreshold = 60,
- MinimumVoltageAlarmThreshold = 0,
- MaximalPowerAtDc = 9000,
- BatteryCurrentSet = 0,
- DynamicCurrentPerMillisecond = 2,
- DcLinkControlMode = 1,
- ReferenceVoltage = 800,
- UpperVoltageWindow = 40,
- LowerVoltageWindow = 40,
- VoltageDeadBand = 0,
- };
-}
\ No newline at end of file
+// using InnovEnergy.App.SaliMax.SaliMaxRelays;
+//
+// namespace InnovEnergy.App.SaliMax.SystemConfig;
+//
+// public static class Defaults
+// {
+// public static readonly TruConvertAcControl TruConvertAcControl = new()
+// {
+// Date = default, // TODO
+// Time = default, // TODO,
+// IpAddress = default, // 0x C0A80102;
+// Subnet = default, //= 0x FFFFFF00;
+// Gateway = default, //= 0x C0A80102;
+// ResetParamToDefault = false, // Coil
+// CommunicationTimeout = TimeSpan.FromSeconds(20),
+// CpuReset = false,
+// ConnectedSystemConfig = Lib.Devices.Trumpf.TruConvert.SystemConfig.AcDcAndDcDc,
+// UpdateSwTrigger = 0,
+// AutomaticSwUpdate = 0,
+// CustomerValuesSaveReset = 0,
+// // SerialNumberSystemControl = 0,
+// // SerialNumberAcDc = 0,
+// IntegrationLevel = 16,
+// // IlBuildnumber = 0,
+// PowerStageEnable = true,
+// SetValueConfig = SymmetricAcOperationMode.Symmetric, // Asymmetric = 0, // this is can not be seen in UI
+// ResetsAlarmAndWarning = false,
+// PreChargeDcLinkConfig = PreChargeDcLinkConfig.Internal, // 1 = internal
+// PowerFactorConvention = PowerFactorConvention.Producer, // 0 = producer
+// SlaveAddress = 1,
+// ErrorHandlingPolicy = AcErrorPolicy.Relaxed, // 0 = relaxed
+// GridType = AcDcGridType.GridTied400V50Hz,
+// // SubSlaveAddress = 0, // Broadcast
+// UseModbusSlaveIdForAddressing = false,
+// SubSlaveErrorPolicy = 0,
+// SignedPowerNominalValue = 0, //signedPowerValue
+// SignedPowerSetValueL1 = 0,
+// SignedPowerSetValueL2 = 0,
+// SignedPowerSetValueL3 = 0,
+// // PowerSetValue = 0,
+// // PowerSetValueL1 = 0,
+// // PowerSetValueL2 = 0,
+// // PowerSetValueL3 = 0,
+// MaximumGridCurrentRmsL1 = 80, // update to the default one
+// MaximumGridCurrentRmsL2 = 80, // update to the default one
+// MaximumGridCurrentRmsL3 = 80, // update to the default one
+// CosPhiSetValueL1 = 0,
+// CosPhiSetValueL2 = 0,
+// CosPhiSetValueL3 = 0,
+// PhaseL1IsCapacitive = false,
+// PhaseL2IsCapacitive = false,
+// PhaseL3IsCapacitive = false,
+// PhasesAreCapacitive = false,
+// SetPointCosPhi = 1,
+// SetPointSinPhi = 0,
+// SetPointSinPhiL1 = 0,
+// SetPointSinPhiL2 = 0,
+// SetPointSinPhiL3 = 0,
+// FrequencyOffsetIm = 0,
+// VoltageAdjustmentFactorIm = 100,
+// PreChargeDcLinkVoltage = 10,
+// MaxPeakCurrentVoltageControlL1 = 0,
+// MaxPeakCurrentVoltageControlL2 = 0,
+// MaxPeakCurrentVoltageControlL3 = 0,
+// GridFormingMode = 1.ConvertTo(), // 0 = not grid-forming (grid-tied) ,1 = grid-forming TODO enum
+//
+// DcLinkRefVoltage = 720,
+// DcLinkMinVoltage = 690,
+// DcLinkMaxVoltage = 780,
+// DcVoltageRefUs = 870,
+// DcMinVoltageUs = 880,
+// DcMaxVoltageUs = 920,
+// //AcDcGcBypassMode = 0,
+// //AcDcGcPMaxThresholdPercent = 150,
+// //AcDcGcStartupRampEnable = 0,
+// DcConfigModule = DcStageConfiguration.Off,
+// DcDcPowerDistribution = 100,
+// AcDcDistributionMode = AcDcDistributionMode.Auto,
+// };
+//
+// public static readonly TruConvertDcControl TruConvertDcControl = new()
+// {
+// Date = default,
+// Time = default,
+// IpAddress = default,
+// Subnet = default,
+// Gateway = default,
+// ResetParamToDefault = false ,
+// TimeoutForCommunication = TimeSpan.FromSeconds(20) ,
+// RestartFlag = false ,
+// ConnectedSystemConfig = Lib.Devices.Trumpf.TruConvert.SystemConfig.DcDcOnly,
+// UpdateSwTrigger = 0,
+// AutomaticSwUpdate = 0,
+// CustomerValuesSaveReset = 0,
+// SerialNumberSystemControl = 0,
+// SerialNumberDcDc = 0,
+// MaterialNumberDcDc = 0,
+// PowerStageEnable = true,
+// ResetsAlarmAndWarning = false,
+// SlaveAddress = 1,
+// SubSlaveAddress = 0,
+// ModbusSlaveId = false,
+// MaximumBatteryVoltage = 57m,
+// MinimumBatteryVoltage = 42,
+// MaximumBatteryChargingCurrent = 210,
+// MaximumBatteryDischargingCurrent = 210,
+// MaximumVoltageAlarmThreshold = 60,
+// MinimumVoltageAlarmThreshold = 0,
+// MaximalPowerAtDc = 10000,
+// BatteryCurrentSet = 0,
+// DynamicCurrentPerMillisecond = 100,
+// DcLinkControlMode = 1,
+// ReferenceVoltage = 720,
+// UpperVoltageWindow = 55,
+// LowerVoltageWindow = 55,
+// VoltageDeadBand = 0,
+// };
+//
+// public static readonly SaliMaxRelayControl SaliMaxRelayControl = new()
+// {
+// K2Control = HighActivePinState.Closed
+// };
+// }
\ No newline at end of file
diff --git a/csharp/App/SaliMax/src/SystemConfig/SalimaxConfig.cs b/csharp/App/SaliMax/src/SystemConfig/SalimaxConfig.cs
deleted file mode 100644
index 2170783e3..000000000
--- a/csharp/App/SaliMax/src/SystemConfig/SalimaxConfig.cs
+++ /dev/null
@@ -1,65 +0,0 @@
-using System.Text.Json;
-using InnovEnergy.Lib.Time.Unix;
-using InnovEnergy.Lib.Utils;
-using static System.Text.Json.JsonSerializer;
-
-namespace InnovEnergy.App.SaliMax.SystemConfig;
-
-// shut up trim warnings
-#pragma warning disable IL2026
-
-public record SalimaxConfig //TODO: let IE choose from config files (Json) and connect to GUI
-{
- private static String DefaultConfigFilePath => Path.Combine(Environment.CurrentDirectory, "config.json");
-
- private static readonly JsonSerializerOptions JsonOptions = new() { WriteIndented = true };
-
- public Decimal MinSoc { get; set; }
- public UnixTime LastEoc { get; set; }
- public Decimal PConstant { get; set; }
- public Decimal ForceChargePower { get; set; }
- public Decimal ForceDischargePower { get; set; }
- public Int32 MaxInverterPower { get; set; }
- public Decimal GridSetPoint { get; set; }
- public Decimal SelfDischargePower { get; set; }
- public Decimal HoldSocZone { get; set; }
- public Decimal ControllerPConstant { get; set; }
-
- public static SalimaxConfig Default => new()
- {
- MinSoc = 20m,
- LastEoc = UnixTime.Epoch,
- PConstant = .5m,
- ForceChargePower = 1_000_000m,
- ForceDischargePower = -1_000_000m,
- MaxInverterPower = 32_000,
- GridSetPoint = 0.0m,
- SelfDischargePower = 200m, // TODO: multiple batteries
- HoldSocZone = 1m, // TODO: find better name,
- ControllerPConstant = 0.5m
- };
-
-
- public Task Save(String? path = null)
- {
- //DefaultConfigFilePath.WriteLine("Saving data");
- var jsonString = Serialize(this, JsonOptions);
- return File.WriteAllTextAsync(path ?? DefaultConfigFilePath, jsonString);
- }
-
- public async Task Load(String? path = null)
- {
- var configFilePath = path ?? DefaultConfigFilePath;
- try
- {
- var jsonString = await File.ReadAllTextAsync(configFilePath);
- return Deserialize(jsonString)!;
- }
- catch (Exception e)
- {
- Console.WriteLine($"Couldn't read config file {configFilePath}, using default config");
- e.Message.WriteLine();
- return Default;
- }
- }
-}
\ No newline at end of file
diff --git a/csharp/App/SaliMax/src/Topology.cs b/csharp/App/SaliMax/src/Topology.cs
deleted file mode 100644
index 9966be228..000000000
--- a/csharp/App/SaliMax/src/Topology.cs
+++ /dev/null
@@ -1,241 +0,0 @@
-#define BatteriesAllowed
-
-using InnovEnergy.App.SaliMax.Controller;
-using InnovEnergy.Lib.Utils;
-using InnovEnergy.Lib.Units;
-
-
-namespace InnovEnergy.App.SaliMax;
-
-public static class Topology
-{
- private static String Separator(Decimal power)
- {
- const String chargingSeparator = ">>>>>>>>>>";
- const String dischargingSeparator = "<<<<<<<<<";
-
- return power > 0 ? chargingSeparator : dischargingSeparator;
- }
-
- public static Decimal Round3(this Decimal d)
- {
- return d.RoundToSignificantDigits(3);
- }
-
- public static void Print(StatusRecord s)
- {
- const Int32 height = 25;
-
- var calculatedActivePwr = - s.InverterStatus!.Ac.ActivePower;
- var measuredActivePwr = (s.InverterStatus.SumActivePowerL1 + s.InverterStatus.SumActivePowerL2 +
- s.InverterStatus.SumActivePowerL3) * -1;
-
- measuredActivePwr.WriteLine(" : measured Sum of Active Pwr ");
-
- var setValueCosPhi = s.InverterStatus.CosPhiSetValue;
- var setValueApparentPower = s.InverterStatus.ApparentPowerSetValue;
-
-
-#if AmptAvailable
- var pvPower = (s.AmptStatus!.Devices[0].Dc.Voltage * s.AmptStatus.Devices[0].Dc.Current + s.AmptStatus!.Devices[1].Dc.Voltage * s.AmptStatus.Devices[1].Dc.Current).Round0(); // TODO using one Ampt
-#else
- var pvPower = 0;
-#endif
- var criticalLoadPower = (s.AcInToAcOutMeterStatus!.Ac.ActivePower.Value).Round3();
-
- var dcTotalPower = -s.DcDcStatus!.TotalDcPower;
- var gridSeparator = Separator(s.GridMeterStatus!.Ac.ActivePower);
- var inverterSeparator = Separator(measuredActivePwr);
- var dcSeparator = Separator(dcTotalPower);
- var something = measuredActivePwr + criticalLoadPower;
- var gridLoadPower = (s.GridMeterStatus!.Ac.ActivePower - something).Value.Round3();
-
-
-
- ////////////////// Grid //////////////////////
- var boxGrid = AsciiArt.CreateBox
- (
- "Grid",
- s.GridMeterStatus.Ac.L1.Voltage.Value.V(),
- s.GridMeterStatus.Ac.L2.Voltage.Value.V(),
- s.GridMeterStatus.Ac.L3.Voltage.Value.V()
- ).AlignCenterVertical(height);
-
- var gridAcBusArrow = AsciiArt.CreateHorizontalArrow(s.GridMeterStatus!.Ac.ActivePower, gridSeparator)
- .AlignCenterVertical(height);
-
-
- ////////////////// Ac Bus //////////////////////
- var boxAcBus = AsciiArt.CreateBox
- (
- "AC Bus",
- s.InverterStatus.Ac.L1.Voltage.Value.V(),
- s.InverterStatus.Ac.L2.Voltage.Value.V(),
- s.InverterStatus.Ac.L3.Voltage.Value.V()
- );
-
- var boxLoad = AsciiArt.CreateBox
- (
- "",
- "LOAD",
- ""
- );
-
- var loadRect = StringUtils.AlignBottom(CreateRect(boxAcBus, boxLoad, gridLoadPower), height);
-
- var acBusInvertArrow = AsciiArt.CreateHorizontalArrow(measuredActivePwr, inverterSeparator)
- .AlignCenterVertical(height);
-
- //////////////////// Inverter /////////////////////////
- var inverterBox = AsciiArt.CreateBox
- (
- "",
- "Inverter",
- ""
- ).AlignCenterVertical(height);
-
- var inverterArrow = AsciiArt.CreateHorizontalArrow(measuredActivePwr, inverterSeparator)
- .AlignCenterVertical(height);
-
-
- //////////////////// DC Bus /////////////////////////
- var dcBusBox = AsciiArt.CreateBox
- (
- "DC Bus",
- (s.InverterStatus.ActualDcLinkVoltageLowerHalfExt.Value + s.InverterStatus.ActualDcLinkVoltageUpperHalfExt.Value).V(),
- ""
- );
-
- var pvBox = AsciiArt.CreateBox
- (
- "MPPT",
- ((s.AmptStatus!.Devices[0].Strings[0].Voltage.Value + s.AmptStatus!.Devices[0].Strings[1].Voltage.Value) / 2).V(),
- ""
- );
-
- var pvRect = StringUtils.AlignTop(CreateRect(pvBox, dcBusBox, pvPower), height);
-
- var dcBusArrow = AsciiArt.CreateHorizontalArrow(-s.DcDcStatus!.Left.Power, dcSeparator)
- .AlignCenterVertical(height);
-
- //////////////////// Dc/Dc /////////////////////////
-
- var dcBox = AsciiArt.CreateBox( "Dc/Dc", s.DcDcStatus.Right.Voltage.Value.V(), "").AlignCenterVertical(height);
-
- var topology = "";
-
- if (s.BatteriesStatus != null)
- {
- var numBatteries = s.BatteriesStatus.Children.Count;
-
- // Create an array of battery arrows using LINQ
- var dcArrows = s
- .BatteriesStatus.Children
- .Select(b => AsciiArt.CreateHorizontalArrow(b.Dc.Power, Separator(b.Dc.Power)))
- .ToArray();
-
- // Create a rectangle from the array of arrows and align it vertically
- var dcArrowRect = CreateRect(dcArrows).AlignCenterVertical(height);
-
- //////////////////// Batteries /////////////////////////
-
- var batteryBox = new String[numBatteries];
-
- for (var i = 0; i < numBatteries; i++)
- {
- if (s.BatteriesStatus.Children[i] != null)
- {
- batteryBox[i] = AsciiArt.CreateBox
- (
- "Battery " + (i+1),
- s.BatteriesStatus.Children[i].Dc.Voltage .Value.V(),
- s.BatteriesStatus.Children[i].Soc .Value.Percent(),
- s.BatteriesStatus.Children[i].Temperature .Value.Celsius(),
- s.BatteriesStatus.Children[i].Dc.Current .Value.A(),
- s.BatteriesStatus.Children[i].TotalCurrent.Value.A()
- );
- }
- else
- {
- batteryBox[i] = AsciiArt.CreateBox
- (
- "Battery " + (i+1),
- "not detected"
- );
- }
- }
-
- var batteryRect = CreateRect(batteryBox).AlignCenterVertical(height);
-
-
- var avgBatteryBox = "";
-
- if (s.BatteriesStatus.Combined != null)
- {
- avgBatteryBox = AsciiArt.CreateBox
- (
- "Batteries",
- s.BatteriesStatus.Combined.CellsVoltage,
- s.BatteriesStatus.Combined.Soc,
- s.BatteriesStatus.Combined.Temperature,
- s.BatteriesStatus.Combined.Dc.Current,
- s.BatteriesStatus.Combined.Alarms.Count > 0 ? String.Join(Environment.NewLine, s.BatteriesStatus.Combined.Alarms) : "No Alarm"
- ).AlignCenterVertical(height);
- }
-
-
- topology = boxGrid.SideBySideWith(gridAcBusArrow, "")
- .SideBySideWith(loadRect, "")
- .SideBySideWith(acBusInvertArrow, "")
- .SideBySideWith(inverterBox, "")
- .SideBySideWith(inverterArrow, "")
- .SideBySideWith(pvRect, "")
- .SideBySideWith(dcBusArrow, "")
- .SideBySideWith(dcBox, "")
- .SideBySideWith(dcArrowRect, "")
- .SideBySideWith(batteryRect, "")
- .SideBySideWith(avgBatteryBox, "")+ "\n";
-
- }
- else
- {
- topology = boxGrid.SideBySideWith(gridAcBusArrow, "")
- .SideBySideWith(loadRect, "")
- .SideBySideWith(acBusInvertArrow, "")
- .SideBySideWith(inverterBox, "")
- .SideBySideWith(inverterArrow, "")
- .SideBySideWith(pvRect, "")
- .SideBySideWith(dcBusArrow, "")
- .SideBySideWith(dcBox, "") + "\n";
- }
-
- Console.WriteLine(topology);
- }
-
- private static String CreateRect(String boxTop, String boxBottom, Decimal power)
- {
- var powerArrow = AsciiArt.CreateVerticalArrow(power);
- var boxes = new[] { boxTop, powerArrow, boxBottom };
- var maxWidth = boxes.Max(l => l.Width());
-
- var rect = boxes.Select(l => l.AlignCenterHorizontal(maxWidth)).JoinLines();
- return rect;
- }
-
- private static String CreateRect(String boxTop, String boxBottom)
- {
- var boxes = new[] { boxTop, boxBottom };
- var maxWidth = boxes.Max(l => l.Width());
-
- var rect = boxes.Select(l => l.AlignCenterHorizontal(maxWidth)).JoinLines();
- return rect;
- }
-
- private static String CreateRect(String[] boxes)
- {
- var maxWidth = boxes.Max(l => l.Width());
- var rect = boxes.Select(l => l.AlignCenterHorizontal(maxWidth)).JoinLines();
- return rect;
- }
-
-}
\ No newline at end of file
diff --git a/csharp/App/SaliMax/tunnels.sh b/csharp/App/SaliMax/tunnels.sh
deleted file mode 100755
index a6c06948b..000000000
--- a/csharp/App/SaliMax/tunnels.sh
+++ /dev/null
@@ -1,41 +0,0 @@
-#!/bin/bash
-
-host=ie-entwicklung@10.2.3.104
-
-tunnel() {
- name=$1
- ip=$2
- rPort=$3
- lPort=$4
-
- echo -n "localhost:$lPort $name "
- ssh -nNTL "$lPort:$ip:$rPort" "$host" 2> /dev/null &
-
- until nc -vz 127.0.0.1 $lPort 2> /dev/null
- do
- echo -n .
- sleep 0.3
- done
-
- echo "ok"
-}
-
-echo ""
-
-tunnel "Trumpf Inverter (http) " 192.168.1.2 80 7001
-tunnel "Trumpf DCDC (http) " 192.168.1.3 80 7002
-tunnel "Emu Meter (http) " 192.168.1.241 80 7003
-tunnel "ADAM (http) " 192.168.1.242 80 7004
-tunnel "AMPT (http) " 192.168.1.249 8080 7005
-
-tunnel "Trumpf Inverter (modbus)" 192.168.1.2 502 5001
-tunnel "Trumpf DCDC (modbus) " 192.168.1.3 502 5002
-tunnel "Emu Meter (modbus) " 192.168.1.241 502 5003
-tunnel "ADAM (modbus) " 192.168.1.242 502 5004
-tunnel "AMPT (modbus) " 192.168.1.249 502 5005
-
-echo
-echo "press any key to close the tunnels ..."
-read -r -n 1 -s
-kill $(jobs -p)
-echo "done"
diff --git a/csharp/App/SaliMax/tunneltoProto.sh b/csharp/App/SaliMax/tunneltoProto.sh
deleted file mode 100644
index 215e43c1d..000000000
--- a/csharp/App/SaliMax/tunneltoProto.sh
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/bin/bash
-
-host=ie-entwicklung@10.2.3.115
-
-tunnel() {
- name=$1
- ip=$2
- rPort=$3
- lPort=$4
-
- echo -n "localhost:$lPort $name "
- ssh -nNTL "$lPort:$ip:$rPort" "$host" 2> /dev/null &
-
- until nc -vz 127.0.0.1 $lPort 2> /dev/null
- do
- echo -n .
- sleep 0.3
- done
-
- echo "ok"
-}
-
-echo ""
-
-tunnel "Trumpf Inverter (http) " 10.0.2.1 80 8001
-tunnel "Trumpf DCDC (http) " 10.0.3.1 80 8002
-tunnel "Ext Emu Meter (http) " 10.0.4.1 80 8003
-tunnel "Int Emu Meter (http) " 10.0.4.2 80 8004
-tunnel "AMPT (http) " 10.0.5.1 8080 8005
-
-tunnel "Trumpf Inverter (modbus)" 10.0.2.1 502 5001
-tunnel "Trumpf DCDC (modbus) " 10.0.3.1 502 5002
-tunnel "Ext Emu Meter (modbus) " 10.0.4.1 502 5003
-tunnel "Int Emu Meter " 10.0.4.2 502 5004
-tunnel "AMPT (modbus) " 10.0.5.1 502 5005
-
-
-
-echo
-echo "press any key to close the tunnels ..."
-read -r -n 1 -s
-kill $(jobs -p)
-echo "done"
\ No newline at end of file