diff --git a/Examples/ExampleScenarios.cs b/Examples/ExampleScenarios.cs new file mode 100644 index 0000000..428d212 --- /dev/null +++ b/Examples/ExampleScenarios.cs @@ -0,0 +1,173 @@ +using MewtocolNet.Logging; +using MewtocolNet; +using System; +using System.Reflection; +using System.Threading.Tasks; +using System.Collections; + +namespace Examples; + +public class ExampleScenarios { + + public static bool MewtocolLoggerEnabled = false; + + public void SetupLogger () { + + //attaching the logger + Logger.LogLevel = LogLevel.Verbose; + Logger.OnNewLogMessage((date, msg) => { + if(MewtocolLoggerEnabled) + Console.WriteLine($"{date.ToString("HH:mm:ss")} {msg}"); + }); + + } + + [Scenario("Permament connection with poller")] + public async Task RunCyclicPollerAsync () { + + Console.WriteLine("Starting poller scenario"); + + int runTime = 10000; + int remainingTime = runTime; + + //setting up a new PLC interface and register collection + MewtocolInterface interf = new MewtocolInterface("192.168.115.210"); + TestRegisters registers = new TestRegisters(); + + //attaching the register collection and an automatic poller + interf.WithRegisterCollection(registers).WithPoller(); + + await interf.ConnectAsync(); + + _ = Task.Factory.StartNew(async () => { + + while (interf.IsConnected) { + + //flip the bool register each tick and wait for it to be registered + await interf.SetRegisterAsync(nameof(registers.TestBool1), !registers.TestBool1); + + Console.Title = $"Polling Paused: {interf.PollingPaused}, " + + $"Poller active: {interf.PollerActive}, " + + $"Speed UP: {interf.BytesPerSecondUpstream} B/s, " + + $"Speed DOWN: {interf.BytesPerSecondDownstream} B/s, " + + $"Poll delay: {interf.PollerDelayMs} ms, " + + $"Queued MSGs: {interf.QueuedMessages}"; + + Console.Clear(); + Console.WriteLine("Underlying registers on tick: \n"); + + foreach (var register in interf.Registers) { + + Console.WriteLine($"{register.GetCombinedName()} / {register.GetRegisterPLCName()} - Value: {register.GetValueString()}"); + + } + + Console.WriteLine($"{registers.TestBool1}"); + Console.WriteLine($"{registers.TestDuplicate}"); + + remainingTime -= 1000; + + Console.WriteLine($"\nStopping in: {remainingTime}ms"); + + await Task.Delay(1000); + + } + + }); + + await Task.Delay(runTime); + interf.Disconnect(); + + } + + [Scenario("Dispose and disconnect connection")] + public async Task RunDisposalAndDisconnectAsync () { + + //automatic disposal + using (var interf = new MewtocolInterface("192.168.115.210")) { + + await interf.ConnectAsync(); + + if (interf.IsConnected) { + + Console.WriteLine("Opened connection"); + + await Task.Delay(5000); + + } + + } + + Console.WriteLine("Disposed, closed connection"); + + //manual close + var interf2 = new MewtocolInterface("192.168.115.210"); + + await interf2.ConnectAsync(); + + if (interf2.IsConnected) { + + Console.WriteLine("Opened connection"); + + await Task.Delay(5000); + + } + + interf2.Disconnect(); + + Console.WriteLine("Disconnected, closed connection"); + + } + + [Scenario("Test auto enums and bitwise, needs the example program from MewtocolNet/PLC_Test")] + public async Task RunEnumsBitwiseAsync () { + + Console.WriteLine("Starting auto enums and bitwise"); + + //setting up a new PLC interface and register collection + MewtocolInterface interf = new MewtocolInterface("192.168.115.210"); + TestRegistersEnumBitwise registers = new TestRegistersEnumBitwise(); + + //attaching the register collection and an automatic poller + interf.WithRegisterCollection(registers).WithPoller(); + + registers.PropertyChanged += (s, e) => { + + Console.Clear(); + + var props = registers.GetType().GetProperties(); + + foreach (var prop in props) { + + var val = prop.GetValue(registers); + string printVal = val?.ToString() ?? "null"; + + if (val is BitArray bitarr) { + printVal = bitarr.ToBitString(); + } + + Console.Write($"{prop.Name} - "); + + if(printVal == "True") { + Console.ForegroundColor = ConsoleColor.Green; + } + + Console.Write($"{printVal}"); + + Console.ResetColor(); + + Console.WriteLine(); + + } + + }; + + await interf.ConnectAsync(); + + await interf.SetRegisterAsync(nameof(registers.StartCyclePLC), true); + + await Task.Delay(-1); + + } + +} diff --git a/Examples/Program.cs b/Examples/Program.cs index e7531f7..c3bd799 100644 --- a/Examples/Program.cs +++ b/Examples/Program.cs @@ -1,14 +1,15 @@ using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; using System.Threading.Tasks; -using MewtocolNet; -using MewtocolNet.Logging; -using MewtocolNet.Registers; -using static System.Net.Mime.MediaTypeNames; namespace Examples; class Program { + static ExampleScenarios ExampleSzenarios = new ExampleScenarios(); + static void Main(string[] args) { AppDomain.CurrentDomain.UnhandledException += (s,e) => { @@ -19,212 +20,81 @@ class Program { Console.WriteLine(e.Exception.ToString()); }; - Console.WriteLine("Enter your scenario number:\n" + - "1 = Permanent connection\n" + - "2 = Dispose connection"); + ExampleSzenarios.SetupLogger(); + + LoopInput(); + + } + + private static void LoopInput () { + + Console.WriteLine("All available scenarios\n"); + + var methods = ExampleSzenarios.GetType().GetMethods(); + var invokeableMethods = new List(); + + for (int i = 0, j = 0; i < methods.Length; i++) { + + MethodInfo method = methods[i]; + var foundAtt = method.GetCustomAttribute(typeof(ScenarioAttribute)); + + if(foundAtt != null && foundAtt is ScenarioAttribute att) { + + Console.WriteLine($"[{j + 1}] {method.Name}() - {att.Description}"); + invokeableMethods.Add(method); + + j++; + + } + + } + + Console.ForegroundColor = ConsoleColor.Yellow; + Console.WriteLine("\nEnter a number to excecute a example"); + Console.ResetColor(); + + Console.WriteLine("\nOther possible commands: \n\n" + + "'toggle logger' - toggle the built in mewtocol logger on/off\n" + + "'exit' - to close this program \n" + + "'clear' - to clear the output \n"); + + Console.Write("> "); var line = Console.ReadLine(); - if(line == "1") { - Scenario1(); + if (line == "toggle logger") { + + ExampleScenarios.MewtocolLoggerEnabled = !ExampleScenarios.MewtocolLoggerEnabled; + + Console.WriteLine(ExampleScenarios.MewtocolLoggerEnabled ? "Logger enabled" : "Logger disabled"); + + } else if (line == "exit") { + + Environment.Exit(0); + + } else if (line == "clear") { + + Console.Clear(); + + } else if (int.TryParse(line, out var lineNum)) { + + var index = Math.Clamp(lineNum - 1, 0, invokeableMethods.Count - 1); + + var task = (Task)invokeableMethods.ElementAt(index).Invoke(ExampleSzenarios, null); + + task.Wait(); + + Console.ForegroundColor = ConsoleColor.Green; + Console.WriteLine("The program ran to completition"); + Console.ResetColor(); + + } else { + + Console.WriteLine("Wrong input"); + } - if (line == "2") { - Scenario2(); - } - - Console.ReadLine(); - } - - private static bool isProgressReadout = false; - - static void Scenario1 () { - - Task.Factory.StartNew(async () => { - - //attaching the logger - Logger.LogLevel = LogLevel.Verbose; - Logger.OnNewLogMessage((date, msg) => { - Console.WriteLine($"{date.ToString("HH:mm:ss")} {msg}"); - }); - - //setting up a new PLC interface and register collection - MewtocolInterface interf = new MewtocolInterface("10.237.191.3"); - TestRegisters registers = new TestRegisters(); - - //attaching the register collection and an automatic poller - interf.WithRegisterCollection(registers).WithPoller(); - - _ = Task.Factory.StartNew(async () => { - while (true) { - if (isProgressReadout) continue; - Console.Title = $"Polling Paused: {interf.PollingPaused}, " + - $"Poller active: {interf.PollerActive}, " + - $"Speed UP: {interf.BytesPerSecondUpstream} B/s, " + - $"Speed DOWN: {interf.BytesPerSecondDownstream} B/s, " + - $"Poll delay: {interf.PollerDelayMs} ms, " + - $"Queued MSGs: {interf.QueuedMessages}"; - await Task.Delay(1000); - } - }); - - await interf.ConnectAsync(); - - //bool flip = false; - //while(true) { - - // if(!flip) { - // await interf.ConnectAsync(); - // } else { - // interf.Disconnect(); - // } - - // flip = !flip; - // await Task.Delay(5000); - - //} - - }); - - } - - static void AfterConnect (MewtocolInterface interf, TestRegisters registers) { - - //reading a value from the register collection - Console.WriteLine($"BitValue is: {registers.BitValue}"); - Console.WriteLine($"TestEnum is: {registers.TestEnum}"); - - _ = Task.Factory.StartNew(async () => { - - while(true) { - - isProgressReadout = true; - - await interf.ReadByteRange(1000, 2000, (p) => { - - var totSteps = 10; - var cSteps = totSteps * p; - - string progBar = ""; - for (int i = 0; i < totSteps; i++) { - - if(i < (int)cSteps) { - progBar += "⬛"; - } else { - progBar += "⬜"; - } - - } - - Console.Title = $"Prog read range: {(p * 100).ToString("N1")}% {progBar} Queued MSGs: {interf.QueuedMessages}"; - - }); - - isProgressReadout = false; - - await Task.Delay(3000); - - } - - }); - - //writing a value to the registers - _ = Task.Factory.StartNew(async () => { - - //set plc to run mode if not already - await interf.SetOperationMode(OPMode.Run); - - int startAdress = 10000; - int entryByteSize = 20 * 20; - - var bytes = await interf.ReadByteRange(startAdress, entryByteSize); - Console.WriteLine($"Bytes: {string.Join('-', bytes)}"); - - await Task.Delay(2000); - - await interf.SetRegisterAsync(nameof(registers.TestInt32), 100); - - //adds 10 each time the plc connects to the PLCs INT regíster - interf.SetRegister(nameof(registers.TestInt16), (short)(registers.TestInt16 + 10)); - //adds 1 each time the plc connects to the PLCs DINT regíster - interf.SetRegister(nameof(registers.TestInt32), (registers.TestInt32 + 1)); - //adds 11.11 each time the plc connects to the PLCs REAL regíster - interf.SetRegister(nameof(registers.TestFloat32), (float)(registers.TestFloat32 + 11.11)); - //writes 'Hello' to the PLCs string register - interf.SetRegister(nameof(registers.TestString2), "Hello"); - //set the current second to the PLCs TIME register - interf.SetRegister(nameof(registers.TestTime), TimeSpan.FromSeconds(DateTime.Now.Second)); - - //test pausing poller - - bool pollerPaused = false; - - while (true) { - - await Task.Delay(5000); - - pollerPaused = !pollerPaused; - - if (pollerPaused) { - Console.WriteLine("Pausing poller"); - await interf.PausePollingAsync(); - //interf.PollerDelayMs += 10; - Console.WriteLine("Paused poller"); - } else { - interf.ResumePolling(); - Console.WriteLine("Resumed poller"); - } - - } - - - }); - - } - - static void Scenario2 () { - - Logger.LogLevel = LogLevel.Critical; - Logger.OnNewLogMessage((date, msg) => { - Console.WriteLine($"{date.ToString("HH:mm:ss")} {msg}"); - }); - - Task.Factory.StartNew(async () => { - - //automatic endpoint - using (var interf = new MewtocolInterface("10.237.191.3")) { - - await interf.ConnectAsync(); - - if (interf.IsConnected) { - - await Task.Delay(5000); - - } - - interf.Disconnect(); - - } - - //manual endpoint - using (var interf = new MewtocolInterface("10.237.191.3")) { - - interf.HostEndpoint = new System.Net.IPEndPoint(System.Net.IPAddress.Parse("10.237.191.77"), 0); - - await interf.ConnectAsync(); - - if(interf.IsConnected) { - - await Task.Delay(5000); - - } - - interf.Disconnect(); - - } - - - }); + LoopInput(); } diff --git a/Examples/ScenarioAttribute.cs b/Examples/ScenarioAttribute.cs new file mode 100644 index 0000000..5a869c2 --- /dev/null +++ b/Examples/ScenarioAttribute.cs @@ -0,0 +1,15 @@ +using System; + +namespace Examples; + +public class ScenarioAttribute : Attribute { + + public string Description { get; private set; } + + public ScenarioAttribute(string description) { + + Description = description; + + } + +} \ No newline at end of file diff --git a/Examples/TestRegisters.cs b/Examples/TestRegisters.cs index acfc887..dfa77cc 100644 --- a/Examples/TestRegisters.cs +++ b/Examples/TestRegisters.cs @@ -10,8 +10,13 @@ namespace Examples { [Register(1000, RegisterType.R)] public bool TestBool1 { get; private set; } + private int testDuplicate; + [Register(1000)] - public int TestDuplicate { get; private set; } + public int TestDuplicate { + get => testDuplicate; + set => AutoSetter(value, ref testDuplicate); + } //corresponds to a XD input of the PLC [Register(RegisterType.X, SpecialAddress.D)] diff --git a/Examples/TestRegistersEnumBitwise.cs b/Examples/TestRegistersEnumBitwise.cs new file mode 100644 index 0000000..2a24825 --- /dev/null +++ b/Examples/TestRegistersEnumBitwise.cs @@ -0,0 +1,106 @@ +using MewtocolNet; +using MewtocolNet.RegisterAttributes; +using System; +using System.Collections; + +namespace Examples { + + public class TestRegistersEnumBitwise : RegisterCollectionBase { + + private bool startCyclePLC; + + [Register(50, RegisterType.R)] + public bool StartCyclePLC { + get => startCyclePLC; + set => AutoSetter(value, ref startCyclePLC); + } + + //the enum you want to read out + public enum CurrentState { + + Undefined = 0, + State1 = 1, + State2 = 2, + //If you leave an enum empty it still works + //State3 = 3, + State4 = 4, + State5 = 5, + State6 = 6, + State7 = 7, + State8 = 8, + State9 = 9, + State10 = 10, + State11 = 11, + State12 = 12, + State13 = 13, + State14 = 14, + State15 = 15, + + } + + //automatically convert the short (PLC int) register to an enum + [Register(500)] + public CurrentState TestEnum16 { get; private set; } + + //also works for 32bit registers + [Register(501, BitCount.B32)] + public CurrentState TestEnum32 { get; private set; } + + //get the whole bit array from DT503 + + [Register(503)] + public BitArray TestBitRegister16 { get; private set; } + + //you can also extract single bits from DT503 + + [Register(503, 0, BitCount.B16)] + public bool BitValue0 { get; private set; } + + [Register(503, 1, BitCount.B16)] + public bool BitValue1 { get; private set; } + + [Register(503, 2, BitCount.B16)] + public bool BitValue2 { get; private set; } + + [Register(503, 3, BitCount.B16)] + public bool BitValue3 { get; private set; } + + [Register(503, 4, BitCount.B16)] + public bool BitValue4 { get; private set; } + + [Register(503, 5, BitCount.B16)] + public bool BitValue5 { get; private set; } + + [Register(503, 6, BitCount.B16)] + public bool BitValue6 { get; private set; } + + [Register(503, 7, BitCount.B16)] + public bool BitValue7 { get; private set; } + + [Register(503, 8, BitCount.B16)] + public bool BitValue8 { get; private set; } + + [Register(503, 9, BitCount.B16)] + public bool BitValue9 { get; private set; } + + [Register(503, 10, BitCount.B16)] + public bool BitValue10 { get; private set; } + + [Register(503, 11, BitCount.B16)] + public bool BitValue11 { get; private set; } + + [Register(503, 12, BitCount.B16)] + public bool BitValue12 { get; private set; } + + [Register(503, 13, BitCount.B16)] + public bool BitValue13 { get; private set; } + + [Register(503, 14, BitCount.B16)] + public bool BitValue14 { get; private set; } + + [Register(503, 15, BitCount.B16)] + public bool BitValue15 { get; private set; } + + } + +} diff --git a/MewtocolNet/Mewtocol/DynamicInterface.cs b/MewtocolNet/Mewtocol/DynamicInterface.cs index 0162b94..a4556a3 100644 --- a/MewtocolNet/Mewtocol/DynamicInterface.cs +++ b/MewtocolNet/Mewtocol/DynamicInterface.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -25,7 +26,7 @@ namespace MewtocolNet { public bool PollerActive => !pollerTaskStopped; internal event Action PolledCycle; - + internal volatile bool pollerTaskRunning; internal volatile bool pollerTaskStopped; internal volatile bool pollerIsPaused; @@ -214,7 +215,7 @@ namespace MewtocolNet { /// A naming definition for QOL, doesn't effect PLC and is optional public void AddRegister (int _address, RegisterType _type, string _name = null) { - Register toAdd = null; + IRegister toAdd = null; //as number registers if (_type == RegisterType.DT_short) { @@ -243,30 +244,29 @@ namespace MewtocolNet { internal void AddRegister (Type _colType, int _address, RegisterType _type, string _name = null) { - Register toAdd = null; + IRegister toAdd = null; //as number registers if (_type == RegisterType.DT_short) { - toAdd = new NRegister(_address, _name); + toAdd = new NRegister(_address, _name).WithCollectionType(_colType); } if (_type == RegisterType.DT_ushort) { - toAdd = new NRegister(_address, _name); + toAdd = new NRegister(_address, _name).WithCollectionType(_colType); } if (_type == RegisterType.DDT_int) { - toAdd = new NRegister(_address, _name); + toAdd = new NRegister(_address, _name).WithCollectionType(_colType); } if (_type == RegisterType.DDT_uint) { - toAdd = new NRegister(_address, _name); + toAdd = new NRegister(_address, _name).WithCollectionType(_colType); } if (_type == RegisterType.DDT_float) { - toAdd = new NRegister(_address, _name); + toAdd = new NRegister(_address, _name).WithCollectionType(_colType); } if (toAdd == null) { - toAdd = new BRegister(_address, _type, _name); + toAdd = new BRegister(_address, _type, _name).WithCollectionType(_colType); } - toAdd.collectionType = _colType; Registers.Add(toAdd); } @@ -328,7 +328,7 @@ namespace MewtocolNet { throw new NotSupportedException($"_lenght parameter only allowed for register of type string"); } - Register toAdd; + IRegister toAdd; if (regType == typeof(short)) { toAdd = new NRegister(_address, _name); @@ -359,7 +359,8 @@ namespace MewtocolNet { } - internal void AddRegister (Type _colType, int _address, int _length = 1, string _name = null, bool _isBitwise = false, Type _enumType = null) { + //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) { Type regType = typeof(T); @@ -367,45 +368,51 @@ namespace MewtocolNet { throw new NotSupportedException($"_lenght parameter only allowed for register of type string"); } - if (Registers.Any(x => x.MemoryAdress == _address) && _isBitwise) { + if (Registers.Any(x => x.MemoryAddress == _address) && _isBitwise) { return; } - Register reg = null; + IRegister reg = null; + + string propName = boundProp.Name; + + //rename the property name to prevent duplicate names in case of a bitwise prop + if(_isBitwise && regType == typeof(short)) + propName = $"Auto_Bitwise_DT{_address}"; + + if (_isBitwise && regType == typeof(int)) + propName = $"Auto_Bitwise_DDT{_address}"; if (regType == typeof(short)) { - reg = new NRegister(_address, _name, _isBitwise); + reg = new NRegister(_address, propName, _isBitwise, _enumType).WithCollectionType(_colType); } else if (regType == typeof(ushort)) { - reg = new NRegister(_address, _name); + reg = new NRegister(_address, propName).WithCollectionType(_colType); } else if (regType == typeof(int)) { - reg = new NRegister(_address, _name, _isBitwise, _enumType); + reg = new NRegister(_address, propName, _isBitwise, _enumType).WithCollectionType(_colType); } else if (regType == typeof(uint)) { - reg = new NRegister(_address, _name); + reg = new NRegister(_address, propName).WithCollectionType(_colType); } else if (regType == typeof(float)) { - reg = new NRegister(_address, _name); + reg = new NRegister(_address, propName).WithCollectionType(_colType); } else if (regType == typeof(string)) { - reg = new SRegister(_address, _length, _name); + reg = new SRegister(_address, _length, propName).WithCollectionType(_colType); } else if (regType == typeof(TimeSpan)) { - reg = new NRegister(_address, _name); + reg = new NRegister(_address, propName).WithCollectionType(_colType); } else if (regType == typeof(bool)) { - reg = new BRegister(_address, RegisterType.R, _name); + reg = new BRegister(_address, RegisterType.R, propName).WithCollectionType(_colType); } if (reg == null) { throw new NotSupportedException($"The type {regType} is not allowed for Registers \n" + $"Allowed are: short, ushort, int, uint, float and string"); - } else { - - - 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."); - } - - reg.collectionType = _colType; - Registers.Add(reg); } + 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."); + } + + Registers.Add(reg); + } #endregion @@ -416,7 +423,7 @@ namespace MewtocolNet { /// Gets a register that was added by its name /// /// - public Register GetRegister (string name) { + public IRegister GetRegister (string name) { return Registers.FirstOrDefault(x => x.Name == name); @@ -427,13 +434,18 @@ namespace MewtocolNet { /// /// The type of register /// A casted register or the default value - public T GetRegister (string name) where T : Register { + public T GetRegister (string name) where T : IRegister { try { + var reg = Registers.FirstOrDefault(x => x.Name == name); - return reg as T; + return (T)reg; + } catch (InvalidCastException) { + return default(T); + } + } #endregion @@ -443,7 +455,7 @@ namespace MewtocolNet { /// /// Gets a list of all added registers /// - public List GetAllRegisters () { + public List GetAllRegisters () { return Registers; @@ -453,7 +465,7 @@ namespace MewtocolNet { #region Event Invoking - internal void InvokeRegisterChanged (Register reg) { + internal void InvokeRegisterChanged (IRegister reg) { RegisterChanged?.Invoke(reg); diff --git a/MewtocolNet/Mewtocol/IRegister.cs b/MewtocolNet/Mewtocol/IRegister.cs new file mode 100644 index 0000000..524bf15 --- /dev/null +++ b/MewtocolNet/Mewtocol/IRegister.cs @@ -0,0 +1,101 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text; + +namespace MewtocolNet { + + /// + /// An interface for all register types + /// + public interface IRegister { + + /// + /// Gets called whenever the value was changed + /// + event Action ValueChanged; + + /// + /// The name of the register + /// + string Name { get; } + + /// + /// The current value of the register + /// + object Value { get; } + + /// + /// The plc memory address of the register + /// + int MemoryAddress { get; } + + /// + /// Indicates if the register is processed bitwise + /// + /// True if bitwise + bool IsUsedBitwise(); + + /// + /// Generates a string describing the starting memory address of the register + /// + string GetStartingMemoryArea(); + + /// + /// Gets the current value formatted as a readable string + /// + string GetValueString(); + + /// + /// Builds the identifier for the mewtocol query string + /// + /// + string BuildMewtocolQuery(); + + /// + /// Builds a register string that prepends the memory address fe. DDT or DT, X, Y etc + /// + string GetRegisterString(); + + /// + /// Builds a combined name for the attached property to uniquely identify the property register binding + /// + /// + string GetCombinedName(); + + /// + /// Gets the name of the class that contains the attached property + /// + /// + string GetContainerName(); + + /// + /// Builds a register name after the PLC convention
+ /// Example DDT100, XA, X6, Y1, DT3300 + ///
+ string GetRegisterPLCName(); + + /// + /// Clears the current value of the register and resets it to default + /// + void ClearValue(); + + /// + /// Triggers a notifychanged update event + /// + void TriggerNotifyChange(); + + /// + /// Gets the type of the class collection its attached property is in or null + /// + /// The class name or null if manually added + Type GetCollectionType(); + + /// + /// Builds a readable string with all important register informations + /// + string ToString(); + + } + +} diff --git a/MewtocolNet/Mewtocol/MewtocolInterface.cs b/MewtocolNet/Mewtocol/MewtocolInterface.cs index 3fcf273..9062077 100644 --- a/MewtocolNet/Mewtocol/MewtocolInterface.cs +++ b/MewtocolNet/Mewtocol/MewtocolInterface.cs @@ -15,8 +15,11 @@ using System.ComponentModel; using System.Net; using System.Threading; using MewtocolNet.Queue; +using System.Reflection; +using System.Timers; -namespace MewtocolNet { +namespace MewtocolNet +{ /// /// The PLC com interface class @@ -36,7 +39,7 @@ namespace MewtocolNet { /// /// Gets triggered when a registered data register changes its value /// - public event Action RegisterChanged; + public event Action RegisterChanged; /// /// Gets triggered when a property of the interface changes @@ -114,7 +117,7 @@ namespace MewtocolNet { /// /// The registered data registers of the PLC /// - public List Registers { get; set; } = new List(); + public List Registers { get; set; } = new List(); private string ip; private int port; @@ -209,7 +212,7 @@ namespace MewtocolNet { RegisterChanged += (o) => { - string address = $"{o.GetRegisterString()}{o.MemoryAdress}".PadRight(5, (char)32); + string address = $"{o.GetRegisterString()}{o.GetStartingMemoryArea()}".PadRight(5, (char)32); Logger.Log($"{address} " + $"{(o.Name != null ? $"({o.Name}) " : "")}" + @@ -442,40 +445,46 @@ namespace MewtocolNet { } if (prop.PropertyType == typeof(short)) { - AddRegister(collection.GetType(), cAttribute.MemoryArea, _name: propName); + AddRegister(collection.GetType(), cAttribute.MemoryArea, prop); } if (prop.PropertyType == typeof(ushort)) { - AddRegister(collection.GetType(), cAttribute.MemoryArea, _name: propName); + AddRegister(collection.GetType(), cAttribute.MemoryArea, prop); } if (prop.PropertyType == typeof(int)) { - AddRegister(collection.GetType(), cAttribute.MemoryArea, _name: propName); + AddRegister(collection.GetType(), cAttribute.MemoryArea, prop); } if (prop.PropertyType == typeof(uint)) { - AddRegister(collection.GetType(), cAttribute.MemoryArea, _name: propName); + AddRegister(collection.GetType(), cAttribute.MemoryArea, prop); } if (prop.PropertyType == typeof(float)) { - AddRegister(collection.GetType(), cAttribute.MemoryArea, _name: propName); + AddRegister(collection.GetType(), cAttribute.MemoryArea, prop); } if (prop.PropertyType == typeof(string)) { - AddRegister(collection.GetType(), cAttribute.MemoryArea, cAttribute.StringLength, _name: propName); + AddRegister(collection.GetType(), cAttribute.MemoryArea, prop, cAttribute.StringLength); } if (prop.PropertyType.IsEnum) { - AddRegister(collection.GetType(), cAttribute.MemoryArea, _name: propName, _enumType: prop.PropertyType); + + 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, _name: propName, _isBitwise: true); + AddRegister(collection.GetType(), cAttribute.MemoryArea, prop, _isBitwise: true); } else { - AddRegister(collection.GetType(), cAttribute.MemoryArea, _name: propName, _isBitwise: true); + AddRegister(collection.GetType(), cAttribute.MemoryArea, prop, _isBitwise: true); } } @@ -486,15 +495,15 @@ namespace MewtocolNet { //var bitwiseCount = Registers.Count(x => x.Value.isUsedBitwise); if (cAttribute.BitCount == BitCount.B16) { - AddRegister(collection.GetType(), cAttribute.MemoryArea, _name: $"Auto_Bitwise_DT{cAttribute.MemoryArea}", _isBitwise: true); + AddRegister(collection.GetType(), cAttribute.MemoryArea, prop, _isBitwise: true); } else { - AddRegister(collection.GetType(), cAttribute.MemoryArea, _name: $"Auto_Bitwise_DDT{cAttribute.MemoryArea}", _isBitwise: true); + AddRegister(collection.GetType(), cAttribute.MemoryArea, prop, _isBitwise: true); } } if (prop.PropertyType == typeof(TimeSpan)) { - AddRegister(collection.GetType(), cAttribute.MemoryArea, _name: propName); + AddRegister(collection.GetType(), cAttribute.MemoryArea, prop); } } @@ -505,43 +514,51 @@ namespace MewtocolNet { RegisterChanged += (reg) => { - //if the register is also used bitwise assign the boolean bit value to the according prop - if(reg.isUsedBitwise) { + //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.MemoryAdress); + .FirstOrDefault(y => y.GetType() == typeof(RegisterAttribute) && ((RegisterAttribute)y).MemoryArea == reg.MemoryAddress); + + if(bitWiseFound != null) { - if(bitWiseFound != null && reg is NRegister reg16) { var casted = (RegisterAttribute)bitWiseFound; var bitIndex = casted.AssignedBitIndex; - var bytes = BitConverter.GetBytes(reg16.Value); - BitArray bitAr = new BitArray(bytes); + BitArray bitAr = null; - if (bitIndex < bitAr.Length && bitIndex >= 0) { + 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 (bitWiseFound != null && reg is NRegister reg32) { - var casted = (RegisterAttribute)bitWiseFound; - var bitIndex = casted.AssignedBitIndex; - var bytes = BitConverter.GetBytes(reg32.Value); - BitArray bitAr = new BitArray(bytes); - 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) { @@ -557,67 +574,25 @@ namespace MewtocolNet { //check if bit parse mode if (registerAttr.AssignedBitIndex == -1) { - //setting back booleans - if (foundToUpdate.PropertyType == typeof(bool)) { - foundToUpdate.SetValue(collection, ((BRegister)reg).Value); - } + HashSet NumericTypes = new HashSet { + typeof(bool), + typeof(short), + typeof(ushort), + typeof(int), + typeof(uint), + typeof(float), + typeof(TimeSpan), + typeof(string) + }; - //setting back numbers + var regValue = ((IRegister)reg).Value; - if (foundToUpdate.PropertyType == typeof(short)) { - foundToUpdate.SetValue(collection, ((NRegister)reg).Value); - } - - if (foundToUpdate.PropertyType == typeof(ushort)) { - foundToUpdate.SetValue(collection, ((NRegister)reg).Value); - } - - if (foundToUpdate.PropertyType == typeof(int)) { - foundToUpdate.SetValue(collection, ((NRegister)reg).Value); - } - - if (foundToUpdate.PropertyType == typeof(uint)) { - foundToUpdate.SetValue(collection, ((NRegister)reg).Value); - } - - if (foundToUpdate.PropertyType == typeof(float)) { - foundToUpdate.SetValue(collection, ((NRegister)reg).Value); + if (NumericTypes.Any(x => foundToUpdate.PropertyType == x)) { + foundToUpdate.SetValue(collection, regValue); } if (foundToUpdate.PropertyType.IsEnum) { - foundToUpdate.SetValue(collection, ((NRegister)reg).Value); - } - - if (foundToUpdate.PropertyType == typeof(TimeSpan)) { - foundToUpdate.SetValue(collection, ((NRegister)reg).Value); - } - - //setting back strings - - if (foundToUpdate.PropertyType == typeof(string)) { - foundToUpdate.SetValue(collection, ((SRegister)reg).Value); - } - - } - - - if (foundToUpdate.PropertyType == typeof(BitArray)) { - - //setting back bit registers - if (reg is NRegister shortReg) { - - var bytes = BitConverter.GetBytes(shortReg.Value); - BitArray bitAr = new BitArray(bytes); - foundToUpdate.SetValue(collection, bitAr); - - } - - if (reg is NRegister intReg) { - - var bytes = BitConverter.GetBytes(intReg.Value); - BitArray bitAr = new BitArray(bytes); - foundToUpdate.SetValue(collection, bitAr); - + foundToUpdate.SetValue(collection, regValue); } } diff --git a/MewtocolNet/Mewtocol/MewtocolInterfaceRequests.cs b/MewtocolNet/Mewtocol/MewtocolInterfaceRequests.cs index 9c69906..375fb73 100644 --- a/MewtocolNet/Mewtocol/MewtocolInterfaceRequests.cs +++ b/MewtocolNet/Mewtocol/MewtocolInterfaceRequests.cs @@ -166,7 +166,7 @@ namespace MewtocolNet { /// The register to read public async Task ReadBoolRegister (BRegister _toRead) { - string requeststring = $"%{GetStationNumber()}#RCS{_toRead.BuildMewtocolIdent()}"; + string requeststring = $"%{GetStationNumber()}#RCS{_toRead.BuildMewtocolQuery()}"; var result = await SendCommandAsync(requeststring); if(!result.Success) { @@ -198,7 +198,7 @@ namespace MewtocolNet { /// The success state of the write operation public async Task WriteBoolRegister (BRegister _toWrite, bool value) { - string requeststring = $"%{GetStationNumber()}#WCS{_toWrite.BuildMewtocolIdent()}{(value ? "1" : "0")}"; + string requeststring = $"%{GetStationNumber()}#WCS{_toWrite.BuildMewtocolQuery()}{(value ? "1" : "0")}"; var result = await SendCommandAsync(requeststring); @@ -220,7 +220,7 @@ namespace MewtocolNet { Type numType = typeof(T); - string requeststring = $"%{GetStationNumber()}#RD{_toRead.BuildMewtocolIdent()}"; + string requeststring = $"%{GetStationNumber()}#RD{_toRead.BuildMewtocolQuery()}"; var result = await SendCommandAsync(requeststring); var failedResult = new NRegisterResult { @@ -335,7 +335,7 @@ namespace MewtocolNet { toWriteVal = null; } - string requeststring = $"%{GetStationNumber()}#WD{_toWrite.BuildMewtocolIdent()}{toWriteVal.ToHexString()}"; + string requeststring = $"%{GetStationNumber()}#WD{_toWrite.BuildMewtocolQuery()}{toWriteVal.ToHexString()}"; var result = await SendCommandAsync(requeststring); @@ -362,7 +362,7 @@ namespace MewtocolNet { /// public async Task ReadStringRegister (SRegister _toRead, int _stationNumber = 1) { - string requeststring = $"%{GetStationNumber()}#RD{_toRead.BuildMewtocolIdent()}"; + string requeststring = $"%{GetStationNumber()}#RD{_toRead.BuildMewtocolQuery()}"; var result = await SendCommandAsync(requeststring); if (result.Success) _toRead.SetValueFromPLC(result.Response.ParseDTString()); diff --git a/MewtocolNet/Mewtocol/RegisterAttributes/RegisterCollectionBase.cs b/MewtocolNet/Mewtocol/RegisterAttributes/RegisterCollectionBase.cs index 1a1a829..955a846 100644 --- a/MewtocolNet/Mewtocol/RegisterAttributes/RegisterCollectionBase.cs +++ b/MewtocolNet/Mewtocol/RegisterAttributes/RegisterCollectionBase.cs @@ -8,7 +8,8 @@ using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; -namespace MewtocolNet.RegisterAttributes { +namespace MewtocolNet.RegisterAttributes +{ /// /// A register collection base with full auto read and notification support built in @@ -34,6 +35,22 @@ namespace MewtocolNet.RegisterAttributes { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } + /// + /// Use this on the setter method of a property to enable automatic property register writing + /// + public static void AutoSetter (object value, ref T privateField) { + + if(value is IRegister reg) { + + privateField = (T)reg.Value; + return; + + } + + privateField = (T)value; + + } + /// /// Gets called when the register collection base was linked to its parent mewtocol interface /// diff --git a/MewtocolNet/Mewtocol/Subregisters/BRegister.cs b/MewtocolNet/Mewtocol/Subregisters/BRegister.cs index d304890..92cb068 100644 --- a/MewtocolNet/Mewtocol/Subregisters/BRegister.cs +++ b/MewtocolNet/Mewtocol/Subregisters/BRegister.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel; using System.Text; using MewtocolNet; @@ -7,17 +8,47 @@ namespace MewtocolNet.Registers { /// /// Defines a register containing a boolean /// - public class BRegister : Register { + public class BRegister : IRegister, INotifyPropertyChanged { + + /// + /// Gets called whenever the value was changed + /// + public event Action ValueChanged; + + /// + /// Triggers when a property on the register changes + /// + public event PropertyChangedEventHandler PropertyChanged; internal RegisterType RegType { get; private set; } - internal SpecialAddress SpecialAddress { get; private set; } - internal bool LastValue; + internal SpecialAddress SpecialAddress { get; private set; } + + internal Type collectionType; + + /// + /// The type of collection the register is in or null of added manually + /// + public Type CollectionType => collectionType; + + internal bool lastValue; /// /// The value of the register /// - public bool Value => LastValue; + public object Value => lastValue; + + internal string name; + /// + /// The register name or null of not defined + /// + public string Name => name; + + internal int memoryAdress; + /// + /// The registers memory adress if not a special register + /// + public int MemoryAddress => memoryAdress; /// /// Defines a register containing a number @@ -27,7 +58,11 @@ namespace MewtocolNet.Registers { /// Name of the register public BRegister (int _address, RegisterType _type = RegisterType.R, string _name = null) { - if (_address > 99999) throw new NotSupportedException("Memory adresses cant be greater than 99999"); + if (_address > 99999) throw new NotSupportedException("Memory addresses cant be greater than 99999"); + + if (_type != RegisterType.X && _type != RegisterType.Y && _type != RegisterType.R) + throw new NotSupportedException("The register type cant be numeric, use X, Y or R"); + memoryAdress = _address; name = _name; @@ -44,7 +79,10 @@ namespace MewtocolNet.Registers { public BRegister (SpecialAddress _address, RegisterType _type = RegisterType.R, string _name = null) { if (_address == SpecialAddress.None) - throw new NotSupportedException("Special adress cant be none"); + throw new NotSupportedException("Special address cant be none"); + + if (_type != RegisterType.X && _type != RegisterType.Y && _type != RegisterType.R) + throw new NotSupportedException("The register type cant be numeric, use X, Y or R"); SpecialAddress = _address; name = _name; @@ -53,15 +91,22 @@ namespace MewtocolNet.Registers { } + internal BRegister WithCollectionType(Type colType) { + + collectionType = colType; + return this; + + } + /// /// Builds the register area name /// - public override string BuildMewtocolIdent () { + public string BuildMewtocolQuery () { //build area code from register type StringBuilder asciistring = new StringBuilder(RegType.ToString()); if(SpecialAddress == SpecialAddress.None) { - asciistring.Append(MemoryAdress.ToString().PadLeft(4, '0')); + asciistring.Append(MemoryAddress.ToString().PadLeft(4, '0')); } else { asciistring.Append(SpecialAddress.ToString().PadLeft(4, '0')); } @@ -71,11 +116,52 @@ namespace MewtocolNet.Registers { } internal void SetValueFromPLC (bool val) { - LastValue = val; + + lastValue = val; TriggerChangedEvnt(this); TriggerNotifyChange(); + } + public string GetStartingMemoryArea() { + + if (SpecialAddress != SpecialAddress.None) + return SpecialAddress.ToString(); + + return MemoryAddress.ToString(); + + } + + public bool IsUsedBitwise() => false; + + public Type GetCollectionType() => CollectionType; + + public string GetValueString() => Value.ToString(); + + public void ClearValue() => SetValueFromPLC(false); + + public string GetRegisterString() => RegType.ToString(); + + public string GetCombinedName() => $"{(CollectionType != null ? $"{CollectionType.Name}." : "")}{Name ?? "Unnamed"}"; + + public string GetContainerName() => $"{(CollectionType != null ? $"{CollectionType.Name}" : "")}"; + + public string GetRegisterPLCName() { + + if (SpecialAddress != SpecialAddress.None) { + return $"{GetRegisterString()}{SpecialAddress}"; + } + + return $"{GetRegisterString()}{MemoryAddress}"; + + } + + internal void TriggerChangedEvnt(object changed) => ValueChanged?.Invoke(changed); + + public void TriggerNotifyChange() => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Value")); + + public override string ToString() => $"{GetRegisterPLCName()} - Value: {GetValueString()}"; + } } diff --git a/MewtocolNet/Mewtocol/Subregisters/NRegister.cs b/MewtocolNet/Mewtocol/Subregisters/NRegister.cs index 760d9c1..79cf631 100644 --- a/MewtocolNet/Mewtocol/Subregisters/NRegister.cs +++ b/MewtocolNet/Mewtocol/Subregisters/NRegister.cs @@ -1,18 +1,63 @@ using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Reflection; +using System.Text; namespace MewtocolNet.Registers { + /// /// Defines a register containing a number /// /// The type of the numeric value - public class NRegister : Register { + public class NRegister : IRegister { - internal T LastValue; + /// + /// Gets called whenever the value was changed + /// + public event Action ValueChanged; + + /// + /// Triggers when a property on the register changes + /// + public event PropertyChangedEventHandler PropertyChanged; + + internal Type collectionType; + + /// + /// The type of collection the register is in or null of added manually + /// + public Type CollectionType => collectionType; + + internal T lastValue; /// /// The value of the register /// - public T Value => LastValue; + public object Value => lastValue; + + internal string name; + /// + /// The register name or null of not defined + /// + public string Name => name; + + internal int memoryAdress; + /// + /// The registers memory adress if not a special register + /// + public int MemoryAddress => memoryAdress; + + internal int memoryLength; + /// + /// The rgisters memory length + /// + public int MemoryLength => memoryLength; + + internal bool isUsedBitwise { get; set; } + + internal Type enumType { get; set; } /// /// Defines a register containing a number @@ -23,6 +68,7 @@ namespace MewtocolNet.Registers { if (_adress > 99999) throw new NotSupportedException("Memory adresses cant be greater than 99999"); + memoryAdress = _adress; name = _name; Type numType = typeof(T); @@ -71,15 +117,159 @@ namespace MewtocolNet.Registers { } + internal NRegister WithCollectionType (Type colType) { + + collectionType = colType; + return this; + + } + internal void SetValueFromPLC (object val) { - LastValue = (T)val; + + lastValue = (T)val; TriggerChangedEvnt(this); TriggerNotifyChange(); + } + public string GetStartingMemoryArea () => this.MemoryAddress.ToString(); + + public Type GetCollectionType() => CollectionType; + + public bool IsUsedBitwise() => isUsedBitwise; + + public string GetValueString() { + + //is number or bitwise + if(enumType == null) { + + return $"{Value}{(isUsedBitwise ? $" [{GetBitwise().ToBitString()}]" : "")}"; + + } + + //is enum + var dict = new Dictionary(); + + foreach (var name in Enum.GetNames(enumType)) { + + int enumKey = (int)Enum.Parse(enumType, name); + if (!dict.ContainsKey(enumKey)) { + dict.Add(enumKey, name); + } + + } + + if (enumType != null && Value is short shortVal) { + + if (dict.ContainsKey(shortVal)) { + + return $"{Value} ({dict[shortVal]})"; + + } else { + + return $"{Value} (Missing Enum)"; + + } + + } + + if (enumType != null && Value is int intVal) { + + if (dict.ContainsKey(intVal)) { + + return $"{Value} ({dict[intVal]})"; + + } else { + + return $"{Value} (Missing Enum)"; + + } + + } + + return Value.ToString(); + + } + + /// + /// Gets the register bitwise if its a 16 or 32 bit int + /// + /// A bitarray + public BitArray GetBitwise() { + + if (this is NRegister shortReg) { + + var bytes = BitConverter.GetBytes((short)Value); + BitArray bitAr = new BitArray(bytes); + return bitAr; + + } + + if (this is NRegister intReg) { + + var bytes = BitConverter.GetBytes((int)Value); + BitArray bitAr = new BitArray(bytes); + return bitAr; + + } + + return null; + + } + + public string BuildMewtocolQuery() { + + StringBuilder asciistring = new StringBuilder("D"); + asciistring.Append(MemoryAddress.ToString().PadLeft(5, '0')); + asciistring.Append((MemoryAddress + MemoryLength).ToString().PadLeft(5, '0')); + return asciistring.ToString(); + + } + + public string GetRegisterString() { + + if(Value is short) { + return "DT"; + } + + if (Value is ushort) { + return "DT"; + } + + if (Value is int) { + return "DDT"; + } + + if (Value is uint) { + return "DDT"; + } + + if (Value is float) { + return "DDT"; + } + + if (Value is TimeSpan) { + return "DDT"; + } + + throw new NotSupportedException("Numeric type is not supported"); + + } + + public string GetCombinedName() => $"{(CollectionType != null ? $"{CollectionType.Name}." : "")}{Name ?? "Unnamed"}"; + + public string GetContainerName() => $"{(CollectionType != null ? $"{CollectionType.Name}" : "")}"; + + public string GetRegisterPLCName() => $"{GetRegisterString()}{MemoryAddress}"; + + public void ClearValue() => SetValueFromPLC(default(T)); + + internal void TriggerChangedEvnt(object changed) => ValueChanged?.Invoke(changed); + + public void TriggerNotifyChange() => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Value")); + + public override string ToString() => $"{GetRegisterPLCName()} - Value: {GetValueString()}"; + } - - - } diff --git a/MewtocolNet/Mewtocol/Subregisters/NRegisterResult.cs b/MewtocolNet/Mewtocol/Subregisters/NRegisterResult.cs index 2296042..74f7214 100644 --- a/MewtocolNet/Mewtocol/Subregisters/NRegisterResult.cs +++ b/MewtocolNet/Mewtocol/Subregisters/NRegisterResult.cs @@ -21,12 +21,14 @@ namespace MewtocolNet.Registers { /// Trys to get the value of there is one /// public bool TryGetValue (out T value) { + if(Result.Success) { - value = Register.Value; + value = (T)Register.Value; return true; } value = default(T); return false; + } } diff --git a/MewtocolNet/Mewtocol/Subregisters/Register.cs b/MewtocolNet/Mewtocol/Subregisters/Register.cs deleted file mode 100644 index eea7ff1..0000000 --- a/MewtocolNet/Mewtocol/Subregisters/Register.cs +++ /dev/null @@ -1,388 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.ComponentModel; -using System.IO; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading.Tasks; - -namespace MewtocolNet.Registers { - - /// - /// A class describing a register - /// - public abstract class Register : INotifyPropertyChanged { - - /// - /// Gets called whenever the value was changed - /// - public event Action ValueChanged; - /// - /// Triggers when a property on the register changes - /// - public event PropertyChangedEventHandler PropertyChanged; - - internal Type collectionType; - /// - /// The type of collection the register is in or null of added manually - /// - public Type CollectionType => collectionType; - - internal string name; - /// - /// The register name or null of not defined - /// - public string Name => name; - - internal int memoryAdress; - /// - /// The registers memory adress if not a special register - /// - public int MemoryAdress => memoryAdress; - - internal int memoryLength; - /// - /// The rgisters memory length - /// - public int MemoryLength => memoryLength; - - /// - /// The value of the register auto converted to a string - /// - public string StringValue => GetValueString(); - - /// - /// The name the register would have in the PLC - /// - public string RegisterPLCName => GetRegisterPLCName(); - - /// - /// The combined name with the holding register class type infront - /// - public string CombinedName => GetCombinedName(); - - /// - /// The name of the class that contains this register or empty if it was added manually - /// - public string ContainerName => GetContainerName(); - - internal bool isUsedBitwise { get; set; } - internal Type enumType { get; set; } - - internal Register () { - ValueChanged += (obj) => { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(StringValue))); - }; - } - - /// - /// Builds the register area name - /// - public virtual string BuildMewtocolIdent() { - - StringBuilder asciistring = new StringBuilder("D"); - asciistring.Append(MemoryAdress.ToString().PadLeft(5, '0')); - asciistring.Append((MemoryAdress + MemoryLength).ToString().PadLeft(5, '0')); - return asciistring.ToString(); - - } - internal void TriggerChangedEvnt(object changed) { - ValueChanged?.Invoke(changed); - } - - internal void TriggerNotifyChange () { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Value")); - } - - internal void ClearValue () { - - if (enumType != null && this is NRegister intEnumReg) { - intEnumReg.SetValueFromPLC((int)0); - } - if (this is NRegister shortReg) { - shortReg.SetValueFromPLC((short)0); - } - if (this is NRegister ushortReg) { - ushortReg.SetValueFromPLC((ushort)0); - } - if (this is NRegister intReg) { - intReg.SetValueFromPLC((int)0); - } - if (this is NRegister uintReg) { - uintReg.SetValueFromPLC((uint)0); - } - if (this is NRegister floatReg) { - floatReg.SetValueFromPLC((float)0); - } - if (this is NRegister tsReg) { - tsReg.SetValueFromPLC(TimeSpan.Zero); - } - if (this is BRegister boolReg) { - boolReg.SetValueFromPLC(false); - } - if (this is SRegister stringReg) { - stringReg.SetValueFromPLC(null); - } - - } - - /// - /// Gets the starting memory are either numeric or A,B,C,D etc for special areas like inputs - /// - /// - public string GetStartingMemoryArea () { - - if (this is BRegister bReg && bReg.SpecialAddress != SpecialAddress.None) { - return bReg.SpecialAddress.ToString(); - } - - return this.MemoryAdress.ToString(); - - } - - /// - /// Gets the current value in the adress as a string - /// - /// - public string GetValueString () { - - if (enumType != null && this is NRegister intEnumReg) { - - var dict = new Dictionary(); - - foreach (var name in Enum.GetNames(enumType)) { - int enumKey = (int)Enum.Parse(enumType, name); - if(!dict.ContainsKey(enumKey)) { - dict.Add(enumKey, name); - } - } - - if(dict.ContainsKey(intEnumReg.Value)) { - return $"{intEnumReg.Value} ({dict[intEnumReg.Value]})"; - } else { - return $"{intEnumReg.Value} (Missing Enum)"; - } - - } - if (this is NRegister shortReg) { - return $"{shortReg.Value}{(isUsedBitwise ? $" [{shortReg.GetBitwise().ToBitString()}]" : "")}"; - } - if (this is NRegister ushortReg) { - return ushortReg.Value.ToString(); - } - if (this is NRegister intReg) { - return $"{intReg.Value}{(isUsedBitwise ? $" [{intReg.GetBitwise().ToBitString()}]" : "")}"; - } - if (this is NRegister uintReg) { - return uintReg.Value.ToString(); - } - if (this is NRegister floatReg) { - return floatReg.Value.ToString(); - } - if (this is NRegister tsReg) { - return tsReg.Value.ToString(); - } - if (this is BRegister boolReg) { - return boolReg.Value.ToString(); - } - if (this is SRegister stringReg) { - return stringReg.Value ?? ""; - } - - return "Type of the register is not supported."; - - } - - /// - /// Gets the register bitwise if its a 16 or 32 bit int - /// - /// A bitarray - public BitArray GetBitwise () { - - if (this is NRegister shortReg) { - - var bytes = BitConverter.GetBytes(shortReg.Value); - BitArray bitAr = new BitArray(bytes); - return bitAr; - - } - - if (this is NRegister intReg) { - - var bytes = BitConverter.GetBytes(intReg.Value); - BitArray bitAr = new BitArray(bytes); - return bitAr; - - } - - return null; - - } - - /// - /// Gets the register dataarea string DT for 16bit and DDT for 32 bit types - /// - public string GetRegisterString () { - - if (this is NRegister shortReg) { - return "DT"; - } - if (this is NRegister ushortReg) { - return "DT"; - } - if (this is NRegister intReg) { - return "DDT"; - } - if (this is NRegister uintReg) { - return "DDT"; - } - if (this is NRegister floatReg) { - return "DDT"; - } - if (this is NRegister tsReg) { - return "DDT"; - } - if (this is BRegister boolReg) { - return boolReg.RegType.ToString(); - } - if (this is SRegister stringReg) { - return "DT"; - - } - - return "Type of the register is not supported."; - - } - - /// - /// Builds a register from a given register string like DT100 / XA / Y1 - /// - /// The input string to parse - /// A built register - public static Register FromString (string regString, string name = null) { - - var match = Regex.Match(regString, @"(X|Y|R)([A-F]|[0-9_.-]{1,5})"); - - if (match != null && match.Success) { - - var typeGroup = match.Groups[1].Value; - var areaGroup = match.Groups[2].Value; - - bool isBool = false; - var parsedRegType = RegisterType.R; - if (new string[] { "X", "Y", "R" }.Contains(typeGroup)) { - switch (typeGroup) { - case "X": - parsedRegType = RegisterType.X; - isBool = true; - break; - case "Y": - parsedRegType = RegisterType.Y; - isBool = true; - break; - case "R": - parsedRegType = RegisterType.R; - isBool = true; - break; - } - } - - if(!isBool) { - throw new NotSupportedException($"Register with value {regString} is not of type bool"); - } - - if (int.TryParse(areaGroup, out var parsedNum) && isBool) { - - return new BRegister(parsedNum, parsedRegType, name); - - } else if(Enum.TryParse(areaGroup, out var parsedSpecial) && isBool) { - - return new BRegister(parsedSpecial, parsedRegType, name); - - } - } - - throw new NotSupportedException($"Register with value {regString} is not supported"); - - } - - public static NRegister FromString (string regString, string name = null) { - - var match = Regex.Match(regString, @"(DT|DDT)([0-9_.-]{1,5})"); - - if (match != null && match.Success) { - - var typeGroup = match.Groups[1].Value; - var areaGroup = match.Groups[2].Value; - - bool isTypeDoubleSize = false; - bool isSupportedNumericFormat = false; - - if(typeGroup == "") - - switch (typeGroup) { - case "DT": - isSupportedNumericFormat = true; - break; - case "DDT": - isTypeDoubleSize = true; - isSupportedNumericFormat = true; - break; - } - - if(typeof(T).IsDoubleNumericRegisterType() != isTypeDoubleSize) { - throw new NotSupportedException($"Input register type was {typeGroup}, the cast type was not of the same size"); - } - - if (int.TryParse(areaGroup, out var parsedNum) && typeof(T).IsNumericSupportedType() && isSupportedNumericFormat ) { - - return new NRegister(parsedNum, name); - - } - - } - - throw new NotSupportedException($"Register with value {regString} is not supported"); - - } - - public static SRegister FromString (string regString, int reserved, string name = null) { - - var match = Regex.Match(regString, @"(DT)([0-9_.-]{1,5})"); - - if (match != null && match.Success) { - - - } - - throw new NotSupportedException($"Register with value {regString} is not supported"); - - } - - internal string GetCombinedName () { - - return $"{(CollectionType != null ? $"{CollectionType.Name}." : "")}{Name ?? "Unnamed"}"; - - } - - internal string GetContainerName () { - - return $"{(CollectionType != null ? $"{CollectionType.Name}" : "")}"; - - } - - internal string GetRegisterPLCName () { - - if (this is BRegister bReg && bReg.SpecialAddress != SpecialAddress.None) { - return $"{GetRegisterString()}{bReg.SpecialAddress}"; - } - - return $"{GetRegisterString()}{MemoryAdress}"; - - } - - } - -} diff --git a/MewtocolNet/Mewtocol/Subregisters/SRegister.cs b/MewtocolNet/Mewtocol/Subregisters/SRegister.cs index 164ae91..49c0d6e 100644 --- a/MewtocolNet/Mewtocol/Subregisters/SRegister.cs +++ b/MewtocolNet/Mewtocol/Subregisters/SRegister.cs @@ -1,18 +1,54 @@ using System; +using System.ComponentModel; using System.Text; namespace MewtocolNet.Registers { /// /// Defines a register containing a string /// - public class SRegister : Register { - - private string lastVal = ""; + public class SRegister : IRegister { /// - /// The current value of the register + /// Gets called whenever the value was changed /// - public string Value => lastVal; + public event Action ValueChanged; + + /// + /// Triggers when a property on the register changes + /// + public event PropertyChangedEventHandler PropertyChanged; + + internal Type collectionType; + + /// + /// The type of collection the register is in or null of added manually + /// + public Type CollectionType => collectionType; + + internal string lastValue; + + /// + /// The value of the register + /// + public object Value => lastValue; + + internal string name; + /// + /// The register name or null of not defined + /// + public string Name => name; + + internal int memoryAdress; + /// + /// The registers memory adress if not a special register + /// + public int MemoryAddress => memoryAdress; + + internal int memoryLength; + /// + /// The registers memory length + /// + public int MemoryLength => memoryLength; internal short ReservedSize { get; set; } @@ -35,15 +71,22 @@ namespace MewtocolNet.Registers { memoryLength = (int)Math.Round(wordsize + 1); } + internal SRegister WithCollectionType(Type colType) { + + collectionType = colType; + return this; + + } + /// /// Builds the register identifier for the mewotocol protocol /// - public override string BuildMewtocolIdent() { + public string BuildMewtocolQuery() { StringBuilder asciistring = new StringBuilder("D"); - asciistring.Append(MemoryAdress.ToString().PadLeft(5, '0')); - asciistring.Append((MemoryAdress + MemoryLength).ToString().PadLeft(5, '0')); + asciistring.Append(MemoryAddress.ToString().PadLeft(5, '0')); + asciistring.Append((MemoryAddress + MemoryLength).ToString().PadLeft(5, '0')); return asciistring.ToString(); } @@ -55,18 +98,45 @@ namespace MewtocolNet.Registers { StringBuilder asciistring = new StringBuilder("D"); - asciistring.Append(MemoryAdress.ToString().PadLeft(5, '0')); - asciistring.Append((MemoryAdress + overwriteWordLength - 1).ToString().PadLeft(5, '0')); + asciistring.Append(MemoryAddress.ToString().PadLeft(5, '0')); + asciistring.Append((MemoryAddress + overwriteWordLength - 1).ToString().PadLeft(5, '0')); return asciistring.ToString(); } + public Type GetCollectionType() => CollectionType; + + public bool IsUsedBitwise() => false; + internal void SetValueFromPLC (string val) { - lastVal = val; + + lastValue = val; + TriggerChangedEvnt(this); TriggerNotifyChange(); + } + public string GetStartingMemoryArea() => this.MemoryAddress.ToString(); + + public string GetValueString() => Value?.ToString() ?? ""; + + public string GetRegisterString() => "DT"; + + public string GetCombinedName() => $"{(CollectionType != null ? $"{CollectionType.Name}." : "")}{Name ?? "Unnamed"}"; + + public string GetContainerName() => $"{(CollectionType != null ? $"{CollectionType.Name}" : "")}"; + + public string GetRegisterPLCName() => $"{GetRegisterString()}{MemoryAddress}"; + + public void ClearValue() => SetValueFromPLC(null); + + internal void TriggerChangedEvnt(object changed) => ValueChanged?.Invoke(changed); + + public void TriggerNotifyChange() => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Value")); + + public override string ToString () => $"{GetRegisterPLCName()} - Value: {GetValueString()}"; + } } diff --git a/MewtocolNet/MewtocolNet.csproj b/MewtocolNet/MewtocolNet.csproj index d0a3792..c226a79 100644 --- a/MewtocolNet/MewtocolNet.csproj +++ b/MewtocolNet/MewtocolNet.csproj @@ -2,7 +2,7 @@ netstandard2.0 Mewtocol.NET - 0.6.2 + 0.7.0 Felix Weiss Womed true @@ -18,4 +18,9 @@ ..\Builds\MewtocolNet.xml ..\Builds + + + <_Parameter1>MewtocolTests + + diff --git a/MewtocolTests/AutomatedPropertyRegisters.cs b/MewtocolTests/AutomatedPropertyRegisters.cs new file mode 100644 index 0000000..30e902d --- /dev/null +++ b/MewtocolTests/AutomatedPropertyRegisters.cs @@ -0,0 +1,306 @@ +using Xunit; + +using MewtocolNet; +using MewtocolNet.Registers; +using System.Diagnostics; +using Xunit.Abstractions; +using System.Collections; +using MewtocolNet.RegisterAttributes; +using Microsoft.Win32; + +namespace MewtocolTests { + + public class AutomatedPropertyRegisters { + + private readonly ITestOutputHelper output; + + public AutomatedPropertyRegisters(ITestOutputHelper output) { + this.output = output; + } + + public class TestRegisterCollection : RegisterCollectionBase { + + //corresponds to a R100 boolean register in the PLC + [Register(1000, RegisterType.R)] + public bool TestBool1 { get; private set; } + + //corresponds to a XD input of the PLC + [Register(RegisterType.X, SpecialAddress.D)] + public bool TestBoolInputXD { get; private set; } + + //corresponds to a DT1101 - DT1104 string register in the PLC with (STRING[4]) + //[Register(1101, 4)] + //public string TestString1 { get; private set; } + + //corresponds to a DT7000 16 bit int register in the PLC + [Register(899)] + public short TestInt16 { get; private set; } + + [Register(342)] + public ushort TestUInt16 { get; private set; } + + //corresponds to a DTD7001 - DTD7002 32 bit int register in the PLC + [Register(7001)] + public int TestInt32 { get; private set; } + + [Register(765)] + public uint TestUInt32 { get; private set; } + + //corresponds to a DTD7001 - DTD7002 32 bit float register in the PLC (REAL) + [Register(7003)] + public float TestFloat32 { get; private set; } + + //corresponds to a DT7005 - DT7009 string register in the PLC with (STRING[5]) + [Register(7005, 5)] + public string TestString2 { get; private set; } + + //corresponds to a DT7010 as a 16bit word/int and parses the word as single bits + [Register(7010)] + public BitArray TestBitRegister { get; private set; } + + [Register(8010, BitCount.B32)] + 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)] + public bool BitValue { get; private set; } + + [Register(1204, 5, BitCount.B16)] + public bool FillTest { get; private set; } + + //corresponds to a DT7012 - DT7013 as a 32bit time value that gets parsed as a timespan (TIME) + //the smallest value to communicate to the PLC is 10ms + [Register(7012)] + public TimeSpan TestTime { get; private set; } + + public enum CurrentState { + Undefined = 0, + State1 = 1, + State2 = 2, + //State3 = 3, + State4 = 4, + State5 = 5, + StateBetween = 100, + State6 = 6, + State7 = 7, + } + + [Register(50)] + public CurrentState TestEnum16 { get; private set; } + + [Register(51, BitCount.B32)] + public CurrentState TestEnum32 { get; private set; } + + } + + private void TestBasicGeneration(IRegister reg, string propName, object expectValue, int expectAddr, string expectPlcName) { + + Assert.NotNull(reg); + Assert.Equal(propName, reg.Name); + Assert.Equal(expectValue, reg.Value); + + Assert.Equal(expectAddr, reg.MemoryAddress); + Assert.Equal(expectPlcName, reg.GetRegisterPLCName()); + + output.WriteLine(reg.ToString()); + + } + + //actual tests + + [Fact(DisplayName = "Boolean R generation")] + public void BooleanGen () { + + var interf = new MewtocolInterface("192.168.0.1"); + interf.WithRegisterCollection(new TestRegisterCollection()).WithPoller(); + + var register = interf.GetRegister(nameof(TestRegisterCollection.TestBool1)); + + //test generic properties + TestBasicGeneration(register, nameof(TestRegisterCollection.TestBool1), false, 1000, "R1000"); + + } + + [Fact(DisplayName = "Boolean input XD generation")] + public void BooleanInputGen () { + + var interf = new MewtocolInterface("192.168.0.1"); + interf.WithRegisterCollection(new TestRegisterCollection()).WithPoller(); + + var register = interf.GetRegister(nameof(TestRegisterCollection.TestBoolInputXD)); + + //test generic properties + TestBasicGeneration(register, nameof(TestRegisterCollection.TestBoolInputXD), false, 0, "XD"); + + } + + [Fact(DisplayName = "Int16 generation")] + public void Int16Gen () { + + var interf = new MewtocolInterface("192.168.0.1"); + interf.WithRegisterCollection(new TestRegisterCollection()).WithPoller(); + + var register = interf.GetRegister(nameof(TestRegisterCollection.TestInt16)); + + //test generic properties + TestBasicGeneration(register, nameof(TestRegisterCollection.TestInt16), (short)0, 899, "DT899"); + + } + + [Fact(DisplayName = "UInt16 generation")] + public void UInt16Gen () { + + var interf = new MewtocolInterface("192.168.0.1"); + interf.WithRegisterCollection(new TestRegisterCollection()).WithPoller(); + + var register = interf.GetRegister(nameof(TestRegisterCollection.TestUInt16)); + + //test generic properties + TestBasicGeneration(register, nameof(TestRegisterCollection.TestUInt16), (ushort)0, 342, "DT342"); + + } + + [Fact(DisplayName = "Int32 generation")] + public void Int32Gen () { + + var interf = new MewtocolInterface("192.168.0.1"); + interf.WithRegisterCollection(new TestRegisterCollection()).WithPoller(); + + var register = interf.GetRegister(nameof(TestRegisterCollection.TestInt32)); + + //test generic properties + TestBasicGeneration(register, nameof(TestRegisterCollection.TestInt32), (int)0, 7001, "DDT7001"); + + } + + [Fact(DisplayName = "UInt32 generation")] + public void UInt32Gen () { + + var interf = new MewtocolInterface("192.168.0.1"); + interf.WithRegisterCollection(new TestRegisterCollection()).WithPoller(); + + var register = interf.GetRegister(nameof(TestRegisterCollection.TestUInt32)); + + //test generic properties + TestBasicGeneration(register, nameof(TestRegisterCollection.TestUInt32), (uint)0, 765, "DDT765"); + + } + + [Fact(DisplayName = "Float32 generation")] + public void Float32Gen () { + + var interf = new MewtocolInterface("192.168.0.1"); + interf.WithRegisterCollection(new TestRegisterCollection()).WithPoller(); + + var register = interf.GetRegister(nameof(TestRegisterCollection.TestFloat32)); + + //test generic properties + TestBasicGeneration(register, nameof(TestRegisterCollection.TestFloat32), (float)0, 7003, "DDT7003"); + + } + + [Fact(DisplayName = "String generation")] + public void StringGen () { + + var interf = new MewtocolInterface("192.168.0.1"); + interf.WithRegisterCollection(new TestRegisterCollection()).WithPoller(); + + var register = interf.GetRegister(nameof(TestRegisterCollection.TestString2)); + + //test generic properties + TestBasicGeneration(register, nameof(TestRegisterCollection.TestString2), null!, 7005, "DT7005"); + + Assert.Equal(5, ((SRegister)register).ReservedSize); + Assert.Equal(4, ((SRegister)register).MemoryLength); + + } + + [Fact(DisplayName = "BitArray 16bit generation")] + public void BitArray16Gen () { + + var interf = new MewtocolInterface("192.168.0.1"); + interf.WithRegisterCollection(new TestRegisterCollection()).WithPoller(); + + var register = interf.GetRegister($"Auto_Bitwise_DT7010"); + + //test generic properties + TestBasicGeneration(register, "Auto_Bitwise_DT7010", (short)0, 7010, "DT7010"); + + Assert.True(((NRegister)register).isUsedBitwise); + Assert.Equal(0, ((NRegister)register).MemoryLength); + + } + + [Fact(DisplayName = "BitArray 32bit generation")] + public void BitArray32Gen () { + + var interf = new MewtocolInterface("192.168.0.1"); + interf.WithRegisterCollection(new TestRegisterCollection()).WithPoller(); + + var register = interf.GetRegister($"Auto_Bitwise_DDT8010"); + + //test generic properties + TestBasicGeneration(register, "Auto_Bitwise_DDT8010", (int)0, 8010, "DDT8010"); + + Assert.True(((NRegister)register).isUsedBitwise); + Assert.Equal(1, ((NRegister)register).MemoryLength); + + } + + [Fact(DisplayName = "BitArray single bool generation")] + public void BitArraySingleBool16Gen () { + + var interf = new MewtocolInterface("192.168.0.1"); + interf.WithRegisterCollection(new TestRegisterCollection()).WithPoller(); + + var register = interf.GetRegister($"Auto_Bitwise_DT1204"); + + //test generic properties + TestBasicGeneration(register, "Auto_Bitwise_DT1204", (short)0, 1204, "DT1204"); + + Assert.True(((NRegister)register).isUsedBitwise); + + } + + [Fact(DisplayName = "TimeSpan generation")] + public void TimespanGen () { + + var interf = new MewtocolInterface("192.168.0.1"); + interf.WithRegisterCollection(new TestRegisterCollection()).WithPoller(); + + var register = interf.GetRegister(nameof(TestRegisterCollection.TestTime)); + + //test generic properties + TestBasicGeneration(register, nameof(TestRegisterCollection.TestTime), TimeSpan.Zero, 7012, "DDT7012"); + + } + + [Fact(DisplayName = "Enum16 generation")] + public void Enum16Gen () { + + var interf = new MewtocolInterface("192.168.0.1"); + interf.WithRegisterCollection(new TestRegisterCollection()).WithPoller(); + + var register = interf.GetRegister(nameof(TestRegisterCollection.TestEnum16)); + + //test generic properties + TestBasicGeneration(register, nameof(TestRegisterCollection.TestEnum16), (short)TestRegisterCollection.CurrentState.Undefined, 50, "DT50"); + + } + + [Fact(DisplayName = "Enum32 generation")] + public void Enum32Gen () { + + var interf = new MewtocolInterface("192.168.0.1"); + interf.WithRegisterCollection(new TestRegisterCollection()).WithPoller(); + + var register = interf.GetRegister(nameof(TestRegisterCollection.TestEnum32)); + + //test generic properties + TestBasicGeneration(register, nameof(TestRegisterCollection.TestEnum32), (int)TestRegisterCollection.CurrentState.Undefined, 51, "DDT51"); + + } + + } + +} \ No newline at end of file diff --git a/MewtocolTests/TestComProtocol.cs b/MewtocolTests/TestComProtocol.cs deleted file mode 100644 index ebe07c0..0000000 --- a/MewtocolTests/TestComProtocol.cs +++ /dev/null @@ -1,53 +0,0 @@ -using Xunit; - -using MewtocolNet; -using MewtocolNet.Registers; -using System.Diagnostics; -using Xunit.Abstractions; -using System.Collections; - -namespace MewtocolTests { - - public class TestComProtocol { - - private readonly ITestOutputHelper output; - - public TestComProtocol (ITestOutputHelper output) { - this.output = output; - } - - [Fact(DisplayName = "Numeric register protocol identifiers")] - public void NumericRegisterMewtocolIdentifiers () { - - List registers = new List { - new NRegister(50), - new NRegister(50), - new NRegister(50), - new NRegister(50), - new NRegister(50), - }; - - List expcectedIdents = new List { - "D0005000050", //single word register - "D0005000050", //single word register - "D0005000051", //double word register - "D0005000051", //double word register - "D0005000051", //double word register - "D0005000051", //double word register - }; - - //test mewtocol idents - for (int i = 0; i < registers.Count; i++) { - - Register? reg = registers[i]; - string expect = expcectedIdents[i]; - - Assert.Equal(expect, reg.BuildMewtocolIdent()); - - } - - } - - } - -} \ No newline at end of file diff --git a/MewtocolTests/TestClient.cs b/MewtocolTests/TestLivePLC.cs similarity index 96% rename from MewtocolTests/TestClient.cs rename to MewtocolTests/TestLivePLC.cs index 3abdde4..eea5f52 100644 --- a/MewtocolTests/TestClient.cs +++ b/MewtocolTests/TestLivePLC.cs @@ -10,7 +10,7 @@ using Xunit.Abstractions; namespace MewtocolTests { - public class TestClient { + public class TestLivePLC { private readonly ITestOutputHelper output; @@ -37,7 +37,7 @@ namespace MewtocolTests { }; - public TestClient (ITestOutputHelper output) { + public TestLivePLC (ITestOutputHelper output) { this.output = output; diff --git a/MewtocolTests/TestRegisterInterface.cs b/MewtocolTests/TestRegisterInterface.cs new file mode 100644 index 0000000..f3acba8 --- /dev/null +++ b/MewtocolTests/TestRegisterInterface.cs @@ -0,0 +1,164 @@ +using Xunit; + +using MewtocolNet; +using MewtocolNet.Registers; +using Xunit.Abstractions; + +namespace MewtocolTests { + + public class TestRegisterInterface { + + private readonly ITestOutputHelper output; + + public TestRegisterInterface (ITestOutputHelper output) { + this.output = output; + } + + [Fact(DisplayName = "Numeric mewtocol query building")] + 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), + }; + + List expcectedIdents = new List { + "D0005000050", //single word register + "D0005000050", //single word register + "D0005000051", //double word register + "D0005000051", //double word register + "D0005000051", //double word register + "D0005000051", //double word register + }; + + //test mewtocol idents + for (int i = 0; i < registers.Count; i++) { + + IRegister? reg = registers[i]; + string expect = expcectedIdents[i]; + + Assert.Equal(expect, reg.BuildMewtocolQuery()); + + } + + } + + [Fact(DisplayName = "PLC register naming convention test")] + public void PLCRegisterIdentifiers () { + + 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), + //boolean + new BRegister(100), + new BRegister(5, RegisterType.X), + new BRegister(SpecialAddress.A, RegisterType.X), + //string + new SRegister(999, 5), + }; + + List expcectedIdents = new List { + "DT50", + "DT60", + "DDT70", + "DDT80", + "DDT90", + "DDT100", + "R100", + "X5", + "XA", + "DT999" + }; + + //test mewtocol idents + for (int i = 0; i < registers.Count; i++) { + + IRegister? reg = registers[i]; + string expect = expcectedIdents[i]; + + Assert.Equal(expect, reg.GetRegisterPLCName()); + + } + + } + + [Fact(DisplayName = "Non allowed (Overflow address)")] + public void OverFlowRegisterAddress () { + + var ex = Assert.Throws(() => { + + new NRegister(100000, _name: null); + + }); + + output.WriteLine(ex.Message.ToString()); + + var ex1 = Assert.Throws(() => { + + new BRegister(100000); + + }); + + output.WriteLine(ex1.Message.ToString()); + + var ex2 = Assert.Throws(() => { + + new SRegister(100000, 5); + + }); + + output.WriteLine(ex2.Message.ToString()); + + } + + [Fact(DisplayName = "Non allowed (Wrong data type)")] + public void WrongDataTypeRegister () { + + var ex = Assert.Throws(() => { + + new NRegister(100, _name: null); + + }); + + output.WriteLine(ex.Message.ToString()); + + } + + [Fact(DisplayName = "Non allowed (Wrong bool type address)")] + public void WrongDataTypeRegisterBool1 () { + + var ex = Assert.Throws(() => { + + new BRegister(100, RegisterType.DDT_int); + + }); + + output.WriteLine(ex.Message.ToString()); + + } + + [Fact(DisplayName = "Non allowed (Wrong bool special address)")] + public void WrongDataTypeRegisterBool2 () { + + var ex = Assert.Throws(() => { + + new BRegister(SpecialAddress.None, RegisterType.X); + + }); + + output.WriteLine(ex.Message.ToString()); + + } + + } + +} \ No newline at end of file diff --git a/PLC_Test/test_c30_fpx_h.ini b/PLC_Test/test_c30_fpx_h.ini new file mode 100644 index 0000000..e810e50 Binary files /dev/null and b/PLC_Test/test_c30_fpx_h.ini differ diff --git a/PLC_Test/test_c30_fpx_h.pro b/PLC_Test/test_c30_fpx_h.pro new file mode 100644 index 0000000..fb08732 Binary files /dev/null and b/PLC_Test/test_c30_fpx_h.pro differ diff --git a/PLC_Test/test_c30_fpx_h.xml b/PLC_Test/test_c30_fpx_h.xml new file mode 100644 index 0000000..4109899 --- /dev/null +++ b/PLC_Test/test_c30_fpx_h.xml @@ -0,0 +1,11 @@ + + + + + + + + + + +