using System.Reflection; using InnovEnergy.Lib.Protocols.Modbus.Reflection.Attributes; using InnovEnergy.Lib.Utils; #pragma warning disable CS8509 namespace InnovEnergy.Lib.Protocols.Modbus.Reflection; internal readonly struct ModbusRegister : IAddress { public UInt16 Address => _Attribute.Address; public Decimal Offset => (Decimal)_Attribute.Offset; public Decimal Scale => (Decimal)_Attribute.Scale; private readonly Object _Record; private readonly ModbusRegisterAttribute _Attribute; private readonly MemberInfo _MemberInfo; private static readonly Type[] UnsignedTypes = { typeof(Byte), typeof(UInt16), typeof(UInt32), typeof(UInt64), }; private Boolean IsUnsigned { get { var type = _MemberInfo switch { FieldInfo fi => fi.FieldType, PropertyInfo pi => pi.PropertyType }; type = type.IsEnum ? Enum.GetUnderlyingType(type) : type; return UnsignedTypes.Contains(type); } } public ModbusRegister(ModbusRegisterAttribute attribute, MemberInfo memberInfo, Object record) { _Record = record; _Attribute = attribute; _MemberInfo = memberInfo; } [Obsolete] public ModbusRegister() => throw new Exception("Forbidden"); public UInt16 SetStatusRecordMemberFromRawModbusValue(UInt16 rawRegisterValue) { var raw = IsUnsigned ? (Decimal)rawRegisterValue : (Int16)rawRegisterValue; // if it is signed we must first reinterpret cast the native UInt16 to Int16 var transformed = (raw + Offset) * Scale; if (_MemberInfo is FieldInfo fi) { var converted = transformed.ConvertTo(fi.FieldType); fi.SetValue(_Record, converted); } else if (_MemberInfo is PropertyInfo pi) { var converted = transformed.ConvertTo(pi.PropertyType); pi.SetValue(_Record, converted); } return Address; } public UInt16 GetRawModbusValueFromControlRecord() { var value = _MemberInfo switch { FieldInfo fi => fi.GetValue(_Record), PropertyInfo pi => pi.GetValue(_Record), _ => throw new ArgumentException(nameof(_MemberInfo)) }; var transformed = value! .CastTo() .ConvertTo() / Scale - Offset; return IsUnsigned ? transformed.ConvertTo() : transformed.ConvertTo().CastTo(); } }