From 5610a10eaffdcf3fa389e89766d53d4d37308dd2 Mon Sep 17 00:00:00 2001 From: atef Date: Mon, 4 Aug 2025 10:39:51 +0200 Subject: [PATCH] Add a writable flag to holding registers --- .../Reflection/Attributes/HoldingRegister.cs | 10 ++-- .../Lib/Protocols/Modbus/Reflection/Batch.cs | 47 ++++++++++++++++--- .../Modbus/Reflection/ModbusMember.cs | 33 +++++++++++-- 3 files changed, 76 insertions(+), 14 deletions(-) diff --git a/csharp/Lib/Protocols/Modbus/Reflection/Attributes/HoldingRegister.cs b/csharp/Lib/Protocols/Modbus/Reflection/Attributes/HoldingRegister.cs index 0e0128a4c..eb2fb3495 100644 --- a/csharp/Lib/Protocols/Modbus/Reflection/Attributes/HoldingRegister.cs +++ b/csharp/Lib/Protocols/Modbus/Reflection/Attributes/HoldingRegister.cs @@ -6,20 +6,24 @@ namespace InnovEnergy.Lib.Protocols.Modbus.Reflection.Attributes; public class HoldingRegister : ModbusRegister { private static readonly Type DefaultModbusType = typeof(UInt16); + public Boolean Writable { get; } - public HoldingRegister(UInt16 address) : this(address, DefaultModbusType) + public HoldingRegister(UInt16 address, Boolean writable = false) : this(address, DefaultModbusType, writable) { } - public HoldingRegister(UInt16 address, Type modbusType) : base(address, modbusType, ModbusKind.HoldingRegister) + public HoldingRegister(UInt16 address, Type modbusType, Boolean writable = false) : base(address, modbusType, ModbusKind.HoldingRegister ) { + Writable = writable; } + + } public class HoldingRegister : HoldingRegister where T : IConvertible { - public HoldingRegister(UInt16 address) : base(address, typeof(T)) + public HoldingRegister(UInt16 address, Boolean writable = false) : base(address, typeof(T), writable) { } } diff --git a/csharp/Lib/Protocols/Modbus/Reflection/Batch.cs b/csharp/Lib/Protocols/Modbus/Reflection/Batch.cs index 15be8c7d0..7e7907c0b 100644 --- a/csharp/Lib/Protocols/Modbus/Reflection/Batch.cs +++ b/csharp/Lib/Protocols/Modbus/Reflection/Batch.cs @@ -2,6 +2,8 @@ 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; @@ -28,12 +30,14 @@ public static class Batches private static IEnumerable> MakeBatches(ModbusClient mb, IEnumerable modbusMembers) { + var batchMembers = new List(); foreach (var member in modbusMembers) { if (CloseBatch(member)) { + //PrintBatchStart(batchMembers); yield return MakeBatch(mb, batchMembers); batchMembers = new List(); } @@ -42,7 +46,10 @@ public static class Batches } if (batchMembers.Count > 0) + { + //PrintBatchStart(batchMembers); yield return MakeBatch(mb, batchMembers); + } Boolean CloseBatch(ModbusMember m) { @@ -51,20 +58,37 @@ public static class Batches 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.Kind != batchMembers[0].Kind // different Kind + || m.IsWritable != batchMembers[0].IsWritable; // writable mismatch } } - + // for debuging + private static void PrintBatchStart(List 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 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; + //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(); @@ -112,17 +136,26 @@ public static class Batches 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 members) + foreach (var member in writableMembers) member.RecordToModbus(rec!, mbData); writeModbus(mbData); }; } } -} \ No newline at end of file +} diff --git a/csharp/Lib/Protocols/Modbus/Reflection/ModbusMember.cs b/csharp/Lib/Protocols/Modbus/Reflection/ModbusMember.cs index ed62ba2b1..f6a1529a9 100644 --- a/csharp/Lib/Protocols/Modbus/Reflection/ModbusMember.cs +++ b/csharp/Lib/Protocols/Modbus/Reflection/ModbusMember.cs @@ -17,8 +17,14 @@ internal record ModbusMember UInt16 EndAddress, ModbusKind Kind, Action ModbusToRecord, - Action RecordToModbus -); + Action RecordToModbus, + MemberInfo Member, + Boolean IsWritable +) +{ + public Boolean HasSetter() => + Member is PropertyInfo pi && pi.SetMethod != null; +} internal static class ModbusMembers { @@ -72,13 +78,18 @@ internal static class ModbusMembers //Console.WriteLine(info.Name +" " + address + " " + modbusType); + var writableProperty = attribute.GetType().GetProperty("Writable"); + var isWritable = writableProperty != null && (Boolean)(writableProperty.GetValue(attribute) ?? false); + return new ModbusMember ( address, endAddress, attribute.Kind, ModbusToRecord(), - RecordToModbus() + RecordToModbus(), + info, + isWritable ); Action ModbusToRecord() @@ -152,7 +163,21 @@ internal static class ModbusMembers var memberValue = get(rec); var encoded = encode(memberValue); - writeToMbData(encoded, mbData.WithEndian(endian)); + //writeToMbData(encoded, mbData.WithEndian(endian)); + try + { + writeToMbData(encoded, mbData.WithEndian(endian)); + } + catch (OverflowException ex) + { + throw new OverflowException( + $"โ— Overflow while writing '{info.Name}': Value = {encoded}, TargetType = {modbusType.Name}", ex); + } + catch (Exception ex) + { + throw new Exception( + $"โ— Error writing '{info.Name}': Value = {encoded}, TargetType = {modbusType.Name}", ex); + } }; } }