From 7be52efb7efd546cab3e463344f08b18feed3c9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Wei=C3=9F?= <72068105+Sandoun@users.noreply.github.com> Date: Mon, 26 Jun 2023 19:13:04 +0200 Subject: [PATCH] Further implementation - added new parsing method --- Examples/ExampleScenarios.cs | 50 ++- Examples/Program.cs | 3 - Examples/TestRegisters.cs | 4 +- Examples/TestRegistersEnumBitwise.cs | 32 +- MewtocolNet/DynamicInterface.cs | 307 ++++++++++++------ MewtocolNet/Exceptions/MewtocolException.cs | 34 ++ MewtocolNet/IRegister.cs | 1 + MewtocolNet/IRegisterInternal.cs | 18 + MewtocolNet/MewtocolHelpers.cs | 28 +- MewtocolNet/MewtocolInterface.cs | 294 +---------------- MewtocolNet/MewtocolInterfaceRequests.cs | 298 +++++------------ MewtocolNet/PLCEnums/PlcVarType.cs | 4 +- MewtocolNet/PLCEnums/PlcVarTypeConversions.cs | 91 ------ .../RegisterAttributes/RegisterAttribute.cs | 75 ++--- MewtocolNet/RegisterBuildInfo.cs | 118 +++++++ .../RegisterBuilding/FinalizerExtensions.cs | 61 +--- MewtocolNet/RegisterBuilding/RegBuilder.cs | 21 +- .../RegisterBuilding/RegisterBuilderStep.cs | 27 +- MewtocolNet/RegisterEnums.cs | 8 +- .../{BRegister.cs => BoolRegister.cs} | 41 ++- ...egisterResult.cs => BoolRegisterResult.cs} | 4 +- .../{SRegister.cs => BytesRegister.cs} | 36 +- ...gisterResult.cs => BytesRegisterResult.cs} | 4 +- .../{NRegister.cs => NumberRegister.cs} | 33 +- ...isterResult.cs => NumberRegisterResult.cs} | 4 +- MewtocolNet/TypeConversion/Conversions.cs | 124 +++++++ .../TypeConversion/IPlcTypeConverter.cs | 21 ++ .../TypeConversion/PlcTypeConversion.cs | 42 +++ MewtocolNet/TypeConversion/PlcValueParser.cs | 55 ++++ .../TypeConversion/PlcVarTypeConversions.cs | 65 ++++ MewtocolTests/AutomatedPropertyRegisters.cs | 22 +- MewtocolTests/TestRegisterBuilder.cs | 198 +++++------ MewtocolTests/TestRegisterInterface.cs | 48 +-- PLC_Test/test_c30_fpx_h.ini | Bin 832 -> 832 bytes PLC_Test/test_c30_fpx_h.pro | Bin 262144 -> 262144 bytes PLC_Test/test_c30_fpx_h.xml | 14 +- 36 files changed, 1181 insertions(+), 1004 deletions(-) create mode 100644 MewtocolNet/Exceptions/MewtocolException.cs create mode 100644 MewtocolNet/IRegisterInternal.cs delete mode 100644 MewtocolNet/PLCEnums/PlcVarTypeConversions.cs create mode 100644 MewtocolNet/RegisterBuildInfo.cs rename MewtocolNet/Registers/{BRegister.cs => BoolRegister.cs} (81%) rename MewtocolNet/Registers/{BRegisterResult.cs => BoolRegisterResult.cs} (78%) rename MewtocolNet/Registers/{SRegister.cs => BytesRegister.cs} (82%) rename MewtocolNet/Registers/{SRegisterResult.cs => BytesRegisterResult.cs} (78%) rename MewtocolNet/Registers/{NRegister.cs => NumberRegister.cs} (90%) rename MewtocolNet/Registers/{NRegisterResult.cs => NumberRegisterResult.cs} (88%) create mode 100644 MewtocolNet/TypeConversion/Conversions.cs create mode 100644 MewtocolNet/TypeConversion/IPlcTypeConverter.cs create mode 100644 MewtocolNet/TypeConversion/PlcTypeConversion.cs create mode 100644 MewtocolNet/TypeConversion/PlcValueParser.cs create mode 100644 MewtocolNet/TypeConversion/PlcVarTypeConversions.cs diff --git a/Examples/ExampleScenarios.cs b/Examples/ExampleScenarios.cs index bd2244a..df60f16 100644 --- a/Examples/ExampleScenarios.cs +++ b/Examples/ExampleScenarios.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using System.Collections; using MewtocolNet.RegisterBuilding; using System.Collections.Generic; +using MewtocolNet.Registers; namespace Examples; @@ -46,7 +47,7 @@ public class ExampleScenarios { while (interf.IsConnected) { //flip the bool register each tick and wait for it to be registered - await interf.SetRegisterAsync(nameof(registers.TestBool1), !registers.TestBool1); + //await interf.SetRegisterAsync(nameof(registers.TestBool1), !registers.TestBool1); Console.Title = $"Polling Paused: {interf.PollingPaused}, " + $"Poller active: {interf.PollerActive}, " + @@ -167,7 +168,7 @@ public class ExampleScenarios { await interf.ConnectAsync(); //use the async method to make sure the cycling is stopped - await interf.SetRegisterAsync(nameof(registers.StartCyclePLC), false); + //await interf.SetRegisterAsync(nameof(registers.StartCyclePLC), false); await Task.Delay(5000); @@ -182,4 +183,49 @@ public class ExampleScenarios { } + [Scenario("Read register test")] + public async Task RunReadTest () { + + Console.WriteLine("Starting auto enums and bitwise"); + + //setting up a new PLC interface and register collection + MewtocolInterface interf = new MewtocolInterface("192.168.115.210").WithPoller(); + + //auto add all built registers to the interface + var builder = RegBuilder.ForInterface(interf); + var r0reg = builder.FromPlcRegName("R0").Build(); + builder.FromPlcRegName("R1").Build(); + builder.FromPlcRegName("R1F").Build(); + builder.FromPlcRegName("R101A").Build(); + + var shortReg = builder.FromPlcRegName("DT35").AsPlcType(PlcVarType.INT).Build(); + builder.FromPlcRegName("DDT36").AsPlcType(PlcVarType.DINT).Build(); + + //builder.FromPlcRegName("DDT38").AsPlcType(PlcVarType.TIME).Build(); + //builder.FromPlcRegName("DT40").AsPlcType(PlcVarType.STRING).Build(); + + //connect + await interf.ConnectAsync(); + + //var res = await interf.SendCommandAsync("%01#RCSR000F"); + + while(true) { + + await interf.SetRegisterAsync(r0reg, !(bool)r0reg.Value); + await interf.SetRegisterAsync(shortReg, (short)new Random().Next(0, 100)); + + foreach (var reg in interf.Registers) { + + Console.WriteLine($"Register {reg.GetRegisterPLCName()} val: {reg.Value}"); + + } + + Console.WriteLine(); + + await Task.Delay(1000); + + } + + } + } diff --git a/Examples/Program.cs b/Examples/Program.cs index a868993..57c913f 100644 --- a/Examples/Program.cs +++ b/Examples/Program.cs @@ -14,9 +14,6 @@ class Program { static void Main(string[] args) { - RegBuilder.FromPlcRegName("DT303").AsPlcType(PlcVarType.INT).Build(); - var res = RegBuilder.FromPlcRegName("DT100").AsPlcType(PlcVarType.INT).Build(); - AppDomain.CurrentDomain.UnhandledException += (s,e) => { Console.WriteLine(e.ExceptionObject.ToString()); }; diff --git a/Examples/TestRegisters.cs b/Examples/TestRegisters.cs index afe76f7..4b13ca1 100644 --- a/Examples/TestRegisters.cs +++ b/Examples/TestRegisters.cs @@ -47,10 +47,10 @@ namespace Examples { public BitArray TestBitRegister { get; private set; } //corresponds to a DT1204 as a 16bit word/int takes the bit at index 9 and writes it back as a boolean - [Register(1204, 9, BitCount.B16)] + [Register(1204, BitCount.B16, 9)] public bool BitValue { get; private set; } - [Register(1204, 5, BitCount.B16)] + [Register(1204, BitCount.B16, 5)] public bool FillTest { get; private set; } //corresponds to a DT7012 - DT7013 as a 32bit time value that gets parsed as a timespan (TIME) diff --git a/Examples/TestRegistersEnumBitwise.cs b/Examples/TestRegistersEnumBitwise.cs index ea4954c..72f5ede 100644 --- a/Examples/TestRegistersEnumBitwise.cs +++ b/Examples/TestRegistersEnumBitwise.cs @@ -53,52 +53,52 @@ namespace Examples { //you can also extract single bits from DT503 - [Register(503, 0, BitCount.B16)] + [Register(503, BitCount.B16, 0)] public bool BitValue0 { get; private set; } - [Register(503, 1, BitCount.B16)] + [Register(503, BitCount.B16, 1)] public bool BitValue1 { get; private set; } - [Register(503, 2, BitCount.B16)] + [Register(503, BitCount.B16, 2)] public bool BitValue2 { get; private set; } - [Register(503, 3, BitCount.B16)] + [Register(503, BitCount.B16, 3)] public bool BitValue3 { get; private set; } - [Register(503, 4, BitCount.B16)] + [Register(503, BitCount.B16, 4)] public bool BitValue4 { get; private set; } - [Register(503, 5, BitCount.B16)] + [Register(503, BitCount.B16, 5)] public bool BitValue5 { get; private set; } - [Register(503, 6, BitCount.B16)] + [Register(503, BitCount.B16, 6)] public bool BitValue6 { get; private set; } - [Register(503, 7, BitCount.B16)] + [Register(503, BitCount.B16, 7)] public bool BitValue7 { get; private set; } - [Register(503, 8, BitCount.B16)] + [Register(503, BitCount.B16, 8)] public bool BitValue8 { get; private set; } - [Register(503, 9, BitCount.B16)] + [Register(503, BitCount.B16, 9)] public bool BitValue9 { get; private set; } - [Register(503, 10, BitCount.B16)] + [Register(503, BitCount.B16, 10)] public bool BitValue10 { get; private set; } - [Register(503, 11, BitCount.B16)] + [Register(503, BitCount.B16, 11)] public bool BitValue11 { get; private set; } - [Register(503, 12, BitCount.B16)] + [Register(503, BitCount.B16, 12)] public bool BitValue12 { get; private set; } - [Register(503, 13, BitCount.B16)] + [Register(503, BitCount.B16, 13)] public bool BitValue13 { get; private set; } - [Register(503, 14, BitCount.B16)] + [Register(503, BitCount.B16, 14)] public bool BitValue14 { get; private set; } - [Register(503, 15, BitCount.B16)] + [Register(503, BitCount.B16, 15)] public bool BitValue15 { get; private set; } } diff --git a/MewtocolNet/DynamicInterface.cs b/MewtocolNet/DynamicInterface.cs index f13ceff..1bd86c6 100644 --- a/MewtocolNet/DynamicInterface.cs +++ b/MewtocolNet/DynamicInterface.cs @@ -1,12 +1,16 @@ -using MewtocolNet.Logging; +using MewtocolNet.Exceptions; +using MewtocolNet.Logging; +using MewtocolNet.RegisterAttributes; using MewtocolNet.Registers; using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Threading.Tasks; -namespace MewtocolNet { +namespace MewtocolNet +{ /// /// The PLC com interface class @@ -118,61 +122,21 @@ namespace MewtocolNet { var reg = Registers[iteration]; - if (reg is NRegister shortReg) { - var lastVal = shortReg.Value; - var readout = (await ReadNumRegister(shortReg)).Register.Value; + if(reg.IsAllowedRegisterGenericType()) { + + var lastVal = reg.Value; + + var rwReg = (IRegisterInternal)reg; + + var readout = await rwReg.ReadAsync(this); + if (lastVal != readout) { - InvokeRegisterChanged(shortReg); - } - } - if (reg is NRegister ushortReg) { - var lastVal = ushortReg.Value; - var readout = (await ReadNumRegister(ushortReg)).Register.Value; - if (lastVal != readout) { - InvokeRegisterChanged(ushortReg); - } - } - if (reg is NRegister intReg) { - var lastVal = intReg.Value; - var readout = (await ReadNumRegister(intReg)).Register.Value; - if (lastVal != readout) { - InvokeRegisterChanged(intReg); - } - } - if (reg is NRegister uintReg) { - var lastVal = uintReg.Value; - var readout = (await ReadNumRegister(uintReg)).Register.Value; - if (lastVal != readout) { - InvokeRegisterChanged(uintReg); - } - } - if (reg is NRegister floatReg) { - var lastVal = floatReg.Value; - var readout = (await ReadNumRegister(floatReg)).Register.Value; - if (lastVal != readout) { - InvokeRegisterChanged(floatReg); - } - } - if (reg is NRegister tsReg) { - var lastVal = tsReg.Value; - var readout = (await ReadNumRegister(tsReg)).Register.Value; - if (lastVal != readout) { - InvokeRegisterChanged(tsReg); - } - } - if (reg is BRegister boolReg) { - var lastVal = boolReg.Value; - var readout = (await ReadBoolRegister(boolReg)).Register.Value; - if (lastVal != readout) { - InvokeRegisterChanged(boolReg); - } - } - if (reg is SRegister stringReg) { - var lastVal = stringReg.Value; - var readout = (await ReadStringRegister(stringReg)).Register.Value; - if (lastVal != readout) { - InvokeRegisterChanged(stringReg); + + rwReg.SetValueFromPLC(readout); + InvokeRegisterChanged(reg); + } + } iteration++; @@ -194,67 +158,218 @@ namespace MewtocolNet { internal void PropertyRegisterWasSet(string propName, object value) { - SetRegister(propName, value); + _ = SetRegisterAsync(GetRegister(propName), value); } #endregion + #region Register Colleciton adding + + #region Register Collection + + /// + /// Attaches a register collection object to + /// the interface that can be updated automatically. + /// + /// Just create a class inheriting from + /// and assert some propertys with the custom . + /// + /// A collection inherting the class + public MewtocolInterface WithRegisterCollection(RegisterCollectionBase collection) { + + collection.PLCInterface = this; + + var props = collection.GetType().GetProperties(); + + foreach (var prop in props) { + + var attributes = prop.GetCustomAttributes(true); + + string propName = prop.Name; + foreach (var attr in attributes) { + + if (attr is RegisterAttribute cAttribute && prop.PropertyType.IsAllowedPlcCastingType()) { + + var dotnetType = prop.PropertyType; + + AddRegister(new RegisterBuildInfo { + memoryAddress = cAttribute.MemoryArea, + specialAddress = cAttribute.SpecialAddress, + memorySizeBytes = cAttribute.ByteLength, + registerType = cAttribute.RegisterType, + dotnetCastType = dotnetType, + collectionType = collection.GetType(), + name = prop.Name, + }); + + } + + } + + } + + RegisterChanged += (reg) => { + + //register is used bitwise + if (reg.IsUsedBitwise()) { + + for (int i = 0; i < props.Length; i++) { + + var prop = props[i]; + var bitWiseFound = prop.GetCustomAttributes(true) + .FirstOrDefault(y => y.GetType() == typeof(RegisterAttribute) && ((RegisterAttribute)y).MemoryArea == reg.MemoryAddress); + + if (bitWiseFound != null) { + + var casted = (RegisterAttribute)bitWiseFound; + var bitIndex = casted.AssignedBitIndex; + + BitArray bitAr = null; + + if (reg is NumberRegister reg16) { + var bytes = BitConverter.GetBytes((short)reg16.Value); + bitAr = new BitArray(bytes); + } else if (reg is NumberRegister reg32) { + var bytes = BitConverter.GetBytes((int)reg32.Value); + bitAr = new BitArray(bytes); + } + + if (bitAr != null && bitIndex < bitAr.Length && bitIndex >= 0) { + + //set the specific bit index if needed + prop.SetValue(collection, bitAr[bitIndex]); + collection.TriggerPropertyChanged(prop.Name); + + } else if (bitAr != null) { + + //set the specific bit array if needed + prop.SetValue(collection, bitAr); + collection.TriggerPropertyChanged(prop.Name); + + } + + } + + } + + } + + //updating normal properties + var foundToUpdate = props.FirstOrDefault(x => x.Name == reg.Name); + + if (foundToUpdate != null) { + + var foundAttributes = foundToUpdate.GetCustomAttributes(true); + var foundAttr = foundAttributes.FirstOrDefault(x => x.GetType() == typeof(RegisterAttribute)); + + if (foundAttr == null) + return; + + var registerAttr = (RegisterAttribute)foundAttr; + + //check if bit parse mode + if (registerAttr.AssignedBitIndex == -1) { + + HashSet NumericTypes = new HashSet { + typeof(bool), + typeof(short), + typeof(ushort), + typeof(int), + typeof(uint), + typeof(float), + typeof(TimeSpan), + typeof(string) + }; + + var regValue = ((IRegister)reg).Value; + + if (NumericTypes.Any(x => foundToUpdate.PropertyType == x)) { + foundToUpdate.SetValue(collection, regValue); + } + + if (foundToUpdate.PropertyType.IsEnum) { + foundToUpdate.SetValue(collection, regValue); + } + + } + + collection.TriggerPropertyChanged(foundToUpdate.Name); + + } + + }; + + if (collection != null) + collection.OnInterfaceLinked(this); + + Connected += (i) => { + if (collection != null) + collection.OnInterfaceLinkedAndOnline(this); + }; + + return this; + + } + + #endregion + + #endregion + #region Register Adding - //Internal register adding for auto register collection building - internal void AddRegister(Type _colType, int _address, PropertyInfo boundProp, int _length = 1, bool _isBitwise = false, Type _enumType = null) { + internal void AddRegister (RegisterBuildInfo buildInfo) { - Type regType = typeof(T); + var builtRegister = buildInfo.Build(); - if (regType != typeof(string) && _length != 1) { - throw new NotSupportedException($"_lenght parameter only allowed for register of type string"); - } + //is bitwise and the register list already contains that area register + if(builtRegister.IsUsedBitwise() && CheckDuplicateRegister(builtRegister, out var existing)) { - if (Registers.Any(x => x.MemoryAddress == _address) && _isBitwise) { return; + } - IRegister reg = null; + if (CheckDuplicateRegister(builtRegister)) + throw MewtocolException.DupeRegister(builtRegister); - string propName = boundProp.Name; + if(CheckDuplicateNameRegister(builtRegister)) + throw MewtocolException.DupeNameRegister(builtRegister); - //rename the property name to prevent duplicate names in case of a bitwise prop - if (_isBitwise && regType == typeof(short)) - propName = $"Auto_Bitwise_DT{_address}"; + Registers.Add(builtRegister); - if (_isBitwise && regType == typeof(int)) - propName = $"Auto_Bitwise_DDT{_address}"; + } - if (regType == typeof(short)) { - reg = new NRegister(_address, propName, _isBitwise, _enumType).WithCollectionType(_colType); - } else if (regType == typeof(ushort)) { - reg = new NRegister(_address, propName).WithCollectionType(_colType); - } else if (regType == typeof(int)) { - reg = new NRegister(_address, propName, _isBitwise, _enumType).WithCollectionType(_colType); - } else if (regType == typeof(uint)) { - reg = new NRegister(_address, propName).WithCollectionType(_colType); - } else if (regType == typeof(float)) { - reg = new NRegister(_address, propName).WithCollectionType(_colType); - } else if (regType == typeof(string)) { - reg = new SRegister(_address, _length, propName).WithCollectionType(_colType); - } else if (regType == typeof(TimeSpan)) { - reg = new NRegister(_address, propName).WithCollectionType(_colType); - } else if (regType == typeof(bool)) { - reg = new BRegister(IOType.R, 0x0, _address, propName).WithCollectionType(_colType); - } + public void AddRegister(IRegister register) { - if (reg == null) { - throw new NotSupportedException($"The type {regType} is not allowed for Registers \n" + - $"Allowed are: short, ushort, int, uint, float and string"); - } + if (CheckDuplicateRegister(register)) + throw MewtocolException.DupeRegister(register); - if (Registers.Any(x => x.GetRegisterPLCName() == reg.GetRegisterPLCName()) && !_isBitwise) { - throw new NotSupportedException($"Cannot add a register multiple times, " + - $"make sure that all register attributes or AddRegister assignments have different adresses."); - } + if (CheckDuplicateNameRegister(register)) + throw MewtocolException.DupeNameRegister(register); - Registers.Add(reg); + Registers.Add(register); + + } + + private bool CheckDuplicateRegister (IRegister instance, out IRegister foundDupe) { + + foundDupe = Registers.FirstOrDefault(x => x.CompareIsDuplicate(instance)); + + return Registers.Contains(instance) || foundDupe != null; + + } + + private bool CheckDuplicateRegister(IRegister instance) { + + var foundDupe = Registers.FirstOrDefault(x => x.CompareIsDuplicate(instance)); + + return Registers.Contains(instance) || foundDupe != null; + + } + + private bool CheckDuplicateNameRegister(IRegister instance) { + + return Registers.Any(x => x.CompareIsNameDuplicate(instance)); } diff --git a/MewtocolNet/Exceptions/MewtocolException.cs b/MewtocolNet/Exceptions/MewtocolException.cs new file mode 100644 index 0000000..0601df4 --- /dev/null +++ b/MewtocolNet/Exceptions/MewtocolException.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MewtocolNet.Exceptions { + + [Serializable] + public class MewtocolException : Exception { + + public MewtocolException() { } + + public MewtocolException(string message) : base(message) { } + + public MewtocolException(string message, Exception inner) : base(message, inner) { } + + protected MewtocolException( + System.Runtime.Serialization.SerializationInfo info, + System.Runtime.Serialization.StreamingContext context) : base(info, context) { } + + public static MewtocolException DupeRegister (IRegister register) { + + return new MewtocolException($"The mewtocol interface already contains this register: {register.GetRegisterPLCName()}"); + + } + + public static MewtocolException DupeNameRegister (IRegister register) { + + return new MewtocolException($"The mewtocol interface registers already contains a register with the name: {register.Name}"); + + } + + } + +} diff --git a/MewtocolNet/IRegister.cs b/MewtocolNet/IRegister.cs index f68d60d..c442584 100644 --- a/MewtocolNet/IRegister.cs +++ b/MewtocolNet/IRegister.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; namespace MewtocolNet { diff --git a/MewtocolNet/IRegisterInternal.cs b/MewtocolNet/IRegisterInternal.cs new file mode 100644 index 0000000..9b9c5a4 --- /dev/null +++ b/MewtocolNet/IRegisterInternal.cs @@ -0,0 +1,18 @@ +using MewtocolNet.Registers; +using System; +using System.Threading.Tasks; + +namespace MewtocolNet { + internal interface IRegisterInternal { + + void WithCollectionType(Type colType); + + void SetValueFromPLC(object value); + + Task ReadAsync(MewtocolInterface interf); + + Task WriteAsync(MewtocolInterface interf, object data); + + } + +} diff --git a/MewtocolNet/MewtocolHelpers.cs b/MewtocolNet/MewtocolHelpers.cs index faee445..7da65b8 100644 --- a/MewtocolNet/MewtocolHelpers.cs +++ b/MewtocolNet/MewtocolHelpers.cs @@ -109,17 +109,17 @@ namespace MewtocolNet { } - internal static string BuildDTString(this string _inString, short _stringReservedSize) { + internal static string BuildDTString (this byte[] inBytes, short reservedSize) { StringBuilder sb = new StringBuilder(); //clamp string lenght - if (_inString.Length > _stringReservedSize) { - _inString = _inString.Substring(0, _stringReservedSize); + if (inBytes.Length > reservedSize) { + inBytes = inBytes.Take(reservedSize).ToArray(); } //actual string content - var hexstring = _inString.GetAsciiHexFromString(); + var hexstring = inBytes.ToHexString(); var sizeBytes = BitConverter.GetBytes((short)(hexstring.Length / 2)).ToHexString(); @@ -133,7 +133,7 @@ namespace MewtocolNet { } - var reservedSizeBytes = BitConverter.GetBytes(_stringReservedSize).ToHexString(); + var reservedSizeBytes = BitConverter.GetBytes(reservedSize).ToHexString(); //reserved string count bytes sb.Append(reservedSizeBytes); @@ -159,8 +159,10 @@ namespace MewtocolNet { } internal static string GetAsciiHexFromString(this string input) { + var bytes = new ASCIIEncoding().GetBytes(input); return bytes.ToHexString(); + } internal static byte[] HexStringToByteArray(this string hex) { @@ -265,6 +267,22 @@ namespace MewtocolNet { } + internal static bool CompareIsDuplicate (this IRegister reg1, IRegister compare) { + + bool valCompare = reg1.RegisterType == compare.RegisterType && + reg1.MemoryAddress == compare.MemoryAddress && + reg1.GetSpecialAddress() == compare.GetSpecialAddress(); + + return valCompare; + + } + + internal static bool CompareIsNameDuplicate(this IRegister reg1, IRegister compare) { + + return ( reg1.Name != null || compare.Name != null) && reg1.Name == compare.Name; + + } + } } \ No newline at end of file diff --git a/MewtocolNet/MewtocolInterface.cs b/MewtocolNet/MewtocolInterface.cs index 81cd8ff..273dc1e 100644 --- a/MewtocolNet/MewtocolInterface.cs +++ b/MewtocolNet/MewtocolInterface.cs @@ -308,7 +308,6 @@ namespace MewtocolNet { public MewtocolInterface WithPoller() { usePoller = true; - return this; } @@ -407,301 +406,10 @@ namespace MewtocolNet { #endregion - #region Register Collection - - /// - /// Attaches a register collection object to - /// the interface that can be updated automatically. - /// - /// Just create a class inheriting from - /// and assert some propertys with the custom . - /// - /// A collection inherting the class - public MewtocolInterface WithRegisterCollection(RegisterCollectionBase collection) { - - collection.PLCInterface = this; - - var props = collection.GetType().GetProperties(); - - foreach (var prop in props) { - - var attributes = prop.GetCustomAttributes(true); - - string propName = prop.Name; - foreach (var attr in attributes) { - - if (attr is RegisterAttribute cAttribute) { - - if (prop.PropertyType == typeof(bool) && cAttribute.AssignedBitIndex == -1) { - - //add bool register non bit assgined - Registers.Add(new BRegister((IOType)(int)cAttribute.RegisterType, cAttribute.SpecialAddress, cAttribute.MemoryArea, _name: propName).WithCollectionType(collection.GetType())); - - } - - if (prop.PropertyType == typeof(short)) { - AddRegister(collection.GetType(), cAttribute.MemoryArea, prop); - } - - if (prop.PropertyType == typeof(ushort)) { - AddRegister(collection.GetType(), cAttribute.MemoryArea, prop); - } - - if (prop.PropertyType == typeof(int)) { - AddRegister(collection.GetType(), cAttribute.MemoryArea, prop); - } - - if (prop.PropertyType == typeof(uint)) { - AddRegister(collection.GetType(), cAttribute.MemoryArea, prop); - } - - if (prop.PropertyType == typeof(float)) { - AddRegister(collection.GetType(), cAttribute.MemoryArea, prop); - } - - if (prop.PropertyType == typeof(string)) { - AddRegister(collection.GetType(), cAttribute.MemoryArea, prop, cAttribute.StringLength); - } - - if (prop.PropertyType.IsEnum) { - - if (cAttribute.BitCount == BitCount.B16) { - AddRegister(collection.GetType(), cAttribute.MemoryArea, prop, _enumType: prop.PropertyType); - } else { - AddRegister(collection.GetType(), cAttribute.MemoryArea, prop, _enumType: prop.PropertyType); - } - - } - - //read number as bit array - if (prop.PropertyType == typeof(BitArray)) { - - if (cAttribute.BitCount == BitCount.B16) { - AddRegister(collection.GetType(), cAttribute.MemoryArea, prop, _isBitwise: true); - } else { - AddRegister(collection.GetType(), cAttribute.MemoryArea, prop, _isBitwise: true); - } - - } - - //read number as bit array by invdividual properties - if (prop.PropertyType == typeof(bool) && cAttribute.AssignedBitIndex != -1) { - - //var bitwiseCount = Registers.Count(x => x.Value.isUsedBitwise); - - if (cAttribute.BitCount == BitCount.B16) { - AddRegister(collection.GetType(), cAttribute.MemoryArea, prop, _isBitwise: true); - } else { - AddRegister(collection.GetType(), cAttribute.MemoryArea, prop, _isBitwise: true); - } - - } - - if (prop.PropertyType == typeof(TimeSpan)) { - AddRegister(collection.GetType(), cAttribute.MemoryArea, prop); - } - - } - - } - - } - - RegisterChanged += (reg) => { - - //register is used bitwise - if (reg.IsUsedBitwise()) { - - for (int i = 0; i < props.Length; i++) { - - var prop = props[i]; - var bitWiseFound = prop.GetCustomAttributes(true) - .FirstOrDefault(y => y.GetType() == typeof(RegisterAttribute) && ((RegisterAttribute)y).MemoryArea == reg.MemoryAddress); - - if (bitWiseFound != null) { - - var casted = (RegisterAttribute)bitWiseFound; - var bitIndex = casted.AssignedBitIndex; - - BitArray bitAr = null; - - if (reg is NRegister reg16) { - var bytes = BitConverter.GetBytes((short)reg16.Value); - bitAr = new BitArray(bytes); - } else if (reg is NRegister reg32) { - var bytes = BitConverter.GetBytes((int)reg32.Value); - bitAr = new BitArray(bytes); - } - - if (bitAr != null && bitIndex < bitAr.Length && bitIndex >= 0) { - - //set the specific bit index if needed - prop.SetValue(collection, bitAr[bitIndex]); - collection.TriggerPropertyChanged(prop.Name); - - } else if (bitAr != null) { - - //set the specific bit array if needed - prop.SetValue(collection, bitAr); - collection.TriggerPropertyChanged(prop.Name); - - } - - } - - } - - } - - //updating normal properties - var foundToUpdate = props.FirstOrDefault(x => x.Name == reg.Name); - - if (foundToUpdate != null) { - - var foundAttributes = foundToUpdate.GetCustomAttributes(true); - var foundAttr = foundAttributes.FirstOrDefault(x => x.GetType() == typeof(RegisterAttribute)); - - if (foundAttr == null) - return; - - var registerAttr = (RegisterAttribute)foundAttr; - - //check if bit parse mode - if (registerAttr.AssignedBitIndex == -1) { - - HashSet NumericTypes = new HashSet { - typeof(bool), - typeof(short), - typeof(ushort), - typeof(int), - typeof(uint), - typeof(float), - typeof(TimeSpan), - typeof(string) - }; - - var regValue = ((IRegister)reg).Value; - - if (NumericTypes.Any(x => foundToUpdate.PropertyType == x)) { - foundToUpdate.SetValue(collection, regValue); - } - - if (foundToUpdate.PropertyType.IsEnum) { - foundToUpdate.SetValue(collection, regValue); - } - - } - - collection.TriggerPropertyChanged(foundToUpdate.Name); - - } - - }; - - if (collection != null) - collection.OnInterfaceLinked(this); - - Connected += (i) => { - if (collection != null) - collection.OnInterfaceLinkedAndOnline(this); - }; - - return this; - - } - - #endregion - - #region Register Writing - - /// - /// Sets a register in the PLCs memory - /// - /// The name the register was given to or a property name from the RegisterCollection class - /// The value to write to the register - public void SetRegister(string registerName, object value) { - - var foundRegister = GetAllRegisters().FirstOrDefault(x => x.Name == registerName); - - if (foundRegister == null) { - throw new Exception($"Register with the name {registerName} was not found"); - } - - _ = SetRegisterAsync(registerName, value); - - } - - /// - /// Sets a register in the PLCs memory asynchronously, returns the result status from the PLC - /// - /// The name the register was given to or a property name from the RegisterCollection class - /// The value to write to the register - public async Task SetRegisterAsync(string registerName, object value) { - - var foundRegister = GetAllRegisters().FirstOrDefault(x => x.Name == registerName); - - if (foundRegister == null) { - throw new Exception($"Register with the name {registerName} was not found"); - } - - if (foundRegister.GetType() == typeof(BRegister)) { - - return await WriteBoolRegister((BRegister)foundRegister, (bool)value); - - } - - if (foundRegister.GetType() == typeof(NRegister)) { - - return await WriteNumRegister((NRegister)foundRegister, (short)value); - - } - - if (foundRegister.GetType() == typeof(NRegister)) { - - return await WriteNumRegister((NRegister)foundRegister, (ushort)value); - - } - - if (foundRegister.GetType() == typeof(NRegister)) { - - return await WriteNumRegister((NRegister)foundRegister, (int)value); - - } - - if (foundRegister.GetType() == typeof(NRegister)) { - - return await WriteNumRegister((NRegister)foundRegister, (uint)value); - - } - - if (foundRegister.GetType() == typeof(NRegister)) { - - return await WriteNumRegister((NRegister)foundRegister, (float)value); - - } - - if (foundRegister.GetType() == typeof(NRegister)) { - - return await WriteNumRegister((NRegister)foundRegister, (TimeSpan)value); - - } - - if (foundRegister.GetType() == typeof(SRegister)) { - - return await WriteStringRegister((SRegister)foundRegister, (string)value); - - } - - return false; - - } - - #endregion - #region Low level command handling /// - /// Calculates checksum and sends a command to the PLC then awaits results + /// Calculates the checksum automatically and sends a command to the PLC then awaits results /// /// MEWTOCOL Formatted request string ex: %01#RT /// Returns the result diff --git a/MewtocolNet/MewtocolInterfaceRequests.cs b/MewtocolNet/MewtocolInterfaceRequests.cs index caa9e5c..6a3b9cc 100644 --- a/MewtocolNet/MewtocolInterfaceRequests.cs +++ b/MewtocolNet/MewtocolInterfaceRequests.cs @@ -4,7 +4,9 @@ using System; using System.Collections.Generic; using System.Globalization; using System.Linq; +using System.Text; using System.Text.RegularExpressions; +using System.Threading; using System.Threading.Tasks; namespace MewtocolNet { @@ -155,244 +157,110 @@ namespace MewtocolNet { #endregion - #region Bool register reading / writing + #region Raw register reading / writing - /// - /// Reads the given boolean register from the PLC - /// - /// The register to read - public async Task ReadBoolRegister(BRegister _toRead) { + internal async Task ReadRawRegisterAsync (IRegister _toRead) { - string requeststring = $"%{GetStationNumber()}#RCS{_toRead.BuildMewtocolQuery()}"; - var result = await SendCommandAsync(requeststring); + //returns a byte array 1 long and with the byte beeing 0 or 1 + if (_toRead.GetType() == typeof(BoolRegister)) { + + string requeststring = $"%{GetStationNumber()}#RCS{_toRead.BuildMewtocolQuery()}"; + var result = await SendCommandAsync(requeststring); + + var resultBool = result.Response.ParseRCSingleBit(); + if (resultBool != null) { + + return resultBool.Value ? new byte[] { 1 } : new byte[] { 0 }; + + } - if (!result.Success) { - return new BRegisterResult { - Result = result, - Register = _toRead - }; } - var resultBool = result.Response.ParseRCSingleBit(); - if (resultBool != null) { - _toRead.SetValueFromPLC(resultBool.Value); + //returns a byte array 2 bytes or 4 bytes long depending on the data size + if (_toRead.GetType().GetGenericTypeDefinition() == typeof(NumberRegister<>)) { + + string requeststring = $"%{GetStationNumber()}#RD{_toRead.BuildMewtocolQuery()}"; + var result = await SendCommandAsync(requeststring); + + if (!result.Success) + throw new Exception($"Failed to load the byte data for: {_toRead}"); + + if(_toRead.RegisterType == RegisterType.DT) { + + return result.Response.ParseDTByteString(4).HexStringToByteArray(); + + } else { + + return result.Response.ParseDTByteString(8).HexStringToByteArray(); + + } + } - var finalRes = new BRegisterResult { - Result = result, - Register = _toRead - }; + //returns a byte array with variable size + if (_toRead.GetType() == typeof(BytesRegister<>)) { - return finalRes; + string requeststring = $"%{GetStationNumber()}#RD{_toRead.BuildMewtocolQuery()}"; + var result = await SendCommandAsync(requeststring); + + if (!result.Success) + throw new Exception($"Failed to load the byte data for: {_toRead}"); + + return result.Response.ParseDTString().ReverseByteOrder().HexStringToByteArray(); + + } + + throw new Exception($"Failed to load the byte data for: {_toRead}"); } - /// - /// Writes to the given bool register on the PLC - /// - /// The register to write to - /// The value to write - /// The success state of the write operation - public async Task WriteBoolRegister(BRegister _toWrite, bool value) { + internal async Task WriteRawRegisterAsync (IRegister _toWrite, byte[] data) { - string requeststring = $"%{GetStationNumber()}#WCS{_toWrite.BuildMewtocolQuery()}{(value ? "1" : "0")}"; + //returns a byte array 1 long and with the byte beeing 0 or 1 + if (_toWrite.GetType() == typeof(BoolRegister)) { - var result = await SendCommandAsync(requeststring); + string requeststring = $"%{GetStationNumber()}#WCS{_toWrite.BuildMewtocolQuery()}{(data[0] == 1 ? "1" : "0")}"; + var result = await SendCommandAsync(requeststring); + return result.Success; - return result.Success && result.Response.StartsWith($"%{GetStationNumber()}$WC"); + } + + //returns a byte array 2 bytes or 4 bytes long depending on the data size + if (_toWrite.GetType().GetGenericTypeDefinition() == typeof(NumberRegister<>)) { + + string requeststring = $"%{GetStationNumber()}#WD{_toWrite.BuildMewtocolQuery()}{data.ToHexString()}"; + var result = await SendCommandAsync(requeststring); + return result.Success; + + } + + //returns a byte array with variable size + if (_toWrite.GetType() == typeof(BytesRegister<>)) { + + //string stationNum = GetStationNumber(); + //string dataString = gotBytes.BuildDTString(_toWrite.ReservedSize); + //string dataArea = _toWrite.BuildCustomIdent(dataString.Length / 4); + + //string requeststring = $"%{stationNum}#WD{dataArea}{dataString}"; + + //var result = await SendCommandAsync(requeststring); + + } + + return false; } #endregion - #region Number register reading / writing + #region Register reading / writing - /// - /// Reads the given numeric register from the PLC - /// - /// Type of number (short, ushort, int, uint, float) - /// The register to read - /// A result with the given NumberRegister containing the readback value and a result struct - public async Task> ReadNumRegister(NRegister _toRead) { + public async Task SetRegisterAsync (IRegister register, object value) { - Type numType = typeof(T); + var internalReg = (IRegisterInternal)register; - string requeststring = $"%{GetStationNumber()}#RD{_toRead.BuildMewtocolQuery()}"; - var result = await SendCommandAsync(requeststring); + return await internalReg.WriteAsync(this, value); - var failedResult = new NRegisterResult { - Result = result, - Register = _toRead - }; - - if (!result.Success || string.IsNullOrEmpty(result.Response)) { - return failedResult; - } - - if (numType == typeof(short)) { - - var resultBytes = result.Response.ParseDTByteString(4).ReverseByteOrder(); - if (resultBytes == null) return failedResult; - var val = short.Parse(resultBytes, NumberStyles.HexNumber); - _toRead.SetValueFromPLC(val); - - } else if (numType == typeof(ushort)) { - - var resultBytes = result.Response.ParseDTByteString(4).ReverseByteOrder(); - if (resultBytes == null) return failedResult; - var val = ushort.Parse(resultBytes, NumberStyles.HexNumber); - _toRead.SetValueFromPLC(val); - - } else if (numType == typeof(int)) { - - var resultBytes = result.Response.ParseDTByteString(8).ReverseByteOrder(); - if (resultBytes == null) return failedResult; - var val = int.Parse(resultBytes, NumberStyles.HexNumber); - _toRead.SetValueFromPLC(val); - - } else if (numType == typeof(uint)) { - - var resultBytes = result.Response.ParseDTByteString(8).ReverseByteOrder(); - if (resultBytes == null) return failedResult; - var val = uint.Parse(resultBytes, NumberStyles.HexNumber); - _toRead.SetValueFromPLC(val); - - } else if (numType == typeof(float)) { - - var resultBytes = result.Response.ParseDTByteString(8).ReverseByteOrder(); - if (resultBytes == null) return failedResult; - //convert to unsigned int first - var val = uint.Parse(resultBytes, NumberStyles.HexNumber); - - byte[] floatVals = BitConverter.GetBytes(val); - float finalFloat = BitConverter.ToSingle(floatVals, 0); - - _toRead.SetValueFromPLC(finalFloat); - - } else if (numType == typeof(TimeSpan)) { - - var resultBytes = result.Response.ParseDTByteString(8).ReverseByteOrder(); - if (resultBytes == null) return failedResult; - //convert to unsigned int first - var vallong = long.Parse(resultBytes, NumberStyles.HexNumber); - var valMillis = vallong * 10; - var ts = TimeSpan.FromMilliseconds(valMillis); - - //minmax writable / readable value is 10ms - _toRead.SetValueFromPLC(ts); - - } - - var finalRes = new NRegisterResult { - Result = result, - Register = _toRead - }; - - return finalRes; - } - - /// - /// Reads the given numeric register from the PLC - /// - /// Type of number (short, ushort, int, uint, float) - /// The register to write - /// The value to write - /// The success state of the write operation - public async Task WriteNumRegister(NRegister _toWrite, T _value) { - - byte[] toWriteVal; - Type numType = typeof(T); - - if (numType == typeof(short)) { - toWriteVal = BitConverter.GetBytes(Convert.ToInt16(_value)); - } else if (numType == typeof(ushort)) { - toWriteVal = BitConverter.GetBytes(Convert.ToUInt16(_value)); - } else if (numType == typeof(int)) { - toWriteVal = BitConverter.GetBytes(Convert.ToInt32(_value)); - } else if (numType == typeof(uint)) { - toWriteVal = BitConverter.GetBytes(Convert.ToUInt32(_value)); - } else if (numType == typeof(float)) { - - var fl = _value as float?; - if (fl == null) - throw new NullReferenceException("Float cannot be null"); - - toWriteVal = BitConverter.GetBytes(fl.Value); - - } else if (numType == typeof(TimeSpan)) { - - var fl = _value as TimeSpan?; - if (fl == null) - throw new NullReferenceException("Timespan cannot be null"); - - var tLong = (uint)(fl.Value.TotalMilliseconds / 10); - toWriteVal = BitConverter.GetBytes(tLong); - - } else { - toWriteVal = null; - } - - string requeststring = $"%{GetStationNumber()}#WD{_toWrite.BuildMewtocolQuery()}{toWriteVal.ToHexString()}"; - - var result = await SendCommandAsync(requeststring); - - return result.Success && result.Response.StartsWith($"%{GetStationNumber()}$WD"); - - } - - #endregion - - #region String register reading / writing - - //string is build up like this - //04 00 04 00 53 50 33 35 13 - //0, 1 = reserved size - //1, 2 = current size - //3,4,5,6 = ASCII encoded chars (SP35) - //7,8 = checksum - - /// - /// Reads back the value of a string register - /// - /// The register to read - /// The station number of the PLC - /// - public async Task ReadStringRegister(SRegister _toRead, int _stationNumber = 1) { - - string requeststring = $"%{GetStationNumber()}#RD{_toRead.BuildMewtocolQuery()}"; - var result = await SendCommandAsync(requeststring); - if (result.Success) - _toRead.SetValueFromPLC(result.Response.ParseDTString()); - return new SRegisterResult { - Result = result, - Register = _toRead - }; - } - - /// - /// Writes a string to a string register - /// - /// The register to write - /// The value to write, if the strings length is longer than the cap size it gets trimmed to the max char length - /// The station number of the PLC - /// The success state of the write operation - public async Task WriteStringRegister(SRegister _toWrite, string _value, int _stationNumber = 1) { - - if (_value == null) _value = ""; - if (_value.Length > _toWrite.ReservedSize) { - throw new ArgumentException("Write string size cannot be longer than reserved string size"); - } - - string stationNum = GetStationNumber(); - string dataString = _value.BuildDTString(_toWrite.ReservedSize); - string dataArea = _toWrite.BuildCustomIdent(dataString.Length / 4); - - string requeststring = $"%{stationNum}#WD{dataArea}{dataString}"; - - var result = await SendCommandAsync(requeststring); - - - return result.Success && result.Response.StartsWith($"%{GetStationNumber()}$WD"); } #endregion diff --git a/MewtocolNet/PLCEnums/PlcVarType.cs b/MewtocolNet/PLCEnums/PlcVarType.cs index d3e13a2..d7ac5de 100644 --- a/MewtocolNet/PLCEnums/PlcVarType.cs +++ b/MewtocolNet/PLCEnums/PlcVarType.cs @@ -11,7 +11,9 @@ namespace MewtocolNet { UDINT, REAL, TIME, - STRING + STRING, + WORD, + DWORD } diff --git a/MewtocolNet/PLCEnums/PlcVarTypeConversions.cs b/MewtocolNet/PLCEnums/PlcVarTypeConversions.cs deleted file mode 100644 index 4c6653b..0000000 --- a/MewtocolNet/PLCEnums/PlcVarTypeConversions.cs +++ /dev/null @@ -1,91 +0,0 @@ -using MewtocolNet.Registers; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace MewtocolNet { - internal static class PlcVarTypeConversions { - - static Dictionary dictTypeConv = new Dictionary { - - { PlcVarType.BOOL, typeof(bool) }, - { PlcVarType.INT, typeof(short) }, - { PlcVarType.UINT, typeof(ushort) }, - { PlcVarType.DINT, typeof(int) }, - { PlcVarType.UDINT, typeof(uint) }, - { PlcVarType.REAL, typeof(float) }, - { PlcVarType.TIME, typeof(TimeSpan) }, - { PlcVarType.STRING, typeof(string) }, - - }; - - static Dictionary dictRegisterConv = new Dictionary { - - { PlcVarType.BOOL, typeof(BRegister) }, - { PlcVarType.INT, typeof(NRegister) }, - { PlcVarType.UINT, typeof(NRegister) }, - { PlcVarType.DINT, typeof(NRegister) }, - { PlcVarType.UDINT, typeof(NRegister) }, - { PlcVarType.REAL, typeof(NRegister) }, - { PlcVarType.TIME, typeof(NRegister) }, - { PlcVarType.STRING, typeof(SRegister) }, - - }; - - internal static bool IsAllowedPlcCastingType () { - - var inversed = dictTypeConv.ToDictionary((i) => i.Value, (i) => i.Key); - - return inversed.ContainsKey(typeof(T)); - - } - - internal static bool IsAllowedPlcCastingType (this Type type) { - - var inversed = dictTypeConv.ToDictionary((i) => i.Value, (i) => i.Key); - - return inversed.ContainsKey(type); - - } - - internal static Type ToDotnetType (this PlcVarType type) { - - if(dictTypeConv.ContainsKey(type)) { - - return dictTypeConv[type]; - - } - - throw new NotSupportedException($"The PlcVarType: '{type}' is not supported"); - - } - - internal static PlcVarType ToPlcVarType (this Type type) { - - var inversed = dictTypeConv.ToDictionary((i) => i.Value, (i) => i.Key); - - if (inversed.ContainsKey(type)) { - - return inversed[type]; - - } - - throw new NotSupportedException($"The Dotnet Type: '{type}' is not supported"); - - } - - internal static Type ToRegisterType (this PlcVarType type) { - - if (dictRegisterConv.ContainsKey(type)) { - - return dictRegisterConv[type]; - - } - - throw new NotSupportedException($"The PlcVarType: '{type}' is not supported"); - - } - - } - -} diff --git a/MewtocolNet/RegisterAttributes/RegisterAttribute.cs b/MewtocolNet/RegisterAttributes/RegisterAttribute.cs index dd90c3c..cd79645 100644 --- a/MewtocolNet/RegisterAttributes/RegisterAttribute.cs +++ b/MewtocolNet/RegisterAttributes/RegisterAttribute.cs @@ -8,10 +8,12 @@ namespace MewtocolNet.RegisterAttributes { [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] public class RegisterAttribute : Attribute { - internal int MemoryArea; - internal int StringLength; - internal RegisterType RegisterType; + internal RegisterType? RegisterType; + + internal int MemoryArea = 0; + internal int ByteLength = 2; internal byte SpecialAddress = 0x0; + internal BitCount BitCount; internal int AssignedBitIndex = -1; @@ -19,11 +21,36 @@ namespace MewtocolNet.RegisterAttributes { /// Attribute for string type or numeric registers /// /// The area in the plcs memory - /// The max string length in the plc - public RegisterAttribute(int memoryArea, int stringLength = 1) { + public RegisterAttribute(int memoryArea) { MemoryArea = memoryArea; - StringLength = stringLength; + + } + + public RegisterAttribute(int memoryArea, int byteLength) { + + MemoryArea = memoryArea; + ByteLength = byteLength; + + } + + public RegisterAttribute(int memoryArea, BitCount bitCount) { + + MemoryArea = memoryArea; + BitCount = bitCount; + AssignedBitIndex = 0; + + RegisterType = BitCount == BitCount.B16 ? MewtocolNet.RegisterType.DT : MewtocolNet.RegisterType.DDT; + + } + + public RegisterAttribute(int memoryArea, BitCount bitCount, int bitIndex) { + + MemoryArea = memoryArea; + BitCount = bitCount; + AssignedBitIndex = bitIndex; + + RegisterType = BitCount == BitCount.B16 ? MewtocolNet.RegisterType.DT : MewtocolNet.RegisterType.DDT; } @@ -49,42 +76,6 @@ namespace MewtocolNet.RegisterAttributes { } - /// - /// Attribute to read numeric registers as bitwise - /// - /// The area in the plcs memory - /// The number of bits to parse - public RegisterAttribute(int memoryArea, BitCount bitcount) { - - MemoryArea = memoryArea; - StringLength = 0; - BitCount = bitcount; - - } - - /// - /// Attribute to read numeric registers as bitwise - /// - /// The area in the plcs memory - /// The number of bits to parse - /// The index of the bit that gets linked to the bool - public RegisterAttribute(int memoryArea, uint assignBit, BitCount bitcount) { - - if (assignBit > 15 && bitcount == BitCount.B16) { - throw new NotSupportedException("The assignBit parameter cannot be greater than 15 in a 16 bit var"); - } - - if (assignBit > 31 && bitcount == BitCount.B32) { - throw new NotSupportedException("The assignBit parameter cannot be greater than 31 in a 32 bit var"); - } - - MemoryArea = memoryArea; - StringLength = 0; - BitCount = bitcount; - AssignedBitIndex = (int)assignBit; - - } - } } diff --git a/MewtocolNet/RegisterBuildInfo.cs b/MewtocolNet/RegisterBuildInfo.cs new file mode 100644 index 0000000..c53074a --- /dev/null +++ b/MewtocolNet/RegisterBuildInfo.cs @@ -0,0 +1,118 @@ +using MewtocolNet.Registers; +using System; +using System.Collections; +using System.Reflection; + +namespace MewtocolNet +{ + + internal struct RegisterBuildInfo { + + internal string name; + internal int memoryAddress; + internal int memorySizeBytes; + internal byte? specialAddress; + + internal RegisterType? registerType; + internal Type dotnetCastType; + internal Type collectionType; + + internal IRegister Build () { + + RegisterType regType = registerType ?? dotnetCastType.ToRegisterTypeDefault(); + + PlcVarType plcType = dotnetCastType.ToPlcVarType(); + Type registerClassType = plcType.GetDefaultPlcVarType(); + + if (regType.IsNumericDTDDT() && (dotnetCastType == typeof(bool) || dotnetCastType == typeof(BitArray))) { + + //------------------------------------------- + //as numeric register with boolean bit target + + var type = typeof(NumberRegister); + + var areaAddr = memoryAddress; + + //create a new bregister instance + var flags = BindingFlags.Public | BindingFlags.Instance; + + //int _adress, string _name = null, bool isBitwise = false, Type _enumType = null + var parameters = new object[] { areaAddr, name, true, null }; + var instance = (IRegister)Activator.CreateInstance(type, flags, null, parameters, null); + + if (collectionType != null) + ((IRegisterInternal)instance).WithCollectionType(collectionType); + + return instance; + + } else if (regType.IsNumericDTDDT()) { + + //------------------------------------------- + //as numeric register + + var type = plcType.GetDefaultPlcVarType(); + + var areaAddr = memoryAddress; + + //create a new bregister instance + var flags = BindingFlags.Public | BindingFlags.Instance; + + //int _adress, string _name = null, bool isBitwise = false, Type _enumType = null + var parameters = new object[] { areaAddr, name, false, null }; + var instance = (IRegister)Activator.CreateInstance(type, flags, null, parameters, null); + + if(collectionType != null) + ((IRegisterInternal)instance).WithCollectionType(collectionType); + + return instance; + + } + + if (regType.IsBoolean()) { + + //------------------------------------------- + //as boolean register + + var io = (IOType)(int)regType; + var spAddr = specialAddress; + var areaAddr = memoryAddress; + + //create a new bregister instance + var flags = BindingFlags.Public | BindingFlags.Instance; + var parameters = new object[] { io, spAddr.Value, areaAddr, name }; + var instance = (BoolRegister)Activator.CreateInstance(typeof(BoolRegister), flags, null, parameters, null); + + if (collectionType != null) + ((IRegisterInternal)instance).WithCollectionType(collectionType); + + return instance; + + } + + if(regType == RegisterType.DT_RANGE) { + + //------------------------------------------- + //as byte range register + + var type = plcType.GetDefaultPlcVarType(); + + //create a new bregister instance + var flags = BindingFlags.Public | BindingFlags.Instance; + //int _adress, int _reservedSize, string _name = null + var parameters = new object[] { memoryAddress, memorySizeBytes, name }; + var instance = (IRegister)Activator.CreateInstance(type, flags, null, parameters, null); + + if (collectionType != null) + ((IRegisterInternal)instance).WithCollectionType(collectionType); + + return instance; + + } + + throw new Exception("Failed to build register"); + + } + + } + +} diff --git a/MewtocolNet/RegisterBuilding/FinalizerExtensions.cs b/MewtocolNet/RegisterBuilding/FinalizerExtensions.cs index b42a30e..a06e77b 100644 --- a/MewtocolNet/RegisterBuilding/FinalizerExtensions.cs +++ b/MewtocolNet/RegisterBuilding/FinalizerExtensions.cs @@ -1,8 +1,10 @@ using MewtocolNet.Registers; using System; +using System.Linq; using System.Reflection; -namespace MewtocolNet.RegisterBuilding { +namespace MewtocolNet.RegisterBuilding +{ public static class FinalizerExtensions { @@ -29,7 +31,7 @@ namespace MewtocolNet.RegisterBuilding { step.dotnetVarType = typeof(bool); - } else if (isTypeNotDefined && step.RegType == RegisterType.DT_START) { + } else if (isTypeNotDefined && step.RegType == RegisterType.DT_RANGE) { step.dotnetVarType = typeof(string); @@ -37,58 +39,31 @@ namespace MewtocolNet.RegisterBuilding { if(step.plcVarType != null) { - step.dotnetVarType = step.plcVarType.Value.ToDotnetType(); + step.dotnetVarType = step.plcVarType.Value.GetDefaultDotnetType(); } - //as numeric register - if (step.RegType.IsNumericDTDDT()) { + var builtReg = new RegisterBuildInfo { - if(step.plcVarType == null && step.dotnetVarType != null) { + name = step.Name, + specialAddress = step.SpecialAddress, + memoryAddress = step.MemAddress, + registerType = step.RegType, + dotnetCastType = step.dotnetVarType, - step.plcVarType = step.dotnetVarType.ToPlcVarType(); + }.Build(); - } + step.AddToRegisterList(builtReg); - var type = step.plcVarType.Value.ToRegisterType(); + return builtReg; - var areaAddr = step.MemAddress; - var name = step.Name; + } - //create a new bregister instance - var flags = BindingFlags.Public | BindingFlags.Instance; + private static void AddToRegisterList (this RegisterBuilderStep step, IRegister instance) { - //int _adress, string _name = null, bool isBitwise = false, Type _enumType = null - var parameters = new object[] { areaAddr, name, false, null }; - var instance = (IRegister)Activator.CreateInstance(type, flags, null, parameters, null); + if (step.forInterface == null) return; - return instance; - - } - - if (step.RegType.IsBoolean()) { - - var io = (IOType)(int)step.RegType; - var spAddr = step.SpecialAddress; - var areaAddr = step.MemAddress; - var name = step.Name; - - //create a new bregister instance - var flags = BindingFlags.Public | BindingFlags.Instance; - var parameters = new object[] { io, spAddr.Value, areaAddr, name }; - var instance = (BRegister)Activator.CreateInstance(typeof(BRegister), flags, null, parameters, null); - - return instance; - - } - - if (step.dotnetVarType != null) { - - - - } - - throw new Exception("Failed to build register"); + step.forInterface.AddRegister(instance); } diff --git a/MewtocolNet/RegisterBuilding/RegBuilder.cs b/MewtocolNet/RegisterBuilding/RegBuilder.cs index ce91a17..4ca8416 100644 --- a/MewtocolNet/RegisterBuilding/RegBuilder.cs +++ b/MewtocolNet/RegisterBuilding/RegBuilder.cs @@ -9,7 +9,9 @@ namespace MewtocolNet.RegisterBuilding { /// /// Contains useful tools for register creation /// - public static class RegBuilder { + public class RegBuilder { + + internal MewtocolInterface forInterface = null; //methods to test the input string on private static List> parseMethods = new List>() { @@ -19,7 +21,18 @@ namespace MewtocolNet.RegisterBuilding { }; - public static RegisterBuilderStep FromPlcRegName (string plcAddrName, string name = null) { + public static RegBuilder ForInterface (MewtocolInterface interf) { + + var rb = new RegBuilder(); + rb.forInterface = interf; + return rb; + + } + + public static RegBuilder Factory { get; private set; } = new RegBuilder(); + + + public RegisterBuilderStep FromPlcRegName (string plcAddrName, string name = null) { foreach (var method in parseMethods) { @@ -30,8 +43,8 @@ namespace MewtocolNet.RegisterBuilding { if (!string.IsNullOrEmpty(name)) res.stepData.Name = name; - res.stepData.OriginalInput = plcAddrName; - + res.stepData.OriginalInput = plcAddrName; + res.stepData.forInterface = forInterface; return res.stepData; } else if(res.state == ParseResultState.FailedHard) { diff --git a/MewtocolNet/RegisterBuilding/RegisterBuilderStep.cs b/MewtocolNet/RegisterBuilding/RegisterBuilderStep.cs index e1c2103..04883be 100644 --- a/MewtocolNet/RegisterBuilding/RegisterBuilderStep.cs +++ b/MewtocolNet/RegisterBuilding/RegisterBuilderStep.cs @@ -1,8 +1,11 @@ using System; namespace MewtocolNet.RegisterBuilding { + public class RegisterBuilderStep { + internal MewtocolInterface forInterface; + internal bool wasCasted = false; internal string OriginalInput; @@ -15,12 +18,12 @@ namespace MewtocolNet.RegisterBuilding { internal PlcVarType? plcVarType; internal Type dotnetVarType; - public RegisterBuilderStep () => throw new NotSupportedException("Cant make a new instance of RegisterBuilderStep, use the builder pattern"); - - internal RegisterBuilderStep (RegisterType regType, int memAddr) { - - RegType = regType; - MemAddress = memAddr; + public RegisterBuilderStep() => throw new NotSupportedException("Cant make a new instance of RegisterBuilderStep, use the builder pattern"); + + internal RegisterBuilderStep(RegisterType regType, int memAddr) { + + RegType = regType; + MemAddress = memAddr; } @@ -28,11 +31,11 @@ namespace MewtocolNet.RegisterBuilding { RegType = regType; MemAddress = memAddr; - SpecialAddress = specialAddr; + SpecialAddress = specialAddr; } - public RegisterBuilderStep AsPlcType (PlcVarType varType) { + public RegisterBuilderStep AsPlcType(PlcVarType varType) { dotnetVarType = null; plcVarType = varType; @@ -43,9 +46,9 @@ namespace MewtocolNet.RegisterBuilding { } - public RegisterBuilderStep AsType () { + public RegisterBuilderStep AsType() { - if(!typeof(T).IsAllowedPlcCastingType()) { + if (!typeof(T).IsAllowedPlcCastingType()) { throw new NotSupportedException($"The dotnet type {typeof(T)}, is not supported for PLC type casting"); @@ -60,7 +63,7 @@ namespace MewtocolNet.RegisterBuilding { } - internal RegisterBuilderStep AutoType () { + internal RegisterBuilderStep AutoType() { switch (RegType) { case RegisterType.X: @@ -74,7 +77,7 @@ namespace MewtocolNet.RegisterBuilding { case RegisterType.DDT: dotnetVarType = typeof(int); break; - case RegisterType.DT_START: + case RegisterType.DT_RANGE: dotnetVarType = typeof(string); break; } diff --git a/MewtocolNet/RegisterEnums.cs b/MewtocolNet/RegisterEnums.cs index 1195f35..e23e89f 100644 --- a/MewtocolNet/RegisterEnums.cs +++ b/MewtocolNet/RegisterEnums.cs @@ -1,4 +1,6 @@ -namespace MewtocolNet { +using System; + +namespace MewtocolNet { /// /// The register prefixed type @@ -26,9 +28,9 @@ /// DDT = 4, /// - /// Start area of a byte sequence longer than 2 words + /// Area of a byte sequence longer than 2 words /// - DT_START = 5, + DT_RANGE = 5, } diff --git a/MewtocolNet/Registers/BRegister.cs b/MewtocolNet/Registers/BoolRegister.cs similarity index 81% rename from MewtocolNet/Registers/BRegister.cs rename to MewtocolNet/Registers/BoolRegister.cs index 3e83adb..2ec09f4 100644 --- a/MewtocolNet/Registers/BRegister.cs +++ b/MewtocolNet/Registers/BoolRegister.cs @@ -1,13 +1,14 @@ using System; using System.ComponentModel; using System.Text; +using System.Threading.Tasks; namespace MewtocolNet.Registers { /// /// Defines a register containing a boolean /// - public class BRegister : IRegister, INotifyPropertyChanged { + public class BoolRegister : IRegister, IRegisterInternal, INotifyPropertyChanged { /// /// Gets called whenever the value was changed @@ -62,7 +63,7 @@ namespace MewtocolNet.Registers { /// The custom name /// /// - public BRegister(IOType _io, byte _spAddress = 0x0, int _areaAdress = 0, string _name = null) { + public BoolRegister(IOType _io, byte _spAddress = 0x0, int _areaAdress = 0, string _name = null) { if (_areaAdress < 0) throw new NotSupportedException("The area address cant be negative"); @@ -84,36 +85,33 @@ namespace MewtocolNet.Registers { } - internal BRegister WithCollectionType(Type colType) { - - collectionType = colType; - return this; - - } + public void WithCollectionType (Type colType) => collectionType = colType; public byte? GetSpecialAddress() => SpecialAddress; /// - /// Builds the register area name + /// Builds the register area name for the mewtocol protocol /// public string BuildMewtocolQuery() { - //build area code from register type - StringBuilder asciistring = new StringBuilder(RegisterType.ToString()); + //(R|X|Y)(area add [3] + special add [1]) + StringBuilder asciistring = new StringBuilder(); - string memPadded = MemoryAddress.ToString().PadLeft(4, '0'); + string prefix = RegisterType.ToString(); + string mem = MemoryAddress.ToString(); string sp = SpecialAddress.ToString("X1"); - asciistring.Append(memPadded); + asciistring.Append(prefix); + asciistring.Append(mem.PadLeft(3, '0')); asciistring.Append(sp); return asciistring.ToString(); } - internal void SetValueFromPLC(bool val) { + public void SetValueFromPLC(object val) { - lastValue = val; + lastValue = (bool)val; TriggerChangedEvnt(this); TriggerNotifyChange(); @@ -183,6 +181,19 @@ namespace MewtocolNet.Registers { } + public async Task ReadAsync (MewtocolInterface interf) { + + var read = await interf.ReadRawRegisterAsync(this); + return PlcValueParser.Parse(read); + + } + + public async Task WriteAsync (MewtocolInterface interf, object data) { + + return await interf.WriteRawRegisterAsync(this, PlcValueParser.Encode((bool)data)); + + } + } } diff --git a/MewtocolNet/Registers/BRegisterResult.cs b/MewtocolNet/Registers/BoolRegisterResult.cs similarity index 78% rename from MewtocolNet/Registers/BRegisterResult.cs rename to MewtocolNet/Registers/BoolRegisterResult.cs index 8a39277..4016a48 100644 --- a/MewtocolNet/Registers/BRegisterResult.cs +++ b/MewtocolNet/Registers/BoolRegisterResult.cs @@ -3,7 +3,7 @@ /// /// Result for a boolean register /// - public class BRegisterResult { + public class BoolRegisterResult { /// /// The command result @@ -13,7 +13,7 @@ /// /// The used register /// - public BRegister Register { get; set; } + public BoolRegister Register { get; set; } } diff --git a/MewtocolNet/Registers/SRegister.cs b/MewtocolNet/Registers/BytesRegister.cs similarity index 82% rename from MewtocolNet/Registers/SRegister.cs rename to MewtocolNet/Registers/BytesRegister.cs index 91b6ca4..8ac6240 100644 --- a/MewtocolNet/Registers/SRegister.cs +++ b/MewtocolNet/Registers/BytesRegister.cs @@ -1,13 +1,15 @@ using System; +using System.Collections.Generic; using System.ComponentModel; using System.Text; +using System.Threading.Tasks; namespace MewtocolNet.Registers { /// /// Defines a register containing a string /// - public class SRegister : IRegister { + public class BytesRegister : IRegister, IRegisterInternal { /// /// Gets called whenever the value was changed @@ -58,30 +60,25 @@ namespace MewtocolNet.Registers { /// /// Defines a register containing a string /// - public SRegister(int _adress, int _reservedStringSize, string _name = null) { + public BytesRegister(int _adress, int _reservedSize, string _name = null) { if (_adress > 99999) throw new NotSupportedException("Memory adresses cant be greater than 99999"); name = _name; memoryAdress = _adress; - ReservedSize = (short)_reservedStringSize; + ReservedSize = (short)_reservedSize; //calc mem length - var wordsize = (double)_reservedStringSize / 2; + var wordsize = (double)_reservedSize / 2; if (wordsize % 2 != 0) { wordsize++; } - RegisterType = RegisterType.DT_START; + RegisterType = RegisterType.DT_RANGE; memoryLength = (int)Math.Round(wordsize + 1); } - internal SRegister WithCollectionType (Type colType) { - - collectionType = colType; - return this; - - } + public void WithCollectionType(Type colType) => collectionType = colType; /// /// Builds the register identifier for the mewotocol protocol @@ -115,9 +112,9 @@ namespace MewtocolNet.Registers { public bool IsUsedBitwise() => false; - internal void SetValueFromPLC(string val) { + public void SetValueFromPLC(object val) { - lastValue = val; + lastValue = (string)val; TriggerChangedEvnt(this); TriggerNotifyChange(); @@ -146,6 +143,19 @@ namespace MewtocolNet.Registers { public string ToString(bool additional) => $"{GetRegisterPLCName()} - Value: {GetValueString()}"; + public async Task ReadAsync(MewtocolInterface interf) { + + var read = await interf.ReadRawRegisterAsync(this); + return PlcValueParser.Parse(read); + + } + + public async Task WriteAsync(MewtocolInterface interf, object data) { + + return await interf.WriteRawRegisterAsync(this, (byte[])data); + + } + } } diff --git a/MewtocolNet/Registers/SRegisterResult.cs b/MewtocolNet/Registers/BytesRegisterResult.cs similarity index 78% rename from MewtocolNet/Registers/SRegisterResult.cs rename to MewtocolNet/Registers/BytesRegisterResult.cs index 45129a6..314decf 100644 --- a/MewtocolNet/Registers/SRegisterResult.cs +++ b/MewtocolNet/Registers/BytesRegisterResult.cs @@ -3,7 +3,7 @@ /// /// The results of a string register operation /// - public class SRegisterResult { + public class BytesRegisterResult { /// /// The command result @@ -12,7 +12,7 @@ /// /// The register definition used /// - public SRegister Register { get; set; } + public BytesRegister Register { get; set; } } diff --git a/MewtocolNet/Registers/NRegister.cs b/MewtocolNet/Registers/NumberRegister.cs similarity index 90% rename from MewtocolNet/Registers/NRegister.cs rename to MewtocolNet/Registers/NumberRegister.cs index c36c0d4..b4f09c6 100644 --- a/MewtocolNet/Registers/NRegister.cs +++ b/MewtocolNet/Registers/NumberRegister.cs @@ -3,6 +3,7 @@ using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Text; +using System.Threading.Tasks; namespace MewtocolNet.Registers { @@ -10,7 +11,7 @@ namespace MewtocolNet.Registers { /// Defines a register containing a number /// /// The type of the numeric value - public class NRegister : IRegister { + public class NumberRegister : IRegister, IRegisterInternal { /// /// Gets called whenever the value was changed @@ -65,7 +66,7 @@ namespace MewtocolNet.Registers { /// /// Memory start adress max 99999 /// Name of the register - public NRegister (int _adress, string _name = null) { + public NumberRegister (int _adress, string _name = null) { if (_adress > 99999) throw new NotSupportedException("Memory adresses cant be greater than 99999"); @@ -98,7 +99,7 @@ namespace MewtocolNet.Registers { } - public NRegister (int _adress, string _name = null, bool isBitwise = false, Type _enumType = null) { + public NumberRegister (int _adress, string _name = null, bool isBitwise = false, Type _enumType = null) { if (_adress > 99999) throw new NotSupportedException("Memory adresses cant be greater than 99999"); memoryAdress = _adress; @@ -132,14 +133,9 @@ namespace MewtocolNet.Registers { } - internal NRegister WithCollectionType(Type colType) { + public void WithCollectionType(Type colType) => collectionType = colType; - collectionType = colType; - return this; - - } - - internal void SetValueFromPLC(object val) { + public void SetValueFromPLC(object val) { lastValue = (T)val; TriggerChangedEvnt(this); @@ -214,7 +210,7 @@ namespace MewtocolNet.Registers { /// A bitarray public BitArray GetBitwise() { - if (this is NRegister shortReg) { + if (this is NumberRegister shortReg) { var bytes = BitConverter.GetBytes((short)Value); BitArray bitAr = new BitArray(bytes); @@ -222,7 +218,7 @@ namespace MewtocolNet.Registers { } - if (this is NRegister intReg) { + if (this is NumberRegister intReg) { var bytes = BitConverter.GetBytes((int)Value); BitArray bitAr = new BitArray(bytes); @@ -291,6 +287,19 @@ namespace MewtocolNet.Registers { public string ToString(bool additional) => $"{GetRegisterPLCName()} - Value: {GetValueString()}"; + public async Task ReadAsync(MewtocolInterface interf) { + + var read = await interf.ReadRawRegisterAsync(this); + return PlcValueParser.Parse(read); + + } + + public async Task WriteAsync(MewtocolInterface interf, object data) { + + return await interf.WriteRawRegisterAsync(this, PlcValueParser.Encode((T)data)); + + } + } } diff --git a/MewtocolNet/Registers/NRegisterResult.cs b/MewtocolNet/Registers/NumberRegisterResult.cs similarity index 88% rename from MewtocolNet/Registers/NRegisterResult.cs rename to MewtocolNet/Registers/NumberRegisterResult.cs index 367233a..04bdec3 100644 --- a/MewtocolNet/Registers/NRegisterResult.cs +++ b/MewtocolNet/Registers/NumberRegisterResult.cs @@ -4,7 +4,7 @@ /// Result for a read/write operation /// /// The type of the numeric value - public class NRegisterResult { + public class NumberRegisterResult { /// /// Command result @@ -14,7 +14,7 @@ /// /// The used register /// - public NRegister Register { get; set; } + public NumberRegister Register { get; set; } /// /// Trys to get the value of there is one diff --git a/MewtocolNet/TypeConversion/Conversions.cs b/MewtocolNet/TypeConversion/Conversions.cs new file mode 100644 index 0000000..db72369 --- /dev/null +++ b/MewtocolNet/TypeConversion/Conversions.cs @@ -0,0 +1,124 @@ +using MewtocolNet.Registers; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; + +namespace MewtocolNet.TypeConversion { + + internal static class Conversions { + + internal static Dictionary dictPlcTypeToRegisterType = new Dictionary { + + { PlcVarType.BOOL, RegisterType.R }, + { PlcVarType.INT, RegisterType.DT }, + { PlcVarType.UINT, RegisterType.DT }, + { PlcVarType.DINT, RegisterType.DDT }, + { PlcVarType.UDINT, RegisterType.DDT }, + { PlcVarType.REAL, RegisterType.DDT }, + { PlcVarType.TIME, RegisterType.DDT }, + { PlcVarType.WORD, RegisterType.DT }, + { PlcVarType.DWORD, RegisterType.DDT }, + { PlcVarType.STRING, RegisterType.DT_RANGE }, + + }; + + internal static List items = new List { + + new PlcTypeConversion(RegisterType.R) { + HoldingRegisterType = typeof(BoolRegister), + PlcVarType = PlcVarType.BOOL, + FromRaw = bytes => { + + return (bool)(bytes[0] == 1); + }, + ToRaw = value => { + + return new byte[] { (byte)(value ? 1 : 0) }; + + }, + }, + new PlcTypeConversion(RegisterType.X) { + HoldingRegisterType = typeof(BoolRegister), + PlcVarType = PlcVarType.BOOL, + FromRaw = bytes => { + + return bytes[0] == 1; + }, + ToRaw = value => { + + return new byte[] { (byte)(value ? 1 : 0) }; + + }, + }, + new PlcTypeConversion(RegisterType.Y) { + HoldingRegisterType = typeof(BoolRegister), + PlcVarType = PlcVarType.BOOL, + FromRaw = bytes => { + + return bytes[0] == 1; + }, + ToRaw = value => { + + return new byte[] { (byte)(value ? 1 : 0) }; + + }, + }, + new PlcTypeConversion(RegisterType.DT) { + HoldingRegisterType = typeof(NumberRegister), + PlcVarType = PlcVarType.INT, + FromRaw = bytes => { + + return BitConverter.ToInt16(bytes, 0); + }, + ToRaw = value => { + + return BitConverter.GetBytes(value); + + }, + }, + new PlcTypeConversion(RegisterType.DT) { + HoldingRegisterType = typeof(NumberRegister), + PlcVarType = PlcVarType.UINT, + FromRaw = bytes => { + + return BitConverter.ToUInt16(bytes, 0); + }, + ToRaw = value => { + + return BitConverter.GetBytes(value); + + }, + }, + new PlcTypeConversion(RegisterType.DDT) { + HoldingRegisterType = typeof(NumberRegister), + PlcVarType = PlcVarType.DINT, + FromRaw = bytes => { + + return BitConverter.ToInt32(bytes, 0); + }, + ToRaw = value => { + + return BitConverter.GetBytes(value); + + }, + }, + new PlcTypeConversion(RegisterType.DDT) { + HoldingRegisterType = typeof(NumberRegister), + PlcVarType = PlcVarType.UDINT, + FromRaw = bytes => { + + return BitConverter.ToUInt32(bytes, 0); + }, + ToRaw = value => { + + return BitConverter.GetBytes(value); + + }, + }, + + }; + + } + +} diff --git a/MewtocolNet/TypeConversion/IPlcTypeConverter.cs b/MewtocolNet/TypeConversion/IPlcTypeConverter.cs new file mode 100644 index 0000000..0b381f8 --- /dev/null +++ b/MewtocolNet/TypeConversion/IPlcTypeConverter.cs @@ -0,0 +1,21 @@ +using System; + +namespace MewtocolNet { + + internal interface IPlcTypeConverter { + + object FromRawData(byte[] data); + + byte[] ToRawData(object value); + + Type GetDotnetType(); + + Type GetHoldingRegisterType(); + + RegisterType GetPlcRegisterType(); + + PlcVarType GetPlcVarType(); + + } + +} diff --git a/MewtocolNet/TypeConversion/PlcTypeConversion.cs b/MewtocolNet/TypeConversion/PlcTypeConversion.cs new file mode 100644 index 0000000..f50d0b5 --- /dev/null +++ b/MewtocolNet/TypeConversion/PlcTypeConversion.cs @@ -0,0 +1,42 @@ +using System; +using System.ComponentModel; + +namespace MewtocolNet { + + internal class PlcTypeConversion : IPlcTypeConverter { + + public Type MainType { get; private set; } + + public RegisterType PlcType { get; private set; } + + public PlcVarType PlcVarType { get; set; } + + public Type HoldingRegisterType { get; set; } + + public Func FromRaw { get; set; } + + public Func ToRaw { get; set; } + + public PlcTypeConversion(RegisterType plcType) { + + MainType = typeof(T); + PlcType = plcType; + + } + + public Type GetDotnetType() => MainType; + + public Type GetHoldingRegisterType() => HoldingRegisterType; + + public RegisterType GetPlcRegisterType() => PlcType; + + public PlcVarType GetPlcVarType() => PlcVarType; + + public object FromRawData(byte[] data) => FromRaw.Invoke(data); + + public byte[] ToRawData(object value) => ToRaw.Invoke((T)value); + + + } + +} diff --git a/MewtocolNet/TypeConversion/PlcValueParser.cs b/MewtocolNet/TypeConversion/PlcValueParser.cs new file mode 100644 index 0000000..05a34a1 --- /dev/null +++ b/MewtocolNet/TypeConversion/PlcValueParser.cs @@ -0,0 +1,55 @@ +using MewtocolNet.Exceptions; +using MewtocolNet.Registers; +using MewtocolNet.TypeConversion; +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; + +namespace MewtocolNet { + + internal static class PlcValueParser { + + private static List conversions => Conversions.items; + + public static T Parse(byte[] bytes) { + + var converter = conversions.FirstOrDefault(x => x.GetDotnetType() == typeof(T)); + + if (converter == null) + throw new MewtocolException($"A converter for the dotnet type {typeof(T)} doesn't exist"); + + return (T)converter.FromRawData(bytes); + + } + + public static byte[] Encode (T value) { + + var converter = conversions.FirstOrDefault(x => x.GetDotnetType() == typeof(T)); + + if (converter == null) + throw new MewtocolException($"A converter for the dotnet type {typeof(T)} doesn't exist"); + + return converter.ToRawData(value); + + } + + public static List GetAllowDotnetTypes () => conversions.Select(x => x.GetDotnetType()).ToList(); + + public static List GetAllowRegisterTypes () => conversions.Select(x => x.GetHoldingRegisterType()).ToList(); + + public static RegisterType? GetDefaultRegisterType (Type type) => + conversions.FirstOrDefault(x => x.GetDotnetType() == type)?.GetPlcRegisterType(); + + public static Type GetDefaultPlcVarType (this PlcVarType type) => + conversions.FirstOrDefault(x => x.GetPlcVarType() == type)?.GetHoldingRegisterType(); + + public static Type GetDefaultDotnetType (this PlcVarType type) => + conversions.FirstOrDefault(x => x.GetPlcVarType() == type)?.GetDotnetType(); + + public static PlcVarType? GetDefaultPlcVarType (this Type type) => + conversions.FirstOrDefault(x => x.GetDotnetType() == type)?.GetPlcVarType(); + + } + +} diff --git a/MewtocolNet/TypeConversion/PlcVarTypeConversions.cs b/MewtocolNet/TypeConversion/PlcVarTypeConversions.cs new file mode 100644 index 0000000..2ff6817 --- /dev/null +++ b/MewtocolNet/TypeConversion/PlcVarTypeConversions.cs @@ -0,0 +1,65 @@ +using MewtocolNet.Exceptions; +using MewtocolNet.Registers; +using MewtocolNet.TypeConversion; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace MewtocolNet { + + internal static class PlcVarTypeConversions { + + static List allowedCastingTypes = PlcValueParser.GetAllowDotnetTypes(); + + static List allowedGenericRegisters = PlcValueParser.GetAllowRegisterTypes(); + + internal static bool IsAllowedRegisterGenericType(this IRegister register) { + + return allowedGenericRegisters.Contains(register.GetType()); + + } + + internal static bool IsAllowedPlcCastingType() { + + return allowedCastingTypes.Contains(typeof(T)); + + } + + internal static bool IsAllowedPlcCastingType(this Type type) { + + return allowedCastingTypes.Contains(type); + + } + + internal static RegisterType ToRegisterTypeDefault(this Type type) { + + var found = PlcValueParser.GetDefaultRegisterType(type); + + if (found != null) { + + return found.Value; + + } + + throw new MewtocolException("No default register type found"); + + } + + internal static PlcVarType ToPlcVarType (this Type type) { + + var found = type.GetDefaultPlcVarType().Value; + + if (found != null) { + + return found; + + } + + throw new MewtocolException("No default plcvar type found"); + + } + + } + +} diff --git a/MewtocolTests/AutomatedPropertyRegisters.cs b/MewtocolTests/AutomatedPropertyRegisters.cs index cd46f3f..d0e4b1c 100644 --- a/MewtocolTests/AutomatedPropertyRegisters.cs +++ b/MewtocolTests/AutomatedPropertyRegisters.cs @@ -19,7 +19,7 @@ namespace MewtocolTests { //corresponds to a R100 boolean register in the PLC //can also be written as R1000 because the last one is a special address - [Register(IOType.R, 100, spAdress: 0)] + [Register(IOType.R, memoryArea: 85, spAdress: 0)] public bool TestBool1 { get; private set; } //corresponds to a XD input of the PLC @@ -60,10 +60,10 @@ namespace MewtocolTests { public BitArray TestBitRegister32 { get; private set; } //corresponds to a DT1204 as a 16bit word/int takes the bit at index 9 and writes it back as a boolean - [Register(1204, 9, BitCount.B16)] + [Register(1204, BitCount.B16, 9)] public bool BitValue { get; private set; } - [Register(1204, 5, BitCount.B16)] + [Register(1204, BitCount.B32, 5)] public bool FillTest { get; private set; } //corresponds to a DT7012 - DT7013 as a 32bit time value that gets parsed as a timespan (TIME) @@ -115,7 +115,7 @@ namespace MewtocolTests { var register = interf.GetRegister(nameof(TestRegisterCollection.TestBool1)); //test generic properties - TestBasicGeneration(register, nameof(TestRegisterCollection.TestBool1), false, 100, "R100"); + TestBasicGeneration(register, nameof(TestRegisterCollection.TestBool1), false, 85, "R85"); } @@ -208,8 +208,8 @@ namespace MewtocolTests { //test generic properties TestBasicGeneration(register, nameof(TestRegisterCollection.TestString2), null!, 7005, "DT7005"); - Assert.Equal(5, ((SRegister)register).ReservedSize); - Assert.Equal(4, ((SRegister)register).MemoryLength); + Assert.Equal(5, ((BytesRegister)register).ReservedSize); + Assert.Equal(4, ((BytesRegister)register).MemoryLength); } @@ -224,8 +224,8 @@ namespace MewtocolTests { //test generic properties TestBasicGeneration(register, "Auto_Bitwise_DT7010", (short)0, 7010, "DT7010"); - Assert.True(((NRegister)register).isUsedBitwise); - Assert.Equal(0, ((NRegister)register).MemoryLength); + Assert.True(((NumberRegister)register).isUsedBitwise); + Assert.Equal(0, ((NumberRegister)register).MemoryLength); } @@ -240,8 +240,8 @@ namespace MewtocolTests { //test generic properties TestBasicGeneration(register, "Auto_Bitwise_DDT8010", (int)0, 8010, "DDT8010"); - Assert.True(((NRegister)register).isUsedBitwise); - Assert.Equal(1, ((NRegister)register).MemoryLength); + Assert.True(((NumberRegister)register).isUsedBitwise); + Assert.Equal(1, ((NumberRegister)register).MemoryLength); } @@ -256,7 +256,7 @@ namespace MewtocolTests { //test generic properties TestBasicGeneration(register, "Auto_Bitwise_DT1204", (short)0, 1204, "DT1204"); - Assert.True(((NRegister)register).isUsedBitwise); + Assert.True(((NumberRegister)register).isUsedBitwise); } diff --git a/MewtocolTests/TestRegisterBuilder.cs b/MewtocolTests/TestRegisterBuilder.cs index d72671c..24a63f3 100644 --- a/MewtocolTests/TestRegisterBuilder.cs +++ b/MewtocolTests/TestRegisterBuilder.cs @@ -16,32 +16,32 @@ public class TestRegisterBuilder { this.output = output; } - [Fact(DisplayName = "Parsing as BRegister List (Phyiscal Outputs)")] + [Fact(DisplayName = "Parsing as Bool Register List (Phyiscal Outputs)")] public void TestParsingBRegisterY() { var tests = new Dictionary() { - {"Y0", new BRegister(IOType.Y)}, - {"Y1", new BRegister(IOType.Y, 0x1)}, - {"Y2", new BRegister(IOType.Y, 0x2)}, - {"Y3", new BRegister(IOType.Y, 0x3)}, - {"Y4", new BRegister(IOType.Y, 0x4)}, - {"Y5", new BRegister(IOType.Y, 0x5)}, - {"Y6", new BRegister(IOType.Y, 0x6)}, - {"Y7", new BRegister(IOType.Y, 0x7)}, - {"Y8", new BRegister(IOType.Y, 0x8)}, - {"Y9", new BRegister(IOType.Y, 0x9)}, + {"Y0", new BoolRegister(IOType.Y)}, + {"Y1", new BoolRegister(IOType.Y, 0x1)}, + {"Y2", new BoolRegister(IOType.Y, 0x2)}, + {"Y3", new BoolRegister(IOType.Y, 0x3)}, + {"Y4", new BoolRegister(IOType.Y, 0x4)}, + {"Y5", new BoolRegister(IOType.Y, 0x5)}, + {"Y6", new BoolRegister(IOType.Y, 0x6)}, + {"Y7", new BoolRegister(IOType.Y, 0x7)}, + {"Y8", new BoolRegister(IOType.Y, 0x8)}, + {"Y9", new BoolRegister(IOType.Y, 0x9)}, - {"YA", new BRegister(IOType.Y, 0xA)}, - {"YB", new BRegister(IOType.Y, 0xB)}, - {"YC", new BRegister(IOType.Y, 0xC)}, - {"YD", new BRegister(IOType.Y, 0xD)}, - {"YE", new BRegister(IOType.Y, 0xE)}, - {"YF", new BRegister(IOType.Y, 0xF)}, + {"YA", new BoolRegister(IOType.Y, 0xA)}, + {"YB", new BoolRegister(IOType.Y, 0xB)}, + {"YC", new BoolRegister(IOType.Y, 0xC)}, + {"YD", new BoolRegister(IOType.Y, 0xD)}, + {"YE", new BoolRegister(IOType.Y, 0xE)}, + {"YF", new BoolRegister(IOType.Y, 0xF)}, - {"Y1A", new BRegister(IOType.Y, 0xA, 1)}, - {"Y10B", new BRegister(IOType.Y, 0xB, 10)}, - {"Y109C", new BRegister(IOType.Y, 0xC, 109)}, + {"Y1A", new BoolRegister(IOType.Y, 0xA, 1)}, + {"Y10B", new BoolRegister(IOType.Y, 0xB, 10)}, + {"Y109C", new BoolRegister(IOType.Y, 0xC, 109)}, }; @@ -49,32 +49,32 @@ public class TestRegisterBuilder { } - [Fact(DisplayName = "Parsing as BRegister List (Phyiscal Inputs)")] + [Fact(DisplayName = "Parsing as Bool Register List (Phyiscal Inputs)")] public void TestParsingBRegisterX() { var tests = new Dictionary() { - {"X0", new BRegister(IOType.X)}, - {"X1", new BRegister(IOType.X, 0x1)}, - {"X2", new BRegister(IOType.X, 0x2)}, - {"X3", new BRegister(IOType.X, 0x3)}, - {"X4", new BRegister(IOType.X, 0x4)}, - {"X5", new BRegister(IOType.X, 0x5)}, - {"X6", new BRegister(IOType.X, 0x6)}, - {"X7", new BRegister(IOType.X, 0x7)}, - {"X8", new BRegister(IOType.X, 0x8)}, - {"X9", new BRegister(IOType.X, 0x9)}, + {"X0", new BoolRegister(IOType.X)}, + {"X1", new BoolRegister(IOType.X, 0x1)}, + {"X2", new BoolRegister(IOType.X, 0x2)}, + {"X3", new BoolRegister(IOType.X, 0x3)}, + {"X4", new BoolRegister(IOType.X, 0x4)}, + {"X5", new BoolRegister(IOType.X, 0x5)}, + {"X6", new BoolRegister(IOType.X, 0x6)}, + {"X7", new BoolRegister(IOType.X, 0x7)}, + {"X8", new BoolRegister(IOType.X, 0x8)}, + {"X9", new BoolRegister(IOType.X, 0x9)}, - {"XA", new BRegister(IOType.X, 0xA)}, - {"XB", new BRegister(IOType.X, 0xB)}, - {"XC", new BRegister(IOType.X, 0xC)}, - {"XD", new BRegister(IOType.X, 0xD)}, - {"XE", new BRegister(IOType.X, 0xE)}, - {"XF", new BRegister(IOType.X, 0xF)}, + {"XA", new BoolRegister(IOType.X, 0xA)}, + {"XB", new BoolRegister(IOType.X, 0xB)}, + {"XC", new BoolRegister(IOType.X, 0xC)}, + {"XD", new BoolRegister(IOType.X, 0xD)}, + {"XE", new BoolRegister(IOType.X, 0xE)}, + {"XF", new BoolRegister(IOType.X, 0xF)}, - {"X1A", new BRegister(IOType.X, 0xA, 1)}, - {"X10B", new BRegister(IOType.X, 0xB, 10)}, - {"X109C", new BRegister(IOType.X, 0xC, 109)}, + {"X1A", new BoolRegister(IOType.X, 0xA, 1)}, + {"X10B", new BoolRegister(IOType.X, 0xB, 10)}, + {"X109C", new BoolRegister(IOType.X, 0xC, 109)}, }; @@ -82,35 +82,35 @@ public class TestRegisterBuilder { } - [Fact(DisplayName = "Parsing as BRegister List (Internal Relay)")] + [Fact(DisplayName = "Parsing as Bool Register List (Internal Relay)")] public void TestParsingBRegisterR() { var tests = new Dictionary() { - {"R0", new BRegister(IOType.R)}, - {"R1", new BRegister(IOType.R, 0x1)}, - {"R2", new BRegister(IOType.R, 0x2)}, - {"R3", new BRegister(IOType.R, 0x3)}, - {"R4", new BRegister(IOType.R, 0x4)}, - {"R5", new BRegister(IOType.R, 0x5)}, - {"R6", new BRegister(IOType.R, 0x6)}, - {"R7", new BRegister(IOType.R, 0x7)}, - {"R8", new BRegister(IOType.R, 0x8)}, - {"R9", new BRegister(IOType.R, 0x9)}, + {"R0", new BoolRegister(IOType.R)}, + {"R1", new BoolRegister(IOType.R, 0x1)}, + {"R2", new BoolRegister(IOType.R, 0x2)}, + {"R3", new BoolRegister(IOType.R, 0x3)}, + {"R4", new BoolRegister(IOType.R, 0x4)}, + {"R5", new BoolRegister(IOType.R, 0x5)}, + {"R6", new BoolRegister(IOType.R, 0x6)}, + {"R7", new BoolRegister(IOType.R, 0x7)}, + {"R8", new BoolRegister(IOType.R, 0x8)}, + {"R9", new BoolRegister(IOType.R, 0x9)}, - {"RA", new BRegister(IOType.R, 0xA)}, - {"RB", new BRegister(IOType.R, 0xB)}, - {"RC", new BRegister(IOType.R, 0xC)}, - {"RD", new BRegister(IOType.R, 0xD)}, - {"RE", new BRegister(IOType.R, 0xE)}, - {"RF", new BRegister(IOType.R, 0xF)}, + {"RA", new BoolRegister(IOType.R, 0xA)}, + {"RB", new BoolRegister(IOType.R, 0xB)}, + {"RC", new BoolRegister(IOType.R, 0xC)}, + {"RD", new BoolRegister(IOType.R, 0xD)}, + {"RE", new BoolRegister(IOType.R, 0xE)}, + {"RF", new BoolRegister(IOType.R, 0xF)}, - {"R1A", new BRegister(IOType.R, 0xA, 1)}, - {"R10B", new BRegister(IOType.R, 0xB, 10)}, - {"R109C", new BRegister(IOType.R, 0xC, 109)}, - {"R1000", new BRegister(IOType.R, 0x0, 100)}, - {"R511", new BRegister(IOType.R, 0x0, 511)}, - {"R511A", new BRegister(IOType.R, 0xA, 511)}, + {"R1A", new BoolRegister(IOType.R, 0xA, 1)}, + {"R10B", new BoolRegister(IOType.R, 0xB, 10)}, + {"R109C", new BoolRegister(IOType.R, 0xC, 109)}, + {"R1000", new BoolRegister(IOType.R, 0x0, 100)}, + {"R511", new BoolRegister(IOType.R, 0x0, 511)}, + {"R511A", new BoolRegister(IOType.R, 0xA, 511)}, }; @@ -126,7 +126,7 @@ public class TestRegisterBuilder { output.WriteLine($"Expected: {item.Key}"); - var built = RegBuilder.FromPlcRegName(item.Key).AsPlcType(PlcVarType.BOOL).Build(); + var built = RegBuilder.Factory.FromPlcRegName(item.Key).AsPlcType(PlcVarType.BOOL).Build(); output.WriteLine($"{(built?.ToString(true) ?? "null")}\n"); Assert.Equivalent(item.Value, built); @@ -141,63 +141,75 @@ public class TestRegisterBuilder { } - [Fact(DisplayName = "Parsing as BRegister (Casted)")] + [Fact(DisplayName = "Parsing as Bool Register (Casted)")] public void TestRegisterBuildingBoolCasted () { - var expect = new BRegister(IOType.R, 0x1, 0); - var expect2 = new BRegister(IOType.Y, 0xA, 103); + var expect = new BoolRegister(IOType.R, 0x1, 0); + var expect2 = new BoolRegister(IOType.Y, 0xA, 103); - Assert.Equivalent(expect, RegBuilder.FromPlcRegName("R1").AsPlcType(PlcVarType.BOOL).Build()); - Assert.Equivalent(expect, RegBuilder.FromPlcRegName("R1").AsType().Build()); + Assert.Equivalent(expect, RegBuilder.Factory.FromPlcRegName("R1").AsPlcType(PlcVarType.BOOL).Build()); + Assert.Equivalent(expect, RegBuilder.Factory.FromPlcRegName("R1").AsType().Build()); - Assert.Equivalent(expect2, RegBuilder.FromPlcRegName("Y103A").AsPlcType(PlcVarType.BOOL).Build()); - Assert.Equivalent(expect2, RegBuilder.FromPlcRegName("Y103A").AsType().Build()); + Assert.Equivalent(expect2, RegBuilder.Factory.FromPlcRegName("Y103A").AsPlcType(PlcVarType.BOOL).Build()); + Assert.Equivalent(expect2, RegBuilder.Factory.FromPlcRegName("Y103A").AsType().Build()); } - [Fact(DisplayName = "Parsing as BRegister (Auto)")] + [Fact(DisplayName = "Parsing as Bool Register (Auto)")] public void TestRegisterBuildingBoolAuto () { - var expect = new BRegister(IOType.R, 0x1, 0); - var expect2 = new BRegister(IOType.Y, 0xA, 103); + var expect = new BoolRegister(IOType.R, 0x1, 0); + var expect2 = new BoolRegister(IOType.Y, 0xA, 103); - Assert.Equivalent(expect, RegBuilder.FromPlcRegName("R1").Build()); - Assert.Equivalent(expect, RegBuilder.FromPlcRegName("R1").Build()); + Assert.Equivalent(expect, RegBuilder.Factory.FromPlcRegName("R1").Build()); + Assert.Equivalent(expect, RegBuilder.Factory.FromPlcRegName("R1").Build()); - Assert.Equivalent(expect2, RegBuilder.FromPlcRegName("Y103A").Build()); - Assert.Equivalent(expect2, RegBuilder.FromPlcRegName("Y103A").Build()); + Assert.Equivalent(expect2, RegBuilder.Factory.FromPlcRegName("Y103A").Build()); + Assert.Equivalent(expect2, RegBuilder.Factory.FromPlcRegName("Y103A").Build()); } - [Fact(DisplayName = "Parsing as NRegister (Casted)")] + [Fact(DisplayName = "Parsing as Number Register (Casted)")] public void TestRegisterBuildingNumericCasted() { - var expect = new NRegister(303, null); - var expect2 = new NRegister(10002, null); - var expect3 = new NRegister(400, null); + var expect = new NumberRegister(303, null); + var expect2 = new NumberRegister(10002, null); + var expect3 = new NumberRegister(400, null); //var expect4 = new NRegister(103, null, true); - Assert.Equivalent(expect, RegBuilder.FromPlcRegName("DT303").AsPlcType(PlcVarType.INT).Build()); - Assert.Equivalent(expect, RegBuilder.FromPlcRegName("DT303").AsType().Build()); + Assert.Equivalent(expect, RegBuilder.Factory.FromPlcRegName("DT303").AsPlcType(PlcVarType.INT).Build()); + Assert.Equivalent(expect, RegBuilder.Factory.FromPlcRegName("DT303").AsType().Build()); - Assert.Equivalent(expect2, RegBuilder.FromPlcRegName("DDT10002").AsPlcType(PlcVarType.DINT).Build()); - Assert.Equivalent(expect2, RegBuilder.FromPlcRegName("DDT10002").AsType().Build()); + Assert.Equivalent(expect2, RegBuilder.Factory.FromPlcRegName("DDT10002").AsPlcType(PlcVarType.DINT).Build()); + Assert.Equivalent(expect2, RegBuilder.Factory.FromPlcRegName("DDT10002").AsType().Build()); - Assert.Equivalent(expect3, RegBuilder.FromPlcRegName("DDT400").AsPlcType(PlcVarType.TIME).Build()); - Assert.Equivalent(expect3, RegBuilder.FromPlcRegName("DDT400").AsType().Build()); + Assert.Equivalent(expect3, RegBuilder.Factory.FromPlcRegName("DDT400").AsPlcType(PlcVarType.TIME).Build()); + Assert.Equivalent(expect3, RegBuilder.Factory.FromPlcRegName("DDT400").AsType().Build()); //Assert.Equivalent(expect4, RegBuilder.FromPlcRegName("DT103").AsType().Build()); } - [Fact(DisplayName = "Parsing as NRegister (Auto)")] + [Fact(DisplayName = "Parsing as Number Register (Auto)")] public void TestRegisterBuildingNumericAuto() { - var expect = new NRegister(303, null); - var expect2 = new NRegister(10002, null); + var expect = new NumberRegister(303, null); + var expect2 = new NumberRegister(10002, null); + + Assert.Equivalent(expect, RegBuilder.Factory.FromPlcRegName("DT303").Build()); + Assert.Equivalent(expect2, RegBuilder.Factory.FromPlcRegName("DDT10002").Build()); + + } + + [Fact(DisplayName = "Parsing as Bytes Register (Casted)")] + public void TestRegisterBuildingByteRangeCasted() { + + var expect = new BytesRegister(303, 5); + + + Assert.Equivalent(expect, RegBuilder.Factory.FromPlcRegName("DT303").AsPlcType(PlcVarType.INT).Build()); + Assert.Equivalent(expect, RegBuilder.Factory.FromPlcRegName("DT303").AsType().Build()); - Assert.Equivalent(expect, RegBuilder.FromPlcRegName("DT303").Build()); - Assert.Equivalent(expect2, RegBuilder.FromPlcRegName("DDT10002").Build()); } diff --git a/MewtocolTests/TestRegisterInterface.cs b/MewtocolTests/TestRegisterInterface.cs index bf11d1b..8a11726 100644 --- a/MewtocolTests/TestRegisterInterface.cs +++ b/MewtocolTests/TestRegisterInterface.cs @@ -17,12 +17,12 @@ namespace MewtocolTests { public void NumericRegisterMewtocolIdentifiers() { List registers = new List { - new NRegister(50, _name: null), - new NRegister(50, _name: null), - new NRegister(50, _name : null), - new NRegister(50, _name : null), - new NRegister(50, _name : null), - new NRegister(50, _name : null), + new NumberRegister(50, _name: null), + new NumberRegister(50, _name: null), + new NumberRegister(50, _name : null), + new NumberRegister(50, _name : null), + new NumberRegister(50, _name : null), + new NumberRegister(50, _name : null), }; List expectedIdents = new List { @@ -51,23 +51,23 @@ namespace MewtocolTests { List registers = new List { //numeric ones - new NRegister(50, _name: null), - new NRegister(60, _name : null), - new NRegister(70, _name : null), - new NRegister(80, _name : null), - new NRegister(90, _name : null), - new NRegister(100, _name : null), + new NumberRegister(50, _name: null), + new NumberRegister(60, _name : null), + new NumberRegister(70, _name : null), + new NumberRegister(80, _name : null), + new NumberRegister(90, _name : null), + new NumberRegister(100, _name : null), //boolean - new BRegister(IOType.R, 0, 100), - new BRegister(IOType.R, 0, 0), - new BRegister(IOType.X, 5), - new BRegister(IOType.X, 0xA), - new BRegister(IOType.X, 0xF, 109), - new BRegister(IOType.Y, 0xC, 75), + new BoolRegister(IOType.R, 0, 100), + new BoolRegister(IOType.R, 0, 0), + new BoolRegister(IOType.X, 5), + new BoolRegister(IOType.X, 0xA), + new BoolRegister(IOType.X, 0xF, 109), + new BoolRegister(IOType.Y, 0xC, 75), //string - new SRegister(999, 5), + new BytesRegister(999, 5), }; List expcectedIdents = new List { @@ -110,7 +110,7 @@ namespace MewtocolTests { var ex = Assert.Throws(() => { - new NRegister(100000, _name: null); + new NumberRegister(100000, _name: null); }); @@ -118,7 +118,7 @@ namespace MewtocolTests { var ex1 = Assert.Throws(() => { - new BRegister(IOType.R, _areaAdress: 512); + new BoolRegister(IOType.R, _areaAdress: 512); }); @@ -126,7 +126,7 @@ namespace MewtocolTests { var ex2 = Assert.Throws(() => { - new BRegister(IOType.X, _areaAdress: 110); + new BoolRegister(IOType.X, _areaAdress: 110); }); @@ -134,7 +134,7 @@ namespace MewtocolTests { var ex3 = Assert.Throws(() => { - new SRegister(100000, 5); + new BytesRegister(100000, 5); }); @@ -147,7 +147,7 @@ namespace MewtocolTests { var ex = Assert.Throws(() => { - new NRegister(100, _name: null); + new NumberRegister(100, _name: null); }); diff --git a/PLC_Test/test_c30_fpx_h.ini b/PLC_Test/test_c30_fpx_h.ini index e810e5053b49cd35fcbb9a00a3778ff8e0125482..90aaa531c22eca64cae0491a956aac4a25380712 100644 GIT binary patch delta 16 YcmX@Wc7Sa|4ddh`j6$3H7&kEj05-D)`2YX_ delta 14 VcmX@Wc7Sa|4I`t$=32(Xi~uKw1mFMw diff --git a/PLC_Test/test_c30_fpx_h.pro b/PLC_Test/test_c30_fpx_h.pro index fb087320a9349313ae522a4eebbcfad0389e9824..5b9447ff957c588719ffb9874c21de6929339927 100644 GIT binary patch delta 9161 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 diff --git a/PLC_Test/test_c30_fpx_h.xml b/PLC_Test/test_c30_fpx_h.xml index 4109899..9a30f61 100644 --- a/PLC_Test/test_c30_fpx_h.xml +++ b/PLC_Test/test_c30_fpx_h.xml @@ -2,10 +2,20 @@ - + - + + + + + + + + + + +