diff --git a/Examples.WPF/Views/ConnectView.xaml.cs b/Examples.WPF/Views/ConnectView.xaml.cs
index b83915c..b3224f5 100644
--- a/Examples.WPF/Views/ConnectView.xaml.cs
+++ b/Examples.WPF/Views/ConnectView.xaml.cs
@@ -70,8 +70,8 @@ public partial class ConnectView : UserControl {
setting.TryReconnectDelayMs = 2000;
setting.SendReceiveTimeoutMs = 1000;
setting.HeartbeatIntervalMs = 3000;
- setting.MaxDataBlocksPerWrite = 20;
- setting.MaxOptimizationDistance = 10;
+ setting.MaxDataBlocksPerWrite = 128;
+ setting.MaxOptimizationDistance = 20;
})
.WithCustomPollLevels(lvl => {
diff --git a/Examples.WPF/Views/PlcDataView.xaml b/Examples.WPF/Views/PlcDataView.xaml
index 8df66b1..bf7574c 100644
--- a/Examples.WPF/Views/PlcDataView.xaml
+++ b/Examples.WPF/Views/PlcDataView.xaml
@@ -25,6 +25,8 @@
Click="ClickedConnect"/>
+
diff --git a/Examples.WPF/Views/PlcDataView.xaml.cs b/Examples.WPF/Views/PlcDataView.xaml.cs
index d38b2c9..8a635bd 100644
--- a/Examples.WPF/Views/PlcDataView.xaml.cs
+++ b/Examples.WPF/Views/PlcDataView.xaml.cs
@@ -54,6 +54,24 @@ public partial class PlcDataView : UserControl {
}
+ private async void ClickedAddQueueTest(object sender, RoutedEventArgs e) {
+
+ var tasks = new List>();
+
+ for (int i = 0; i < 100; i++) {
+
+ var t = viewModel.Plc.Register.Struct("DT1000").ReadAsync();
+
+ tasks.Add(t);
+
+ }
+
+ var list = await Task.WhenAll(tasks);
+
+ Console.WriteLine();
+
+ }
+
private async void ClickedToggleRunMode(object sender, RoutedEventArgs e) {
await viewModel.Plc.ToggleOperationModeAsync();
diff --git a/MewtocolNet/Events/ReconnectArgs.cs b/MewtocolNet/Events/ReconnectArgs.cs
index 969d559..184e9f0 100644
--- a/MewtocolNet/Events/ReconnectArgs.cs
+++ b/MewtocolNet/Events/ReconnectArgs.cs
@@ -15,6 +15,8 @@ namespace MewtocolNet.Events {
public event PropertyChangedEventHandler PropertyChanged;
+ public event Action Reconnected;
+
public int ReconnectTry { get; internal set; }
public int MaxAttempts { get; internal set; }
@@ -29,6 +31,16 @@ namespace MewtocolNet.Events {
}
}
+ private bool isReconnected;
+
+ public bool IsReconnected {
+ get { return isReconnected; }
+ set {
+ isReconnected = value;
+ OnPropChange();
+ }
+ }
+
private System.Timers.Timer countDownTimer;
internal ReconnectArgs(int currentAttempt, int totalAttempts, TimeSpan delayBetween) {
@@ -62,7 +74,9 @@ namespace MewtocolNet.Events {
internal void ConnectionSuccess () {
+ IsReconnected = true;
StopTimer();
+ Reconnected?.Invoke();
}
diff --git a/MewtocolNet/Helpers/AsyncQueue.cs b/MewtocolNet/Helpers/AsyncQueue.cs
deleted file mode 100644
index 0e05257..0000000
--- a/MewtocolNet/Helpers/AsyncQueue.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MewtocolNet.Helpers {
-
- internal class AsyncQueue {
-
- readonly object _locker = new object();
- readonly WeakReference _lastTask = new WeakReference(null);
-
-
- private List queuedTasks = new List();
-
- //internal Task Enqueue(Func> asyncFunction) {
-
- // lock (_locker) {
-
- // var token = tSource.Token;
-
- // Task lastTask;
- // Task resultTask;
-
- // if (_lastTask.TryGetTarget(out lastTask)) {
- // resultTask = lastTask.ContinueWith(_ => asyncFunction(), token, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Current).Unwrap();
- // } else {
- // resultTask = Task.Run(asyncFunction, token);
- // }
-
- // _lastTask.SetTarget(resultTask);
-
- // return resultTask;
-
- // }
-
- //}
-
- }
-
-}
diff --git a/MewtocolNet/Helpers/MewtocolHelpers.cs b/MewtocolNet/Helpers/MewtocolHelpers.cs
index b5c840e..b7640cf 100644
--- a/MewtocolNet/Helpers/MewtocolHelpers.cs
+++ b/MewtocolNet/Helpers/MewtocolHelpers.cs
@@ -10,6 +10,8 @@ using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using System.Threading;
namespace MewtocolNet {
@@ -18,6 +20,16 @@ namespace MewtocolNet {
///
public static class MewtocolHelpers {
+ #region Async extensions
+
+ internal static Task WhenCanceled(this CancellationToken cancellationToken) {
+ var tcs = new TaskCompletionSource();
+ cancellationToken.Register(s => ((TaskCompletionSource)s).SetResult(true), tcs);
+ return tcs.Task;
+ }
+
+ #endregion
+
#region Byte and string operation helpers
public static T SetFlag(this Enum value, T flag, bool set) {
@@ -143,7 +155,7 @@ namespace MewtocolNet {
_onString = _onString.Replace("\r", "");
- var res = new Regex(@"\%([0-9a-fA-F]{2})\$(?:RD|RP|RC)(?.*)(?..)").Match(_onString);
+ var res = new Regex(@"(?:\%|\<)([0-9a-fA-F]{2})\$(?:RD|RP|RC)(?.*)(?..)").Match(_onString);
if (res.Success) {
string val = res.Groups["data"].Value;
diff --git a/MewtocolNet/IPlc.cs b/MewtocolNet/IPlc.cs
index eb903d1..1bf2bb0 100644
--- a/MewtocolNet/IPlc.cs
+++ b/MewtocolNet/IPlc.cs
@@ -129,12 +129,14 @@ namespace MewtocolNet {
void Disconnect();
///
- /// Sends a command to the PLC then awaits results
- /// The checksum and BCC are appended automatically
+ /// Stops a running reconnect task
///
- /// MEWTOCOL Formatted request string ex: %01#RT
- /// Returns the result
- //Task SendCommandAsync(string _msg, Action onReceiveProgress = null);
+ void StopReconnecting();
+
+ ///
+ /// Adds a task to each reconnect cycle that is run before each individual try
+ ///
+ void WithReconnectTask(Func callback);
///
/// Changes the PLCs operation mode to the given one
diff --git a/MewtocolNet/Mewtocol.cs b/MewtocolNet/Mewtocol.cs
index f7076c3..98cac0d 100644
--- a/MewtocolNet/Mewtocol.cs
+++ b/MewtocolNet/Mewtocol.cs
@@ -401,11 +401,21 @@ namespace MewtocolNet
}
///
- /// Repeats the passed method each time the hearbeat is triggered,
- /// use
+ /// Adds a task to each reconnect cycle that is run before each individual try
+ ///
+ public PostInit WithReconnectTask(Func callback) {
+
+ var plc = (MewtocolInterface)(object)intf;
+
+ plc.onBeforeReconnectTryTask = callback;
+
+ return this;
+
+ }
+
+ ///
+ /// Adds a task to each heartbeat cycle that is run before each individual cycle request
///
- ///
- ///
public EndInitSetup WithHeartbeatTask(Func heartBeatAsync, bool executeInProg = false) {
try {
diff --git a/MewtocolNet/MewtocolInterface.cs b/MewtocolNet/MewtocolInterface.cs
index 2c9755a..36e1bae 100644
--- a/MewtocolNet/MewtocolInterface.cs
+++ b/MewtocolNet/MewtocolInterface.cs
@@ -50,8 +50,12 @@ namespace MewtocolNet {
#region Private fields
+ //thread locker for messages
+ private SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1, 1);
+
//cancellation token for the messages
- internal CancellationTokenSource tSource = new CancellationTokenSource();
+ internal CancellationTokenSource tSourceMessageCancel = new CancellationTokenSource();
+ internal CancellationTokenSource tSourceReconnecting;
private protected Stream stream;
@@ -71,17 +75,12 @@ namespace MewtocolNet {
private protected Stopwatch speedStopwatchUpstr;
private protected Stopwatch speedStopwatchDownstr;
- protected Task firstPollTask;
- protected Task reconnectTask;
- protected Task regularSendTask;
- protected Queue> userInputSendTasks = new Queue>();
-
+ private protected Task reconnectTask;
+ private protected Task regularSendTask;
private protected bool wasInitialStatusReceived;
private protected MewtocolVersion mewtocolVersion;
- private protected List lastMsgs = new List();
-
#endregion
#region Internal fields
@@ -95,7 +94,7 @@ namespace MewtocolNet {
//configuration
- private protected bool isMessageLocked;
+ private volatile protected bool isMessageLocked;
private volatile bool isReceiving;
private volatile bool isSending;
@@ -107,15 +106,17 @@ namespace MewtocolNet {
internal bool usePoller = false;
internal bool alwaysGetMetadata = true;
+ internal Func onBeforeReconnectTryTask;
+
#endregion
#region Public Read Only Properties / Fields
///
- public bool Disposed { get; private set; }
+ public int QueuedMessages => semaphoreSlim.CurrentCount;
///
- public int QueuedMessages => userInputSendTasks.Count;
+ public bool Disposed { get; private set; }
///
public bool IsConnected {
@@ -204,7 +205,7 @@ namespace MewtocolNet {
RegisterChanged += OnRegisterChanged;
PropertyChanged += (s, e) => {
- if (e.PropertyName == nameof(PlcInfo)) {
+ if (e.PropertyName == nameof(PlcInfo) && PlcInfo != null) {
PlcInfo.PropertyChanged += (s1, e1) => {
if (e1.PropertyName == nameof(PlcInfo.IsRunMode))
OnPropChange(nameof(IsRunMode));
@@ -317,7 +318,7 @@ namespace MewtocolNet {
}
///
- protected virtual Task ReconnectAsync(int conTimeout) => throw new NotImplementedException();
+ protected virtual Task ReconnectAsync(int conTimeout, CancellationToken cancellationToken) => throw new NotImplementedException();
///
public async Task AwaitFirstDataCycleAsync() {
@@ -347,7 +348,7 @@ namespace MewtocolNet {
if (IsConnected) {
- tSource.Cancel();
+ tSourceMessageCancel.Cancel();
isMessageLocked = false;
Logger.Log("The PLC connection was closed manually", LogLevel.Error, this);
@@ -372,10 +373,21 @@ namespace MewtocolNet {
///
public virtual string GetConnectionInfo() => throw new NotImplementedException();
+ #region Reconnecting
+
+ ///
+ public void WithReconnectTask(Func callback) {
+
+ onBeforeReconnectTryTask = callback;
+
+ }
+
internal void StartReconnectTask() {
if (reconnectTask == null) {
+ tSourceReconnecting = new CancellationTokenSource();
+
reconnectTask = Task.Run(async () => {
int retryCount = 1;
@@ -389,7 +401,12 @@ namespace MewtocolNet {
}
- while (!IsConnected && tryReconnectAttempts > 0 && retryCount < tryReconnectAttempts + 1) {
+ while (!IsConnected && tryReconnectAttempts > 0 && retryCount < tryReconnectAttempts + 1 && !tSourceReconnecting.Token.IsCancellationRequested) {
+
+ if (tSourceReconnecting.Token.IsCancellationRequested) break;
+
+ if(onBeforeReconnectTryTask != null)
+ await onBeforeReconnectTryTask(retryCount);
Logger.Log($"Reconnecting {retryCount}/{tryReconnectAttempts} ...", this);
@@ -399,12 +416,14 @@ namespace MewtocolNet {
//stop the heartbeat timer for the time of retries
StopHeartBeat();
- var eArgs = new ReconnectArgs(retryCount, tryReconnectAttempts, TimeSpan.FromMilliseconds(tryReconnectDelayMs));
+ var eArgs = new ReconnectArgs(retryCount, tryReconnectAttempts, TimeSpan.FromMilliseconds(tryReconnectDelayMs + ConnectTimeout));
ReconnectTryStarted?.Invoke(this, eArgs);
Reconnected += (s, e) => eArgs.ConnectionSuccess();
- await ReconnectAsync(tryReconnectDelayMs);
+ await ReconnectAsync(tryReconnectDelayMs, tSourceReconnecting.Token);
+
+ if (tSourceReconnecting.Token.IsCancellationRequested) break;
if (IsConnected) return;
@@ -431,30 +450,39 @@ namespace MewtocolNet {
}
+ ///
+ public void StopReconnecting () {
+
+ if (tSourceReconnecting != null && !tSourceReconnecting.Token.IsCancellationRequested)
+ tSourceReconnecting.Cancel();
+
+ }
+
+ #endregion
+
+ #region Message sending and queuing
+
//internally used send task
internal async Task SendCommandInternalAsync(string _msg, Action onReceiveProgress = null) {
- if (regularSendTask != null && !regularSendTask.IsCompleted) {
-
- //queue self
- Logger.LogCritical($"Queued {_msg}...", this);
- return await EnqueueMessage(_msg, onReceiveProgress);
-
- }
-
- if (tSource.Token.IsCancellationRequested) return MewtocolFrameResponse.Canceled;
+ if (tSourceMessageCancel.Token.IsCancellationRequested) return MewtocolFrameResponse.Canceled;
if (!IsConnected && !isConnectingStage && !isReconnectingStage)
throw new NotSupportedException("The device must be connected to send a message");
+ //thread lock the current cycle
+ await semaphoreSlim.WaitAsync();
+
isMessageLocked = true;
- //send request
- regularSendTask = SendTwoDirectionalFrameAsync(_msg, onReceiveProgress);
+ MewtocolFrameResponse responseData;
try {
- var timeoutAwaiter = await Task.WhenAny(regularSendTask, Task.Delay(sendReceiveTimeoutMs, tSource.Token));
+ //send request
+ regularSendTask = SendTwoDirectionalFrameAsync(_msg, onReceiveProgress);
+
+ var timeoutAwaiter = await Task.WhenAny(regularSendTask, Task.Delay(sendReceiveTimeoutMs, tSourceMessageCancel.Token));
if (timeoutAwaiter != regularSendTask) {
@@ -466,77 +494,40 @@ namespace MewtocolNet {
}
+ //canceled
+ if (regularSendTask.IsCanceled) {
+
+ isMessageLocked = false;
+ regularSendTask = null;
+
+ return MewtocolFrameResponse.Canceled;
+
+ }
+
+ responseData = regularSendTask.Result;
+
+ tcpMessagesSentThisCycle++;
+
} catch (OperationCanceledException) {
return MewtocolFrameResponse.Canceled;
- }
+ } finally {
- //canceled
- if (regularSendTask.IsCanceled) {
+ //unlock
+ semaphoreSlim.Release();
- isMessageLocked = false;
- regularSendTask = null;
-
- return MewtocolFrameResponse.Canceled;
+ OnPropChange(nameof(QueuedMessages));
}
- MewtocolFrameResponse responseData = regularSendTask.Result;
-
- tcpMessagesSentThisCycle++;
-
isMessageLocked = false;
regularSendTask = null;
- //run the remaining tasks if no poller is used
- if(!PollerActive && !tSource.Token.IsCancellationRequested) {
-
- while(userInputSendTasks.Count > 1) {
-
- if (PollerActive || tSource.Token.IsCancellationRequested) break;
-
- await RunOneOpenQueuedTask();
-
- }
-
- }
-
return responseData;
}
- protected async Task RunOneOpenQueuedTask() {
-
- if (userInputSendTasks != null && userInputSendTasks.Count > 0) {
-
- var t = userInputSendTasks.Dequeue();
-
- t.Start();
-
- await t;
-
- }
-
- await Task.CompletedTask;
-
- }
-
- protected async Task EnqueueMessage(string _msg, Action onReceiveProgress = null) {
-
- var t = new Task(() => SendCommandInternalAsync(_msg, onReceiveProgress).Result);
- userInputSendTasks.Enqueue(t);
-
- OnPropChange(nameof(QueuedMessages));
-
- await t;
-
- OnPropChange(nameof(QueuedMessages));
-
- return t.Result;
-
- }
-
private protected async Task SendOneDirectionalFrameAsync (string frame) {
try {
@@ -549,11 +540,11 @@ namespace MewtocolNet {
IsSending = true;
- if (tSource.Token.IsCancellationRequested) return MewtocolFrameResponse.Canceled;
+ if (tSourceMessageCancel.Token.IsCancellationRequested) return MewtocolFrameResponse.Canceled;
//write inital command
byte[] writeBuffer = Encoding.UTF8.GetBytes(frame);
- await stream.WriteAsync(writeBuffer, 0, writeBuffer.Length, tSource.Token);
+ await stream.WriteAsync(writeBuffer, 0, writeBuffer.Length, tSourceMessageCancel.Token);
IsSending = false;
@@ -586,11 +577,11 @@ namespace MewtocolNet {
IsSending = true;
- if (tSource.Token.IsCancellationRequested) return MewtocolFrameResponse.Canceled;
+ if (tSourceMessageCancel.Token.IsCancellationRequested) return MewtocolFrameResponse.Canceled;
//write inital command
byte[] writeBuffer = Encoding.UTF8.GetBytes(frame);
- await stream.WriteAsync(writeBuffer, 0, writeBuffer.Length, tSource.Token);
+ await stream.WriteAsync(writeBuffer, 0, writeBuffer.Length, tSourceMessageCancel.Token);
IsSending = false;
@@ -649,7 +640,10 @@ namespace MewtocolNet {
}
- if (j > 0) split[j] = split[j].Replace($"%{GetStationNumber()}", "");
+ if (j > 0)
+ split[j] = split[j]
+ .Replace($"%{GetStationNumber()}", "")
+ .Replace($"<{GetStationNumber()}", "");
}
@@ -694,9 +688,9 @@ namespace MewtocolNet {
byte[] buffer = new byte[RecBufferSize];
IsReceiving = true;
- if (tSource.Token.IsCancellationRequested) break;
+ if (tSourceMessageCancel.Token.IsCancellationRequested) break;
- int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, tSource.Token);
+ int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, tSourceMessageCancel.Token);
IsReceiving = false;
CalcDownstreamSpeed(bytesRead);
@@ -707,17 +701,22 @@ namespace MewtocolNet {
var commandRes = ParseBufferFrame(received);
needsRead = commandRes == CommandState.LineFeed || commandRes == CommandState.RequestedNextFrame;
- OnInMsgPart(Encoding.UTF8.GetString(received));
+ var tempMsgStr = Encoding.UTF8.GetString(received);
+
+ OnInMsgPart(tempMsgStr);
//add complete response to collector without empty bytes
totalResponse.AddRange(received.Where(x => x != (byte)0x0));
if (commandRes == CommandState.RequestedNextFrame) {
+ string cmdPrefix = tempMsgStr.StartsWith("<") ? "<" : "%";
+
//request next frame
- var writeBuffer = Encoding.UTF8.GetBytes($"%{GetStationNumber()}**&\r");
+ var writeBuffer = Encoding.UTF8.GetBytes($"{cmdPrefix}{GetStationNumber()}**&\r");
+
IsSending = true;
- await stream.WriteAsync(writeBuffer, 0, writeBuffer.Length, tSource.Token);
+ await stream.WriteAsync(writeBuffer, 0, writeBuffer.Length, tSourceMessageCancel.Token);
IsSending = false;
Logger.Log($">> Requested next frame", LogLevel.Critical, this);
wasMultiFramedResponse = true;
@@ -783,7 +782,7 @@ namespace MewtocolNet {
private protected int CheckForErrorMsg(string msg) {
//error catching
- Regex errorcheck = new Regex(@"\%..\!([0-9]{2})", RegexOptions.IgnoreCase);
+ Regex errorcheck = new Regex(@"...\!([0-9]{2})", RegexOptions.IgnoreCase);
Match m = errorcheck.Match(msg);
if (m.Success) {
@@ -801,7 +800,6 @@ namespace MewtocolNet {
Logger.Log($"[---------CMD START--------]", LogLevel.Critical, this);
var formatted = $"S -> : {outMsg.Replace("\r", "(CR)")}";
- AddToLastMsgs(formatted);
Logger.Log(formatted, LogLevel.Critical, this);
}
@@ -809,7 +807,6 @@ namespace MewtocolNet {
private protected void OnInMsgPart(string inPart) {
var formatted = $"<< IN PART: {inPart.Replace("\r", "(CR)")}";
- AddToLastMsgs(formatted);
Logger.Log(formatted, LogLevel.Critical, this);
}
@@ -817,7 +814,6 @@ namespace MewtocolNet {
private protected void OnInMsg(string inMsg) {
var formatted = $"R <- : {inMsg.Replace("\r", "(CR)")}";
- AddToLastMsgs(formatted);
Logger.Log(formatted, LogLevel.Critical, this);
OnEndMsg();
@@ -829,20 +825,11 @@ namespace MewtocolNet {
}
- private protected void AddToLastMsgs (string msgTxt) {
-
- lastMsgs.Add(msgTxt);
- if(lastMsgs.Count >= 51) {
- lastMsgs.RemoveAt(0);
- }
-
- }
-
private protected void OnMajorSocketExceptionWhileConnecting() {
if (IsConnected) {
- tSource.Cancel();
+ tSourceMessageCancel.Cancel();
isMessageLocked = false;
Logger.Log("The PLC connection timed out", LogLevel.Error, this);
@@ -854,7 +841,7 @@ namespace MewtocolNet {
private protected void OnSocketExceptionWhileConnected() {
- tSource.Cancel();
+ tSourceMessageCancel.Cancel();
bytesPerSecondDownstream = 0;
bytesPerSecondUpstream = 0;
@@ -874,6 +861,8 @@ namespace MewtocolNet {
}
+ #endregion
+
private protected virtual void OnConnected(PLCInfo plcinf) {
Logger.Log("Connected to PLC", LogLevel.Info, this);
@@ -911,7 +900,7 @@ namespace MewtocolNet {
//GetAllRegisters().Cast().ToList().ForEach(x => x.OnPlcDisconnected());
//generate a new cancellation token source
- tSource = new CancellationTokenSource();
+ tSourceMessageCancel = new CancellationTokenSource();
IsConnected = true;
isReconnectingStage = false;
@@ -948,7 +937,7 @@ namespace MewtocolNet {
GetAllRegisters().Cast().ToList().ForEach(x => x.OnPlcDisconnected());
//generate a new cancellation token source
- tSource = new CancellationTokenSource();
+ tSourceMessageCancel = new CancellationTokenSource();
}
diff --git a/MewtocolNet/MewtocolInterfaceRegisterHandling.cs b/MewtocolNet/MewtocolInterfaceRegisterHandling.cs
index 1b86c64..017379c 100644
--- a/MewtocolNet/MewtocolInterfaceRegisterHandling.cs
+++ b/MewtocolNet/MewtocolInterfaceRegisterHandling.cs
@@ -20,6 +20,8 @@ namespace MewtocolNet {
private bool heartbeatNeedsRun = false;
private bool heartbeatTimerRunning = false;
+ private protected Task firstPollTask;
+
internal Task heartbeatTask = Task.CompletedTask;
internal Func heartbeatCallbackTask;
@@ -234,16 +236,16 @@ namespace MewtocolNet {
}
- await RunOneOpenQueuedTask();
-
});
sw.Stop();
if (firstPollTask != null && !firstPollTask.IsCompleted) {
+
firstPollTask.RunSynchronously();
firstPollTask = null;
Logger.Log("poll cycle first done");
+
}
pollerFirstCycleCompleted = true;
@@ -413,7 +415,6 @@ namespace MewtocolNet {
var reg = internals[i];
reg.ClearValue();
- //reg.TriggerNotifyChange();
}
diff --git a/MewtocolNet/MewtocolInterfaceRequests.cs b/MewtocolNet/MewtocolInterfaceRequests.cs
index 5f907fe..10c3d7a 100644
--- a/MewtocolNet/MewtocolInterfaceRequests.cs
+++ b/MewtocolNet/MewtocolInterfaceRequests.cs
@@ -33,7 +33,7 @@ namespace MewtocolNet {
MewtocolFrameResponse resRT = await SendCommandInternalAsync("%EE#RT");
- if (!resRT.Success || tSource.Token.IsCancellationRequested) return null;
+ if (!resRT.Success || tSourceMessageCancel.Token.IsCancellationRequested) return null;
MewtocolFrameResponse? resEXRT = null;
@@ -316,7 +316,7 @@ namespace MewtocolNet {
int blockSize = wordEnd - wordStart + 1;
string startStr = wordStart.ToString().PadLeft(padLeftLen, '0');
string endStr = wordEnd.ToString().PadLeft(padLeftLen, '0');
- string requeststring = $"%{GetStationNumber()}#{areaCodeStr}{startStr}{endStr}";
+ string requeststring = $"<{GetStationNumber()}#{areaCodeStr}{startStr}{endStr}";
var result = await SendCommandInternalAsync(requeststring, onReceiveProgress: readProg);
diff --git a/MewtocolNet/MewtocolInterfaceTcp.cs b/MewtocolNet/MewtocolInterfaceTcp.cs
index 9ff754f..760d602 100644
--- a/MewtocolNet/MewtocolInterfaceTcp.cs
+++ b/MewtocolNet/MewtocolInterfaceTcp.cs
@@ -5,6 +5,7 @@ using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
+using MewtocolNet.Helpers;
namespace MewtocolNet {
@@ -174,7 +175,7 @@ namespace MewtocolNet {
}
- protected override async Task ReconnectAsync (int conTimeout) {
+ protected override async Task ReconnectAsync (int conTimeout, CancellationToken cancellationToken) {
try {
@@ -188,10 +189,15 @@ namespace MewtocolNet {
BuildTcpClient();
- var result = client.BeginConnect(ipAddr, Port, null, null);
- var success = result.AsyncWaitHandle.WaitOne(TimeSpan.FromMilliseconds(ConnectTimeout));
+ var conTask = client.ConnectAsync(ipAddr, Port);
- if (!success || !client.Connected) {
+ if (await Task.WhenAny(conTask, Task.Delay(ConnectTimeout), cancellationToken.WhenCanceled()) != conTask) {
+
+ return;
+
+ }
+
+ if (!client.Connected) {
Logger.Log("The PLC connection timed out", LogLevel.Error, this);
OnMajorSocketExceptionWhileConnecting();
@@ -216,6 +222,8 @@ namespace MewtocolNet {
regularSendTask = null;
reconnectTask = Task.CompletedTask;
+ if (cancellationToken.IsCancellationRequested) return;
+
if (await SendCommandInternalAsync($"%{GetStationNumber()}#RT") != null) {
Logger.Log("Reconnect successfull");
diff --git a/MewtocolNet/RegisterBuilding/BuilderPatterns/RBuildMulti.cs b/MewtocolNet/RegisterBuilding/BuilderPatterns/RBuildMulti.cs
index 21db4a4..c0d85af 100644
--- a/MewtocolNet/RegisterBuilding/BuilderPatterns/RBuildMulti.cs
+++ b/MewtocolNet/RegisterBuilding/BuilderPatterns/RBuildMulti.cs
@@ -78,6 +78,10 @@ namespace MewtocolNet.RegisterBuilding.BuilderPatterns {
public class MultTypedArr1D : TypedArr1D {
+ public void Build() => builder.Assemble(this);
+
+ public void Build(out IArrayRegister reference) => reference = (IArrayRegister)builder.Assemble(this);
+
public MultTypedArr1DOut PollLevel(int level) {
Data.pollLevel = level;
@@ -89,7 +93,7 @@ namespace MewtocolNet.RegisterBuilding.BuilderPatterns {
public class MultTypedArr1DOut : TypedArr1DOut {
- public IArrayRegister Build() => (IArrayRegister)builder.Assemble(this);
+ public void Build() => builder.Assemble(this);
public void Build(out IArrayRegister reference) => reference = (IArrayRegister)builder.Assemble(this);
@@ -99,6 +103,10 @@ namespace MewtocolNet.RegisterBuilding.BuilderPatterns {
public class MultTypedArr2D : TypedArr2D {
+ public void Build() => builder.Assemble(this);
+
+ public void Build(out IArrayRegister2D reference) => reference = (IArrayRegister2D)builder.Assemble(this);
+
public MultTypedArr2DOut PollLevel(int level) {
Data.pollLevel = level;
@@ -110,7 +118,7 @@ namespace MewtocolNet.RegisterBuilding.BuilderPatterns {
public class MultTypedArr2DOut : TypedArr2DOut {
- public IArrayRegister2D Build() => (IArrayRegister2D)builder.Assemble(this);
+ public void Build() => builder.Assemble(this);
public void Build(out IArrayRegister2D reference) => reference = (IArrayRegister2D)builder.Assemble(this);
@@ -120,6 +128,10 @@ namespace MewtocolNet.RegisterBuilding.BuilderPatterns {
public class MultTypedArr3D : TypedArr3D {
+ public void Build() => builder.Assemble(this);
+
+ public void Build(out IArrayRegister3D reference) => reference = (IArrayRegister3D)builder.Assemble(this);
+
public MultTypedArr3DOut PollLevel(int level) {
Data.pollLevel = level;
@@ -131,7 +143,7 @@ namespace MewtocolNet.RegisterBuilding.BuilderPatterns {
public class MultTypedArr3DOut : TypedArr3DOut {
- public IArrayRegister3D Build() => (IArrayRegister3D)builder.Assemble(this);
+ public void Build() => builder.Assemble(this);
public void Build(out IArrayRegister3D reference) => reference = (IArrayRegister3D)builder.Assemble(this);
diff --git a/MewtocolNet/UnderlyingRegisters/MemoryAreaManager.cs b/MewtocolNet/UnderlyingRegisters/MemoryAreaManager.cs
index b7e9c19..fa0b24d 100644
--- a/MewtocolNet/UnderlyingRegisters/MemoryAreaManager.cs
+++ b/MewtocolNet/UnderlyingRegisters/MemoryAreaManager.cs
@@ -52,6 +52,18 @@ namespace MewtocolNet.UnderlyingRegisters {
mewInterface = mewIf;
Setup(wrSize, dtSize);
+ mewInterface.Connected += (s, e) => {
+
+ pollIteration = 0;
+
+ };
+
+ mewInterface.Reconnected += (s, e) => {
+
+ pollIteration = 0;
+
+ };
+
}
// Later on pass memory area sizes here
@@ -63,11 +75,7 @@ namespace MewtocolNet.UnderlyingRegisters {
}
- internal async Task OnPlcConnected () {
-
- await Task.CompletedTask;
-
- }
+ internal async Task OnPlcConnected () => await Task.CompletedTask;
internal void LinkAndMergeRegisters(List registers = null) {
diff --git a/MewtocolTests/AutomatedPropertyRegisters.cs b/MewtocolTests/AutomatedPropertyRegisters.cs
deleted file mode 100644
index d4188db..0000000
--- a/MewtocolTests/AutomatedPropertyRegisters.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using MewtocolNet;
-using MewtocolNet.Registers;
-using MewtocolTests.EncapsulatedTests;
-using Xunit;
-using Xunit.Abstractions;
-
-namespace MewtocolTests {
-
- public partial class AutomatedPropertyRegisters {
-
- private readonly ITestOutputHelper output;
-
- public AutomatedPropertyRegisters(ITestOutputHelper output) {
- this.output = output;
- }
-
- }
-
-}
\ No newline at end of file
diff --git a/MewtocolTests/TestPublicBuilderPattern.cs b/MewtocolTests/TestPublicBuilderPattern.cs
deleted file mode 100644
index a7eb134..0000000
--- a/MewtocolTests/TestPublicBuilderPattern.cs
+++ /dev/null
@@ -1,89 +0,0 @@
-using MewtocolNet;
-using MewtocolNet.RegisterBuilding;
-using MewtocolNet.RegisterBuilding.BuilderPatterns;
-using MewtocolNet.Registers;
-using MewtocolTests.EncapsulatedTests;
-using System.Collections;
-using Xunit;
-using Xunit.Abstractions;
-
-namespace MewtocolTests;
-
-public class TestPublicBuilderPattern {
-
- private readonly ITestOutputHelper output;
-
- public TestPublicBuilderPattern(ITestOutputHelper output) => this.output = output;
-
- private void TestStruct (string buildAddr, uint expectAddr, uint expectByteSize) where T : struct {
-
- using var interf = (MewtocolInterface)Mewtocol.Ethernet("192.168.115.210").Build();
- var builder = new RBuild(interf);
-
- var comparer = new StructRegister(expectAddr, expectByteSize) {
- attachedInterface = interf,
- pollLevel = 1,
- };
-
- //test building to the internal list
- builder.Struct(buildAddr).Build();
- var generated = builder.assembler.assembled.First();
- Assert.Equivalent(comparer, generated);
- builder.assembler.assembled.Clear();
- output.WriteLine(generated.Explain());
-
- //test building with direct out
- builder.Struct(buildAddr).Build(out var testRef);
- Assert.Equivalent(comparer, testRef);
- builder.assembler.assembled.Clear();
- output.WriteLine(((Register)testRef).Explain());
-
- comparer.pollLevel++;
-
- //test building to the internal list with poll level
- builder.Struct(buildAddr).PollLevel(2).Build();
- var generated2 = builder.assembler.assembled.First();
- Assert.Equivalent(comparer, generated2);
- builder.assembler.assembled.Clear();
- output.WriteLine(generated2.Explain());
-
- //test building direct out with poll level
- builder.Struct(buildAddr).PollLevel(2).Build(out var testRef2);
- Assert.Equivalent(comparer, testRef2);
- builder.assembler.assembled.Clear();
- output.WriteLine(((Register)testRef2).Explain());
-
- }
-
- //16 bit structs
-
- [Fact(DisplayName = "[16 Bit] short")]
- public void TestStruct_1() => TestStruct("DT100", 100, 2);
-
- [Fact(DisplayName = "[16 Bit] ushort")]
- public void TestStruct_2() => TestStruct("DT101", 101, 2);
-
- [Fact(DisplayName = "[16 Bit] Word")]
- public void TestStruct_3() => TestStruct("DT102", 102, 2);
-
- [Fact(DisplayName = "[16 Bit] Enum")]
- public void TestStruct_4() => TestStruct("DT103", 103, 2);
-
- //32 bit structs
-
- [Fact(DisplayName = "[32 Bit] int")]
- public void TestStruct_5() => TestStruct("DT104", 104, 4);
-
- [Fact(DisplayName = "[32 Bit] uint")]
- public void TestStruct_6() => TestStruct("DT105", 105, 4);
-
- [Fact(DisplayName = "[32 Bit] DWord")]
- public void TestStruct_7() => TestStruct("DT106", 106, 4);
-
- [Fact(DisplayName = "[32 Bit] Enum")]
- public void TestStruct_8() => TestStruct("DT107", 107, 4);
-
- [Fact(DisplayName = "[32 Bit] TimeSpan")]
- public void TestStruct_9() => TestStruct("DT108", 108, 4);
-
-}
diff --git a/MewtocolTests/TestPublicBuilderPatternArrays.cs b/MewtocolTests/TestPublicBuilderPatternArrays.cs
deleted file mode 100644
index e938b0f..0000000
--- a/MewtocolTests/TestPublicBuilderPatternArrays.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-using MewtocolNet;
-using MewtocolNet.RegisterBuilding;
-using MewtocolNet.RegisterBuilding.BuilderPatterns;
-using MewtocolNet.Registers;
-using MewtocolTests.EncapsulatedTests;
-using System.Collections;
-using System.Collections.Generic;
-using Xunit;
-using Xunit.Abstractions;
-
-namespace MewtocolTests;
-
-public class TestPublicBuilderPatternArray {
-
- private readonly ITestOutputHelper output;
-
- public TestPublicBuilderPatternArray(ITestOutputHelper output) => this.output = output;
-
- private void TestArray1D (string buildAddr, int indices1, uint expectAddr, uint expectByteSize) where T : struct {
-
- using var interf = (MewtocolInterface)Mewtocol.Ethernet("192.168.115.210").Build();
- var builder = new RBuild(interf);
-
- var comparer = new ArrayRegister(expectAddr, expectByteSize, new int[] { indices1 }) {
- attachedInterface = interf,
- pollLevel = 1
- };
-
- //test building to the internal list
- builder.Struct(buildAddr).AsArray(indices1).Build();
- var generated = builder.assembler.assembled.First();
-
- Assert.Equivalent(comparer, generated);
-
- builder.assembler.assembled.Clear();
- output.WriteLine(generated.Explain());
-
- ////test building with direct out
- //builder.Struct(buildAddr).AsArray(indices1).Build(out var testRef);
- //Assert.Equivalent(comparer, testRef);
- //builder.assembler.assembled.Clear();
- //output.WriteLine(((Register)testRef).Explain());
-
- //comparer.pollLevel++;
-
- ////test building to the internal list with poll level
- //builder.Struct(buildAddr).AsArray(indices1).PollLevel(2).Build();
- //var generated2 = builder.assembler.assembled.First();
- //Assert.Equivalent(comparer, generated2);
- //builder.assembler.assembled.Clear();
- //output.WriteLine(generated2.Explain());
-
- ////test building direct out with poll level
- //builder.Struct(buildAddr).AsArray(indices1).PollLevel(2).Build(out var testRef2);
- //Assert.Equivalent(comparer, testRef2);
- //builder.assembler.assembled.Clear();
- //output.WriteLine(((Register)testRef2).Explain());
-
- }
-
- //16 bit structs
-
-}