mirror of
https://github.com/OpenLogics/MewtocolNet.git
synced 2025-12-06 03:01:24 +00:00
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:
@@ -175,7 +175,7 @@ namespace MewtocolNet {
|
||||
/// <inheritdoc/>
|
||||
public async Task DisconnectAsync () {
|
||||
|
||||
await pollCycleTask;
|
||||
if(pollCycleTask != null) await pollCycleTask;
|
||||
|
||||
Disconnect();
|
||||
|
||||
@@ -186,7 +186,7 @@ namespace MewtocolNet {
|
||||
|
||||
if (!IsConnected) return;
|
||||
|
||||
if (!pollCycleTask.IsCompleted) pollCycleTask.Wait();
|
||||
if (pollCycleTask != null && !pollCycleTask.IsCompleted) pollCycleTask.Wait();
|
||||
|
||||
OnMajorSocketExceptionWhileConnected();
|
||||
|
||||
|
||||
@@ -255,7 +255,7 @@ namespace MewtocolNet {
|
||||
}
|
||||
|
||||
var assembler = new RegisterAssembler(this);
|
||||
var registers = assembler.Assemble(regBuild);
|
||||
var registers = assembler.AssembleAll(regBuild);
|
||||
AddRegisters(registers.ToArray());
|
||||
|
||||
}
|
||||
|
||||
@@ -94,9 +94,16 @@ namespace MewtocolNet {
|
||||
/// /// <param name="start">start address of the array</param>
|
||||
/// <param name="byteArr"></param>
|
||||
/// <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;
|
||||
if (byteArr.Length % 2 != 0)
|
||||
wordLength++;
|
||||
@@ -120,7 +127,7 @@ namespace MewtocolNet {
|
||||
/// <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>
|
||||
/// <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>();
|
||||
|
||||
@@ -167,99 +174,6 @@ namespace MewtocolNet {
|
||||
|
||||
#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
|
||||
|
||||
internal string GetStationNumber() {
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using MewtocolNet.Exceptions;
|
||||
using MewtocolNet.RegisterAttributes;
|
||||
using MewtocolNet.Registers;
|
||||
using MewtocolNet.RegisterAttributes;
|
||||
using MewtocolNet.UnderlyingRegisters;
|
||||
using System;
|
||||
using System.Collections;
|
||||
@@ -10,7 +8,6 @@ using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
@@ -76,6 +73,7 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
//optional
|
||||
internal uint? byteSize;
|
||||
internal uint? bitSize;
|
||||
internal int? stringSize;
|
||||
|
||||
internal int pollLevel = 1;
|
||||
|
||||
@@ -88,12 +86,15 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
|
||||
public SBase() { }
|
||||
|
||||
internal SBase(SData data) {
|
||||
internal SBase(SData data, RBuild bldr) {
|
||||
Data = data;
|
||||
builder = bldr;
|
||||
}
|
||||
|
||||
internal SData Data { get; set; }
|
||||
|
||||
internal RBuild builder;
|
||||
|
||||
}
|
||||
|
||||
internal struct ParseResult {
|
||||
@@ -311,6 +312,13 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
|
||||
}
|
||||
|
||||
/// <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) {
|
||||
|
||||
foreach (var method in parseMethods) {
|
||||
@@ -326,7 +334,8 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
unfinishedList.Add(res.stepData);
|
||||
|
||||
return new SAddress {
|
||||
Data = res.stepData
|
||||
Data = res.stepData,
|
||||
builder = this,
|
||||
};
|
||||
|
||||
} else if(res.state == ParseResultState.FailedHard) {
|
||||
@@ -347,6 +356,22 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
|
||||
public class SAddress : SBase {
|
||||
|
||||
/// <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> () {
|
||||
|
||||
if (!typeof(T).IsAllowedPlcCastingType()) {
|
||||
@@ -357,10 +382,11 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
|
||||
Data.dotnetVarType = typeof(T);
|
||||
|
||||
return new TempRegister<T>(Data);
|
||||
return new TempRegister<T>(Data, builder);
|
||||
|
||||
}
|
||||
|
||||
///<inheritdoc cref="AsType{T}()"/>
|
||||
public TempRegister AsType (Type type) {
|
||||
|
||||
if (!type.IsAllowedPlcCastingType()) {
|
||||
@@ -371,10 +397,70 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
|
||||
Data.dotnetVarType = type;
|
||||
|
||||
return new TempRegister(Data);
|
||||
return new TempRegister(Data, builder);
|
||||
|
||||
}
|
||||
|
||||
/// <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) {
|
||||
|
||||
if (Data.regType != RegisterType.DT) {
|
||||
@@ -386,10 +472,14 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
Data.byteSize = byteLength;
|
||||
Data.dotnetVarType = typeof(byte[]);
|
||||
|
||||
return new TempRegister(Data);
|
||||
return new TempRegister(Data, builder);
|
||||
|
||||
}
|
||||
|
||||
/// <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) {
|
||||
@@ -401,10 +491,13 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
Data.bitSize = bitCount;
|
||||
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() {
|
||||
|
||||
switch (Data.regType) {
|
||||
@@ -424,7 +517,7 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
break;
|
||||
}
|
||||
|
||||
return new TempRegister(Data);
|
||||
return new TempRegister(Data, builder);
|
||||
|
||||
}
|
||||
|
||||
@@ -436,8 +529,11 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
|
||||
public class TempRegister<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) {
|
||||
|
||||
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 {
|
||||
|
||||
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) {
|
||||
|
||||
Data.pollLevel = level;
|
||||
@@ -462,7 +572,20 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
|
||||
}
|
||||
|
||||
internal TempRegister RegCollection (RegisterCollection col) {
|
||||
/// <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) {
|
||||
|
||||
Data.regCollection = col;
|
||||
|
||||
@@ -470,7 +593,7 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
|
||||
}
|
||||
|
||||
internal TempRegister BoundProp (PropertyInfo prop) {
|
||||
internal TempRegister BoundProp(PropertyInfo prop) {
|
||||
|
||||
Data.boundProperty = prop;
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
builder.Invoke(regBuilder);
|
||||
|
||||
var assembler = new RegisterAssembler((MewtocolInterface)plc);
|
||||
var registers = assembler.Assemble(regBuilder);
|
||||
var registers = assembler.AssembleAll(regBuilder);
|
||||
|
||||
var interf = (MewtocolInterface)plc;
|
||||
|
||||
|
||||
138
MewtocolNet/RegisterBuilding/RegisterAssembler.cs
Normal file
138
MewtocolNet/RegisterBuilding/RegisterAssembler.cs
Normal 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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -169,7 +169,8 @@ namespace MewtocolNet.Registers {
|
||||
sb.AppendLine($"Perf. Reads: {successfulReads}, Writes: {successfulWrites}");
|
||||
sb.AppendLine($"Register Type: {RegisterType}");
|
||||
sb.AppendLine($"Address: {GetRegisterWordRangeString()}");
|
||||
if(GetSpecialAddress() != null) sb.AppendLine($"SPAddress: {GetSpecialAddress():X1}");
|
||||
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()}");
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
using MewtocolNet.UnderlyingRegisters;
|
||||
using MewtocolNet.Exceptions;
|
||||
using MewtocolNet.RegisterBuilding;
|
||||
using MewtocolNet.UnderlyingRegisters;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MewtocolNet.Registers {
|
||||
@@ -18,16 +21,11 @@ namespace MewtocolNet.Registers {
|
||||
/// </summary>
|
||||
public byte SpecialAddress => specialAddress;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new boolean register
|
||||
/// </summary>
|
||||
/// <param name="_io">The io type prefix</param>
|
||||
/// <param name="_spAddress">The special address</param>
|
||||
/// <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) {
|
||||
[Obsolete("Creating registers directly is not supported use IPlc.Register instead")]
|
||||
public BoolRegister() =>
|
||||
throw new NotSupportedException("Direct register instancing is not supported, use the builder pattern");
|
||||
|
||||
internal BoolRegister(IOType _io, byte _spAddress = 0x0, uint _areaAdress = 0, string _name = null) {
|
||||
|
||||
lastValue = null;
|
||||
|
||||
@@ -50,7 +48,7 @@ namespace MewtocolNet.Registers {
|
||||
throw new NotSupportedException("XY area addresses cant be greater than 110");
|
||||
|
||||
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);
|
||||
|
||||
@@ -61,31 +59,47 @@ namespace MewtocolNet.Registers {
|
||||
/// <inheritdoc/>
|
||||
public override async Task<object> ReadAsync() {
|
||||
|
||||
if (!attachedInterface.IsConnected) return null;
|
||||
if (!attachedInterface.IsConnected)
|
||||
throw MewtocolException.NotConnectedSend();
|
||||
|
||||
var read = await attachedInterface.ReadRawRegisterAsync(this);
|
||||
if(read == null) return null;
|
||||
|
||||
var parsed = PlcValueParser.Parse<bool>(this, read);
|
||||
|
||||
SetValueFromPLC(parsed);
|
||||
return parsed;
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
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();
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using MewtocolNet.Exceptions;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
@@ -21,12 +22,13 @@ namespace MewtocolNet.Registers {
|
||||
|
||||
internal uint ReservedBytesSize { get; set; }
|
||||
|
||||
internal ushort? ReservedBitSize { get; set; }
|
||||
internal ushort? ReservedBitSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Defines a register containing bytes
|
||||
/// </summary>
|
||||
public BytesRegister(uint _address, uint _reservedByteSize, string _name = null) {
|
||||
[Obsolete("Creating registers directly is not supported use IPlc.Register instead")]
|
||||
public BytesRegister() =>
|
||||
throw new NotSupportedException("Direct register instancing is not supported, use the builder pattern");
|
||||
|
||||
internal BytesRegister(uint _address, uint _reservedByteSize, string _name = null) {
|
||||
|
||||
name = _name;
|
||||
memoryAddress = _address;
|
||||
@@ -123,7 +125,8 @@ namespace MewtocolNet.Registers {
|
||||
/// <inheritdoc/>
|
||||
public override async Task<object> ReadAsync() {
|
||||
|
||||
if (!attachedInterface.IsConnected) return null;
|
||||
if (!attachedInterface.IsConnected)
|
||||
throw MewtocolException.NotConnectedSend();
|
||||
|
||||
var res = await underlyingMemory.ReadRegisterAsync(this);
|
||||
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/>
|
||||
public override async Task<bool> WriteAsync(object data) {
|
||||
|
||||
if (!attachedInterface.IsConnected) return false;
|
||||
if (!attachedInterface.IsConnected)
|
||||
throw MewtocolException.NotConnectedSend();
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,12 +17,11 @@ namespace MewtocolNet.Registers {
|
||||
/// <typeparam name="T">The type of the numeric value</typeparam>
|
||||
public class NumberRegister<T> : BaseRegister {
|
||||
|
||||
/// <summary>
|
||||
/// Defines a register containing a number
|
||||
/// </summary>
|
||||
/// <param name="_address">Memory start adress max 99999</param>
|
||||
/// <param name="_name">Name of the register</param>
|
||||
public NumberRegister (uint _address, string _name = null) {
|
||||
[Obsolete("Creating registers directly is not supported use IPlc.Register instead")]
|
||||
public NumberRegister() =>
|
||||
throw new NotSupportedException("Direct register instancing is not supported, use the builder pattern");
|
||||
|
||||
internal NumberRegister (uint _address, string _name = null) {
|
||||
|
||||
memoryAddress = _address;
|
||||
name = _name;
|
||||
@@ -130,7 +129,8 @@ namespace MewtocolNet.Registers {
|
||||
/// <inheritdoc/>
|
||||
public override async Task<object> ReadAsync() {
|
||||
|
||||
if (!attachedInterface.IsConnected) return null;
|
||||
if (!attachedInterface.IsConnected)
|
||||
throw MewtocolException.NotConnectedSend();
|
||||
|
||||
var res = await underlyingMemory.ReadRegisterAsync(this);
|
||||
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/>
|
||||
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 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) {
|
||||
|
||||
if (!attachedInterface.IsConnected)
|
||||
@@ -183,7 +184,7 @@ namespace MewtocolNet.Registers {
|
||||
if (!attachedInterface.IsConnected)
|
||||
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;
|
||||
|
||||
return PlcValueParser.Parse<T>(this, res);
|
||||
|
||||
@@ -21,12 +21,13 @@ namespace MewtocolNet.Registers {
|
||||
|
||||
internal uint WordsSize { get; set; }
|
||||
|
||||
private bool isCalibratedFromPlc = false;
|
||||
internal bool isCalibratedFromPlc = false;
|
||||
|
||||
/// <summary>
|
||||
/// Defines a register containing a string
|
||||
/// </summary>
|
||||
public StringRegister (uint _address, string _name = null) {
|
||||
[Obsolete("Creating registers directly is not supported use IPlc.Register instead")]
|
||||
public StringRegister() =>
|
||||
throw new NotSupportedException("Direct register instancing is not supported, use the builder pattern");
|
||||
|
||||
internal StringRegister (uint _address, string _name = null) {
|
||||
|
||||
name = _name;
|
||||
memoryAddress = _address;
|
||||
@@ -55,7 +56,7 @@ namespace MewtocolNet.Registers {
|
||||
/// <inheritdoc/>
|
||||
public override void SetValueFromPLC (object val) {
|
||||
|
||||
if (!val.Equals(lastValue)) {
|
||||
if (val == null || !val.Equals(lastValue)) {
|
||||
|
||||
lastValue = (string)val;
|
||||
|
||||
@@ -72,27 +73,7 @@ namespace MewtocolNet.Registers {
|
||||
/// <inheritdoc/>
|
||||
public override uint GetRegisterAddressLen() => Math.Max(1, WordsSize);
|
||||
|
||||
/// <inheritdoc/>
|
||||
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 () {
|
||||
internal async Task CalibrateFromPLC () {
|
||||
|
||||
Logger.Log($"Calibrating string ({PLCAddressName}) from PLC source", LogLevel.Verbose, attachedInterface);
|
||||
|
||||
@@ -115,30 +96,79 @@ namespace MewtocolNet.Registers {
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task<object> ReadAsync() {
|
||||
|
||||
if (!attachedInterface.IsConnected)
|
||||
throw MewtocolException.NotConnectedSend();
|
||||
|
||||
if (!isCalibratedFromPlc) await CalibrateFromPLC();
|
||||
|
||||
var res = await underlyingMemory.ReadRegisterAsync(this);
|
||||
if (!res) return null;
|
||||
|
||||
var bytes = underlyingMemory.GetUnderlyingBytes(this);
|
||||
|
||||
return SetValueFromBytes(bytes);
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task<bool> WriteAsync(object data) {
|
||||
|
||||
if (!attachedInterface.IsConnected) return false;
|
||||
if (!attachedInterface.IsConnected)
|
||||
throw MewtocolException.NotConnectedSend();
|
||||
|
||||
if (!isCalibratedFromPlc) {
|
||||
|
||||
//try to calibrate from plc
|
||||
await CalibrateFromPLC();
|
||||
|
||||
}
|
||||
if (!isCalibratedFromPlc) await CalibrateFromPLC();
|
||||
|
||||
var encoded = PlcValueParser.Encode(this, (string)data);
|
||||
var res = await attachedInterface.WriteRawRegisterAsync(this, encoded);
|
||||
|
||||
var res = await underlyingMemory.WriteRegisterAsync(this, encoded);
|
||||
|
||||
if (res) {
|
||||
AddSuccessWrite();
|
||||
SetValueFromPLC(data);
|
||||
UsedSize = (short)((string)Value).Length;
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using MewtocolNet.Helpers;
|
||||
using MewtocolNet.Exceptions;
|
||||
|
||||
namespace MewtocolNet.TypeConversion {
|
||||
|
||||
@@ -35,119 +36,82 @@ namespace MewtocolNet.TypeConversion {
|
||||
new PlcTypeConversion<bool>(RegisterType.R) {
|
||||
HoldingRegisterType = typeof(BoolRegister),
|
||||
PlcVarType = PlcVarType.BOOL,
|
||||
FromRaw = (reg, bytes) => {
|
||||
|
||||
return (bool)(bytes[0] == 1);
|
||||
},
|
||||
ToRaw = (reg, value) => {
|
||||
|
||||
return new byte[] { (byte)(value ? 1 : 0) };
|
||||
|
||||
},
|
||||
FromRaw = (reg, bytes) => (bool)(bytes[0] == 1),
|
||||
ToRaw = (reg, value) => new byte[] { (byte)(value ? 1 : 0) },
|
||||
},
|
||||
|
||||
//default bool X conversion
|
||||
new PlcTypeConversion<bool>(RegisterType.X) {
|
||||
HoldingRegisterType = typeof(BoolRegister),
|
||||
PlcVarType = PlcVarType.BOOL,
|
||||
FromRaw = (reg, bytes) => {
|
||||
|
||||
return bytes[0] == 1;
|
||||
},
|
||||
ToRaw = (reg, value) => {
|
||||
|
||||
return new byte[] { (byte)(value ? 1 : 0) };
|
||||
|
||||
},
|
||||
FromRaw = (reg, bytes) => (bool)(bytes[0] == 1),
|
||||
ToRaw = (reg, value) => new byte[] { (byte)(value ? 1 : 0) },
|
||||
},
|
||||
|
||||
//default bool Y conversion
|
||||
new PlcTypeConversion<bool>(RegisterType.Y) {
|
||||
HoldingRegisterType = typeof(BoolRegister),
|
||||
PlcVarType = PlcVarType.BOOL,
|
||||
FromRaw = (reg, bytes) => {
|
||||
|
||||
return bytes[0] == 1;
|
||||
},
|
||||
ToRaw = (reg, value) => {
|
||||
|
||||
return new byte[] { (byte)(value ? 1 : 0) };
|
||||
|
||||
},
|
||||
FromRaw = (reg, bytes) => (bool)(bytes[0] == 1),
|
||||
ToRaw = (reg, value) => new byte[] { (byte)(value ? 1 : 0) },
|
||||
},
|
||||
|
||||
//default short DT conversion
|
||||
new PlcTypeConversion<short>(RegisterType.DT) {
|
||||
HoldingRegisterType = typeof(NumberRegister<short>),
|
||||
PlcVarType = PlcVarType.INT,
|
||||
FromRaw = (reg, bytes) => {
|
||||
|
||||
return BitConverter.ToInt16(bytes, 0);
|
||||
},
|
||||
ToRaw = (reg, value) => {
|
||||
|
||||
return BitConverter.GetBytes(value);
|
||||
|
||||
},
|
||||
FromRaw = (reg, bytes) => BitConverter.ToInt16(bytes, 0),
|
||||
ToRaw = (reg, value) => BitConverter.GetBytes(value),
|
||||
},
|
||||
|
||||
//default ushort DT conversion
|
||||
new PlcTypeConversion<ushort>(RegisterType.DT) {
|
||||
HoldingRegisterType = typeof(NumberRegister<ushort>),
|
||||
PlcVarType = PlcVarType.UINT,
|
||||
FromRaw = (reg, bytes) => {
|
||||
|
||||
return BitConverter.ToUInt16(bytes, 0);
|
||||
},
|
||||
ToRaw = (reg, value) => {
|
||||
|
||||
return BitConverter.GetBytes(value);
|
||||
|
||||
},
|
||||
FromRaw = (reg, bytes) => BitConverter.ToUInt16(bytes, 0),
|
||||
ToRaw = (reg, value) => 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
|
||||
new PlcTypeConversion<int>(RegisterType.DDT) {
|
||||
HoldingRegisterType = typeof(NumberRegister<int>),
|
||||
PlcVarType = PlcVarType.DINT,
|
||||
FromRaw = (reg, bytes) => {
|
||||
|
||||
return BitConverter.ToInt32(bytes, 0);
|
||||
},
|
||||
ToRaw = (reg, value) => {
|
||||
|
||||
return BitConverter.GetBytes(value);
|
||||
|
||||
},
|
||||
FromRaw = (reg, bytes) => BitConverter.ToInt32(bytes, 0),
|
||||
ToRaw = (reg, value) => BitConverter.GetBytes(value),
|
||||
},
|
||||
|
||||
//default uint DDT conversion
|
||||
new PlcTypeConversion<uint>(RegisterType.DDT) {
|
||||
HoldingRegisterType = typeof(NumberRegister<uint>),
|
||||
PlcVarType = PlcVarType.UDINT,
|
||||
FromRaw = (reg, bytes) => {
|
||||
|
||||
return BitConverter.ToUInt32(bytes, 0);
|
||||
},
|
||||
ToRaw = (reg, value) => {
|
||||
|
||||
return BitConverter.GetBytes(value);
|
||||
|
||||
},
|
||||
FromRaw = (reg, bytes) => BitConverter.ToUInt32(bytes, 0),
|
||||
ToRaw = (reg, value) => 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
|
||||
new PlcTypeConversion<float>(RegisterType.DDT) {
|
||||
HoldingRegisterType = typeof(NumberRegister<float>),
|
||||
PlcVarType = PlcVarType.REAL,
|
||||
FromRaw = (reg, bytes) => {
|
||||
|
||||
//bytes = new byte[] { 0xCD, 0xCC, 0x8C, 0x40 };
|
||||
|
||||
float finalFloat = BitConverter.ToSingle(bytes, 0);
|
||||
|
||||
return finalFloat;
|
||||
|
||||
},
|
||||
ToRaw = (reg, value) => {
|
||||
|
||||
return BitConverter.GetBytes(value);
|
||||
|
||||
},
|
||||
FromRaw = (reg, bytes) => BitConverter.ToSingle(bytes, 0),
|
||||
ToRaw = (reg, value) => BitConverter.GetBytes(value),
|
||||
},
|
||||
|
||||
//default TimeSpan DDT conversion
|
||||
new PlcTypeConversion<TimeSpan>(RegisterType.DDT) {
|
||||
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) {
|
||||
HoldingRegisterType = typeof(BytesRegister),
|
||||
FromRaw = (reg, bytes) => bytes,
|
||||
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) {
|
||||
HoldingRegisterType = typeof(StringRegister),
|
||||
PlcVarType = PlcVarType.STRING,
|
||||
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
|
||||
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) {
|
||||
HoldingRegisterType = typeof(BytesRegister),
|
||||
FromRaw = (reg, bytes) => {
|
||||
|
||||
@@ -75,6 +75,8 @@ namespace MewtocolNet.UnderlyingRegisters {
|
||||
|
||||
internal async Task<bool> RequestByteReadAsync (ulong addStart, ulong addEnd) {
|
||||
|
||||
await CheckDynamicallySizedRegistersAsync();
|
||||
|
||||
var station = mewInterface.GetStationNumber();
|
||||
|
||||
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 () {
|
||||
|
||||
StringBuilder asciistring = new StringBuilder("D");
|
||||
|
||||
@@ -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 () {
|
||||
|
||||
foreach (var pollLevel in pollLevels) {
|
||||
|
||||
Reference in New Issue
Block a user