diff --git a/csharp/Lib/Devices/AMPT/AmptDevice.cs b/csharp/Lib/Devices/AMPT/AmptDevice.cs deleted file mode 100644 index 37e9de5cc..000000000 --- a/csharp/Lib/Devices/AMPT/AmptDevice.cs +++ /dev/null @@ -1,67 +0,0 @@ -using InnovEnergy.Lib.Protocols.Modbus.Clients; -using InnovEnergy.Lib.Protocols.Modbus.Slaves; -using InnovEnergy.Lib.Units.Composite; -using InnovEnergy.Lib.Utils; - -namespace InnovEnergy.Lib.Devices.AMPT; - -public class AmptDevice -{ - private readonly ModbusDevice _CommunicationUnit; - private readonly IEnumerable> _StringOptimizers; - - public AmptDevice(ModbusClient modbusClient) - { - _CommunicationUnit = new ModbusDevice(modbusClient); - _StringOptimizers = StringOptimizers(modbusClient); - } - - - public AmptStatus? Read() - { - var cuStatus = _CommunicationUnit.Read(); - - if (cuStatus.NumberOfStrings == 0) - return default; - - var rs = _StringOptimizers - .Take(cuStatus.NumberOfStrings) - .Select(so => so.Read()) - .ToArray(cuStatus.NumberOfStrings); - - var busVoltage = rs.Average(r => r.Voltage); - var busCurrent = rs.Sum(r => r.Current); - var strings = rs.SelectMany(GetStrings).ToArray(rs.Length * 2); - - return new AmptStatus - ( - Dc : DcBus.FromVoltageCurrent(busVoltage, busCurrent), - Strings: strings - ); - } - - private static IEnumerable GetStrings(StringOptimizerRegisters r) - { - yield return DcBus.FromVoltageCurrent(r.String1Voltage, r.String1Current); - yield return DcBus.FromVoltageCurrent(r.String2Voltage, r.String2Current); - } - - - private static IEnumerable> StringOptimizers(ModbusClient modbusClient) - { - var cache = new List>(); - - ModbusDevice GetOptimizer(Int32 i) - { - if (i < cache.Count) - return cache[i]; - - var modbusDevice = new ModbusDevice(modbusClient, i * 16); - return modbusDevice.Apply(cache.Add); - } - - return Enumerable - .Range(0, Byte.MaxValue) - .Select(GetOptimizer); - } -} \ No newline at end of file diff --git a/csharp/Lib/Devices/AMPT/AmptDevices.cs b/csharp/Lib/Devices/AMPT/AmptDevices.cs new file mode 100644 index 000000000..1750d0029 --- /dev/null +++ b/csharp/Lib/Devices/AMPT/AmptDevices.cs @@ -0,0 +1,100 @@ +using InnovEnergy.Lib.Protocols.Modbus.Channels; +using InnovEnergy.Lib.Protocols.Modbus.Clients; +using InnovEnergy.Lib.Protocols.Modbus.Slaves; +using InnovEnergy.Lib.Units.Composite; +using InnovEnergy.Lib.Utils; + +namespace InnovEnergy.Lib.Devices.AMPT; + +public class AmptDevices +{ + private readonly ModbusDevice _CommunicationUnit; + private readonly IEnumerable> _StringOptimizers; + + public AmptDevices(String hostname, UInt16 port = 502) : this(new TcpChannel(hostname, port)) + { + } + + public AmptDevices(Channel transport) : this(new ModbusTcpClient(transport, 2)) + { + } + + + public AmptDevices(ModbusClient modbusClient) + { + _CommunicationUnit = new ModbusDevice(modbusClient); + _StringOptimizers = StringOptimizers(modbusClient); + } + + public AmptStatus Read() + { + CommunicationUnitRegisters? cuStatus = null; + + try + { + cuStatus = _CommunicationUnit.Read(); + } + catch (Exception e) + { + Console.WriteLine(e); + // TODO: log + } + + // CommunicationUnit knows how many StringOptimizers are connected + var nStringOptimizers = cuStatus?.NumberOfStringOptimizers ?? 0; + + // hardcoded: every SO has 2 strings (produced like this by AMPT) + var nStrings = nStringOptimizers * 2; + + // read stati from optimizers + var soStati = _StringOptimizers + .Take(nStringOptimizers) + .Select(so => so.Read()) + .ToArray(nStringOptimizers); + + // every SO has 2 strings but ONE Dc Link Connection + // they are connected to a shared Dc Link, so Voltage seen by them should be approx the same. + // voltages are averaged, currents added + + // TODO: alarm when we see substantially different voltages + + var busVoltage = nStringOptimizers == 0 ? 0 : soStati.Average(r => r.Voltage); + var busCurrent = nStringOptimizers == 0 ? 0 : soStati.Sum (r => r.Current); + var dc = DcBus.FromVoltageCurrent(busVoltage, busCurrent); + + // flatten the 2 strings of each SO into one array + var strings = soStati.SelectMany(GetStrings).ToArray(nStrings); + + return new AmptStatus(dc, strings); + } + + + + private static IEnumerable GetStrings(StringOptimizerRegisters r) + { + // hardcoded: every SO has 2 strings (produced like this by AMPT) + + yield return DcBus.FromVoltageCurrent(r.String1Voltage, r.String1Current); + yield return DcBus.FromVoltageCurrent(r.String2Voltage, r.String2Current); + } + + + private static IEnumerable> StringOptimizers(ModbusClient modbusClient) + { + var cache = new List>(); + + ModbusDevice GetOptimizer(Int32 i) + { + if (i < cache.Count) + return cache[i]; + + var modbusDevice = new ModbusDevice(modbusClient, i * 16); + cache.Add(modbusDevice); + return modbusDevice; + } + + return Enumerable + .Range(0, Byte.MaxValue) + .Select(GetOptimizer); + } +} \ No newline at end of file diff --git a/csharp/Lib/Devices/AMPT/AmptStatus.cs b/csharp/Lib/Devices/AMPT/AmptStatus.cs index 23311d13c..6f1b2af0d 100644 --- a/csharp/Lib/Devices/AMPT/AmptStatus.cs +++ b/csharp/Lib/Devices/AMPT/AmptStatus.cs @@ -3,4 +3,35 @@ using InnovEnergy.Lib.Units.Composite; namespace InnovEnergy.Lib.Devices.AMPT; -public record AmptStatus(DcBus Dc, IReadOnlyList Strings) : IMppt; +public class AmptStatus : IMppt +{ + public AmptStatus(DcBus dc, IReadOnlyList strings) + { + Dc = dc; + Strings = strings; + } + + public DcBus Dc { get; } + public IReadOnlyList Strings { get; } +} + + +// public static AmptStatus Parallel(IReadOnlyList stati) + // { + // if (stati.Count == 0) + // { + // return new AmptStatus + // ( + // Dc: DcBus.FromVoltageCurrent(0, 0), + // Strings: Array.Empty() + // ); + // } + // + // var voltage = stati.Average(s => s.Dc.Voltage.Value); + // var current = stati.Sum(s => s.Dc.Current.Value); + // var dc = DcBus.FromVoltageCurrent(voltage, current); + // + // var strings = stati.SelectMany(s => s.Strings).ToList(); + // + // return new AmptStatus(dc, strings); + // } \ No newline at end of file diff --git a/csharp/Lib/Devices/AMPT/CommunicationUnitRegisters.cs b/csharp/Lib/Devices/AMPT/CommunicationUnitRegisters.cs index 157758505..5f9edcd41 100644 --- a/csharp/Lib/Devices/AMPT/CommunicationUnitRegisters.cs +++ b/csharp/Lib/Devices/AMPT/CommunicationUnitRegisters.cs @@ -14,5 +14,5 @@ public record CommunicationUnitRegisters [HoldingRegister(74)] public Int16 VoltageScaleFactor { get; private set; } [HoldingRegister(76)] public Int16 EnergyScaleFactor { get; private set; } - [HoldingRegister(78)] public UInt16 NumberOfStrings { get; private set; } + [HoldingRegister(78)] public UInt16 NumberOfStringOptimizers { get; private set; } } \ No newline at end of file diff --git a/csharp/Lib/Devices/AMPT/Program.cs b/csharp/Lib/Devices/AMPT/Program.cs index 1e94c56dd..55fb5194d 100644 --- a/csharp/Lib/Devices/AMPT/Program.cs +++ b/csharp/Lib/Devices/AMPT/Program.cs @@ -1,6 +1,6 @@ -using System.Text.Json; using InnovEnergy.Lib.Protocols.Modbus.Channels; using InnovEnergy.Lib.Protocols.Modbus.Clients; +using InnovEnergy.Lib.Units; using InnovEnergy.Lib.Utils; namespace InnovEnergy.Lib.Devices.AMPT; @@ -11,15 +11,14 @@ public static class Program { var ch = new TcpChannel("localhost", 5005); var cl = new ModbusTcpClient(ch, 1); - var d = new AmptDevice(cl); + var d = new AmptDevices(cl); while (true) { - var x = d.Read(); + AmptStatus x = d.Read(); + + x.ToCsv().WriteLine(); - var options = new JsonSerializerOptions{WriteIndented = true}; - (x, options).Apply(JsonSerializer.Serialize).WriteLine(); - //Console.WriteLine(x); }