Add a writable flag to holding registers

This commit is contained in:
atef 2025-08-04 10:39:51 +02:00
parent 5425c1b713
commit 5610a10eaf
3 changed files with 76 additions and 14 deletions

View File

@ -6,20 +6,24 @@ namespace InnovEnergy.Lib.Protocols.Modbus.Reflection.Attributes;
public class HoldingRegister : ModbusRegister public class HoldingRegister : ModbusRegister
{ {
private static readonly Type DefaultModbusType = typeof(UInt16); 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<T> : HoldingRegister where T : IConvertible public class HoldingRegister<T> : HoldingRegister where T : IConvertible
{ {
public HoldingRegister(UInt16 address) : base(address, typeof(T)) public HoldingRegister(UInt16 address, Boolean writable = false) : base(address, typeof(T), writable)
{ {
} }
} }

View File

@ -2,6 +2,8 @@ using InnovEnergy.Lib.Protocols.Modbus.Clients;
using InnovEnergy.Lib.Protocols.Modbus.Protocol; using InnovEnergy.Lib.Protocols.Modbus.Protocol;
using InnovEnergy.Lib.Protocols.Modbus.Protocol.Frames; using InnovEnergy.Lib.Protocols.Modbus.Protocol.Frames;
using InnovEnergy.Lib.Protocols.Modbus.Protocol.Frames.Accessors; 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; namespace InnovEnergy.Lib.Protocols.Modbus.Reflection;
@ -28,12 +30,14 @@ public static class Batches
private static IEnumerable<Batch<R>> MakeBatches<R>(ModbusClient mb, IEnumerable<ModbusMember> modbusMembers) private static IEnumerable<Batch<R>> MakeBatches<R>(ModbusClient mb, IEnumerable<ModbusMember> modbusMembers)
{ {
var batchMembers = new List<ModbusMember>(); var batchMembers = new List<ModbusMember>();
foreach (var member in modbusMembers) foreach (var member in modbusMembers)
{ {
if (CloseBatch(member)) if (CloseBatch(member))
{ {
//PrintBatchStart(batchMembers);
yield return MakeBatch<R>(mb, batchMembers); yield return MakeBatch<R>(mb, batchMembers);
batchMembers = new List<ModbusMember>(); batchMembers = new List<ModbusMember>();
} }
@ -42,7 +46,10 @@ public static class Batches
} }
if (batchMembers.Count > 0) if (batchMembers.Count > 0)
{
//PrintBatchStart(batchMembers);
yield return MakeBatch<R>(mb, batchMembers); yield return MakeBatch<R>(mb, batchMembers);
}
Boolean CloseBatch(ModbusMember m) Boolean CloseBatch(ModbusMember m)
{ {
@ -51,10 +58,24 @@ public static class Batches
return m.StartAddress > batchMembers[^1].EndAddress // gap between registers return m.StartAddress > batchMembers[^1].EndAddress // gap between registers
|| m.EndAddress > batchMembers[0].StartAddress + Constants.MaxRegs // max batch size reached || 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<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) private static Batch<R> MakeBatch<R>(ModbusClient modbusClient, IReadOnlyList<ModbusMember> members)
{ {
@ -62,9 +83,12 @@ public static class Batches
var endAddress = members[^1].EndAddress; var endAddress = members[^1].EndAddress;
var count = (UInt16)(endAddress - startAddress); var count = (UInt16)(endAddress - startAddress);
var kind = members[0].Kind; 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 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 modPoll = $"{kind}: {startAddress}-{endAddress}"; // TODO
var read = MakeRead(); var read = MakeRead();
@ -113,12 +137,21 @@ public static class Batches
// ^^ TODO: Coils.count is broken, fix when refactoring to use direct binary codec // ^^ 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 => return rec =>
{ {
var mbData = createMbData(); var mbData = createMbData();
foreach (var member in members) foreach (var member in writableMembers)
member.RecordToModbus(rec!, mbData); member.RecordToModbus(rec!, mbData);
writeModbus(mbData); writeModbus(mbData);

View File

@ -17,8 +17,14 @@ internal record ModbusMember
UInt16 EndAddress, UInt16 EndAddress,
ModbusKind Kind, ModbusKind Kind,
Action<MbData, Object> ModbusToRecord, Action<MbData, Object> ModbusToRecord,
Action<Object, MbData> RecordToModbus Action<Object, MbData> RecordToModbus,
); MemberInfo Member,
Boolean IsWritable
)
{
public Boolean HasSetter() =>
Member is PropertyInfo pi && pi.SetMethod != null;
}
internal static class ModbusMembers internal static class ModbusMembers
{ {
@ -72,13 +78,18 @@ internal static class ModbusMembers
//Console.WriteLine(info.Name +" " + address + " " + modbusType); //Console.WriteLine(info.Name +" " + address + " " + modbusType);
var writableProperty = attribute.GetType().GetProperty("Writable");
var isWritable = writableProperty != null && (Boolean)(writableProperty.GetValue(attribute) ?? false);
return new ModbusMember return new ModbusMember
( (
address, address,
endAddress, endAddress,
attribute.Kind, attribute.Kind,
ModbusToRecord(), ModbusToRecord(),
RecordToModbus() RecordToModbus(),
info,
isWritable
); );
Action<MbData, Object> ModbusToRecord() Action<MbData, Object> ModbusToRecord()
@ -152,7 +163,21 @@ internal static class ModbusMembers
var memberValue = get(rec); var memberValue = get(rec);
var encoded = encode(memberValue); var encoded = encode(memberValue);
//writeToMbData(encoded, mbData.WithEndian(endian));
try
{
writeToMbData(encoded, mbData.WithEndian(endian)); 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);
}
}; };
} }
} }