From 4666d3071b7d47d11d6f565a0f00b57910c289a0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Felix=20Wei=C3=9F?=
<72068105+Sandoun@users.noreply.github.com>
Date: Fri, 14 Jul 2023 17:45:31 +0200
Subject: [PATCH] Array support first addition
---
.../PlcCodeTestedAttribute.cs | 2 +-
.../PlcEXRTAttribute.cs | 2 +-
.../PlcLegacyAttribute.cs | 2 +-
MewtocolNet/Documentation/docs.xml | 75 +++++++
MewtocolNet/Helpers/MewtocolHelpers.cs | 2 +-
MewtocolNet/Mewtocol.cs | 2 +-
MewtocolNet/MewtocolInterface.cs | 13 +-
.../MewtocolInterfaceRegisterHandling.cs | 14 +-
MewtocolNet/PublicEnums/PlcType.cs | 2 +-
.../PublicEnums/RegisterBuildSource.cs | 11 +
.../RegisterAttributes/RegisterAttribute.cs | 12 +-
MewtocolNet/RegisterBuilding/RBuild.cs | 205 ++++++++++++++----
.../RegisterBuilding/RegisterAssembler.cs | 13 +-
MewtocolNet/Registers/BaseRegister.cs | 47 ++--
MewtocolNet/Registers/NumberRegister.cs | 6 +
MewtocolNet/UnderlyingRegisters/DTArea.cs | 14 +-
.../LinkedRegisterGroup.cs | 18 ++
.../UnderlyingRegisters/MemoryAreaManager.cs | 190 ++++++++--------
18 files changed, 444 insertions(+), 186 deletions(-)
rename MewtocolNet/{DocAttributes => Documentation}/PlcCodeTestedAttribute.cs (87%)
rename MewtocolNet/{DocAttributes => Documentation}/PlcEXRTAttribute.cs (87%)
rename MewtocolNet/{DocAttributes => Documentation}/PlcLegacyAttribute.cs (87%)
create mode 100644 MewtocolNet/Documentation/docs.xml
create mode 100644 MewtocolNet/PublicEnums/RegisterBuildSource.cs
create mode 100644 MewtocolNet/UnderlyingRegisters/LinkedRegisterGroup.cs
diff --git a/MewtocolNet/DocAttributes/PlcCodeTestedAttribute.cs b/MewtocolNet/Documentation/PlcCodeTestedAttribute.cs
similarity index 87%
rename from MewtocolNet/DocAttributes/PlcCodeTestedAttribute.cs
rename to MewtocolNet/Documentation/PlcCodeTestedAttribute.cs
index 3883f15..a29c9f8 100644
--- a/MewtocolNet/DocAttributes/PlcCodeTestedAttribute.cs
+++ b/MewtocolNet/Documentation/PlcCodeTestedAttribute.cs
@@ -2,7 +2,7 @@
using System.Collections.Generic;
using System.Text;
-namespace MewtocolNet.DocAttributes {
+namespace MewtocolNet.Documentation {
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
internal class PlcCodeTestedAttribute : Attribute {
diff --git a/MewtocolNet/DocAttributes/PlcEXRTAttribute.cs b/MewtocolNet/Documentation/PlcEXRTAttribute.cs
similarity index 87%
rename from MewtocolNet/DocAttributes/PlcEXRTAttribute.cs
rename to MewtocolNet/Documentation/PlcEXRTAttribute.cs
index 1975a52..ecffa7f 100644
--- a/MewtocolNet/DocAttributes/PlcEXRTAttribute.cs
+++ b/MewtocolNet/Documentation/PlcEXRTAttribute.cs
@@ -2,7 +2,7 @@
using System.Collections.Generic;
using System.Text;
-namespace MewtocolNet.DocAttributes {
+namespace MewtocolNet.Documentation {
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
internal class PlcEXRTAttribute : Attribute {
diff --git a/MewtocolNet/DocAttributes/PlcLegacyAttribute.cs b/MewtocolNet/Documentation/PlcLegacyAttribute.cs
similarity index 87%
rename from MewtocolNet/DocAttributes/PlcLegacyAttribute.cs
rename to MewtocolNet/Documentation/PlcLegacyAttribute.cs
index 01367dc..555ef52 100644
--- a/MewtocolNet/DocAttributes/PlcLegacyAttribute.cs
+++ b/MewtocolNet/Documentation/PlcLegacyAttribute.cs
@@ -2,7 +2,7 @@
using System.Collections.Generic;
using System.Text;
-namespace MewtocolNet.DocAttributes {
+namespace MewtocolNet.Documentation {
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
internal class PlcLegacyAttribute : Attribute {
diff --git a/MewtocolNet/Documentation/docs.xml b/MewtocolNet/Documentation/docs.xml
new file mode 100644
index 0000000..a32571f
--- /dev/null
+++ b/MewtocolNet/Documentation/docs.xml
@@ -0,0 +1,75 @@
+
+
+
+
+ -
+
+
+
+ Boolean R/X/Y registers
+
+ -
+
+
+
+ 16 bit signed integer
+
+ -
+
+
+
+ 16 bit un-signed integer
+
+ -
+
+
+
+ 16 bit word (2 bytes)
+
+ -
+
+
+
+ 32 bit signed integer
+
+ -
+
+
+
+ 32 bit un-signed integer
+
+ -
+
+
+
+ 32 bit word (4 bytes)
+
+ -
+
+
+
+ 32 bit floating point
+
+ -
+
+
+
+
+ 32 bit time from interpreted as
+
+
+ -
+
+
+
+ 16 or 32 bit enums, also supports flags
+
+ -
+
+
+
+ String of chars, the interface will automatically get the length
+
+
+
+
\ No newline at end of file
diff --git a/MewtocolNet/Helpers/MewtocolHelpers.cs b/MewtocolNet/Helpers/MewtocolHelpers.cs
index 493f44d..7f3d009 100644
--- a/MewtocolNet/Helpers/MewtocolHelpers.cs
+++ b/MewtocolNet/Helpers/MewtocolHelpers.cs
@@ -1,4 +1,4 @@
-using MewtocolNet.DocAttributes;
+using MewtocolNet.Documentation;
using MewtocolNet.Registers;
using System;
using System.Collections;
diff --git a/MewtocolNet/Mewtocol.cs b/MewtocolNet/Mewtocol.cs
index ada7bc7..1aaa327 100644
--- a/MewtocolNet/Mewtocol.cs
+++ b/MewtocolNet/Mewtocol.cs
@@ -125,7 +125,7 @@ namespace MewtocolNet {
///
///
- public int MaxOptimizationDistance { get; set; } = 8;
+ public int MaxOptimizationDistance { get; set; } = 4;
///
/// The max number of registers per request group
diff --git a/MewtocolNet/MewtocolInterface.cs b/MewtocolNet/MewtocolInterface.cs
index 5abde05..6b8fb62 100644
--- a/MewtocolNet/MewtocolInterface.cs
+++ b/MewtocolNet/MewtocolInterface.cs
@@ -158,10 +158,15 @@ namespace MewtocolNet {
var asInternal = (BaseRegister)o;
- Logger.Log($"{asInternal.GetMewName()} " +
- $"{(o.Name != null ? $"({o.Name}) " : "")}" +
- $"{asInternal.underlyingSystemType} " +
- $"changed to \"{asInternal.GetValueString()}\"", LogLevel.Change, this);
+ var sb = new StringBuilder();
+ sb.Append(asInternal.GetMewName());
+ if(asInternal.Name != null) {
+ sb.Append(asInternal.autoGenerated ? $" (Auto)" : $" ({o.Name})");
+ }
+ sb.Append($" {asInternal.underlyingSystemType.Name}");
+ sb.Append($" changed to \"{asInternal.GetValueString()}\"");
+
+ Logger.Log(sb.ToString(), LogLevel.Change, this);
OnRegisterChangedUpdateProps((IRegisterInternal)o);
diff --git a/MewtocolNet/MewtocolInterfaceRegisterHandling.cs b/MewtocolNet/MewtocolInterfaceRegisterHandling.cs
index 28dc91e..ecb9288 100644
--- a/MewtocolNet/MewtocolInterfaceRegisterHandling.cs
+++ b/MewtocolNet/MewtocolInterfaceRegisterHandling.cs
@@ -190,7 +190,7 @@ namespace MewtocolNet {
#endregion
- #region Register Colleciton adding
+ #region Register Collection adding
///
/// Adds the given register collection and all its registers with attributes to the register list
@@ -219,18 +219,14 @@ namespace MewtocolNet {
var pollFreqAttr = (PollLevelAttribute)attributes.FirstOrDefault(x => x.GetType() == typeof(PollLevelAttribute));
- if (!prop.PropertyType.IsAllowedPlcCastingType()) {
- throw new MewtocolException($"The register attribute property type is not allowed ({prop.PropertyType})");
- }
-
var dotnetType = prop.PropertyType;
int pollLevel = 1;
if (pollFreqAttr != null) pollLevel = pollFreqAttr.pollLevel;
//add builder item
- regBuild
- .Address(cAttribute.MewAddress)
+ var stp1 = regBuild
+ .AddressFromAttribute(cAttribute.MewAddress, cAttribute.TypeDef)
.AsType(dotnetType.IsEnum ? dotnetType.UnderlyingSystemType : dotnetType)
.PollLevel(pollLevel)
.RegCollection(collection)
@@ -255,7 +251,7 @@ namespace MewtocolNet {
}
var assembler = new RegisterAssembler(this);
- var registers = assembler.AssembleAll(regBuild);
+ var registers = assembler.AssembleAll(regBuild, true);
AddRegisters(registers.ToArray());
}
@@ -290,7 +286,7 @@ namespace MewtocolNet {
internal void InsertRegistersToMemoryStack (List registers) {
- memoryManager.LinkRegisters(registers);
+ memoryManager.LinkAndMergeRegisters(registers);
}
diff --git a/MewtocolNet/PublicEnums/PlcType.cs b/MewtocolNet/PublicEnums/PlcType.cs
index 2a3ce36..5f92a92 100644
--- a/MewtocolNet/PublicEnums/PlcType.cs
+++ b/MewtocolNet/PublicEnums/PlcType.cs
@@ -1,4 +1,4 @@
-using MewtocolNet.DocAttributes;
+using MewtocolNet.Documentation;
namespace MewtocolNet {
diff --git a/MewtocolNet/PublicEnums/RegisterBuildSource.cs b/MewtocolNet/PublicEnums/RegisterBuildSource.cs
new file mode 100644
index 0000000..3b62a58
--- /dev/null
+++ b/MewtocolNet/PublicEnums/RegisterBuildSource.cs
@@ -0,0 +1,11 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace MewtocolNet.PublicEnums {
+ public enum RegisterBuildSource {
+ Anonymous,
+ Manual,
+ Attribute,
+ }
+}
diff --git a/MewtocolNet/RegisterAttributes/RegisterAttribute.cs b/MewtocolNet/RegisterAttributes/RegisterAttribute.cs
index 5546f82..4fd66c9 100644
--- a/MewtocolNet/RegisterAttributes/RegisterAttribute.cs
+++ b/MewtocolNet/RegisterAttributes/RegisterAttribute.cs
@@ -9,13 +9,19 @@ namespace MewtocolNet.RegisterAttributes {
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class RegisterAttribute : Attribute {
- internal int AssignedBitIndex = -1;
-
internal string MewAddress = null;
+ internal string TypeDef = null;
- public RegisterAttribute(string mewAddress) {
+ ///
+ /// Builds automatic data transfer between the property below this and
+ /// the plc register
+ ///
+ /// The FP-Address (DT, DDT, R, X, Y..)
+ /// The type definition from the PLC (STRING[n], ARRAY [0..2] OF ...)
+ public RegisterAttribute(string mewAddress, string plcTypeDef = null) {
MewAddress = mewAddress;
+ TypeDef = plcTypeDef;
}
diff --git a/MewtocolNet/RegisterBuilding/RBuild.cs b/MewtocolNet/RegisterBuilding/RBuild.cs
index 28087e0..1ca9264 100644
--- a/MewtocolNet/RegisterBuilding/RBuild.cs
+++ b/MewtocolNet/RegisterBuilding/RBuild.cs
@@ -1,16 +1,20 @@
-using MewtocolNet.RegisterAttributes;
+using MewtocolNet.PublicEnums;
+using MewtocolNet.RegisterAttributes;
using MewtocolNet.UnderlyingRegisters;
using System;
using System.Collections;
using System.Collections.Generic;
+using System.Data;
using System.Data.Common;
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;
+using static MewtocolNet.RegisterBuilding.RBuild;
namespace MewtocolNet.RegisterBuilding {
@@ -63,6 +67,9 @@ namespace MewtocolNet.RegisterBuilding {
internal class SData {
+ internal RegisterBuildSource buildSource = RegisterBuildSource.Anonymous;
+
+ internal bool wasAddressStringRangeBased;
internal string originalParseStr;
internal string name;
internal RegisterType regType;
@@ -77,9 +84,12 @@ namespace MewtocolNet.RegisterBuilding {
internal int pollLevel = 1;
+ //only for building from attributes
internal RegisterCollection regCollection;
internal PropertyInfo boundProperty;
+ internal string typeDef;
+
}
public class SBase {
@@ -304,6 +314,7 @@ namespace MewtocolNet.RegisterBuilding {
state = ParseResultState.Success,
stepData = new SData {
regType = RegisterType.DT_BYTE_RANGE,
+ wasAddressStringRangeBased = true,
dotnetVarType = typeof(byte[]),
memAddress = addresses[0],
byteSize = (addresses[1] - addresses[0] + 1) * 2
@@ -330,6 +341,7 @@ namespace MewtocolNet.RegisterBuilding {
if (!string.IsNullOrEmpty(name)) res.stepData.name = name;
res.stepData.originalParseStr = plcAddrName;
+ res.stepData.buildSource = RegisterBuildSource.Manual;
unfinishedList.Add(res.stepData);
@@ -350,6 +362,16 @@ namespace MewtocolNet.RegisterBuilding {
}
+ //internal use only, adds a type definition (for use when building from attibute)
+ internal SAddress AddressFromAttribute (string plcAddrName, string typeDef) {
+
+ var built = Address(plcAddrName);
+ built.Data.typeDef = typeDef;
+ built.Data.buildSource = RegisterBuildSource.Attribute;
+ return built;
+
+ }
+
#endregion
#region Type determination stage
@@ -358,21 +380,10 @@ namespace MewtocolNet.RegisterBuilding {
///
/// Sets the register as a dotnet type for direct conversion
- ///
- /// - Boolean R/X/Y registers
- /// - 16 bit signed integer
- /// - 16 bit un-signed integer
- /// - 16 bit word (2 bytes)
- /// - 32 bit signed integer
- /// - 32 bit un-signed integer
- /// - 32 bit word (4 bytes)
- /// - 32 bit floating point
- /// - 32 bit time from interpreted as
- /// - 16 or 32 bit enums, also supports flags
- /// - String of chars, the interface will automatically get the length
- /// - As an array of bytes
- ///
///
+ ///
+ ///
+ ///
public TempRegister AsType () {
if (!typeof(T).IsAllowedPlcCastingType()) {
@@ -387,9 +398,77 @@ namespace MewtocolNet.RegisterBuilding {
}
- ///
+ ///
+ /// Sets the register as a dotnet type for direct conversion
+ ///
+ ///
+ ///
+ ///
+ ///
public TempRegister AsType (Type type) {
+ //was ranged syntax array build
+ if (Data.wasAddressStringRangeBased && type.IsArray && type.GetArrayRank() == 1) {
+
+ //invoke generic AsTypeArray
+ MethodInfo method = typeof(SAddress).GetMethod(nameof(AsTypeArray));
+ MethodInfo generic = method.MakeGenericMethod(type);
+
+ var elementType = type.GetElementType();
+
+ if (!elementType.IsAllowedPlcCastingType()) {
+
+ throw new NotSupportedException($"The dotnet type {elementType}, is not supported for PLC type casting");
+
+ }
+
+ bool isExtensionTypeDT = typeof(MewtocolExtensionTypeDT).IsAssignableFrom(elementType);
+ bool isExtensionTypeDDT = typeof(MewtocolExtensionTypeDDT).IsAssignableFrom(elementType);
+
+ int byteSizePerItem = 0;
+ if(elementType.Namespace.StartsWith("System")) {
+ byteSizePerItem = Marshal.SizeOf(elementType);
+ } else if (isExtensionTypeDT) {
+ byteSizePerItem = 2;
+ } else if (isExtensionTypeDDT) {
+ byteSizePerItem = 4;
+ }
+
+ //check if it fits without remainder
+ if(Data.byteSize % byteSizePerItem != 0) {
+ throw new NotSupportedException($"The array element type {elementType} doesn't fit into the adress range");
+ }
+
+ return (TempRegister)generic.Invoke(this, new object[] {
+ //element count
+ new int[] { (int)((Data.byteSize / byteSizePerItem) / 2) }
+ });
+
+ } else if(Data.wasAddressStringRangeBased) {
+
+ throw new NotSupportedException("DT range building is only allowed for 1 dimensional arrays");
+
+ }
+
+ //for internal only, relay to AsType from string
+ if (Data.buildSource == RegisterBuildSource.Attribute) {
+
+ if ((type.IsArray || type == typeof(string)) && Data.typeDef != null) {
+
+ return AsType(Data.typeDef);
+
+ } else if ((type.IsArray || type == typeof(string)) && Data.typeDef == null) {
+
+ throw new NotSupportedException("Typedef parameter is needed for array or string types");
+
+ } else if (Data.typeDef != null) {
+
+ throw new NotSupportedException("Can't use the typedef parameter on non array or string types");
+
+ }
+
+ }
+
if (!type.IsAllowedPlcCastingType()) {
throw new NotSupportedException($"The dotnet type {type}, is not supported for PLC type casting");
@@ -430,7 +509,7 @@ namespace MewtocolNet.RegisterBuilding {
/// - DWORD32 bit double word interpreted as
///
///
- public TempRegister AsType(string type) {
+ 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);
@@ -446,11 +525,46 @@ namespace MewtocolNet.RegisterBuilding {
} else if (arrayMatch.Success) {
- throw new NotSupportedException("Arrays are currently not supported");
+ //invoke generic AsTypeArray
+
+ string arrTypeString = arrayMatch.Groups["t"].Value;
+
+ if (Enum.TryParse(arrTypeString, out var parsedArrType)) {
+
+ var dotnetArrType = parsedArrType.GetDefaultDotnetType();
+ var indices = new List();
+
+ for (int i = 1; i < 4; i++) {
+
+ var arrStart = arrayMatch.Groups[$"S{i}"]?.Value;
+ var arrEnd = arrayMatch.Groups[$"E{i}"]?.Value;
+ if (string.IsNullOrEmpty(arrStart) || string.IsNullOrEmpty(arrEnd)) break;
+
+ var arrStartInt = int.Parse(arrStart);
+ var arrEndInt = int.Parse(arrEnd);
+
+ indices.Add(arrEndInt - arrStartInt + 1);
+
+ }
+
+ var arr = Array.CreateInstance(dotnetArrType, indices.ToArray());
+ var arrType = arr.GetType();
+
+ MethodInfo method = typeof(SAddress).GetMethod(nameof(AsTypeArray));
+ MethodInfo generic = method.MakeGenericMethod(arrType);
+
+ return (TempRegister)generic.Invoke(this, new object[] {
+ indices.ToArray()
+ });
+
+ } else {
+
+ throw new NotSupportedException($"The FP type '{arrTypeString}' was not recognized");
+ }
} else {
- throw new NotSupportedException($"The mewtocol type '{type}' was not recognized");
+ throw new NotSupportedException($"The FP type '{type}' was not recognized");
}
@@ -459,38 +573,41 @@ namespace MewtocolNet.RegisterBuilding {
}
///
- /// Gets the data DT area as a
+ /// Sets the register as a (multidimensional) array targeting a PLC array
///
- /// Bytes to assign
- public TempRegister AsBytes (uint byteLength) {
+ ///
+ ///
+ ///
+ ///
+ /// Indicies for multi dimensional arrays, for normal arrays just one INT
+ ///
+ ///
+ /// One dimensional arrays:
+ /// ARRAY [0..2] OF INT = AsTypeArray<short[]>(3)
+ /// ARRAY [5..6] OF DWORD = AsTypeArray<DWord[]>(2)
+ ///
+ /// Multi dimensional arrays:
+ /// ARRAY [0..2, 0..3, 0..4] OF INT = AsTypeArray<short[,,]>(3,4,5)
+ /// ARRAY [5..6, 0..2] OF DWORD = AsTypeArray<DWord[,]>(2, 3)
+ ///
+ public TempRegister AsTypeArray (params int[] indicies) {
- if (Data.regType != RegisterType.DT) {
+ if (!typeof(T).IsArray)
+ throw new NotSupportedException($"The type {typeof(T)} was no array");
- throw new NotSupportedException($"Cant use the {nameof(AsBytes)} converter on a non {nameof(RegisterType.DT)} register");
+ var arrRank = typeof(T).GetArrayRank();
+ var elBaseType = typeof(T).GetElementType();
- }
+ if (arrRank > 3)
+ throw new NotSupportedException($"4+ dimensional arrays are not supported");
- Data.byteSize = byteLength;
- Data.dotnetVarType = typeof(byte[]);
+ if (!elBaseType.IsAllowedPlcCastingType())
+ throw new NotSupportedException($"The dotnet type {typeof(T)}, is not supported for PLC array type casting");
- return new TempRegister(Data, builder);
+ if (arrRank != indicies.Length)
+ throw new NotSupportedException($"All dimensional array indicies must be set");
- }
-
- ///
- /// Gets the data DT area as a
- ///
- /// Number of bits to read
- public TempRegister AsBits (ushort bitCount = 16) {
-
- if (Data.regType != RegisterType.DT) {
-
- throw new NotSupportedException($"Cant use the {nameof(AsBits)} converter on a non {nameof(RegisterType.DT)} register");
-
- }
-
- Data.bitSize = bitCount;
- Data.dotnetVarType = typeof(BitArray);
+ Data.dotnetVarType = typeof(T);
return new TempRegister(Data, builder);
diff --git a/MewtocolNet/RegisterBuilding/RegisterAssembler.cs b/MewtocolNet/RegisterBuilding/RegisterAssembler.cs
index 8accdce..24da53f 100644
--- a/MewtocolNet/RegisterBuilding/RegisterAssembler.cs
+++ b/MewtocolNet/RegisterBuilding/RegisterAssembler.cs
@@ -21,16 +21,16 @@ namespace MewtocolNet.RegisterBuilding {
}
- internal List AssembleAll (RBuild rBuildData) {
+ internal List AssembleAll (RBuild rBuildData, bool flagAutoGenerated = false) {
List generatedInstances = new List();
foreach (var data in rBuildData.unfinishedList) {
var generatedInstance = Assemble(data);
-
+ generatedInstance.autoGenerated = flagAutoGenerated;
generatedInstances.Add(generatedInstance);
-
+
}
return generatedInstances;
@@ -44,6 +44,13 @@ namespace MewtocolNet.RegisterBuilding {
BaseRegister generatedInstance = null;
+ if(data.dotnetVarType.IsArray) {
+
+ Console.WriteLine();
+ return new ArrayRegister(0, 0);
+
+ }
+
if (data.dotnetVarType.IsEnum) {
//-------------------------------------------
diff --git a/MewtocolNet/Registers/BaseRegister.cs b/MewtocolNet/Registers/BaseRegister.cs
index 853454a..fc80712 100644
--- a/MewtocolNet/Registers/BaseRegister.cs
+++ b/MewtocolNet/Registers/BaseRegister.cs
@@ -24,6 +24,8 @@ namespace MewtocolNet.Registers {
internal Type underlyingSystemType;
internal IMemoryArea underlyingMemory;
+ internal bool autoGenerated;
+
internal object lastValue = null;
internal string name;
internal uint memoryAddress;
@@ -128,6 +130,8 @@ namespace MewtocolNet.Registers {
public virtual uint GetRegisterAddressLen() => throw new NotImplementedException();
+ public virtual uint GetRegisterAddressEnd() => MemoryAddress + GetRegisterAddressLen() - 1;
+
public string GetRegisterWordRangeString() => $"{GetMewName()} - {MemoryAddress + GetRegisterAddressLen() - 1}";
#endregion
@@ -156,6 +160,7 @@ namespace MewtocolNet.Registers {
return this.MemoryAddress == toCompare.MemoryAddress &&
this.RegisterType == toCompare.RegisterType &&
+ this.underlyingSystemType == toCompare.underlyingSystemType &&
this.GetRegisterAddressLen() == toCompare.GetRegisterAddressLen() &&
this.GetSpecialAddress() == toCompare.GetSpecialAddress();
@@ -199,20 +204,34 @@ namespace MewtocolNet.Registers {
public virtual string Explain () {
StringBuilder sb = new StringBuilder();
- sb.AppendLine($"MewName: {GetMewName()}");
- sb.AppendLine($"Name: {Name ?? "Not named"}");
- sb.AppendLine($"Value: {GetValueString()}");
- sb.AppendLine($"Perf. Reads: {successfulReads}, Writes: {successfulWrites}");
- sb.AppendLine($"Register Type: {RegisterType}");
- sb.AppendLine($"Underlying System Type: {underlyingSystemType}");
- sb.AppendLine($"Address: {GetRegisterWordRangeString()}");
- if (this is StringRegister sr) sb.AppendLine($"Reserved: {sr.ReservedSize}, Used: {sr.UsedSize}");
- if (GetSpecialAddress() != null) sb.AppendLine($"SPAddress: {GetSpecialAddress():X1}");
- 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 (boundProperties != null && boundProperties.Count > 0) sb.AppendLine($"Bound props: {string.Join(", ", boundProperties)}");
- else sb.AppendLine("No bound properties");
+ sb.Append($"Address: {GetRegisterWordRangeString()}\n");
+
+ if (GetType().IsGenericType)
+ sb.Append($"Type: {RegisterType}, NumberRegister<{GetType().GenericTypeArguments[0]}>\n");
+ else
+ sb.AppendLine($"Type: {RegisterType}, {GetType().Name}\n");
+
+ sb.Append($"Name: {Name ?? "Not named"}\n");
+
+ if(Value != null)
+ sb.Append($"Value: {GetValueString()}\n");
+
+ sb.Append($"Reads: {successfulReads}, Writes: {successfulWrites}\n");
+ sb.Append($"Underlying System Type: {underlyingSystemType}\n");
+
+ if (this is StringRegister sr)
+ sb.Append($"Reserved: {sr.ReservedSize}, Used: {sr.UsedSize}\n");
+
+ if (GetSpecialAddress() != null)
+ sb.Append($"SPAddress: {GetSpecialAddress():X1}\n");
+
+ if (containedCollection != null)
+ sb.Append($"In collection: {containedCollection.GetType()}\n");
+
+ if (boundProperties != null && boundProperties.Count > 0)
+ sb.Append($"Bound props: \n\t{string.Join(",\n\t", boundProperties)}");
+ else
+ sb.Append("No bound properties");
return sb.ToString();
diff --git a/MewtocolNet/Registers/NumberRegister.cs b/MewtocolNet/Registers/NumberRegister.cs
index 924ce39..24354c7 100644
--- a/MewtocolNet/Registers/NumberRegister.cs
+++ b/MewtocolNet/Registers/NumberRegister.cs
@@ -154,6 +154,12 @@ namespace MewtocolNet.Registers {
var res = await attachedInterface.ReadByteRangeNonBlocking((int)MemoryAddress, (int)GetRegisterAddressLen() * 2, false);
if (res == null) return null;
+ var matchingReg = attachedInterface.memoryManager.GetAllRegisters()
+ .FirstOrDefault(x => x.IsSameAddressAndType(this));
+
+ if (matchingReg != null)
+ matchingReg.underlyingMemory.SetUnderlyingBytes(matchingReg, res);
+
return SetValueFromBytes(res);
}
diff --git a/MewtocolNet/UnderlyingRegisters/DTArea.cs b/MewtocolNet/UnderlyingRegisters/DTArea.cs
index adf4a0b..b3a24dc 100644
--- a/MewtocolNet/UnderlyingRegisters/DTArea.cs
+++ b/MewtocolNet/UnderlyingRegisters/DTArea.cs
@@ -17,9 +17,10 @@ namespace MewtocolNet.UnderlyingRegisters {
internal byte[] underlyingBytes = new byte[2];
- internal List linkedRegisters = new List();
-
- internal Dictionary> crossRegisterBindings = new Dictionary>();
+ ///
+ /// List of register link groups that are managed in this memory area
+ ///
+ internal List managedRegisters = new List();
public ulong AddressStart => addressStart;
public ulong AddressEnd => addressEnd;
@@ -51,7 +52,7 @@ namespace MewtocolNet.UnderlyingRegisters {
public void UpdateAreaRegisterValues() {
- foreach (var register in this.linkedRegisters) {
+ foreach (var register in this.managedRegisters.SelectMany(x => x.Linked)) {
var regStart = register.MemoryAddress;
var addLen = (int)register.GetRegisterAddressLen();
@@ -120,7 +121,8 @@ namespace MewtocolNet.UnderlyingRegisters {
private async Task CheckDynamicallySizedRegistersAsync () {
//calibrating at runtime sized registers
- var uncalibratedStringRegisters = linkedRegisters
+ var uncalibratedStringRegisters = managedRegisters
+ .SelectMany(x => x.Linked)
.Where(x => x is StringRegister sreg && !sreg.isCalibratedFromPlc)
.Cast()
.ToList();
@@ -129,7 +131,7 @@ namespace MewtocolNet.UnderlyingRegisters {
await register.CalibrateFromPLC();
if (uncalibratedStringRegisters.Count > 0)
- mewInterface.memoryManager.MergeAndSizeDataAreas();
+ mewInterface.memoryManager.LinkAndMergeRegisters();
}
diff --git a/MewtocolNet/UnderlyingRegisters/LinkedRegisterGroup.cs b/MewtocolNet/UnderlyingRegisters/LinkedRegisterGroup.cs
new file mode 100644
index 0000000..28110fa
--- /dev/null
+++ b/MewtocolNet/UnderlyingRegisters/LinkedRegisterGroup.cs
@@ -0,0 +1,18 @@
+using MewtocolNet.Registers;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace MewtocolNet.UnderlyingRegisters {
+
+ internal class LinkedRegisterGroup {
+
+ internal uint AddressStart;
+
+ internal uint AddressEnd;
+
+ internal List Linked = new List();
+
+ }
+
+}
diff --git a/MewtocolNet/UnderlyingRegisters/MemoryAreaManager.cs b/MewtocolNet/UnderlyingRegisters/MemoryAreaManager.cs
index ab7edcf..b654888 100644
--- a/MewtocolNet/UnderlyingRegisters/MemoryAreaManager.cs
+++ b/MewtocolNet/UnderlyingRegisters/MemoryAreaManager.cs
@@ -47,12 +47,12 @@ namespace MewtocolNet.UnderlyingRegisters {
}
- internal void LinkRegisters (List registers = null) {
+ internal void LinkAndMergeRegisters (List registers = null) {
//for self calling
if (registers == null) registers = GetAllRegisters().ToList();
- //pre combine
+ //pre combine per address
var groupedByAdd = registers
.GroupBy(x => new {
x.MemoryAddress,
@@ -60,39 +60,20 @@ namespace MewtocolNet.UnderlyingRegisters {
spadd = x.GetSpecialAddress(),
});
- var filteredRegisters = new List();
- var propertyLookupTable = new Dictionary();
-
+ //poll level merging
foreach (var addressGroup in groupedByAdd) {
- var ordered = addressGroup.OrderBy(x => x.pollLevel);
- var highestPollLevel = ordered.Max(x => x.pollLevel);
+ //determine highest poll level for same addresses
+ var highestPollLevel = addressGroup.Max(x => x.pollLevel);
- var distinctByUnderlyingType =
- ordered.GroupBy(x => x.underlyingSystemType).ToList();
-
- foreach (var underlyingTypeGroup in distinctByUnderlyingType) {
-
- foreach (var register in underlyingTypeGroup) {
-
- register.pollLevel = highestPollLevel;
-
- var alreadyAdded = filteredRegisters
- .FirstOrDefault(x => x.underlyingSystemType == register.underlyingSystemType);
-
- if(alreadyAdded == null) {
- filteredRegisters.Add(register);
- } else {
- alreadyAdded.WithBoundProperties(register.boundProperties);
- }
-
- }
-
- }
+ //apply poll level to all registers in same group
+ foreach (var reg in addressGroup)
+ reg.pollLevel = highestPollLevel;
}
- foreach (var reg in filteredRegisters) {
+ //insert into area
+ foreach (var reg in registers) {
TestPollLevelExistence(reg);
@@ -111,6 +92,18 @@ namespace MewtocolNet.UnderlyingRegisters {
}
+ //order
+
+ foreach (var lvl in pollLevels) {
+
+ foreach (var area in lvl.dataAreas) {
+
+ area.managedRegisters = area.managedRegisters.OrderBy(x => x.AddressStart).ToList();
+
+ }
+
+ }
+
}
private void TestPollLevelExistence (BaseRegister reg) {
@@ -271,17 +264,27 @@ namespace MewtocolNet.UnderlyingRegisters {
insertReg.name = $"auto_{Guid.NewGuid().ToString("N")}";
}
- Console.WriteLine($"Adding linked register: {insertReg}");
- targetArea.linkedRegisters.Add(insertReg);
- return;
+ var existinglinkedGroup = targetArea.managedRegisters
+ .FirstOrDefault(x => x.AddressStart == insertReg.MemoryAddress &&
+ x.AddressEnd == insertReg.GetRegisterAddressEnd());
- }
+ if (existinglinkedGroup == null) {
+ // make a new linked group
+ existinglinkedGroup = new LinkedRegisterGroup {
+ AddressStart = insertReg.MemoryAddress,
+ AddressEnd = insertReg.GetRegisterAddressEnd(),
+ };
+ targetArea.managedRegisters.Add(existinglinkedGroup);
+ }
- internal void MergeAndSizeDataAreas () {
+ //check if the linked group has duplicate type registers
- //merge gaps that the algorithm didn't catch be rerunning the register attachment
-
- LinkRegisters();
+ var dupedTypeReg = existinglinkedGroup.Linked.FirstOrDefault(x => x.IsSameAddressAndType(insertReg));
+ if (dupedTypeReg != null) {
+ dupedTypeReg.WithBoundProperties(insertReg.boundProperties);
+ } else {
+ existinglinkedGroup.Linked.Add(insertReg);
+ }
}
@@ -350,75 +353,67 @@ namespace MewtocolNet.UnderlyingRegisters {
foreach (var pollLevel in pollLevels) {
- sb.AppendLine($"==== Poll lvl {pollLevel.level} ====");
+ sb.AppendLine($"\n> ==== Poll lvl {pollLevel.level} ====");
sb.AppendLine();
if (pollLevelConfigs[pollLevel.level].delay != null) {
- sb.AppendLine($"Poll each {pollLevelConfigs[pollLevel.level].delay?.TotalMilliseconds}ms");
+ sb.AppendLine($"> Poll each {pollLevelConfigs[pollLevel.level].delay?.TotalMilliseconds}ms");
} else {
- sb.AppendLine($"Poll every {pollLevelConfigs[pollLevel.level].skipNth} iterations");
+ sb.AppendLine($"> Poll every {pollLevelConfigs[pollLevel.level].skipNth} iterations");
}
- sb.AppendLine($"Level read time: {pollLevel.lastReadTimeMs}ms");
- sb.AppendLine($"Optimization distance: {maxOptimizationDistance}");
+ sb.AppendLine($"> Level read time: {pollLevel.lastReadTimeMs}ms");
+ sb.AppendLine($"> Optimization distance: {maxOptimizationDistance}");
sb.AppendLine();
- sb.AppendLine($"---- DT Areas: ----");
-
foreach (var area in pollLevel.dataAreas) {
+ var areaHeader = $"AREA => {area} = {area.underlyingBytes.Length} bytes";
+ sb.AppendLine($"* {new string('-', areaHeader.Length)}*");
+ sb.AppendLine($"* {areaHeader}");
+ sb.AppendLine($"* {new string('-', areaHeader.Length)}*");
+ sb.AppendLine("*");
+ sb.AppendLine($"* {(string.Join("\n* ", area.underlyingBytes.ToHexString(" ").SplitInParts(3 * 8)))}");
+ sb.AppendLine("*");
+
+ int seperatorLen = 50;
+
+ LinkedRegisterGroup prevGroup = null;
+
+ foreach (var linkedG in area.managedRegisters) {
+
+ if (prevGroup != null && (linkedG.AddressStart - prevGroup.AddressEnd - 1 > 0)) {
+
+ var dist = linkedG.AddressStart - prevGroup.AddressEnd - 1;
+
+ sb.AppendLine($"* {new string('=', seperatorLen + 3)}");
+ sb.AppendLine($"* Byte spacer: {dist} Words");
+ sb.AppendLine($"* {new string('=', seperatorLen + 3)}");
+
+ }
+
+ sb.AppendLine($"* {new string('_', seperatorLen + 3)}");
+ sb.AppendLine($"* || Linked group {linkedG.AddressStart} - {linkedG.AddressEnd}");
+ sb.AppendLine($"* || {new string('=', seperatorLen)}");
+
+ foreach (var reg in linkedG.Linked) {
+
+ string explained = reg.Explain();
+
+ sb.AppendLine($"* || {explained.Replace("\n", "\n* || ")}");
+
+ if (linkedG.Linked.Count > 1) {
+ sb.AppendLine($"* || {new string('-', seperatorLen)}");
+ }
+
+ }
+
+ sb.AppendLine($"* {new string('=', seperatorLen + 3)}");
+
+ prevGroup = linkedG;
+
+ }
+
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.Explain()}");
-
- }
-
- }
-
- sb.AppendLine($"---- WR X Area ----");
-
- foreach (var area in pollLevel.externalRelayInAreas) {
-
- sb.AppendLine(area.ToString());
-
- foreach (var reg in area.linkedRegisters) {
-
- sb.AppendLine($"{reg.Explain()}");
-
- }
-
- }
-
- sb.AppendLine($"---- WR Y Area ---");
-
- foreach (var area in pollLevel.externalRelayOutAreas) {
-
- sb.AppendLine(area.ToString());
-
- foreach (var reg in area.linkedRegisters) {
-
- sb.AppendLine($"{reg.Explain()}");
-
- }
-
- }
-
- sb.AppendLine($"---- WR R Area ----");
-
- foreach (var area in pollLevel.internalRelayAreas) {
-
- sb.AppendLine(area.ToString());
-
- foreach (var reg in area.linkedRegisters) {
-
- sb.AppendLine($"{reg.Explain()}");
-
- }
}
@@ -434,7 +429,8 @@ namespace MewtocolNet.UnderlyingRegisters {
foreach (var lvl in pollLevels) {
- registers.AddRange(lvl.dataAreas.SelectMany(x => x.linkedRegisters));
+ registers.AddRange(lvl.dataAreas.SelectMany(x => x.managedRegisters).SelectMany(x => x.Linked));
+
registers.AddRange(lvl.internalRelayAreas.SelectMany(x => x.linkedRegisters));
registers.AddRange(lvl.externalRelayInAreas.SelectMany(x => x.linkedRegisters));
registers.AddRange(lvl.externalRelayOutAreas.SelectMany(x => x.linkedRegisters));