diff --git a/Examples/ExampleScenarios.cs b/Examples/ExampleScenarios.cs index a0ccdf9..bd2244a 100644 --- a/Examples/ExampleScenarios.cs +++ b/Examples/ExampleScenarios.cs @@ -4,6 +4,8 @@ using System; using System.Reflection; using System.Threading.Tasks; using System.Collections; +using MewtocolNet.RegisterBuilding; +using System.Collections.Generic; namespace Examples; diff --git a/Examples/Program.cs b/Examples/Program.cs index c3bd799..a868993 100644 --- a/Examples/Program.cs +++ b/Examples/Program.cs @@ -1,4 +1,6 @@ -using System; +using MewtocolNet.RegisterBuilding; +using MewtocolNet; +using System; using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -12,6 +14,9 @@ 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/MewtocolNet/CpuInfo.cs b/MewtocolNet/CpuInfo.cs index 4f21723..001d491 100644 --- a/MewtocolNet/CpuInfo.cs +++ b/MewtocolNet/CpuInfo.cs @@ -1,5 +1,4 @@ -using MewtocolNet.PLCEnums; -using System; +using System; namespace MewtocolNet { diff --git a/MewtocolNet/IRegister.cs b/MewtocolNet/IRegister.cs index c771084..f68d60d 100644 --- a/MewtocolNet/IRegister.cs +++ b/MewtocolNet/IRegister.cs @@ -12,6 +12,11 @@ namespace MewtocolNet { /// event Action ValueChanged; + /// + /// Type of the underlying register + /// + RegisterType RegisterType { get; } + /// /// The name of the register /// @@ -27,6 +32,12 @@ namespace MewtocolNet { /// int MemoryAddress { get; } + /// + /// Gets the special address of the register or -1 if it has none + /// + /// + byte? GetSpecialAddress(); + /// /// Indicates if the register is processed bitwise /// @@ -93,6 +104,11 @@ namespace MewtocolNet { /// string ToString(); + /// + /// Builds a readable string with all important register informations and additional infos + /// + string ToString(bool detailed); + } } diff --git a/MewtocolNet/MewtocolHelpers.cs b/MewtocolNet/MewtocolHelpers.cs index e3f0d2f..faee445 100644 --- a/MewtocolNet/MewtocolHelpers.cs +++ b/MewtocolNet/MewtocolHelpers.cs @@ -239,14 +239,23 @@ namespace MewtocolNet { } /// - /// Checks if the register type is non numeric + /// Checks if the register type is boolean /// - internal static bool IsBoolean(this RegisterType type) { + internal static bool IsBoolean (this RegisterType type) { return type == RegisterType.X || type == RegisterType.Y || type == RegisterType.R; } + /// + /// Checks if the register type numeric + /// + internal static bool IsNumericDTDDT (this RegisterType type) { + + return type == RegisterType.DT || type == RegisterType.DDT; + + } + /// /// Checks if the register type is an physical in or output of the plc /// diff --git a/MewtocolNet/MewtocolInterfaceRequests.cs b/MewtocolNet/MewtocolInterfaceRequests.cs index 90aab30..caa9e5c 100644 --- a/MewtocolNet/MewtocolInterfaceRequests.cs +++ b/MewtocolNet/MewtocolInterfaceRequests.cs @@ -1,5 +1,4 @@ using MewtocolNet.Logging; -using MewtocolNet.PLCEnums; using MewtocolNet.Registers; using System; using System.Collections.Generic; diff --git a/MewtocolNet/PLCEnums/CpuType.cs b/MewtocolNet/PLCEnums/CpuType.cs index cc5bcef..9312177 100644 --- a/MewtocolNet/PLCEnums/CpuType.cs +++ b/MewtocolNet/PLCEnums/CpuType.cs @@ -1,4 +1,4 @@ -namespace MewtocolNet.PLCEnums { +namespace MewtocolNet { /// /// CPU type of the PLC diff --git a/MewtocolNet/PLCEnums/OPMode.cs b/MewtocolNet/PLCEnums/OPMode.cs index 9ae73f1..e760230 100644 --- a/MewtocolNet/PLCEnums/OPMode.cs +++ b/MewtocolNet/PLCEnums/OPMode.cs @@ -1,4 +1,4 @@ -namespace MewtocolNet.PLCEnums { +namespace MewtocolNet { /// /// CPU type of the PLC diff --git a/MewtocolNet/PLCEnums/PlcVarType.cs b/MewtocolNet/PLCEnums/PlcVarType.cs new file mode 100644 index 0000000..d3e13a2 --- /dev/null +++ b/MewtocolNet/PLCEnums/PlcVarType.cs @@ -0,0 +1,18 @@ +using System.Text; + +namespace MewtocolNet { + + public enum PlcVarType { + + BOOL, + INT, + UINT, + DINT, + UDINT, + REAL, + TIME, + STRING + + } + +} diff --git a/MewtocolNet/PLCEnums/PlcVarTypeConversions.cs b/MewtocolNet/PLCEnums/PlcVarTypeConversions.cs new file mode 100644 index 0000000..4c6653b --- /dev/null +++ b/MewtocolNet/PLCEnums/PlcVarTypeConversions.cs @@ -0,0 +1,91 @@ +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/RegisterBuilding/FinalizerExtensions.cs b/MewtocolNet/RegisterBuilding/FinalizerExtensions.cs new file mode 100644 index 0000000..b42a30e --- /dev/null +++ b/MewtocolNet/RegisterBuilding/FinalizerExtensions.cs @@ -0,0 +1,97 @@ +using MewtocolNet.Registers; +using System; +using System.Reflection; + +namespace MewtocolNet.RegisterBuilding { + + public static class FinalizerExtensions { + + public static IRegister Build (this RegisterBuilderStep step) { + + //if no casting method in builder was called => autocast the type from the RegisterType + if (!step.wasCasted) + step.AutoType(); + + bool isBoolean = step.RegType.IsBoolean(); + bool isTypeNotDefined = step.plcVarType == null && step.dotnetVarType == null; + + //fallbacks if no casting builder was given + if (isTypeNotDefined && step.RegType == RegisterType.DT) { + + step.dotnetVarType = typeof(short); + + } + if (isTypeNotDefined && step.RegType == RegisterType.DDT) { + + step.dotnetVarType = typeof(int); + + } else if (isTypeNotDefined && isBoolean) { + + step.dotnetVarType = typeof(bool); + + } else if (isTypeNotDefined && step.RegType == RegisterType.DT_START) { + + step.dotnetVarType = typeof(string); + + } + + if(step.plcVarType != null) { + + step.dotnetVarType = step.plcVarType.Value.ToDotnetType(); + + } + + //as numeric register + if (step.RegType.IsNumericDTDDT()) { + + if(step.plcVarType == null && step.dotnetVarType != null) { + + step.plcVarType = step.dotnetVarType.ToPlcVarType(); + + } + + var type = step.plcVarType.Value.ToRegisterType(); + + var areaAddr = step.MemAddress; + var name = step.Name; + + //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); + + 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"); + + } + + } + +} diff --git a/MewtocolNet/RegisterBuilding/ParseResult.cs b/MewtocolNet/RegisterBuilding/ParseResult.cs new file mode 100644 index 0000000..d548d2c --- /dev/null +++ b/MewtocolNet/RegisterBuilding/ParseResult.cs @@ -0,0 +1,12 @@ +namespace MewtocolNet.RegisterBuilding { + internal struct ParseResult { + + public ParseResultState state; + + public string hardFailReason; + + public RegisterBuilderStep stepData; + + } + +} diff --git a/MewtocolNet/RegisterBuilding/ParseResultState.cs b/MewtocolNet/RegisterBuilding/ParseResultState.cs new file mode 100644 index 0000000..142ac3c --- /dev/null +++ b/MewtocolNet/RegisterBuilding/ParseResultState.cs @@ -0,0 +1,19 @@ +namespace MewtocolNet.RegisterBuilding { + internal enum ParseResultState { + + /// + /// The parse try failed at the intial regex match + /// + FailedSoft, + /// + /// The parse try failed at the afer- regex match + /// + FailedHard, + /// + /// The parse try did work + /// + Success, + + } + +} diff --git a/MewtocolNet/RegisterBuilding/RegBuilder.cs b/MewtocolNet/RegisterBuilding/RegBuilder.cs new file mode 100644 index 0000000..ce91a17 --- /dev/null +++ b/MewtocolNet/RegisterBuilding/RegBuilder.cs @@ -0,0 +1,184 @@ +using System; +using System.Collections.Generic; +using System.Data.Common; +using System.Globalization; +using System.Text.RegularExpressions; + +namespace MewtocolNet.RegisterBuilding { + + /// + /// Contains useful tools for register creation + /// + public static class RegBuilder { + + //methods to test the input string on + private static List> parseMethods = new List>() { + + (x) => TryBuildBoolean(x), + (x) => TryBuildNumericBased(x), + + }; + + public static RegisterBuilderStep FromPlcRegName (string plcAddrName, string name = null) { + + foreach (var method in parseMethods) { + + var res = method.Invoke(plcAddrName); + + if(res.state == ParseResultState.Success) { + + if (!string.IsNullOrEmpty(name)) + res.stepData.Name = name; + + res.stepData.OriginalInput = plcAddrName; + + return res.stepData; + + } else if(res.state == ParseResultState.FailedHard) { + + throw new Exception(res.hardFailReason); + + } + + } + + throw new Exception("Wrong input format"); + + } + + //bool registers + private static ParseResult TryBuildBoolean (string plcAddrName) { + + //regex to find special register values + var patternBool = new Regex(@"(?X|Y|R)(?[0-9]{0,3})(?(?:[0-9]|[A-F]){1})?"); + + var match = patternBool.Match(plcAddrName); + + if (!match.Success) + return new ParseResult { + state = ParseResultState.FailedSoft + }; + + string prefix = match.Groups["prefix"].Value; + string area = match.Groups["area"].Value; + string special = match.Groups["special"].Value; + + IOType regType; + int areaAdd = 0; + byte specialAdd = 0x0; + + //try cast the prefix + if(!Enum.TryParse(prefix, out regType)) { + + return new ParseResult { + state = ParseResultState.FailedHard, + hardFailReason = $"Cannot parse '{plcAddrName}', the prefix is not allowed for boolean registers" + }; + + } + + if(!string.IsNullOrEmpty(area) && !int.TryParse(area, out areaAdd) ) { + + return new ParseResult { + state = ParseResultState.FailedHard, + hardFailReason = $"Cannot parse '{plcAddrName}', the area address: '{area}' is wrong" + }; + + } + + //special address not given + if(string.IsNullOrEmpty(special) && !string.IsNullOrEmpty(area)) { + + var isAreaInt = int.TryParse(area, NumberStyles.Number, CultureInfo.InvariantCulture, out var areaInt); + + if (isAreaInt && areaInt >= 0 && areaInt <= 9) { + + //area address is actually meant as special address but 0-9 + specialAdd = (byte)areaInt; + areaAdd = 0; + + + } else if (isAreaInt && areaInt > 9) { + + //area adress is meant to be the actual area address + areaAdd = areaInt; + specialAdd = 0; + + } else { + + return new ParseResult { + state = ParseResultState.FailedHard, + hardFailReason = $"Cannot parse '{plcAddrName}', the special address: '{special}' is wrong 1", + }; + + } + + } else { + + //special address parsed as hex num + if (!byte.TryParse(special, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out specialAdd)) { + + return new ParseResult { + state = ParseResultState.FailedHard, + hardFailReason = $"Cannot parse '{plcAddrName}', the special address: '{special}' is wrong 2", + }; + + } + + } + + return new ParseResult { + state = ParseResultState.Success, + stepData = new RegisterBuilderStep ((RegisterType)(int)regType, areaAdd, specialAdd), + }; + + } + + // one to two word registers + private static ParseResult TryBuildNumericBased (string plcAddrName) { + + var patternByte = new Regex(@"(?DT|DDT)(?[0-9]{1,5})"); + + var match = patternByte.Match(plcAddrName); + + if (!match.Success) + return new ParseResult { + state = ParseResultState.FailedSoft + }; + + + string prefix = match.Groups["prefix"].Value; + string area = match.Groups["area"].Value; + + RegisterType regType; + int areaAdd = 0; + + //try cast the prefix + if (!Enum.TryParse(prefix, out regType)) { + + return new ParseResult { + state = ParseResultState.FailedHard, + hardFailReason = $"Cannot parse '{plcAddrName}', the prefix is not allowed for numeric registers" + }; + + } + + if (!string.IsNullOrEmpty(area) && !int.TryParse(area, out areaAdd)) { + + return new ParseResult { + state = ParseResultState.FailedHard, + hardFailReason = $"Cannot parse '{plcAddrName}', the area address: '{area}' is wrong" + }; + + } + + return new ParseResult { + state = ParseResultState.Success, + stepData = new RegisterBuilderStep(regType, areaAdd), + }; + + } + + } + +} diff --git a/MewtocolNet/RegisterBuilding/RegisterBuilder.cs b/MewtocolNet/RegisterBuilding/RegisterBuilder.cs deleted file mode 100644 index 96cbf67..0000000 --- a/MewtocolNet/RegisterBuilding/RegisterBuilder.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace MewtocolNet.RegisterBuilding { - - /// - /// Contains useful tools for register creation - /// - public static class RegisterBuilder { - - /// - /// Parses a register from its PLC name - /// - /// The name, fe. DT100 - /// An or null if - /// True if successfully parsed - public static bool TryBuildFromName(string name, out IRegister reg) { - - reg = null; - - return false; - - } - - } - -} diff --git a/MewtocolNet/RegisterBuilding/RegisterBuilderStep.cs b/MewtocolNet/RegisterBuilding/RegisterBuilderStep.cs new file mode 100644 index 0000000..e1c2103 --- /dev/null +++ b/MewtocolNet/RegisterBuilding/RegisterBuilderStep.cs @@ -0,0 +1,92 @@ +using System; + +namespace MewtocolNet.RegisterBuilding { + public class RegisterBuilderStep { + + internal bool wasCasted = false; + + internal string OriginalInput; + + internal string Name; + internal RegisterType RegType; + internal int MemAddress; + internal byte? SpecialAddress; + + 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; + + } + + internal RegisterBuilderStep(RegisterType regType, int memAddr, byte specialAddr) { + + RegType = regType; + MemAddress = memAddr; + SpecialAddress = specialAddr; + + } + + public RegisterBuilderStep AsPlcType (PlcVarType varType) { + + dotnetVarType = null; + plcVarType = varType; + + wasCasted = true; + + return this; + + } + + public RegisterBuilderStep AsType () { + + if(!typeof(T).IsAllowedPlcCastingType()) { + + throw new NotSupportedException($"The dotnet type {typeof(T)}, is not supported for PLC type casting"); + + } + + dotnetVarType = typeof(T); + plcVarType = null; + + wasCasted = true; + + return this; + + } + + internal RegisterBuilderStep AutoType () { + + switch (RegType) { + case RegisterType.X: + case RegisterType.Y: + case RegisterType.R: + dotnetVarType = typeof(bool); + break; + case RegisterType.DT: + dotnetVarType = typeof(short); + break; + case RegisterType.DDT: + dotnetVarType = typeof(int); + break; + case RegisterType.DT_START: + dotnetVarType = typeof(string); + break; + } + + plcVarType = null; + + wasCasted = true; + + return this; + + } + + } + +} diff --git a/MewtocolNet/RegisterEnums.cs b/MewtocolNet/RegisterEnums.cs index 55c3528..1195f35 100644 --- a/MewtocolNet/RegisterEnums.cs +++ b/MewtocolNet/RegisterEnums.cs @@ -18,28 +18,23 @@ /// R = 2, /// - /// Data area as a short (Register) + /// Single word area (Register) /// - DT_short = 3, + DT = 3, /// - /// Data area as an unsigned short (Register) + /// Double word area (Register) /// - DT_ushort = 4, + DDT = 4, /// - /// Double data area as an integer (Register) + /// Start area of a byte sequence longer than 2 words /// - DDT_int = 5, - /// - /// Double data area as an unsigned integer (Register) - /// - DDT_uint = 6, - /// - /// Double data area as an floating point number (Register) - /// - DDT_float = 7, + DT_START = 5, } + // this is just used as syntactic sugar, + // when creating registers that are R/X/Y typed you dont need the DT types + /// /// The type of an input/output register /// diff --git a/MewtocolNet/Registers/BRegister.cs b/MewtocolNet/Registers/BRegister.cs index d42cfd4..3e83adb 100644 --- a/MewtocolNet/Registers/BRegister.cs +++ b/MewtocolNet/Registers/BRegister.cs @@ -19,7 +19,7 @@ namespace MewtocolNet.Registers { /// public event PropertyChangedEventHandler PropertyChanged; - internal RegisterType RegType { get; private set; } + public RegisterType RegisterType { get; private set; } internal Type collectionType; @@ -80,7 +80,7 @@ namespace MewtocolNet.Registers { specialAddress = _spAddress; name = _name; - RegType = (RegisterType)(int)_io; + RegisterType = (RegisterType)(int)_io; } @@ -91,13 +91,15 @@ namespace MewtocolNet.Registers { } + public byte? GetSpecialAddress() => SpecialAddress; + /// /// Builds the register area name /// public string BuildMewtocolQuery() { //build area code from register type - StringBuilder asciistring = new StringBuilder(RegType.ToString()); + StringBuilder asciistring = new StringBuilder(RegisterType.ToString()); string memPadded = MemoryAddress.ToString().PadLeft(4, '0'); string sp = SpecialAddress.ToString("X1"); @@ -127,11 +129,13 @@ namespace MewtocolNet.Registers { public Type GetCollectionType() => CollectionType; + public RegisterType GetRegisterType() => RegisterType; + public string GetValueString() => Value.ToString(); public void ClearValue() => SetValueFromPLC(false); - public string GetRegisterString() => RegType.ToString(); + public string GetRegisterString() => RegisterType.ToString(); public string GetCombinedName() => $"{(CollectionType != null ? $"{CollectionType.Name}." : "")}{Name ?? "Unnamed"}"; @@ -163,6 +167,22 @@ namespace MewtocolNet.Registers { public override string ToString() => $"{GetRegisterPLCName()} - Value: {GetValueString()}"; + public string ToString(bool additional) { + + if (!additional) return this.ToString(); + + StringBuilder sb = new StringBuilder(); + sb.AppendLine($"PLC Naming: {GetRegisterPLCName()}"); + sb.AppendLine($"Name: {Name ?? "Not named"}"); + sb.AppendLine($"Value: {GetValueString()}"); + sb.AppendLine($"Register Type: {RegisterType}"); + sb.AppendLine($"Memory Address: {MemoryAddress}"); + sb.AppendLine($"Special Address: {SpecialAddress:X1}"); + + return sb.ToString(); + + } + } } diff --git a/MewtocolNet/Registers/NRegister.cs b/MewtocolNet/Registers/NRegister.cs index 5550e5a..c36c0d4 100644 --- a/MewtocolNet/Registers/NRegister.cs +++ b/MewtocolNet/Registers/NRegister.cs @@ -22,6 +22,8 @@ namespace MewtocolNet.Registers { /// public event PropertyChangedEventHandler PropertyChanged; + public RegisterType RegisterType { get; private set; } + internal Type collectionType; /// @@ -63,7 +65,7 @@ namespace MewtocolNet.Registers { /// /// Memory start adress max 99999 /// Name of the register - public NRegister(int _adress, string _name = null) { + public NRegister (int _adress, string _name = null) { if (_adress > 99999) throw new NotSupportedException("Memory adresses cant be greater than 99999"); @@ -87,9 +89,16 @@ namespace MewtocolNet.Registers { throw new NotSupportedException($"The type {numType} is not allowed for Number Registers"); } + //set register type + if(memoryLength == 1) { + RegisterType = RegisterType.DDT; + } else { + RegisterType = RegisterType.DT; + } + } - internal NRegister(int _adress, string _name = null, bool isBitwise = false, Type _enumType = null) { + public NRegister (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; @@ -111,6 +120,13 @@ namespace MewtocolNet.Registers { throw new NotSupportedException($"The type {numType} is not allowed for Number Registers"); } + //set register type + if (memoryLength == 1) { + RegisterType = RegisterType.DDT; + } else { + RegisterType = RegisterType.DT; + } + isUsedBitwise = isBitwise; enumType = _enumType; @@ -131,6 +147,8 @@ namespace MewtocolNet.Registers { } + public byte? GetSpecialAddress() => null; + public string GetStartingMemoryArea() => MemoryAddress.ToString(); public Type GetCollectionType() => CollectionType; @@ -267,8 +285,12 @@ namespace MewtocolNet.Registers { public void TriggerNotifyChange() => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Value")); + public RegisterType GetRegisterType() => RegisterType; + public override string ToString() => $"{GetRegisterPLCName()} - Value: {GetValueString()}"; + public string ToString(bool additional) => $"{GetRegisterPLCName()} - Value: {GetValueString()}"; + } } diff --git a/MewtocolNet/Registers/SRegister.cs b/MewtocolNet/Registers/SRegister.cs index 00e7b2a..91b6ca4 100644 --- a/MewtocolNet/Registers/SRegister.cs +++ b/MewtocolNet/Registers/SRegister.cs @@ -19,6 +19,8 @@ namespace MewtocolNet.Registers { /// public event PropertyChangedEventHandler PropertyChanged; + public RegisterType RegisterType { get; private set; } + internal Type collectionType; /// @@ -69,10 +71,12 @@ namespace MewtocolNet.Registers { wordsize++; } + RegisterType = RegisterType.DT_START; + memoryLength = (int)Math.Round(wordsize + 1); } - internal SRegister WithCollectionType(Type colType) { + internal SRegister WithCollectionType (Type colType) { collectionType = colType; return this; @@ -105,6 +109,8 @@ namespace MewtocolNet.Registers { return asciistring.ToString(); } + public byte? GetSpecialAddress() => null; + public Type GetCollectionType() => CollectionType; public bool IsUsedBitwise() => false; @@ -138,6 +144,8 @@ namespace MewtocolNet.Registers { public override string ToString() => $"{GetRegisterPLCName()} - Value: {GetValueString()}"; + public string ToString(bool additional) => $"{GetRegisterPLCName()} - Value: {GetValueString()}"; + } } diff --git a/MewtocolTests/TestLivePLC.cs b/MewtocolTests/TestLivePLC.cs index b4d40f1..420e5d4 100644 --- a/MewtocolTests/TestLivePLC.cs +++ b/MewtocolTests/TestLivePLC.cs @@ -1,6 +1,5 @@ using MewtocolNet; using MewtocolNet.Logging; -using MewtocolNet.PLCEnums; using Xunit; using Xunit.Abstractions; diff --git a/MewtocolTests/TestRegisterBuilder.cs b/MewtocolTests/TestRegisterBuilder.cs new file mode 100644 index 0000000..d72671c --- /dev/null +++ b/MewtocolTests/TestRegisterBuilder.cs @@ -0,0 +1,204 @@ +using MewtocolNet; +using MewtocolNet.RegisterBuilding; +using MewtocolNet.Registers; +using System.Collections; +using Xunit; +using Xunit.Abstractions; +using static System.Net.Mime.MediaTypeNames; + +namespace MewtocolTests; + +public class TestRegisterBuilder { + + private readonly ITestOutputHelper output; + + public TestRegisterBuilder(ITestOutputHelper output) { + this.output = output; + } + + [Fact(DisplayName = "Parsing as BRegister 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)}, + + {"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)}, + + {"Y1A", new BRegister(IOType.Y, 0xA, 1)}, + {"Y10B", new BRegister(IOType.Y, 0xB, 10)}, + {"Y109C", new BRegister(IOType.Y, 0xC, 109)}, + + }; + + TestBoolDict(tests); + + } + + [Fact(DisplayName = "Parsing as BRegister 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)}, + + {"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)}, + + {"X1A", new BRegister(IOType.X, 0xA, 1)}, + {"X10B", new BRegister(IOType.X, 0xB, 10)}, + {"X109C", new BRegister(IOType.X, 0xC, 109)}, + + }; + + TestBoolDict(tests); + + } + + [Fact(DisplayName = "Parsing as BRegister 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)}, + + {"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)}, + + {"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)}, + + }; + + TestBoolDict(tests); + + } + + private void TestBoolDict (Dictionary dict) { + + foreach (var item in dict) { + + try { + + output.WriteLine($"Expected: {item.Key}"); + + var built = RegBuilder.FromPlcRegName(item.Key).AsPlcType(PlcVarType.BOOL).Build(); + + output.WriteLine($"{(built?.ToString(true) ?? "null")}\n"); + Assert.Equivalent(item.Value, built); + + } catch (Exception ex) { + + output.WriteLine(ex.Message.ToString()); + + } + + } + + } + + [Fact(DisplayName = "Parsing as BRegister (Casted)")] + public void TestRegisterBuildingBoolCasted () { + + var expect = new BRegister(IOType.R, 0x1, 0); + var expect2 = new BRegister(IOType.Y, 0xA, 103); + + Assert.Equivalent(expect, RegBuilder.FromPlcRegName("R1").AsPlcType(PlcVarType.BOOL).Build()); + Assert.Equivalent(expect, RegBuilder.FromPlcRegName("R1").AsType().Build()); + + Assert.Equivalent(expect2, RegBuilder.FromPlcRegName("Y103A").AsPlcType(PlcVarType.BOOL).Build()); + Assert.Equivalent(expect2, RegBuilder.FromPlcRegName("Y103A").AsType().Build()); + + } + + [Fact(DisplayName = "Parsing as BRegister (Auto)")] + public void TestRegisterBuildingBoolAuto () { + + var expect = new BRegister(IOType.R, 0x1, 0); + var expect2 = new BRegister(IOType.Y, 0xA, 103); + + Assert.Equivalent(expect, RegBuilder.FromPlcRegName("R1").Build()); + Assert.Equivalent(expect, RegBuilder.FromPlcRegName("R1").Build()); + + Assert.Equivalent(expect2, RegBuilder.FromPlcRegName("Y103A").Build()); + Assert.Equivalent(expect2, RegBuilder.FromPlcRegName("Y103A").Build()); + + } + + [Fact(DisplayName = "Parsing as NRegister (Casted)")] + public void TestRegisterBuildingNumericCasted() { + + var expect = new NRegister(303, null); + var expect2 = new NRegister(10002, null); + var expect3 = new NRegister(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(expect2, RegBuilder.FromPlcRegName("DDT10002").AsPlcType(PlcVarType.DINT).Build()); + Assert.Equivalent(expect2, RegBuilder.FromPlcRegName("DDT10002").AsType().Build()); + + Assert.Equivalent(expect3, RegBuilder.FromPlcRegName("DDT400").AsPlcType(PlcVarType.TIME).Build()); + Assert.Equivalent(expect3, RegBuilder.FromPlcRegName("DDT400").AsType().Build()); + + //Assert.Equivalent(expect4, RegBuilder.FromPlcRegName("DT103").AsType().Build()); + + } + + [Fact(DisplayName = "Parsing as NRegister (Auto)")] + public void TestRegisterBuildingNumericAuto() { + + var expect = new NRegister(303, null); + var expect2 = new NRegister(10002, null); + + Assert.Equivalent(expect, RegBuilder.FromPlcRegName("DT303").Build()); + Assert.Equivalent(expect2, RegBuilder.FromPlcRegName("DDT10002").Build()); + + } + +} diff --git a/MewtocolTests/TestRegisterParsing.cs b/MewtocolTests/TestRegisterParsing.cs deleted file mode 100644 index 41687f1..0000000 --- a/MewtocolTests/TestRegisterParsing.cs +++ /dev/null @@ -1,78 +0,0 @@ -using MewtocolNet; -using MewtocolNet.Registers; -using Xunit; -using Xunit.Abstractions; - -namespace MewtocolTests; - -public class TestRegisterParsing { - - private readonly ITestOutputHelper output; - - public TestRegisterParsing(ITestOutputHelper output) { - this.output = output; - } - - [Fact(DisplayName = "Parsing as BRegister (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)}, - - {"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)}, - - {"Y1A", new BRegister(IOType.Y, 0xA, 1)}, - {"Y10B", new BRegister(IOType.Y, 0xB, 10)}, - {"Y109C", new BRegister(IOType.Y, 0xC, 109)}, - - }; - - } - - [Fact(DisplayName = "Parsing as BRegister (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)}, - - {"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)}, - - {"X1A", new BRegister(IOType.X, 0xA, 1)}, - {"X10B", new BRegister(IOType.X, 0xB, 10)}, - {"X109C", new BRegister(IOType.X, 0xC, 109)}, - - }; - - } - -}