Innovenergy_trunk/csharp/Lib/Protocols/Modbus/Reflection/Batch.cs

115 lines
4.0 KiB
C#

using InnovEnergy.Lib.Protocols.Modbus.Clients;
using InnovEnergy.Lib.Protocols.Modbus.Protocol.Frames;
using InnovEnergy.Lib.Protocols.Modbus.Protocol.Frames.Accessors;
using InnovEnergy.Lib.Protocols.Modbus.Reflection.Attributes;
namespace InnovEnergy.Lib.Protocols.Modbus.Reflection;
#pragma warning disable CS8509
internal record Batch<R>(Action<R> Read, Action<R> Write);
public static class Batches
{
internal static IEnumerable<Batch<R>> MakeBatchesFor<R>(this ModbusClient modbusClient, Int32 addressOffset)
{
var members = ModbusMembers
.From<R>(addressOffset)
.OrderBy(m => m.Attribute.GetType().Name)
.ThenBy(m => m.StartAddress)
.ThenBy(m => m.EndAddress);
return MakeBatches<R>(modbusClient, members);
}
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.Attribute.GetType() != batchMembers[0].Attribute.GetType(); // different ModbusType
}
}
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 attribute = members[0].Attribute;
var isWritable = attribute is HoldingRegisterAttribute or CoilAttribute;
return new Batch<R>(Read(), Write());
Action<R> Read()
{
Func<MbData> readModbus = attribute switch
{
InputRegisterAttribute => () => modbusClient.ReadInputRegisters (startAddress, count),
HoldingRegisterAttribute => () => modbusClient.ReadHoldingRegisters(startAddress, count),
DiscreteInputAttribute => () => modbusClient.ReadDiscreteInputs (startAddress, count),
CoilAttribute => () => modbusClient.ReadCoils (startAddress, count),
};
return record =>
{
foreach (var member in members)
{
var mbData = readModbus();
member.ModbusToRecord(mbData, record!);
}
};
}
Action<R> Write()
{
if (!isWritable)
return _ => { }; // nop
Func<MbData> createMbData = attribute switch
{
HoldingRegisterAttribute => () => MbData.Registers(startAddress, count),
CoilAttribute => () => MbData.Coils (startAddress, count),
};
Action<MbData> writeModbus = attribute switch
{
HoldingRegisterAttribute => d => modbusClient.WriteRegisters(startAddress, d.GetRegisters()),
CoilAttribute => d => modbusClient.WriteCoils (startAddress, d.GetCoils()),
};
return rec =>
{
foreach (var member in members)
{
var mbData = createMbData();
member.RecordToModbus(rec!, mbData);
writeModbus(mbData);
}
};
}
}
}