Array support first addition

This commit is contained in:
Felix Weiß
2023-07-14 17:45:31 +02:00
parent 32c20e7360
commit 4666d3071b
18 changed files with 444 additions and 186 deletions

View File

@@ -2,7 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
namespace MewtocolNet.DocAttributes { namespace MewtocolNet.Documentation {
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)] [AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
internal class PlcCodeTestedAttribute : Attribute { internal class PlcCodeTestedAttribute : Attribute {

View File

@@ -2,7 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
namespace MewtocolNet.DocAttributes { namespace MewtocolNet.Documentation {
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)] [AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
internal class PlcEXRTAttribute : Attribute { internal class PlcEXRTAttribute : Attribute {

View File

@@ -2,7 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
namespace MewtocolNet.DocAttributes { namespace MewtocolNet.Documentation {
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)] [AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
internal class PlcLegacyAttribute : Attribute { internal class PlcLegacyAttribute : Attribute {

View File

@@ -0,0 +1,75 @@
<?xml version="1.0" encoding="utf-8" ?>
<extradoc>
<class name="support-conv-types">
<list type="bullet">
<item>
<term>
<see cref="bool"/>
</term>
<description>Boolean R/X/Y registers</description>
</item>
<item>
<term>
<see cref="short"/>
</term>
<description>16 bit signed integer</description>
</item>
<item>
<term>
<see cref="ushort"/>
</term>
<description>16 bit un-signed integer</description>
</item>
<item>
<term>
<see cref="Word"/>
</term>
<description>16 bit word (2 bytes)</description>
</item>
<item>
<term>
<see cref="int"/>
</term>
<description>32 bit signed integer</description>
</item>
<item>
<term>
<see cref="uint"/>
</term>
<description>32 bit un-signed integer</description>
</item>
<item>
<term>
<see cref="DWord"/>
</term>
<description>32 bit word (4 bytes)</description>
</item>
<item>
<term>
<see cref="float"/>
</term>
<description>32 bit floating point</description>
</item>
<item>
<term>
<see cref="TimeSpan"/>
</term>
<description>
32 bit time from <see cref="PlcVarType.TIME"/> interpreted as <see cref="TimeSpan"/>
</description>
</item>
<item>
<term>
<see cref="Enum"/>
</term>
<description>16 or 32 bit enums, also supports flags</description>
</item>
<item>
<term>
<see cref="string"/>
</term>
<description>String of chars, the interface will automatically get the length</description>
</item>
</list>
</class>
</extradoc>

View File

@@ -1,4 +1,4 @@
using MewtocolNet.DocAttributes; using MewtocolNet.Documentation;
using MewtocolNet.Registers; using MewtocolNet.Registers;
using System; using System;
using System.Collections; using System.Collections;

View File

@@ -125,7 +125,7 @@ namespace MewtocolNet {
/// </example> /// </example>
/// </summary> /// </summary>
public int MaxOptimizationDistance { get; set; } = 8; public int MaxOptimizationDistance { get; set; } = 4;
/// <summary> /// <summary>
/// The max number of registers per request group /// The max number of registers per request group

View File

@@ -158,10 +158,15 @@ namespace MewtocolNet {
var asInternal = (BaseRegister)o; var asInternal = (BaseRegister)o;
Logger.Log($"{asInternal.GetMewName()} " + var sb = new StringBuilder();
$"{(o.Name != null ? $"({o.Name}) " : "")}" + sb.Append(asInternal.GetMewName());
$"{asInternal.underlyingSystemType} " + if(asInternal.Name != null) {
$"changed to \"{asInternal.GetValueString()}\"", LogLevel.Change, this); 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); OnRegisterChangedUpdateProps((IRegisterInternal)o);

View File

@@ -190,7 +190,7 @@ namespace MewtocolNet {
#endregion #endregion
#region Register Colleciton adding #region Register Collection adding
/// <summary> /// <summary>
/// Adds the given register collection and all its registers with attributes to the register list /// 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)); 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; var dotnetType = prop.PropertyType;
int pollLevel = 1; int pollLevel = 1;
if (pollFreqAttr != null) pollLevel = pollFreqAttr.pollLevel; if (pollFreqAttr != null) pollLevel = pollFreqAttr.pollLevel;
//add builder item //add builder item
regBuild var stp1 = regBuild
.Address(cAttribute.MewAddress) .AddressFromAttribute(cAttribute.MewAddress, cAttribute.TypeDef)
.AsType(dotnetType.IsEnum ? dotnetType.UnderlyingSystemType : dotnetType) .AsType(dotnetType.IsEnum ? dotnetType.UnderlyingSystemType : dotnetType)
.PollLevel(pollLevel) .PollLevel(pollLevel)
.RegCollection(collection) .RegCollection(collection)
@@ -255,7 +251,7 @@ namespace MewtocolNet {
} }
var assembler = new RegisterAssembler(this); var assembler = new RegisterAssembler(this);
var registers = assembler.AssembleAll(regBuild); var registers = assembler.AssembleAll(regBuild, true);
AddRegisters(registers.ToArray()); AddRegisters(registers.ToArray());
} }
@@ -290,7 +286,7 @@ namespace MewtocolNet {
internal void InsertRegistersToMemoryStack (List<BaseRegister> registers) { internal void InsertRegistersToMemoryStack (List<BaseRegister> registers) {
memoryManager.LinkRegisters(registers); memoryManager.LinkAndMergeRegisters(registers);
} }

View File

@@ -1,4 +1,4 @@
using MewtocolNet.DocAttributes; using MewtocolNet.Documentation;
namespace MewtocolNet { namespace MewtocolNet {

View File

@@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace MewtocolNet.PublicEnums {
public enum RegisterBuildSource {
Anonymous,
Manual,
Attribute,
}
}

View File

@@ -9,13 +9,19 @@ namespace MewtocolNet.RegisterAttributes {
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class RegisterAttribute : Attribute { public class RegisterAttribute : Attribute {
internal int AssignedBitIndex = -1;
internal string MewAddress = null; internal string MewAddress = null;
internal string TypeDef = null;
public RegisterAttribute(string mewAddress) { /// <summary>
/// Builds automatic data transfer between the property below this and
/// the plc register
/// </summary>
/// <param name="mewAddress">The FP-Address (DT, DDT, R, X, Y..)</param>
/// <param name="plcTypeDef">The type definition from the PLC (STRING[n], ARRAY [0..2] OF ...)</param>
public RegisterAttribute(string mewAddress, string plcTypeDef = null) {
MewAddress = mewAddress; MewAddress = mewAddress;
TypeDef = plcTypeDef;
} }

View File

@@ -1,16 +1,20 @@
using MewtocolNet.RegisterAttributes; using MewtocolNet.PublicEnums;
using MewtocolNet.RegisterAttributes;
using MewtocolNet.UnderlyingRegisters; using MewtocolNet.UnderlyingRegisters;
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Data;
using System.Data.Common; using System.Data.Common;
using System.Diagnostics; using System.Diagnostics;
using System.Globalization; using System.Globalization;
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Xml.Linq; using System.Xml.Linq;
using static MewtocolNet.RegisterBuilding.RBuild;
namespace MewtocolNet.RegisterBuilding { namespace MewtocolNet.RegisterBuilding {
@@ -63,6 +67,9 @@ namespace MewtocolNet.RegisterBuilding {
internal class SData { internal class SData {
internal RegisterBuildSource buildSource = RegisterBuildSource.Anonymous;
internal bool wasAddressStringRangeBased;
internal string originalParseStr; internal string originalParseStr;
internal string name; internal string name;
internal RegisterType regType; internal RegisterType regType;
@@ -77,9 +84,12 @@ namespace MewtocolNet.RegisterBuilding {
internal int pollLevel = 1; internal int pollLevel = 1;
//only for building from attributes
internal RegisterCollection regCollection; internal RegisterCollection regCollection;
internal PropertyInfo boundProperty; internal PropertyInfo boundProperty;
internal string typeDef;
} }
public class SBase { public class SBase {
@@ -304,6 +314,7 @@ namespace MewtocolNet.RegisterBuilding {
state = ParseResultState.Success, state = ParseResultState.Success,
stepData = new SData { stepData = new SData {
regType = RegisterType.DT_BYTE_RANGE, regType = RegisterType.DT_BYTE_RANGE,
wasAddressStringRangeBased = true,
dotnetVarType = typeof(byte[]), dotnetVarType = typeof(byte[]),
memAddress = addresses[0], memAddress = addresses[0],
byteSize = (addresses[1] - addresses[0] + 1) * 2 byteSize = (addresses[1] - addresses[0] + 1) * 2
@@ -330,6 +341,7 @@ namespace MewtocolNet.RegisterBuilding {
if (!string.IsNullOrEmpty(name)) res.stepData.name = name; if (!string.IsNullOrEmpty(name)) res.stepData.name = name;
res.stepData.originalParseStr = plcAddrName; res.stepData.originalParseStr = plcAddrName;
res.stepData.buildSource = RegisterBuildSource.Manual;
unfinishedList.Add(res.stepData); 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 #endregion
#region Type determination stage #region Type determination stage
@@ -358,21 +380,10 @@ namespace MewtocolNet.RegisterBuilding {
/// <summary> /// <summary>
/// Sets the register as a dotnet <see cref="System"/> type for direct conversion /// Sets the register as a dotnet <see cref="System"/> type for direct conversion
/// <list type="bullet">
/// <item><term><see cref="bool"/></term><description>Boolean R/X/Y registers</description></item>
/// <item><term><see cref="short"/></term><description>16 bit signed integer</description></item>
/// <item><term><see cref="ushort"/></term><description>16 bit un-signed integer</description></item>
/// <item><term><see cref="Word"/></term><description>16 bit word (2 bytes)</description></item>
/// <item><term><see cref="int"/></term><description>32 bit signed integer</description></item>
/// <item><term><see cref="uint"/></term><description>32 bit un-signed integer</description></item>
/// <item><term><see cref="DWord"/></term><description>32 bit word (4 bytes)</description></item>
/// <item><term><see cref="float"/></term><description>32 bit floating point</description></item>
/// <item><term><see cref="TimeSpan"/></term><description>32 bit time from <see cref="PlcVarType.TIME"/> interpreted as <see cref="TimeSpan"/></description></item>
/// <item><term><see cref="Enum"/></term><description>16 or 32 bit enums, also supports flags</description></item>
/// <item><term><see cref="string"/></term><description>String of chars, the interface will automatically get the length</description></item>
/// <item><term><see cref="byte[]"/></term><description>As an array of bytes</description></item>
/// </list>
/// </summary> /// </summary>
/// <typeparam name="T">
/// <include file="../Documentation/docs.xml" path='extradoc/class[@name="support-conv-types"]/*' />
/// </typeparam>
public TempRegister<T> AsType<T> () { public TempRegister<T> AsType<T> () {
if (!typeof(T).IsAllowedPlcCastingType()) { if (!typeof(T).IsAllowedPlcCastingType()) {
@@ -387,9 +398,77 @@ namespace MewtocolNet.RegisterBuilding {
} }
///<inheritdoc cref="AsType{T}()"/> /// <summary>
/// Sets the register as a dotnet <see cref="System"/> type for direct conversion
/// <include file="../Documentation/docs.xml" path='extradoc/class[@name="support-conv-types"]/*' />
/// </summary>
/// <param name="type">
/// <include file="../Documentation/docs.xml" path='extradoc/class[@name="support-conv-types"]/*' />
/// </param>
public TempRegister AsType (Type type) { 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()) { if (!type.IsAllowedPlcCastingType()) {
throw new NotSupportedException($"The dotnet type {type}, is not supported for PLC type casting"); throw new NotSupportedException($"The dotnet type {type}, is not supported for PLC type casting");
@@ -430,7 +509,7 @@ namespace MewtocolNet.RegisterBuilding {
/// <item><term>DWORD</term><description>32 bit double word interpreted as <see cref="uint"/></description></item> /// <item><term>DWORD</term><description>32 bit double word interpreted as <see cref="uint"/></description></item>
/// </list> /// </list>
/// </summary> /// </summary>
public TempRegister AsType(string type) { public TempRegister AsType (string type) {
var stringMatch = Regex.Match(type, @"STRING *\[(?<len>[0-9]*)\]", RegexOptions.IgnoreCase); var stringMatch = Regex.Match(type, @"STRING *\[(?<len>[0-9]*)\]", RegexOptions.IgnoreCase);
var arrayMatch = Regex.Match(type, @"ARRAY *\[(?<S1>[0-9]*)..(?<E1>[0-9]*)(?:\,(?<S2>[0-9]*)..(?<E2>[0-9]*))?(?:\,(?<S3>[0-9]*)..(?<E3>[0-9]*))?\] *OF {1,}(?<t>.*)", RegexOptions.IgnoreCase); var arrayMatch = Regex.Match(type, @"ARRAY *\[(?<S1>[0-9]*)..(?<E1>[0-9]*)(?:\,(?<S2>[0-9]*)..(?<E2>[0-9]*))?(?:\,(?<S3>[0-9]*)..(?<E3>[0-9]*))?\] *OF {1,}(?<t>.*)", RegexOptions.IgnoreCase);
@@ -446,11 +525,46 @@ namespace MewtocolNet.RegisterBuilding {
} else if (arrayMatch.Success) { } else if (arrayMatch.Success) {
throw new NotSupportedException("Arrays are currently not supported"); //invoke generic AsTypeArray
string arrTypeString = arrayMatch.Groups["t"].Value;
if (Enum.TryParse<PlcVarType>(arrTypeString, out var parsedArrType)) {
var dotnetArrType = parsedArrType.GetDefaultDotnetType();
var indices = new List<int>();
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 { } else {
throw new NotSupportedException($"The mewtocol type '{type}' was not recognized"); throw new NotSupportedException($"The FP type '{arrTypeString}' was not recognized");
}
} else {
throw new NotSupportedException($"The FP type '{type}' was not recognized");
} }
@@ -459,38 +573,41 @@ namespace MewtocolNet.RegisterBuilding {
} }
/// <summary> /// <summary>
/// Gets the data DT area as a <see cref="byte[]"/> /// Sets the register as a (multidimensional) array targeting a PLC array
/// </summary> /// </summary>
/// <param name="byteLength">Bytes to assign</param> /// <typeparam name="T">
public TempRegister AsBytes (uint byteLength) { /// <include file="../Documentation/docs.xml" path='extradoc/class[@name="support-conv-types"]/*' />
/// </typeparam>
/// <param name="indicies">
/// Indicies for multi dimensional arrays, for normal arrays just one INT
/// </param>
/// <example>
/// <b>One dimensional arrays:</b><br/>
/// ARRAY [0..2] OF INT = <c>AsTypeArray&lt;short[]&gt;(3)</c><br/>
/// ARRAY [5..6] OF DWORD = <c>AsTypeArray&lt;DWord[]&gt;(2)</c><br/>
/// <br/>
/// <b>Multi dimensional arrays:</b><br/>
/// ARRAY [0..2, 0..3, 0..4] OF INT = <c>AsTypeArray&lt;short[,,]&gt;(3,4,5)</c><br/>
/// ARRAY [5..6, 0..2] OF DWORD = <c>AsTypeArray&lt;DWord[,]&gt;(2, 3)</c><br/>
/// </example>
public TempRegister AsTypeArray<T> (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; if (!elBaseType.IsAllowedPlcCastingType())
Data.dotnetVarType = typeof(byte[]); 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");
} Data.dotnetVarType = typeof(T);
/// <summary>
/// Gets the data DT area as a <see cref="BitArray"/>
/// </summary>
/// <param name="bitCount">Number of bits to read</param>
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);
return new TempRegister(Data, builder); return new TempRegister(Data, builder);

View File

@@ -21,14 +21,14 @@ namespace MewtocolNet.RegisterBuilding {
} }
internal List<BaseRegister> AssembleAll (RBuild rBuildData) { internal List<BaseRegister> AssembleAll (RBuild rBuildData, bool flagAutoGenerated = false) {
List<BaseRegister> generatedInstances = new List<BaseRegister>(); List<BaseRegister> generatedInstances = new List<BaseRegister>();
foreach (var data in rBuildData.unfinishedList) { foreach (var data in rBuildData.unfinishedList) {
var generatedInstance = Assemble(data); var generatedInstance = Assemble(data);
generatedInstance.autoGenerated = flagAutoGenerated;
generatedInstances.Add(generatedInstance); generatedInstances.Add(generatedInstance);
} }
@@ -44,6 +44,13 @@ namespace MewtocolNet.RegisterBuilding {
BaseRegister generatedInstance = null; BaseRegister generatedInstance = null;
if(data.dotnetVarType.IsArray) {
Console.WriteLine();
return new ArrayRegister(0, 0);
}
if (data.dotnetVarType.IsEnum) { if (data.dotnetVarType.IsEnum) {
//------------------------------------------- //-------------------------------------------

View File

@@ -24,6 +24,8 @@ namespace MewtocolNet.Registers {
internal Type underlyingSystemType; internal Type underlyingSystemType;
internal IMemoryArea underlyingMemory; internal IMemoryArea underlyingMemory;
internal bool autoGenerated;
internal object lastValue = null; internal object lastValue = null;
internal string name; internal string name;
internal uint memoryAddress; internal uint memoryAddress;
@@ -128,6 +130,8 @@ namespace MewtocolNet.Registers {
public virtual uint GetRegisterAddressLen() => throw new NotImplementedException(); public virtual uint GetRegisterAddressLen() => throw new NotImplementedException();
public virtual uint GetRegisterAddressEnd() => MemoryAddress + GetRegisterAddressLen() - 1;
public string GetRegisterWordRangeString() => $"{GetMewName()} - {MemoryAddress + GetRegisterAddressLen() - 1}"; public string GetRegisterWordRangeString() => $"{GetMewName()} - {MemoryAddress + GetRegisterAddressLen() - 1}";
#endregion #endregion
@@ -156,6 +160,7 @@ namespace MewtocolNet.Registers {
return this.MemoryAddress == toCompare.MemoryAddress && return this.MemoryAddress == toCompare.MemoryAddress &&
this.RegisterType == toCompare.RegisterType && this.RegisterType == toCompare.RegisterType &&
this.underlyingSystemType == toCompare.underlyingSystemType &&
this.GetRegisterAddressLen() == toCompare.GetRegisterAddressLen() && this.GetRegisterAddressLen() == toCompare.GetRegisterAddressLen() &&
this.GetSpecialAddress() == toCompare.GetSpecialAddress(); this.GetSpecialAddress() == toCompare.GetSpecialAddress();
@@ -199,20 +204,34 @@ namespace MewtocolNet.Registers {
public virtual string Explain () { public virtual string Explain () {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.AppendLine($"MewName: {GetMewName()}"); sb.Append($"Address: {GetRegisterWordRangeString()}\n");
sb.AppendLine($"Name: {Name ?? "Not named"}");
sb.AppendLine($"Value: {GetValueString()}"); if (GetType().IsGenericType)
sb.AppendLine($"Perf. Reads: {successfulReads}, Writes: {successfulWrites}"); sb.Append($"Type: {RegisterType}, NumberRegister<{GetType().GenericTypeArguments[0]}>\n");
sb.AppendLine($"Register Type: {RegisterType}"); else
sb.AppendLine($"Underlying System Type: {underlyingSystemType}"); sb.AppendLine($"Type: {RegisterType}, {GetType().Name}\n");
sb.AppendLine($"Address: {GetRegisterWordRangeString()}");
if (this is StringRegister sr) sb.AppendLine($"Reserved: {sr.ReservedSize}, Used: {sr.UsedSize}"); sb.Append($"Name: {Name ?? "Not named"}\n");
if (GetSpecialAddress() != null) sb.AppendLine($"SPAddress: {GetSpecialAddress():X1}");
if (GetType().IsGenericType) sb.AppendLine($"Type: NumberRegister<{GetType().GenericTypeArguments[0]}>"); if(Value != null)
else sb.AppendLine($"Type: {GetType()}"); sb.Append($"Value: {GetValueString()}\n");
if (containedCollection != null) sb.AppendLine($"In collection: {containedCollection.GetType()}");
if (boundProperties != null && boundProperties.Count > 0) sb.AppendLine($"Bound props: {string.Join(", ", boundProperties)}"); sb.Append($"Reads: {successfulReads}, Writes: {successfulWrites}\n");
else sb.AppendLine("No bound properties"); 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(); return sb.ToString();

View File

@@ -154,6 +154,12 @@ namespace MewtocolNet.Registers {
var res = await attachedInterface.ReadByteRangeNonBlocking((int)MemoryAddress, (int)GetRegisterAddressLen() * 2, false); var res = await attachedInterface.ReadByteRangeNonBlocking((int)MemoryAddress, (int)GetRegisterAddressLen() * 2, false);
if (res == null) return null; 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); return SetValueFromBytes(res);
} }

View File

@@ -17,9 +17,10 @@ namespace MewtocolNet.UnderlyingRegisters {
internal byte[] underlyingBytes = new byte[2]; internal byte[] underlyingBytes = new byte[2];
internal List<BaseRegister> linkedRegisters = new List<BaseRegister>(); /// <summary>
/// List of register link groups that are managed in this memory area
internal Dictionary<BaseRegister, List<BaseRegister>> crossRegisterBindings = new Dictionary<BaseRegister, List<BaseRegister>>(); /// </summary>
internal List<LinkedRegisterGroup> managedRegisters = new List<LinkedRegisterGroup>();
public ulong AddressStart => addressStart; public ulong AddressStart => addressStart;
public ulong AddressEnd => addressEnd; public ulong AddressEnd => addressEnd;
@@ -51,7 +52,7 @@ namespace MewtocolNet.UnderlyingRegisters {
public void UpdateAreaRegisterValues() { public void UpdateAreaRegisterValues() {
foreach (var register in this.linkedRegisters) { foreach (var register in this.managedRegisters.SelectMany(x => x.Linked)) {
var regStart = register.MemoryAddress; var regStart = register.MemoryAddress;
var addLen = (int)register.GetRegisterAddressLen(); var addLen = (int)register.GetRegisterAddressLen();
@@ -120,7 +121,8 @@ namespace MewtocolNet.UnderlyingRegisters {
private async Task CheckDynamicallySizedRegistersAsync () { private async Task CheckDynamicallySizedRegistersAsync () {
//calibrating at runtime sized registers //calibrating at runtime sized registers
var uncalibratedStringRegisters = linkedRegisters var uncalibratedStringRegisters = managedRegisters
.SelectMany(x => x.Linked)
.Where(x => x is StringRegister sreg && !sreg.isCalibratedFromPlc) .Where(x => x is StringRegister sreg && !sreg.isCalibratedFromPlc)
.Cast<StringRegister>() .Cast<StringRegister>()
.ToList(); .ToList();
@@ -129,7 +131,7 @@ namespace MewtocolNet.UnderlyingRegisters {
await register.CalibrateFromPLC(); await register.CalibrateFromPLC();
if (uncalibratedStringRegisters.Count > 0) if (uncalibratedStringRegisters.Count > 0)
mewInterface.memoryManager.MergeAndSizeDataAreas(); mewInterface.memoryManager.LinkAndMergeRegisters();
} }

View File

@@ -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<BaseRegister> Linked = new List<BaseRegister>();
}
}

View File

@@ -47,12 +47,12 @@ namespace MewtocolNet.UnderlyingRegisters {
} }
internal void LinkRegisters (List<BaseRegister> registers = null) { internal void LinkAndMergeRegisters (List<BaseRegister> registers = null) {
//for self calling //for self calling
if (registers == null) registers = GetAllRegisters().ToList(); if (registers == null) registers = GetAllRegisters().ToList();
//pre combine //pre combine per address
var groupedByAdd = registers var groupedByAdd = registers
.GroupBy(x => new { .GroupBy(x => new {
x.MemoryAddress, x.MemoryAddress,
@@ -60,39 +60,20 @@ namespace MewtocolNet.UnderlyingRegisters {
spadd = x.GetSpecialAddress(), spadd = x.GetSpecialAddress(),
}); });
var filteredRegisters = new List<BaseRegister>(); //poll level merging
var propertyLookupTable = new Dictionary<PropertyInfo, BaseRegister>();
foreach (var addressGroup in groupedByAdd) { foreach (var addressGroup in groupedByAdd) {
var ordered = addressGroup.OrderBy(x => x.pollLevel); //determine highest poll level for same addresses
var highestPollLevel = ordered.Max(x => x.pollLevel); var highestPollLevel = addressGroup.Max(x => x.pollLevel);
var distinctByUnderlyingType = //apply poll level to all registers in same group
ordered.GroupBy(x => x.underlyingSystemType).ToList(); foreach (var reg in addressGroup)
reg.pollLevel = highestPollLevel;
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);
}
} }
} //insert into area
foreach (var reg in registers) {
}
foreach (var reg in filteredRegisters) {
TestPollLevelExistence(reg); 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) { private void TestPollLevelExistence (BaseRegister reg) {
@@ -271,17 +264,27 @@ namespace MewtocolNet.UnderlyingRegisters {
insertReg.name = $"auto_{Guid.NewGuid().ToString("N")}"; insertReg.name = $"auto_{Guid.NewGuid().ToString("N")}";
} }
Console.WriteLine($"Adding linked register: {insertReg}"); var existinglinkedGroup = targetArea.managedRegisters
targetArea.linkedRegisters.Add(insertReg); .FirstOrDefault(x => x.AddressStart == insertReg.MemoryAddress &&
return; 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 var dupedTypeReg = existinglinkedGroup.Linked.FirstOrDefault(x => x.IsSameAddressAndType(insertReg));
if (dupedTypeReg != null) {
LinkRegisters(); dupedTypeReg.WithBoundProperties(insertReg.boundProperties);
} else {
existinglinkedGroup.Linked.Add(insertReg);
}
} }
@@ -350,75 +353,67 @@ namespace MewtocolNet.UnderlyingRegisters {
foreach (var pollLevel in pollLevels) { foreach (var pollLevel in pollLevels) {
sb.AppendLine($"==== Poll lvl {pollLevel.level} ===="); sb.AppendLine($"\n> ==== Poll lvl {pollLevel.level} ====");
sb.AppendLine(); sb.AppendLine();
if (pollLevelConfigs[pollLevel.level].delay != null) { 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 { } 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($"> Level read time: {pollLevel.lastReadTimeMs}ms");
sb.AppendLine($"Optimization distance: {maxOptimizationDistance}"); sb.AppendLine($"> Optimization distance: {maxOptimizationDistance}");
sb.AppendLine(); sb.AppendLine();
sb.AppendLine($"---- DT Areas: ----");
foreach (var area in pollLevel.dataAreas) { 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();
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) { 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.internalRelayAreas.SelectMany(x => x.linkedRegisters));
registers.AddRange(lvl.externalRelayInAreas.SelectMany(x => x.linkedRegisters)); registers.AddRange(lvl.externalRelayInAreas.SelectMany(x => x.linkedRegisters));
registers.AddRange(lvl.externalRelayOutAreas.SelectMany(x => x.linkedRegisters)); registers.AddRange(lvl.externalRelayOutAreas.SelectMany(x => x.linkedRegisters));