diff --git a/MewtocolNet/MewtocolInterfaceRegisterHandling.cs b/MewtocolNet/MewtocolInterfaceRegisterHandling.cs index 17d962b..27be26b 100644 --- a/MewtocolNet/MewtocolInterfaceRegisterHandling.cs +++ b/MewtocolNet/MewtocolInterfaceRegisterHandling.cs @@ -393,7 +393,7 @@ namespace MewtocolNet { Register = reg, PreviousValue = preValue, PreviousValueString = preValueString, - Value = reg.Value, + Value = reg.ValueObj, }); } diff --git a/MewtocolNet/MewtocolInterfaceRequests.cs b/MewtocolNet/MewtocolInterfaceRequests.cs index 7b6c6f2..51769a2 100644 --- a/MewtocolNet/MewtocolInterfaceRequests.cs +++ b/MewtocolNet/MewtocolInterfaceRequests.cs @@ -108,6 +108,9 @@ namespace MewtocolNet { /// public async Task WriteByteRange(int start, byte[] byteArr) { + if (byteArr == null) + throw new ArgumentNullException(nameof(byteArr)); + string byteString = byteArr.ToHexString(); var wordLength = byteArr.Length / 2; diff --git a/MewtocolNet/RegisterAttributes/RegisterCollection.cs b/MewtocolNet/RegisterAttributes/RegisterCollection.cs index 5217e8a..b05bc5a 100644 --- a/MewtocolNet/RegisterAttributes/RegisterCollection.cs +++ b/MewtocolNet/RegisterAttributes/RegisterCollection.cs @@ -37,7 +37,7 @@ namespace MewtocolNet.RegisterAttributes { if (value is IRegister reg) { - privateField = (T)reg.Value; + privateField = (T)reg.ValueObj; return; } diff --git a/MewtocolNet/RegisterBuilding/RBuildAnon.cs b/MewtocolNet/RegisterBuilding/RBuildAnon.cs index 40ab58f..2c0198c 100644 --- a/MewtocolNet/RegisterBuilding/RBuildAnon.cs +++ b/MewtocolNet/RegisterBuilding/RBuildAnon.cs @@ -1,4 +1,5 @@ -using MewtocolNet.Registers; +using System; +using MewtocolNet.Registers; using System.Threading.Tasks; namespace MewtocolNet.RegisterBuilding { @@ -33,16 +34,18 @@ namespace MewtocolNet.RegisterBuilding { /// True if success public async Task WriteToAsync(T value) { - try { + throw new NotImplementedException(); - var tempRegister = AssembleTemporaryRegister(); - return await tempRegister.WriteAsync(value); + //try { - } catch { + // var tempRegister = AssembleTemporaryRegister(); + // return await tempRegister.WriteAsync(value); - throw; + //} catch { - } + // throw; + + //} } @@ -52,16 +55,18 @@ namespace MewtocolNet.RegisterBuilding { /// The value read or null if failed public async Task ReadFromAsync() { - try { + throw new NotImplementedException(); - var tempRegister = AssembleTemporaryRegister(); - return (T)await tempRegister.ReadAsync(); + //try { - } catch { + // var tempRegister = AssembleTemporaryRegister(); + // return (T)await tempRegister.ReadAsync(); - throw; + //} catch { - } + // throw; + + //} } diff --git a/MewtocolNet/RegisterBuilding/RBuildBase.cs b/MewtocolNet/RegisterBuilding/RBuildBase.cs index 97d6ca2..f0d6641 100644 --- a/MewtocolNet/RegisterBuilding/RBuildBase.cs +++ b/MewtocolNet/RegisterBuilding/RBuildBase.cs @@ -16,7 +16,7 @@ namespace MewtocolNet.RegisterBuilding { internal RBuildBase(MewtocolInterface plc) => attachedPLC = plc; - internal List unfinishedList = new List(); + internal List unfinishedList = new List(); #region Parser stage @@ -50,7 +50,7 @@ namespace MewtocolNet.RegisterBuilding { internal string hardFailReason; - internal StepData stepData; + internal BaseStepData stepData; } @@ -277,9 +277,7 @@ namespace MewtocolNet.RegisterBuilding { res.stepData.originalParseStr = plcAddrName; res.stepData.buildSource = RegisterBuildSource.Manual; - unfinishedList.Add(res.stepData); - - return res.stepData; + return new StepData().Map(res.stepData); } else if (res.state == ParseResultState.FailedHard) { @@ -305,7 +303,7 @@ namespace MewtocolNet.RegisterBuilding { /// /// /// - public TypedRegister AsType() { + internal TypedRegister AsType() { if (!typeof(T).IsAllowedPlcCastingType()) { @@ -326,7 +324,7 @@ namespace MewtocolNet.RegisterBuilding { /// /// /// - public TypedRegister AsType(Type type) { + internal TypedRegister AsType(Type type) { //was ranged syntax array build if (Data.wasAddressStringRangeBased && type.IsArray && type.GetArrayRank() == 1) { @@ -395,7 +393,7 @@ namespace MewtocolNet.RegisterBuilding { /// /// Sets the register type as a predefined /// - public TypedRegister AsType(PlcVarType type) { + internal TypedRegister AsType(PlcVarType type) { Data.dotnetVarType = type.GetDefaultDotnetType(); @@ -420,7 +418,7 @@ namespace MewtocolNet.RegisterBuilding { /// DWORD32 bit double word interpreted as /// /// - public TypedRegister AsType(string type) { + internal TypedRegister AsType(string type) { var regexString = new Regex(@"^STRING *\[(?[0-9]*)\]$", RegexOptions.IgnoreCase); var regexArray = new Regex(@"^ARRAY *\[(?[0-9]*)..(?[0-9]*)(?:\,(?[0-9]*)..(?[0-9]*))?(?:\,(?[0-9]*)..(?[0-9]*))?\] *OF {1,}(?.*)$", RegexOptions.IgnoreCase); @@ -520,7 +518,7 @@ namespace MewtocolNet.RegisterBuilding { /// ARRAY [0..2, 0..3, 0..4] OF INT = AsTypeArray<short[,,]>(3,4,5)
/// ARRAY [5..6, 0..2] OF DWORD = AsTypeArray<DWord[,]>(2, 3)
/// - public TypedRegister AsTypeArray(params int[] indicies) { + internal TypedRegister AsTypeArray(params int[] indicies) { if (!typeof(T).IsArray) throw new NotSupportedException($"The type {typeof(T)} was no array"); diff --git a/MewtocolNet/RegisterBuilding/RBuildMult.cs b/MewtocolNet/RegisterBuilding/RBuildMult.cs index 84613ba..311c1d9 100644 --- a/MewtocolNet/RegisterBuilding/RBuildMult.cs +++ b/MewtocolNet/RegisterBuilding/RBuildMult.cs @@ -15,6 +15,8 @@ namespace MewtocolNet.RegisterBuilding { #region String parse stage + //at runtime constructor + /// /// Starts the register builder for a new mewtocol address
/// Examples: @@ -26,6 +28,8 @@ namespace MewtocolNet.RegisterBuilding { var data = ParseAddress(plcAddrName, name); + unfinishedList.Add(data); + return new SAddress { Data = data, builder = this, @@ -33,6 +37,68 @@ namespace MewtocolNet.RegisterBuilding { } + //struct constructor + + public SAddressStruct Address(string plcAddrName, string name = null) where T : struct { + + var data = ParseAddress(plcAddrName, name); + + data.dotnetVarType = typeof(T); + + unfinishedList.Add(data); + + return new SAddressStruct(data) { + builder = this, + }; + + } + + public SAddressString AddressString(string plcAddrName, int sizeHint, string name = null) where T : class { + + var data = ParseAddress(plcAddrName, name); + + data.dotnetVarType = typeof(T); + + unfinishedList.Add(data); + + if (typeof(T).IsArray) { + + return new SAddressString(data, true) { + Data = data, + builder = this, + }; + + } + + return new SAddressString(data) { + builder = this, + }; + + } + + public SAddressArray AddressArray(string plcAddrName, string name = null) where T : class { + + var data = ParseAddress(plcAddrName, name); + + data.dotnetVarType = typeof(T); + + unfinishedList.Add(data); + + if (typeof(T).IsArray) { + + return new SAddressArray(data, true) { + Data = data, + builder = this, + }; + + } + + return new SAddressArray(data) { + builder = this, + }; + + } + //internal use only, adds a type definition (for use when building from attibute) internal SAddress AddressFromAttribute(string plcAddrName, string typeDef, RegisterCollection regCol, PropertyInfo prop, uint? bytesizeHint = null) { @@ -52,6 +118,7 @@ namespace MewtocolNet.RegisterBuilding { #region Typing stage + //non generic public new class SAddress : RBuildBase.SAddress { public new TypedRegister AsType() => new TypedRegister().Map(base.AsType()); @@ -64,9 +131,95 @@ namespace MewtocolNet.RegisterBuilding { public new TypedRegister AsTypeArray(params int[] indicies) => new TypedRegister().Map(base.AsTypeArray(indicies)); + } + + //structs + public class SAddressStruct : RBuildBase.SAddress where T : struct { + + internal SAddressStruct(StepData data) { + + this.Data = data; + + this.Map(AsType(typeof(T))); + + } + + internal SAddressStruct(StepData data, bool arrayed) { + + this.Data = data; + + } + + /// + /// Outputs the generated + /// + public void Out(Action> registerOut) { + + Data.registerOut = new Action(o => registerOut((IRegister)o)); + + } } + //strings + public class SAddressString : RBuildBase.SAddress where T : class { + + internal SAddressString(StepData data) { + + this.Data = data; + + this.Map(AsType(typeof(T))); + + } + + internal SAddressString(StepData data, bool arrayed) { + + this.Data = data; + + } + + /// + /// Outputs the generated + /// + public void Out(Action> registerOut) { + + Data.registerOut = new Action(o => registerOut((IStringRegister)o)); + + } + + } + + //arrays + public class SAddressArray : RBuildBase.SAddress { + + internal SAddressArray(StepData data) { + + this.Data = data; + + this.Map(AsType(typeof(T))); + + } + + internal SAddressArray(StepData data, bool arrayed) { + + this.Data = data; + + } + + public TypedRegister Indicies(params int[] indicies) => new TypedRegister().Map(base.AsTypeArray(indicies)); + + /// + /// Outputs the generated + /// + public void Out(Action> registerOut) { + + Data.registerOut = new Action(o => registerOut((IArrayRegister)o)); + + } + + } + + #endregion #region Typing size hint @@ -83,7 +236,7 @@ namespace MewtocolNet.RegisterBuilding { /// public void Out(Action registerOut) { - Data.registerOut = registerOut; + Data.registerOut = (Action)registerOut; } @@ -103,7 +256,7 @@ namespace MewtocolNet.RegisterBuilding { /// public void Out(Action registerOut) { - Data.registerOut = registerOut; + Data.registerOut = (Action)registerOut; } @@ -118,7 +271,7 @@ namespace MewtocolNet.RegisterBuilding { /// public void Out(Action registerOut) { - Data.registerOut = registerOut; + Data.registerOut = (Action)registerOut; } diff --git a/MewtocolNet/RegisterBuilding/RegisterAssembler.cs b/MewtocolNet/RegisterBuilding/RegisterAssembler.cs index 0f101fb..c70dbe3 100644 --- a/MewtocolNet/RegisterBuilding/RegisterAssembler.cs +++ b/MewtocolNet/RegisterBuilding/RegisterAssembler.cs @@ -28,7 +28,8 @@ namespace MewtocolNet.RegisterBuilding { var generatedInstance = Assemble(data); generatedInstance.autoGenerated = flagAutoGenerated; - data.registerOut?.Invoke(generatedInstance); + + data?.InvokeBuilt(generatedInstance); generatedInstances.Add(generatedInstance); @@ -38,7 +39,7 @@ namespace MewtocolNet.RegisterBuilding { } - internal Register Assemble(StepData data) { + internal Register Assemble(BaseStepData data) { Register generatedInstance = null; @@ -114,7 +115,7 @@ namespace MewtocolNet.RegisterBuilding { var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; - Type paramedClass = typeof(SingleRegister<>).MakeGenericType(data.dotnetVarType); + Type paramedClass = typeof(StructRegister<>).MakeGenericType(data.dotnetVarType); ConstructorInfo constr = paramedClass.GetConstructor(flags, null, new Type[] { typeof(uint), typeof(uint) ,typeof(string) }, null); diff --git a/MewtocolNet/RegisterBuilding/StepData.cs b/MewtocolNet/RegisterBuilding/StepData.cs index 2b44007..35ee44b 100644 --- a/MewtocolNet/RegisterBuilding/StepData.cs +++ b/MewtocolNet/RegisterBuilding/StepData.cs @@ -6,10 +6,9 @@ using System.Reflection; namespace MewtocolNet.RegisterBuilding { - internal class StepData { + internal class BaseStepData { - //for referencing the output at builder level - internal Action registerOut; + internal Action registerOut; internal RegisterBuildSource buildSource = RegisterBuildSource.Anonymous; @@ -34,6 +33,40 @@ namespace MewtocolNet.RegisterBuilding { internal string typeDef; + internal void InvokeBuilt(Register reg) { + + registerOut.Invoke(reg); + + //var selftype = this.GetType(); + + //if ((selftype.IsGenericType && selftype.GetGenericTypeDefinition() == typeof(StepData<>))) { + + // var field = selftype.GetField("registerOut", BindingFlags.NonPublic | BindingFlags.Instance); + + // var generic = typeof(IRegister<>).MakeGenericType() + + // var action = Action.CreateDelegate(typeof(IRegister)); + + // field.SetValue(this,); + + //} + + } + + } + + internal class StepData : BaseStepData { + + //for referencing the output at builder level + //internal Action> registerOut; + + } + + internal class StepData : BaseStepData { + + //for referencing the output at builder level + //internal Action registerOut; + } } diff --git a/MewtocolNet/Registers/ArrayRegister.cs b/MewtocolNet/Registers/ArrayRegister.cs index 085f0ec..7413841 100644 --- a/MewtocolNet/Registers/ArrayRegister.cs +++ b/MewtocolNet/Registers/ArrayRegister.cs @@ -10,7 +10,7 @@ namespace MewtocolNet.Registers { /// /// Defines a register containing a string /// - public class ArrayRegister : Register { + public class ArrayRegister : Register, IArrayRegister { internal int[] indices; @@ -21,6 +21,8 @@ namespace MewtocolNet.Registers { /// public uint AddressLength => addressLength; + public T[] Value => (T[])ValueObj; + [Obsolete("Creating registers directly is not supported use IPlc.Register instead")] public ArrayRegister() => throw new NotSupportedException("Direct register instancing is not supported, use the builder pattern"); @@ -47,16 +49,16 @@ namespace MewtocolNet.Registers { public override string GetValueString() { - if (Value == null) return "null"; + if (ValueObj == null) return "null"; if(typeof(T) == typeof(byte[])) { - return ((byte[])Value).ToHexString("-"); + return ((byte[])ValueObj).ToHexString("-"); } StringBuilder sb = new StringBuilder(); - var valueIenum = (IEnumerable)Value; + var valueIenum = (IEnumerable)ValueObj; foreach (var el in valueIenum) { @@ -64,7 +66,7 @@ namespace MewtocolNet.Registers { } - return ArrayToString((Array)Value); + return ArrayToString((Array)ValueObj); } @@ -75,9 +77,9 @@ namespace MewtocolNet.Registers { public override uint GetRegisterAddressLen() => AddressLength; /// - public override async Task WriteAsync(object value) { + public async Task WriteAsync(T value) { - var encoded = PlcValueParser.Encode(this, (T)value); + var encoded = PlcValueParser.EncodeArray(this, value); var res = await attachedInterface.WriteByteRange((int)MemoryAddress, encoded); if (res) { @@ -99,18 +101,18 @@ namespace MewtocolNet.Registers { } /// - public override async Task ReadAsync() { + async Task IArrayRegister.ReadAsync() { var res = await attachedInterface.ReadByteRangeNonBlocking((int)MemoryAddress, (int)GetRegisterAddressLen() * 2); - if (res == null) return null; + if (res == null) throw new Exception(); - //var matchingReg = attachedInterface.memoryManager.GetAllRegisters() - //.FirstOrDefault(x => x.IsSameAddressAndType(this)); + var matchingReg = attachedInterface.memoryManager.GetAllRegisters() + .FirstOrDefault(x => x.IsSameAddressAndType(this)); - //if (matchingReg != null) - // matchingReg.underlyingMemory.SetUnderlyingBytes(matchingReg, res); + if (matchingReg != null) + matchingReg.underlyingMemory.SetUnderlyingBytes(matchingReg, res); - return SetValueFromBytes(res); + return (T[])SetValueFromBytes(res); } diff --git a/MewtocolNet/Registers/Base/IArrayRegister.cs b/MewtocolNet/Registers/Base/IArrayRegister.cs new file mode 100644 index 0000000..7cdb364 --- /dev/null +++ b/MewtocolNet/Registers/Base/IArrayRegister.cs @@ -0,0 +1,26 @@ +using System.Threading.Tasks; + +namespace MewtocolNet.Registers { + + public interface IArrayRegister : IRegister { + + /// + /// The current value of the register + /// + T[] Value { get; } + + /// + /// Reads the register value async from the plc + /// + /// The register value + Task ReadAsync(); + + /// + /// Writes the register content async to the plc + /// + /// True if successfully set + Task WriteAsync(T data); + + } + +} diff --git a/MewtocolNet/Registers/Base/IRegister.cs b/MewtocolNet/Registers/Base/IRegister.cs index fbadb7c..ebecb81 100644 --- a/MewtocolNet/Registers/Base/IRegister.cs +++ b/MewtocolNet/Registers/Base/IRegister.cs @@ -32,7 +32,7 @@ namespace MewtocolNet.Registers { /// /// The current value of the register /// - object Value { get; } + object ValueObj { get; } /// /// The plc memory address of the register @@ -55,18 +55,6 @@ namespace MewtocolNet.Registers { /// string ToString(bool detailed); - /// - /// Reads the register value async from the plc - /// - /// The register value - Task ReadAsync(); - - /// - /// Writes the register content async to the plc - /// - /// True if successfully set - Task WriteAsync(object data); - } } diff --git a/MewtocolNet/Registers/Base/IRegisterT.cs b/MewtocolNet/Registers/Base/IRegisterT.cs new file mode 100644 index 0000000..6ffa350 --- /dev/null +++ b/MewtocolNet/Registers/Base/IRegisterT.cs @@ -0,0 +1,31 @@ +using System; +using System.Threading.Tasks; +using MewtocolNet.Events; + +namespace MewtocolNet.Registers { + + /// + /// An interface for all register types + /// + public interface IRegister : IRegister where T : struct { + + /// + /// The current value of the register + /// + T? Value { get; } + + /// + /// Reads the register value async from the plc + /// + /// The register value + Task ReadAsync(); + + /// + /// Writes the register content async to the plc + /// + /// True if successfully set + Task WriteAsync(T data); + + } + +} diff --git a/MewtocolNet/Registers/Base/IStringRegister.cs b/MewtocolNet/Registers/Base/IStringRegister.cs new file mode 100644 index 0000000..48da7a9 --- /dev/null +++ b/MewtocolNet/Registers/Base/IStringRegister.cs @@ -0,0 +1,25 @@ +using System.Threading.Tasks; + +namespace MewtocolNet.Registers { + public interface IStringRegister : IRegister where T : class { + + /// + /// The current value of the register + /// + T Value { get; } + + /// + /// Reads the register value async from the plc + /// + /// The register value + Task ReadAsync(); + + /// + /// Writes the register content async to the plc + /// + /// True if successfully set + Task WriteAsync(T data); + + } + +} diff --git a/MewtocolNet/Registers/Base/Register.cs b/MewtocolNet/Registers/Base/Register.cs index 8d57d68..6ca98de 100644 --- a/MewtocolNet/Registers/Base/Register.cs +++ b/MewtocolNet/Registers/Base/Register.cs @@ -44,7 +44,7 @@ namespace MewtocolNet.Registers { public MewtocolInterface AttachedInterface => attachedInterface; /// - public object Value => lastValue; + public object ValueObj => lastValue; /// public RegisterType RegisterType { get; internal set; } @@ -62,7 +62,7 @@ namespace MewtocolNet.Registers { public event PropertyChangedEventHandler PropertyChanged; - public void TriggerNotifyChange() => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Value))); + public void TriggerNotifyChange() => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ValueObj))); #endregion @@ -97,23 +97,13 @@ namespace MewtocolNet.Registers { } - #region Read / Write - - public virtual Task ReadAsync() => throw new NotImplementedException(); - - public virtual Task WriteAsync(object data) => throw new NotImplementedException(); - - internal virtual Task UpdateDynamicSize () => throw new NotImplementedException(); - - #endregion - #region Default accessors public virtual byte? GetSpecialAddress() => null; - public virtual string GetValueString() => Value?.ToString() ?? "null"; + public virtual string GetValueString() => ValueObj?.ToString() ?? "null"; - public virtual string GetAsPLC() => Value?.ToString() ?? "null"; + public virtual string GetAsPLC() => ValueObj?.ToString() ?? "null"; public virtual string GetRegisterString() => RegisterType == RegisterType.DT_BYTE_RANGE ? "DT" : RegisterType.ToString(); @@ -202,7 +192,7 @@ namespace MewtocolNet.Registers { sb.Append(GetMewName()); if (Name != null) sb.Append($" ({Name})"); sb.Append($" [{this.GetType().Name}({underlyingSystemType.Name})]"); - if (Value != null) sb.Append($" Val: {GetValueString()}"); + if (ValueObj != null) sb.Append($" Val: {GetValueString()}"); return sb.ToString(); @@ -235,7 +225,7 @@ namespace MewtocolNet.Registers { sb.Append($"Name: {Name ?? "Not named"}\n"); - if (Value != null) + if (ValueObj != null) sb.Append($"Value: {GetValueString()}\n"); sb.Append($"Reads: {successfulReads}, Writes: {successfulWrites}\n"); diff --git a/MewtocolNet/Registers/SingleRegister.cs b/MewtocolNet/Registers/StructRegister.cs similarity index 75% rename from MewtocolNet/Registers/SingleRegister.cs rename to MewtocolNet/Registers/StructRegister.cs index cca4ca4..a4296f9 100644 --- a/MewtocolNet/Registers/SingleRegister.cs +++ b/MewtocolNet/Registers/StructRegister.cs @@ -14,7 +14,7 @@ namespace MewtocolNet.Registers { /// Defines a register containing a number /// /// The type of the numeric value - public class SingleRegister : Register { + public class StructRegister : Register, IRegister where T : struct { internal uint byteLength; @@ -25,11 +25,13 @@ namespace MewtocolNet.Registers { /// public uint AddressLength => addressLength; + public T? Value => (T?)ValueObj; + [Obsolete("Creating registers directly is not supported use IPlc.Register instead")] - public SingleRegister() => + public StructRegister() => throw new NotSupportedException("Direct register instancing is not supported, use the builder pattern"); - internal SingleRegister(uint _address, uint _reservedByteSize, string _name = null) { + internal StructRegister(uint _address, uint _reservedByteSize, string _name = null) { memoryAddress = _address; name = _name; @@ -56,33 +58,33 @@ namespace MewtocolNet.Registers { /// public override string GetAsPLC() { - if (typeof(T) == typeof(TimeSpan)) return ((TimeSpan)Value).ToPlcTime(); + if (typeof(T) == typeof(TimeSpan)) return ((TimeSpan)(object)ValueObj).ToPlcTime(); - return Value.ToString(); + return ValueObj.ToString(); } /// public override string GetValueString() { - if (Value != null && typeof(T) == typeof(TimeSpan)) return $"{Value} [{((TimeSpan)Value).ToPlcTime()}]"; + if (ValueObj != null && typeof(T) == typeof(TimeSpan)) return $"{ValueObj} [{((TimeSpan)(object)ValueObj).ToPlcTime()}]"; - if (Value != null && typeof(T) == typeof(Word)) return $"{Value} [{((Word)Value).ToStringBitsPlc()}]"; + if (ValueObj != null && typeof(T) == typeof(Word)) return $"{ValueObj} [{((Word)(object)ValueObj).ToStringBitsPlc()}]"; - if (Value != null && typeof(T) == typeof(DWord)) return $"{Value} [{((DWord)Value).ToStringBitsPlc()}]"; + if (ValueObj != null && typeof(T) == typeof(DWord)) return $"{ValueObj} [{((DWord)(object)ValueObj).ToStringBitsPlc()}]"; var hasFlags = typeof(T).GetCustomAttribute() != null; - if (Value != null && typeof(T).IsEnum && !hasFlags) { + if (ValueObj != null && typeof(T).IsEnum && !hasFlags) { var underlying = Enum.GetUnderlyingType(typeof(T)); - object val = Convert.ChangeType(Value, underlying); - return $"{Value} [{val}]"; + object val = Convert.ChangeType(ValueObj, underlying); + return $"{ValueObj} [{val}]"; } - if (Value != null && typeof(T).IsEnum && hasFlags) return $"{Value}"; + if (ValueObj != null && typeof(T).IsEnum && hasFlags) return $"{ValueObj}"; - return Value?.ToString() ?? "null"; + return ValueObj?.ToString() ?? "null"; } @@ -90,7 +92,7 @@ namespace MewtocolNet.Registers { public override uint GetRegisterAddressLen() => AddressLength; /// - public override async Task WriteAsync(object value) { + public async Task WriteAsync(T value) { var encoded = PlcValueParser.Encode(this, (T)value); var res = await attachedInterface.WriteByteRange((int)MemoryAddress, encoded); @@ -114,25 +116,27 @@ namespace MewtocolNet.Registers { } /// - public override async Task ReadAsync() { + public async Task ReadAsync() { var res = await attachedInterface.ReadByteRangeNonBlocking((int)MemoryAddress, (int)GetRegisterAddressLen() * 2); - if (res == null) return null; + if (res == null) return (T?)(object)null; var matchingReg = attachedInterface.memoryManager.GetAllRegisters() .FirstOrDefault(x => x.IsSameAddressAndType(this)); if (matchingReg != null) { - if (matchingReg is SingleRegister sreg && this is SingleRegister selfSreg) { - sreg.addressLength = selfSreg.addressLength; - } + //if (matchingReg is StructRegister sreg && this.GetType() == typeof(StructRegister)) { + + // sreg.addressLength = selfSreg.addressLength; + + //} matchingReg.underlyingMemory.SetUnderlyingBytes(matchingReg, res); } - return SetValueFromBytes(res); + return (T)SetValueFromBytes(res); } diff --git a/MewtocolNet/TypeConversion/Conversions.cs b/MewtocolNet/TypeConversion/Conversions.cs index 788ef55..1788939 100644 --- a/MewtocolNet/TypeConversion/Conversions.cs +++ b/MewtocolNet/TypeConversion/Conversions.cs @@ -54,7 +54,7 @@ namespace MewtocolNet.TypeConversion { //default short DT conversion new PlcTypeConversion(RegisterType.DT) { - HoldingRegisterType = typeof(SingleRegister), + HoldingRegisterType = typeof(StructRegister), PlcVarType = PlcVarType.INT, FromRaw = (reg, bytes) => BitConverter.ToInt16(bytes, 0), ToRaw = (reg, value) => BitConverter.GetBytes(value), @@ -62,7 +62,7 @@ namespace MewtocolNet.TypeConversion { //default ushort DT conversion new PlcTypeConversion(RegisterType.DT) { - HoldingRegisterType = typeof(SingleRegister), + HoldingRegisterType = typeof(StructRegister), PlcVarType = PlcVarType.UINT, FromRaw = (reg, bytes) => BitConverter.ToUInt16(bytes, 0), ToRaw = (reg, value) => BitConverter.GetBytes(value), @@ -70,7 +70,7 @@ namespace MewtocolNet.TypeConversion { //default Word DT conversion new PlcTypeConversion(RegisterType.DT) { - HoldingRegisterType = typeof(SingleRegister), + HoldingRegisterType = typeof(StructRegister), PlcVarType = PlcVarType.WORD, FromRaw = (reg, bytes) => new Word(bytes), ToRaw = (reg, value) => value.ToByteArray(), @@ -78,7 +78,7 @@ namespace MewtocolNet.TypeConversion { //default int DDT conversion new PlcTypeConversion(RegisterType.DDT) { - HoldingRegisterType = typeof(SingleRegister), + HoldingRegisterType = typeof(StructRegister), PlcVarType = PlcVarType.DINT, FromRaw = (reg, bytes) => BitConverter.ToInt32(bytes, 0), ToRaw = (reg, value) => BitConverter.GetBytes(value), @@ -86,7 +86,7 @@ namespace MewtocolNet.TypeConversion { //default uint DDT conversion new PlcTypeConversion(RegisterType.DDT) { - HoldingRegisterType = typeof(SingleRegister), + HoldingRegisterType = typeof(StructRegister), PlcVarType = PlcVarType.UDINT, FromRaw = (reg, bytes) => BitConverter.ToUInt32(bytes, 0), ToRaw = (reg, value) => BitConverter.GetBytes(value), @@ -94,7 +94,7 @@ namespace MewtocolNet.TypeConversion { //default DWord DDT conversion new PlcTypeConversion(RegisterType.DDT) { - HoldingRegisterType = typeof(SingleRegister), + HoldingRegisterType = typeof(StructRegister), PlcVarType = PlcVarType.DWORD, FromRaw = (reg, bytes) => new DWord(bytes), ToRaw = (reg, value) => value.ToByteArray(), @@ -102,7 +102,7 @@ namespace MewtocolNet.TypeConversion { //default float DDT conversion new PlcTypeConversion(RegisterType.DDT) { - HoldingRegisterType = typeof(SingleRegister), + HoldingRegisterType = typeof(StructRegister), PlcVarType = PlcVarType.REAL, FromRaw = (reg, bytes) => BitConverter.ToSingle(bytes, 0), ToRaw = (reg, value) => BitConverter.GetBytes(value), @@ -110,7 +110,7 @@ namespace MewtocolNet.TypeConversion { //default TimeSpan DDT conversion new PlcTypeConversion(RegisterType.DDT) { - HoldingRegisterType = typeof(SingleRegister), + HoldingRegisterType = typeof(StructRegister), PlcVarType = PlcVarType.TIME, FromRaw = (reg, bytes) => { @@ -130,48 +130,48 @@ namespace MewtocolNet.TypeConversion { //default string DT Range conversion Example bytes: (04 00 03 00 XX XX XX) //first 4 bytes are reserved size (2 bytes) and used size (2 bytes) //the remaining bytes are the ascii bytes for the string - new PlcTypeConversion(RegisterType.DT_BYTE_RANGE) { - HoldingRegisterType = typeof(SingleRegister), - PlcVarType = PlcVarType.STRING, - FromRaw = (reg, bytes) => { + //new PlcTypeConversion(RegisterType.DT_BYTE_RANGE) { + // HoldingRegisterType = typeof(StructRegister), + // PlcVarType = PlcVarType.STRING, + // FromRaw = (reg, bytes) => { - if(bytes.Length == 4) return string.Empty; + // if(bytes.Length == 4) return string.Empty; - if(bytes == null || bytes.Length < 4) { + // if(bytes == null || bytes.Length < 4) { - throw new Exception("Failed to convert string bytes, response not long enough"); + // throw new Exception("Failed to convert string bytes, response not long enough"); - } + // } - //get actual showed size - short actualLen = BitConverter.ToInt16(bytes, 2); + // //get actual showed size + // short actualLen = BitConverter.ToInt16(bytes, 2); - //skip 4 bytes because they only describe the length - string gotVal = Encoding.UTF8.GetString(bytes.Skip(4).Take(actualLen).ToArray()); + // //skip 4 bytes because they only describe the length + // string gotVal = Encoding.UTF8.GetString(bytes.Skip(4).Take(actualLen).ToArray()); - return gotVal; + // return gotVal; - }, - ToRaw = (reg, value) => { + // }, + // ToRaw = (reg, value) => { - int padLen = value.Length; - if(value.Length % 2 != 0) padLen++; + // int padLen = value.Length; + // if(value.Length % 2 != 0) padLen++; - var strBytes = Encoding.UTF8.GetBytes(value.PadRight(padLen, '\0')); + // var strBytes = Encoding.UTF8.GetBytes(value.PadRight(padLen, '\0')); - List finalBytes = new List(); + // List finalBytes = new List(); - short reserved = (short)(reg.GetRegisterAddressLen() * 2 - 4); - short used = (short)value.Length; + // short reserved = (short)(reg.GetRegisterAddressLen() * 2 - 4); + // short used = (short)value.Length; - finalBytes.AddRange(BitConverter.GetBytes(reserved)); - finalBytes.AddRange(BitConverter.GetBytes(used)); - finalBytes.AddRange(strBytes); + // finalBytes.AddRange(BitConverter.GetBytes(reserved)); + // finalBytes.AddRange(BitConverter.GetBytes(used)); + // finalBytes.AddRange(strBytes); - return finalBytes.ToArray(); + // return finalBytes.ToArray(); - }, - }, + // }, + //}, }; diff --git a/MewtocolNet/TypeConversion/PlcValueParser.cs b/MewtocolNet/TypeConversion/PlcValueParser.cs index f731f91..f2dad34 100644 --- a/MewtocolNet/TypeConversion/PlcValueParser.cs +++ b/MewtocolNet/TypeConversion/PlcValueParser.cs @@ -1,6 +1,7 @@ using MewtocolNet.Registers; using MewtocolNet.TypeConversion; using System; +using System.Collections; using System.Collections.Generic; using System.Linq; @@ -29,7 +30,7 @@ namespace MewtocolNet { converter = conversions.FirstOrDefault(x => x.GetDotnetType() == underlyingType); if (converter == null) - throw new Exception($"A converter for the dotnet type {underlyingType} doesn't exist"); + throw new Exception($"A converter for the type {underlyingType} doesn't exist"); return (T)converter.FromRawData(register, bytes); @@ -37,6 +38,9 @@ namespace MewtocolNet { internal static T ParseArray (Register register, int[] indices, byte[] bytes) { + //if byte array directly return the bytes + if (typeof(T) == typeof(byte[])) return (T)(object)bytes; + IPlcTypeConverter converter; Type underlyingElementType; @@ -54,19 +58,18 @@ namespace MewtocolNet { converter = conversions.FirstOrDefault(x => x.GetDotnetType() == underlyingElementType); if (converter == null) - throw new Exception($"A converter for the dotnet type {underlyingElementType} doesn't exist"); + throw new Exception($"A converter for the type {underlyingElementType} doesn't exist"); //parse the array from one to n dimensions var outArray = Array.CreateInstance(underlyingElementType, indices); - if(outArray.GetType() == typeof(byte[])) { - - Console.WriteLine(); - + int sizePerItem = 0; + if(underlyingElementType == typeof(string)) { + throw new NotImplementedException(); + } else { + sizePerItem = underlyingElementType.DetermineTypeByteIntialSize(); } - int sizePerItem = underlyingElementType.DetermineTypeByteIntialSize(); - var iterateItems = indices.Aggregate((a, x) => a * x); var indexer = new int[indices.Length]; for (int i = 0; i < iterateItems; i++) { @@ -92,44 +95,6 @@ namespace MewtocolNet { } - static void ConvertFlatArrayToDim ( - IPlcTypeConverter converter, - Register register, - byte[] source, - Array target, - int sizePerVal, - int[] dims, - int currentIndex, - int currentArrayIndex - ) { - - if (currentIndex == dims.Length - 1) { - - for (int i = 0; i < dims[currentIndex]; i++) { - - byte[] rawDataItem = source.Skip(currentArrayIndex).Take(sizePerVal).ToArray(); - var value = converter.FromRawData(register, rawDataItem); - - target.SetValue(value, i); - currentArrayIndex += sizePerVal; - - } - - } else { - - for (int i = 0; i < dims[currentIndex]; i++) { - - Array innerArray = (Array)target.GetValue(i); - ConvertFlatArrayToDim(converter, register, source, innerArray, sizePerVal, dims, currentIndex + 1, currentArrayIndex); - currentArrayIndex += innerArray.Length * sizePerVal; - - } - - } - - } - - internal static byte[] Encode(Register register, T value) { IPlcTypeConverter converter; @@ -155,10 +120,53 @@ namespace MewtocolNet { } - //internal static byte[] EncodeArray (IRegister register, T value) { + internal static byte[] EncodeArray(Register register, T value) { + //if byte array directly return the bytes + if (typeof(T) == typeof(byte[])) return (byte[])(object)value; - //} + IPlcTypeConverter converter; + Type underlyingElementType; + + //special case for enums + if (typeof(T).IsEnum) { + + underlyingElementType = typeof(T).GetElementType().GetEnumUnderlyingType(); + + } else { + + underlyingElementType = typeof(T).GetElementType(); + + } + + converter = conversions.FirstOrDefault(x => x.GetDotnetType() == underlyingElementType); + + if (converter == null) + throw new Exception($"A converter for the type {underlyingElementType} doesn't exist"); + + int sizePerItem = 0; + if (underlyingElementType == typeof(string)) { + throw new NotImplementedException(); + } else { + sizePerItem = underlyingElementType.DetermineTypeByteIntialSize(); + } + + byte[] encodedData = new byte[((ICollection)value).Count * sizePerItem]; + + int i = 0; + foreach (object item in (IEnumerable)value) { + + var encoded = converter.ToRawData(register, item); + + encoded.CopyTo(encodedData, i); + + i += sizePerItem; + + } + + return encodedData; + + } public static List GetAllowDotnetTypes() => conversions.Select(x => x.GetDotnetType()).ToList();