120 lines
3.6 KiB
C#
120 lines
3.6 KiB
C#
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<CommunicationUnitRegisters> _CommunicationUnit;
|
|
private readonly IEnumerable<ModbusDevice<StringOptimizerRegisters>> _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<CommunicationUnitRegisters>(modbusClient);
|
|
_StringOptimizers = StringOptimizers(modbusClient);
|
|
}
|
|
|
|
public AmptStatus? Read()
|
|
{
|
|
try
|
|
{
|
|
return TryRead();
|
|
}
|
|
catch ( Exception e)
|
|
{
|
|
if( e is not NullChannelException)
|
|
Console.WriteLine("Failed to read Ampt data \n" + e.Message);
|
|
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public AmptStatus TryRead()
|
|
{
|
|
var cuStatus = _CommunicationUnit.Read();
|
|
|
|
// CommunicationUnit knows how many StringOptimizers are connected
|
|
var nStringOptimizers = cuStatus.NumberOfStringOptimizers;
|
|
|
|
// 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 = new DcBus
|
|
{
|
|
Voltage = busVoltage,
|
|
Current = busCurrent,
|
|
};
|
|
|
|
// flatten the 2 strings of each SO into one array
|
|
var strings = soStati.SelectMany(GetStrings).ToArray(nStrings);
|
|
|
|
return new AmptStatus
|
|
{
|
|
Dc = dc,
|
|
Strings = strings
|
|
};
|
|
}
|
|
|
|
|
|
|
|
private static IEnumerable<DcBus> GetStrings(StringOptimizerRegisters r)
|
|
{
|
|
// hardcoded: every SO has 2 strings (produced like this by AMPT)
|
|
|
|
yield return new()
|
|
{
|
|
Voltage = r.String1Voltage,
|
|
Current = r.String1Current,
|
|
};
|
|
yield return new()
|
|
{
|
|
Voltage = r.String2Voltage,
|
|
Current = r.String2Current,
|
|
};
|
|
}
|
|
|
|
|
|
private static IEnumerable<ModbusDevice<StringOptimizerRegisters>> StringOptimizers(ModbusClient modbusClient)
|
|
{
|
|
var cache = new List<ModbusDevice<StringOptimizerRegisters>>();
|
|
|
|
ModbusDevice<StringOptimizerRegisters> GetOptimizer(Int32 i)
|
|
{
|
|
if (i < cache.Count)
|
|
return cache[i];
|
|
|
|
var modbusDevice = new ModbusDevice<StringOptimizerRegisters>(modbusClient, i * 16);
|
|
cache.Add(modbusDevice);
|
|
return modbusDevice;
|
|
}
|
|
|
|
return Enumerable
|
|
.Range(0, Byte.MaxValue)
|
|
.Select(GetOptimizer);
|
|
}
|
|
} |