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

162 lines
6.1 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;
using InnovEnergy.Lib.Protocols.Modbus.Reflection.Attributes;
using InnovEnergy.Lib.Utils;
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))
{
//PrintBatchStart(batchMembers);
yield return MakeBatch<R>(mb, batchMembers);
batchMembers = new List<ModbusMember>();
}
batchMembers.Add(member);
}
if (batchMembers.Count > 0)
{
//PrintBatchStart(batchMembers);
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
|| m.IsWritable != batchMembers[0].IsWritable; // writable mismatch
}
}
// for debuging
private static void PrintBatchStart(List<ModbusMember> members)
{
if (members.Count == 0) return;
var first = members.First();
var last = members.Last();
var range = first.StartAddress == last.EndAddress - 1
? first.StartAddress.ToString()
: $"{first.StartAddress}-{last.EndAddress - 1}";
var writable = first.IsWritable ? "Writable" : "ReadOnly";
Console.WriteLine($"📦 New Batch: {first.Kind} [{range}] ({writable})");
}
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 isWritable = members.Any(m => m.IsWritable);
//foreach (var m in members)
// Console.WriteLine($"🧪 Address {m.StartAddress} Writable: {m.IsWritable}");
// 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
};
//if (isWritable && members.Any(m => !m.IsWritable))
//{
// Console.WriteLine($"⚠️ Batch {debugString} has both writable and non-writable members — writing might fail.");
//}
var writableMembers = members.Where(m => m.IsWritable).ToList();
if (!writableMembers.Any())
return _ => { }; // No writable members = no-op*/
return rec =>
{
var mbData = createMbData();
foreach (var member in writableMembers)
member.RecordToModbus(rec!, mbData);
writeModbus(mbData);
};
}
}
}