using InnovEnergy.Lib.Protocols.Modbus.Clients; using InnovEnergy.Lib.Protocols.Modbus.Connections; using InnovEnergy.Lib.Units.Composite; using static DecimalMath.DecimalEx; namespace InnovEnergy.Lib.Devices.AMPT; public class AmptCommunicationUnit { private ModbusTcpClient? Modbus { get; set; } private const UInt16 RegistersPerDevice = 16; private const UInt16 FirstDeviceOffset = 85; public String Hostname { get; } public UInt16 Port { get; } public Byte SlaveAddress { get; } public AmptCommunicationUnit(String hostname, UInt16 port = 502, Byte slaveAddress = 1) { Hostname = hostname; Port = port; SlaveAddress = slaveAddress; } public AmptCommunicationUnitStatus? ReadStatus() { try { OpenConnection(); return TryReadStatus(); } catch { CloseConnection(); return null; } } private void CloseConnection() { try { Modbus?.CloseConnection(); } catch { // ignored } Modbus = null; } private void OpenConnection() { if (Modbus is null) { var connection = new ModbusTcpConnection(Hostname, Port); Modbus = new ModbusTcpClient(connection, SlaveAddress); } } private AmptCommunicationUnitStatus TryReadStatus() { var r = Modbus!.ReadHoldingRegisters(1, 116); var currentFactor = Pow(10.0m, r.GetInt16(73)); var voltageFactor = Pow(10.0m, r.GetInt16(74)); var energyFactor = Pow(10.0m, r.GetInt16(76) + 3); // +3 => converted from Wh to kWh var nbrOfDevices = r.GetUInt16(78); var devices = Enumerable .Range(0, nbrOfDevices) .Select(ReadDeviceStatus) .ToList(); return new AmptCommunicationUnitStatus { Sid = r.GetUInt32(1), IdSunSpec = r.GetUInt16(3), Manufacturer = r.GetString(5, 16), Model = r.GetString(21, 16), Version = r.GetString(45, 8), SerialNumber = r.GetString(53, 16), DeviceAddress = r.GetInt16(69), IdVendor = r.GetUInt16(71), Devices = devices }; AmptStatus ReadDeviceStatus(Int32 deviceNumber) { var baseAddress = (UInt16)(FirstDeviceOffset + deviceNumber * RegistersPerDevice); // base address return new AmptStatus { Dc = new DcBus { Voltage = r.GetUInt32((UInt16)(baseAddress + 6)) * voltageFactor, Current = r.GetUInt16((UInt16)(baseAddress + 5)) * currentFactor }, Strings = new DcBus[] { new() { Voltage = r.GetUInt32((UInt16)(baseAddress + 8)) * voltageFactor, Current = r.GetUInt16((UInt16)(baseAddress + 14)) * currentFactor }, new() { Voltage = r.GetUInt32((UInt16)(baseAddress + 9)) * voltageFactor, Current = r.GetUInt16((UInt16)(baseAddress + 15)) * currentFactor } }, ProductionToday = r.GetUInt32((UInt16)(baseAddress + 12)) * energyFactor, }; } } }