Add a writable flag to holding registers
This commit is contained in:
parent
5425c1b713
commit
5610a10eaf
|
|
@ -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)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,20 +58,37 @@ 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)
|
||||||
{
|
{
|
||||||
var startAddress = members[0].StartAddress;
|
var startAddress = members[0].StartAddress;
|
||||||
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();
|
||||||
|
|
@ -112,17 +136,26 @@ public static class Batches
|
||||||
ModbusKind.Coil => d => modbusClient.WriteCoils (startAddress, d.GetCoils().Take(count).ToList()),
|
ModbusKind.Coil => d => modbusClient.WriteCoils (startAddress, d.GetCoils().Take(count).ToList()),
|
||||||
// ^^ 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);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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));
|
//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);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue