From daecd73a6d6afe06171fbee01bac18c6e3b45394 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Felix=20Wei=C3=9F?=
<72068105+Sandoun@users.noreply.github.com>
Date: Thu, 13 Jul 2023 00:34:06 +0200
Subject: [PATCH] Add more xml doc - fixed dynamically sized registers auto dt
area resize - add more anonymous register r/w - add more register builder
type casting methods
---
MewtocolNet/MewtocolInterface.cs | 4 +-
.../MewtocolInterfaceRegisterHandling.cs | 2 +-
MewtocolNet/MewtocolInterfaceRequests.cs | 106 +-----
MewtocolNet/RegisterBuilding/RBuild.cs | 311 ++++++++++--------
.../RegisterBuilding/RegBuilderExtensions.cs | 2 +-
.../RegisterBuilding/RegisterAssembler.cs | 138 ++++++++
MewtocolNet/Registers/BaseRegister.cs | 3 +-
MewtocolNet/Registers/BoolRegister.cs | 68 ++--
MewtocolNet/Registers/BytesRegister.cs | 73 ++--
MewtocolNet/Registers/NumberRegister.cs | 39 +--
MewtocolNet/Registers/StringRegister.cs | 106 +++---
MewtocolNet/TypeConversion/Conversions.cs | 135 ++++----
MewtocolNet/UnderlyingRegisters/DTArea.cs | 18 +
.../UnderlyingRegisters/MemoryAreaManager.cs | 16 +
14 files changed, 595 insertions(+), 426 deletions(-)
create mode 100644 MewtocolNet/RegisterBuilding/RegisterAssembler.cs
diff --git a/MewtocolNet/MewtocolInterface.cs b/MewtocolNet/MewtocolInterface.cs
index be48263..147ee10 100644
--- a/MewtocolNet/MewtocolInterface.cs
+++ b/MewtocolNet/MewtocolInterface.cs
@@ -175,7 +175,7 @@ namespace MewtocolNet {
///
public async Task DisconnectAsync () {
- await pollCycleTask;
+ if(pollCycleTask != null) await pollCycleTask;
Disconnect();
@@ -186,7 +186,7 @@ namespace MewtocolNet {
if (!IsConnected) return;
- if (!pollCycleTask.IsCompleted) pollCycleTask.Wait();
+ if (pollCycleTask != null && !pollCycleTask.IsCompleted) pollCycleTask.Wait();
OnMajorSocketExceptionWhileConnected();
diff --git a/MewtocolNet/MewtocolInterfaceRegisterHandling.cs b/MewtocolNet/MewtocolInterfaceRegisterHandling.cs
index 3a37c65..1c80a4f 100644
--- a/MewtocolNet/MewtocolInterfaceRegisterHandling.cs
+++ b/MewtocolNet/MewtocolInterfaceRegisterHandling.cs
@@ -255,7 +255,7 @@ namespace MewtocolNet {
}
var assembler = new RegisterAssembler(this);
- var registers = assembler.Assemble(regBuild);
+ var registers = assembler.AssembleAll(regBuild);
AddRegisters(registers.ToArray());
}
diff --git a/MewtocolNet/MewtocolInterfaceRequests.cs b/MewtocolNet/MewtocolInterfaceRequests.cs
index 4d562a8..a44fd16 100644
--- a/MewtocolNet/MewtocolInterfaceRequests.cs
+++ b/MewtocolNet/MewtocolInterfaceRequests.cs
@@ -94,9 +94,16 @@ namespace MewtocolNet {
/// /// start address of the array
///
///
- public async Task WriteByteRange (int start, byte[] byteArr) {
+ public async Task WriteByteRange (int start, byte[] byteArr, bool flipBytes = false) {
+
+ string byteString;
+
+ if(flipBytes) {
+ byteString = byteArr.BigToMixedEndian().ToHexString();
+ } else {
+ byteString = byteArr.ToHexString();
+ }
- string byteString = byteArr.BigToMixedEndian().ToHexString();
var wordLength = byteArr.Length / 2;
if (byteArr.Length % 2 != 0)
wordLength++;
@@ -120,7 +127,7 @@ namespace MewtocolNet {
/// Flips bytes from big to mixed endian
/// Gets invoked when the progress changes, contains the progress as a double
/// A byte array or null of there was an error
- public async Task ReadByteRangeNonBlocking (int start, int count, bool flipBytes = true, Action onProgress = null) {
+ public async Task ReadByteRangeNonBlocking (int start, int count, bool flipBytes = false, Action onProgress = null) {
var byteList = new List();
@@ -167,99 +174,6 @@ namespace MewtocolNet {
#endregion
- #region Raw register reading / writing
-
- [Obsolete]
- internal async Task ReadRawRegisterAsync (IRegisterInternal _toRead) {
-
- var toreadType = _toRead.GetType();
-
- //returns a byte array 1 long and with the byte beeing 0 or 1
- if (toreadType == typeof(BoolRegister)) {
-
- string requeststring = $"%{GetStationNumber()}#RCS{_toRead.BuildMewtocolQuery()}";
- var result = await SendCommandAsync(requeststring);
- if (!result.Success) return null;
-
- var resultBool = result.Response.ParseRCSingleBit();
- if (resultBool != null) {
-
- return resultBool.Value ? new byte[] { 1 } : new byte[] { 0 };
-
- }
-
- }
-
- //returns a byte array 2 bytes or 4 bytes long depending on the data size
- if (toreadType.IsGenericType && _toRead.GetType().GetGenericTypeDefinition() == typeof(NumberRegister<>)) {
-
- string requeststring = $"%{GetStationNumber()}#RD{_toRead.BuildMewtocolQuery()}";
- var result = await SendCommandAsync(requeststring);
- if (!result.Success) return null;
-
- if(_toRead.RegisterType == RegisterType.DT) {
-
- return result.Response.ParseDTByteString(4).HexStringToByteArray();
-
- } else {
-
- return result.Response.ParseDTByteString(8).HexStringToByteArray();
-
- }
-
- }
-
- //returns a byte array with variable size
- if (toreadType == typeof(BytesRegister)) {
-
- string requeststring = $"%{GetStationNumber()}#RD{_toRead.BuildMewtocolQuery()}";
- var result = await SendCommandAsync(requeststring);
- if (!result.Success) return null;
-
- var resBytes = result.Response.ParseDTRawStringAsBytes();
-
- return resBytes;
-
- }
-
- if (toreadType == typeof(StringRegister)) {
-
- string requeststring = $"%{GetStationNumber()}#RD{_toRead.BuildMewtocolQuery()}";
- var result = await SendCommandAsync(requeststring);
- if (!result.Success) return null;
-
- var resBytes = result.Response.ParseDTRawStringAsBytes();
-
- return resBytes;
-
- }
-
- throw new Exception($"Failed to load the byte data for: {_toRead}");
-
- }
-
- [Obsolete]
- internal async Task WriteRawRegisterAsync (IRegisterInternal _toWrite, byte[] data) {
-
- var toWriteType = _toWrite.GetType();
-
- //returns a byte array 1 long and with the byte beeing 0 or 1
- if (toWriteType == typeof(BoolRegister)) {
-
- string reqStr = $"%{GetStationNumber()}#WCS{_toWrite.BuildMewtocolQuery()}{(data[0] == 1 ? "1" : "0")}";
- var res = await SendCommandAsync(reqStr);
- return res.Success;
-
- }
-
- string requeststring = $"%{GetStationNumber()}#WD{_toWrite.BuildMewtocolQuery()}{data.ToHexString()}";
- var result = await SendCommandAsync(requeststring);
- return result.Success;
-
- }
-
- #endregion
-
#region Helpers
internal string GetStationNumber() {
diff --git a/MewtocolNet/RegisterBuilding/RBuild.cs b/MewtocolNet/RegisterBuilding/RBuild.cs
index 3bd82b7..7547560 100644
--- a/MewtocolNet/RegisterBuilding/RBuild.cs
+++ b/MewtocolNet/RegisterBuilding/RBuild.cs
@@ -1,6 +1,4 @@
-using MewtocolNet.Exceptions;
-using MewtocolNet.RegisterAttributes;
-using MewtocolNet.Registers;
+using MewtocolNet.RegisterAttributes;
using MewtocolNet.UnderlyingRegisters;
using System;
using System.Collections;
@@ -10,7 +8,6 @@ using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Xml.Linq;
@@ -76,6 +73,7 @@ namespace MewtocolNet.RegisterBuilding {
//optional
internal uint? byteSize;
internal uint? bitSize;
+ internal int? stringSize;
internal int pollLevel = 1;
@@ -88,12 +86,15 @@ namespace MewtocolNet.RegisterBuilding {
public SBase() { }
- internal SBase(SData data) {
+ internal SBase(SData data, RBuild bldr) {
Data = data;
+ builder = bldr;
}
internal SData Data { get; set; }
+ internal RBuild builder;
+
}
internal struct ParseResult {
@@ -311,6 +312,13 @@ namespace MewtocolNet.RegisterBuilding {
}
+ ///
+ /// Starts the register builder for a new mewtocol address
+ /// Examples:
+ /// Address("DT100") | Address("R10A") | Address("DDT50", "MyRegisterName")
+ ///
+ /// Address name formatted as FP-Address like in FP-Winpro
+ /// Custom name for the register to referr to it later
public SAddress Address (string plcAddrName, string name = null) {
foreach (var method in parseMethods) {
@@ -326,7 +334,8 @@ namespace MewtocolNet.RegisterBuilding {
unfinishedList.Add(res.stepData);
return new SAddress {
- Data = res.stepData
+ Data = res.stepData,
+ builder = this,
};
} else if(res.state == ParseResultState.FailedHard) {
@@ -347,6 +356,22 @@ namespace MewtocolNet.RegisterBuilding {
public class SAddress : SBase {
+ ///
+ /// Sets the register as a dotnet type for direct conversion
+ ///
+ /// - Boolean R/X/Y registers
+ /// - 16 bit signed integer
+ /// - 16 bit un-signed integer
+ /// - 32 bit signed integer
+ /// - 32 bit un-signed integer
+ /// - 32 bit floating point
+ /// - 32 bit time from interpreted as
+ /// - 16 or 32 bit enums
+ /// - String of chars, the interface will automatically get the length
+ /// - As an array of bits
+ /// - As an array of bytes
+ ///
+ ///
public TempRegister AsType () {
if (!typeof(T).IsAllowedPlcCastingType()) {
@@ -357,10 +382,11 @@ namespace MewtocolNet.RegisterBuilding {
Data.dotnetVarType = typeof(T);
- return new TempRegister(Data);
+ return new TempRegister(Data, builder);
}
+ ///
public TempRegister AsType (Type type) {
if (!type.IsAllowedPlcCastingType()) {
@@ -371,10 +397,70 @@ namespace MewtocolNet.RegisterBuilding {
Data.dotnetVarType = type;
- return new TempRegister(Data);
+ return new TempRegister(Data, builder);
}
+ ///
+ /// Sets the register type as a predefined
+ ///
+ public TempRegister AsType (PlcVarType type) {
+
+ Data.dotnetVarType = type.GetDefaultDotnetType();
+
+ return new TempRegister(Data, builder);
+
+ }
+
+ ///
+ /// Sets the register type from the plc type string
+ /// Supported types:
+ ///
+ /// - BOOLBoolean R/X/Y registers
+ /// - INT16 bit signed integer
+ /// - UINT16 bit un-signed integer
+ /// - DINT32 bit signed integer
+ /// - UDINT32 bit un-signed integer
+ /// - REAL32 bit floating point
+ /// - TIME32 bit time interpreted as
+ /// - STRINGString of chars, the interface will automatically get the length
+ /// - STRING[N]String of chars, pre capped to N
+ /// - WORD16 bit word interpreted as
+ /// - DWORD32 bit double word interpreted as
+ ///
+ ///
+ public TempRegister AsType(string type) {
+
+ var stringMatch = Regex.Match(type, @"STRING *\[(?[0-9]*)\]", RegexOptions.IgnoreCase);
+ var arrayMatch = Regex.Match(type, @"ARRAY *\[(?[0-9]*)..(?[0-9]*)(?:\,(?[0-9]*)..(?[0-9]*))?(?:\,(?[0-9]*)..(?[0-9]*))?\] *OF {1,}(?.*)", RegexOptions.IgnoreCase);
+
+ if (Enum.TryParse(type, out var parsed)) {
+
+ Data.dotnetVarType = parsed.GetDefaultDotnetType();
+
+ } else if (stringMatch.Success) {
+
+ Data.dotnetVarType = typeof(string);
+ Data.stringSize = int.Parse(stringMatch.Groups["len"].Value);
+
+ } else if (arrayMatch.Success) {
+
+ throw new NotSupportedException("Arrays are currently not supported");
+
+ } else {
+
+ throw new NotSupportedException($"The mewtocol type '{type}' was not recognized");
+
+ }
+
+ return new TempRegister(Data, builder);
+
+ }
+
+ ///
+ /// Gets the data DT area as a
+ ///
+ /// Bytes to assign
public TempRegister AsBytes (uint byteLength) {
if (Data.regType != RegisterType.DT) {
@@ -386,10 +472,14 @@ namespace MewtocolNet.RegisterBuilding {
Data.byteSize = byteLength;
Data.dotnetVarType = typeof(byte[]);
- return new TempRegister(Data);
+ return new TempRegister(Data, builder);
}
+ ///
+ /// Gets the data DT area as a
+ ///
+ /// Number of bits to read
public TempRegister AsBits (ushort bitCount = 16) {
if (Data.regType != RegisterType.DT) {
@@ -401,10 +491,13 @@ namespace MewtocolNet.RegisterBuilding {
Data.bitSize = bitCount;
Data.dotnetVarType = typeof(BitArray);
- return new TempRegister(Data);
+ return new TempRegister(Data, builder);
}
+ ///
+ /// Automatically finds the best type for the register
+ ///
public TempRegister AutoType() {
switch (Data.regType) {
@@ -424,7 +517,7 @@ namespace MewtocolNet.RegisterBuilding {
break;
}
- return new TempRegister(Data);
+ return new TempRegister(Data, builder);
}
@@ -436,8 +529,11 @@ namespace MewtocolNet.RegisterBuilding {
public class TempRegister : SBase {
- internal TempRegister(SData data) : base(data) {}
+ internal TempRegister(SData data, RBuild bldr) : base(data, bldr) {}
+ ///
+ /// Sets the poll level of the register
+ ///
public TempRegister PollLevel (int level) {
Data.pollLevel = level;
@@ -446,14 +542,28 @@ namespace MewtocolNet.RegisterBuilding {
}
- public async Task WriteToAsync (T value) => throw new NotImplementedException();
+ ///
+ /// Writes data to the register and bypasses the memory manager
+ ///
+ /// The value to write
+ /// True if success
+ public async Task WriteToAsync (T value) => await builder.WriteAnonymousAsync(this, value);
+
+ ///
+ /// Reads data from the register and bypasses the memory manager
+ ///
+ /// The value read or null if failed
+ public async Task ReadFromAsync () => await builder.ReadAnonymousAsync(this);
}
public class TempRegister : SBase {
- internal TempRegister(SData data) : base(data) { }
+ internal TempRegister(SData data, RBuild bldr) : base(data, bldr) { }
+ ///
+ /// Sets the poll level of the register
+ ///
public TempRegister PollLevel (int level) {
Data.pollLevel = level;
@@ -462,7 +572,20 @@ namespace MewtocolNet.RegisterBuilding {
}
- internal TempRegister RegCollection (RegisterCollection col) {
+ ///
+ /// Writes data to the register and bypasses the memory manager
+ ///
+ /// The value to write
+ /// True if success
+ public async Task WriteToAsync(object value) => await builder.WriteAnonymousAsync(this, value);
+
+ ///
+ /// Reads data from the register and bypasses the memory manager
+ ///
+ /// The value read or null if failed
+ public async Task