128 lines
4.6 KiB
C#
128 lines
4.6 KiB
C#
using InnovEnergy.Lib.Protocols.Modbus.Clients;
|
|
using InnovEnergy.Lib.Protocols.Modbus.Protocol;
|
|
using InnovEnergy.Lib.Protocols.Modbus.Protocol.Frames;
|
|
using InnovEnergy.Lib.Protocols.Modbus.Protocol.Frames.Accessors;
|
|
|
|
namespace InnovEnergy.Lib.Protocols.Modbus.Reflection;
|
|
|
|
#pragma warning disable CS8509
|
|
#pragma warning disable CS8524
|
|
|
|
internal record Batch<R>(Action<R> Read, Action<R> Write, String DebugString)
|
|
{
|
|
public override String ToString() => DebugString;
|
|
}
|
|
|
|
public static class Batches
|
|
{
|
|
internal static IReadOnlyList<Batch<R>> MakeBatchesFor<R>(this ModbusClient modbusClient, Int32 addressOffset)
|
|
{
|
|
var members = ModbusMembers
|
|
.From<R>(addressOffset)
|
|
.OrderBy(m => m.Kind)
|
|
.ThenBy(m => m.StartAddress)
|
|
.ThenBy(m => m.EndAddress);
|
|
|
|
return MakeBatches<R>(modbusClient, members).ToList();
|
|
}
|
|
|
|
private static IEnumerable<Batch<R>> MakeBatches<R>(ModbusClient mb, IEnumerable<ModbusMember> modbusMembers)
|
|
{
|
|
var batchMembers = new List<ModbusMember>();
|
|
|
|
foreach (var member in modbusMembers)
|
|
{
|
|
if (CloseBatch(member))
|
|
{
|
|
yield return MakeBatch<R>(mb, batchMembers);
|
|
batchMembers = new List<ModbusMember>();
|
|
}
|
|
|
|
batchMembers.Add(member);
|
|
}
|
|
|
|
if (batchMembers.Count > 0)
|
|
yield return MakeBatch<R>(mb, batchMembers);
|
|
|
|
Boolean CloseBatch(ModbusMember m)
|
|
{
|
|
if (batchMembers.Count == 0)
|
|
return false;
|
|
|
|
return m.StartAddress > batchMembers[^1].EndAddress // gap between registers
|
|
|| m.EndAddress > batchMembers[0].StartAddress + Constants.MaxRegs // max batch size reached
|
|
|| m.Kind != batchMembers[0].Kind; // different Kind
|
|
}
|
|
}
|
|
|
|
|
|
private static Batch<R> MakeBatch<R>(ModbusClient modbusClient, IReadOnlyList<ModbusMember> members)
|
|
{
|
|
var startAddress = members[0].StartAddress;
|
|
var endAddress = members[^1].EndAddress;
|
|
var count = (UInt16)(endAddress - startAddress);
|
|
var kind = members[0].Kind;
|
|
var isWritable = kind is ModbusKind.HoldingRegister or ModbusKind.Coil;
|
|
var debugString = $"{kind}: {startAddress}" + (endAddress - 1 == startAddress ? "" : $"-{endAddress - 1}");
|
|
|
|
|
|
// var modPoll = $"{kind}: {startAddress}-{endAddress}"; // TODO
|
|
var read = MakeRead();
|
|
var write = MakeWrite();
|
|
|
|
return new Batch<R>(read, write, debugString);
|
|
|
|
Action<R> MakeRead()
|
|
{
|
|
Func<MbData> readModbus = kind switch
|
|
{
|
|
ModbusKind.InputRegister => () => modbusClient.ReadInputRegisters (startAddress, count),
|
|
ModbusKind.HoldingRegister => () => modbusClient.ReadHoldingRegisters(startAddress, count),
|
|
ModbusKind.DiscreteInput => () => modbusClient.ReadDiscreteInputs (startAddress, count),
|
|
ModbusKind.Coil => () => modbusClient.ReadCoils (startAddress, count),
|
|
};
|
|
|
|
//Console.WriteLine("start: " + startAddress + " count: " + count);
|
|
|
|
return record =>
|
|
{
|
|
var mbData = readModbus();
|
|
foreach (var member in members)
|
|
{
|
|
member.ModbusToRecord(mbData, record!);
|
|
}
|
|
};
|
|
}
|
|
|
|
Action<R> MakeWrite()
|
|
{
|
|
if (!isWritable)
|
|
return _ => { }; // nop
|
|
|
|
Func<MbData> createMbData = kind switch
|
|
{
|
|
ModbusKind.HoldingRegister => () => MbData.Registers(startAddress, count),
|
|
ModbusKind.Coil => () => MbData.Coils (startAddress, count),
|
|
};
|
|
|
|
|
|
Action<MbData> writeModbus = kind switch
|
|
{
|
|
ModbusKind.HoldingRegister => d => modbusClient.WriteRegisters(startAddress, d.GetRegisters()),
|
|
ModbusKind.Coil => d => modbusClient.WriteCoils (startAddress, d.GetCoils().Take(count).ToList()),
|
|
// ^^ TODO: Coils.count is broken, fix when refactoring to use direct binary codec
|
|
};
|
|
|
|
|
|
return rec =>
|
|
{
|
|
var mbData = createMbData();
|
|
|
|
foreach (var member in members)
|
|
member.RecordToModbus(rec!, mbData);
|
|
|
|
writeModbus(mbData);
|
|
};
|
|
}
|
|
}
|
|
} |