mirror of
https://github.com/OpenLogics/MewtocolNet.git
synced 2025-12-06 03:01:24 +00:00
Change datatypes to uint for registers
- add new tests / fix old tests - change register builder pattern - add register attribute from string name - fix register update triggers
This commit is contained in:
@@ -19,13 +19,20 @@ namespace MewtocolNet.Exceptions {
|
||||
|
||||
internal static MewtocolException DupeRegister (IRegisterInternal register) {
|
||||
|
||||
return new MewtocolException($"The mewtocol interface already contains this register: {register.GetRegisterPLCName()}");
|
||||
return new MewtocolException($"The mewtocol interface already contains this register: {register.GetMewName()}");
|
||||
|
||||
}
|
||||
|
||||
internal static MewtocolException DupeNameRegister (IRegisterInternal register) {
|
||||
|
||||
return new MewtocolException($"The mewtocol interface registers already contains a register with the name: {register.GetRegisterPLCName()}");
|
||||
return new MewtocolException($"The mewtocol interface registers already contains a register with the name: {register.GetMewName()}");
|
||||
|
||||
}
|
||||
|
||||
internal static MewtocolException OverlappingRegister (IRegisterInternal registerA, IRegisterInternal registerB) {
|
||||
|
||||
throw new MewtocolException($"The register: {registerA.GetRegisterWordRangeString()} " +
|
||||
$"has overlapping addresses with: {registerB.GetRegisterWordRangeString()}");
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -14,50 +14,6 @@ namespace MewtocolNet {
|
||||
/// </summary>
|
||||
public static class MewtocolHelpers {
|
||||
|
||||
#region Value PLC Humanizers
|
||||
|
||||
/// <summary>
|
||||
/// Gets the TimeSpan as a PLC representation string fe.
|
||||
/// <code>
|
||||
/// T#1h10m30s20ms
|
||||
/// </code>
|
||||
/// </summary>
|
||||
/// <param name="timespan"></param>
|
||||
/// <returns></returns>
|
||||
public static string AsPLCTime (this TimeSpan timespan) {
|
||||
|
||||
if (timespan == null || timespan == TimeSpan.Zero)
|
||||
return $"T#0s";
|
||||
|
||||
StringBuilder sb = new StringBuilder("T#");
|
||||
|
||||
int millis = timespan.Milliseconds;
|
||||
int seconds = timespan.Seconds;
|
||||
int minutes = timespan.Minutes;
|
||||
int hours = timespan.Hours;
|
||||
|
||||
if (hours > 0) sb.Append($"{hours}h");
|
||||
if (minutes > 0) sb.Append($"{minutes}m");
|
||||
if (seconds > 0) sb.Append($"{seconds}s");
|
||||
if (millis > 0) sb.Append($"{millis}ms");
|
||||
|
||||
return sb.ToString();
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Turns a bit array into a 0 and 1 string
|
||||
/// </summary>
|
||||
public static string ToBitString(this BitArray arr) {
|
||||
|
||||
var bits = new bool[arr.Length];
|
||||
arr.CopyTo(bits, 0);
|
||||
return string.Join("", bits.Select(x => x ? "1" : "0"));
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Byte and string operation helpers
|
||||
|
||||
/// <summary>
|
||||
|
||||
89
MewtocolNet/Helpers/PlcFormat.cs
Normal file
89
MewtocolNet/Helpers/PlcFormat.cs
Normal file
@@ -0,0 +1,89 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace MewtocolNet {
|
||||
|
||||
public static class PlcFormat {
|
||||
|
||||
/// <summary>
|
||||
/// Gets the TimeSpan as a PLC representation string fe.
|
||||
/// <code>
|
||||
/// T#1h10m30s20ms
|
||||
/// </code>
|
||||
/// </summary>
|
||||
/// <param name="timespan"></param>
|
||||
/// <returns></returns>
|
||||
public static string ToPlcTime (this TimeSpan timespan) {
|
||||
|
||||
if (timespan == null || timespan == TimeSpan.Zero)
|
||||
return $"T#0s";
|
||||
|
||||
StringBuilder sb = new StringBuilder("T#");
|
||||
|
||||
int millis = timespan.Milliseconds;
|
||||
int seconds = timespan.Seconds;
|
||||
int minutes = timespan.Minutes;
|
||||
int hours = timespan.Hours;
|
||||
int days = timespan.Days;
|
||||
|
||||
if (days > 0) sb.Append($"{days}d");
|
||||
if (hours > 0) sb.Append($"{hours}h");
|
||||
if (minutes > 0) sb.Append($"{minutes}m");
|
||||
if (seconds > 0) sb.Append($"{seconds}s");
|
||||
if (millis > 0) sb.Append($"{millis}ms");
|
||||
|
||||
return sb.ToString();
|
||||
|
||||
}
|
||||
|
||||
public static TimeSpan ParsePlcTime (string plcTimeFormat) {
|
||||
|
||||
var reg = new Regex(@"(?:T|t)#(?:(?<d>[0-9]{1,2})d)?(?:(?<h>[0-9]{1,2})h)?(?:(?<m>[0-9]{1,2})m)?(?:(?<s>[0-9]{1,2})s)?(?:(?<ms>[0-9]{1,3})ms)?");
|
||||
var match = reg.Match(plcTimeFormat);
|
||||
|
||||
if(match.Success) {
|
||||
|
||||
var days = match.Groups["d"].Value;
|
||||
var hours = match.Groups["h"].Value;
|
||||
var minutes = match.Groups["m"].Value;
|
||||
var seconds = match.Groups["s"].Value;
|
||||
var milliseconds = match.Groups["ms"].Value;
|
||||
|
||||
TimeSpan retTime = TimeSpan.Zero;
|
||||
|
||||
if (!string.IsNullOrEmpty(days)) retTime += TimeSpan.FromDays(int.Parse(days));
|
||||
if (!string.IsNullOrEmpty(hours)) retTime += TimeSpan.FromHours(int.Parse(hours));
|
||||
if (!string.IsNullOrEmpty(minutes)) retTime += TimeSpan.FromMinutes(int.Parse(minutes));
|
||||
if (!string.IsNullOrEmpty(seconds)) retTime += TimeSpan.FromSeconds(int.Parse(seconds));
|
||||
if (!string.IsNullOrEmpty(milliseconds)) retTime += TimeSpan.FromMilliseconds(int.Parse(milliseconds));
|
||||
|
||||
if ((retTime.TotalMilliseconds % 10) != 0)
|
||||
throw new NotSupportedException("Plc times can't have a millisecond component lower than 10ms");
|
||||
|
||||
return retTime;
|
||||
|
||||
}
|
||||
|
||||
return TimeSpan.Zero;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Turns a bit array into a 0 and 1 string
|
||||
/// </summary>
|
||||
public static string ToBitString(this BitArray arr) {
|
||||
|
||||
var bits = new bool[arr.Length];
|
||||
arr.CopyTo(bits, 0);
|
||||
return string.Join("", bits.Select(x => x ? "1" : "0"));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -77,7 +77,14 @@ namespace MewtocolNet {
|
||||
/// <param name="withTerminator">Append the checksum and bcc automatically</param>
|
||||
/// <param name="timeoutMs">Timout to wait for a response</param>
|
||||
/// <returns>Returns the result</returns>
|
||||
Task<MewtocolFrameResponse> SendCommandAsync(string _msg, bool withTerminator = true, int timeoutMs = -1);
|
||||
Task<MewtocolFrameResponse> SendCommandAsync(string _msg, bool withTerminator = true, int timeoutMs = -1, Action<double> onReceiveProgress = null);
|
||||
|
||||
/// <summary>
|
||||
/// Changes the PLCs operation mode to the given one
|
||||
/// </summary>
|
||||
/// <param name="setRun">True for run mode, false for prog mode</param>
|
||||
/// <returns>The success state of the write operation</returns>
|
||||
Task<bool> SetOperationModeAsync(bool setRun);
|
||||
|
||||
/// <summary>
|
||||
/// Use this to await the first poll iteration after connecting,
|
||||
|
||||
@@ -143,6 +143,7 @@ namespace MewtocolNet {
|
||||
private protected MewtocolInterface () {
|
||||
|
||||
Connected += MewtocolInterface_Connected;
|
||||
RegisterChanged += OnRegisterChanged;
|
||||
|
||||
void MewtocolInterface_Connected(PLCInfo obj) {
|
||||
|
||||
@@ -153,16 +154,15 @@ namespace MewtocolNet {
|
||||
|
||||
}
|
||||
|
||||
RegisterChanged += (o) => {
|
||||
}
|
||||
|
||||
private void OnRegisterChanged(IRegister o) {
|
||||
|
||||
var asInternal = (IRegisterInternal)o;
|
||||
|
||||
string address = $"{asInternal.GetRegisterString()}{asInternal.GetStartingMemoryArea()}".PadRight(5, (char)32);
|
||||
|
||||
Logger.Log($"{address} " +
|
||||
Logger.Log($"{asInternal.GetMewName()} " +
|
||||
$"{(o.Name != null ? $"({o.Name}) " : "")}" +
|
||||
$"changed to \"{asInternal.GetValueString()}\"", LogLevel.Change, this);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -198,12 +198,12 @@ namespace MewtocolNet {
|
||||
public virtual string GetConnectionInfo () => throw new NotImplementedException();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<MewtocolFrameResponse> SendCommandAsync(string _msg, bool withTerminator = true, int timeoutMs = -1) {
|
||||
public async Task<MewtocolFrameResponse> SendCommandAsync (string _msg, bool withTerminator = true, int timeoutMs = -1, Action<double> onReceiveProgress = null) {
|
||||
|
||||
//send request
|
||||
queuedMessages++;
|
||||
|
||||
var tempResponse = queue.Enqueue(async () => await SendFrameAsync(_msg, withTerminator, withTerminator));
|
||||
var tempResponse = queue.Enqueue(async () => await SendFrameAsync(_msg, withTerminator, withTerminator, onReceiveProgress));
|
||||
|
||||
if (await Task.WhenAny(tempResponse, Task.Delay(timeoutMs)) != tempResponse) {
|
||||
// timeout logic
|
||||
@@ -217,7 +217,7 @@ namespace MewtocolNet {
|
||||
|
||||
}
|
||||
|
||||
private protected async Task<MewtocolFrameResponse> SendFrameAsync (string frame, bool useBcc = true, bool useCr = true) {
|
||||
private protected async Task<MewtocolFrameResponse> SendFrameAsync (string frame, bool useBcc = true, bool useCr = true, Action<double> onReceiveProgress = null) {
|
||||
|
||||
try {
|
||||
|
||||
@@ -236,13 +236,27 @@ namespace MewtocolNet {
|
||||
byte[] writeBuffer = Encoding.UTF8.GetBytes(frame);
|
||||
stream.Write(writeBuffer, 0, writeBuffer.Length);
|
||||
|
||||
//calculate the expected number of frames from the message request
|
||||
int? wordsCountRequested = null;
|
||||
if(onReceiveProgress != null) {
|
||||
|
||||
var match = Regex.Match(frame, @"RDD(?<from>[0-9]{5})(?<to>[0-9]{5})");
|
||||
|
||||
if (match.Success) {
|
||||
var from = int.Parse(match.Groups["from"].Value);
|
||||
var to = int.Parse(match.Groups["to"].Value);
|
||||
wordsCountRequested = (to - from) + 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//calc upstream speed
|
||||
CalcUpstreamSpeed(writeBuffer.Length);
|
||||
|
||||
Logger.Log($"[---------CMD START--------]", LogLevel.Critical, this);
|
||||
Logger.Log($"--> OUT MSG: {frame.Replace("\r", "(CR)")}", LogLevel.Critical, this);
|
||||
|
||||
var readResult = await ReadCommandAsync();
|
||||
var readResult = await ReadCommandAsync(wordsCountRequested, onReceiveProgress);
|
||||
|
||||
//did not receive bytes but no errors, the com port was not configured right
|
||||
if (readResult.Item1.Length == 0) {
|
||||
@@ -294,7 +308,7 @@ namespace MewtocolNet {
|
||||
|
||||
}
|
||||
|
||||
private protected async Task<(byte[], bool)> ReadCommandAsync () {
|
||||
private protected async Task<(byte[], bool)> ReadCommandAsync (int? wordsCountRequested = null, Action<double> onReceiveProgress = null) {
|
||||
|
||||
//read total
|
||||
List<byte> totalResponse = new List<byte>();
|
||||
@@ -303,6 +317,7 @@ namespace MewtocolNet {
|
||||
try {
|
||||
|
||||
bool needsRead = false;
|
||||
int readFrames = 0;
|
||||
|
||||
do {
|
||||
|
||||
@@ -327,6 +342,15 @@ namespace MewtocolNet {
|
||||
|
||||
if (commandRes == CommandState.RequestedNextFrame) {
|
||||
|
||||
//calc frame progress
|
||||
if(onReceiveProgress != null && wordsCountRequested != null) {
|
||||
|
||||
var frameBytesCount = tempMsg.Length - 6;
|
||||
double prog = (double)frameBytesCount / wordsCountRequested.Value;
|
||||
onReceiveProgress(prog);
|
||||
|
||||
}
|
||||
|
||||
//request next frame
|
||||
var writeBuffer = Encoding.UTF8.GetBytes("%01**&\r");
|
||||
await stream.WriteAsync(writeBuffer, 0, writeBuffer.Length);
|
||||
@@ -334,6 +358,8 @@ namespace MewtocolNet {
|
||||
|
||||
}
|
||||
|
||||
readFrames++;
|
||||
|
||||
} while (needsRead);
|
||||
|
||||
} catch (OperationCanceledException) { }
|
||||
@@ -405,8 +431,8 @@ namespace MewtocolNet {
|
||||
|
||||
private protected virtual void OnConnected (PLCInfo plcinf) {
|
||||
|
||||
Logger.Log("Connected", LogLevel.Info, this);
|
||||
Logger.Log($"\n\n{plcinf.ToString()}\n\n", LogLevel.Verbose, this);
|
||||
Logger.Log("Connected to PLC", LogLevel.Info, this);
|
||||
Logger.Log($"{plcinf.ToString()}", LogLevel.Verbose, this);
|
||||
|
||||
IsConnected = true;
|
||||
|
||||
@@ -440,17 +466,6 @@ namespace MewtocolNet {
|
||||
|
||||
}
|
||||
|
||||
private protected void ClearRegisterVals() {
|
||||
|
||||
for (int i = 0; i < RegistersUnderlying.Count; i++) {
|
||||
|
||||
var reg = (IRegisterInternal)RegistersUnderlying[i];
|
||||
reg.ClearValue();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void SetUpstreamStopWatchStart () {
|
||||
|
||||
if (speedStopwatchUpstr == null) {
|
||||
|
||||
@@ -168,7 +168,6 @@ namespace MewtocolNet {
|
||||
|
||||
if((bool)register.Value != resultBitArray[k]) {
|
||||
register.SetValueFromPLC(resultBitArray[k]);
|
||||
InvokeRegisterChanged(register);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -192,7 +191,6 @@ namespace MewtocolNet {
|
||||
|
||||
if (lastVal != readout) {
|
||||
rwReg.SetValueFromPLC(readout);
|
||||
InvokeRegisterChanged(reg);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -218,16 +216,21 @@ namespace MewtocolNet {
|
||||
string propName = prop.Name;
|
||||
foreach (var attr in attributes) {
|
||||
|
||||
if (attr is RegisterAttribute cAttribute && prop.PropertyType.IsAllowedPlcCastingType()) {
|
||||
if (attr is RegisterAttribute cAttribute) {
|
||||
|
||||
if(!prop.PropertyType.IsAllowedPlcCastingType()) {
|
||||
throw new MewtocolException($"The register attribute property type is not allowed ({prop.PropertyType})");
|
||||
}
|
||||
|
||||
var dotnetType = prop.PropertyType;
|
||||
|
||||
AddRegister(new RegisterBuildInfo {
|
||||
mewAddress = cAttribute.MewAddress,
|
||||
memoryAddress = cAttribute.MemoryArea,
|
||||
specialAddress = cAttribute.SpecialAddress,
|
||||
memorySizeBytes = cAttribute.ByteLength,
|
||||
registerType = cAttribute.RegisterType,
|
||||
dotnetCastType = dotnetType,
|
||||
dotnetCastType = dotnetType.IsEnum ? dotnetType.UnderlyingSystemType : dotnetType,
|
||||
collectionType = collection.GetType(),
|
||||
name = prop.Name,
|
||||
});
|
||||
@@ -345,6 +348,9 @@ namespace MewtocolNet {
|
||||
|
||||
#region Register Adding
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void AddRegister(IRegister register) => AddRegister(register as BaseRegister);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void AddRegister(BaseRegister register) {
|
||||
|
||||
@@ -354,14 +360,14 @@ namespace MewtocolNet {
|
||||
if (CheckDuplicateNameRegister(register))
|
||||
throw MewtocolException.DupeNameRegister(register);
|
||||
|
||||
if (CheckOverlappingRegister(register, out var regB))
|
||||
throw MewtocolException.OverlappingRegister(register, regB);
|
||||
|
||||
register.attachedInterface = this;
|
||||
RegistersUnderlying.Add(register);
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void AddRegister(IRegister register) => AddRegister(register as BaseRegister);
|
||||
|
||||
internal void AddRegister (RegisterBuildInfo buildInfo) {
|
||||
|
||||
var builtRegister = buildInfo.Build();
|
||||
@@ -379,6 +385,9 @@ namespace MewtocolNet {
|
||||
if(CheckDuplicateNameRegister(builtRegister))
|
||||
throw MewtocolException.DupeNameRegister(builtRegister);
|
||||
|
||||
if (CheckOverlappingRegister(builtRegister, out var regB))
|
||||
throw MewtocolException.OverlappingRegister(builtRegister, regB);
|
||||
|
||||
builtRegister.attachedInterface = this;
|
||||
RegistersUnderlying.Add(builtRegister);
|
||||
|
||||
@@ -406,6 +415,38 @@ namespace MewtocolNet {
|
||||
|
||||
}
|
||||
|
||||
private bool CheckOverlappingRegister (IRegisterInternal instance, out IRegisterInternal regB) {
|
||||
|
||||
//ignore bool registers, they have their own address spectrum
|
||||
regB = null;
|
||||
if (instance is BoolRegister) return false;
|
||||
|
||||
uint addressFrom = instance.MemoryAddress;
|
||||
uint addressTo = addressFrom + instance.GetRegisterAddressLen();
|
||||
|
||||
var foundOverlapping = RegistersInternal.FirstOrDefault(x => {
|
||||
|
||||
//ignore bool registers, they have their own address spectrum
|
||||
if (x is BoolRegister) return false;
|
||||
|
||||
uint addressF = x.MemoryAddress;
|
||||
uint addressT = addressF + x.GetRegisterAddressLen();
|
||||
|
||||
bool matchingBaseAddress = addressFrom < addressT && addressF < addressTo;
|
||||
|
||||
return matchingBaseAddress;
|
||||
|
||||
});
|
||||
|
||||
if (foundOverlapping != null) {
|
||||
regB = foundOverlapping;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Register accessing
|
||||
@@ -428,6 +469,18 @@ namespace MewtocolNet {
|
||||
|
||||
#region Event Invoking
|
||||
|
||||
private protected void ClearRegisterVals() {
|
||||
|
||||
for (int i = 0; i < RegistersUnderlying.Count; i++) {
|
||||
|
||||
var reg = (IRegisterInternal)RegistersUnderlying[i];
|
||||
reg.ClearValue();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
internal void PropertyRegisterWasSet(string propName, object value) {
|
||||
|
||||
_ = SetRegisterAsync(GetRegister(propName), value);
|
||||
|
||||
@@ -64,12 +64,8 @@ namespace MewtocolNet {
|
||||
|
||||
#region Operation mode changing
|
||||
|
||||
/// <summary>
|
||||
/// Changes the PLCs operation mode to the given one
|
||||
/// </summary>
|
||||
/// <param name="mode">The mode to change to</param>
|
||||
/// <returns>The success state of the write operation</returns>
|
||||
public async Task<bool> SetOperationMode (bool setRun) {
|
||||
/// <inheritdoc/>
|
||||
public async Task<bool> SetOperationModeAsync (bool setRun) {
|
||||
|
||||
string modeChar = setRun ? "R" : "P";
|
||||
|
||||
@@ -77,7 +73,7 @@ namespace MewtocolNet {
|
||||
var result = await SendCommandAsync(requeststring);
|
||||
|
||||
if (result.Success) {
|
||||
Logger.Log($"operation mode was changed to {(setRun ? "Run" : "Prog")}", LogLevel.Info, this);
|
||||
Logger.Log($"Operation mode was changed to {(setRun ? "Run" : "Prog")}", LogLevel.Info, this);
|
||||
} else {
|
||||
Logger.Log("Operation mode change failed", LogLevel.Error, this);
|
||||
}
|
||||
@@ -116,14 +112,15 @@ namespace MewtocolNet {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the bytes from the start adress for counts byte length
|
||||
/// Reads the bytes from the start adress for counts byte length,
|
||||
/// doesn't block the receive thread
|
||||
/// </summary>
|
||||
/// <param name="start">Start adress</param>
|
||||
/// <param name="count">Number of bytes to get</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>
|
||||
/// <returns>A byte array or null of there was an error</returns>
|
||||
public async Task<byte[]> ReadByteRange(int start, int count, bool flipBytes = true, Action<double> onProgress = null) {
|
||||
public async Task<byte[]> ReadByteRangeNonBlocking (int start, int count, bool flipBytes = true, Action<double> onProgress = null) {
|
||||
|
||||
var byteList = new List<byte>();
|
||||
|
||||
@@ -131,12 +128,13 @@ namespace MewtocolNet {
|
||||
if (count % 2 != 0)
|
||||
wordLength++;
|
||||
|
||||
int blockSize = 8;
|
||||
|
||||
//read blocks of max 4 words per msg
|
||||
for (int i = 0; i < wordLength; i += 8) {
|
||||
for (int i = 0; i < wordLength; i += blockSize) {
|
||||
|
||||
int curWordStart = start + i;
|
||||
int curWordEnd = curWordStart + 7;
|
||||
int curWordEnd = curWordStart + blockSize - 1;
|
||||
|
||||
string startStr = curWordStart.ToString().PadLeft(5, '0');
|
||||
string endStr = (curWordEnd).ToString().PadLeft(5, '0');
|
||||
@@ -146,7 +144,7 @@ namespace MewtocolNet {
|
||||
|
||||
if (result.Success && !string.IsNullOrEmpty(result.Response)) {
|
||||
|
||||
var bytes = result.Response.ParseDTByteString(8 * 4).HexStringToByteArray();
|
||||
var bytes = result.Response.ParseDTByteString(blockSize * 4).HexStringToByteArray();
|
||||
|
||||
if (bytes == null) return null;
|
||||
|
||||
@@ -246,41 +244,18 @@ namespace MewtocolNet {
|
||||
//returns a byte array 1 long and with the byte beeing 0 or 1
|
||||
if (toWriteType == typeof(BoolRegister)) {
|
||||
|
||||
string requeststring = $"%{GetStationNumber()}#WCS{_toWrite.BuildMewtocolQuery()}{(data[0] == 1 ? "1" : "0")}";
|
||||
var result = await SendCommandAsync(requeststring);
|
||||
return result.Success;
|
||||
string reqStr = $"%{GetStationNumber()}#WCS{_toWrite.BuildMewtocolQuery()}{(data[0] == 1 ? "1" : "0")}";
|
||||
var res = await SendCommandAsync(reqStr);
|
||||
return res.Success;
|
||||
|
||||
}
|
||||
|
||||
//writes a byte array 2 bytes or 4 bytes long depending on the data size
|
||||
if (toWriteType.IsGenericType && toWriteType.GetGenericTypeDefinition() == typeof(NumberRegister<>)) {
|
||||
|
||||
string requeststring = $"%{GetStationNumber()}#WD{_toWrite.BuildMewtocolQuery()}{data.ToHexString()}";
|
||||
var result = await SendCommandAsync(requeststring);
|
||||
return result.Success;
|
||||
|
||||
}
|
||||
|
||||
//returns a byte array with variable size
|
||||
if (toWriteType == typeof(BytesRegister)) {
|
||||
|
||||
throw new NotImplementedException("Not imp");
|
||||
|
||||
}
|
||||
|
||||
//writes to the string area
|
||||
if (toWriteType == typeof(StringRegister)) {
|
||||
|
||||
string requeststring = $"%{GetStationNumber()}#WD{_toWrite.BuildMewtocolQuery()}{data.ToHexString()}";
|
||||
var result = await SendCommandAsync(requeststring);
|
||||
return result.Success;
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Register reading / writing
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using MewtocolNet.RegisterBuilding;
|
||||
using System;
|
||||
|
||||
namespace MewtocolNet.RegisterAttributes {
|
||||
|
||||
@@ -10,31 +11,39 @@ namespace MewtocolNet.RegisterAttributes {
|
||||
|
||||
internal RegisterType? RegisterType;
|
||||
|
||||
internal int MemoryArea = 0;
|
||||
internal int ByteLength = 2;
|
||||
internal uint MemoryArea = 0;
|
||||
internal uint ByteLength = 2;
|
||||
internal byte SpecialAddress = 0x0;
|
||||
|
||||
internal BitCount BitCount;
|
||||
internal int AssignedBitIndex = -1;
|
||||
|
||||
internal string MewAddress = null;
|
||||
|
||||
public RegisterAttribute(string mewAddress) {
|
||||
|
||||
MewAddress = mewAddress;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attribute for string type or numeric registers
|
||||
/// </summary>
|
||||
/// <param name="memoryArea">The area in the plcs memory</param>
|
||||
public RegisterAttribute(int memoryArea) {
|
||||
public RegisterAttribute(uint memoryArea) {
|
||||
|
||||
MemoryArea = memoryArea;
|
||||
|
||||
}
|
||||
|
||||
public RegisterAttribute(int memoryArea, int byteLength) {
|
||||
public RegisterAttribute(uint memoryArea, uint byteLength) {
|
||||
|
||||
MemoryArea = memoryArea;
|
||||
ByteLength = byteLength;
|
||||
|
||||
}
|
||||
|
||||
public RegisterAttribute(int memoryArea, BitCount bitCount) {
|
||||
public RegisterAttribute(uint memoryArea, BitCount bitCount) {
|
||||
|
||||
MemoryArea = memoryArea;
|
||||
BitCount = bitCount;
|
||||
@@ -44,7 +53,7 @@ namespace MewtocolNet.RegisterAttributes {
|
||||
|
||||
}
|
||||
|
||||
public RegisterAttribute(int memoryArea, BitCount bitCount, int bitIndex) {
|
||||
public RegisterAttribute(uint memoryArea, BitCount bitCount, int bitIndex) {
|
||||
|
||||
MemoryArea = memoryArea;
|
||||
BitCount = bitCount;
|
||||
@@ -68,7 +77,7 @@ namespace MewtocolNet.RegisterAttributes {
|
||||
/// <summary>
|
||||
/// Attribute for boolean registers
|
||||
/// </summary>
|
||||
public RegisterAttribute(IOType type, int memoryArea, byte spAdress = 0x0) {
|
||||
public RegisterAttribute(IOType type, uint memoryArea, byte spAdress = 0x0) {
|
||||
|
||||
MemoryArea = memoryArea;
|
||||
RegisterType = (RegisterType)(int)type;
|
||||
|
||||
267
MewtocolNet/RegisterBuilding/BuilderStep.cs
Normal file
267
MewtocolNet/RegisterBuilding/BuilderStep.cs
Normal file
@@ -0,0 +1,267 @@
|
||||
using MewtocolNet.Registers;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace MewtocolNet.RegisterBuilding {
|
||||
|
||||
public static class BuilderStepExtensions {
|
||||
|
||||
public static BuilderStep<T> AsType<T> (this BuilderStepBase baseStep) {
|
||||
|
||||
if (!typeof(T).IsAllowedPlcCastingType()) {
|
||||
|
||||
throw new NotSupportedException($"The dotnet type {typeof(T)}, is not supported for PLC type casting");
|
||||
|
||||
}
|
||||
|
||||
var castStp = new BuilderStep<T>();
|
||||
|
||||
if (baseStep.SpecialAddress != null) castStp.SpecialAddress = baseStep.SpecialAddress;
|
||||
|
||||
castStp.Name = baseStep.Name;
|
||||
castStp.RegType = baseStep.RegType;
|
||||
castStp.MemAddress = baseStep.MemAddress;
|
||||
castStp.MemByteSize = baseStep.MemByteSize;
|
||||
castStp.dotnetVarType = typeof(T);
|
||||
castStp.plcVarType = null;
|
||||
castStp.wasCasted = true;
|
||||
|
||||
return castStp;
|
||||
|
||||
}
|
||||
|
||||
public static BuilderStep AsType (this BuilderStepBase baseStep, Type type) {
|
||||
|
||||
if (!type.IsAllowedPlcCastingType()) {
|
||||
|
||||
throw new NotSupportedException($"The dotnet type {type}, is not supported for PLC type casting");
|
||||
|
||||
}
|
||||
|
||||
var castStp = new BuilderStep();
|
||||
|
||||
if (baseStep.SpecialAddress != null) castStp.SpecialAddress = baseStep.SpecialAddress;
|
||||
|
||||
castStp.Name = baseStep.Name;
|
||||
castStp.RegType = baseStep.RegType;
|
||||
castStp.MemAddress = baseStep.MemAddress;
|
||||
castStp.MemByteSize = baseStep.MemByteSize;
|
||||
castStp.dotnetVarType = type;
|
||||
castStp.plcVarType = null;
|
||||
castStp.wasCasted = true;
|
||||
|
||||
return castStp;
|
||||
|
||||
}
|
||||
|
||||
public static IRegister Build (this BuilderStepBase step) {
|
||||
|
||||
//if no casting method in builder was called => autocast the type from the RegisterType
|
||||
if (!step.wasCasted && step.MemByteSize == null) step.AutoType();
|
||||
|
||||
//fallbacks if no casting builder was given
|
||||
BuilderStepBase.GetFallbackDotnetType(step);
|
||||
|
||||
BaseRegister builtReg;
|
||||
|
||||
var bInfo = new RegisterBuildInfo {
|
||||
|
||||
name = step.Name,
|
||||
specialAddress = step.SpecialAddress,
|
||||
memoryAddress = step.MemAddress,
|
||||
memorySizeBytes = step.MemByteSize,
|
||||
memorySizeBits = step.MemBitSize,
|
||||
registerType = step.RegType,
|
||||
dotnetCastType = step.dotnetVarType,
|
||||
|
||||
};
|
||||
|
||||
builtReg = bInfo.Build();
|
||||
|
||||
BuilderStepBase.AddToRegisterList(step, builtReg);
|
||||
|
||||
return builtReg;
|
||||
|
||||
}
|
||||
|
||||
public static IRegister Build<T>(this BuilderStep<T> step) {
|
||||
|
||||
//fallbacks if no casting builder was given
|
||||
BuilderStepBase.GetFallbackDotnetType(step);
|
||||
|
||||
BaseRegister builtReg;
|
||||
|
||||
var bInfo = new RegisterBuildInfo {
|
||||
|
||||
name = step.Name,
|
||||
specialAddress = step.SpecialAddress,
|
||||
memoryAddress = step.MemAddress,
|
||||
memorySizeBytes = step.MemByteSize,
|
||||
memorySizeBits = step.MemBitSize,
|
||||
registerType = step.RegType,
|
||||
dotnetCastType = step.dotnetVarType,
|
||||
|
||||
};
|
||||
|
||||
if (step.dotnetVarType.IsEnum) {
|
||||
|
||||
builtReg = bInfo.Build();
|
||||
|
||||
} else {
|
||||
|
||||
builtReg = bInfo.Build();
|
||||
|
||||
}
|
||||
|
||||
BuilderStepBase.AddToRegisterList(step, builtReg);
|
||||
|
||||
return builtReg;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public abstract class BuilderStepBase {
|
||||
|
||||
internal MewtocolInterface forInterface;
|
||||
|
||||
internal bool wasCasted = false;
|
||||
|
||||
internal string OriginalInput;
|
||||
|
||||
internal string Name;
|
||||
internal RegisterType RegType;
|
||||
|
||||
internal uint MemAddress;
|
||||
|
||||
internal uint? MemByteSize;
|
||||
internal ushort? MemBitSize;
|
||||
internal byte? SpecialAddress;
|
||||
|
||||
internal PlcVarType? plcVarType;
|
||||
internal Type dotnetVarType;
|
||||
|
||||
internal BuilderStepBase AutoType() {
|
||||
|
||||
switch (RegType) {
|
||||
case RegisterType.X:
|
||||
case RegisterType.Y:
|
||||
case RegisterType.R:
|
||||
dotnetVarType = typeof(bool);
|
||||
break;
|
||||
case RegisterType.DT:
|
||||
dotnetVarType = typeof(short);
|
||||
break;
|
||||
case RegisterType.DDT:
|
||||
dotnetVarType = typeof(int);
|
||||
break;
|
||||
case RegisterType.DT_BYTE_RANGE:
|
||||
dotnetVarType = typeof(string);
|
||||
break;
|
||||
}
|
||||
|
||||
plcVarType = null;
|
||||
|
||||
wasCasted = true;
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
internal static void GetFallbackDotnetType (BuilderStepBase step) {
|
||||
|
||||
bool isBoolean = step.RegType.IsBoolean();
|
||||
bool isTypeNotDefined = step.plcVarType == null && step.dotnetVarType == null;
|
||||
|
||||
if (isTypeNotDefined && step.RegType == RegisterType.DT) {
|
||||
|
||||
step.dotnetVarType = typeof(short);
|
||||
|
||||
}
|
||||
if (isTypeNotDefined && step.RegType == RegisterType.DDT) {
|
||||
|
||||
step.dotnetVarType = typeof(int);
|
||||
|
||||
} else if (isTypeNotDefined && isBoolean) {
|
||||
|
||||
step.dotnetVarType = typeof(bool);
|
||||
|
||||
} else if (isTypeNotDefined && step.RegType == RegisterType.DT_BYTE_RANGE) {
|
||||
|
||||
step.dotnetVarType = typeof(string);
|
||||
|
||||
}
|
||||
|
||||
if (step.plcVarType != null) {
|
||||
|
||||
step.dotnetVarType = step.plcVarType.Value.GetDefaultDotnetType();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal static void AddToRegisterList(BuilderStepBase step, BaseRegister instance) {
|
||||
|
||||
if (step.forInterface == null) return;
|
||||
|
||||
step.forInterface.AddRegister(instance);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class BuilderStep<T> : BuilderStepBase { }
|
||||
|
||||
public class BuilderStep : BuilderStepBase {
|
||||
|
||||
public BuilderStep AsPlcType (PlcVarType varType) {
|
||||
|
||||
dotnetVarType = null;
|
||||
plcVarType = varType;
|
||||
|
||||
wasCasted = true;
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
public BuilderStep AsBytes (uint byteLength) {
|
||||
|
||||
if (RegType != RegisterType.DT) {
|
||||
|
||||
throw new NotSupportedException($"Cant use the {nameof(AsBytes)} converter on a non {nameof(RegisterType.DT)} register");
|
||||
|
||||
}
|
||||
|
||||
MemByteSize = byteLength;
|
||||
dotnetVarType = typeof(byte[]);
|
||||
plcVarType = null;
|
||||
|
||||
wasCasted = true;
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
public BuilderStep AsBits(ushort bitCount = 16) {
|
||||
|
||||
if (RegType != RegisterType.DT) {
|
||||
|
||||
throw new NotSupportedException($"Cant use the {nameof(AsBits)} converter on a non {nameof(RegisterType.DT)} register");
|
||||
|
||||
}
|
||||
|
||||
MemBitSize = bitCount;
|
||||
dotnetVarType = typeof(BitArray);
|
||||
plcVarType = null;
|
||||
|
||||
wasCasted = true;
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
using MewtocolNet.Registers;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace MewtocolNet.RegisterBuilding {
|
||||
|
||||
public static class FinalizerExtensions {
|
||||
|
||||
public static IRegister Build(this RegisterBuilderStep step) {
|
||||
|
||||
//if no casting method in builder was called => autocast the type from the RegisterType
|
||||
if (!step.wasCasted)
|
||||
step.AutoType();
|
||||
|
||||
//fallbacks if no casting builder was given
|
||||
step.GetFallbackDotnetType();
|
||||
|
||||
var builtReg = new RegisterBuildInfo {
|
||||
|
||||
name = step.Name,
|
||||
specialAddress = step.SpecialAddress,
|
||||
memoryAddress = step.MemAddress,
|
||||
memorySizeBytes = step.MemByteSize,
|
||||
registerType = step.RegType,
|
||||
dotnetCastType = step.dotnetVarType,
|
||||
|
||||
}.Build();
|
||||
|
||||
step.AddToRegisterList(builtReg);
|
||||
|
||||
return builtReg;
|
||||
|
||||
}
|
||||
|
||||
private static void GetFallbackDotnetType(this RegisterBuilderStep step) {
|
||||
|
||||
bool isBoolean = step.RegType.IsBoolean();
|
||||
bool isTypeNotDefined = step.plcVarType == null && step.dotnetVarType == null;
|
||||
|
||||
if (isTypeNotDefined && step.RegType == RegisterType.DT) {
|
||||
|
||||
step.dotnetVarType = typeof(short);
|
||||
|
||||
}
|
||||
if (isTypeNotDefined && step.RegType == RegisterType.DDT) {
|
||||
|
||||
step.dotnetVarType = typeof(int);
|
||||
|
||||
} else if (isTypeNotDefined && isBoolean) {
|
||||
|
||||
step.dotnetVarType = typeof(bool);
|
||||
|
||||
} else if (isTypeNotDefined && step.RegType == RegisterType.DT_BYTE_RANGE) {
|
||||
|
||||
step.dotnetVarType = typeof(string);
|
||||
|
||||
}
|
||||
|
||||
if (step.plcVarType != null) {
|
||||
|
||||
step.dotnetVarType = step.plcVarType.Value.GetDefaultDotnetType();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void AddToRegisterList(this RegisterBuilderStep step, BaseRegister instance) {
|
||||
|
||||
if (step.forInterface == null) return;
|
||||
|
||||
step.forInterface.AddRegister(instance);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
public string hardFailReason;
|
||||
|
||||
public RegisterBuilderStep stepData;
|
||||
public BuilderStep stepData;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Common;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
@@ -18,6 +19,7 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
|
||||
(x) => TryBuildBoolean(x),
|
||||
(x) => TryBuildNumericBased(x),
|
||||
(x) => TryBuildByteRangeBased(x),
|
||||
|
||||
};
|
||||
|
||||
@@ -32,7 +34,7 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
public static RegBuilder Factory { get; private set; } = new RegBuilder();
|
||||
|
||||
|
||||
public RegisterBuilderStep FromPlcRegName (string plcAddrName, string name = null) {
|
||||
public BuilderStep FromPlcRegName (string plcAddrName, string name = null) {
|
||||
|
||||
foreach (var method in parseMethods) {
|
||||
|
||||
@@ -45,6 +47,7 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
|
||||
res.stepData.OriginalInput = plcAddrName;
|
||||
res.stepData.forInterface = forInterface;
|
||||
|
||||
return res.stepData;
|
||||
|
||||
} else if(res.state == ParseResultState.FailedHard) {
|
||||
@@ -77,7 +80,7 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
string special = match.Groups["special"].Value;
|
||||
|
||||
IOType regType;
|
||||
int areaAdd = 0;
|
||||
uint areaAdd = 0;
|
||||
byte specialAdd = 0x0;
|
||||
|
||||
//try cast the prefix
|
||||
@@ -90,7 +93,7 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
|
||||
}
|
||||
|
||||
if(!string.IsNullOrEmpty(area) && !int.TryParse(area, out areaAdd) ) {
|
||||
if(!string.IsNullOrEmpty(area) && !uint.TryParse(area, out areaAdd) ) {
|
||||
|
||||
return new ParseResult {
|
||||
state = ParseResultState.FailedHard,
|
||||
@@ -102,7 +105,7 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
//special address not given
|
||||
if(string.IsNullOrEmpty(special) && !string.IsNullOrEmpty(area)) {
|
||||
|
||||
var isAreaInt = int.TryParse(area, NumberStyles.Number, CultureInfo.InvariantCulture, out var areaInt);
|
||||
var isAreaInt = uint.TryParse(area, NumberStyles.Number, CultureInfo.InvariantCulture, out var areaInt);
|
||||
|
||||
if (isAreaInt && areaInt >= 0 && areaInt <= 9) {
|
||||
|
||||
@@ -142,7 +145,11 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
|
||||
return new ParseResult {
|
||||
state = ParseResultState.Success,
|
||||
stepData = new RegisterBuilderStep ((RegisterType)(int)regType, areaAdd, specialAdd),
|
||||
stepData = new BuilderStep {
|
||||
RegType = (RegisterType)(int)regType,
|
||||
MemAddress = areaAdd,
|
||||
SpecialAddress = specialAdd,
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -150,7 +157,7 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
// one to two word registers
|
||||
private static ParseResult TryBuildNumericBased (string plcAddrName) {
|
||||
|
||||
var patternByte = new Regex(@"(?<prefix>DT|DDT)(?<area>[0-9]{1,5})");
|
||||
var patternByte = new Regex(@"^(?<prefix>DT|DDT)(?<area>[0-9]{1,5})$");
|
||||
|
||||
var match = patternByte.Match(plcAddrName);
|
||||
|
||||
@@ -159,12 +166,11 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
state = ParseResultState.FailedSoft
|
||||
};
|
||||
|
||||
|
||||
string prefix = match.Groups["prefix"].Value;
|
||||
string area = match.Groups["area"].Value;
|
||||
|
||||
RegisterType regType;
|
||||
int areaAdd = 0;
|
||||
uint areaAdd = 0;
|
||||
|
||||
//try cast the prefix
|
||||
if (!Enum.TryParse(prefix, out regType)) {
|
||||
@@ -176,7 +182,7 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(area) && !int.TryParse(area, out areaAdd)) {
|
||||
if (!string.IsNullOrEmpty(area) && !uint.TryParse(area, out areaAdd)) {
|
||||
|
||||
return new ParseResult {
|
||||
state = ParseResultState.FailedHard,
|
||||
@@ -187,7 +193,76 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
|
||||
return new ParseResult {
|
||||
state = ParseResultState.Success,
|
||||
stepData = new RegisterBuilderStep(regType, areaAdd),
|
||||
stepData = new BuilderStep {
|
||||
RegType = regType,
|
||||
MemAddress = areaAdd,
|
||||
},
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
// one to two word registers
|
||||
private static ParseResult TryBuildByteRangeBased (string plcAddrName) {
|
||||
|
||||
var split = plcAddrName.Split('-');
|
||||
|
||||
if(split.Length > 2)
|
||||
return new ParseResult {
|
||||
state = ParseResultState.FailedHard,
|
||||
hardFailReason = $"Cannot parse '{plcAddrName}', to many delimters '-'"
|
||||
};
|
||||
|
||||
uint[] addresses = new uint[2];
|
||||
|
||||
for (int i = 0; i < split.Length; i++) {
|
||||
|
||||
string addr = split[i];
|
||||
var patternByte = new Regex(@"(?<prefix>DT|DDT)(?<area>[0-9]{1,5})");
|
||||
|
||||
var match = patternByte.Match(addr);
|
||||
|
||||
if (!match.Success)
|
||||
return new ParseResult {
|
||||
state = ParseResultState.FailedSoft
|
||||
};
|
||||
|
||||
string prefix = match.Groups["prefix"].Value;
|
||||
string area = match.Groups["area"].Value;
|
||||
|
||||
RegisterType regType;
|
||||
uint areaAdd = 0;
|
||||
|
||||
//try cast the prefix
|
||||
if (!Enum.TryParse(prefix, out regType) || regType != RegisterType.DT) {
|
||||
|
||||
return new ParseResult {
|
||||
state = ParseResultState.FailedHard,
|
||||
hardFailReason = $"Cannot parse '{plcAddrName}', the prefix is not allowed for word range registers"
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(area) && !uint.TryParse(area, out areaAdd)) {
|
||||
|
||||
return new ParseResult {
|
||||
state = ParseResultState.FailedHard,
|
||||
hardFailReason = $"Cannot parse '{plcAddrName}', the area address: '{area}' is wrong"
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
addresses[i] = areaAdd;
|
||||
|
||||
}
|
||||
|
||||
return new ParseResult {
|
||||
state = ParseResultState.Success,
|
||||
stepData = new BuilderStep {
|
||||
RegType = RegisterType.DT_BYTE_RANGE,
|
||||
dotnetVarType = typeof(byte[]),
|
||||
MemAddress = addresses[0],
|
||||
MemByteSize = (addresses[1] - addresses[0] + 1) * 2,
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
using MewtocolNet.Registers;
|
||||
using MewtocolNet.Exceptions;
|
||||
using MewtocolNet.Registers;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Data.Common;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace MewtocolNet.RegisterBuilding {
|
||||
|
||||
internal struct RegisterBuildInfo {
|
||||
|
||||
internal string mewAddress;
|
||||
|
||||
internal string name;
|
||||
internal int memoryAddress;
|
||||
internal int memorySizeBytes;
|
||||
internal uint memoryAddress;
|
||||
|
||||
internal uint? memorySizeBytes;
|
||||
internal ushort? memorySizeBits;
|
||||
internal byte? specialAddress;
|
||||
|
||||
internal RegisterType? registerType;
|
||||
@@ -18,10 +25,37 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
|
||||
internal BaseRegister Build () {
|
||||
|
||||
//Has mew address use this before the default checks
|
||||
if (mewAddress != null) return BuildFromMewAddress();
|
||||
|
||||
//parse enums
|
||||
if (dotnetCastType.IsEnum) {
|
||||
|
||||
//-------------------------------------------
|
||||
//as numeric register with enum target
|
||||
|
||||
var underlying = Enum.GetUnderlyingType(dotnetCastType);
|
||||
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(dotnetCastType);
|
||||
ConstructorInfo constr = myParameterizedSomeClass.GetConstructor(new Type[] { typeof(uint), typeof(string) });
|
||||
|
||||
var parameters = new object[] { memoryAddress, name };
|
||||
var instance = (BaseRegister)constr.Invoke(parameters);
|
||||
|
||||
if (collectionType != null)
|
||||
instance.WithCollectionType(collectionType);
|
||||
|
||||
return instance;
|
||||
|
||||
}
|
||||
|
||||
//parse all others where the type is known
|
||||
RegisterType regType = registerType ?? dotnetCastType.ToRegisterTypeDefault();
|
||||
|
||||
Type registerClassType = dotnetCastType.GetDefaultRegisterHoldingType();
|
||||
|
||||
bool isBytesRegister = !registerClassType.IsGenericType && registerClassType == typeof(BytesRegister);
|
||||
bool isStringRegister = !registerClassType.IsGenericType && registerClassType == typeof(StringRegister);
|
||||
|
||||
@@ -30,7 +64,7 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
//-------------------------------------------
|
||||
//as numeric register with boolean bit target
|
||||
//create a new bregister instance
|
||||
var instance = new BytesRegister(memoryAddress, memorySizeBytes, name);
|
||||
var instance = new BytesRegister(memoryAddress, memorySizeBytes.Value, name);
|
||||
|
||||
if (collectionType != null)
|
||||
instance.WithCollectionType(collectionType);
|
||||
@@ -42,13 +76,11 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
//-------------------------------------------
|
||||
//as numeric register
|
||||
|
||||
var areaAddr = memoryAddress;
|
||||
|
||||
//create a new bregister instance
|
||||
var flags = BindingFlags.Public | BindingFlags.Instance;
|
||||
|
||||
//int _adress, Type _enumType = null, string _name = null
|
||||
var parameters = new object[] { areaAddr, null, name };
|
||||
var parameters = new object[] { memoryAddress, name };
|
||||
var instance = (BaseRegister)Activator.CreateInstance(registerClassType, flags, null, parameters, null);
|
||||
|
||||
if(collectionType != null)
|
||||
@@ -62,7 +94,14 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
|
||||
//-------------------------------------------
|
||||
//as byte range register
|
||||
var instance = new BytesRegister(memoryAddress, memorySizeBytes, name);
|
||||
|
||||
BytesRegister instance;
|
||||
|
||||
if(memorySizeBits != null) {
|
||||
instance = new BytesRegister(memoryAddress, memorySizeBits.Value, name);
|
||||
} else {
|
||||
instance = new BytesRegister(memoryAddress, memorySizeBytes.Value, name);
|
||||
}
|
||||
|
||||
if (collectionType != null)
|
||||
instance.WithCollectionType(collectionType);
|
||||
@@ -106,6 +145,12 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
|
||||
}
|
||||
|
||||
private BaseRegister BuildFromMewAddress () {
|
||||
|
||||
return (BaseRegister)RegBuilder.Factory.FromPlcRegName(mewAddress, name).AsType(dotnetCastType).Build();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,114 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace MewtocolNet.RegisterBuilding {
|
||||
|
||||
public class RegisterBuilderStep {
|
||||
|
||||
internal MewtocolInterface forInterface;
|
||||
|
||||
internal bool wasCasted = false;
|
||||
|
||||
internal string OriginalInput;
|
||||
|
||||
internal string Name;
|
||||
internal RegisterType RegType;
|
||||
internal int MemAddress;
|
||||
internal int MemByteSize;
|
||||
internal byte? SpecialAddress;
|
||||
|
||||
internal PlcVarType? plcVarType;
|
||||
internal Type dotnetVarType;
|
||||
|
||||
public RegisterBuilderStep() => throw new NotSupportedException("Cant make a new instance of RegisterBuilderStep, use the builder pattern");
|
||||
|
||||
internal RegisterBuilderStep(RegisterType regType, int memAddr) {
|
||||
|
||||
RegType = regType;
|
||||
MemAddress = memAddr;
|
||||
|
||||
}
|
||||
|
||||
internal RegisterBuilderStep(RegisterType regType, int memAddr, byte specialAddr) {
|
||||
|
||||
RegType = regType;
|
||||
MemAddress = memAddr;
|
||||
SpecialAddress = specialAddr;
|
||||
|
||||
}
|
||||
|
||||
public RegisterBuilderStep AsPlcType(PlcVarType varType) {
|
||||
|
||||
dotnetVarType = null;
|
||||
plcVarType = varType;
|
||||
|
||||
wasCasted = true;
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
public RegisterBuilderStep AsType<T>() {
|
||||
|
||||
if (!typeof(T).IsAllowedPlcCastingType()) {
|
||||
|
||||
throw new NotSupportedException($"The dotnet type {typeof(T)}, is not supported for PLC type casting");
|
||||
|
||||
}
|
||||
|
||||
dotnetVarType = typeof(T);
|
||||
plcVarType = null;
|
||||
|
||||
wasCasted = true;
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
public RegisterBuilderStep AsBytes (int byteLength) {
|
||||
|
||||
if (RegType != RegisterType.DT) {
|
||||
|
||||
throw new NotSupportedException($"Cant use the AsByte converter on a non DT register");
|
||||
|
||||
}
|
||||
|
||||
MemByteSize = byteLength;
|
||||
dotnetVarType = typeof(byte[]);
|
||||
plcVarType = null;
|
||||
|
||||
wasCasted = true;
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
internal RegisterBuilderStep AutoType() {
|
||||
|
||||
switch (RegType) {
|
||||
case RegisterType.X:
|
||||
case RegisterType.Y:
|
||||
case RegisterType.R:
|
||||
dotnetVarType = typeof(bool);
|
||||
break;
|
||||
case RegisterType.DT:
|
||||
dotnetVarType = typeof(short);
|
||||
break;
|
||||
case RegisterType.DDT:
|
||||
dotnetVarType = typeof(int);
|
||||
break;
|
||||
case RegisterType.DT_BYTE_RANGE:
|
||||
dotnetVarType = typeof(string);
|
||||
break;
|
||||
}
|
||||
|
||||
plcVarType = null;
|
||||
|
||||
wasCasted = true;
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,10 +13,10 @@ namespace MewtocolNet.Registers {
|
||||
public event Action<object> ValueChanged;
|
||||
|
||||
internal MewtocolInterface attachedInterface;
|
||||
internal object lastValue;
|
||||
internal object lastValue = null;
|
||||
internal Type collectionType;
|
||||
internal string name;
|
||||
internal int memoryAddress;
|
||||
internal uint memoryAddress;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public MewtocolInterface AttachedInterface => attachedInterface;
|
||||
@@ -34,18 +34,16 @@ namespace MewtocolNet.Registers {
|
||||
public string Name => name;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string PLCAddressName => GetRegisterPLCName();
|
||||
public string PLCAddressName => GetMewName();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int MemoryAddress => memoryAddress;
|
||||
public uint MemoryAddress => memoryAddress;
|
||||
|
||||
#region Trigger update notify
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
internal void TriggerChangedEvnt(object changed) => ValueChanged?.Invoke(changed);
|
||||
|
||||
public void TriggerNotifyChange() => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Value"));
|
||||
public void TriggerNotifyChange() => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Value)));
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -53,9 +51,14 @@ namespace MewtocolNet.Registers {
|
||||
|
||||
public virtual void SetValueFromPLC(object val) {
|
||||
|
||||
if(lastValue?.ToString() != val?.ToString()) {
|
||||
|
||||
lastValue = val;
|
||||
TriggerChangedEvnt(this);
|
||||
|
||||
TriggerNotifyChange();
|
||||
attachedInterface.InvokeRegisterChanged(this);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -90,7 +93,11 @@ namespace MewtocolNet.Registers {
|
||||
|
||||
public virtual string GetContainerName() => $"{(CollectionType != null ? $"{CollectionType.Name}" : "")}";
|
||||
|
||||
public virtual string GetRegisterPLCName() => $"{GetRegisterString()}{MemoryAddress}";
|
||||
public virtual string GetMewName() => $"{GetRegisterString()}{MemoryAddress}";
|
||||
|
||||
public virtual uint GetRegisterAddressLen() => throw new NotImplementedException();
|
||||
|
||||
public string GetRegisterWordRangeString() => $"{GetMewName()} - {MemoryAddress + GetRegisterAddressLen() - 1}";
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -102,14 +109,24 @@ namespace MewtocolNet.Registers {
|
||||
|
||||
#endregion
|
||||
|
||||
public override string ToString() => $"{GetRegisterPLCName()}{(Name != null ? $" ({Name})" : "")} - Value: {GetValueString()}";
|
||||
protected virtual void CheckAddressOverflow (uint addressStart, uint addressLen) {
|
||||
|
||||
if (addressStart < 0)
|
||||
throw new NotSupportedException("The area address can't be negative");
|
||||
|
||||
if (addressStart + addressLen > 99999)
|
||||
throw new NotSupportedException($"Memory adresses cant be greater than 99999 (DT{addressStart}-{addressStart + addressLen})");
|
||||
|
||||
}
|
||||
|
||||
public override string ToString() => $"{GetMewName()}{(Name != null ? $" ({Name})" : "")} - Value: {GetValueString()}";
|
||||
|
||||
public virtual string ToString(bool additional) {
|
||||
|
||||
if (!additional) return this.ToString();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.AppendLine($"PLC Naming: {GetRegisterPLCName()}");
|
||||
sb.AppendLine($"PLC Naming: {GetMewName()}");
|
||||
sb.AppendLine($"Name: {Name ?? "Not named"}");
|
||||
sb.AppendLine($"Value: {GetValueString()}");
|
||||
sb.AppendLine($"Register Type: {RegisterType}");
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@@ -25,21 +26,9 @@ namespace MewtocolNet.Registers {
|
||||
/// <param name="_name">The custom name</param>
|
||||
/// <exception cref="NotSupportedException"></exception>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public BoolRegister(IOType _io, byte _spAddress = 0x0, int _areaAdress = 0, string _name = null) {
|
||||
public BoolRegister(IOType _io, byte _spAddress = 0x0, uint _areaAdress = 0, string _name = null) {
|
||||
|
||||
if (_areaAdress < 0)
|
||||
throw new NotSupportedException("The area address cant be negative");
|
||||
|
||||
if (_io == IOType.R && _areaAdress >= 512)
|
||||
throw new NotSupportedException("R area addresses cant be greater than 511");
|
||||
|
||||
if ((_io == IOType.X || _io == IOType.Y) && _areaAdress >= 110)
|
||||
throw new NotSupportedException("XY area addresses cant be greater than 110");
|
||||
|
||||
if (_spAddress > 0xF)
|
||||
throw new NotSupportedException("Special address cant be greater 15 or 0xF");
|
||||
|
||||
lastValue = false;
|
||||
lastValue = null;
|
||||
|
||||
memoryAddress = _areaAdress;
|
||||
specialAddress = _spAddress;
|
||||
@@ -47,18 +36,27 @@ namespace MewtocolNet.Registers {
|
||||
|
||||
RegisterType = (RegisterType)(int)_io;
|
||||
|
||||
CheckAddressOverflow(memoryAddress, 0);
|
||||
|
||||
}
|
||||
|
||||
protected override void CheckAddressOverflow(uint addressStart, uint addressLen) {
|
||||
|
||||
if ((int)RegisterType == (int)IOType.R && addressStart >= 512)
|
||||
throw new NotSupportedException("R area addresses cant be greater than 511");
|
||||
|
||||
if (((int)RegisterType == (int)IOType.X || (int)RegisterType == (int)IOType.Y) && addressStart >= 110)
|
||||
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");
|
||||
|
||||
base.CheckAddressOverflow(addressStart, addressLen);
|
||||
|
||||
}
|
||||
|
||||
#region Read / Write
|
||||
|
||||
public override void SetValueFromPLC(object val) {
|
||||
|
||||
lastValue = (bool)val;
|
||||
TriggerChangedEvnt(this);
|
||||
TriggerNotifyChange();
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task<object> ReadAsync() {
|
||||
|
||||
@@ -111,7 +109,7 @@ namespace MewtocolNet.Registers {
|
||||
public override void ClearValue() => SetValueFromPLC(false);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetRegisterPLCName() {
|
||||
public override string GetMewName() {
|
||||
|
||||
var spAdressEnd = SpecialAddress.ToString("X1");
|
||||
|
||||
@@ -131,13 +129,16 @@ namespace MewtocolNet.Registers {
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override uint GetRegisterAddressLen () => 1;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString(bool additional) {
|
||||
|
||||
if (!additional) return this.ToString();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.AppendLine($"PLC Naming: {GetRegisterPLCName()}");
|
||||
sb.AppendLine($"PLC Naming: {GetMewName()}");
|
||||
sb.AppendLine($"Name: {Name ?? "Not named"}");
|
||||
sb.AppendLine($"Value: {GetValueString()}");
|
||||
sb.AppendLine($"Register Type: {RegisterType}");
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Text;
|
||||
@@ -12,35 +13,75 @@ namespace MewtocolNet.Registers {
|
||||
/// </summary>
|
||||
public class BytesRegister : BaseRegister {
|
||||
|
||||
internal int addressLength;
|
||||
internal uint addressLength;
|
||||
/// <summary>
|
||||
/// The rgisters memory length
|
||||
/// </summary>
|
||||
public int AddressLength => addressLength;
|
||||
public uint AddressLength => addressLength;
|
||||
|
||||
internal short ReservedSize { get; set; }
|
||||
internal uint ReservedBytesSize { get; private set; }
|
||||
|
||||
internal ushort? ReservedBitSize { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Defines a register containing a string
|
||||
/// Defines a register containing bytes
|
||||
/// </summary>
|
||||
public BytesRegister(int _address, int _reservedByteSize, string _name = null) {
|
||||
public BytesRegister(uint _address, uint _reservedByteSize, string _name = null) {
|
||||
|
||||
if (_address > 99999) throw new NotSupportedException("Memory adresses cant be greater than 99999");
|
||||
name = _name;
|
||||
memoryAddress = _address;
|
||||
ReservedSize = (short)_reservedByteSize;
|
||||
ReservedBytesSize = _reservedByteSize;
|
||||
|
||||
//calc mem length
|
||||
//because one register is always 1 word (2 bytes) long, if the bytecount is uneven we get the trailing word too
|
||||
var byteSize = _reservedByteSize;
|
||||
if (_reservedByteSize % 2 != 0) byteSize++;
|
||||
var byteSize = ReservedBytesSize;
|
||||
if (ReservedBytesSize % 2 != 0) byteSize++;
|
||||
|
||||
RegisterType = RegisterType.DT_BYTE_RANGE;
|
||||
addressLength = (byteSize / 2) - 1;
|
||||
addressLength = Math.Max((byteSize / 2), 1);
|
||||
|
||||
CheckAddressOverflow(memoryAddress, addressLength);
|
||||
|
||||
lastValue = null;
|
||||
|
||||
}
|
||||
|
||||
public override string GetValueString() => Value == null ? "null" : ((byte[])Value).ToHexString("-");
|
||||
public BytesRegister(uint _address, ushort _reservedBitSize, string _name = null) {
|
||||
|
||||
name = _name;
|
||||
memoryAddress = _address;
|
||||
ReservedBytesSize = (uint)Math.Max(1, _reservedBitSize / 8);
|
||||
ReservedBitSize = _reservedBitSize;
|
||||
|
||||
//calc mem length
|
||||
//because one register is always 1 word (2 bytes) long, if the bytecount is uneven we get the trailing word too
|
||||
var byteSize = ReservedBytesSize;
|
||||
if (ReservedBytesSize % 2 != 0) byteSize++;
|
||||
|
||||
RegisterType = RegisterType.DT_BYTE_RANGE;
|
||||
addressLength = Math.Max((byteSize / 2), 1);
|
||||
|
||||
CheckAddressOverflow(memoryAddress, addressLength);
|
||||
|
||||
lastValue = null;
|
||||
|
||||
}
|
||||
|
||||
public override string GetValueString() {
|
||||
|
||||
if (Value == null) return "null";
|
||||
|
||||
if(Value != null && Value is BitArray bitArr) {
|
||||
|
||||
return bitArr.ToBitString();
|
||||
|
||||
} else {
|
||||
|
||||
return ((byte[])Value).ToHexString("-");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string BuildMewtocolQuery() {
|
||||
@@ -48,7 +89,7 @@ namespace MewtocolNet.Registers {
|
||||
StringBuilder asciistring = new StringBuilder("D");
|
||||
|
||||
asciistring.Append(MemoryAddress.ToString().PadLeft(5, '0'));
|
||||
asciistring.Append((MemoryAddress + AddressLength).ToString().PadLeft(5, '0'));
|
||||
asciistring.Append((MemoryAddress + AddressLength - 1).ToString().PadLeft(5, '0'));
|
||||
|
||||
return asciistring.ToString();
|
||||
}
|
||||
@@ -56,10 +97,20 @@ namespace MewtocolNet.Registers {
|
||||
/// <inheritdoc/>
|
||||
public override void SetValueFromPLC (object val) {
|
||||
|
||||
lastValue = (byte[])val;
|
||||
bool changeTriggerBitArr = val is BitArray bitArr &&
|
||||
lastValue is BitArray bitArr2 &&
|
||||
(bitArr.ToBitString() != bitArr2.ToBitString());
|
||||
|
||||
bool changeTriggerGeneral = (lastValue?.ToString() != val?.ToString());
|
||||
|
||||
if (changeTriggerBitArr || changeTriggerGeneral) {
|
||||
|
||||
lastValue = val;
|
||||
|
||||
TriggerChangedEvnt(this);
|
||||
TriggerNotifyChange();
|
||||
attachedInterface.InvokeRegisterChanged(this);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -69,6 +120,9 @@ namespace MewtocolNet.Registers {
|
||||
/// <inheritdoc/>
|
||||
public override void ClearValue() => SetValueFromPLC(null);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override uint GetRegisterAddressLen() => AddressLength;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task<object> ReadAsync() {
|
||||
|
||||
@@ -77,7 +131,13 @@ namespace MewtocolNet.Registers {
|
||||
var read = await attachedInterface.ReadRawRegisterAsync(this);
|
||||
if (read == null) return null;
|
||||
|
||||
var parsed = PlcValueParser.Parse<byte[]>(this, read);
|
||||
object parsed;
|
||||
|
||||
if(ReservedBitSize != null) {
|
||||
parsed = PlcValueParser.Parse<BitArray>(this, read);
|
||||
} else {
|
||||
parsed = PlcValueParser.Parse<byte[]>(this, read);
|
||||
}
|
||||
|
||||
SetValueFromPLC(parsed);
|
||||
return parsed;
|
||||
@@ -89,8 +149,17 @@ namespace MewtocolNet.Registers {
|
||||
|
||||
if (!attachedInterface.IsConnected) return false;
|
||||
|
||||
var res = await attachedInterface.WriteRawRegisterAsync(this, (byte[])data);
|
||||
byte[] encoded;
|
||||
|
||||
if (ReservedBitSize != null) {
|
||||
encoded = PlcValueParser.Encode(this, (BitArray)data);
|
||||
} else {
|
||||
encoded = PlcValueParser.Encode(this, (byte[])data);
|
||||
}
|
||||
|
||||
var res = await attachedInterface.WriteRawRegisterAsync(this, encoded);
|
||||
if (res) SetValueFromPLC(data);
|
||||
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using MewtocolNet.Registers;
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@@ -37,7 +38,7 @@ namespace MewtocolNet {
|
||||
/// <summary>
|
||||
/// The plc memory address of the register
|
||||
/// </summary>
|
||||
int MemoryAddress { get; }
|
||||
uint MemoryAddress { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the register as the plc representation string
|
||||
@@ -67,7 +68,6 @@ namespace MewtocolNet {
|
||||
/// <returns>True if successfully set</returns>
|
||||
Task<bool> WriteAsync(object data);
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace MewtocolNet {
|
||||
|
||||
object Value { get; }
|
||||
|
||||
int MemoryAddress { get; }
|
||||
uint MemoryAddress { get; }
|
||||
|
||||
// setters
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace MewtocolNet {
|
||||
|
||||
string GetContainerName();
|
||||
|
||||
string GetRegisterPLCName();
|
||||
string GetMewName();
|
||||
|
||||
byte? GetSpecialAddress();
|
||||
|
||||
@@ -47,6 +47,9 @@ namespace MewtocolNet {
|
||||
|
||||
string BuildMewtocolQuery();
|
||||
|
||||
uint GetRegisterAddressLen();
|
||||
|
||||
string GetRegisterWordRangeString();
|
||||
|
||||
//others
|
||||
|
||||
|
||||
@@ -14,66 +14,63 @@ namespace MewtocolNet.Registers {
|
||||
/// <typeparam name="T">The type of the numeric value</typeparam>
|
||||
public class NumberRegister<T> : BaseRegister {
|
||||
|
||||
internal Type enumType { get; set; }
|
||||
|
||||
/// <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 (int _address, string _name = null) {
|
||||
|
||||
if (_address > 99999) throw new NotSupportedException("Memory adresses cant be greater than 99999");
|
||||
public NumberRegister (uint _address, string _name = null) {
|
||||
|
||||
memoryAddress = _address;
|
||||
name = _name;
|
||||
|
||||
Type numType = typeof(T);
|
||||
uint areaLen = 0;
|
||||
|
||||
if (typeof(T).IsEnum) {
|
||||
|
||||
//for enums
|
||||
|
||||
var underlyingType = typeof(T).GetEnumUnderlyingType(); //the numeric type
|
||||
areaLen = (uint)(Marshal.SizeOf(underlyingType) / 2) - 1;
|
||||
|
||||
if (areaLen == 0) RegisterType = RegisterType.DT;
|
||||
if (areaLen == 1) RegisterType = RegisterType.DDT;
|
||||
if (areaLen >= 2) RegisterType = RegisterType.DT_BYTE_RANGE;
|
||||
|
||||
lastValue = null;
|
||||
Console.WriteLine();
|
||||
|
||||
} else {
|
||||
|
||||
//for all others known pre-defined numeric structs
|
||||
|
||||
var allowedTypes = PlcValueParser.GetAllowDotnetTypes();
|
||||
if (!allowedTypes.Contains(numType))
|
||||
throw new NotSupportedException($"The type {numType} is not allowed for Number Registers");
|
||||
|
||||
var areaLen = (Marshal.SizeOf(numType) / 2) - 1;
|
||||
areaLen = (uint)(Marshal.SizeOf(numType) / 2) - 1;
|
||||
RegisterType = areaLen >= 1 ? RegisterType.DDT : RegisterType.DT;
|
||||
|
||||
lastValue = default(T);
|
||||
lastValue = null;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines a register containing a number
|
||||
/// </summary>
|
||||
/// <param name="_address">Memory start adress max 99999</param>
|
||||
/// <param name="_enumType">Enum type to parse as</param>
|
||||
/// <param name="_name">Name of the register</param>
|
||||
public NumberRegister(int _address, Type _enumType, string _name = null) {
|
||||
|
||||
if (_address > 99999) throw new NotSupportedException("Memory adresses cant be greater than 99999");
|
||||
|
||||
memoryAddress = _address;
|
||||
name = _name;
|
||||
|
||||
Type numType = typeof(T);
|
||||
|
||||
var allowedTypes = PlcValueParser.GetAllowDotnetTypes();
|
||||
if (!allowedTypes.Contains(numType))
|
||||
throw new NotSupportedException($"The type {numType} is not allowed for Number Registers");
|
||||
|
||||
var areaLen = (Marshal.SizeOf(numType) / 2) - 1;
|
||||
RegisterType = areaLen >= 1 ? RegisterType.DDT : RegisterType.DT;
|
||||
|
||||
enumType = _enumType;
|
||||
lastValue = default(T);
|
||||
CheckAddressOverflow(memoryAddress, areaLen);
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void SetValueFromPLC(object val) {
|
||||
|
||||
if (lastValue?.ToString() != val?.ToString()) {
|
||||
|
||||
lastValue = (T)val;
|
||||
TriggerChangedEvnt(this);
|
||||
|
||||
TriggerNotifyChange();
|
||||
attachedInterface.InvokeRegisterChanged(this);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -93,71 +90,42 @@ namespace MewtocolNet.Registers {
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetAsPLC() => ((TimeSpan)Value).AsPLCTime();
|
||||
public override string GetAsPLC() {
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetValueString() {
|
||||
|
||||
if(typeof(T) == typeof(TimeSpan)) {
|
||||
|
||||
return $"{Value} [{((TimeSpan)Value).AsPLCTime()}]";
|
||||
|
||||
}
|
||||
|
||||
//is number or bitwise
|
||||
if (enumType == null) {
|
||||
|
||||
return $"{Value}";
|
||||
|
||||
}
|
||||
|
||||
//is enum
|
||||
var dict = new Dictionary<int, string>();
|
||||
|
||||
foreach (var name in Enum.GetNames(enumType)) {
|
||||
|
||||
int enumKey = (int)Enum.Parse(enumType, name);
|
||||
if (!dict.ContainsKey(enumKey)) {
|
||||
dict.Add(enumKey, name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (enumType != null && Value is short shortVal) {
|
||||
|
||||
if (dict.ContainsKey(shortVal)) {
|
||||
|
||||
return $"{Value} ({dict[shortVal]})";
|
||||
|
||||
} else {
|
||||
|
||||
return $"{Value} (Missing Enum)";
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (enumType != null && Value is int intVal) {
|
||||
|
||||
if (dict.ContainsKey(intVal)) {
|
||||
|
||||
return $"{Value} ({dict[intVal]})";
|
||||
|
||||
} else {
|
||||
|
||||
return $"{Value} (Missing Enum)";
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
if (typeof(T) == typeof(TimeSpan)) return ((TimeSpan)Value).ToPlcTime();
|
||||
|
||||
return Value.ToString();
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetValueString() {
|
||||
|
||||
if(Value != null && typeof(T) == typeof(TimeSpan)) {
|
||||
|
||||
return $"{Value} [{((TimeSpan)Value).ToPlcTime()}]";
|
||||
|
||||
}
|
||||
|
||||
if (Value != null && typeof(T).IsEnum) {
|
||||
|
||||
var underlying = Enum.GetUnderlyingType(typeof(T));
|
||||
object val = Convert.ChangeType(Value, underlying);
|
||||
|
||||
return $"{Value} [{val}]";
|
||||
|
||||
}
|
||||
|
||||
return Value?.ToString() ?? "null";
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void ClearValue() => SetValueFromPLC(default(T));
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override uint GetRegisterAddressLen() => (uint)(RegisterType == RegisterType.DT ? 1 : 2);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task<object> ReadAsync() {
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
using System;
|
||||
using MewtocolNet.Exceptions;
|
||||
using MewtocolNet.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
@@ -16,20 +19,23 @@ namespace MewtocolNet.Registers {
|
||||
|
||||
internal short UsedSize { get; set; }
|
||||
|
||||
internal int WordsSize { get; set; }
|
||||
internal uint WordsSize { get; set; }
|
||||
|
||||
private bool isCalibrated = false;
|
||||
private bool isCalibratedFromPlc = false;
|
||||
|
||||
/// <summary>
|
||||
/// Defines a register containing a string
|
||||
/// </summary>
|
||||
public StringRegister (int _address, string _name = null) {
|
||||
public StringRegister (uint _address, string _name = null) {
|
||||
|
||||
if (_address > 99999) throw new NotSupportedException("Memory adresses cant be greater than 99999");
|
||||
name = _name;
|
||||
memoryAddress = _address;
|
||||
RegisterType = RegisterType.DT_BYTE_RANGE;
|
||||
|
||||
CheckAddressOverflow(memoryAddress, 0);
|
||||
|
||||
lastValue = null;
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -38,7 +44,7 @@ namespace MewtocolNet.Registers {
|
||||
StringBuilder asciistring = new StringBuilder("D");
|
||||
|
||||
asciistring.Append(MemoryAddress.ToString().PadLeft(5, '0'));
|
||||
asciistring.Append((MemoryAddress + WordsSize - 1).ToString().PadLeft(5, '0'));
|
||||
asciistring.Append((MemoryAddress + Math.Max(1, WordsSize) - 1).ToString().PadLeft(5, '0'));
|
||||
|
||||
return asciistring.ToString();
|
||||
}
|
||||
@@ -49,10 +55,14 @@ namespace MewtocolNet.Registers {
|
||||
/// <inheritdoc/>
|
||||
public override void SetValueFromPLC (object val) {
|
||||
|
||||
if (!val.Equals(lastValue)) {
|
||||
|
||||
lastValue = (string)val;
|
||||
|
||||
TriggerChangedEvnt(this);
|
||||
TriggerNotifyChange();
|
||||
attachedInterface.InvokeRegisterChanged(this);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -62,6 +72,9 @@ namespace MewtocolNet.Registers {
|
||||
/// <inheritdoc/>
|
||||
public override void ClearValue() => SetValueFromPLC("");
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override uint GetRegisterAddressLen() => Math.Max(1, WordsSize);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task<object> ReadAsync() {
|
||||
|
||||
@@ -69,25 +82,39 @@ namespace MewtocolNet.Registers {
|
||||
|
||||
//get the string params first
|
||||
|
||||
if(!isCalibrated) await Calibrate();
|
||||
if(!isCalibratedFromPlc) await CalibrateFromPLC();
|
||||
|
||||
var read = await attachedInterface.ReadRawRegisterAsync(this);
|
||||
if (read == null) return null;
|
||||
|
||||
return PlcValueParser.Parse<string>(this, read);
|
||||
var parsed = PlcValueParser.Parse<string>(this, read);
|
||||
|
||||
SetValueFromPLC(parsed);
|
||||
|
||||
return parsed;
|
||||
|
||||
}
|
||||
|
||||
private async Task Calibrate () {
|
||||
private async Task CalibrateFromPLC () {
|
||||
|
||||
Logger.Log($"Calibrating string ({PLCAddressName}) from PLC source", LogLevel.Verbose, attachedInterface);
|
||||
|
||||
//get the string describer bytes
|
||||
var bytes = await attachedInterface.ReadByteRange(MemoryAddress, 4, false);
|
||||
var bytes = await attachedInterface.ReadByteRangeNonBlocking((int)MemoryAddress, 4, false);
|
||||
|
||||
if (bytes == null || bytes.Length == 0 || bytes.All(x => x == 0x0)) {
|
||||
|
||||
throw new MewtocolException($"The string register ({PLCAddressName}) doesn't exist in the PLC program");
|
||||
|
||||
}
|
||||
|
||||
ReservedSize = BitConverter.ToInt16(bytes, 0);
|
||||
UsedSize = BitConverter.ToInt16(bytes, 2);
|
||||
WordsSize = 2 + (ReservedSize + 1) / 2;
|
||||
WordsSize = Math.Max(0, (uint)(2 + (ReservedSize + 1) / 2));
|
||||
|
||||
isCalibrated = true;
|
||||
CheckAddressOverflow(memoryAddress, WordsSize);
|
||||
|
||||
isCalibratedFromPlc = true;
|
||||
|
||||
}
|
||||
|
||||
@@ -96,10 +123,18 @@ namespace MewtocolNet.Registers {
|
||||
|
||||
if (!attachedInterface.IsConnected) return false;
|
||||
|
||||
if (!isCalibratedFromPlc) {
|
||||
|
||||
//try to calibrate from plc
|
||||
await CalibrateFromPLC();
|
||||
|
||||
}
|
||||
|
||||
var res = await attachedInterface.WriteRawRegisterAsync(this, PlcValueParser.Encode(this, (string)data));
|
||||
|
||||
if (res) {
|
||||
UsedSize = (short)((string)Value).Length;
|
||||
SetValueFromPLC(data);
|
||||
UsedSize = (short)((string)Value).Length;
|
||||
}
|
||||
|
||||
return res;
|
||||
|
||||
@@ -26,10 +26,11 @@ namespace MewtocolNet.TypeConversion {
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// All conversions for reading dataf from and to the plc
|
||||
/// All conversions for reading data from and to the plc, excluding Enum types
|
||||
/// </summary>
|
||||
internal static List<IPlcTypeConverter> items = new List<IPlcTypeConverter> {
|
||||
|
||||
//default bool R conversion
|
||||
new PlcTypeConversion<bool>(RegisterType.R) {
|
||||
HoldingRegisterType = typeof(BoolRegister),
|
||||
PlcVarType = PlcVarType.BOOL,
|
||||
@@ -43,6 +44,7 @@ namespace MewtocolNet.TypeConversion {
|
||||
|
||||
},
|
||||
},
|
||||
//default bool X conversion
|
||||
new PlcTypeConversion<bool>(RegisterType.X) {
|
||||
HoldingRegisterType = typeof(BoolRegister),
|
||||
PlcVarType = PlcVarType.BOOL,
|
||||
@@ -56,6 +58,7 @@ namespace MewtocolNet.TypeConversion {
|
||||
|
||||
},
|
||||
},
|
||||
//default bool Y conversion
|
||||
new PlcTypeConversion<bool>(RegisterType.Y) {
|
||||
HoldingRegisterType = typeof(BoolRegister),
|
||||
PlcVarType = PlcVarType.BOOL,
|
||||
@@ -69,6 +72,7 @@ namespace MewtocolNet.TypeConversion {
|
||||
|
||||
},
|
||||
},
|
||||
//default short DT conversion
|
||||
new PlcTypeConversion<short>(RegisterType.DT) {
|
||||
HoldingRegisterType = typeof(NumberRegister<short>),
|
||||
PlcVarType = PlcVarType.INT,
|
||||
@@ -82,6 +86,7 @@ namespace MewtocolNet.TypeConversion {
|
||||
|
||||
},
|
||||
},
|
||||
//default ushort DT conversion
|
||||
new PlcTypeConversion<ushort>(RegisterType.DT) {
|
||||
HoldingRegisterType = typeof(NumberRegister<ushort>),
|
||||
PlcVarType = PlcVarType.UINT,
|
||||
@@ -95,6 +100,7 @@ namespace MewtocolNet.TypeConversion {
|
||||
|
||||
},
|
||||
},
|
||||
//default int DDT conversion
|
||||
new PlcTypeConversion<int>(RegisterType.DDT) {
|
||||
HoldingRegisterType = typeof(NumberRegister<int>),
|
||||
PlcVarType = PlcVarType.DINT,
|
||||
@@ -108,6 +114,7 @@ namespace MewtocolNet.TypeConversion {
|
||||
|
||||
},
|
||||
},
|
||||
//default uint DDT conversion
|
||||
new PlcTypeConversion<uint>(RegisterType.DDT) {
|
||||
HoldingRegisterType = typeof(NumberRegister<uint>),
|
||||
PlcVarType = PlcVarType.UDINT,
|
||||
@@ -121,6 +128,7 @@ namespace MewtocolNet.TypeConversion {
|
||||
|
||||
},
|
||||
},
|
||||
//default float DDT conversion
|
||||
new PlcTypeConversion<float>(RegisterType.DDT) {
|
||||
HoldingRegisterType = typeof(NumberRegister<float>),
|
||||
PlcVarType = PlcVarType.REAL,
|
||||
@@ -139,6 +147,7 @@ namespace MewtocolNet.TypeConversion {
|
||||
|
||||
},
|
||||
},
|
||||
//default TimeSpan DDT conversion
|
||||
new PlcTypeConversion<TimeSpan>(RegisterType.DDT) {
|
||||
HoldingRegisterType = typeof(NumberRegister<TimeSpan>),
|
||||
PlcVarType = PlcVarType.TIME,
|
||||
@@ -157,11 +166,13 @@ namespace MewtocolNet.TypeConversion {
|
||||
|
||||
},
|
||||
},
|
||||
new PlcTypeConversion<byte[]>(RegisterType.DT) {
|
||||
//default byte array DT Range conversion
|
||||
new PlcTypeConversion<byte[]>(RegisterType.DT_BYTE_RANGE) {
|
||||
HoldingRegisterType = typeof(BytesRegister),
|
||||
FromRaw = (reg, bytes) => bytes,
|
||||
ToRaw = (reg, value) => value,
|
||||
},
|
||||
//default string DT Range conversion
|
||||
new PlcTypeConversion<string>(RegisterType.DT_BYTE_RANGE) {
|
||||
HoldingRegisterType = typeof(StringRegister),
|
||||
PlcVarType = PlcVarType.STRING,
|
||||
@@ -195,12 +206,16 @@ namespace MewtocolNet.TypeConversion {
|
||||
|
||||
},
|
||||
},
|
||||
new PlcTypeConversion<BitArray>(RegisterType.DT) {
|
||||
//default bitn array DT conversion
|
||||
new PlcTypeConversion<BitArray>(RegisterType.DT_BYTE_RANGE) {
|
||||
HoldingRegisterType = typeof(BytesRegister),
|
||||
PlcVarType = PlcVarType.WORD,
|
||||
FromRaw = (reg, bytes) => {
|
||||
|
||||
var byteReg = (BytesRegister)reg;
|
||||
|
||||
BitArray bitAr = new BitArray(bytes);
|
||||
bitAr.Length = (int)byteReg.ReservedBitSize;
|
||||
|
||||
return bitAr;
|
||||
|
||||
},
|
||||
@@ -208,6 +223,15 @@ namespace MewtocolNet.TypeConversion {
|
||||
|
||||
byte[] ret = new byte[(value.Length - 1) / 8 + 1];
|
||||
value.CopyTo(ret, 0);
|
||||
|
||||
if(ret.Length % 2 != 0) {
|
||||
|
||||
var lst = ret.ToList();
|
||||
lst.Add(0);
|
||||
ret = lst.ToArray();
|
||||
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
},
|
||||
|
||||
@@ -12,9 +12,22 @@ namespace MewtocolNet {
|
||||
|
||||
private static List<IPlcTypeConverter> conversions => Conversions.items;
|
||||
|
||||
public static T Parse<T>(IRegister register, byte[] bytes) {
|
||||
internal static T Parse<T> (IRegister register, byte[] bytes) {
|
||||
|
||||
var converter = conversions.FirstOrDefault(x => x.GetDotnetType() == typeof(T));
|
||||
IPlcTypeConverter converter;
|
||||
|
||||
//special case for enums
|
||||
if(typeof(T).IsEnum) {
|
||||
|
||||
var underlyingNumberType = typeof(T).GetEnumUnderlyingType();
|
||||
|
||||
converter = conversions.FirstOrDefault(x => x.GetDotnetType() == underlyingNumberType);
|
||||
|
||||
} else {
|
||||
|
||||
converter = conversions.FirstOrDefault(x => x.GetDotnetType() == typeof(T));
|
||||
|
||||
}
|
||||
|
||||
if (converter == null)
|
||||
throw new MewtocolException($"A converter for the dotnet type {typeof(T)} doesn't exist");
|
||||
@@ -23,9 +36,22 @@ namespace MewtocolNet {
|
||||
|
||||
}
|
||||
|
||||
public static byte[] Encode <T>(IRegister register, T value) {
|
||||
internal static byte[] Encode<T> (IRegister register, T value) {
|
||||
|
||||
var converter = conversions.FirstOrDefault(x => x.GetDotnetType() == typeof(T));
|
||||
IPlcTypeConverter converter;
|
||||
|
||||
//special case for enums
|
||||
if (typeof(T).IsEnum) {
|
||||
|
||||
var underlyingNumberType = typeof(T).GetEnumUnderlyingType();
|
||||
|
||||
converter = conversions.FirstOrDefault(x => x.GetDotnetType() == underlyingNumberType);
|
||||
|
||||
} else {
|
||||
|
||||
converter = conversions.FirstOrDefault(x => x.GetDotnetType() == typeof(T));
|
||||
|
||||
}
|
||||
|
||||
if (converter == null)
|
||||
throw new MewtocolException($"A converter for the dotnet type {typeof(T)} doesn't exist");
|
||||
|
||||
@@ -22,18 +22,24 @@ namespace MewtocolNet {
|
||||
|
||||
internal static bool IsAllowedPlcCastingType<T>() {
|
||||
|
||||
if (typeof(T).IsEnum) return true;
|
||||
|
||||
return allowedCastingTypes.Contains(typeof(T));
|
||||
|
||||
}
|
||||
|
||||
internal static bool IsAllowedPlcCastingType(this Type type) {
|
||||
|
||||
if (type.IsEnum) return true;
|
||||
|
||||
return allowedCastingTypes.Contains(type);
|
||||
|
||||
}
|
||||
|
||||
internal static RegisterType ToRegisterTypeDefault(this Type type) {
|
||||
|
||||
if (type.IsEnum) return RegisterType.DT;
|
||||
|
||||
var found = PlcValueParser.GetDefaultRegisterType(type);
|
||||
|
||||
if (found != null) {
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
using MewtocolNet;
|
||||
using MewtocolNet.RegisterAttributes;
|
||||
using MewtocolNet.Registers;
|
||||
using System.Collections;
|
||||
using MewtocolTests.EncapsulatedTests;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace MewtocolTests {
|
||||
|
||||
public class AutomatedPropertyRegisters {
|
||||
public partial class AutomatedPropertyRegisters {
|
||||
|
||||
private readonly ITestOutputHelper output;
|
||||
|
||||
@@ -15,90 +14,14 @@ namespace MewtocolTests {
|
||||
this.output = output;
|
||||
}
|
||||
|
||||
public class TestRegisterCollection : RegisterCollection {
|
||||
|
||||
//corresponds to a R100 boolean register in the PLC
|
||||
//can also be written as R1000 because the last one is a special address
|
||||
[Register(IOType.R, memoryArea: 85, spAdress: 0)]
|
||||
public bool TestBool1 { get; private set; }
|
||||
|
||||
//corresponds to a XD input of the PLC
|
||||
[Register(IOType.X, (byte)0xD)]
|
||||
public bool TestBoolInputXD { get; private set; }
|
||||
|
||||
//corresponds to a DT1101 - DT1104 string register in the PLC with (STRING[4])
|
||||
//[Register(1101, 4)]
|
||||
//public string TestString1 { get; private set; }
|
||||
|
||||
//corresponds to a DT7000 16 bit int register in the PLC
|
||||
[Register(899)]
|
||||
public short TestInt16 { get; private set; }
|
||||
|
||||
[Register(342)]
|
||||
public ushort TestUInt16 { get; private set; }
|
||||
|
||||
//corresponds to a DTD7001 - DTD7002 32 bit int register in the PLC
|
||||
[Register(7001)]
|
||||
public int TestInt32 { get; private set; }
|
||||
|
||||
[Register(765)]
|
||||
public uint TestUInt32 { get; private set; }
|
||||
|
||||
//corresponds to a DTD7001 - DTD7002 32 bit float register in the PLC (REAL)
|
||||
[Register(7003)]
|
||||
public float TestFloat32 { get; private set; }
|
||||
|
||||
//corresponds to a DT7005 - DT7009 string register in the PLC with (STRING[5])
|
||||
[Register(7005, 5)]
|
||||
public string TestString2 { get; private set; }
|
||||
|
||||
//corresponds to a DT7010 as a 16bit word/int and parses the word as single bits
|
||||
[Register(7010)]
|
||||
public BitArray TestBitRegister { get; private set; }
|
||||
|
||||
[Register(8010, BitCount.B32)]
|
||||
public BitArray TestBitRegister32 { get; private set; }
|
||||
|
||||
//corresponds to a DT1204 as a 16bit word/int takes the bit at index 9 and writes it back as a boolean
|
||||
[Register(1204, BitCount.B16, 9)]
|
||||
public bool BitValue { get; private set; }
|
||||
|
||||
[Register(1204, BitCount.B32, 5)]
|
||||
public bool FillTest { get; private set; }
|
||||
|
||||
//corresponds to a DT7012 - DT7013 as a 32bit time value that gets parsed as a timespan (TIME)
|
||||
//the smallest value to communicate to the PLC is 10ms
|
||||
[Register(7012)]
|
||||
public TimeSpan TestTime { get; private set; }
|
||||
|
||||
public enum CurrentState {
|
||||
Undefined = 0,
|
||||
State1 = 1,
|
||||
State2 = 2,
|
||||
//State3 = 3,
|
||||
State4 = 4,
|
||||
State5 = 5,
|
||||
StateBetween = 100,
|
||||
State6 = 6,
|
||||
State7 = 7,
|
||||
}
|
||||
|
||||
[Register(50)]
|
||||
public CurrentState TestEnum16 { get; private set; }
|
||||
|
||||
[Register(51, BitCount.B32)]
|
||||
public CurrentState TestEnum32 { get; private set; }
|
||||
|
||||
}
|
||||
|
||||
private void TestBasicGeneration(IRegisterInternal reg, string propName, object expectValue, int expectAddr, string expectPlcName) {
|
||||
private void Test(IRegisterInternal reg, string propName, uint expectAddr, string expectPlcName) {
|
||||
|
||||
Assert.NotNull(reg);
|
||||
Assert.Equal(propName, reg.Name);
|
||||
Assert.Equal(expectValue, reg.Value);
|
||||
Assert.Null(reg.Value);
|
||||
|
||||
Assert.Equal(expectAddr, reg.MemoryAddress);
|
||||
Assert.Equal(expectPlcName, reg.GetRegisterPLCName());
|
||||
Assert.Equal(expectPlcName, reg.GetMewName());
|
||||
|
||||
output.WriteLine(reg.ToString());
|
||||
|
||||
@@ -106,107 +29,86 @@ namespace MewtocolTests {
|
||||
|
||||
//actual tests
|
||||
|
||||
[Fact(DisplayName = "Boolean R generation")]
|
||||
[Fact(DisplayName = "Boolean generation")]
|
||||
public void BooleanGen() {
|
||||
|
||||
var interf = Mewtocol.Ethernet("192.168.0.1");
|
||||
interf.AddRegisterCollection(new TestRegisterCollection()).WithPoller();
|
||||
interf.AddRegisterCollection(new TestBoolRegisters());
|
||||
|
||||
var register = interf.GetRegister(nameof(TestRegisterCollection.TestBool1));
|
||||
var register1 = interf.GetRegister(nameof(TestBoolRegisters.RType));
|
||||
var register2 = interf.GetRegister(nameof(TestBoolRegisters.XType));
|
||||
|
||||
//test generic properties
|
||||
TestBasicGeneration((IRegisterInternal)register, nameof(TestRegisterCollection.TestBool1), false, 85, "R85");
|
||||
var register3 = interf.GetRegister(nameof(TestBoolRegisters.RType_MewString));
|
||||
|
||||
Test((IRegisterInternal)register1, nameof(TestBoolRegisters.RType), 85, "R85A");
|
||||
Test((IRegisterInternal)register2, nameof(TestBoolRegisters.XType), 0, "XD");
|
||||
|
||||
Test((IRegisterInternal)register3, nameof(TestBoolRegisters.RType_MewString), 85, "R85B");
|
||||
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Boolean input XD generation")]
|
||||
public void BooleanInputGen() {
|
||||
[Fact(DisplayName = "Number 16 bit generation")]
|
||||
public void N16BitGen () {
|
||||
|
||||
var interf = Mewtocol.Ethernet("192.168.0.1");
|
||||
interf.AddRegisterCollection(new TestRegisterCollection()).WithPoller();
|
||||
interf.AddRegisterCollection(new Nums16Bit());
|
||||
|
||||
var register = interf.GetRegister(nameof(TestRegisterCollection.TestBoolInputXD));
|
||||
var register1 = interf.GetRegister(nameof(Nums16Bit.Int16Type));
|
||||
var register2 = interf.GetRegister(nameof(Nums16Bit.UInt16Type));
|
||||
var register3 = interf.GetRegister(nameof(Nums16Bit.Enum16Type));
|
||||
|
||||
var register4 = interf.GetRegister(nameof(Nums16Bit.Int16Type_MewString));
|
||||
var register5 = interf.GetRegister(nameof(Nums16Bit.Enum16Type_MewString));
|
||||
|
||||
//test generic properties
|
||||
TestBasicGeneration((IRegisterInternal)register, nameof(TestRegisterCollection.TestBoolInputXD), false, 0, "XD");
|
||||
Test((IRegisterInternal)register1, nameof(Nums16Bit.Int16Type), 899, "DT899");
|
||||
Test((IRegisterInternal)register2, nameof(Nums16Bit.UInt16Type), 342, "DT342");
|
||||
Test((IRegisterInternal)register3, nameof(Nums16Bit.Enum16Type), 50, "DT50");
|
||||
|
||||
Test((IRegisterInternal)register4, nameof(Nums16Bit.Int16Type_MewString), 900, "DT900");
|
||||
Test((IRegisterInternal)register5, nameof(Nums16Bit.Enum16Type_MewString), 51, "DT51");
|
||||
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Int16 generation")]
|
||||
public void Int16Gen() {
|
||||
[Fact(DisplayName = "Number 32 bit generation")]
|
||||
public void N32BitGen () {
|
||||
|
||||
var interf = Mewtocol.Ethernet("192.168.0.1");
|
||||
interf.AddRegisterCollection(new TestRegisterCollection()).WithPoller();
|
||||
interf.AddRegisterCollection(new Nums32Bit());
|
||||
|
||||
var register = interf.GetRegister(nameof(TestRegisterCollection.TestInt16));
|
||||
var register1 = interf.GetRegister(nameof(Nums32Bit.Int32Type));
|
||||
var register2 = interf.GetRegister(nameof(Nums32Bit.UInt32Type));
|
||||
var register3 = interf.GetRegister(nameof(Nums32Bit.Enum32Type));
|
||||
var register4 = interf.GetRegister(nameof(Nums32Bit.FloatType));
|
||||
var register5 = interf.GetRegister(nameof(Nums32Bit.TimeSpanType));
|
||||
|
||||
var register6 = interf.GetRegister(nameof(Nums32Bit.Enum32Type_MewString));
|
||||
var register7 = interf.GetRegister(nameof(Nums32Bit.TimeSpanType_MewString));
|
||||
|
||||
//test generic properties
|
||||
TestBasicGeneration((IRegisterInternal)register, nameof(TestRegisterCollection.TestInt16), (short)0, 899, "DT899");
|
||||
Test((IRegisterInternal)register1, nameof(Nums32Bit.Int32Type), 7001, "DDT7001");
|
||||
Test((IRegisterInternal)register2, nameof(Nums32Bit.UInt32Type), 765, "DDT765");
|
||||
Test((IRegisterInternal)register3, nameof(Nums32Bit.Enum32Type), 51, "DDT51");
|
||||
Test((IRegisterInternal)register4, nameof(Nums32Bit.FloatType), 7003, "DDT7003");
|
||||
Test((IRegisterInternal)register5, nameof(Nums32Bit.TimeSpanType), 7012, "DDT7012");
|
||||
|
||||
Test((IRegisterInternal)register6, nameof(Nums32Bit.Enum32Type_MewString), 53, "DDT53");
|
||||
Test((IRegisterInternal)register7, nameof(Nums32Bit.TimeSpanType_MewString), 7014, "DDT7014");
|
||||
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "UInt16 generation")]
|
||||
public void UInt16Gen() {
|
||||
[Fact(DisplayName = "String generation")]
|
||||
public void StringGen() {
|
||||
|
||||
var interf = Mewtocol.Ethernet("192.168.0.1");
|
||||
interf.AddRegisterCollection(new TestRegisterCollection()).WithPoller();
|
||||
interf.AddRegisterCollection(new TestStringRegisters());
|
||||
|
||||
var register = interf.GetRegister(nameof(TestRegisterCollection.TestUInt16));
|
||||
var register1 = interf.GetRegister(nameof(TestStringRegisters.StringType));
|
||||
var register2 = interf.GetRegister(nameof(TestStringRegisters.StringType_MewString));
|
||||
|
||||
//test generic properties
|
||||
TestBasicGeneration((IRegisterInternal)register, nameof(TestRegisterCollection.TestUInt16), (ushort)0, 342, "DT342");
|
||||
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Int32 generation")]
|
||||
public void Int32Gen() {
|
||||
|
||||
var interf = Mewtocol.Ethernet("192.168.0.1");
|
||||
interf.AddRegisterCollection(new TestRegisterCollection()).WithPoller();
|
||||
|
||||
var register = interf.GetRegister(nameof(TestRegisterCollection.TestInt32));
|
||||
|
||||
//test generic properties
|
||||
TestBasicGeneration((IRegisterInternal)register, nameof(TestRegisterCollection.TestInt32), (int)0, 7001, "DDT7001");
|
||||
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "UInt32 generation")]
|
||||
public void UInt32Gen() {
|
||||
|
||||
var interf = Mewtocol.Ethernet("192.168.0.1");
|
||||
interf.AddRegisterCollection(new TestRegisterCollection()).WithPoller();
|
||||
|
||||
var register = interf.GetRegister(nameof(TestRegisterCollection.TestUInt32));
|
||||
|
||||
//test generic properties
|
||||
TestBasicGeneration((IRegisterInternal)register, nameof(TestRegisterCollection.TestUInt32), (uint)0, 765, "DDT765");
|
||||
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Float32 generation")]
|
||||
public void Float32Gen() {
|
||||
|
||||
var interf = Mewtocol.Ethernet("192.168.0.1");
|
||||
interf.AddRegisterCollection(new TestRegisterCollection()).WithPoller();
|
||||
|
||||
var register = interf.GetRegister(nameof(TestRegisterCollection.TestFloat32));
|
||||
|
||||
//test generic properties
|
||||
TestBasicGeneration((IRegisterInternal)register, nameof(TestRegisterCollection.TestFloat32), (float)0, 7003, "DDT7003");
|
||||
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "TimeSpan generation")]
|
||||
public void TimespanGen() {
|
||||
|
||||
var interf = Mewtocol.Ethernet("192.168.0.1");
|
||||
interf.AddRegisterCollection(new TestRegisterCollection()).WithPoller();
|
||||
|
||||
var register = interf.GetRegister(nameof(TestRegisterCollection.TestTime));
|
||||
|
||||
//test generic properties
|
||||
TestBasicGeneration((IRegisterInternal)register, nameof(TestRegisterCollection.TestTime), TimeSpan.Zero, 7012, "DDT7012");
|
||||
Test((IRegisterInternal)register1, nameof(TestStringRegisters.StringType), 7005, "DT7005");
|
||||
Test((IRegisterInternal)register2, nameof(TestStringRegisters.StringType_MewString), 7050, "DT7050");
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@ internal class RegisterReadWriteTest {
|
||||
|
||||
public object IntialValue { get; set; }
|
||||
|
||||
public object IntermediateValue { get; set; }
|
||||
|
||||
public object AfterWriteValue { get; set; }
|
||||
|
||||
public string RegisterPlcAddressName { get; set; }
|
||||
|
||||
115
MewtocolTests/EncapsulatedTests/TestRegisterCollection.cs
Normal file
115
MewtocolTests/EncapsulatedTests/TestRegisterCollection.cs
Normal file
@@ -0,0 +1,115 @@
|
||||
using MewtocolNet;
|
||||
using MewtocolNet.RegisterAttributes;
|
||||
using System.Collections;
|
||||
|
||||
namespace MewtocolTests.EncapsulatedTests {
|
||||
|
||||
public enum CurrentState : short {
|
||||
Undefined = 0,
|
||||
State1 = 1,
|
||||
State2 = 2,
|
||||
//State3 = 3, <= leave empty for test purposes
|
||||
State4 = 4,
|
||||
State5 = 5,
|
||||
StateBetween = 100,
|
||||
State6 = 6,
|
||||
State7 = 7,
|
||||
}
|
||||
|
||||
public enum CurrentState32 : int {
|
||||
Undefined = 0,
|
||||
State1 = 1,
|
||||
State2 = 2,
|
||||
//State3 = 3, <= leave empty for test purposes
|
||||
State4 = 4,
|
||||
State5 = 5,
|
||||
StateBetween = 100,
|
||||
State6 = 6,
|
||||
State7 = 7,
|
||||
}
|
||||
|
||||
public class TestBoolRegisters : RegisterCollection {
|
||||
|
||||
[Register(IOType.R, memoryArea: 85, spAdress: 0xA)]
|
||||
public bool RType { get; set; }
|
||||
|
||||
[Register(IOType.X, (byte)0xD)]
|
||||
public bool XType { get; set; }
|
||||
|
||||
[Register("R85B")]
|
||||
public bool RType_MewString { get; set; }
|
||||
|
||||
}
|
||||
|
||||
public class Nums16Bit : RegisterCollection {
|
||||
|
||||
|
||||
[Register(899)]
|
||||
public short Int16Type { get; set; }
|
||||
|
||||
[Register(342)]
|
||||
public ushort UInt16Type { get; set; }
|
||||
|
||||
[Register(50)]
|
||||
public CurrentState Enum16Type { get; set; }
|
||||
|
||||
[Register("DT900")]
|
||||
public short Int16Type_MewString { get; set; }
|
||||
|
||||
[Register("DT51")]
|
||||
public CurrentState Enum16Type_MewString { get; set; }
|
||||
|
||||
}
|
||||
|
||||
public class Nums32Bit : RegisterCollection {
|
||||
|
||||
[Register(7001)]
|
||||
public int Int32Type { get; set; }
|
||||
|
||||
[Register(765)]
|
||||
public uint UInt32Type { get; set; }
|
||||
|
||||
[Register(51)]
|
||||
public CurrentState32 Enum32Type { get; set; }
|
||||
|
||||
[Register(7003)]
|
||||
public float FloatType { get; set; }
|
||||
|
||||
[Register(7012)]
|
||||
public TimeSpan TimeSpanType { get; set; }
|
||||
|
||||
[Register("DDT53")]
|
||||
public CurrentState32 Enum32Type_MewString { get; set; }
|
||||
|
||||
[Register("DDT7014")]
|
||||
public TimeSpan TimeSpanType_MewString { get; set; }
|
||||
|
||||
}
|
||||
|
||||
public class TestStringRegisters : RegisterCollection {
|
||||
|
||||
[Register(7005, 5)]
|
||||
public string? StringType { get; set; }
|
||||
|
||||
[Register("DT7050")]
|
||||
public string? StringType_MewString { get; set; }
|
||||
|
||||
}
|
||||
|
||||
public class TestBitwiseRegisters : RegisterCollection {
|
||||
|
||||
[Register(7010)]
|
||||
public BitArray TestBitRegister { get; set; }
|
||||
|
||||
[Register(8010, BitCount.B32)]
|
||||
public BitArray TestBitRegister32 { get; set; }
|
||||
|
||||
[Register(1204, BitCount.B16, 9)]
|
||||
public bool BitValue { get; set; }
|
||||
|
||||
[Register(1204, BitCount.B32, 5)]
|
||||
public bool FillTest { get; set; }
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,7 +13,7 @@ namespace MewtocolTests {
|
||||
this.output = output;
|
||||
}
|
||||
|
||||
[Fact(DisplayName = nameof(MewtocolHelpers.ToBitString))]
|
||||
[Fact(DisplayName = nameof(PlcFormat.ToBitString))]
|
||||
public void ToBitStringGeneration() {
|
||||
|
||||
var bitarr = new BitArray(16);
|
||||
@@ -91,6 +91,25 @@ namespace MewtocolTests {
|
||||
|
||||
}
|
||||
|
||||
[Fact(DisplayName = nameof(PlcFormat.ParsePlcTime))]
|
||||
public void ParsePlcTime () {
|
||||
|
||||
Assert.Equal(new TimeSpan(5, 30, 30, 15, 10), PlcFormat.ParsePlcTime("T#5d30h30m15s10ms"));
|
||||
Assert.Equal(new TimeSpan(0, 30, 30, 15, 10), PlcFormat.ParsePlcTime("T#30h30m15s10ms"));
|
||||
Assert.Equal(new TimeSpan(0, 1, 30, 15, 10), PlcFormat.ParsePlcTime("T#1h30m15s10ms"));
|
||||
Assert.Equal(new TimeSpan(0, 0, 5, 30, 10), PlcFormat.ParsePlcTime("T#5m30s10ms"));
|
||||
Assert.Throws<NotSupportedException>(() => PlcFormat.ParsePlcTime("T#5m30s5ms"));
|
||||
|
||||
}
|
||||
|
||||
[Fact(DisplayName = nameof(PlcFormat.ToPlcTime))]
|
||||
public void ToPlcTime() {
|
||||
|
||||
Assert.Equal("T#1d6h5m30s10ms", PlcFormat.ToPlcTime(new TimeSpan(0, 30, 5, 30, 10)));
|
||||
Assert.Equal("T#6d5h30m10s", PlcFormat.ToPlcTime(new TimeSpan(6, 5, 30, 10)));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,6 +3,7 @@ using MewtocolNet.Logging;
|
||||
using MewtocolNet.RegisterBuilding;
|
||||
using MewtocolNet.Registers;
|
||||
using MewtocolTests.EncapsulatedTests;
|
||||
using System.Collections;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
@@ -41,15 +42,51 @@ namespace MewtocolTests
|
||||
new RegisterReadWriteTest {
|
||||
TargetRegister = new BoolRegister(IOType.R, 0xA, 10),
|
||||
RegisterPlcAddressName = "R10A",
|
||||
IntialValue = false,
|
||||
IntermediateValue = false,
|
||||
AfterWriteValue = true,
|
||||
},
|
||||
new RegisterReadWriteTest {
|
||||
TargetRegister = new NumberRegister<short>(3000),
|
||||
RegisterPlcAddressName = "DT3000",
|
||||
IntialValue = (short)0,
|
||||
IntermediateValue = (short)0,
|
||||
AfterWriteValue = (short)-513,
|
||||
},
|
||||
new RegisterReadWriteTest {
|
||||
TargetRegister = new NumberRegister<CurrentState>(3001),
|
||||
RegisterPlcAddressName = "DT3001",
|
||||
IntermediateValue = CurrentState.Undefined,
|
||||
AfterWriteValue = CurrentState.State4,
|
||||
},
|
||||
new RegisterReadWriteTest {
|
||||
TargetRegister = new NumberRegister<CurrentState32>(3002),
|
||||
RegisterPlcAddressName = "DDT3002",
|
||||
IntermediateValue = CurrentState32.Undefined,
|
||||
AfterWriteValue = CurrentState32.StateBetween,
|
||||
},
|
||||
new RegisterReadWriteTest {
|
||||
TargetRegister = new NumberRegister<TimeSpan>(3004),
|
||||
RegisterPlcAddressName = "DDT3004",
|
||||
IntermediateValue = TimeSpan.Zero,
|
||||
AfterWriteValue = TimeSpan.FromSeconds(11),
|
||||
},
|
||||
new RegisterReadWriteTest {
|
||||
TargetRegister = new NumberRegister<TimeSpan>(3006),
|
||||
RegisterPlcAddressName = "DDT3006",
|
||||
IntermediateValue = TimeSpan.Zero,
|
||||
AfterWriteValue = PlcFormat.ParsePlcTime("T#50m"),
|
||||
},
|
||||
new RegisterReadWriteTest {
|
||||
TargetRegister = new StringRegister(40),
|
||||
RegisterPlcAddressName = "DT40",
|
||||
IntermediateValue = "Hello",
|
||||
AfterWriteValue = "TestV",
|
||||
},
|
||||
new RegisterReadWriteTest {
|
||||
TargetRegister = RegBuilder.Factory.FromPlcRegName("DT3008").AsBits(5).Build(),
|
||||
RegisterPlcAddressName = "DT3008",
|
||||
IntermediateValue = new BitArray(new bool[] { false, false, false, false, false }),
|
||||
AfterWriteValue = new BitArray(new bool[] { false, true, false, false, false }),
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
@@ -57,16 +94,9 @@ namespace MewtocolTests
|
||||
|
||||
this.output = output;
|
||||
|
||||
Logger.LogLevel = LogLevel.Critical;
|
||||
Logger.OnNewLogMessage((d, l, m) => {
|
||||
|
||||
output.WriteLine($"Mewtocol Logger: {d} {m}");
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Connection cycle client to PLC")]
|
||||
[Fact(DisplayName = "Connection cycle client to PLC (Ethernet)")]
|
||||
public async void TestClientConnection() {
|
||||
|
||||
foreach (var plc in testPlcInformationData) {
|
||||
@@ -87,7 +117,7 @@ namespace MewtocolTests
|
||||
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Reading basic information from PLC")]
|
||||
[Fact(DisplayName = "Reading basic status from PLC (Ethernet)")]
|
||||
public async void TestClientReadPLCStatus() {
|
||||
|
||||
foreach (var plc in testPlcInformationData) {
|
||||
@@ -111,12 +141,19 @@ namespace MewtocolTests
|
||||
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Reading basic information from PLC")]
|
||||
[Fact(DisplayName = "Reading / Writing registers from PLC (Ethernet)")]
|
||||
public async void TestRegisterReadWriteAsync() {
|
||||
|
||||
foreach (var plc in testPlcInformationData) {
|
||||
Logger.LogLevel = LogLevel.Verbose;
|
||||
Logger.OnNewLogMessage((d, l, m) => {
|
||||
|
||||
output.WriteLine($"Testing: {plc.PLCName}\n");
|
||||
output.WriteLine($"{d:HH:mm:ss:fff} {m}");
|
||||
|
||||
});
|
||||
|
||||
var plc = testPlcInformationData[0];
|
||||
|
||||
output.WriteLine($"\n\n --- Testing: {plc.PLCName} ---\n");
|
||||
|
||||
var client = Mewtocol.Ethernet(plc.PLCIP, plc.PLCPort);
|
||||
|
||||
@@ -129,14 +166,23 @@ namespace MewtocolTests
|
||||
await client.ConnectAsync();
|
||||
Assert.True(client.IsConnected);
|
||||
|
||||
//cycle run mode to reset registers to inital
|
||||
await client.SetOperationModeAsync(false);
|
||||
await client.SetOperationModeAsync(true);
|
||||
|
||||
foreach (var testRW in testRegisterRW) {
|
||||
|
||||
var testRegister = client.Registers.First(x => x.PLCAddressName == testRW.RegisterPlcAddressName);
|
||||
|
||||
//test inital val
|
||||
Assert.Equal(testRW.IntialValue, testRegister.Value);
|
||||
Assert.Null(testRegister.Value);
|
||||
|
||||
await testRegister.ReadAsync();
|
||||
|
||||
Assert.Equal(testRW.IntermediateValue, testRegister.Value);
|
||||
|
||||
await testRegister.WriteAsync(testRW.AfterWriteValue);
|
||||
await testRegister.ReadAsync();
|
||||
|
||||
//test after write val
|
||||
Assert.Equal(testRW.AfterWriteValue, testRegister.Value);
|
||||
@@ -150,5 +196,3 @@ namespace MewtocolTests
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using MewtocolNet;
|
||||
using MewtocolNet.RegisterBuilding;
|
||||
using MewtocolNet.Registers;
|
||||
using MewtocolTests.EncapsulatedTests;
|
||||
using System.Collections;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
@@ -164,10 +165,12 @@ public class TestRegisterBuilder {
|
||||
[Fact(DisplayName = "Parsing as Number Register (Casted)")]
|
||||
public void TestRegisterBuildingNumericCasted () {
|
||||
|
||||
var expect = new NumberRegister<short>(303, null);
|
||||
var expect2 = new NumberRegister<int>(10002, null);
|
||||
var expect3 = new NumberRegister<TimeSpan>(400, null);
|
||||
//var expect4 = new NRegister<TimeSpan>(103, null, true);
|
||||
var expect = new NumberRegister<short>(303);
|
||||
var expect2 = new NumberRegister<int>(10002);
|
||||
var expect3 = new NumberRegister<float>(404);
|
||||
var expect4 = new NumberRegister<TimeSpan>(400);
|
||||
var expect5 = new NumberRegister<CurrentState>(203);
|
||||
var expect6 = new NumberRegister<CurrentState32>(204);
|
||||
|
||||
Assert.Equivalent(expect, RegBuilder.Factory.FromPlcRegName("DT303").AsPlcType(PlcVarType.INT).Build());
|
||||
Assert.Equivalent(expect, RegBuilder.Factory.FromPlcRegName("DT303").AsType<short>().Build());
|
||||
@@ -175,20 +178,24 @@ public class TestRegisterBuilder {
|
||||
Assert.Equivalent(expect2, RegBuilder.Factory.FromPlcRegName("DDT10002").AsPlcType(PlcVarType.DINT).Build());
|
||||
Assert.Equivalent(expect2, RegBuilder.Factory.FromPlcRegName("DDT10002").AsType<int>().Build());
|
||||
|
||||
Assert.Equivalent(expect3, RegBuilder.Factory.FromPlcRegName("DDT400").AsPlcType(PlcVarType.TIME).Build());
|
||||
Assert.Equivalent(expect3, RegBuilder.Factory.FromPlcRegName("DDT400").AsType<TimeSpan>().Build());
|
||||
Assert.Equivalent(expect3, RegBuilder.Factory.FromPlcRegName("DDT404").AsPlcType(PlcVarType.REAL).Build());
|
||||
Assert.Equivalent(expect3, RegBuilder.Factory.FromPlcRegName("DDT404").AsType<float>().Build());
|
||||
|
||||
//Assert.Equivalent(expect4, RegBuilder.FromPlcRegName("DT103").AsType<BitArray>().Build());
|
||||
Assert.Equivalent(expect4, RegBuilder.Factory.FromPlcRegName("DDT400").AsPlcType(PlcVarType.TIME).Build());
|
||||
Assert.Equivalent(expect4, RegBuilder.Factory.FromPlcRegName("DDT400").AsType<TimeSpan>().Build());
|
||||
|
||||
Assert.Equivalent(expect5, RegBuilder.Factory.FromPlcRegName("DT203").AsType<CurrentState>().Build());
|
||||
Assert.Equivalent(expect6, RegBuilder.Factory.FromPlcRegName("DT204").AsType<CurrentState32>().Build());
|
||||
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Parsing as Number Register (Auto)")]
|
||||
public void TestRegisterBuildingNumericAuto () {
|
||||
|
||||
var expect = new NumberRegister<short>(303, null);
|
||||
var expect2 = new NumberRegister<int>(10002, null);
|
||||
var expect = new NumberRegister<short>(201);
|
||||
var expect2 = new NumberRegister<int>(10002);
|
||||
|
||||
Assert.Equivalent(expect, RegBuilder.Factory.FromPlcRegName("DT303").Build());
|
||||
Assert.Equivalent(expect, RegBuilder.Factory.FromPlcRegName("DT201").Build());
|
||||
Assert.Equivalent(expect2, RegBuilder.Factory.FromPlcRegName("DDT10002").Build());
|
||||
|
||||
}
|
||||
@@ -196,11 +203,57 @@ public class TestRegisterBuilder {
|
||||
[Fact(DisplayName = "Parsing as Bytes Register (Casted)")]
|
||||
public void TestRegisterBuildingByteRangeCasted () {
|
||||
|
||||
var expect = new BytesRegister(303, 5);
|
||||
|
||||
Assert.Equivalent(expect, RegBuilder.Factory.FromPlcRegName("DT303").AsBytes(5).Build());
|
||||
var expect = new BytesRegister(305, (uint)35);
|
||||
|
||||
Assert.Equal((uint)18, expect.AddressLength);
|
||||
Assert.Equivalent(expect, RegBuilder.Factory.FromPlcRegName("DT305").AsBytes(35).Build());
|
||||
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Parsing as Bytes Register (Auto)")]
|
||||
public void TestRegisterBuildingByteRangeAuto () {
|
||||
|
||||
var expect = new BytesRegister(300, (uint)20 * 2);
|
||||
var actual = (BytesRegister)RegBuilder.Factory.FromPlcRegName("DT300-DT319").Build();
|
||||
|
||||
Assert.Equal((uint)20, expect.AddressLength);
|
||||
Assert.Equivalent(expect, actual);
|
||||
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Parsing as Bit Array")]
|
||||
public void TestRegisterBuildingBitArray () {
|
||||
|
||||
var expect1 = new BytesRegister(311, (ushort)5);
|
||||
var expect2 = new BytesRegister(312, (ushort)16);
|
||||
var expect3 = new BytesRegister(313, (ushort)32);
|
||||
|
||||
var actual1 = (BytesRegister)RegBuilder.Factory.FromPlcRegName("DT311").AsBits(5).Build();
|
||||
var actual2 = (BytesRegister)RegBuilder.Factory.FromPlcRegName("DT312").AsBits(16).Build();
|
||||
var actual3 = (BytesRegister)RegBuilder.Factory.FromPlcRegName("DT313").AsBits(32).Build();
|
||||
|
||||
Assert.Equivalent(expect1, actual1);
|
||||
Assert.Equivalent(expect2, actual2);
|
||||
Assert.Equivalent(expect3, actual3);
|
||||
|
||||
Assert.Equal((uint)1, actual1.AddressLength);
|
||||
Assert.Equal((uint)1, actual2.AddressLength);
|
||||
Assert.Equal((uint)2, actual3.AddressLength);
|
||||
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Parsing as String Register")]
|
||||
public void TestRegisterBuildingString () {
|
||||
|
||||
var expect1 = new StringRegister(314);
|
||||
|
||||
var actual1 = (StringRegister)RegBuilder.Factory.FromPlcRegName("DT314").AsType<string>().Build();
|
||||
|
||||
Assert.Equivalent(expect1, actual1);
|
||||
|
||||
Assert.Equal((uint)0, actual1.WordsSize);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -23,8 +23,8 @@ namespace MewtocolTests {
|
||||
new NumberRegister<uint>(50),
|
||||
new NumberRegister<float>(50),
|
||||
new NumberRegister<TimeSpan>(50),
|
||||
new BytesRegister(50, 30),
|
||||
new BytesRegister(50, 31),
|
||||
new BytesRegister(50, (uint)30),
|
||||
new BytesRegister(50, (uint)31),
|
||||
};
|
||||
|
||||
List<string> expectedIdents = new List<string> {
|
||||
@@ -103,7 +103,7 @@ namespace MewtocolTests {
|
||||
IRegisterInternal? reg = registers[i];
|
||||
string expect = expcectedIdents[i];
|
||||
|
||||
Assert.Equal(expect, reg.GetRegisterPLCName());
|
||||
Assert.Equal(expect, reg.GetMewName());
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user