From 6c7e91f648c38ceb1434db30a4c0b3c979b719ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Wei=C3=9F?= <72068105+Sandoun@users.noreply.github.com> Date: Wed, 12 Jul 2023 00:32:56 +0200 Subject: [PATCH] Add underlying byte data for registers - change backend logic for register r/w - remade interface builder pattern for better syntactic sugar - refined tests --- .../Commands/OnlineCommands/OnlineCommand.cs | 2 +- MewTerminal/Commands/ScanCommand.cs | 2 +- MewTerminal/Program.cs | 1 + MewtocolNet/Helpers/MewtocolHelpers.cs | 15 +- MewtocolNet/IPlc.cs | 5 + MewtocolNet/IPlcEthernet.cs | 12 - MewtocolNet/IPlcSerial.cs | 12 - MewtocolNet/Mewtocol.cs | 132 +++++++- MewtocolNet/MewtocolInterface.cs | 13 +- .../MewtocolInterfaceRegisterHandling.cs | 262 +++++++-------- MewtocolNet/MewtocolInterfaceRequests.cs | 2 +- MewtocolNet/MewtocolInterfaceSerial.cs | 9 +- MewtocolNet/MewtocolInterfaceTcp.cs | 18 +- .../PollFrequencyAttribute.cs | 20 ++ .../RegisterAttributes/RegisterAttribute.cs | 66 ---- .../RegisterCollectionCollector.cs | 31 ++ .../RegisterAttributes/RegisterPropTarget.cs | 26 ++ .../RegisterBuilding/RegisterBuildInfo.cs | 86 +++-- MewtocolNet/Registers/BaseRegister.cs | 67 ++-- MewtocolNet/Registers/BoolRegister.cs | 32 +- MewtocolNet/Registers/BytesRegister.cs | 29 +- .../Registers/Interfaces/IRegisterInternal.cs | 9 +- MewtocolNet/Registers/NumberRegister.cs | 26 +- MewtocolNet/Registers/StringRegister.cs | 3 +- MewtocolNet/TypeConversion/Conversions.cs | 7 +- MewtocolNet/UnderlyingRegisters/DTArea.cs | 152 +++++++++ .../UnderlyingRegisters/IMemoryArea.cs | 16 + .../UnderlyingRegisters/MemoryAreaManager.cs | 318 ++++++++++++++++++ MewtocolNet/UnderlyingRegisters/WRArea.cs | 83 +++++ MewtocolTests/AutomatedPropertyRegisters.cs | 114 ++++--- .../TestRegisterCollection.cs | 59 ++-- MewtocolTests/TestLivePLC.cs | 6 +- 32 files changed, 1172 insertions(+), 463 deletions(-) create mode 100644 MewtocolNet/RegisterAttributes/PollFrequencyAttribute.cs create mode 100644 MewtocolNet/RegisterAttributes/RegisterCollectionCollector.cs create mode 100644 MewtocolNet/RegisterAttributes/RegisterPropTarget.cs create mode 100644 MewtocolNet/UnderlyingRegisters/DTArea.cs create mode 100644 MewtocolNet/UnderlyingRegisters/IMemoryArea.cs create mode 100644 MewtocolNet/UnderlyingRegisters/MemoryAreaManager.cs create mode 100644 MewtocolNet/UnderlyingRegisters/WRArea.cs diff --git a/MewTerminal/Commands/OnlineCommands/OnlineCommand.cs b/MewTerminal/Commands/OnlineCommands/OnlineCommand.cs index 4f60c8f..6f45eaf 100644 --- a/MewTerminal/Commands/OnlineCommands/OnlineCommand.cs +++ b/MewTerminal/Commands/OnlineCommands/OnlineCommand.cs @@ -25,7 +25,7 @@ internal class OnlineCommand : CommandLineExcecuteable { string ip = split[0]; int port = int.Parse(split[1]); - using (var plc = Mewtocol.Ethernet(ip, port)) { + using (var plc = Mewtocol.Ethernet(ip, port).Build()) { await AfterSetup(plc); diff --git a/MewTerminal/Commands/ScanCommand.cs b/MewTerminal/Commands/ScanCommand.cs index d197c68..f2e2809 100644 --- a/MewTerminal/Commands/ScanCommand.cs +++ b/MewTerminal/Commands/ScanCommand.cs @@ -45,7 +45,7 @@ internal class ScanCommand : CommandLineExcecuteable { ctx.Status($"Getting cassette PLC {item.Cassette.IPAddress}:{item.Cassette.Port}") .Spinner(Spinner.Known.Dots); - var dev = Mewtocol.Ethernet(item.Cassette.IPAddress, item.Cassette.Port); + var dev = Mewtocol.Ethernet(item.Cassette.IPAddress, item.Cassette.Port).Build(); dev.ConnectTimeout = 1000; await dev.ConnectAsync(); item.PLCInf = dev.PlcInfo; diff --git a/MewTerminal/Program.cs b/MewTerminal/Program.cs index f12e52a..143ec4d 100644 --- a/MewTerminal/Program.cs +++ b/MewTerminal/Program.cs @@ -1,6 +1,7 @@ using CommandLine; using CommandLine.Text; using MewTerminal.Commands; +using MewTerminal.Commands.OnlineCommands; using MewtocolNet.Logging; using Spectre.Console; using System.Globalization; diff --git a/MewtocolNet/Helpers/MewtocolHelpers.cs b/MewtocolNet/Helpers/MewtocolHelpers.cs index 6a144c9..f27f1ff 100644 --- a/MewtocolNet/Helpers/MewtocolHelpers.cs +++ b/MewtocolNet/Helpers/MewtocolHelpers.cs @@ -1,4 +1,5 @@ using MewtocolNet.DocAttributes; +using MewtocolNet.Registers; using System; using System.Collections; using System.Collections.Generic; @@ -118,7 +119,9 @@ namespace MewtocolNet { /// A or null of failed internal static byte[] ParseDTRawStringAsBytes (this string _onString) { - var res = new Regex(@"\%([0-9]{2})\$RD(?.*)(?..)..").Match(_onString); + _onString = _onString.Replace("\r", ""); + + var res = new Regex(@"\%([0-9]{2})\$RD(?.*)(?..)").Match(_onString); if (res.Success) { string val = res.Groups["data"].Value; @@ -248,6 +251,16 @@ namespace MewtocolNet { } + internal static bool CompareIsDuplicateNonCast (this BaseRegister reg1, BaseRegister compare) { + + bool valCompare = reg1.GetType() != compare.GetType() && + reg1.MemoryAddress == compare.MemoryAddress && + reg1.GetSpecialAddress() == compare.GetSpecialAddress(); + + return valCompare; + + } + internal static bool CompareIsNameDuplicate(this IRegisterInternal reg1, IRegisterInternal compare) { return ( reg1.Name != null || compare.Name != null) && reg1.Name == compare.Name; diff --git a/MewtocolNet/IPlc.cs b/MewtocolNet/IPlc.cs index b92a434..cfa3fcb 100644 --- a/MewtocolNet/IPlc.cs +++ b/MewtocolNet/IPlc.cs @@ -124,6 +124,11 @@ namespace MewtocolNet { /// IEnumerable GetAllRegisters(); + /// + /// Explains the register internal layout at this moment in time + /// + string Explain(); + } } diff --git a/MewtocolNet/IPlcEthernet.cs b/MewtocolNet/IPlcEthernet.cs index dd95ddf..0fa9ec4 100644 --- a/MewtocolNet/IPlcEthernet.cs +++ b/MewtocolNet/IPlcEthernet.cs @@ -38,18 +38,6 @@ namespace MewtocolNet { /// Station Number of the PLC void ConfigureConnection(string _ip, int _port = 9094, int _station = 1); - /// - /// Attaches a poller to the interface - /// - IPlcEthernet WithPoller(); - - /// - /// Attaches a register collection object to - /// the interface that can be updated automatically. - /// - /// The type of the collection base class - IPlcEthernet AddRegisterCollection(RegisterCollection collection); - } } diff --git a/MewtocolNet/IPlcSerial.cs b/MewtocolNet/IPlcSerial.cs index 609bf40..760a9fa 100644 --- a/MewtocolNet/IPlcSerial.cs +++ b/MewtocolNet/IPlcSerial.cs @@ -58,18 +58,6 @@ namespace MewtocolNet { /// Task ConnectAsync(Action onTryingConfig); - /// - /// Attaches a poller to the interface - /// - IPlcSerial WithPoller(); - - /// - /// Attaches a register collection object to - /// the interface that can be updated automatically. - /// - /// The type of the collection base class - IPlcSerial AddRegisterCollection(RegisterCollection collection); - } } diff --git a/MewtocolNet/Mewtocol.cs b/MewtocolNet/Mewtocol.cs index ca88908..f9d34c1 100644 --- a/MewtocolNet/Mewtocol.cs +++ b/MewtocolNet/Mewtocol.cs @@ -1,6 +1,8 @@ using MewtocolNet.Exceptions; +using MewtocolNet.RegisterAttributes; using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.IO.Ports; using System.Linq; using System.Net; @@ -20,11 +22,13 @@ namespace MewtocolNet { /// /// Plc station number /// - public static IPlcEthernet Ethernet (string ip, int port = 9094, int station = 1) { + public static PostInit Ethernet (string ip, int port = 9094, int station = 1) { var instance = new MewtocolInterfaceTcp(); instance.ConfigureConnection(ip, port, station); - return instance; + return new PostInit { + intf = instance + }; } @@ -35,11 +39,13 @@ namespace MewtocolNet { /// /// Plc station number /// - public static IPlcEthernet Ethernet(IPAddress ip, int port = 9094, int station = 1) { + public static PostInit Ethernet(IPAddress ip, int port = 9094, int station = 1) { var instance = new MewtocolInterfaceTcp(); instance.ConfigureConnection(ip, port, station); - return instance; + return new PostInit { + intf = instance + }; } @@ -53,13 +59,15 @@ namespace MewtocolNet { /// /// /// - public static IPlcSerial Serial (string portName, BaudRate baudRate = BaudRate._19200, DataBits dataBits = DataBits.Eight, Parity parity = Parity.Odd, StopBits stopBits = StopBits.One, int station = 1) { + public static PostInit Serial (string portName, BaudRate baudRate = BaudRate._19200, DataBits dataBits = DataBits.Eight, Parity parity = Parity.Odd, StopBits stopBits = StopBits.One, int station = 1) { TestPortName(portName); var instance = new MewtocolInterfaceSerial(); instance.ConfigureConnection(portName, (int)baudRate, (int)dataBits, parity, stopBits, station); - return instance; + return new PostInit { + intf = instance + }; } @@ -69,14 +77,16 @@ namespace MewtocolNet { /// /// /// - public static IPlcSerial SerialAuto (string portName, int station = 1) { + public static PostInit SerialAuto (string portName, int station = 1) { TestPortName(portName); var instance = new MewtocolInterfaceSerial(); instance.ConfigureConnection(portName, station); instance.ConfigureConnectionAuto(); - return instance; + return new PostInit { + intf = instance + }; } @@ -89,6 +99,112 @@ namespace MewtocolNet { } + public class MemoryManagerSettings { + + /// + /// + /// This feature can improve read write times by a big margin but also + /// block outgoing messages inbetween polling cycles more frequently + /// + /// The max distance of the gap between registers (if there is a gap between + /// adjacent registers) to merge them into one request
+ /// Example:
+ /// + /// We have a register at DT100 (1 word long) and a + /// register at DT101 (1 word long)
+ /// - If the max distance is 0 it will not merge them into one request
+ /// - If the max distance is 1 it will merge them into one request
+ /// - If the max distance is 2 and the next register is at DT102 it will also merge them and ignore the spacer byte in the response
+ ///
+ ///
+ + public int MaxOptimizationDistance { get; set; } = 8; + + /// + /// The max number of registers per request group + /// + public int MaxRegistersPerGroup { get; set; } = -1; + + } + + public class PostInit { + + internal T intf; + + /// + /// Attaches a auto poller to the interface that reads all registers + /// cyclic + /// + /// + public PostInit WithPoller() { + + if (intf is MewtocolInterface imew) { + imew.usePoller = true; + } + + return this; + + } + + /// + /// General setting for the memory manager + /// + public PostInit WithMemoryManagerSettings (Action settings) { + + var res = new MemoryManagerSettings(); + settings.Invoke(res); + + if (res.MaxOptimizationDistance < 0) + throw new NotSupportedException($"A value lower than 0 is not allowed for " + + $"{nameof(MemoryManagerSettings.MaxOptimizationDistance)}"); + + if (intf is MewtocolInterface imew) { + + imew.memoryManager.maxOptimizationDistance = res.MaxOptimizationDistance; + imew.memoryManager.maxRegistersPerGroup = res.MaxRegistersPerGroup; + + } + + return this; + + } + + /// + /// A builder for attaching register collections + /// + public EndInit WithRegisterCollections(Action collector) { + + var res = new RegisterCollectionCollector(); + collector.Invoke(res); + + if (intf is MewtocolInterface imew) { + imew.WithRegisterCollections(res.collections); + } + + return new EndInit { + postInit = this + }; + + } + + /// + /// Builds and returns the final plc interface + /// + public T Build() => intf; + + } + + public class EndInit { + + internal PostInit postInit; + + /// + /// Builds and returns the final plc interface + /// + public T Build() => postInit.intf; + + } + } } diff --git a/MewtocolNet/MewtocolInterface.cs b/MewtocolNet/MewtocolInterface.cs index a1ac273..0aa006f 100644 --- a/MewtocolNet/MewtocolInterface.cs +++ b/MewtocolNet/MewtocolInterface.cs @@ -11,10 +11,11 @@ using System.Runtime.CompilerServices; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; +using MewtocolNet.UnderlyingRegisters; namespace MewtocolNet { - public partial class MewtocolInterface : IPlc, INotifyPropertyChanged, IDisposable { + public abstract partial class MewtocolInterface : IPlc, INotifyPropertyChanged, IDisposable { #region Private fields @@ -49,6 +50,7 @@ namespace MewtocolNet { internal volatile bool pollerTaskStopped = true; internal volatile bool pollerFirstCycle; internal bool usePoller = false; + internal MemoryAreaManager memoryManager; internal List RegistersUnderlying { get; private set; } = new List(); internal IEnumerable RegistersInternal => RegistersUnderlying.Cast(); @@ -142,6 +144,8 @@ namespace MewtocolNet { private protected MewtocolInterface () { + memoryManager = new MemoryAreaManager(this); + Connected += MewtocolInterface_Connected; RegisterChanged += OnRegisterChanged; @@ -164,6 +168,8 @@ namespace MewtocolNet { $"{(o.Name != null ? $"({o.Name}) " : "")}" + $"changed to \"{asInternal.GetValueString()}\"", LogLevel.Change, this); + OnRegisterChangedUpdateProps((IRegisterInternal)o); + } /// @@ -352,8 +358,9 @@ namespace MewtocolNet { } //request next frame - var writeBuffer = Encoding.UTF8.GetBytes("%01**&\r"); + var writeBuffer = Encoding.UTF8.GetBytes($"%{GetStationNumber()}**&\r"); await stream.WriteAsync(writeBuffer, 0, writeBuffer.Length); + Logger.Log($">> Requested next frame", LogLevel.Critical, this); wasMultiFramedResponse = true; } @@ -521,6 +528,8 @@ namespace MewtocolNet { #endregion + public string Explain() => memoryManager.ExplainLayout(); + } } diff --git a/MewtocolNet/MewtocolInterfaceRegisterHandling.cs b/MewtocolNet/MewtocolInterfaceRegisterHandling.cs index 8c188bf..fa19a1b 100644 --- a/MewtocolNet/MewtocolInterfaceRegisterHandling.cs +++ b/MewtocolNet/MewtocolInterfaceRegisterHandling.cs @@ -3,9 +3,11 @@ using MewtocolNet.Logging; using MewtocolNet.RegisterAttributes; using MewtocolNet.RegisterBuilding; using MewtocolNet.Registers; +using MewtocolNet.UnderlyingRegisters; using System; using System.Collections; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Diagnostics; using System.Linq; using System.Reflection; @@ -18,7 +20,7 @@ namespace MewtocolNet { /// /// The PLC com interface class /// - public partial class MewtocolInterface { + public abstract partial class MewtocolInterface { internal Task pollCycleTask; @@ -30,14 +32,16 @@ namespace MewtocolNet { /// /// Current poller cycle duration /// - public int PollerCycleDurationMs { - get => pollerCycleDurationMs; + public int PollerCycleDurationMs { + get => pollerCycleDurationMs; private set { pollerCycleDurationMs = value; OnPropChange(); } } + private List registerCollections = new List(); + #region Register Polling /// @@ -70,7 +74,7 @@ namespace MewtocolNet { /// useful if you want to use a custom update frequency /// /// The number of inidvidual mewtocol commands sent - public async Task RunPollerCylceManual () { + public async Task RunPollerCylceManual() { if (!pollerTaskStopped) throw new NotSupportedException($"The poller is already running, " + @@ -86,7 +90,7 @@ namespace MewtocolNet { } //polls all registers one by one (slow) - internal async Task Poll () { + internal async Task Poll() { Logger.Log("Poller is attaching", LogLevel.Info, this); @@ -111,13 +115,15 @@ namespace MewtocolNet { } - private async Task OnMultiFrameCycle () { + private async Task OnMultiFrameCycle() { var sw = Stopwatch.StartNew(); - await UpdateRCPRegisters(); + //await UpdateRCPRegisters(); - await UpdateDTRegisters(); + //await UpdateDTRegisters(); + + await memoryManager.PollAllAreasAsync(); await GetPLCInfoAsync(); @@ -130,7 +136,7 @@ namespace MewtocolNet { #region Smart register polling methods - private async Task UpdateRCPRegisters () { + private async Task UpdateRCPRegisters() { //build booleans var rcpList = RegistersUnderlying.Where(x => x.GetType() == typeof(BoolRegister)) @@ -145,7 +151,7 @@ namespace MewtocolNet { int toReadRegistersCount = 8; - if(i == rcpFrameCount - 1) toReadRegistersCount = rcpLastFrameRemainder; + if (i == rcpFrameCount - 1) toReadRegistersCount = rcpLastFrameRemainder; var rcpString = new StringBuilder($"%{GetStationNumber()}#RCP{toReadRegistersCount}"); @@ -166,23 +172,23 @@ namespace MewtocolNet { var register = rcpList[i + k]; - if((bool)register.Value != resultBitArray[k]) { + if ((bool)register.Value != resultBitArray[k]) { register.SetValueFromPLC(resultBitArray[k]); } } - + } } - private async Task UpdateDTRegisters () { + private async Task UpdateDTRegisters() { foreach (var reg in RegistersUnderlying) { var type = reg.GetType(); - if(reg.RegisterType.IsNumericDTDDT() || reg.RegisterType == RegisterType.DT_BYTE_RANGE) { + if (reg.RegisterType.IsNumericDTDDT() || reg.RegisterType == RegisterType.DT_BYTE_RANGE) { var lastVal = reg.Value; var rwReg = (IRegisterInternal)reg; @@ -203,144 +209,81 @@ namespace MewtocolNet { #region Register Colleciton adding - internal MewtocolInterface WithRegisterCollection (RegisterCollection collection) { + /// + /// Adds the given register collection and all its registers with attributes to the register list + /// + internal void WithRegisterCollections(List collections) { - collection.PLCInterface = this; + if (registerCollections.Count != 0) + throw new NotSupportedException("Register collections can only be build once"); - var props = collection.GetType().GetProperties(); + List buildInfos = new List(); - foreach (var prop in props) { + foreach (var collection in collections) { - var attributes = prop.GetCustomAttributes(true); + collection.PLCInterface = this; - string propName = prop.Name; - foreach (var attr in attributes) { + var props = collection.GetType().GetProperties(); - if (attr is RegisterAttribute cAttribute) { + foreach (var prop in props) { + + var attributes = prop.GetCustomAttributes(true); + + string propName = prop.Name; + foreach (var attr in attributes) { + + if (attr is RegisterAttribute cAttribute) { + + if (!prop.PropertyType.IsAllowedPlcCastingType()) { + throw new MewtocolException($"The register attribute property type is not allowed ({prop.PropertyType})"); + } + + var dotnetType = prop.PropertyType; + + buildInfos.Add(new RegisterBuildInfo { + mewAddress = cAttribute.MewAddress, + dotnetCastType = dotnetType.IsEnum ? dotnetType.UnderlyingSystemType : dotnetType, + collectionTarget = collection, + boundPropTarget = prop, + }); - if(!prop.PropertyType.IsAllowedPlcCastingType()) { - throw new MewtocolException($"The register attribute property type is not allowed ({prop.PropertyType})"); } - var dotnetType = prop.PropertyType; - - AddRegister(new RegisterBuildInfo { - mewAddress = cAttribute.MewAddress, - memoryAddress = cAttribute.MemoryArea, - specialAddress = cAttribute.SpecialAddress, - memorySizeBytes = cAttribute.ByteLength, - registerType = cAttribute.RegisterType, - dotnetCastType = dotnetType.IsEnum ? dotnetType.UnderlyingSystemType : dotnetType, - collectionType = collection.GetType(), - name = prop.Name, - }); - } } + if (collection != null) { + registerCollections.Add(collection); + collection.OnInterfaceLinked(this); + } + + Connected += (i) => { + if (collection != null) + collection.OnInterfaceLinkedAndOnline(this); + }; + } - RegisterChanged += (reg) => { + AddRegisters(buildInfos); - //register is used bitwise - if (reg.GetType() == typeof(BytesRegister)) { + } - for (int i = 0; i < props.Length; i++) { + /// + /// Writes back the values changes of the underlying registers to the corrosponding property + /// + private void OnRegisterChangedUpdateProps(IRegisterInternal reg) { - var prop = props[i]; - var bitWiseFound = prop.GetCustomAttributes(true) - .FirstOrDefault(y => y.GetType() == typeof(RegisterAttribute) && ((RegisterAttribute)y).MemoryArea == reg.MemoryAddress); + var collection = reg.ContainedCollection; + if (collection == null) return; - if (bitWiseFound != null) { + var props = collection.GetType().GetProperties(); - var casted = (RegisterAttribute)bitWiseFound; - var bitIndex = casted.AssignedBitIndex; + //set the specific bit array if needed + //prop.SetValue(collection, bitAr); + //collection.TriggerPropertyChanged(prop.Name); - BitArray bitAr = null; - if (reg is NumberRegister reg16) { - var bytes = BitConverter.GetBytes((short)reg16.Value); - bitAr = new BitArray(bytes); - } else if (reg is NumberRegister reg32) { - var bytes = BitConverter.GetBytes((int)reg32.Value); - bitAr = new BitArray(bytes); - } - - if (bitAr != null && bitIndex < bitAr.Length && bitIndex >= 0) { - - //set the specific bit index if needed - prop.SetValue(collection, bitAr[bitIndex]); - collection.TriggerPropertyChanged(prop.Name); - - } else if (bitAr != null) { - - //set the specific bit array if needed - prop.SetValue(collection, bitAr); - collection.TriggerPropertyChanged(prop.Name); - - } - - } - - } - - } - - //updating normal properties - var foundToUpdate = props.FirstOrDefault(x => x.Name == reg.Name); - - if (foundToUpdate != null) { - - var foundAttributes = foundToUpdate.GetCustomAttributes(true); - var foundAttr = foundAttributes.FirstOrDefault(x => x.GetType() == typeof(RegisterAttribute)); - - if (foundAttr == null) - return; - - var registerAttr = (RegisterAttribute)foundAttr; - - //check if bit parse mode - if (registerAttr.AssignedBitIndex == -1) { - - HashSet NumericTypes = new HashSet { - typeof(bool), - typeof(short), - typeof(ushort), - typeof(int), - typeof(uint), - typeof(float), - typeof(TimeSpan), - typeof(string) - }; - - var regValue = ((IRegister)reg).Value; - - if (NumericTypes.Any(x => foundToUpdate.PropertyType == x)) { - foundToUpdate.SetValue(collection, regValue); - } - - if (foundToUpdate.PropertyType.IsEnum) { - foundToUpdate.SetValue(collection, regValue); - } - - } - - collection.TriggerPropertyChanged(foundToUpdate.Name); - - } - - }; - - if (collection != null) - collection.OnInterfaceLinked(this); - - Connected += (i) => { - if (collection != null) - collection.OnInterfaceLinkedAndOnline(this); - }; - - return this; } @@ -349,10 +292,10 @@ namespace MewtocolNet { #region Register Adding /// - public void AddRegister(IRegister register) => AddRegister(register as BaseRegister); + public void AddRegister (IRegister register) => AddRegister(register as BaseRegister); /// - public void AddRegister(BaseRegister register) { + public void AddRegister (BaseRegister register) { if (CheckDuplicateRegister(register)) throw MewtocolException.DupeRegister(register); @@ -368,28 +311,56 @@ namespace MewtocolNet { } - internal void AddRegister (RegisterBuildInfo buildInfo) { + // Used for internal property based register building + internal void AddRegisters (List buildInfos) { - var builtRegister = buildInfo.Build(); + //build all from attribute + List registers = new List(); - //is bitwise and the register list already contains that area register - if(builtRegister.GetType() == typeof(BytesRegister) && CheckDuplicateRegister(builtRegister, out var existing)) { + foreach (var buildInfo in buildInfos) { - return; + var builtRegister = buildInfo.BuildForCollectionAttribute(); + + int? linkLen = null; + + if(builtRegister is BytesRegister bReg) { + + linkLen = (int?)bReg.ReservedBytesSize ?? bReg.ReservedBitSize; + + } + + //attach the property and collection + builtRegister.WithBoundProperty(new RegisterPropTarget { + BoundProperty = buildInfo.boundPropTarget, + LinkLength = linkLen, + }); + + builtRegister.WithRegisterCollection(buildInfo.collectionTarget); + + builtRegister.attachedInterface = this; + registers.Add(builtRegister); } - if (CheckDuplicateRegister(builtRegister)) - throw MewtocolException.DupeRegister(builtRegister); + //order by address + registers = registers.OrderBy(x => x.GetSpecialAddress()).ToList(); + registers = registers.OrderBy(x => x.MemoryAddress).ToList(); - if(CheckDuplicateNameRegister(builtRegister)) - throw MewtocolException.DupeNameRegister(builtRegister); + //link to memory manager + for (int i = 0, j = 0; i < registers.Count; i++) { - if (CheckOverlappingRegister(builtRegister, out var regB)) - throw MewtocolException.OverlappingRegister(builtRegister, regB); + BaseRegister reg = registers[i]; + reg.name = $"auto_prop_register_{j + 1}"; - builtRegister.attachedInterface = this; - RegistersUnderlying.Add(builtRegister); + //link the memory area to the register + if (memoryManager.LinkRegister(reg)) { + + RegistersUnderlying.Add(reg); + j++; + + } + + } } @@ -480,7 +451,6 @@ namespace MewtocolNet { } - internal void PropertyRegisterWasSet(string propName, object value) { _ = SetRegisterAsync(GetRegister(propName), value); diff --git a/MewtocolNet/MewtocolInterfaceRequests.cs b/MewtocolNet/MewtocolInterfaceRequests.cs index 6e3f7d7..7dadf6a 100644 --- a/MewtocolNet/MewtocolInterfaceRequests.cs +++ b/MewtocolNet/MewtocolInterfaceRequests.cs @@ -12,7 +12,7 @@ using System.Threading.Tasks; namespace MewtocolNet { - public partial class MewtocolInterface { + public abstract partial class MewtocolInterface { #region PLC info getters diff --git a/MewtocolNet/MewtocolInterfaceSerial.cs b/MewtocolNet/MewtocolInterfaceSerial.cs index 62abbc8..008d385 100644 --- a/MewtocolNet/MewtocolInterfaceSerial.cs +++ b/MewtocolNet/MewtocolInterfaceSerial.cs @@ -14,7 +14,7 @@ using MewtocolNet.RegisterAttributes; namespace MewtocolNet { - public class MewtocolInterfaceSerial : MewtocolInterface, IPlcSerial { + public sealed class MewtocolInterfaceSerial : MewtocolInterface, IPlcSerial { private bool autoSerial; @@ -50,13 +50,6 @@ namespace MewtocolNet { } - public IPlcSerial AddRegisterCollection (RegisterCollection collection) { - - WithRegisterCollection(collection); - return this; - - } - /// public override string GetConnectionInfo() { diff --git a/MewtocolNet/MewtocolInterfaceTcp.cs b/MewtocolNet/MewtocolInterfaceTcp.cs index f90181b..c047f11 100644 --- a/MewtocolNet/MewtocolInterfaceTcp.cs +++ b/MewtocolNet/MewtocolInterfaceTcp.cs @@ -11,7 +11,7 @@ namespace MewtocolNet { /// /// The PLC com interface class /// - public class MewtocolInterfaceTcp : MewtocolInterface, IPlcEthernet { + public sealed class MewtocolInterfaceTcp : MewtocolInterface, IPlcEthernet { //TCP internal TcpClient client; @@ -29,22 +29,6 @@ namespace MewtocolNet { internal MewtocolInterfaceTcp () : base() { } - /// - public IPlcEthernet WithPoller () { - - usePoller = true; - return this; - - } - - /// - public IPlcEthernet AddRegisterCollection (RegisterCollection collection) { - - WithRegisterCollection(collection); - return this; - - } - #region TCP connection state handling /// diff --git a/MewtocolNet/RegisterAttributes/PollFrequencyAttribute.cs b/MewtocolNet/RegisterAttributes/PollFrequencyAttribute.cs new file mode 100644 index 0000000..981e3ee --- /dev/null +++ b/MewtocolNet/RegisterAttributes/PollFrequencyAttribute.cs @@ -0,0 +1,20 @@ +using System; + +namespace MewtocolNet.RegisterAttributes { + /// + /// Defines the behavior of a register property + /// + [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] + public class PollFrequencyAttribute : Attribute { + + internal int skipEachCycle; + + public PollFrequencyAttribute(int eachCycleN) { + + skipEachCycle = eachCycleN; + + } + + } + +} diff --git a/MewtocolNet/RegisterAttributes/RegisterAttribute.cs b/MewtocolNet/RegisterAttributes/RegisterAttribute.cs index a08b29a..5546f82 100644 --- a/MewtocolNet/RegisterAttributes/RegisterAttribute.cs +++ b/MewtocolNet/RegisterAttributes/RegisterAttribute.cs @@ -9,13 +9,6 @@ namespace MewtocolNet.RegisterAttributes { [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] public class RegisterAttribute : Attribute { - internal RegisterType? RegisterType; - - internal uint MemoryArea = 0; - internal uint ByteLength = 2; - internal byte SpecialAddress = 0x0; - - internal BitCount BitCount; internal int AssignedBitIndex = -1; internal string MewAddress = null; @@ -26,65 +19,6 @@ namespace MewtocolNet.RegisterAttributes { } - /// - /// Attribute for string type or numeric registers - /// - /// The area in the plcs memory - public RegisterAttribute(uint memoryArea) { - - MemoryArea = memoryArea; - - } - - public RegisterAttribute(uint memoryArea, uint byteLength) { - - MemoryArea = memoryArea; - ByteLength = byteLength; - - } - - public RegisterAttribute(uint memoryArea, BitCount bitCount) { - - MemoryArea = memoryArea; - BitCount = bitCount; - AssignedBitIndex = 0; - - RegisterType = BitCount == BitCount.B16 ? MewtocolNet.RegisterType.DT : MewtocolNet.RegisterType.DDT; - - } - - public RegisterAttribute(uint memoryArea, BitCount bitCount, int bitIndex) { - - MemoryArea = memoryArea; - BitCount = bitCount; - AssignedBitIndex = bitIndex; - - RegisterType = BitCount == BitCount.B16 ? MewtocolNet.RegisterType.DT : MewtocolNet.RegisterType.DDT; - - } - - /// - /// Attribute for boolean registers - /// - public RegisterAttribute(IOType type, byte spAdress = 0x0) { - - MemoryArea = 0; - RegisterType = (RegisterType)(int)type; - SpecialAddress = spAdress; - - } - - /// - /// Attribute for boolean registers - /// - public RegisterAttribute(IOType type, uint memoryArea, byte spAdress = 0x0) { - - MemoryArea = memoryArea; - RegisterType = (RegisterType)(int)type; - SpecialAddress = spAdress; - - } - } } diff --git a/MewtocolNet/RegisterAttributes/RegisterCollectionCollector.cs b/MewtocolNet/RegisterAttributes/RegisterCollectionCollector.cs new file mode 100644 index 0000000..e7ce46e --- /dev/null +++ b/MewtocolNet/RegisterAttributes/RegisterCollectionCollector.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MewtocolNet.RegisterAttributes { + + public class RegisterCollectionCollector { + + internal List collections = new List(); + + public RegisterCollectionCollector AddCollection (RegisterCollection collection) { + + collections.Add(collection); + + return this; + + } + + public RegisterCollectionCollector AddCollection () where T : RegisterCollection { + + var instance = (RegisterCollection)Activator.CreateInstance(typeof(T)); + + collections.Add(instance); + + return this; + + } + + } + +} diff --git a/MewtocolNet/RegisterAttributes/RegisterPropTarget.cs b/MewtocolNet/RegisterAttributes/RegisterPropTarget.cs new file mode 100644 index 0000000..4468440 --- /dev/null +++ b/MewtocolNet/RegisterAttributes/RegisterPropTarget.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using System.Text; + +namespace MewtocolNet.RegisterAttributes { + + internal class RegisterPropTarget { + + //propinfo of the bound property + internal PropertyInfo BoundProperty; + + //general number of bits or bytes to read back to the prop + internal int? LinkLength; + + public override string ToString() { + + var sb = new StringBuilder(); + sb.Append($"{BoundProperty}"); + if(LinkLength != null) sb.Append($" -Len: {LinkLength}"); + + return sb.ToString(); + + } + + } + +} diff --git a/MewtocolNet/RegisterBuilding/RegisterBuildInfo.cs b/MewtocolNet/RegisterBuilding/RegisterBuildInfo.cs index e6b8de3..bfd9ad5 100644 --- a/MewtocolNet/RegisterBuilding/RegisterBuildInfo.cs +++ b/MewtocolNet/RegisterBuilding/RegisterBuildInfo.cs @@ -1,4 +1,5 @@ using MewtocolNet.Exceptions; +using MewtocolNet.RegisterAttributes; using MewtocolNet.Registers; using System; using System.Collections; @@ -20,14 +21,20 @@ namespace MewtocolNet.RegisterBuilding { internal byte? specialAddress; internal RegisterType? registerType; + internal Type dotnetCastType; - internal Type collectionType; + + internal RegisterCollection collectionTarget; + internal PropertyInfo boundPropTarget; + + internal BaseRegister BuildForCollectionAttribute () { + + return (BaseRegister)RegBuilder.Factory.FromPlcRegName(mewAddress, name).AsType(dotnetCastType).Build(); + + } internal BaseRegister Build () { - //Has mew address use this before the default checks - if (mewAddress != null) return BuildFromMewAddress(); - //parse enums if (dotnetCastType.IsEnum) { @@ -46,8 +53,8 @@ namespace MewtocolNet.RegisterBuilding { var parameters = new object[] { memoryAddress, name }; var instance = (BaseRegister)constr.Invoke(parameters); - if (collectionType != null) - instance.WithCollectionType(collectionType); + if (collectionTarget != null) + instance.WithRegisterCollection(collectionTarget); return instance; @@ -56,22 +63,18 @@ namespace MewtocolNet.RegisterBuilding { //parse all others where the type is known RegisterType regType = registerType ?? dotnetCastType.ToRegisterTypeDefault(); Type registerClassType = dotnetCastType.GetDefaultRegisterHoldingType(); - bool isBytesRegister = !registerClassType.IsGenericType && registerClassType == typeof(BytesRegister); + + bool isBoolRegister = regType.IsBoolean(); + + bool isBytesArrRegister = !registerClassType.IsGenericType && registerClassType == typeof(BytesRegister) && dotnetCastType == typeof(byte[]); + + bool isBytesBitsRegister = !registerClassType.IsGenericType && registerClassType == typeof(BytesRegister) && dotnetCastType == typeof(BitArray); + bool isStringRegister = !registerClassType.IsGenericType && registerClassType == typeof(StringRegister); - if (regType.IsNumericDTDDT() && (dotnetCastType == typeof(bool))) { + bool isNormalNumericResiter = regType.IsNumericDTDDT() && !isBytesArrRegister && !isBytesBitsRegister && !isStringRegister; - //------------------------------------------- - //as numeric register with boolean bit target - //create a new bregister instance - var instance = new BytesRegister(memoryAddress, memorySizeBytes.Value, name); - - if (collectionType != null) - instance.WithCollectionType(collectionType); - - return instance; - - } else if (regType.IsNumericDTDDT() && !isBytesRegister && !isStringRegister) { + if (isNormalNumericResiter) { //------------------------------------------- //as numeric register @@ -83,28 +86,43 @@ namespace MewtocolNet.RegisterBuilding { var parameters = new object[] { memoryAddress, name }; var instance = (BaseRegister)Activator.CreateInstance(registerClassType, flags, null, parameters, null); - if(collectionType != null) - instance.WithCollectionType(collectionType); + if(collectionTarget != null) + instance.WithRegisterCollection(collectionTarget); return instance; } - if(isBytesRegister) { + if(isBytesArrRegister) { //------------------------------------------- //as byte range register + BytesRegister instance = new BytesRegister(memoryAddress, memorySizeBytes.Value, name); + instance.ReservedBytesSize = (ushort)memorySizeBytes.Value; + + if (collectionTarget != null) + instance.WithRegisterCollection(collectionTarget); + + return instance; + + } + + if(isBytesBitsRegister) { + + //------------------------------------------- + //as bit range register + BytesRegister instance; - if(memorySizeBits != null) { + if (memorySizeBits != null) { instance = new BytesRegister(memoryAddress, memorySizeBits.Value, name); } else { - instance = new BytesRegister(memoryAddress, memorySizeBytes.Value, name); + instance = new BytesRegister(memoryAddress, 16, name); } - if (collectionType != null) - instance.WithCollectionType(collectionType); + if (collectionTarget != null) + instance.WithRegisterCollection(collectionTarget); return instance; @@ -116,14 +134,14 @@ namespace MewtocolNet.RegisterBuilding { //as byte range register var instance = (BaseRegister)new StringRegister(memoryAddress, name); - if (collectionType != null) - instance.WithCollectionType(collectionType); + if (collectionTarget != null) + instance.WithRegisterCollection(collectionTarget); return instance; } - if (regType.IsBoolean()) { + if (isBoolRegister) { //------------------------------------------- //as boolean register @@ -134,8 +152,8 @@ namespace MewtocolNet.RegisterBuilding { var instance = new BoolRegister(io, spAddr.Value, areaAddr, name); - if (collectionType != null) - ((IRegisterInternal)instance).WithCollectionType(collectionType); + if (collectionTarget != null) + instance.WithRegisterCollection(collectionTarget); return instance; @@ -145,12 +163,6 @@ namespace MewtocolNet.RegisterBuilding { } - private BaseRegister BuildFromMewAddress () { - - return (BaseRegister)RegBuilder.Factory.FromPlcRegName(mewAddress, name).AsType(dotnetCastType).Build(); - - } - } } diff --git a/MewtocolNet/Registers/BaseRegister.cs b/MewtocolNet/Registers/BaseRegister.cs index ac3f8d5..8388d95 100644 --- a/MewtocolNet/Registers/BaseRegister.cs +++ b/MewtocolNet/Registers/BaseRegister.cs @@ -1,5 +1,9 @@ -using System; +using MewtocolNet.RegisterAttributes; +using MewtocolNet.UnderlyingRegisters; +using System; +using System.Collections.Generic; using System.ComponentModel; +using System.Reflection; using System.Text; using System.Threading.Tasks; @@ -12,12 +16,19 @@ namespace MewtocolNet.Registers { /// public event Action ValueChanged; + //links to + internal RegisterCollection containedCollection; internal MewtocolInterface attachedInterface; + internal List boundToProps = new List(); + + internal IMemoryArea underlyingMemory; internal object lastValue = null; - internal Type collectionType; internal string name; internal uint memoryAddress; + /// + public RegisterCollection ContainedCollection => containedCollection; + /// public MewtocolInterface AttachedInterface => attachedInterface; @@ -27,9 +38,6 @@ namespace MewtocolNet.Registers { /// public RegisterType RegisterType { get; protected set; } - /// - public Type CollectionType => collectionType; - /// public string Name => name; @@ -62,12 +70,22 @@ namespace MewtocolNet.Registers { } - public void WithCollectionType(Type colType) => collectionType = colType; + internal virtual object SetValueFromBytes(byte[] bytes) => throw new NotImplementedException(); + + internal void WithRegisterCollection (RegisterCollection collection) => containedCollection = collection; + + internal void WithBoundProperty(RegisterPropTarget propInfo) => boundToProps.Add(propInfo); + + #region Read / Write + + public virtual Task ReadAsync() => throw new NotImplementedException(); + + public virtual Task WriteAsync(object data) => throw new NotImplementedException(); + + #endregion #region Default accessors - public Type GetCollectionType() => CollectionType; - public RegisterType GetRegisterType() => RegisterType; public virtual string BuildMewtocolQuery() { @@ -89,9 +107,9 @@ namespace MewtocolNet.Registers { public virtual string GetRegisterString() => RegisterType.ToString(); - public virtual string GetCombinedName() => $"{(CollectionType != null ? $"{CollectionType.Name}." : "")}{Name ?? "Unnamed"}"; + public virtual string GetCombinedName() => $"{GetContainerName()}{(GetContainerName() != null ? "." : "")}{Name ?? "Unnamed"}"; - public virtual string GetContainerName() => $"{(CollectionType != null ? $"{CollectionType.Name}" : "")}"; + public virtual string GetContainerName() => $"{(containedCollection != null ? $"{containedCollection.GetType().Name}" : null)}"; public virtual string GetMewName() => $"{GetRegisterString()}{MemoryAddress}"; @@ -101,14 +119,6 @@ namespace MewtocolNet.Registers { #endregion - #region Read / Write - - public virtual async Task ReadAsync() => throw new NotImplementedException(); - - public virtual async Task WriteAsync(object data) => throw new NotImplementedException(); - - #endregion - protected virtual void CheckAddressOverflow (uint addressStart, uint addressLen) { if (addressStart < 0) @@ -119,18 +129,33 @@ namespace MewtocolNet.Registers { } - public override string ToString() => $"{GetMewName()}{(Name != null ? $" ({Name})" : "")} - Value: {GetValueString()}"; + public override string ToString() { + + var sb = new StringBuilder(); + sb.Append(GetMewName()); + if(Name != null) sb.Append($" ({Name})"); + if (Value != null) sb.Append($" Val: {GetValueString()}"); + + return sb.ToString(); + + } public virtual string ToString(bool additional) { if (!additional) return this.ToString(); StringBuilder sb = new StringBuilder(); - sb.AppendLine($"PLC Naming: {GetMewName()}"); + sb.AppendLine($"MewName: {GetMewName()}"); sb.AppendLine($"Name: {Name ?? "Not named"}"); sb.AppendLine($"Value: {GetValueString()}"); sb.AppendLine($"Register Type: {RegisterType}"); - sb.AppendLine($"Memory Address: {MemoryAddress}"); + sb.AppendLine($"Address: {GetRegisterWordRangeString()}"); + if(GetSpecialAddress() != null) sb.AppendLine($"SPAddress: {GetSpecialAddress()}"); + if (GetType().IsGenericType) sb.AppendLine($"Type: NumberRegister<{GetType().GenericTypeArguments[0]}>"); + else sb.AppendLine($"Type: {GetType()}"); + if(containedCollection != null) sb.AppendLine($"In collection: {containedCollection.GetType()}"); + if(boundToProps != null && boundToProps.Count != 0) + sb.AppendLine($"Bound props: {string.Join(",", boundToProps)}"); return sb.ToString(); diff --git a/MewtocolNet/Registers/BoolRegister.cs b/MewtocolNet/Registers/BoolRegister.cs index 60a2a24..b17b071 100644 --- a/MewtocolNet/Registers/BoolRegister.cs +++ b/MewtocolNet/Registers/BoolRegister.cs @@ -1,4 +1,5 @@ -using System; +using MewtocolNet.UnderlyingRegisters; +using System; using System.ComponentModel; using System.Net; using System.Text; @@ -63,7 +64,8 @@ namespace MewtocolNet.Registers { if (!attachedInterface.IsConnected) return null; var read = await attachedInterface.ReadRawRegisterAsync(this); - if(read == null) return null; + if(read == null) return null; + var parsed = PlcValueParser.Parse(this, read); SetValueFromPLC(parsed); @@ -76,8 +78,13 @@ namespace MewtocolNet.Registers { if (!attachedInterface.IsConnected) return false; - var res = await attachedInterface.WriteRawRegisterAsync(this, PlcValueParser.Encode(this, (bool)data)); - if (res) SetValueFromPLC(data); + var encoded = PlcValueParser.Encode(this, (bool)data); + + var res = await attachedInterface.WriteRawRegisterAsync(this, encoded); + if (res) { + SetValueFromPLC(data); + } + return res; } @@ -132,23 +139,6 @@ namespace MewtocolNet.Registers { /// public override uint GetRegisterAddressLen () => 1; - /// - public override string ToString(bool additional) { - - if (!additional) return this.ToString(); - - StringBuilder sb = new StringBuilder(); - sb.AppendLine($"PLC Naming: {GetMewName()}"); - 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/BytesRegister.cs b/MewtocolNet/Registers/BytesRegister.cs index 3425c30..813bb92 100644 --- a/MewtocolNet/Registers/BytesRegister.cs +++ b/MewtocolNet/Registers/BytesRegister.cs @@ -19,9 +19,9 @@ namespace MewtocolNet.Registers { /// public uint AddressLength => addressLength; - internal uint ReservedBytesSize { get; private set; } + internal uint ReservedBytesSize { get; set; } - internal ushort? ReservedBitSize { get; private set; } + internal ushort? ReservedBitSize { get; set; } /// /// Defines a register containing bytes @@ -128,15 +128,22 @@ namespace MewtocolNet.Registers { if (!attachedInterface.IsConnected) return null; - var read = await attachedInterface.ReadRawRegisterAsync(this); - if (read == null) return null; + var res = await underlyingMemory.ReadRegisterAsync(this); + if (!res) return null; + + var bytes = underlyingMemory.GetUnderlyingBytes(this); + + return SetValueFromBytes(bytes); + + } + + internal override object SetValueFromBytes(byte[] bytes) { object parsed; - - if(ReservedBitSize != null) { - parsed = PlcValueParser.Parse(this, read); + if (ReservedBitSize != null) { + parsed = PlcValueParser.Parse(this, bytes); } else { - parsed = PlcValueParser.Parse(this, read); + parsed = PlcValueParser.Parse(this, bytes); } SetValueFromPLC(parsed); @@ -157,8 +164,10 @@ namespace MewtocolNet.Registers { encoded = PlcValueParser.Encode(this, (byte[])data); } - var res = await attachedInterface.WriteRawRegisterAsync(this, encoded); - if (res) SetValueFromPLC(data); + var res = await underlyingMemory.WriteRegisterAsync(this, encoded); + if (res) { + SetValueFromPLC(data); + } return res; diff --git a/MewtocolNet/Registers/Interfaces/IRegisterInternal.cs b/MewtocolNet/Registers/Interfaces/IRegisterInternal.cs index 01b24d3..3b87415 100644 --- a/MewtocolNet/Registers/Interfaces/IRegisterInternal.cs +++ b/MewtocolNet/Registers/Interfaces/IRegisterInternal.cs @@ -1,4 +1,5 @@ -using MewtocolNet.Registers; +using MewtocolNet.RegisterAttributes; +using MewtocolNet.Registers; using System; using System.Threading.Tasks; @@ -19,9 +20,9 @@ namespace MewtocolNet { uint MemoryAddress { get; } - // setters + RegisterCollection ContainedCollection { get; } - void WithCollectionType(Type colType); + // setters void SetValueFromPLC(object value); @@ -29,8 +30,6 @@ namespace MewtocolNet { // Accessors - Type GetCollectionType(); - string GetRegisterString(); string GetCombinedName(); diff --git a/MewtocolNet/Registers/NumberRegister.cs b/MewtocolNet/Registers/NumberRegister.cs index 6d1d433..66c9838 100644 --- a/MewtocolNet/Registers/NumberRegister.cs +++ b/MewtocolNet/Registers/NumberRegister.cs @@ -1,4 +1,6 @@ -using System; +using MewtocolNet.Exceptions; +using MewtocolNet.UnderlyingRegisters; +using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; @@ -131,11 +133,18 @@ namespace MewtocolNet.Registers { if (!attachedInterface.IsConnected) return null; - var read = await attachedInterface.ReadRawRegisterAsync(this); - if (read == null) return null; + var res = await underlyingMemory.ReadRegisterAsync(this); + if (!res) return null; - var parsed = PlcValueParser.Parse(this, read); + var bytes = underlyingMemory.GetUnderlyingBytes(this); + + return SetValueFromBytes(bytes); + } + + internal override object SetValueFromBytes(byte[] bytes) { + + var parsed = PlcValueParser.Parse(this, bytes); SetValueFromPLC(parsed); return parsed; @@ -146,8 +155,13 @@ namespace MewtocolNet.Registers { if (!attachedInterface.IsConnected) return false; - var res = await attachedInterface.WriteRawRegisterAsync(this, PlcValueParser.Encode(this, (T)data)); - if (res) SetValueFromPLC(data); + var encoded = PlcValueParser.Encode(this, (T)data); + var res = await underlyingMemory.WriteRegisterAsync(this, encoded); + + if (res) { + SetValueFromPLC(data); + } + return res; } diff --git a/MewtocolNet/Registers/StringRegister.cs b/MewtocolNet/Registers/StringRegister.cs index 25aea63..7d5f013 100644 --- a/MewtocolNet/Registers/StringRegister.cs +++ b/MewtocolNet/Registers/StringRegister.cs @@ -130,7 +130,8 @@ namespace MewtocolNet.Registers { } - var res = await attachedInterface.WriteRawRegisterAsync(this, PlcValueParser.Encode(this, (string)data)); + var encoded = PlcValueParser.Encode(this, (string)data); + var res = await attachedInterface.WriteRawRegisterAsync(this, encoded); if (res) { SetValueFromPLC(data); diff --git a/MewtocolNet/TypeConversion/Conversions.cs b/MewtocolNet/TypeConversion/Conversions.cs index ef513b8..6e9a2e9 100644 --- a/MewtocolNet/TypeConversion/Conversions.cs +++ b/MewtocolNet/TypeConversion/Conversions.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; +using MewtocolNet.Helpers; namespace MewtocolNet.TypeConversion { @@ -134,9 +135,9 @@ namespace MewtocolNet.TypeConversion { PlcVarType = PlcVarType.REAL, FromRaw = (reg, bytes) => { - var val = BitConverter.ToUInt32(bytes, 0); - byte[] floatVals = BitConverter.GetBytes(val); - float finalFloat = BitConverter.ToSingle(floatVals, 0); + //bytes = new byte[] { 0xCD, 0xCC, 0x8C, 0x40 }; + + float finalFloat = BitConverter.ToSingle(bytes, 0); return finalFloat; diff --git a/MewtocolNet/UnderlyingRegisters/DTArea.cs b/MewtocolNet/UnderlyingRegisters/DTArea.cs new file mode 100644 index 0000000..47136a8 --- /dev/null +++ b/MewtocolNet/UnderlyingRegisters/DTArea.cs @@ -0,0 +1,152 @@ +using MewtocolNet.Registers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace MewtocolNet.UnderlyingRegisters { + public class DTArea : IMemoryArea { + + private MewtocolInterface mewInterface; + + internal RegisterType registerType; + internal ulong addressStart; + internal ulong addressEnd; + + internal byte[] underlyingBytes = new byte[2]; + + internal List linkedRegisters = new List(); + + public ulong AddressStart => addressStart; + public ulong AddressEnd => addressEnd; + + internal DTArea (MewtocolInterface mewIf) { + + mewInterface = mewIf; + + } + + internal void BoundaryUdpdate (uint? addrFrom = null, uint? addrTo = null) { + + var addFrom = addrFrom ?? addressStart; + var addTo = addrTo ?? addressEnd; + + var oldFrom = addressStart; + var oldUnderlying = underlyingBytes.ToArray(); + + underlyingBytes = new byte[(addTo + 1 - addFrom) * 2]; + + //copy old bytes to new array + var offset = (int)(oldFrom - addFrom) * 2; + oldUnderlying.CopyTo(oldUnderlying, offset); + + addressStart = addFrom; + addressEnd = addTo; + + } + + public async Task ReadRegisterAsync (BaseRegister reg) { + + return await RequestByteReadAsync(reg.MemoryAddress, reg.MemoryAddress + reg.GetRegisterAddressLen() - 1); + + } + + public async Task WriteRegisterAsync (BaseRegister reg, byte[] bytes) { + + return await RequestByteWriteAsync(reg.MemoryAddress, bytes); + + } + + internal async Task RequestByteReadAsync (ulong addStart, ulong addEnd) { + + var station = mewInterface.GetStationNumber(); + + string requeststring = $"%{station}#RD{GetMewtocolIdent(addStart, addEnd)}"; + var result = await mewInterface.SendCommandAsync(requeststring); + + if(result.Success) { + + var resBytes = result.Response.ParseDTRawStringAsBytes(); + SetUnderlyingBytes(resBytes, addStart); + + } + + return result.Success; + + } + + internal async Task RequestByteWriteAsync(ulong addStart, byte[] bytes) { + + var station = mewInterface.GetStationNumber(); + var addEnd = addStart + ((ulong)bytes.Length / 2) - 1; + + string requeststring = $"%{station}#WD{GetMewtocolIdent(addStart, addEnd)}{bytes.ToHexString()}"; + var result = await mewInterface.SendCommandAsync(requeststring); + + if (result.Success) { + + SetUnderlyingBytes(bytes, addStart); + + } + + return result.Success; + + } + + public byte[] GetUnderlyingBytes(BaseRegister reg) { + + int byteLen = (int)(reg.GetRegisterAddressLen() * 2); + + return GetUnderlyingBytes(reg.MemoryAddress, byteLen); + + } + + internal byte[] GetUnderlyingBytes (uint addStart, int addLen) { + + int byteLen = (int)(addLen * 2); + + int copyOffset = (int)((addStart - addressStart) * 2); + var gotBytes = underlyingBytes.Skip(copyOffset).Take(byteLen).ToArray(); + + return gotBytes; + + } + + public void SetUnderlyingBytes(BaseRegister reg, byte[] bytes) { + + SetUnderlyingBytes(bytes, reg.MemoryAddress); + + } + + private void SetUnderlyingBytes(byte[] bytes, ulong addStart) { + + int copyOffset = (int)((addStart - addressStart) * 2); + bytes.CopyTo(underlyingBytes, copyOffset); + + } + + private string GetMewtocolIdent () { + + StringBuilder asciistring = new StringBuilder("D"); + asciistring.Append(AddressStart.ToString().PadLeft(5, '0')); + asciistring.Append(AddressEnd.ToString().PadLeft(5, '0')); + return asciistring.ToString(); + + } + + private string GetMewtocolIdent(ulong addStart, ulong addEnd) { + + StringBuilder asciistring = new StringBuilder("D"); + asciistring.Append(addStart.ToString().PadLeft(5, '0')); + asciistring.Append(addEnd.ToString().PadLeft(5, '0')); + return asciistring.ToString(); + + } + + public override string ToString() => $"DT{AddressStart}-{AddressEnd}"; + + } + +} diff --git a/MewtocolNet/UnderlyingRegisters/IMemoryArea.cs b/MewtocolNet/UnderlyingRegisters/IMemoryArea.cs new file mode 100644 index 0000000..3310f3c --- /dev/null +++ b/MewtocolNet/UnderlyingRegisters/IMemoryArea.cs @@ -0,0 +1,16 @@ +using MewtocolNet.Registers; +using System.Threading.Tasks; + +namespace MewtocolNet.UnderlyingRegisters { + + internal interface IMemoryArea { + + byte[] GetUnderlyingBytes(BaseRegister reg); + + Task ReadRegisterAsync(BaseRegister reg); + + Task WriteRegisterAsync(BaseRegister reg, byte[] bytes); + + } + +} diff --git a/MewtocolNet/UnderlyingRegisters/MemoryAreaManager.cs b/MewtocolNet/UnderlyingRegisters/MemoryAreaManager.cs new file mode 100644 index 0000000..9f2a25c --- /dev/null +++ b/MewtocolNet/UnderlyingRegisters/MemoryAreaManager.cs @@ -0,0 +1,318 @@ +using MewtocolNet.Registers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MewtocolNet.UnderlyingRegisters { + + internal class MemoryAreaManager { + + internal int maxOptimizationDistance = 8; + internal int maxRegistersPerGroup = -1; + + internal MewtocolInterface mewInterface; + + // WR areas are n of words, each word has 2 bytes representing the "special address component" + + //X WR + internal List externalRelayInAreas; + + //Y WR + internal List externalRelayOutAreas; + + //R WR + internal List internalRelayAreas; + + //DT + internal List dataAreas; + + internal MemoryAreaManager (MewtocolInterface mewIf, int wrSize = 512, int dtSize = 32_765) { + + mewInterface = mewIf; + Setup(wrSize, dtSize); + + } + + // Later on pass memory area sizes here + internal void Setup (int wrSize, int dtSize) { + + externalRelayInAreas = new List(wrSize * 16); + externalRelayOutAreas = new List(wrSize * 16); + internalRelayAreas = new List(wrSize * 16); + dataAreas = new List(dtSize); + + } + + internal bool LinkRegister (BaseRegister reg) { + + switch (reg.RegisterType) { + case RegisterType.X: + return AddWRArea(reg, externalRelayInAreas); + case RegisterType.Y: + return AddWRArea(reg, externalRelayOutAreas); + case RegisterType.R: + return AddWRArea(reg, internalRelayAreas); + case RegisterType.DT: + case RegisterType.DDT: + case RegisterType.DT_BYTE_RANGE: + return AddDTArea(reg); + } + + return false; + + } + + private bool AddWRArea (BaseRegister insertReg, List collection) { + + WRArea area = collection.FirstOrDefault(x => x.AddressStart == insertReg.MemoryAddress); + + if(area != null) { + + var existingLinkedRegister = area.linkedRegisters + .FirstOrDefault(x => x.CompareIsDuplicate(insertReg)); + + if(existingLinkedRegister != null) { + + foreach (var prop in insertReg.boundToProps) + existingLinkedRegister.WithBoundProperty(prop); + + return false; + + } else { + + insertReg.underlyingMemory = area; + area.linkedRegisters.Add(insertReg); + return true; + + } + + } else { + + area = new WRArea(mewInterface) { + registerType = insertReg.RegisterType, + addressStart = insertReg.MemoryAddress, + }; + + insertReg.underlyingMemory = area; + area.linkedRegisters.Add(insertReg); + + collection.Add(area); + collection = collection.OrderBy(x => x.AddressStart).ToList(); + + return true; + + } + + } + + private bool AddDTArea (BaseRegister insertReg) { + + uint regInsAddStart = insertReg.MemoryAddress; + uint regInsAddEnd = insertReg.MemoryAddress + insertReg.GetRegisterAddressLen() - 1; + + DTArea targetArea = null; + + foreach (var dtArea in dataAreas) { + + bool matchingAddress = regInsAddStart >= dtArea.AddressStart && + regInsAddEnd <= dtArea.addressEnd; + + //found matching + if (matchingAddress) { + + //check if the area has registers linked that are overlapping (not matching) + var foundDupe = dtArea.linkedRegisters.FirstOrDefault(x => x.CompareIsDuplicateNonCast(insertReg)); + + if(foundDupe != null) { + throw new NotSupportedException( + message: $"Can't have registers of different types at the same referenced plc address: " + + $"{insertReg.PLCAddressName} ({insertReg.GetType()}) <=> " + + $"{foundDupe.PLCAddressName} ({foundDupe.GetType()})" + ); + } + + targetArea = dtArea; + + break; + + } + + //found adjacent before + if(dtArea.AddressEnd <= regInsAddStart) { + + ulong distance = regInsAddStart - dtArea.AddressEnd; + + if (distance <= (uint)maxOptimizationDistance) { + + //expand the boundaries for the area to include the new adjacent area + dtArea.BoundaryUdpdate(addrTo: regInsAddEnd); + targetArea = dtArea; + break; + + } + + } + + //found adjacent after + if (dtArea.AddressStart >= regInsAddEnd) { + + ulong distance = dtArea.AddressStart - regInsAddEnd; + + if (distance <= (uint)maxOptimizationDistance) { + + //expand the boundaries for the area to include the new adjacent area + dtArea.BoundaryUdpdate(addrFrom: regInsAddStart); + + targetArea = dtArea; + break; + + } + + } + + } + + if (targetArea == null) { + + targetArea = new DTArea(mewInterface) { + addressStart = regInsAddStart, + addressEnd = regInsAddEnd, + registerType = insertReg.RegisterType, + }; + + targetArea.BoundaryUdpdate(); + + dataAreas.Add(targetArea); + + } + + insertReg.underlyingMemory = targetArea; + + var existingLinkedRegister = targetArea.linkedRegisters + .FirstOrDefault(x => x.CompareIsDuplicate(insertReg)); + + if (existingLinkedRegister != null) { + + foreach (var prop in insertReg.boundToProps) + existingLinkedRegister.WithBoundProperty(prop); + + return false; + + } else { + + targetArea.linkedRegisters.Add(insertReg); + return true; + + } + + } + + internal async Task PollAllAreasAsync () { + + foreach (var dtArea in dataAreas) { + + //set the whole memory area at once + var res = await dtArea.RequestByteReadAsync(dtArea.AddressStart, dtArea.AddressEnd); + + foreach (var register in dtArea.linkedRegisters) { + + var regStart = register.MemoryAddress; + var addLen = (int)register.GetRegisterAddressLen(); + + var bytes = dtArea.GetUnderlyingBytes(regStart, addLen); + register.SetValueFromBytes(bytes); + + } + + } + + } + + internal void Merge () { + + //merge gaps that the algorithm didn't catch be rerunning the register attachment + + var allDataAreaRegisters = dataAreas.SelectMany(x => x.linkedRegisters).ToList(); + dataAreas = new List(allDataAreaRegisters.Capacity); + + foreach (var reg in allDataAreaRegisters) + AddDTArea(reg); + + } + + internal string ExplainLayout () { + + var sb = new StringBuilder(); + + sb.AppendLine("---- DT Area ----"); + + sb.AppendLine($"Optimization distance: {maxOptimizationDistance}"); + + foreach (var area in dataAreas) { + + sb.AppendLine(); + sb.AppendLine($"=> {area} = {area.underlyingBytes.Length} bytes"); + sb.AppendLine(); + sb.AppendLine(string.Join("\n", area.underlyingBytes.ToHexString(" ").SplitInParts(3 * 8))); + sb.AppendLine(); + + foreach (var reg in area.linkedRegisters) { + + sb.AppendLine($"{reg.ToString(true)}"); + + } + + } + + sb.AppendLine("---- WR X Area ----"); + + foreach (var area in externalRelayInAreas) { + + sb.AppendLine(area.ToString()); + + foreach (var reg in area.linkedRegisters) { + + sb.AppendLine($"{reg.ToString(true)}"); + + } + + } + + sb.AppendLine("---- WR Y Area ----"); + + foreach (var area in externalRelayOutAreas) { + + sb.AppendLine(area.ToString()); + + foreach (var reg in area.linkedRegisters) { + + sb.AppendLine($"{reg.ToString(true)}"); + + } + + } + + sb.AppendLine("---- WR R Area ----"); + + foreach (var area in internalRelayAreas) { + + sb.AppendLine(area.ToString()); + + foreach (var reg in area.linkedRegisters) { + + sb.AppendLine($"{reg.ToString(true)}"); + + } + + } + + return sb.ToString(); + + } + + + } + +} diff --git a/MewtocolNet/UnderlyingRegisters/WRArea.cs b/MewtocolNet/UnderlyingRegisters/WRArea.cs new file mode 100644 index 0000000..d040cd6 --- /dev/null +++ b/MewtocolNet/UnderlyingRegisters/WRArea.cs @@ -0,0 +1,83 @@ +using MewtocolNet.Registers; +using System.Collections; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace MewtocolNet.UnderlyingRegisters { + + public class WRArea : IMemoryArea { + + private MewtocolInterface mewInterface; + + internal RegisterType registerType; + internal ulong addressStart; + + internal byte[] wordData = new byte[2]; + + internal List linkedRegisters = new List(); + + public ulong AddressStart => addressStart; + + internal WRArea(MewtocolInterface mewIf) { + + mewInterface = mewIf; + + } + + public byte[] GetUnderlyingBytes(BaseRegister reg) { + + return null; + + } + + public async Task ReadRegisterAsync(BaseRegister reg) { + + return true; + + } + + public async Task WriteRegisterAsync(BaseRegister reg, byte[] bytes) { + + return true; + + } + + public string GetMewtocolIdent() => GetMewtocolIdentsAllBits(); + + public string GetMewtocolIdentsAllBits () { + + StringBuilder asciistring = new StringBuilder(); + + for (byte i = 0; i < 16; i++) { + + asciistring.Append(GetMewtocolIdentSingleBit(i)); + + } + + return asciistring.ToString(); + + } + + public string GetMewtocolIdentSingleBit (byte specialAddress) { + + //(R|X|Y)(area add [3] + special add [1]) + StringBuilder asciistring = new StringBuilder(); + + string prefix = registerType.ToString(); + string mem = AddressStart.ToString(); + string sp = specialAddress.ToString("X1"); + + asciistring.Append(prefix); + asciistring.Append(mem.PadLeft(3, '0')); + asciistring.Append(sp); + + return asciistring.ToString(); + + } + + public override string ToString() => $"{registerType}{AddressStart} 0-F"; + + } + +} diff --git a/MewtocolTests/AutomatedPropertyRegisters.cs b/MewtocolTests/AutomatedPropertyRegisters.cs index 416fcdc..90ce676 100644 --- a/MewtocolTests/AutomatedPropertyRegisters.cs +++ b/MewtocolTests/AutomatedPropertyRegisters.cs @@ -14,10 +14,10 @@ namespace MewtocolTests { this.output = output; } - private void Test(IRegisterInternal reg, string propName, uint expectAddr, string expectPlcName) { + private void Test(IRegisterInternal reg, uint expectAddr, string expectPlcName) { Assert.NotNull(reg); - Assert.Equal(propName, reg.Name); + Assert.StartsWith("auto_prop_register_", reg.Name); Assert.Null(reg.Value); Assert.Equal(expectAddr, reg.MemoryAddress); @@ -32,83 +32,103 @@ namespace MewtocolTests { [Fact(DisplayName = "Boolean generation")] public void BooleanGen() { - var interf = Mewtocol.Ethernet("192.168.0.1"); - interf.AddRegisterCollection(new TestBoolRegisters()); + var interf = Mewtocol.Ethernet("192.168.0.1") + .WithRegisterCollections(x => + x.AddCollection(new TestBoolRegisters()) + ).Build(); - var register1 = interf.GetRegister(nameof(TestBoolRegisters.RType)); - var register2 = interf.GetRegister(nameof(TestBoolRegisters.XType)); + output.WriteLine(((MewtocolInterface)interf).memoryManager.ExplainLayout()); - var register3 = interf.GetRegister(nameof(TestBoolRegisters.RType_MewString)); + var register1 = interf.GetRegister("auto_prop_register_1"); + var register2 = interf.GetRegister("auto_prop_register_2"); + var register3 = interf.GetRegister("auto_prop_register_3"); - Test((IRegisterInternal)register1, nameof(TestBoolRegisters.RType), 85, "R85A"); - Test((IRegisterInternal)register2, nameof(TestBoolRegisters.XType), 0, "XD"); - - Test((IRegisterInternal)register3, nameof(TestBoolRegisters.RType_MewString), 85, "R85B"); + Test((IRegisterInternal)register1, 0, "XD"); + Test((IRegisterInternal)register2, 85, "R85A"); + Test((IRegisterInternal)register3, 85, "R85B"); } [Fact(DisplayName = "Number 16 bit generation")] public void N16BitGen () { - var interf = Mewtocol.Ethernet("192.168.0.1"); - interf.AddRegisterCollection(new Nums16Bit()); + var interf = Mewtocol.Ethernet("192.168.0.1") + .WithRegisterCollections(x => + x.AddCollection(new Nums16Bit()) + ).Build(); - var register1 = interf.GetRegister(nameof(Nums16Bit.Int16Type)); - var register2 = interf.GetRegister(nameof(Nums16Bit.UInt16Type)); - var register3 = interf.GetRegister(nameof(Nums16Bit.Enum16Type)); - - var register4 = interf.GetRegister(nameof(Nums16Bit.Int16Type_MewString)); - var register5 = interf.GetRegister(nameof(Nums16Bit.Enum16Type_MewString)); + var register1 = interf.GetRegister("auto_prop_register_1"); + var register2 = interf.GetRegister("auto_prop_register_2"); + var register3 = interf.GetRegister("auto_prop_register_3"); //test generic properties - Test((IRegisterInternal)register1, nameof(Nums16Bit.Int16Type), 899, "DT899"); - Test((IRegisterInternal)register2, nameof(Nums16Bit.UInt16Type), 342, "DT342"); - Test((IRegisterInternal)register3, nameof(Nums16Bit.Enum16Type), 50, "DT50"); - - Test((IRegisterInternal)register4, nameof(Nums16Bit.Int16Type_MewString), 900, "DT900"); - Test((IRegisterInternal)register5, nameof(Nums16Bit.Enum16Type_MewString), 51, "DT51"); + Test((IRegisterInternal)register1, 50, "DT50"); + Test((IRegisterInternal)register2, 342, "DT342"); + Test((IRegisterInternal)register3, 899, "DT899"); } [Fact(DisplayName = "Number 32 bit generation")] public void N32BitGen () { - var interf = Mewtocol.Ethernet("192.168.0.1"); - interf.AddRegisterCollection(new Nums32Bit()); + var interf = Mewtocol.Ethernet("192.168.0.1") + .WithRegisterCollections(x => x + .AddCollection(new Nums32Bit()) + ).Build(); - var register1 = interf.GetRegister(nameof(Nums32Bit.Int32Type)); - var register2 = interf.GetRegister(nameof(Nums32Bit.UInt32Type)); - var register3 = interf.GetRegister(nameof(Nums32Bit.Enum32Type)); - var register4 = interf.GetRegister(nameof(Nums32Bit.FloatType)); - var register5 = interf.GetRegister(nameof(Nums32Bit.TimeSpanType)); + output.WriteLine(((MewtocolInterface)interf).memoryManager.ExplainLayout()); - var register6 = interf.GetRegister(nameof(Nums32Bit.Enum32Type_MewString)); - var register7 = interf.GetRegister(nameof(Nums32Bit.TimeSpanType_MewString)); + var register1 = interf.GetRegister("auto_prop_register_1"); + var register2 = interf.GetRegister("auto_prop_register_2"); + var register3 = interf.GetRegister("auto_prop_register_3"); + + //only one generated because same type + var register4 = interf.GetRegister("auto_prop_register_4"); + + var register6 = interf.GetRegister("auto_prop_register_5"); + var register7 = interf.GetRegister("auto_prop_register_6"); //test generic properties - Test((IRegisterInternal)register1, nameof(Nums32Bit.Int32Type), 7001, "DDT7001"); - Test((IRegisterInternal)register2, nameof(Nums32Bit.UInt32Type), 765, "DDT765"); - Test((IRegisterInternal)register3, nameof(Nums32Bit.Enum32Type), 51, "DDT51"); - Test((IRegisterInternal)register4, nameof(Nums32Bit.FloatType), 7003, "DDT7003"); - Test((IRegisterInternal)register5, nameof(Nums32Bit.TimeSpanType), 7012, "DDT7012"); + Test((IRegisterInternal)register1, 7000, "DDT7000"); + Test((IRegisterInternal)register2, 7002, "DDT7002"); + Test((IRegisterInternal)register3, 7004, "DDT7004"); + + Test((IRegisterInternal)register4, 7006, "DDT7006"); - Test((IRegisterInternal)register6, nameof(Nums32Bit.Enum32Type_MewString), 53, "DDT53"); - Test((IRegisterInternal)register7, nameof(Nums32Bit.TimeSpanType_MewString), 7014, "DDT7014"); + Test((IRegisterInternal)register6, 7008, "DDT7008"); + Test((IRegisterInternal)register7, 7010, "DDT7010"); } [Fact(DisplayName = "String generation")] public void StringGen() { - var interf = Mewtocol.Ethernet("192.168.0.1"); - interf.AddRegisterCollection(new TestStringRegisters()); + var interf = Mewtocol.Ethernet("192.168.0.1") + .WithRegisterCollections(x => + x.AddCollection(new TestStringRegisters()) + ).Build(); - var register1 = interf.GetRegister(nameof(TestStringRegisters.StringType)); - var register2 = interf.GetRegister(nameof(TestStringRegisters.StringType_MewString)); + var register1 = interf.GetRegister("auto_prop_register_1"); //test generic properties - Test((IRegisterInternal)register1, nameof(TestStringRegisters.StringType), 7005, "DT7005"); - Test((IRegisterInternal)register2, nameof(TestStringRegisters.StringType_MewString), 7050, "DT7050"); + Test((IRegisterInternal)register1, 7005, "DT7005"); + + } + + [Fact(DisplayName = "Byte Array generation")] + public void ByteArrGen() { + + var interf = Mewtocol.Ethernet("192.168.0.1") + .WithRegisterCollections(x => + x.AddCollection(new TestBitwiseRegisters()) + ).Build(); + + var register1 = interf.GetRegister("auto_prop_register_1"); + //var register2 = interf.GetRegister("auto_prop_register_2"); + + //test generic properties + Test((IRegisterInternal)register1, 7000, "DT7000"); + //Test((IRegisterInternal)register2, 7001, "DT7001"); } diff --git a/MewtocolTests/EncapsulatedTests/TestRegisterCollection.cs b/MewtocolTests/EncapsulatedTests/TestRegisterCollection.cs index e222dc9..6558f2d 100644 --- a/MewtocolTests/EncapsulatedTests/TestRegisterCollection.cs +++ b/MewtocolTests/EncapsulatedTests/TestRegisterCollection.cs @@ -30,10 +30,10 @@ namespace MewtocolTests.EncapsulatedTests { public class TestBoolRegisters : RegisterCollection { - [Register(IOType.R, memoryArea: 85, spAdress: 0xA)] + [Register("R85A")] public bool RType { get; set; } - [Register(IOType.X, (byte)0xD)] + [Register("XD")] public bool XType { get; set; } [Register("R85B")] @@ -44,71 +44,62 @@ namespace MewtocolTests.EncapsulatedTests { public class Nums16Bit : RegisterCollection { - [Register(899)] + [Register("DT899")] public short Int16Type { get; set; } - [Register(342)] + [Register("DT342")] public ushort UInt16Type { get; set; } - [Register(50)] + [Register("DT50")] public CurrentState Enum16Type { get; set; } - [Register("DT900")] - public short Int16Type_MewString { get; set; } - - [Register("DT51")] - public CurrentState Enum16Type_MewString { get; set; } - } public class Nums32Bit : RegisterCollection { - [Register(7001)] + [Register("DDT7000")] public int Int32Type { get; set; } - [Register(765)] + [Register("DDT7002")] public uint UInt32Type { get; set; } - [Register(51)] + [Register("DDT7004")] public CurrentState32 Enum32Type { get; set; } - [Register(7003)] + [Register("DDT7006")] public float FloatType { get; set; } - [Register(7012)] + [Register("DDT7006")] + public float FloatType2 { get; set; } // this is legal, because the cast type is the same + + //[Register("DDT7006")] + //public int FloatType3 { get; set; } // this is not legal + + [Register("DDT7010")] public TimeSpan TimeSpanType { get; set; } - [Register("DDT53")] - public CurrentState32 Enum32Type_MewString { get; set; } + [Register("DDT7008")] + public TimeSpan TimeSpanType2 { get; set; } - [Register("DDT7014")] - public TimeSpan TimeSpanType_MewString { get; set; } + [Register("DDT7013")] + public TimeSpan TimeSpanType3 { get; set; } } public class TestStringRegisters : RegisterCollection { - [Register(7005, 5)] + [Register("DT7005")] public string? StringType { get; set; } - [Register("DT7050")] - public string? StringType_MewString { get; set; } - } public class TestBitwiseRegisters : RegisterCollection { - [Register(7010)] - public BitArray TestBitRegister { get; set; } + [Register("DT7000")] + public BitArray BitArr16 { get; set; } - [Register(8010, BitCount.B32)] - public BitArray TestBitRegister32 { get; set; } - - [Register(1204, BitCount.B16, 9)] - public bool BitValue { get; set; } - - [Register(1204, BitCount.B32, 5)] - public bool FillTest { get; set; } + //[Register("DT7001")] + //public BitArray BitArr32 { get; set; } } diff --git a/MewtocolTests/TestLivePLC.cs b/MewtocolTests/TestLivePLC.cs index 3a04ac5..16dfd65 100644 --- a/MewtocolTests/TestLivePLC.cs +++ b/MewtocolTests/TestLivePLC.cs @@ -103,7 +103,7 @@ namespace MewtocolTests output.WriteLine($"Testing: {plc.PLCName}"); - var cycleClient = Mewtocol.Ethernet(plc.PLCIP, plc.PLCPort); + var cycleClient = Mewtocol.Ethernet(plc.PLCIP, plc.PLCPort).Build(); await cycleClient.ConnectAsync(); @@ -124,7 +124,7 @@ namespace MewtocolTests output.WriteLine($"Testing: {plc.PLCName}\n"); - var client = Mewtocol.Ethernet(plc.PLCIP, plc.PLCPort); + var client = Mewtocol.Ethernet(plc.PLCIP, plc.PLCPort).Build(); await client.ConnectAsync(); @@ -155,7 +155,7 @@ namespace MewtocolTests output.WriteLine($"\n\n --- Testing: {plc.PLCName} ---\n"); - var client = Mewtocol.Ethernet(plc.PLCIP, plc.PLCPort); + var client = Mewtocol.Ethernet(plc.PLCIP, plc.PLCPort).Build(); foreach (var testRW in testRegisterRW) {