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(Action Read, Action Write); public static class Batches { internal static IEnumerable> MakeBatchesFor(this ModbusClient modbusClient, Int32 addressOffset) { var members = ModbusMembers .From(addressOffset) .OrderBy(m => m.Attribute.GetType().Name) .ThenBy(m => m.StartAddress) .ThenBy(m => m.EndAddress); return MakeBatches(modbusClient, members); } private static IEnumerable> MakeBatches(ModbusClient mb, IEnumerable modbusMembers) { var batchMembers = new List(); foreach (var member in modbusMembers) { if (CloseBatch(member)) { yield return MakeBatch(mb, batchMembers); batchMembers = new List(); } batchMembers.Add(member); } if (batchMembers.Count > 0) yield return MakeBatch(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 MakeBatch(ModbusClient modbusClient, IReadOnlyList 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(Read(), Write()); Action Read() { Func 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 Write() { if (!isWritable) return _ => { }; // nop Func createMbData = attribute switch { HoldingRegisterAttribute => () => MbData.Registers(startAddress, count), CoilAttribute => () => MbData.Coils (startAddress, count), }; Action 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); } }; } } }