diff --git a/csharp/App/Backend/DataTypes/Installation.cs b/csharp/App/Backend/DataTypes/Installation.cs index f4620d56b..62db53970 100644 --- a/csharp/App/Backend/DataTypes/Installation.cs +++ b/csharp/App/Backend/DataTypes/Installation.cs @@ -2,7 +2,6 @@ using SQLite; namespace InnovEnergy.App.Backend.DataTypes; - public class Installation : TreeNode { public String Location { get; set; } = ""; diff --git a/csharp/App/OpenVpnCertificatesServer/Program.cs b/csharp/App/OpenVpnCertificatesServer/Program.cs index 581699d5f..6189489b3 100644 --- a/csharp/App/OpenVpnCertificatesServer/Program.cs +++ b/csharp/App/OpenVpnCertificatesServer/Program.cs @@ -153,8 +153,6 @@ public static class Program var installations = await vrm.GetInstallations(); - - var installationName = installations .Where(i => i.UniqueId == uniqueId) .Select(i => i.Name) diff --git a/csharp/App/SaliMax/Doc/AllStates.graphml b/csharp/App/SaliMax/Doc/AllStates.graphml new file mode 100644 index 000000000..23d73c65a --- /dev/null +++ b/csharp/App/SaliMax/Doc/AllStates.graphml @@ -0,0 +1,964 @@ + + + + + + + + + + + + + + + + + + + + + + + + 23 + K1 ✓ +K2 ✓ +K3 ✓ + + + + + + + + + + + + + + 19 + K1 ✓ +K2 ✓ +K3 ✘ + + + + + + + + + + + + + + 3 + K1 ✓ +K2 ✓ +K3 ✘ + + + + + + + + + + + + + + 9 + K1 ✓ +K2 ✘ +K3 ✘ + + + + + + + + + + + + + + 1 + K1 ✓ +K2 ✘ +K3 ✘ + + + + + + + + + + + + + + 13 + K1 ✓ +K2 ✘ +K3 ✓ + + + + + + + + + + + + + + 29 + K1 ✓ +K2 ✘ +K3 ✓ + + + + + + + + + + + + + + 5 + K1 ✓ +K2 ✘ +K3 ✓ + + + + + + + + + + + + + + 7 + K1 ✓ +K2 ✓ +K3 ✓ + + + + + + + + + + + + + + + 11 + K1 ✓ +K2 ✓ +K3 ✘ + + + + + + + + + + + + + + + 15 + K1 ✓ +K2 ✓ +K3 ✓ + + + + + + + + + + + + + + 21 + K1 ✓ +K2 ✘ +K3 ✓ + + + + + + + + + + + + + + 17 + K1 ✓ +K2 ✘ +K3 ✘ + + + + + + + + + + + + + + + 25 + K1 ✓ +K2 ✘ +K3 ✘ + + + + + + + + + + + + + + + 27 + K1 ✓ +K2 ✓ +K3 ✘ + + + + + + + + + + + + + + + 31 + K1 ✓ +K2 ✓ +K3 ✓ + + + + + + + + + + + + + + 28 + K1 ✘ +K2 ✘ +K3 ✓ + + + + + + + + + + + + + + 24 + K1 ✘ +K2 ✘ +K3 ✘ + + + + + + + + + + + + + + 8 + K1 ✘ +K2 ✘ +K3 ✘ + + + + + + + + + + + + + + 6 + K1 ✘ +K2 ✓ +K3 ✓ + + + + + + + + + + + + + + 0 + K1 ✘ +K2 ✘ +K3 ✘ + + + + + + + + + + + + + + 4 + K1 ✘ +K2 ✘ +K3 ✓ + + + + + + + + + + + + + + 22 + K1 ✘ +K2 ✓ +K3 ✓ + + + + + + + + + + + + + + 16 + K1 ✘ +K2 ✘ +K3 ✘ + + + + + + + + + + + + + + 20 + K1 ✘ +K2 ✘ +K3 ✓ + + + + + + + + + + + + + + 18 + K1 ✘ +K2 ✓ +K3 ✘ + + + + + + + + + + + + + + 2 + K1 ✘ +K2 ✓ +K3 ✘ + + + + + + + + + + + + + + 10 + K1 ✘ +K2 ✓ +K3 ✘ + + + + + + + + + + + + + + 12 + K1 ✘ +K2 ✘ +K3 ✓ + + + + + + + + + + + + + + 14 + K1 ✘ +K2 ✓ +K3 ✓ + + + + + + + + + + + + + + 26 + K1 ✘ +K2 ✓ +K3 ✘ + + + + + + + + + + + + + + 30 + K1 ✘ +K2 ✓ +K3 ✓ + + + + + + + + + + + + + turn off +Inverters + + + + + + + + + + + + switch to +grid tie + + + + + + + + + + + + K3's open + + + + + + + + + + + + close K2 + + + + + + + + + + + + turn on +Inverters + + + + + + + + + + + + K3's close + + + + + + + + + + + + open K2 + + + + + + + + + + + + open K2 + + + + + + + + + + + + turn off +inverter + + + + + + + + + + + + turn off +inverters + + + + + + + + + + + + turn off +inverters + + + + + + + + + + + + turn off +inverters + + + + + + + + + + + + turn off +inverters + + + + + + + + + + + K3's close + + + + + + + + + + + turn off +Inverters + + + + + + + + + + + + switch to +island mode + + + + + + + + + + + + turn on +inverters + + + + + + + + + + + turn off +Inverters + + + + + + + + + + + K3 opens + + + + + + + + + + + + open K2 + + + + + + + + + + + + turn off +inverters + + + + + + + + + + + + open K2 + + + + + + + + + + + + open K2 + + + + + + + + + + + + K3 opens + + + + + + + + + + + + open K2 + + + + + + + + + + + + turn off +inverters + + + + + + + + + + + + turn off +inverters + + + + + + + + + + + + K1 closes + + + + + + + + + + + + K1 opens + + + + + + + + + + + + turn off +inverters + + + + + + + + + + + + K3's open + + + + + + + + + + + K3's open + + + + + + + + + diff --git a/csharp/App/SaliMax/Doc/States_Table.xlsx b/csharp/App/SaliMax/Doc/States_Table.xlsx new file mode 100644 index 000000000..776df481c Binary files /dev/null and b/csharp/App/SaliMax/Doc/States_Table.xlsx differ diff --git a/csharp/App/SaliMax/Doc/TransitionToGridTied.graphml b/csharp/App/SaliMax/Doc/TransitionToGridTied.graphml new file mode 100644 index 000000000..b73645cf6 --- /dev/null +++ b/csharp/App/SaliMax/Doc/TransitionToGridTied.graphml @@ -0,0 +1,501 @@ + + + + + + + + + + + + + + + + + + + + + + + + 19 + K1 ✓ +K2 ✓ +K3 ✘ + + + + + + + + + + + + + + 3 + K1 ✓ +K2 ✓ +K3 ✘ + + + + + + + + + + + + + + 9 + K1 ✓ +K2 ✘ +K3 ✘ + + + + + + + + + + + + + + 1 + K1 ✓ +K2 ✘ +K3 ✘ + + + + + + + + + + + + + + 13 + K1 ✓ +K2 ✘ +K3 ✓ + + + + + + + + + + + + + + 29 + K1 ✓ +K2 ✘ +K3 ✓ + + + + + + + + + + + + + + 23 + K1 ✓ +K2 ✓ +K3 ✓ + + + + + + + + + + + + + + 5 + K1 ✓ +K2 ✘ +K3 ✓ + + + + + + + + + + + + + + 7 + K1 ✓ +K2 ✓ +K3 ✓ + + + + + + + + + + + + + + + 11 + K1 ✓ +K2 ✓ +K3 ✘ + + + + + + + + + + + + + + + 15 + K1 ✓ +K2 ✓ +K3 ✓ + + + + + + + + + + + + + + 21 + K1 ✓ +K2 ✘ +K3 ✓ + + + + + + + + + + + + + + 17 + K1 ✓ +K2 ✘ +K3 ✘ + + + + + + + + + + + + + + + 25 + K1 ✓ +K2 ✘ +K3 ✘ + + + + + + + + + + + + + + + 27 + K1 ✓ +K2 ✓ +K3 ✘ + + + + + + + + + + + + + + + 31 + K1 ✓ +K2 ✓ +K3 ✓ + + + + + + + + + + + + + turn off +Inverters + + + + + + + + + + + + switch to +grid tie + + + + + + + + + + + + + + + K3's open + + + + + + + + + + + + close K2 + + + + + + + + + + + + turn on +Inverters + + + + + + + + + + + + K3's close + + + + + + + + + + + + open K2 + + + + + + + + + + + + + + + open K2 + + + + + + + + + + + + + + + turn on +Inverters + + + + + + + + + + + + close K2 + + + + + + + + + + + + + + + turn off +inverter + + + + + + + + + + + + turn off +inverters + + + + + + + + + + + + turn off +inverters + + + + + + + + + + + + turn off +inverters + + + + + + + + + + + + + + + turn off +inverters + + + + + + + + + diff --git a/csharp/App/SaliMax/Doc/TransitionToIsland.graphml b/csharp/App/SaliMax/Doc/TransitionToIsland.graphml new file mode 100644 index 000000000..800dffa96 --- /dev/null +++ b/csharp/App/SaliMax/Doc/TransitionToIsland.graphml @@ -0,0 +1,487 @@ + + + + + + + + + + + + + + + + + + + + + + + + 28 + K1 ✘ +K2 ✘ +K3 ✓ + + + + + + + + + + + + + + 24 + K1 ✘ +K2 ✘ +K3 ✘ + + + + + + + + + + + + + + 8 + K1 ✘ +K2 ✘ +K3 ✘ + + + + + + + + + + + + + + 6 + K1 ✘ +K2 ✓ +K3 ✓ + + + + + + + + + + + + + + 0 + K1 ✘ +K2 ✘ +K3 ✘ + + + + + + + + + + + + + + 4 + K1 ✘ +K2 ✘ +K3 ✓ + + + + + + + + + + + + + + 22 + K1 ✘ +K2 ✓ +K3 ✓ + + + + + + + + + + + + + + 16 + K1 ✘ +K2 ✘ +K3 ✘ + + + + + + + + + + + + + + 20 + K1 ✘ +K2 ✘ +K3 ✓ + + + + + + + + + + + + + + 18 + K1 ✘ +K2 ✓ +K3 ✘ + + + + + + + + + + + + + + 2 + K1 ✘ +K2 ✓ +K3 ✘ + + + + + + + + + + + + + + 10 + K1 ✘ +K2 ✓ +K3 ✘ + + + + + + + + + + + + + + 12 + K1 ✘ +K2 ✘ +K3 ✓ + + + + + + + + + + + + + + 14 + K1 ✘ +K2 ✓ +K3 ✓ + + + + + + + + + + + + + + 26 + K1 ✘ +K2 ✓ +K3 ✘ + + + + + + + + + + + + + + 30 + K1 ✘ +K2 ✓ +K3 ✓ + + + + + + + + + + + + + K3's open + + + + + + + + + + + K3's close + + + + + + + + + + + turn off +Inverters + + + + + + + + + + + + + + + switch to +island mode + + + + + + + + + + + + turn on +inverters + + + + + + + + + + + turn off +Inverters + + + + + + + + + + + + + + K3 opens + + + + + + + + + + + + open K2 + + + + + + + + + + + + turn off +inverters + + + + + + + + + + + + + + + open K2 + + + + + + + + + + + + open K2 + + + + + + + + + + + + + + + K3 opens + + + + + + + + + + + + open K2 + + + + + + + + + + + + turn off +inverters + + + + + + + + + + + + turn off +inverters + + + + + + + + + diff --git a/csharp/App/SaliMax/Doc/Zustandsdiagramm_IBN_Standard.graphml b/csharp/App/SaliMax/Doc/Zustandsdiagramm_IBN_Standard.graphml deleted file mode 100644 index 1968f44f8..000000000 --- a/csharp/App/SaliMax/Doc/Zustandsdiagramm_IBN_Standard.graphml +++ /dev/null @@ -1,503 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - 2 -Aus (mit Netz) - - - - - - - - - - - 3 - - - - - - - - - - - 4 - - - - - - - - - - - 5 - - - - - - - - - - - 6 - - - - - - - - - - - 7 - - - - - - - - - - - 8 - - - - - - - - - - - 9 - - - - - - - - - - - 4 - - - - - - - - - - - 11 - - - - - - - - - - - 12 - - - - - - - - - - - 13 - - - - - - - - - - - 14 - - - - - - - - - - - 15 - - - - - - - - - - - 16 -Netzparallel - - - - - - - - - - - 17 - - - - - - - - - - - 18 - - - - - - - - - - - 19 - - - - - - - - - - - 20 - - - - - - - - - - - 21 -Inselbetrieb - - - - - - - - - - - 22 - - - - - - - - - - - 23 - - - - - - - - - - - 24 - - - - - - - - - - - 1 - - - - - - - - - - - 9 - - - - - - - - - - - 13 - - - - - - - - - - - 15 - - - - - - - - - - - 17 - - - - - - - - - - - 1 -Aus (ohne Netz) - - - - - - - - - - - K1 schliesst - - - - - - - - - - - Uebergang nach Off - - - - - - - - - - - Uebergang nach Netzparallel - - - - - - - - - - - K3 öffnet - - - - - - - - - - - K2 schliesst - - - - - - - - - - - K3 schliesst - - - - - - - - - - - K1 öffnet - - - - - - - - - - - K2 öffnet - - - - - - - - - - - K3 öffnet - - - - - - - - - - - K3 schliesst - - - - - - - - - - - Uebergang nach Off - - - - - - - - - - - Uebergan nach Inselbetrieb - - - - - - - - - - - - K1 Schliesst - - - - - - - - - - - - - - - Turnoff The inverter - - - - - - - - - diff --git a/csharp/App/SaliMax/Doc/Zustandsmatrix_Salimax.xlsx b/csharp/App/SaliMax/Doc/Zustandsmatrix_Salimax.xlsx deleted file mode 100644 index 6cd1ceb13..000000000 Binary files a/csharp/App/SaliMax/Doc/Zustandsmatrix_Salimax.xlsx and /dev/null differ diff --git a/csharp/App/SaliMax/run (Salimax0003).sh b/csharp/App/SaliMax/run (Salimax0003).sh index ba6f5b68c..a47112ee4 100644 --- a/csharp/App/SaliMax/run (Salimax0003).sh +++ b/csharp/App/SaliMax/run (Salimax0003).sh @@ -18,17 +18,17 @@ echo -e "\n============================ Deploy ============================\n" rsync -v \ ./bin/Release/$dotnet_version/linux-x64/publish/* \ - ie-entwicklung@10.2.4.33:~/salimax + $username@$salimax_ip:~/salimax echo -e "\n============================ Restart Salimax sevice ============================\n" ssh -tt \ - ie-entwicklung@10.2.4.33 \ + $username@$salimax_ip \ sudo systemctl restart salimax.service echo -e "\n============================ Print service output ============================\n" ssh -tt \ - ie-entwicklung@10.2.4.33 \ + $username@$salimax_ip \ journalctl -f -u salimax.service \ No newline at end of file diff --git a/csharp/App/SaliMax/src/Logfile.cs b/csharp/App/SaliMax/src/Logfile.cs index 2d95a323d..6465109df 100644 --- a/csharp/App/SaliMax/src/Logfile.cs +++ b/csharp/App/SaliMax/src/Logfile.cs @@ -42,6 +42,8 @@ public class CustomLogger : ILogger // Write the log message to the file File.AppendAllText(_LogFilePath, logMessage + Environment.NewLine); _CurrentFileSizeBytes += logMessage.Length; + + Console.WriteLine( logMessage + Environment.NewLine); } private void RotateLogFile() diff --git a/csharp/App/SaliMax/src/Program.cs b/csharp/App/SaliMax/src/Program.cs index 665201e29..ed99a83da 100644 --- a/csharp/App/SaliMax/src/Program.cs +++ b/csharp/App/SaliMax/src/Program.cs @@ -33,7 +33,7 @@ internal static class Program private const UInt32 UpdateIntervalSeconds = 2; - private static readonly Byte[] BatteryNodes = { 2, 3, 4, 5, 6 }; + private static readonly IReadOnlyList BatteryNodes; private static readonly TcpChannel TruConvertAcChannel ; private static readonly TcpChannel TruConvertDcChannel ; @@ -55,6 +55,12 @@ internal static class Program AmptChannel = new TcpChannel(d.AmptIp); RelaysChannel = new TcpChannel(d.RelaysIp); BatteriesChannel = new TcpChannel(d.BatteryIp); + + BatteryNodes = config + .Devices + .BatteryNodes + .Select(n=>n.ConvertTo()) + .ToArray(config.Devices.BatteryNodes.Length); } public static async Task Main(String[] args) @@ -93,17 +99,30 @@ internal static class Program StatusRecord ReadStatus() { + Console.WriteLine(" Reading AcDC"); var acDc = acDcDevices.Read(); + + Console.WriteLine(" Reading dcDc"); var dcDc = dcDcDevices.Read(); + + Console.WriteLine(" Reading battery"); var battery = batteryDevices.Read(); + + Console.WriteLine(" Reading relays"); var relays = saliMaxRelaysDevice.Read(); + + Console.WriteLine(" loadOnAcIsland"); var loadOnAcIsland = acIslandLoadMeter.Read(); + + Console.WriteLine(" Reading gridMeter"); var gridMeter = gridMeterDevice.Read(); + + Console.WriteLine(" Reading pvOnDc"); var pvOnDc = amptDevice.Read(); var pvOnAcGrid = AcPowerDevice.Null; var pvOnAcIsland = AcPowerDevice.Null; - var gridPower = gridMeter is null ? AcPower.Null : gridMeter.Ac.Power; + var gridPower = gridMeter is null ? AcPower.Null : gridMeter.Ac.Power; var islandLoadPower = loadOnAcIsland is null ? AcPower.Null : loadOnAcIsland.Ac.Power; var inverterAcPower = acDc.Ac.Power; @@ -235,17 +254,15 @@ internal static class Program var dcLinkVoltage = TextBlock.AlignCenterHorizontal("", s.DcDc.Dc.Link.Voltage.ToDisplayString(), ""); - - //var inverterPowerByPhase = new ActivePower[(Int32)s.AcDc.Ac.L1.Power.Active, (Int32)s.AcDc.Ac.L2.Power.Active, (Int32)s.AcDc.Ac.L3.Power.Active]; - - // Voltage Measurement Values - //var inverterVoltage = new Voltage [(Int32)s.AcDc.Ac.L1.Voltage, (Int32)s.AcDc.Ac.L2.Voltage, (Int32)s.AcDc.Ac.L3.Voltage]; - //var dcLinkVoltage = s.DcDc.Dc.Link.Voltage; + var dc48Voltage = s.DcDc.Dc.Battery.Voltage.ToDisplayString(); - var batteryVoltage = s.Battery.Dc.Voltage.ToDisplayString(); - var batterySoc = s.Battery.Soc.ToDisplayString(); + var batteryVoltage = s.Battery.Dc.Voltage.Value.RoundToSignificantDigits(3); + var batterySoc = s.Battery.Devices.Any()? s.Battery.Devices.Average(b=>b.Soc).Percent().ToDisplayString() : "0"; var batteryCurrent = s.Battery.Dc.Current.ToDisplayString(); var batteryTemp = s.Battery.Temperature.ToDisplayString(); + var batteryHeatingCurrent = s.Battery.HeatingCurrent.ToDisplayString(); + var anyBatteryAlarm = s.Battery.Alarms.Any(); + var anyBatteryWarning = s.Battery.Warnings.Any(); var gridBusColumn = ColumnBox("Pv", "Grid Bus", "Load" , gridVoltageByPhase , gridLoadPower); var islandBusColumn = ColumnBox("Pv", "Island Bus", "Load" , inverterPowerByPhase, islandLoadPower); @@ -263,12 +280,15 @@ internal static class Program var batteryAvgBox = TextBlock.AlignLeft(batteryVoltage, batterySoc, batteryCurrent, - batteryTemp) + batteryTemp, + batteryHeatingCurrent, + anyBatteryWarning, + anyBatteryAlarm) .TitleBox("Battery"); //////////////////// Batteries ///////////////////////// - + IReadOnlyList batteryBoxes = s.Battery .Devices .Select(CreateIndividualBattery) @@ -297,13 +317,17 @@ internal static class Program private static TextBlock CreateIndividualBattery(Battery48TlRecord battery, Int32 i) { + var batteryWarnings = battery.Warnings.Any(); + var batteryAlarms = battery.Alarms.Any(); var content = TextBlock.AlignLeft(battery.Dc.Voltage.ToDisplayString(), battery.Soc.ToDisplayString(), battery.Dc.Current.ToDisplayString(), battery.Temperatures.Cells.Average.ToDisplayString(), - battery.BusCurrent.ToDisplayString(), - battery.HeatingCurrent.ToDisplayString()); + // battery.BusCurrent.ToDisplayString(), + battery.HeatingCurrent.ToDisplayString(), + batteryWarnings, + batteryAlarms); var box = content.TitleBox($"Battery {i + 1}"); diff --git a/csharp/App/SaliMax/src/SaliMaxRelays/RelaysDevice.cs b/csharp/App/SaliMax/src/SaliMaxRelays/RelaysDevice.cs index 31afb815c..118d1ed54 100644 --- a/csharp/App/SaliMax/src/SaliMaxRelays/RelaysDevice.cs +++ b/csharp/App/SaliMax/src/SaliMaxRelays/RelaysDevice.cs @@ -18,7 +18,7 @@ public class RelaysDevice } catch (Exception e) { - $"Failed to read from {nameof(RelaysDevice)}\n{e}".LogInfo(); + $"Failed to read from {nameof(RelaysDevice)}\n{e}".LogError(); // TODO: log return null; @@ -33,7 +33,7 @@ public class RelaysDevice } catch (Exception e) { - $"Failed to write to {nameof(RelaysDevice)}\n{e}".LogInfo(); + $"Failed to write to {nameof(RelaysDevice)}\n{e}".LogError(); } } } diff --git a/csharp/App/SaliMax/src/SaliMaxRelays/RelaysRecord.cs b/csharp/App/SaliMax/src/SaliMaxRelays/RelaysRecord.cs index 092c7cb8f..2f0f98aee 100644 --- a/csharp/App/SaliMax/src/SaliMaxRelays/RelaysRecord.cs +++ b/csharp/App/SaliMax/src/SaliMaxRelays/RelaysRecord.cs @@ -2,19 +2,11 @@ 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; @@ -39,16 +31,9 @@ public class RelaysRecord public Boolean FiWarning => !_Regs.DigitalInput5; public Boolean FiError => !_Regs.DigitalInput7; - public Boolean K2ConnectIslandBusToGridBus { get => _Regs.Relay0; set => _Regs.Relay0 = value;} + 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/SystemConfig/Config.cs b/csharp/App/SaliMax/src/SystemConfig/Config.cs index c75a188d9..294cf1f5d 100644 --- a/csharp/App/SaliMax/src/SystemConfig/Config.cs +++ b/csharp/App/SaliMax/src/SystemConfig/Config.cs @@ -75,7 +75,15 @@ public class Config //TODO: let IE choose from config files (Json) and connect t MinDcBusVoltage = 690, ReferenceDcBusVoltage = 750, MaxDcBusVoltage = 810, - S3 = null, // TODO + S3 = new() + { + Bucket = "saliomameiringen", + Region = "sos-ch-dk-2", + Provider = "exo.io", + ContentType = "text/plain; charset=utf-8", + Key = "EXO2bf0cbd97fbfa75aa36ed46f", + Secret = "Bn1CDPqOG-XpDSbYjfIJxojcHTm391vZTc8z8l_fEPs" + }, Devices = new () { RelaysIp = new() { Host = "10.0.1.1", Port = 502}, @@ -85,7 +93,7 @@ public class Config //TODO: let IE choose from config files (Json) and connect t IslandBusLoadMeterIp = new() { Host = "10.0.4.2", Port = 502}, AmptIp = new() { Host = "10.0.5.1", Port = 502}, BatteryIp = new() { Host = "localhost", Port = 6855}, - BatteryNodes = new []{ 2, 3, 4, 5, 6 }, + BatteryNodes = new []{ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, }, }; #endif diff --git a/csharp/App/SaliMax/tunnelstoSalimax0001.sh b/csharp/App/SaliMax/tunnelstoSalimax0001.sh index f538423bb..8b5fb2697 100755 --- a/csharp/App/SaliMax/tunnelstoSalimax0001.sh +++ b/csharp/App/SaliMax/tunnelstoSalimax0001.sh @@ -35,6 +35,8 @@ tunnel "Ext Emu Meter (modbus) " 10.0.4.1 502 4003 tunnel "Int Emu Meter " 10.0.4.2 502 4004 tunnel "AMPT (modbus) " 10.0.5.1 502 4005 tunnel "Adam " 10.0.1.1 502 4006 +tunnel "Batteries " 127.0.0.1 6855 5007 + diff --git a/csharp/App/SaliMax/tunnelstoSalimax0003.sh b/csharp/App/SaliMax/tunnelstoSalimax0003.sh new file mode 100755 index 000000000..da40a584a --- /dev/null +++ b/csharp/App/SaliMax/tunnelstoSalimax0003.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +host=ie-entwicklung@10.2.4.33 + +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 6001 +tunnel "Trumpf DCDC (http) " 10.0.3.1 80 6002 +tunnel "Ext Emu Meter (http) " 10.0.4.1 80 6003 +tunnel "Int Emu Meter (http) " 10.0.4.2 80 6004 +tunnel "AMPT (http) " 10.0.5.1 8080 6005 + +tunnel "Trumpf Inverter (modbus)" 10.0.2.1 502 3001 +tunnel "Trumpf DCDC (modbus) " 10.0.3.1 502 3002 +tunnel "Ext Emu Meter (modbus) " 10.0.4.1 502 3003 +tunnel "Int Emu Meter " 10.0.4.2 502 3004 +tunnel "AMPT (modbus) " 10.0.5.1 502 3005 +tunnel "Batteries " 127.0.0.1 6855 5007 + + +echo +echo "press any key to close the tunnels ..." +read -r -n 1 -s +kill $(jobs -p) +echo "done" diff --git a/csharp/InnovEnergy.sln b/csharp/InnovEnergy.sln index 64acaf80f..beaac246d 100644 --- a/csharp/InnovEnergy.sln +++ b/csharp/InnovEnergy.sln @@ -75,6 +75,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Logging", "Lib\Logging\Logg EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Adam6360D", "Lib\Devices\Adam6360D\Adam6360D.csproj", "{A3C79247-4CAA-44BE-921E-7285AB39E71F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IEM3kGridMeter", "Lib\Devices\IEM3kGridMeter\IEM3kGridMeter.csproj", "{1391165D-51F1-45B4-8B7F-042A20AA0277}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -190,6 +192,10 @@ Global {A3C79247-4CAA-44BE-921E-7285AB39E71F}.Debug|Any CPU.Build.0 = Debug|Any CPU {A3C79247-4CAA-44BE-921E-7285AB39E71F}.Release|Any CPU.ActiveCfg = Release|Any CPU {A3C79247-4CAA-44BE-921E-7285AB39E71F}.Release|Any CPU.Build.0 = Release|Any CPU + {1391165D-51F1-45B4-8B7F-042A20AA0277}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1391165D-51F1-45B4-8B7F-042A20AA0277}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1391165D-51F1-45B4-8B7F-042A20AA0277}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1391165D-51F1-45B4-8B7F-042A20AA0277}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {CF4834CB-91B7-4172-AC13-ECDA8613CD17} = {145597B4-3E30-45E6-9F72-4DD43194539A} @@ -223,5 +229,6 @@ Global {B816BB44-E97E-4E02-B80A-BEDB5B923A96} = {DDDBEFD0-5DEA-4C7C-A9F2-FDB4636CF092} {1A56992B-CB72-490F-99A4-DF1186BA3A18} = {AD5B98A8-AB7F-4DA2-B66D-5B4E63E7D854} {A3C79247-4CAA-44BE-921E-7285AB39E71F} = {4931A385-24DC-4E78-BFF4-356F8D6D5183} + {1391165D-51F1-45B4-8B7F-042A20AA0277} = {4931A385-24DC-4E78-BFF4-356F8D6D5183} EndGlobalSection EndGlobal diff --git a/csharp/Lib/Devices/AMPT/AmptDevices.cs b/csharp/Lib/Devices/AMPT/AmptDevices.cs index 1750d0029..4e8b47256 100644 --- a/csharp/Lib/Devices/AMPT/AmptDevices.cs +++ b/csharp/Lib/Devices/AMPT/AmptDevices.cs @@ -36,7 +36,7 @@ public class AmptDevices } catch (Exception e) { - Console.WriteLine(e); + Console.WriteLine( "Failed to read Ampt data \n"+ e.Message ); // TODO: log } diff --git a/csharp/Lib/Devices/Battery48TL/Battery48TlDevices.cs b/csharp/Lib/Devices/Battery48TL/Battery48TlDevices.cs index 19b636678..866d4a49d 100644 --- a/csharp/Lib/Devices/Battery48TL/Battery48TlDevices.cs +++ b/csharp/Lib/Devices/Battery48TL/Battery48TlDevices.cs @@ -18,13 +18,11 @@ public class Battery48TlDevices return new Battery48TlRecords(records); } - catch + catch (Exception e) { - //Console.WriteLine(e); + Console.WriteLine( "Failed to read Battery data \n"+ e.Message ); // TODO: log - "Failed to read Battery data".WriteLine(); - return Battery48TlRecords.Null; } diff --git a/csharp/Lib/Devices/Battery48TL/Battery48TlRecords.cs b/csharp/Lib/Devices/Battery48TL/Battery48TlRecords.cs index 5d5db55d2..6db267887 100644 --- a/csharp/Lib/Devices/Battery48TL/Battery48TlRecords.cs +++ b/csharp/Lib/Devices/Battery48TL/Battery48TlRecords.cs @@ -9,12 +9,13 @@ public class Battery48TlRecords { var empty = records.Count == 0; - Devices = records; - Eoc = !empty && records.All(r => r.Eoc); - Warnings = records.SelectMany(r => r.Warnings).Distinct().ToList(); - Alarms = records.SelectMany(r => r.Alarms).Distinct().ToList(); - Soc = empty ? 0 : records.Min(r => r.Soc.Value); - Temperature = records.Any() ? records.Average(b => b.Temperatures.Cells.Average.Value) : 0; + Devices = records; + Eoc = !empty && records.All(r => r.Eoc); + Warnings = records.SelectMany(r => r.Warnings).Distinct().ToList(); + Alarms = records.SelectMany(r => r.Alarms).Distinct().ToList(); + Soc = empty ? 0 : records.Min(r => r.Soc.Value); + Temperature = records.Any() ? records.Average(b => b.Temperatures.Cells.Average.Value) : 0; + HeatingCurrent = records.Any() ? records.Sum(b => b.HeatingCurrent) : 0; Dc = empty ? DcBus.FromVoltageCurrent(0, 0) @@ -25,12 +26,13 @@ public class Battery48TlRecords ); } - public DcBus Dc { get; init; } - public Boolean Eoc { get; init; } - public IReadOnlyList Warnings { get; init; } - public IReadOnlyList Alarms { get; init; } - public Percent Soc { get; init; } - public Temperature Temperature { get; init; } + public DcBus Dc { get; init; } + public Boolean Eoc { get; init; } + public IReadOnlyList Warnings { get; init; } + public IReadOnlyList Alarms { get; init; } + public Percent Soc { get; init; } + public Temperature Temperature { get; init; } + public Current HeatingCurrent { get; init; } public IReadOnlyList Devices { get; init; } diff --git a/csharp/Lib/Devices/EmuMeter/EmuMeterDevice.cs b/csharp/Lib/Devices/EmuMeter/EmuMeterDevice.cs index 80c460a8d..aab4ca0fd 100644 --- a/csharp/Lib/Devices/EmuMeter/EmuMeterDevice.cs +++ b/csharp/Lib/Devices/EmuMeter/EmuMeterDevice.cs @@ -28,7 +28,7 @@ public class EmuMeterDevice: ModbusDevice } catch { - "Failed to read data from Inverters".WriteLine(); + "Failed to read data from EmuMeter".WriteLine(); return null; } } diff --git a/csharp/Lib/Devices/IEM3kGridMeter/Doc/Acti 9 iEM3000_A9MEM3255.pdf b/csharp/Lib/Devices/IEM3kGridMeter/Doc/Acti 9 iEM3000_A9MEM3255.pdf new file mode 100644 index 000000000..fc758ccd2 Binary files /dev/null and b/csharp/Lib/Devices/IEM3kGridMeter/Doc/Acti 9 iEM3000_A9MEM3255.pdf differ diff --git a/csharp/Lib/Devices/IEM3kGridMeter/Doc/DOCA0005EN-13.pdf b/csharp/Lib/Devices/IEM3kGridMeter/Doc/DOCA0005EN-13.pdf new file mode 100644 index 000000000..3d954f8cd Binary files /dev/null and b/csharp/Lib/Devices/IEM3kGridMeter/Doc/DOCA0005EN-13.pdf differ diff --git a/csharp/Lib/Devices/IEM3kGridMeter/Doc/NHA15801-06.pdf b/csharp/Lib/Devices/IEM3kGridMeter/Doc/NHA15801-06.pdf new file mode 100644 index 000000000..9da2ba2a5 Binary files /dev/null and b/csharp/Lib/Devices/IEM3kGridMeter/Doc/NHA15801-06.pdf differ diff --git a/csharp/Lib/Devices/IEM3kGridMeter/IEM3kGridMeter.csproj b/csharp/Lib/Devices/IEM3kGridMeter/IEM3kGridMeter.csproj new file mode 100644 index 000000000..622823c62 --- /dev/null +++ b/csharp/Lib/Devices/IEM3kGridMeter/IEM3kGridMeter.csproj @@ -0,0 +1,14 @@ + + + + + InnovEnergy.Lib.Devices.IEM3kGridMeter + + + + + + + + + diff --git a/csharp/Lib/Devices/IEM3kGridMeter/IEM3kGridMeterDevice.cs b/csharp/Lib/Devices/IEM3kGridMeter/IEM3kGridMeterDevice.cs new file mode 100644 index 000000000..de9e04b62 --- /dev/null +++ b/csharp/Lib/Devices/IEM3kGridMeter/IEM3kGridMeterDevice.cs @@ -0,0 +1,50 @@ +using InnovEnergy.Lib.Protocols.Modbus.Channels; +using InnovEnergy.Lib.Protocols.Modbus.Clients; +using InnovEnergy.Lib.Protocols.Modbus.Slaves; +using InnovEnergy.Lib.Utils; + +namespace InnovEnergy.Lib.Devices.IEM3kGridMeter; + +public class Iem3KGridMeterDevice: ModbusDevice +{ + public Iem3KGridMeterDevice(String hostname, UInt16 port = 502, Byte slaveId = 1) : this(new TcpChannel(hostname, port), slaveId) + { + } + + public Iem3KGridMeterDevice(Channel channel, Byte slaveId = 1) : base(new ModbusTcpClient(channel, slaveId)) + { + } + + public Iem3KGridMeterDevice(ModbusClient client) : base(client) + { + } + + + public new Iem3KGridMeterRegisters? Read() + { + try + { + return base.Read(); + } + catch + { + // TODO: Log + $"Failed to read data from {nameof(Iem3KGridMeterDevice)}".WriteLine(); + return null; + } + } + + + public new void Write(Iem3KGridMeterRegisters registers) + { + try + { + base.Write(registers); + } + catch + { + // TODO: Log + $"Failed to write data to {nameof(Iem3KGridMeterDevice)}".WriteLine(); + } + } +} \ No newline at end of file diff --git a/csharp/Lib/Devices/IEM3kGridMeter/IEM3kGridMeterRegisters.cs b/csharp/Lib/Devices/IEM3kGridMeter/IEM3kGridMeterRegisters.cs new file mode 100644 index 000000000..19fe9907a --- /dev/null +++ b/csharp/Lib/Devices/IEM3kGridMeter/IEM3kGridMeterRegisters.cs @@ -0,0 +1,73 @@ +using InnovEnergy.Lib.Protocols.Modbus.Reflection.Attributes; +using InnovEnergy.Lib.StatusApi.DeviceTypes; +using InnovEnergy.Lib.Units.Composite; + +#pragma warning disable CS0649 + +namespace InnovEnergy.Lib.Devices.IEM3kGridMeter; + +using Float32 = Single; + + +[AddressOffset(-2)] // why? +public class Iem3KGridMeterRegisters : IAc3Meter +{ + + // TODO + // The registers below are kept as example (from EmuMeter) + // replace with actual registers from datasheet + + [HoldingRegister(9002)] private Float32 _ActivePowerL1; + [HoldingRegister(9004)] private Float32 _ActivePowerL2; + [HoldingRegister(9006)] private Float32 _ActivePowerL3; + + [HoldingRegister(9012)] private Float32 _ReactivePowerL1; + [HoldingRegister(9014)] private Float32 _ReactivePowerL2; + [HoldingRegister(9016)] private Float32 _ReactivePowerL3; + + [HoldingRegister(9022)] private Float32 _ApparentPowerL1; + [HoldingRegister(9024)] private Float32 _ApparentPowerL2; + [HoldingRegister(9026)] private Float32 _ApparentPowerL3; + + [HoldingRegister(9102)] private Float32 _CurrentL1; + [HoldingRegister(9104)] private Float32 _CurrentL2; + [HoldingRegister(9106)] private Float32 _CurrentL3; + + [HoldingRegister(9200)] private Float32 _VoltageL1N; + [HoldingRegister(9202)] private Float32 _VoltageL2N; + [HoldingRegister(9204)] private Float32 _VoltageL3N; + + [HoldingRegister(9310)] private Float32 _Frequency; + + public Ac3Bus Ac => Ac3Bus.FromPhasesAndFrequency + ( + l1: AcPhase.FromVoltageCurrentActiveReactiveApparent + ( + _VoltageL1N, + _CurrentL1, + _ActivePowerL1, + _ReactivePowerL1, + _ApparentPowerL1 + ), + l2: AcPhase.FromVoltageCurrentActiveReactiveApparent + ( + _VoltageL2N, + _CurrentL2, + _ActivePowerL2, + _ReactivePowerL2, + _ApparentPowerL2 + ), + l3: AcPhase.FromVoltageCurrentActiveReactiveApparent + ( + _VoltageL3N, + _CurrentL3, + _ActivePowerL3, + _ReactivePowerL3, + _ApparentPowerL3 + ), + frequency: _Frequency + ); +} + + + diff --git a/csharp/Lib/Devices/Trumpf/SystemControl/SystemControl.csproj b/csharp/Lib/Devices/Trumpf/SystemControl/SystemControl.csproj index 60ab530b6..31f201d1f 100644 --- a/csharp/Lib/Devices/Trumpf/SystemControl/SystemControl.csproj +++ b/csharp/Lib/Devices/Trumpf/SystemControl/SystemControl.csproj @@ -1,9 +1,14 @@ - - - - - + + + + InnovEnergy.Lib.Devices.Trumpf.SystemControl + + + + + + diff --git a/csharp/Lib/Devices/Trumpf/TruConvertAc/TruConvertAc.csproj b/csharp/Lib/Devices/Trumpf/TruConvertAc/TruConvertAc.csproj index 74d8eea2f..4969d5dab 100644 --- a/csharp/Lib/Devices/Trumpf/TruConvertAc/TruConvertAc.csproj +++ b/csharp/Lib/Devices/Trumpf/TruConvertAc/TruConvertAc.csproj @@ -1,11 +1,15 @@ - - - - - - - + + + + InnovEnergy.Lib.Devices.Trumpf.TruConvertAc + + + + + + + \ No newline at end of file diff --git a/csharp/Lib/Devices/Trumpf/TruConvertAc/TruConvertAcDcDevices.cs b/csharp/Lib/Devices/Trumpf/TruConvertAc/TruConvertAcDcDevices.cs index 8a049cbb8..31cea2131 100644 --- a/csharp/Lib/Devices/Trumpf/TruConvertAc/TruConvertAcDcDevices.cs +++ b/csharp/Lib/Devices/Trumpf/TruConvertAc/TruConvertAcDcDevices.cs @@ -47,8 +47,10 @@ public class TruConvertAcDcDevices return new AcDcDevicesRecord(scStatus, acDcRecords); } - catch + catch (Exception e) { + Console.WriteLine( "Failed to read AcDc data \n"+ e.Message ); + // TODO: log return new AcDcDevicesRecord(null, Array.Empty()); } } diff --git a/csharp/Lib/Devices/Trumpf/TruConvertDc/DcDcRecord.Modbus.cs b/csharp/Lib/Devices/Trumpf/TruConvertDc/DcDcRecord.Modbus.cs index 62a8396e2..c58364efb 100644 --- a/csharp/Lib/Devices/Trumpf/TruConvertDc/DcDcRecord.Modbus.cs +++ b/csharp/Lib/Devices/Trumpf/TruConvertDc/DcDcRecord.Modbus.cs @@ -106,11 +106,3 @@ public partial class DcDcRecord // // #endregion IDcDc } - - - - - - - - diff --git a/csharp/Lib/Devices/Trumpf/TruConvertDc/TruConvertDc.csproj b/csharp/Lib/Devices/Trumpf/TruConvertDc/TruConvertDc.csproj index c365582be..ce1debdf7 100644 --- a/csharp/Lib/Devices/Trumpf/TruConvertDc/TruConvertDc.csproj +++ b/csharp/Lib/Devices/Trumpf/TruConvertDc/TruConvertDc.csproj @@ -1,12 +1,15 @@ + - - - - - - - - + + InnovEnergy.Lib.Devices.Trumpf.TruConvertDc + + + + + + + + diff --git a/csharp/Lib/Protocols/Modbus/Channels/TcpChannel.cs b/csharp/Lib/Protocols/Modbus/Channels/TcpChannel.cs index d026cf6cf..4aa3bd71b 100644 --- a/csharp/Lib/Protocols/Modbus/Channels/TcpChannel.cs +++ b/csharp/Lib/Protocols/Modbus/Channels/TcpChannel.cs @@ -6,9 +6,15 @@ namespace InnovEnergy.Lib.Protocols.Modbus.Channels; public class TcpChannel : ConnectionChannel { + private const Int32 TimeoutMs = 500; // TODO: parametrize - public TcpChannel(Ip4Address ip4Address,Boolean closeAfterSuccessfulRead = false, - Boolean closeAfterSuccessfulWrite = false) : this(ip4Address.Host, ip4Address.Port, closeAfterSuccessfulRead, closeAfterSuccessfulWrite) + public TcpChannel(Ip4Address ip4Address, + Boolean closeAfterSuccessfulRead = false, + Boolean closeAfterSuccessfulWrite = false) : + this(ip4Address.Host, + ip4Address.Port, + closeAfterSuccessfulRead, + closeAfterSuccessfulWrite) { } @@ -18,7 +24,30 @@ public class TcpChannel : ConnectionChannel Boolean closeAfterSuccessfulRead = false, Boolean closeAfterSuccessfulWrite = false) : base(closeAfterSuccessfulRead, closeAfterSuccessfulWrite) { - TcpClient Open() => new(hostname, port); + TcpClient Open() + { + var tcpClient = new TcpClient + { + LingerState = new LingerOption(false, 0), + NoDelay = true, + ReceiveTimeout = TimeoutMs, + SendTimeout = TimeoutMs + }; + + var cts = new CancellationTokenSource(); + + var connected = tcpClient + .ConnectAsync(hostname, port, cts.Token) + .AsTask() + .Wait(TimeoutMs); + + if (connected) + return tcpClient; + + cts.Cancel(); + throw new Exception($"Failed to connect to {hostname}:{port}"); + } + _Open = Open; } diff --git a/csharp/Lib/Protocols/Modbus/Slaves/ModbusDevice.cs b/csharp/Lib/Protocols/Modbus/Slaves/ModbusDevice.cs index 8f0acf770..e04c4bef7 100644 --- a/csharp/Lib/Protocols/Modbus/Slaves/ModbusDevice.cs +++ b/csharp/Lib/Protocols/Modbus/Slaves/ModbusDevice.cs @@ -5,7 +5,7 @@ using static System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes; namespace InnovEnergy.Lib.Protocols.Modbus.Slaves; -public class ModbusDevice where R : notnull +public class ModbusDevice<[DynamicallyAccessedMembers(All)] R> where R : notnull { private readonly IReadOnlyList> _Batches; @@ -35,7 +35,7 @@ public class ModbusDevice where R : notnull return Read(r); } - public R Read([DynamicallyAccessedMembers(All)] R record) + public R Read(R record) { foreach (var batch in _Batches) batch.Read(record); @@ -43,7 +43,7 @@ public class ModbusDevice where R : notnull return record; } - public void Write([DynamicallyAccessedMembers(All)] R record) + public void Write(R record) { foreach (var batch in _Batches) batch.Write(record); diff --git a/csharp/Lib/SrcGen/SrcGen.csproj b/csharp/Lib/SrcGen/SrcGen.csproj index 7f0fcac19..92cbc3d8c 100644 --- a/csharp/Lib/SrcGen/SrcGen.csproj +++ b/csharp/Lib/SrcGen/SrcGen.csproj @@ -2,6 +2,12 @@ + + + InnovEnergy.Lib.SrcGen + + + diff --git a/csharp/Lib/Units/Units.cs b/csharp/Lib/Units/Units.cs index 161b52a0e..50fae56a2 100644 --- a/csharp/Lib/Units/Units.cs +++ b/csharp/Lib/Units/Units.cs @@ -21,6 +21,7 @@ public static class Units public static Temperature Celsius(this Double value) => value; public static Energy KWh (this Double value) => value * 1000; public static Energy Wh (this Double value) => value; + public static Percent Percent(this Double value) => value; public static String ToCsv(this Object thing) { diff --git a/csharp/Lib/Victron/VeDBus/VeDBus.csproj b/csharp/Lib/Victron/VeDBus/VeDBus.csproj index 23827110a..ac40b053d 100644 --- a/csharp/Lib/Victron/VeDBus/VeDBus.csproj +++ b/csharp/Lib/Victron/VeDBus/VeDBus.csproj @@ -7,6 +7,10 @@ + + InnovEnergy.Lib.Victron.VeDBus + + diff --git a/csharp/Lib/Victron/VictronVRM/VictronVRM.csproj b/csharp/Lib/Victron/VictronVRM/VictronVRM.csproj index 57128f448..9f1ced645 100644 --- a/csharp/Lib/Victron/VictronVRM/VictronVRM.csproj +++ b/csharp/Lib/Victron/VictronVRM/VictronVRM.csproj @@ -11,6 +11,10 @@ AnyCPU;linux-arm + + InnovEnergy.Lib.Victron.VictronVRM + +