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
This commit is contained in:
Felix Weiß
2023-07-13 00:34:06 +02:00
parent 82821e773d
commit daecd73a6d
14 changed files with 595 additions and 426 deletions

View File

@@ -175,7 +175,7 @@ namespace MewtocolNet {
/// <inheritdoc/> /// <inheritdoc/>
public async Task DisconnectAsync () { public async Task DisconnectAsync () {
await pollCycleTask; if(pollCycleTask != null) await pollCycleTask;
Disconnect(); Disconnect();
@@ -186,7 +186,7 @@ namespace MewtocolNet {
if (!IsConnected) return; if (!IsConnected) return;
if (!pollCycleTask.IsCompleted) pollCycleTask.Wait(); if (pollCycleTask != null && !pollCycleTask.IsCompleted) pollCycleTask.Wait();
OnMajorSocketExceptionWhileConnected(); OnMajorSocketExceptionWhileConnected();

View File

@@ -255,7 +255,7 @@ namespace MewtocolNet {
} }
var assembler = new RegisterAssembler(this); var assembler = new RegisterAssembler(this);
var registers = assembler.Assemble(regBuild); var registers = assembler.AssembleAll(regBuild);
AddRegisters(registers.ToArray()); AddRegisters(registers.ToArray());
} }

View File

@@ -94,9 +94,16 @@ namespace MewtocolNet {
/// /// <param name="start">start address of the array</param> /// /// <param name="start">start address of the array</param>
/// <param name="byteArr"></param> /// <param name="byteArr"></param>
/// <returns></returns> /// <returns></returns>
public async Task<bool> WriteByteRange (int start, byte[] byteArr) { public async Task<bool> 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; var wordLength = byteArr.Length / 2;
if (byteArr.Length % 2 != 0) if (byteArr.Length % 2 != 0)
wordLength++; wordLength++;
@@ -120,7 +127,7 @@ namespace MewtocolNet {
/// <param name="flipBytes">Flips bytes from big to mixed endian</param> /// <param name="flipBytes">Flips bytes from big to mixed endian</param>
/// <param name="onProgress">Gets invoked when the progress changes, contains the progress as a double</param> /// <param name="onProgress">Gets invoked when the progress changes, contains the progress as a double</param>
/// <returns>A byte array or null of there was an error</returns> /// <returns>A byte array or null of there was an error</returns>
public async Task<byte[]> ReadByteRangeNonBlocking (int start, int count, bool flipBytes = true, Action<double> onProgress = null) { public async Task<byte[]> ReadByteRangeNonBlocking (int start, int count, bool flipBytes = false, Action<double> onProgress = null) {
var byteList = new List<byte>(); var byteList = new List<byte>();
@@ -167,99 +174,6 @@ namespace MewtocolNet {
#endregion #endregion
#region Raw register reading / writing
[Obsolete]
internal async Task<byte[]> 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<bool> 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 #region Helpers
internal string GetStationNumber() { internal string GetStationNumber() {

View File

@@ -1,6 +1,4 @@
using MewtocolNet.Exceptions; using MewtocolNet.RegisterAttributes;
using MewtocolNet.RegisterAttributes;
using MewtocolNet.Registers;
using MewtocolNet.UnderlyingRegisters; using MewtocolNet.UnderlyingRegisters;
using System; using System;
using System.Collections; using System.Collections;
@@ -10,7 +8,6 @@ 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;
@@ -76,6 +73,7 @@ namespace MewtocolNet.RegisterBuilding {
//optional //optional
internal uint? byteSize; internal uint? byteSize;
internal uint? bitSize; internal uint? bitSize;
internal int? stringSize;
internal int pollLevel = 1; internal int pollLevel = 1;
@@ -88,12 +86,15 @@ namespace MewtocolNet.RegisterBuilding {
public SBase() { } public SBase() { }
internal SBase(SData data) { internal SBase(SData data, RBuild bldr) {
Data = data; Data = data;
builder = bldr;
} }
internal SData Data { get; set; } internal SData Data { get; set; }
internal RBuild builder;
} }
internal struct ParseResult { internal struct ParseResult {
@@ -311,6 +312,13 @@ namespace MewtocolNet.RegisterBuilding {
} }
/// <summary>
/// Starts the register builder for a new mewtocol address <br/>
/// Examples:
/// <code>Address("DT100") | Address("R10A") | Address("DDT50", "MyRegisterName")</code>
/// </summary>
/// <param name="plcAddrName">Address name formatted as FP-Address like in FP-Winpro</param>
/// <param name="name">Custom name for the register to referr to it later</param>
public SAddress Address (string plcAddrName, string name = null) { public SAddress Address (string plcAddrName, string name = null) {
foreach (var method in parseMethods) { foreach (var method in parseMethods) {
@@ -326,7 +334,8 @@ namespace MewtocolNet.RegisterBuilding {
unfinishedList.Add(res.stepData); unfinishedList.Add(res.stepData);
return new SAddress { return new SAddress {
Data = res.stepData Data = res.stepData,
builder = this,
}; };
} else if(res.state == ParseResultState.FailedHard) { } else if(res.state == ParseResultState.FailedHard) {
@@ -347,6 +356,22 @@ namespace MewtocolNet.RegisterBuilding {
public class SAddress : SBase { public class SAddress : SBase {
/// <summary>
/// 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="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="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</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="BitArray"/></term><description>As an array of bits</description></item>
/// <item><term><see cref="byte[]"/></term><description>As an array of bytes</description></item>
/// </list>
/// </summary>
public TempRegister<T> AsType<T> () { public TempRegister<T> AsType<T> () {
if (!typeof(T).IsAllowedPlcCastingType()) { if (!typeof(T).IsAllowedPlcCastingType()) {
@@ -357,10 +382,11 @@ namespace MewtocolNet.RegisterBuilding {
Data.dotnetVarType = typeof(T); Data.dotnetVarType = typeof(T);
return new TempRegister<T>(Data); return new TempRegister<T>(Data, builder);
} }
///<inheritdoc cref="AsType{T}()"/>
public TempRegister AsType (Type type) { public TempRegister AsType (Type type) {
if (!type.IsAllowedPlcCastingType()) { if (!type.IsAllowedPlcCastingType()) {
@@ -371,10 +397,70 @@ namespace MewtocolNet.RegisterBuilding {
Data.dotnetVarType = type; Data.dotnetVarType = type;
return new TempRegister(Data); return new TempRegister(Data, builder);
} }
/// <summary>
/// Sets the register type as a predefined <see cref="PlcVarType"/>
/// </summary>
public TempRegister AsType (PlcVarType type) {
Data.dotnetVarType = type.GetDefaultDotnetType();
return new TempRegister(Data, builder);
}
/// <summary>
/// Sets the register type from the plc type string <br/>
/// <c>Supported types:</c>
/// <list type="bullet">
/// <item><term>BOOL</term><description>Boolean R/X/Y registers</description></item>
/// <item><term>INT</term><description>16 bit signed integer</description></item>
/// <item><term>UINT</term><description>16 bit un-signed integer</description></item>
/// <item><term>DINT</term><description>32 bit signed integer</description></item>
/// <item><term>UDINT</term><description>32 bit un-signed integer</description></item>
/// <item><term>REAL</term><description>32 bit floating point</description></item>
/// <item><term>TIME</term><description>32 bit time interpreted as <see cref="TimeSpan"/></description></item>
/// <item><term>STRING</term><description>String of chars, the interface will automatically get the length</description></item>
/// <item><term>STRING[N]</term><description>String of chars, pre capped to N</description></item>
/// <item><term>WORD</term><description>16 bit word interpreted as <see cref="ushort"/></description></item>
/// <item><term>DWORD</term><description>32 bit double word interpreted as <see cref="uint"/></description></item>
/// </list>
/// </summary>
public TempRegister AsType(string type) {
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);
if (Enum.TryParse<PlcVarType>(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);
}
/// <summary>
/// Gets the data DT area as a <see cref="byte[]"/>
/// </summary>
/// <param name="byteLength">Bytes to assign</param>
public TempRegister AsBytes (uint byteLength) { public TempRegister AsBytes (uint byteLength) {
if (Data.regType != RegisterType.DT) { if (Data.regType != RegisterType.DT) {
@@ -386,10 +472,14 @@ namespace MewtocolNet.RegisterBuilding {
Data.byteSize = byteLength; Data.byteSize = byteLength;
Data.dotnetVarType = typeof(byte[]); Data.dotnetVarType = typeof(byte[]);
return new TempRegister(Data); return new TempRegister(Data, builder);
} }
/// <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) { public TempRegister AsBits (ushort bitCount = 16) {
if (Data.regType != RegisterType.DT) { if (Data.regType != RegisterType.DT) {
@@ -401,10 +491,13 @@ namespace MewtocolNet.RegisterBuilding {
Data.bitSize = bitCount; Data.bitSize = bitCount;
Data.dotnetVarType = typeof(BitArray); Data.dotnetVarType = typeof(BitArray);
return new TempRegister(Data); return new TempRegister(Data, builder);
} }
/// <summary>
/// Automatically finds the best type for the register
/// </summary>
public TempRegister AutoType() { public TempRegister AutoType() {
switch (Data.regType) { switch (Data.regType) {
@@ -424,7 +517,7 @@ namespace MewtocolNet.RegisterBuilding {
break; break;
} }
return new TempRegister(Data); return new TempRegister(Data, builder);
} }
@@ -436,8 +529,11 @@ namespace MewtocolNet.RegisterBuilding {
public class TempRegister<T> : SBase { public class TempRegister<T> : SBase {
internal TempRegister(SData data) : base(data) {} internal TempRegister(SData data, RBuild bldr) : base(data, bldr) {}
/// <summary>
/// Sets the poll level of the register
/// </summary>
public TempRegister<T> PollLevel (int level) { public TempRegister<T> PollLevel (int level) {
Data.pollLevel = level; Data.pollLevel = level;
@@ -446,14 +542,28 @@ namespace MewtocolNet.RegisterBuilding {
} }
public async Task WriteToAsync (T value) => throw new NotImplementedException(); /// <summary>
/// Writes data to the register and bypasses the memory manager <br/>
/// </summary>
/// <param name="value">The value to write</param>
/// <returns>True if success</returns>
public async Task<bool> WriteToAsync (T value) => await builder.WriteAnonymousAsync(this, value);
/// <summary>
/// Reads data from the register and bypasses the memory manager <br/>
/// </summary>
/// <returns>The value read or null if failed</returns>
public async Task<T> ReadFromAsync () => await builder.ReadAnonymousAsync(this);
} }
public class TempRegister : SBase { public class TempRegister : SBase {
internal TempRegister(SData data) : base(data) { } internal TempRegister(SData data, RBuild bldr) : base(data, bldr) { }
/// <summary>
/// Sets the poll level of the register
/// </summary>
public TempRegister PollLevel (int level) { public TempRegister PollLevel (int level) {
Data.pollLevel = level; Data.pollLevel = level;
@@ -462,6 +572,19 @@ namespace MewtocolNet.RegisterBuilding {
} }
/// <summary>
/// Writes data to the register and bypasses the memory manager <br/>
/// </summary>
/// <param name="value">The value to write</param>
/// <returns>True if success</returns>
public async Task<bool> WriteToAsync(object value) => await builder.WriteAnonymousAsync(this, value);
/// <summary>
/// Reads data from the register and bypasses the memory manager <br/>
/// </summary>
/// <returns>The value read or null if failed</returns>
public async Task<object> ReadFromAsync () => await builder.ReadAnonymousAsync(this);
internal TempRegister RegCollection(RegisterCollection col) { internal TempRegister RegCollection(RegisterCollection col) {
Data.regCollection = col; Data.regCollection = col;
@@ -478,7 +601,41 @@ namespace MewtocolNet.RegisterBuilding {
} }
public async Task WriteToAsync (object value) => throw new NotImplementedException(); }
#endregion
#region Anonymous read/write bindings
private async Task<bool> WriteAnonymousAsync (TempRegister reg, object value) {
var assembler = new RegisterAssembler(attachedPLC);
var tempRegister = assembler.Assemble(reg.Data);
return await tempRegister.WriteToAnonymousAsync(value);
}
private async Task<bool> WriteAnonymousAsync<T>(TempRegister<T> reg, object value) {
var assembler = new RegisterAssembler(attachedPLC);
var tempRegister = assembler.Assemble(reg.Data);
return await tempRegister.WriteToAnonymousAsync(value);
}
private async Task<object> ReadAnonymousAsync (TempRegister reg) {
var assembler = new RegisterAssembler(attachedPLC);
var tempRegister = assembler.Assemble(reg.Data);
return await tempRegister.ReadFromAnonymousAsync();
}
private async Task<T> ReadAnonymousAsync<T>(TempRegister<T> reg) {
var assembler = new RegisterAssembler(attachedPLC);
var tempRegister = assembler.Assemble(reg.Data);
return (T)await tempRegister.ReadFromAnonymousAsync();
} }
@@ -486,124 +643,4 @@ namespace MewtocolNet.RegisterBuilding {
} }
internal class RegisterAssembler {
internal RegisterCollection collectionTarget;
internal MewtocolInterface onInterface;
internal RegisterAssembler (MewtocolInterface interf) {
onInterface = interf;
}
internal List<BaseRegister> Assemble (RBuild rBuildData) {
List<BaseRegister> generatedInstances = new List<BaseRegister>();
foreach (var data in rBuildData.unfinishedList) {
//parse all others where the type is known
Type registerClassType = data.dotnetVarType.GetDefaultRegisterHoldingType();
BaseRegister generatedInstance = null;
if (data.dotnetVarType.IsEnum) {
//-------------------------------------------
//as numeric register with enum target
var underlying = Enum.GetUnderlyingType(data.dotnetVarType);
var enuSize = Marshal.SizeOf(underlying);
if (enuSize > 4)
throw new NotSupportedException("Enums not based on 16 or 32 bit numbers are not supported");
Type myParameterizedSomeClass = typeof(NumberRegister<>).MakeGenericType(data.dotnetVarType);
ConstructorInfo constr = myParameterizedSomeClass.GetConstructor(new Type[] { typeof(uint), typeof(string) });
var parameters = new object[] { data.memAddress, data.name };
var instance = (BaseRegister)constr.Invoke(parameters);
generatedInstance = instance;
} else if (registerClassType.IsGenericType) {
//-------------------------------------------
//as numeric register
//create a new bregister instance
var flags = BindingFlags.Public | BindingFlags.Instance;
//int _adress, Type _enumType = null, string _name = null
var parameters = new object[] { data.memAddress, data.name };
var instance = (BaseRegister)Activator.CreateInstance(registerClassType, flags, null, parameters, null);
instance.pollLevel = data.pollLevel;
generatedInstance = instance;
} else if (registerClassType == typeof(BytesRegister) && data.byteSize != null) {
//-------------------------------------------
//as byte range register
BytesRegister instance = new BytesRegister(data.memAddress, (uint)data.byteSize, data.name);
generatedInstance = instance;
} else if (registerClassType == typeof(BytesRegister) && data.bitSize != null) {
//-------------------------------------------
//as bit range register
BytesRegister instance = new BytesRegister(data.memAddress, (ushort)data.bitSize, data.name);
generatedInstance = instance;
} else if (registerClassType == typeof(StringRegister)) {
//-------------------------------------------
//as byte range register
var instance = (BaseRegister)new StringRegister(data.memAddress, data.name);
generatedInstance = instance;
} else if (data.regType.IsBoolean()) {
//-------------------------------------------
//as boolean register
var io = (IOType)(int)data.regType;
var spAddr = data.specialAddress;
var areaAddr = data.memAddress;
var instance = new BoolRegister(io, spAddr, areaAddr, data.name);
generatedInstance = instance;
}
//finalize set for every
if(generatedInstance == null)
throw new MewtocolException("Failed to build register");
if (collectionTarget != null)
generatedInstance.WithRegisterCollection(collectionTarget);
generatedInstance.attachedInterface = onInterface;
generatedInstance.pollLevel = data.pollLevel;
generatedInstances.Add(generatedInstance);
}
return generatedInstances;
}
}
} }

View File

@@ -12,7 +12,7 @@ namespace MewtocolNet.RegisterBuilding {
builder.Invoke(regBuilder); builder.Invoke(regBuilder);
var assembler = new RegisterAssembler((MewtocolInterface)plc); var assembler = new RegisterAssembler((MewtocolInterface)plc);
var registers = assembler.Assemble(regBuilder); var registers = assembler.AssembleAll(regBuilder);
var interf = (MewtocolInterface)plc; var interf = (MewtocolInterface)plc;

View File

@@ -0,0 +1,138 @@
using MewtocolNet.Exceptions;
using MewtocolNet.RegisterAttributes;
using MewtocolNet.Registers;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.InteropServices;
using static MewtocolNet.RegisterBuilding.RBuild;
namespace MewtocolNet.RegisterBuilding {
internal class RegisterAssembler {
internal RegisterCollection collectionTarget;
internal MewtocolInterface onInterface;
internal RegisterAssembler (MewtocolInterface interf) {
onInterface = interf;
}
internal List<BaseRegister> AssembleAll (RBuild rBuildData) {
List<BaseRegister> generatedInstances = new List<BaseRegister>();
foreach (var data in rBuildData.unfinishedList) {
var generatedInstance = Assemble(data);
generatedInstances.Add(generatedInstance);
}
return generatedInstances;
}
internal BaseRegister Assemble (SData data) {
//parse all others where the type is known
Type registerClassType = data.dotnetVarType.GetDefaultRegisterHoldingType();
BaseRegister generatedInstance = null;
if (data.dotnetVarType.IsEnum) {
//-------------------------------------------
//as numeric register with enum target
var underlying = Enum.GetUnderlyingType(data.dotnetVarType);
var enuSize = Marshal.SizeOf(underlying);
if (enuSize > 4)
throw new NotSupportedException("Enums not based on 16 or 32 bit numbers are not supported");
var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
Type paramedClass = typeof(NumberRegister<>).MakeGenericType(data.dotnetVarType);
ConstructorInfo constr = paramedClass.GetConstructor(flags, null, new Type[] { typeof(uint), typeof(string) }, null);
var parameters = new object[] { data.memAddress, data.name };
var instance = (BaseRegister)constr.Invoke(parameters);
generatedInstance = instance;
} else if (registerClassType.IsGenericType) {
//-------------------------------------------
//as numeric register
//create a new bregister instance
var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
//int _adress, Type _enumType = null, string _name = null
var parameters = new object[] { data.memAddress, data.name };
var instance = (BaseRegister)Activator.CreateInstance(registerClassType, flags, null, parameters, null);
instance.pollLevel = data.pollLevel;
generatedInstance = instance;
} else if (registerClassType == typeof(BytesRegister) && data.byteSize != null) {
//-------------------------------------------
//as byte range register
BytesRegister instance = new BytesRegister(data.memAddress, (uint)data.byteSize, data.name);
generatedInstance = instance;
} else if (registerClassType == typeof(BytesRegister) && data.bitSize != null) {
//-------------------------------------------
//as bit range register
BytesRegister instance = new BytesRegister(data.memAddress, (ushort)data.bitSize, data.name);
generatedInstance = instance;
} else if (registerClassType == typeof(StringRegister)) {
//-------------------------------------------
//as byte range register
var instance = (BaseRegister)new StringRegister(data.memAddress, data.name) {
ReservedSize = (short)(data.stringSize ?? 0),
};
generatedInstance = instance;
} else if (data.regType.IsBoolean()) {
//-------------------------------------------
//as boolean register
var io = (IOType)(int)data.regType;
var spAddr = data.specialAddress;
var areaAddr = data.memAddress;
var instance = new BoolRegister(io, spAddr, areaAddr, data.name);
generatedInstance = instance;
}
//finalize set for every
if (generatedInstance == null)
throw new MewtocolException("Failed to build register");
if (collectionTarget != null)
generatedInstance.WithRegisterCollection(collectionTarget);
generatedInstance.attachedInterface = onInterface;
generatedInstance.pollLevel = data.pollLevel;
return generatedInstance;
}
}
}

View File

@@ -169,6 +169,7 @@ namespace MewtocolNet.Registers {
sb.AppendLine($"Perf. Reads: {successfulReads}, Writes: {successfulWrites}"); sb.AppendLine($"Perf. Reads: {successfulReads}, Writes: {successfulWrites}");
sb.AppendLine($"Register Type: {RegisterType}"); sb.AppendLine($"Register Type: {RegisterType}");
sb.AppendLine($"Address: {GetRegisterWordRangeString()}"); 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 (GetSpecialAddress() != null) sb.AppendLine($"SPAddress: {GetSpecialAddress():X1}");
if (GetType().IsGenericType) sb.AppendLine($"Type: NumberRegister<{GetType().GenericTypeArguments[0]}>"); if (GetType().IsGenericType) sb.AppendLine($"Type: NumberRegister<{GetType().GenericTypeArguments[0]}>");
else sb.AppendLine($"Type: {GetType()}"); else sb.AppendLine($"Type: {GetType()}");

View File

@@ -1,8 +1,11 @@
using MewtocolNet.UnderlyingRegisters; using MewtocolNet.Exceptions;
using MewtocolNet.RegisterBuilding;
using MewtocolNet.UnderlyingRegisters;
using System; using System;
using System.ComponentModel; using System.ComponentModel;
using System.Net; using System.Net;
using System.Text; using System.Text;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace MewtocolNet.Registers { namespace MewtocolNet.Registers {
@@ -18,16 +21,11 @@ namespace MewtocolNet.Registers {
/// </summary> /// </summary>
public byte SpecialAddress => specialAddress; public byte SpecialAddress => specialAddress;
/// <summary> [Obsolete("Creating registers directly is not supported use IPlc.Register instead")]
/// Creates a new boolean register public BoolRegister() =>
/// </summary> throw new NotSupportedException("Direct register instancing is not supported, use the builder pattern");
/// <param name="_io">The io type prefix</param>
/// <param name="_spAddress">The special address</param> internal BoolRegister(IOType _io, byte _spAddress = 0x0, uint _areaAdress = 0, string _name = null) {
/// <param name="_areaAdress">The area special address</param>
/// <param name="_name">The custom name</param>
/// <exception cref="NotSupportedException"></exception>
/// <exception cref="Exception"></exception>
public BoolRegister(IOType _io, byte _spAddress = 0x0, uint _areaAdress = 0, string _name = null) {
lastValue = null; lastValue = null;
@@ -50,7 +48,7 @@ namespace MewtocolNet.Registers {
throw new NotSupportedException("XY area addresses cant be greater than 110"); throw new NotSupportedException("XY area addresses cant be greater than 110");
if (specialAddress > 0xF) if (specialAddress > 0xF)
throw new NotSupportedException("Special address cant be greater 15 or 0xF"); throw new NotSupportedException("Special address cant be greater than 15 or 0xF");
base.CheckAddressOverflow(addressStart, addressLen); base.CheckAddressOverflow(addressStart, addressLen);
@@ -61,31 +59,47 @@ namespace MewtocolNet.Registers {
/// <inheritdoc/> /// <inheritdoc/>
public override async Task<object> ReadAsync() { public override async Task<object> ReadAsync() {
if (!attachedInterface.IsConnected) return null; if (!attachedInterface.IsConnected)
throw MewtocolException.NotConnectedSend();
var read = await attachedInterface.ReadRawRegisterAsync(this); return null;
if(read == null) return null;
var parsed = PlcValueParser.Parse<bool>(this, read);
SetValueFromPLC(parsed);
return parsed;
} }
/// <inheritdoc/> /// <inheritdoc/>
public override async Task<bool> WriteAsync(object data) { public override async Task<bool> WriteAsync(object data) {
if (!attachedInterface.IsConnected) return false; if (!attachedInterface.IsConnected)
throw MewtocolException.NotConnectedSend();
var encoded = PlcValueParser.Encode(this, (bool)data); return false;
var res = await attachedInterface.WriteRawRegisterAsync(this, encoded);
if (res) {
SetValueFromPLC(data);
} }
return res; internal override async Task<bool> WriteToAnonymousAsync (object value) {
if (!attachedInterface.IsConnected)
throw MewtocolException.NotConnectedSend();
var station = attachedInterface.GetStationNumber();
string reqStr = $"%{station}#WCS{BuildMewtocolQuery()}{((bool)value ? "1" : "0")}";
var res = await attachedInterface.SendCommandAsync(reqStr);
return res.Success;
}
internal override async Task<object> ReadFromAnonymousAsync() {
if (!attachedInterface.IsConnected)
throw MewtocolException.NotConnectedSend();
var station = attachedInterface.GetStationNumber();
string requeststring = $"%{station}#RCS{BuildMewtocolQuery()}";
var result = await attachedInterface.SendCommandAsync(requeststring);
if (!result.Success) return null;
return result.Response.ParseRCSingleBit();
} }

View File

@@ -1,4 +1,5 @@
using System; using MewtocolNet.Exceptions;
using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
@@ -23,10 +24,11 @@ namespace MewtocolNet.Registers {
internal ushort? ReservedBitSize { get; set; } internal ushort? ReservedBitSize { get; set; }
/// <summary> [Obsolete("Creating registers directly is not supported use IPlc.Register instead")]
/// Defines a register containing bytes public BytesRegister() =>
/// </summary> throw new NotSupportedException("Direct register instancing is not supported, use the builder pattern");
public BytesRegister(uint _address, uint _reservedByteSize, string _name = null) {
internal BytesRegister(uint _address, uint _reservedByteSize, string _name = null) {
name = _name; name = _name;
memoryAddress = _address; memoryAddress = _address;
@@ -123,7 +125,8 @@ namespace MewtocolNet.Registers {
/// <inheritdoc/> /// <inheritdoc/>
public override async Task<object> ReadAsync() { public override async Task<object> ReadAsync() {
if (!attachedInterface.IsConnected) return null; if (!attachedInterface.IsConnected)
throw MewtocolException.NotConnectedSend();
var res = await underlyingMemory.ReadRegisterAsync(this); var res = await underlyingMemory.ReadRegisterAsync(this);
if (!res) return null; if (!res) return null;
@@ -134,26 +137,11 @@ namespace MewtocolNet.Registers {
} }
internal override object SetValueFromBytes(byte[] bytes) {
AddSuccessRead();
object parsed;
if (ReservedBitSize != null) {
parsed = PlcValueParser.Parse<BitArray>(this, bytes);
} else {
parsed = PlcValueParser.Parse<byte[]>(this, bytes);
}
SetValueFromPLC(parsed);
return parsed;
}
/// <inheritdoc/> /// <inheritdoc/>
public override async Task<bool> WriteAsync(object data) { public override async Task<bool> WriteAsync(object data) {
if (!attachedInterface.IsConnected) return false; if (!attachedInterface.IsConnected)
throw MewtocolException.NotConnectedSend();
byte[] encoded; byte[] encoded;
@@ -173,6 +161,43 @@ namespace MewtocolNet.Registers {
} }
internal override object SetValueFromBytes(byte[] bytes) {
AddSuccessRead();
object parsed;
if (ReservedBitSize != null) {
parsed = PlcValueParser.Parse<BitArray>(this, bytes);
} else {
parsed = PlcValueParser.Parse<byte[]>(this, bytes);
}
SetValueFromPLC(parsed);
return parsed;
}
internal override async Task<bool> WriteToAnonymousAsync(object value) {
if (!attachedInterface.IsConnected)
throw MewtocolException.NotConnectedSend();
return await attachedInterface.WriteByteRange((int)MemoryAddress, (byte[])value);
}
internal override async Task<object> ReadFromAnonymousAsync() {
if (!attachedInterface.IsConnected)
throw MewtocolException.NotConnectedSend();
var res = await attachedInterface.ReadByteRangeNonBlocking((int)MemoryAddress, (int)GetRegisterAddressLen() * 2, false);
if (res == null) return null;
return res;
}
} }
} }

View File

@@ -17,12 +17,11 @@ namespace MewtocolNet.Registers {
/// <typeparam name="T">The type of the numeric value</typeparam> /// <typeparam name="T">The type of the numeric value</typeparam>
public class NumberRegister<T> : BaseRegister { public class NumberRegister<T> : BaseRegister {
/// <summary> [Obsolete("Creating registers directly is not supported use IPlc.Register instead")]
/// Defines a register containing a number public NumberRegister() =>
/// </summary> throw new NotSupportedException("Direct register instancing is not supported, use the builder pattern");
/// <param name="_address">Memory start adress max 99999</param>
/// <param name="_name">Name of the register</param> internal NumberRegister (uint _address, string _name = null) {
public NumberRegister (uint _address, string _name = null) {
memoryAddress = _address; memoryAddress = _address;
name = _name; name = _name;
@@ -130,7 +129,8 @@ namespace MewtocolNet.Registers {
/// <inheritdoc/> /// <inheritdoc/>
public override async Task<object> ReadAsync() { public override async Task<object> ReadAsync() {
if (!attachedInterface.IsConnected) return null; if (!attachedInterface.IsConnected)
throw MewtocolException.NotConnectedSend();
var res = await underlyingMemory.ReadRegisterAsync(this); var res = await underlyingMemory.ReadRegisterAsync(this);
if (!res) return null; if (!res) return null;
@@ -141,20 +141,11 @@ namespace MewtocolNet.Registers {
} }
internal override object SetValueFromBytes(byte[] bytes) {
AddSuccessRead();
var parsed = PlcValueParser.Parse<T>(this, bytes);
SetValueFromPLC(parsed);
return parsed;
}
/// <inheritdoc/> /// <inheritdoc/>
public override async Task<bool> WriteAsync(object data) { public override async Task<bool> WriteAsync(object data) {
if (!attachedInterface.IsConnected) return false; if (!attachedInterface.IsConnected)
throw MewtocolException.NotConnectedSend();
var encoded = PlcValueParser.Encode(this, (T)data); var encoded = PlcValueParser.Encode(this, (T)data);
var res = await underlyingMemory.WriteRegisterAsync(this, encoded); var res = await underlyingMemory.WriteRegisterAsync(this, encoded);
@@ -168,6 +159,16 @@ namespace MewtocolNet.Registers {
} }
internal override object SetValueFromBytes(byte[] bytes) {
AddSuccessRead();
var parsed = PlcValueParser.Parse<T>(this, bytes);
SetValueFromPLC(parsed);
return parsed;
}
internal override async Task<bool> WriteToAnonymousAsync (object value) { internal override async Task<bool> WriteToAnonymousAsync (object value) {
if (!attachedInterface.IsConnected) if (!attachedInterface.IsConnected)
@@ -183,7 +184,7 @@ namespace MewtocolNet.Registers {
if (!attachedInterface.IsConnected) if (!attachedInterface.IsConnected)
throw MewtocolException.NotConnectedSend(); throw MewtocolException.NotConnectedSend();
var res = await attachedInterface.ReadByteRangeNonBlocking((int)MemoryAddress, (int)GetRegisterAddressLen() * 2); var res = await attachedInterface.ReadByteRangeNonBlocking((int)MemoryAddress, (int)GetRegisterAddressLen() * 2, false);
if (res == null) return null; if (res == null) return null;
return PlcValueParser.Parse<T>(this, res); return PlcValueParser.Parse<T>(this, res);

View File

@@ -21,12 +21,13 @@ namespace MewtocolNet.Registers {
internal uint WordsSize { get; set; } internal uint WordsSize { get; set; }
private bool isCalibratedFromPlc = false; internal bool isCalibratedFromPlc = false;
/// <summary> [Obsolete("Creating registers directly is not supported use IPlc.Register instead")]
/// Defines a register containing a string public StringRegister() =>
/// </summary> throw new NotSupportedException("Direct register instancing is not supported, use the builder pattern");
public StringRegister (uint _address, string _name = null) {
internal StringRegister (uint _address, string _name = null) {
name = _name; name = _name;
memoryAddress = _address; memoryAddress = _address;
@@ -55,7 +56,7 @@ namespace MewtocolNet.Registers {
/// <inheritdoc/> /// <inheritdoc/>
public override void SetValueFromPLC (object val) { public override void SetValueFromPLC (object val) {
if (!val.Equals(lastValue)) { if (val == null || !val.Equals(lastValue)) {
lastValue = (string)val; lastValue = (string)val;
@@ -72,27 +73,7 @@ namespace MewtocolNet.Registers {
/// <inheritdoc/> /// <inheritdoc/>
public override uint GetRegisterAddressLen() => Math.Max(1, WordsSize); public override uint GetRegisterAddressLen() => Math.Max(1, WordsSize);
/// <inheritdoc/> internal async Task CalibrateFromPLC () {
public override async Task<object> ReadAsync() {
if (!attachedInterface.IsConnected) return null;
//get the string params first
if(!isCalibratedFromPlc) await CalibrateFromPLC();
var read = await attachedInterface.ReadRawRegisterAsync(this);
if (read == null) return null;
var parsed = PlcValueParser.Parse<string>(this, read);
SetValueFromPLC(parsed);
return parsed;
}
private async Task CalibrateFromPLC () {
Logger.Log($"Calibrating string ({PLCAddressName}) from PLC source", LogLevel.Verbose, attachedInterface); Logger.Log($"Calibrating string ({PLCAddressName}) from PLC source", LogLevel.Verbose, attachedInterface);
@@ -116,29 +97,78 @@ namespace MewtocolNet.Registers {
} }
/// <inheritdoc/> /// <inheritdoc/>
public override async Task<bool> WriteAsync(object data) { public override async Task<object> ReadAsync() {
if (!attachedInterface.IsConnected) return false; if (!attachedInterface.IsConnected)
throw MewtocolException.NotConnectedSend();
if (!isCalibratedFromPlc) { if (!isCalibratedFromPlc) await CalibrateFromPLC();
//try to calibrate from plc var res = await underlyingMemory.ReadRegisterAsync(this);
await CalibrateFromPLC(); if (!res) return null;
var bytes = underlyingMemory.GetUnderlyingBytes(this);
return SetValueFromBytes(bytes);
} }
/// <inheritdoc/>
public override async Task<bool> WriteAsync(object data) {
if (!attachedInterface.IsConnected)
throw MewtocolException.NotConnectedSend();
if (!isCalibratedFromPlc) await CalibrateFromPLC();
var encoded = PlcValueParser.Encode(this, (string)data); var encoded = PlcValueParser.Encode(this, (string)data);
var res = await attachedInterface.WriteRawRegisterAsync(this, encoded); var res = await underlyingMemory.WriteRegisterAsync(this, encoded);
if (res) { if (res) {
AddSuccessWrite();
SetValueFromPLC(data); SetValueFromPLC(data);
UsedSize = (short)((string)Value).Length;
} }
return res; return res;
} }
internal override object SetValueFromBytes(byte[] bytes) {
AddSuccessRead();
var parsed = PlcValueParser.Parse<string>(this, bytes);
SetValueFromPLC(parsed);
return parsed;
}
internal override async Task<bool> WriteToAnonymousAsync(object value) {
if (!attachedInterface.IsConnected)
throw MewtocolException.NotConnectedSend();
if (!isCalibratedFromPlc) await CalibrateFromPLC();
var encoded = PlcValueParser.Encode(this, (string)value);
return await attachedInterface.WriteByteRange((int)MemoryAddress, encoded);
}
internal override async Task<object> ReadFromAnonymousAsync() {
if (!attachedInterface.IsConnected)
throw MewtocolException.NotConnectedSend();
if (!isCalibratedFromPlc) await CalibrateFromPLC();
var res = await attachedInterface.ReadByteRangeNonBlocking((int)MemoryAddress, (int)GetRegisterAddressLen() * 2);
if (res == null) return null;
return PlcValueParser.Parse<string>(this, res);
}
} }
} }

View File

@@ -6,6 +6,7 @@ using System.Globalization;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using MewtocolNet.Helpers; using MewtocolNet.Helpers;
using MewtocolNet.Exceptions;
namespace MewtocolNet.TypeConversion { namespace MewtocolNet.TypeConversion {
@@ -35,119 +36,82 @@ namespace MewtocolNet.TypeConversion {
new PlcTypeConversion<bool>(RegisterType.R) { new PlcTypeConversion<bool>(RegisterType.R) {
HoldingRegisterType = typeof(BoolRegister), HoldingRegisterType = typeof(BoolRegister),
PlcVarType = PlcVarType.BOOL, PlcVarType = PlcVarType.BOOL,
FromRaw = (reg, bytes) => { FromRaw = (reg, bytes) => (bool)(bytes[0] == 1),
ToRaw = (reg, value) => new byte[] { (byte)(value ? 1 : 0) },
return (bool)(bytes[0] == 1);
}, },
ToRaw = (reg, value) => {
return new byte[] { (byte)(value ? 1 : 0) };
},
},
//default bool X conversion //default bool X conversion
new PlcTypeConversion<bool>(RegisterType.X) { new PlcTypeConversion<bool>(RegisterType.X) {
HoldingRegisterType = typeof(BoolRegister), HoldingRegisterType = typeof(BoolRegister),
PlcVarType = PlcVarType.BOOL, PlcVarType = PlcVarType.BOOL,
FromRaw = (reg, bytes) => { FromRaw = (reg, bytes) => (bool)(bytes[0] == 1),
ToRaw = (reg, value) => new byte[] { (byte)(value ? 1 : 0) },
return bytes[0] == 1;
}, },
ToRaw = (reg, value) => {
return new byte[] { (byte)(value ? 1 : 0) };
},
},
//default bool Y conversion //default bool Y conversion
new PlcTypeConversion<bool>(RegisterType.Y) { new PlcTypeConversion<bool>(RegisterType.Y) {
HoldingRegisterType = typeof(BoolRegister), HoldingRegisterType = typeof(BoolRegister),
PlcVarType = PlcVarType.BOOL, PlcVarType = PlcVarType.BOOL,
FromRaw = (reg, bytes) => { FromRaw = (reg, bytes) => (bool)(bytes[0] == 1),
ToRaw = (reg, value) => new byte[] { (byte)(value ? 1 : 0) },
return bytes[0] == 1;
}, },
ToRaw = (reg, value) => {
return new byte[] { (byte)(value ? 1 : 0) };
},
},
//default short DT conversion //default short DT conversion
new PlcTypeConversion<short>(RegisterType.DT) { new PlcTypeConversion<short>(RegisterType.DT) {
HoldingRegisterType = typeof(NumberRegister<short>), HoldingRegisterType = typeof(NumberRegister<short>),
PlcVarType = PlcVarType.INT, PlcVarType = PlcVarType.INT,
FromRaw = (reg, bytes) => { FromRaw = (reg, bytes) => BitConverter.ToInt16(bytes, 0),
ToRaw = (reg, value) => BitConverter.GetBytes(value),
return BitConverter.ToInt16(bytes, 0);
}, },
ToRaw = (reg, value) => {
return BitConverter.GetBytes(value);
},
},
//default ushort DT conversion //default ushort DT conversion
new PlcTypeConversion<ushort>(RegisterType.DT) { new PlcTypeConversion<ushort>(RegisterType.DT) {
HoldingRegisterType = typeof(NumberRegister<ushort>), HoldingRegisterType = typeof(NumberRegister<ushort>),
PlcVarType = PlcVarType.UINT, PlcVarType = PlcVarType.UINT,
FromRaw = (reg, bytes) => { FromRaw = (reg, bytes) => BitConverter.ToUInt16(bytes, 0),
ToRaw = (reg, value) => BitConverter.GetBytes(value),
return BitConverter.ToUInt16(bytes, 0);
}, },
ToRaw = (reg, value) => {
return BitConverter.GetBytes(value); //default ushort DT conversion
new PlcTypeConversion<ushort>(RegisterType.DT) {
HoldingRegisterType = typeof(NumberRegister<ushort>),
PlcVarType = PlcVarType.WORD,
FromRaw = (reg, bytes) => BitConverter.ToUInt16(bytes, 0),
ToRaw = (reg, value) => BitConverter.GetBytes(value),
},
},
},
//default int DDT conversion //default int DDT conversion
new PlcTypeConversion<int>(RegisterType.DDT) { new PlcTypeConversion<int>(RegisterType.DDT) {
HoldingRegisterType = typeof(NumberRegister<int>), HoldingRegisterType = typeof(NumberRegister<int>),
PlcVarType = PlcVarType.DINT, PlcVarType = PlcVarType.DINT,
FromRaw = (reg, bytes) => { FromRaw = (reg, bytes) => BitConverter.ToInt32(bytes, 0),
ToRaw = (reg, value) => BitConverter.GetBytes(value),
return BitConverter.ToInt32(bytes, 0);
}, },
ToRaw = (reg, value) => {
return BitConverter.GetBytes(value);
},
},
//default uint DDT conversion //default uint DDT conversion
new PlcTypeConversion<uint>(RegisterType.DDT) { new PlcTypeConversion<uint>(RegisterType.DDT) {
HoldingRegisterType = typeof(NumberRegister<uint>), HoldingRegisterType = typeof(NumberRegister<uint>),
PlcVarType = PlcVarType.UDINT, PlcVarType = PlcVarType.UDINT,
FromRaw = (reg, bytes) => { FromRaw = (reg, bytes) => BitConverter.ToUInt32(bytes, 0),
ToRaw = (reg, value) => BitConverter.GetBytes(value),
return BitConverter.ToUInt32(bytes, 0);
}, },
ToRaw = (reg, value) => {
return BitConverter.GetBytes(value); //default uint DDT conversion
new PlcTypeConversion<uint>(RegisterType.DDT) {
HoldingRegisterType = typeof(NumberRegister<uint>),
PlcVarType = PlcVarType.DWORD,
FromRaw = (reg, bytes) => BitConverter.ToUInt32(bytes, 0),
ToRaw = (reg, value) => BitConverter.GetBytes(value),
},
},
},
//default float DDT conversion //default float DDT conversion
new PlcTypeConversion<float>(RegisterType.DDT) { new PlcTypeConversion<float>(RegisterType.DDT) {
HoldingRegisterType = typeof(NumberRegister<float>), HoldingRegisterType = typeof(NumberRegister<float>),
PlcVarType = PlcVarType.REAL, PlcVarType = PlcVarType.REAL,
FromRaw = (reg, bytes) => { FromRaw = (reg, bytes) => BitConverter.ToSingle(bytes, 0),
ToRaw = (reg, value) => BitConverter.GetBytes(value),
//bytes = new byte[] { 0xCD, 0xCC, 0x8C, 0x40 };
float finalFloat = BitConverter.ToSingle(bytes, 0);
return finalFloat;
}, },
ToRaw = (reg, value) => {
return BitConverter.GetBytes(value);
},
},
//default TimeSpan DDT conversion //default TimeSpan DDT conversion
new PlcTypeConversion<TimeSpan>(RegisterType.DDT) { new PlcTypeConversion<TimeSpan>(RegisterType.DDT) {
HoldingRegisterType = typeof(NumberRegister<TimeSpan>), HoldingRegisterType = typeof(NumberRegister<TimeSpan>),
@@ -167,18 +131,28 @@ namespace MewtocolNet.TypeConversion {
}, },
}, },
//default byte array DT Range conversion
//default byte array DT Range conversion, direct pass through
new PlcTypeConversion<byte[]>(RegisterType.DT_BYTE_RANGE) { new PlcTypeConversion<byte[]>(RegisterType.DT_BYTE_RANGE) {
HoldingRegisterType = typeof(BytesRegister), HoldingRegisterType = typeof(BytesRegister),
FromRaw = (reg, bytes) => bytes, FromRaw = (reg, bytes) => bytes,
ToRaw = (reg, value) => value, ToRaw = (reg, value) => value,
}, },
//default string DT Range conversion
//default string DT Range conversion Example bytes: (04 00 03 00 XX XX XX)
//first 4 bytes are reserved size (2 bytes) and used size (2 bytes)
//the remaining bytes are the ascii bytes for the string
new PlcTypeConversion<string>(RegisterType.DT_BYTE_RANGE) { new PlcTypeConversion<string>(RegisterType.DT_BYTE_RANGE) {
HoldingRegisterType = typeof(StringRegister), HoldingRegisterType = typeof(StringRegister),
PlcVarType = PlcVarType.STRING, PlcVarType = PlcVarType.STRING,
FromRaw = (reg, bytes) => { FromRaw = (reg, bytes) => {
if(bytes == null || bytes.Length <= 4) {
throw new MewtocolException("Failed to convert string bytes, response not long enough");
}
//get actual showed size //get actual showed size
short actualLen = BitConverter.ToInt16(bytes, 2); short actualLen = BitConverter.ToInt16(bytes, 2);
@@ -207,7 +181,8 @@ namespace MewtocolNet.TypeConversion {
}, },
}, },
//default bitn array DT conversion
//default bit array <=> byte array conversion
new PlcTypeConversion<BitArray>(RegisterType.DT_BYTE_RANGE) { new PlcTypeConversion<BitArray>(RegisterType.DT_BYTE_RANGE) {
HoldingRegisterType = typeof(BytesRegister), HoldingRegisterType = typeof(BytesRegister),
FromRaw = (reg, bytes) => { FromRaw = (reg, bytes) => {

View File

@@ -75,6 +75,8 @@ namespace MewtocolNet.UnderlyingRegisters {
internal async Task<bool> RequestByteReadAsync (ulong addStart, ulong addEnd) { internal async Task<bool> RequestByteReadAsync (ulong addStart, ulong addEnd) {
await CheckDynamicallySizedRegistersAsync();
var station = mewInterface.GetStationNumber(); var station = mewInterface.GetStationNumber();
string requeststring = $"%{station}#RD{GetMewtocolIdent(addStart, addEnd)}"; string requeststring = $"%{station}#RD{GetMewtocolIdent(addStart, addEnd)}";
@@ -143,6 +145,22 @@ namespace MewtocolNet.UnderlyingRegisters {
} }
private async Task CheckDynamicallySizedRegistersAsync () {
//calibrating at runtime sized registers
var uncalibratedStringRegisters = linkedRegisters
.Where(x => x is StringRegister sreg && !sreg.isCalibratedFromPlc)
.Cast<StringRegister>()
.ToList();
foreach (var register in uncalibratedStringRegisters)
await register.CalibrateFromPLC();
if (uncalibratedStringRegisters.Count > 0)
mewInterface.memoryManager.MergeAndSizeDataAreas();
}
private string GetMewtocolIdent () { private string GetMewtocolIdent () {
StringBuilder asciistring = new StringBuilder("D"); StringBuilder asciistring = new StringBuilder("D");

View File

@@ -253,6 +253,22 @@ namespace MewtocolNet.UnderlyingRegisters {
} }
internal void MergeAndSizeDataAreas () {
//merge gaps that the algorithm didn't catch be rerunning the register attachment
foreach (var pLevel in pollLevels) {
var allDataAreaRegisters = pLevel.dataAreas.SelectMany(x => x.linkedRegisters).ToList();
var dataAreas = new List<DTArea>(allDataAreaRegisters.Capacity);
foreach (var reg in allDataAreaRegisters)
AddDTArea(reg);
}
}
internal async Task PollAllAreasAsync () { internal async Task PollAllAreasAsync () {
foreach (var pollLevel in pollLevels) { foreach (var pollLevel in pollLevels) {