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(Action Read, Action Write); public static class Batches { internal static IReadOnlyList> MakeBatchesFor(this ModbusClient modbusClient, Int32 addressOffset) { var members = ModbusMembers .From(addressOffset) .OrderBy(m => m.Kind) .ThenBy(m => m.StartAddress) .ThenBy(m => m.EndAddress); return MakeBatches(modbusClient, members).ToList(); } 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.Kind != batchMembers[0].Kind; // different Kind } } private static Batch MakeBatch(ModbusClient modbusClient, IReadOnlyList 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; return new Batch(Read(), Write()); Action Read() { Func 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 Write() { if (!isWritable) return _ => { }; // nop Func createMbData = kind switch { ModbusKind.HoldingRegister => () => MbData.Registers(startAddress, count), ModbusKind.Coil => () => MbData.Coils (startAddress, count), }; Action 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); }; } } }