mirror of
https://github.com/OpenLogics/MewtocolNet.git
synced 2025-12-06 03:01:24 +00:00
Add reconnect task cancellation
- fix missing build methods for array registers - add reconnect injection task - add slim semaphore for async message single thread handling
This commit is contained in:
@@ -70,8 +70,8 @@ public partial class ConnectView : UserControl {
|
|||||||
setting.TryReconnectDelayMs = 2000;
|
setting.TryReconnectDelayMs = 2000;
|
||||||
setting.SendReceiveTimeoutMs = 1000;
|
setting.SendReceiveTimeoutMs = 1000;
|
||||||
setting.HeartbeatIntervalMs = 3000;
|
setting.HeartbeatIntervalMs = 3000;
|
||||||
setting.MaxDataBlocksPerWrite = 20;
|
setting.MaxDataBlocksPerWrite = 128;
|
||||||
setting.MaxOptimizationDistance = 10;
|
setting.MaxOptimizationDistance = 20;
|
||||||
|
|
||||||
})
|
})
|
||||||
.WithCustomPollLevels(lvl => {
|
.WithCustomPollLevels(lvl => {
|
||||||
|
|||||||
@@ -25,6 +25,8 @@
|
|||||||
Click="ClickedConnect"/>
|
Click="ClickedConnect"/>
|
||||||
<MenuItem Header="Set Random DT1000" IsEnabled="{Binding Plc.IsConnected}"
|
<MenuItem Header="Set Random DT1000" IsEnabled="{Binding Plc.IsConnected}"
|
||||||
Click="ClickedSetRandom"/>
|
Click="ClickedSetRandom"/>
|
||||||
|
<MenuItem Header="Queue test" IsEnabled="{Binding Plc.IsConnected}"
|
||||||
|
Click="ClickedAddQueueTest"/>
|
||||||
<MenuItem Header="Toggle OP mode" IsEnabled="{Binding Plc.IsConnected}"
|
<MenuItem Header="Toggle OP mode" IsEnabled="{Binding Plc.IsConnected}"
|
||||||
Click="ClickedToggleRunMode"/>
|
Click="ClickedToggleRunMode"/>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
|||||||
@@ -54,6 +54,24 @@ public partial class PlcDataView : UserControl {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async void ClickedAddQueueTest(object sender, RoutedEventArgs e) {
|
||||||
|
|
||||||
|
var tasks = new List<Task<short>>();
|
||||||
|
|
||||||
|
for (int i = 0; i < 100; i++) {
|
||||||
|
|
||||||
|
var t = viewModel.Plc.Register.Struct<short>("DT1000").ReadAsync();
|
||||||
|
|
||||||
|
tasks.Add(t);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var list = await Task.WhenAll(tasks);
|
||||||
|
|
||||||
|
Console.WriteLine();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private async void ClickedToggleRunMode(object sender, RoutedEventArgs e) {
|
private async void ClickedToggleRunMode(object sender, RoutedEventArgs e) {
|
||||||
|
|
||||||
await viewModel.Plc.ToggleOperationModeAsync();
|
await viewModel.Plc.ToggleOperationModeAsync();
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ namespace MewtocolNet.Events {
|
|||||||
|
|
||||||
public event PropertyChangedEventHandler PropertyChanged;
|
public event PropertyChangedEventHandler PropertyChanged;
|
||||||
|
|
||||||
|
public event Action Reconnected;
|
||||||
|
|
||||||
public int ReconnectTry { get; internal set; }
|
public int ReconnectTry { get; internal set; }
|
||||||
|
|
||||||
public int MaxAttempts { 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;
|
private System.Timers.Timer countDownTimer;
|
||||||
|
|
||||||
internal ReconnectArgs(int currentAttempt, int totalAttempts, TimeSpan delayBetween) {
|
internal ReconnectArgs(int currentAttempt, int totalAttempts, TimeSpan delayBetween) {
|
||||||
@@ -62,7 +74,9 @@ namespace MewtocolNet.Events {
|
|||||||
|
|
||||||
internal void ConnectionSuccess () {
|
internal void ConnectionSuccess () {
|
||||||
|
|
||||||
|
IsReconnected = true;
|
||||||
StopTimer();
|
StopTimer();
|
||||||
|
Reconnected?.Invoke();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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<Task> _lastTask = new WeakReference<Task>(null);
|
|
||||||
|
|
||||||
|
|
||||||
private List<Task> queuedTasks = new List<Task>();
|
|
||||||
|
|
||||||
//internal Task<T> Enqueue<T>(Func<Task<T>> asyncFunction) {
|
|
||||||
|
|
||||||
// lock (_locker) {
|
|
||||||
|
|
||||||
// var token = tSource.Token;
|
|
||||||
|
|
||||||
// Task lastTask;
|
|
||||||
// Task<T> 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;
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
//}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -10,6 +10,8 @@ using System.Reflection;
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace MewtocolNet {
|
namespace MewtocolNet {
|
||||||
|
|
||||||
@@ -18,6 +20,16 @@ namespace MewtocolNet {
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static class MewtocolHelpers {
|
public static class MewtocolHelpers {
|
||||||
|
|
||||||
|
#region Async extensions
|
||||||
|
|
||||||
|
internal static Task WhenCanceled(this CancellationToken cancellationToken) {
|
||||||
|
var tcs = new TaskCompletionSource<bool>();
|
||||||
|
cancellationToken.Register(s => ((TaskCompletionSource<bool>)s).SetResult(true), tcs);
|
||||||
|
return tcs.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region Byte and string operation helpers
|
#region Byte and string operation helpers
|
||||||
|
|
||||||
public static T SetFlag<T>(this Enum value, T flag, bool set) {
|
public static T SetFlag<T>(this Enum value, T flag, bool set) {
|
||||||
@@ -143,7 +155,7 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
_onString = _onString.Replace("\r", "");
|
_onString = _onString.Replace("\r", "");
|
||||||
|
|
||||||
var res = new Regex(@"\%([0-9a-fA-F]{2})\$(?:RD|RP|RC)(?<data>.*)(?<csum>..)").Match(_onString);
|
var res = new Regex(@"(?:\%|\<)([0-9a-fA-F]{2})\$(?:RD|RP|RC)(?<data>.*)(?<csum>..)").Match(_onString);
|
||||||
if (res.Success) {
|
if (res.Success) {
|
||||||
|
|
||||||
string val = res.Groups["data"].Value;
|
string val = res.Groups["data"].Value;
|
||||||
|
|||||||
@@ -129,12 +129,14 @@ namespace MewtocolNet {
|
|||||||
void Disconnect();
|
void Disconnect();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sends a command to the PLC then awaits results<br/>
|
/// Stops a running reconnect task
|
||||||
/// The checksum and BCC are appended automatically
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="_msg">MEWTOCOL Formatted request string ex: %01#RT</param>
|
void StopReconnecting();
|
||||||
/// <returns>Returns the result</returns>
|
|
||||||
//Task<MewtocolFrameResponse> SendCommandAsync(string _msg, Action<double> onReceiveProgress = null);
|
/// <summary>
|
||||||
|
/// Adds a task to each reconnect cycle that is run before each individual try
|
||||||
|
/// </summary>
|
||||||
|
void WithReconnectTask(Func<int, Task> callback);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Changes the PLCs operation mode to the given one
|
/// Changes the PLCs operation mode to the given one
|
||||||
|
|||||||
@@ -401,11 +401,21 @@ namespace MewtocolNet
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Repeats the passed method each time the hearbeat is triggered,
|
/// Adds a task to each reconnect cycle that is run before each individual try
|
||||||
/// use
|
/// </summary>
|
||||||
|
public PostInit<T> WithReconnectTask(Func<int, Task> callback) {
|
||||||
|
|
||||||
|
var plc = (MewtocolInterface)(object)intf;
|
||||||
|
|
||||||
|
plc.onBeforeReconnectTryTask = callback;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a task to each heartbeat cycle that is run before each individual cycle request
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="heartBeatAsync"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public EndInitSetup<T> WithHeartbeatTask(Func<IPlc,Task> heartBeatAsync, bool executeInProg = false) {
|
public EndInitSetup<T> WithHeartbeatTask(Func<IPlc,Task> heartBeatAsync, bool executeInProg = false) {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
|
|||||||
@@ -50,8 +50,12 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
#region Private fields
|
#region Private fields
|
||||||
|
|
||||||
|
//thread locker for messages
|
||||||
|
private SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1, 1);
|
||||||
|
|
||||||
//cancellation token for the messages
|
//cancellation token for the messages
|
||||||
internal CancellationTokenSource tSource = new CancellationTokenSource();
|
internal CancellationTokenSource tSourceMessageCancel = new CancellationTokenSource();
|
||||||
|
internal CancellationTokenSource tSourceReconnecting;
|
||||||
|
|
||||||
private protected Stream stream;
|
private protected Stream stream;
|
||||||
|
|
||||||
@@ -71,17 +75,12 @@ namespace MewtocolNet {
|
|||||||
private protected Stopwatch speedStopwatchUpstr;
|
private protected Stopwatch speedStopwatchUpstr;
|
||||||
private protected Stopwatch speedStopwatchDownstr;
|
private protected Stopwatch speedStopwatchDownstr;
|
||||||
|
|
||||||
protected Task firstPollTask;
|
private protected Task reconnectTask;
|
||||||
protected Task reconnectTask;
|
private protected Task<MewtocolFrameResponse> regularSendTask;
|
||||||
protected Task<MewtocolFrameResponse> regularSendTask;
|
|
||||||
protected Queue<Task<MewtocolFrameResponse>> userInputSendTasks = new Queue<Task<MewtocolFrameResponse>>();
|
|
||||||
|
|
||||||
|
|
||||||
private protected bool wasInitialStatusReceived;
|
private protected bool wasInitialStatusReceived;
|
||||||
private protected MewtocolVersion mewtocolVersion;
|
private protected MewtocolVersion mewtocolVersion;
|
||||||
|
|
||||||
private protected List<string> lastMsgs = new List<string>();
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Internal fields
|
#region Internal fields
|
||||||
@@ -95,7 +94,7 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
//configuration
|
//configuration
|
||||||
|
|
||||||
private protected bool isMessageLocked;
|
private volatile protected bool isMessageLocked;
|
||||||
private volatile bool isReceiving;
|
private volatile bool isReceiving;
|
||||||
private volatile bool isSending;
|
private volatile bool isSending;
|
||||||
|
|
||||||
@@ -107,15 +106,17 @@ namespace MewtocolNet {
|
|||||||
internal bool usePoller = false;
|
internal bool usePoller = false;
|
||||||
internal bool alwaysGetMetadata = true;
|
internal bool alwaysGetMetadata = true;
|
||||||
|
|
||||||
|
internal Func<int, Task> onBeforeReconnectTryTask;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Public Read Only Properties / Fields
|
#region Public Read Only Properties / Fields
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public bool Disposed { get; private set; }
|
public int QueuedMessages => semaphoreSlim.CurrentCount;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public int QueuedMessages => userInputSendTasks.Count;
|
public bool Disposed { get; private set; }
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public bool IsConnected {
|
public bool IsConnected {
|
||||||
@@ -204,7 +205,7 @@ namespace MewtocolNet {
|
|||||||
RegisterChanged += OnRegisterChanged;
|
RegisterChanged += OnRegisterChanged;
|
||||||
|
|
||||||
PropertyChanged += (s, e) => {
|
PropertyChanged += (s, e) => {
|
||||||
if (e.PropertyName == nameof(PlcInfo)) {
|
if (e.PropertyName == nameof(PlcInfo) && PlcInfo != null) {
|
||||||
PlcInfo.PropertyChanged += (s1, e1) => {
|
PlcInfo.PropertyChanged += (s1, e1) => {
|
||||||
if (e1.PropertyName == nameof(PlcInfo.IsRunMode))
|
if (e1.PropertyName == nameof(PlcInfo.IsRunMode))
|
||||||
OnPropChange(nameof(IsRunMode));
|
OnPropChange(nameof(IsRunMode));
|
||||||
@@ -317,7 +318,7 @@ namespace MewtocolNet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
protected virtual Task ReconnectAsync(int conTimeout) => throw new NotImplementedException();
|
protected virtual Task ReconnectAsync(int conTimeout, CancellationToken cancellationToken) => throw new NotImplementedException();
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public async Task AwaitFirstDataCycleAsync() {
|
public async Task AwaitFirstDataCycleAsync() {
|
||||||
@@ -347,7 +348,7 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
if (IsConnected) {
|
if (IsConnected) {
|
||||||
|
|
||||||
tSource.Cancel();
|
tSourceMessageCancel.Cancel();
|
||||||
isMessageLocked = false;
|
isMessageLocked = false;
|
||||||
|
|
||||||
Logger.Log("The PLC connection was closed manually", LogLevel.Error, this);
|
Logger.Log("The PLC connection was closed manually", LogLevel.Error, this);
|
||||||
@@ -372,10 +373,21 @@ namespace MewtocolNet {
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public virtual string GetConnectionInfo() => throw new NotImplementedException();
|
public virtual string GetConnectionInfo() => throw new NotImplementedException();
|
||||||
|
|
||||||
|
#region Reconnecting
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void WithReconnectTask(Func<int, Task> callback) {
|
||||||
|
|
||||||
|
onBeforeReconnectTryTask = callback;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
internal void StartReconnectTask() {
|
internal void StartReconnectTask() {
|
||||||
|
|
||||||
if (reconnectTask == null) {
|
if (reconnectTask == null) {
|
||||||
|
|
||||||
|
tSourceReconnecting = new CancellationTokenSource();
|
||||||
|
|
||||||
reconnectTask = Task.Run(async () => {
|
reconnectTask = Task.Run(async () => {
|
||||||
|
|
||||||
int retryCount = 1;
|
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);
|
Logger.Log($"Reconnecting {retryCount}/{tryReconnectAttempts} ...", this);
|
||||||
|
|
||||||
@@ -399,12 +416,14 @@ namespace MewtocolNet {
|
|||||||
//stop the heartbeat timer for the time of retries
|
//stop the heartbeat timer for the time of retries
|
||||||
StopHeartBeat();
|
StopHeartBeat();
|
||||||
|
|
||||||
var eArgs = new ReconnectArgs(retryCount, tryReconnectAttempts, TimeSpan.FromMilliseconds(tryReconnectDelayMs));
|
var eArgs = new ReconnectArgs(retryCount, tryReconnectAttempts, TimeSpan.FromMilliseconds(tryReconnectDelayMs + ConnectTimeout));
|
||||||
ReconnectTryStarted?.Invoke(this, eArgs);
|
ReconnectTryStarted?.Invoke(this, eArgs);
|
||||||
|
|
||||||
Reconnected += (s, e) => eArgs.ConnectionSuccess();
|
Reconnected += (s, e) => eArgs.ConnectionSuccess();
|
||||||
|
|
||||||
await ReconnectAsync(tryReconnectDelayMs);
|
await ReconnectAsync(tryReconnectDelayMs, tSourceReconnecting.Token);
|
||||||
|
|
||||||
|
if (tSourceReconnecting.Token.IsCancellationRequested) break;
|
||||||
|
|
||||||
if (IsConnected) return;
|
if (IsConnected) return;
|
||||||
|
|
||||||
@@ -431,30 +450,39 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void StopReconnecting () {
|
||||||
|
|
||||||
|
if (tSourceReconnecting != null && !tSourceReconnecting.Token.IsCancellationRequested)
|
||||||
|
tSourceReconnecting.Cancel();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Message sending and queuing
|
||||||
|
|
||||||
//internally used send task
|
//internally used send task
|
||||||
internal async Task<MewtocolFrameResponse> SendCommandInternalAsync(string _msg, Action<double> onReceiveProgress = null) {
|
internal async Task<MewtocolFrameResponse> SendCommandInternalAsync(string _msg, Action<double> onReceiveProgress = null) {
|
||||||
|
|
||||||
if (regularSendTask != null && !regularSendTask.IsCompleted) {
|
if (tSourceMessageCancel.Token.IsCancellationRequested) return MewtocolFrameResponse.Canceled;
|
||||||
|
|
||||||
//queue self
|
|
||||||
Logger.LogCritical($"Queued {_msg}...", this);
|
|
||||||
return await EnqueueMessage(_msg, onReceiveProgress);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tSource.Token.IsCancellationRequested) return MewtocolFrameResponse.Canceled;
|
|
||||||
|
|
||||||
if (!IsConnected && !isConnectingStage && !isReconnectingStage)
|
if (!IsConnected && !isConnectingStage && !isReconnectingStage)
|
||||||
throw new NotSupportedException("The device must be connected to send a message");
|
throw new NotSupportedException("The device must be connected to send a message");
|
||||||
|
|
||||||
|
//thread lock the current cycle
|
||||||
|
await semaphoreSlim.WaitAsync();
|
||||||
|
|
||||||
isMessageLocked = true;
|
isMessageLocked = true;
|
||||||
|
|
||||||
//send request
|
MewtocolFrameResponse responseData;
|
||||||
regularSendTask = SendTwoDirectionalFrameAsync(_msg, onReceiveProgress);
|
|
||||||
|
|
||||||
try {
|
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) {
|
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) {
|
} catch (OperationCanceledException) {
|
||||||
|
|
||||||
return MewtocolFrameResponse.Canceled;
|
return MewtocolFrameResponse.Canceled;
|
||||||
|
|
||||||
}
|
} finally {
|
||||||
|
|
||||||
//canceled
|
//unlock
|
||||||
if (regularSendTask.IsCanceled) {
|
semaphoreSlim.Release();
|
||||||
|
|
||||||
isMessageLocked = false;
|
OnPropChange(nameof(QueuedMessages));
|
||||||
regularSendTask = null;
|
|
||||||
|
|
||||||
return MewtocolFrameResponse.Canceled;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MewtocolFrameResponse responseData = regularSendTask.Result;
|
|
||||||
|
|
||||||
tcpMessagesSentThisCycle++;
|
|
||||||
|
|
||||||
isMessageLocked = false;
|
isMessageLocked = false;
|
||||||
regularSendTask = null;
|
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;
|
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<MewtocolFrameResponse> EnqueueMessage(string _msg, Action<double> onReceiveProgress = null) {
|
|
||||||
|
|
||||||
var t = new Task<MewtocolFrameResponse>(() => SendCommandInternalAsync(_msg, onReceiveProgress).Result);
|
|
||||||
userInputSendTasks.Enqueue(t);
|
|
||||||
|
|
||||||
OnPropChange(nameof(QueuedMessages));
|
|
||||||
|
|
||||||
await t;
|
|
||||||
|
|
||||||
OnPropChange(nameof(QueuedMessages));
|
|
||||||
|
|
||||||
return t.Result;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private protected async Task<MewtocolFrameResponse> SendOneDirectionalFrameAsync (string frame) {
|
private protected async Task<MewtocolFrameResponse> SendOneDirectionalFrameAsync (string frame) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -549,11 +540,11 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
IsSending = true;
|
IsSending = true;
|
||||||
|
|
||||||
if (tSource.Token.IsCancellationRequested) return MewtocolFrameResponse.Canceled;
|
if (tSourceMessageCancel.Token.IsCancellationRequested) return MewtocolFrameResponse.Canceled;
|
||||||
|
|
||||||
//write inital command
|
//write inital command
|
||||||
byte[] writeBuffer = Encoding.UTF8.GetBytes(frame);
|
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;
|
IsSending = false;
|
||||||
|
|
||||||
@@ -586,11 +577,11 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
IsSending = true;
|
IsSending = true;
|
||||||
|
|
||||||
if (tSource.Token.IsCancellationRequested) return MewtocolFrameResponse.Canceled;
|
if (tSourceMessageCancel.Token.IsCancellationRequested) return MewtocolFrameResponse.Canceled;
|
||||||
|
|
||||||
//write inital command
|
//write inital command
|
||||||
byte[] writeBuffer = Encoding.UTF8.GetBytes(frame);
|
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;
|
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];
|
byte[] buffer = new byte[RecBufferSize];
|
||||||
IsReceiving = true;
|
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;
|
IsReceiving = false;
|
||||||
|
|
||||||
CalcDownstreamSpeed(bytesRead);
|
CalcDownstreamSpeed(bytesRead);
|
||||||
@@ -707,17 +701,22 @@ namespace MewtocolNet {
|
|||||||
var commandRes = ParseBufferFrame(received);
|
var commandRes = ParseBufferFrame(received);
|
||||||
needsRead = commandRes == CommandState.LineFeed || commandRes == CommandState.RequestedNextFrame;
|
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
|
//add complete response to collector without empty bytes
|
||||||
totalResponse.AddRange(received.Where(x => x != (byte)0x0));
|
totalResponse.AddRange(received.Where(x => x != (byte)0x0));
|
||||||
|
|
||||||
if (commandRes == CommandState.RequestedNextFrame) {
|
if (commandRes == CommandState.RequestedNextFrame) {
|
||||||
|
|
||||||
|
string cmdPrefix = tempMsgStr.StartsWith("<") ? "<" : "%";
|
||||||
|
|
||||||
//request next frame
|
//request next frame
|
||||||
var writeBuffer = Encoding.UTF8.GetBytes($"%{GetStationNumber()}**&\r");
|
var writeBuffer = Encoding.UTF8.GetBytes($"{cmdPrefix}{GetStationNumber()}**&\r");
|
||||||
|
|
||||||
IsSending = true;
|
IsSending = true;
|
||||||
await stream.WriteAsync(writeBuffer, 0, writeBuffer.Length, tSource.Token);
|
await stream.WriteAsync(writeBuffer, 0, writeBuffer.Length, tSourceMessageCancel.Token);
|
||||||
IsSending = false;
|
IsSending = false;
|
||||||
Logger.Log($">> Requested next frame", LogLevel.Critical, this);
|
Logger.Log($">> Requested next frame", LogLevel.Critical, this);
|
||||||
wasMultiFramedResponse = true;
|
wasMultiFramedResponse = true;
|
||||||
@@ -783,7 +782,7 @@ namespace MewtocolNet {
|
|||||||
private protected int CheckForErrorMsg(string msg) {
|
private protected int CheckForErrorMsg(string msg) {
|
||||||
|
|
||||||
//error catching
|
//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);
|
Match m = errorcheck.Match(msg);
|
||||||
|
|
||||||
if (m.Success) {
|
if (m.Success) {
|
||||||
@@ -801,7 +800,6 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
Logger.Log($"[---------CMD START--------]", LogLevel.Critical, this);
|
Logger.Log($"[---------CMD START--------]", LogLevel.Critical, this);
|
||||||
var formatted = $"S -> : {outMsg.Replace("\r", "(CR)")}";
|
var formatted = $"S -> : {outMsg.Replace("\r", "(CR)")}";
|
||||||
AddToLastMsgs(formatted);
|
|
||||||
Logger.Log(formatted, LogLevel.Critical, this);
|
Logger.Log(formatted, LogLevel.Critical, this);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -809,7 +807,6 @@ namespace MewtocolNet {
|
|||||||
private protected void OnInMsgPart(string inPart) {
|
private protected void OnInMsgPart(string inPart) {
|
||||||
|
|
||||||
var formatted = $"<< IN PART: {inPart.Replace("\r", "(CR)")}";
|
var formatted = $"<< IN PART: {inPart.Replace("\r", "(CR)")}";
|
||||||
AddToLastMsgs(formatted);
|
|
||||||
Logger.Log(formatted, LogLevel.Critical, this);
|
Logger.Log(formatted, LogLevel.Critical, this);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -817,7 +814,6 @@ namespace MewtocolNet {
|
|||||||
private protected void OnInMsg(string inMsg) {
|
private protected void OnInMsg(string inMsg) {
|
||||||
|
|
||||||
var formatted = $"R <- : {inMsg.Replace("\r", "(CR)")}";
|
var formatted = $"R <- : {inMsg.Replace("\r", "(CR)")}";
|
||||||
AddToLastMsgs(formatted);
|
|
||||||
Logger.Log(formatted, LogLevel.Critical, this);
|
Logger.Log(formatted, LogLevel.Critical, this);
|
||||||
OnEndMsg();
|
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() {
|
private protected void OnMajorSocketExceptionWhileConnecting() {
|
||||||
|
|
||||||
if (IsConnected) {
|
if (IsConnected) {
|
||||||
|
|
||||||
tSource.Cancel();
|
tSourceMessageCancel.Cancel();
|
||||||
isMessageLocked = false;
|
isMessageLocked = false;
|
||||||
|
|
||||||
Logger.Log("The PLC connection timed out", LogLevel.Error, this);
|
Logger.Log("The PLC connection timed out", LogLevel.Error, this);
|
||||||
@@ -854,7 +841,7 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
private protected void OnSocketExceptionWhileConnected() {
|
private protected void OnSocketExceptionWhileConnected() {
|
||||||
|
|
||||||
tSource.Cancel();
|
tSourceMessageCancel.Cancel();
|
||||||
|
|
||||||
bytesPerSecondDownstream = 0;
|
bytesPerSecondDownstream = 0;
|
||||||
bytesPerSecondUpstream = 0;
|
bytesPerSecondUpstream = 0;
|
||||||
@@ -874,6 +861,8 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
private protected virtual void OnConnected(PLCInfo plcinf) {
|
private protected virtual void OnConnected(PLCInfo plcinf) {
|
||||||
|
|
||||||
Logger.Log("Connected to PLC", LogLevel.Info, this);
|
Logger.Log("Connected to PLC", LogLevel.Info, this);
|
||||||
@@ -911,7 +900,7 @@ namespace MewtocolNet {
|
|||||||
//GetAllRegisters().Cast<Register>().ToList().ForEach(x => x.OnPlcDisconnected());
|
//GetAllRegisters().Cast<Register>().ToList().ForEach(x => x.OnPlcDisconnected());
|
||||||
|
|
||||||
//generate a new cancellation token source
|
//generate a new cancellation token source
|
||||||
tSource = new CancellationTokenSource();
|
tSourceMessageCancel = new CancellationTokenSource();
|
||||||
|
|
||||||
IsConnected = true;
|
IsConnected = true;
|
||||||
isReconnectingStage = false;
|
isReconnectingStage = false;
|
||||||
@@ -948,7 +937,7 @@ namespace MewtocolNet {
|
|||||||
GetAllRegisters().Cast<Register>().ToList().ForEach(x => x.OnPlcDisconnected());
|
GetAllRegisters().Cast<Register>().ToList().ForEach(x => x.OnPlcDisconnected());
|
||||||
|
|
||||||
//generate a new cancellation token source
|
//generate a new cancellation token source
|
||||||
tSource = new CancellationTokenSource();
|
tSourceMessageCancel = new CancellationTokenSource();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ namespace MewtocolNet {
|
|||||||
private bool heartbeatNeedsRun = false;
|
private bool heartbeatNeedsRun = false;
|
||||||
private bool heartbeatTimerRunning = false;
|
private bool heartbeatTimerRunning = false;
|
||||||
|
|
||||||
|
private protected Task firstPollTask;
|
||||||
|
|
||||||
internal Task heartbeatTask = Task.CompletedTask;
|
internal Task heartbeatTask = Task.CompletedTask;
|
||||||
|
|
||||||
internal Func<IPlc, Task> heartbeatCallbackTask;
|
internal Func<IPlc, Task> heartbeatCallbackTask;
|
||||||
@@ -234,16 +236,16 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await RunOneOpenQueuedTask();
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
sw.Stop();
|
sw.Stop();
|
||||||
|
|
||||||
if (firstPollTask != null && !firstPollTask.IsCompleted) {
|
if (firstPollTask != null && !firstPollTask.IsCompleted) {
|
||||||
|
|
||||||
firstPollTask.RunSynchronously();
|
firstPollTask.RunSynchronously();
|
||||||
firstPollTask = null;
|
firstPollTask = null;
|
||||||
Logger.Log("poll cycle first done");
|
Logger.Log("poll cycle first done");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pollerFirstCycleCompleted = true;
|
pollerFirstCycleCompleted = true;
|
||||||
@@ -413,7 +415,6 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
var reg = internals[i];
|
var reg = internals[i];
|
||||||
reg.ClearValue();
|
reg.ClearValue();
|
||||||
//reg.TriggerNotifyChange();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
MewtocolFrameResponse resRT = await SendCommandInternalAsync("%EE#RT");
|
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;
|
MewtocolFrameResponse? resEXRT = null;
|
||||||
|
|
||||||
@@ -316,7 +316,7 @@ namespace MewtocolNet {
|
|||||||
int blockSize = wordEnd - wordStart + 1;
|
int blockSize = wordEnd - wordStart + 1;
|
||||||
string startStr = wordStart.ToString().PadLeft(padLeftLen, '0');
|
string startStr = wordStart.ToString().PadLeft(padLeftLen, '0');
|
||||||
string endStr = wordEnd.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);
|
var result = await SendCommandInternalAsync(requeststring, onReceiveProgress: readProg);
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using System.Net;
|
|||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using MewtocolNet.Helpers;
|
||||||
|
|
||||||
namespace MewtocolNet {
|
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 {
|
try {
|
||||||
|
|
||||||
@@ -188,10 +189,15 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
BuildTcpClient();
|
BuildTcpClient();
|
||||||
|
|
||||||
var result = client.BeginConnect(ipAddr, Port, null, null);
|
var conTask = client.ConnectAsync(ipAddr, Port);
|
||||||
var success = result.AsyncWaitHandle.WaitOne(TimeSpan.FromMilliseconds(ConnectTimeout));
|
|
||||||
|
|
||||||
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);
|
Logger.Log("The PLC connection timed out", LogLevel.Error, this);
|
||||||
OnMajorSocketExceptionWhileConnecting();
|
OnMajorSocketExceptionWhileConnecting();
|
||||||
@@ -216,6 +222,8 @@ namespace MewtocolNet {
|
|||||||
regularSendTask = null;
|
regularSendTask = null;
|
||||||
reconnectTask = Task.CompletedTask;
|
reconnectTask = Task.CompletedTask;
|
||||||
|
|
||||||
|
if (cancellationToken.IsCancellationRequested) return;
|
||||||
|
|
||||||
if (await SendCommandInternalAsync($"%{GetStationNumber()}#RT") != null) {
|
if (await SendCommandInternalAsync($"%{GetStationNumber()}#RT") != null) {
|
||||||
|
|
||||||
Logger.Log("Reconnect successfull");
|
Logger.Log("Reconnect successfull");
|
||||||
|
|||||||
@@ -78,6 +78,10 @@ namespace MewtocolNet.RegisterBuilding.BuilderPatterns {
|
|||||||
|
|
||||||
public class MultTypedArr1D<T> : TypedArr1D<T> {
|
public class MultTypedArr1D<T> : TypedArr1D<T> {
|
||||||
|
|
||||||
|
public void Build() => builder.Assemble(this);
|
||||||
|
|
||||||
|
public void Build(out IArrayRegister<T> reference) => reference = (IArrayRegister<T>)builder.Assemble(this);
|
||||||
|
|
||||||
public MultTypedArr1DOut<T> PollLevel(int level) {
|
public MultTypedArr1DOut<T> PollLevel(int level) {
|
||||||
|
|
||||||
Data.pollLevel = level;
|
Data.pollLevel = level;
|
||||||
@@ -89,7 +93,7 @@ namespace MewtocolNet.RegisterBuilding.BuilderPatterns {
|
|||||||
|
|
||||||
public class MultTypedArr1DOut<T> : TypedArr1DOut<T> {
|
public class MultTypedArr1DOut<T> : TypedArr1DOut<T> {
|
||||||
|
|
||||||
public IArrayRegister<T> Build() => (IArrayRegister<T>)builder.Assemble(this);
|
public void Build() => builder.Assemble(this);
|
||||||
|
|
||||||
public void Build(out IArrayRegister<T> reference) => reference = (IArrayRegister<T>)builder.Assemble(this);
|
public void Build(out IArrayRegister<T> reference) => reference = (IArrayRegister<T>)builder.Assemble(this);
|
||||||
|
|
||||||
@@ -99,6 +103,10 @@ namespace MewtocolNet.RegisterBuilding.BuilderPatterns {
|
|||||||
|
|
||||||
public class MultTypedArr2D<T> : TypedArr2D<T> {
|
public class MultTypedArr2D<T> : TypedArr2D<T> {
|
||||||
|
|
||||||
|
public void Build() => builder.Assemble(this);
|
||||||
|
|
||||||
|
public void Build(out IArrayRegister2D<T> reference) => reference = (IArrayRegister2D<T>)builder.Assemble(this);
|
||||||
|
|
||||||
public MultTypedArr2DOut<T> PollLevel(int level) {
|
public MultTypedArr2DOut<T> PollLevel(int level) {
|
||||||
|
|
||||||
Data.pollLevel = level;
|
Data.pollLevel = level;
|
||||||
@@ -110,7 +118,7 @@ namespace MewtocolNet.RegisterBuilding.BuilderPatterns {
|
|||||||
|
|
||||||
public class MultTypedArr2DOut<T> : TypedArr2DOut<T> {
|
public class MultTypedArr2DOut<T> : TypedArr2DOut<T> {
|
||||||
|
|
||||||
public IArrayRegister2D<T> Build() => (IArrayRegister2D<T>)builder.Assemble(this);
|
public void Build() => builder.Assemble(this);
|
||||||
|
|
||||||
public void Build(out IArrayRegister2D<T> reference) => reference = (IArrayRegister2D<T>)builder.Assemble(this);
|
public void Build(out IArrayRegister2D<T> reference) => reference = (IArrayRegister2D<T>)builder.Assemble(this);
|
||||||
|
|
||||||
@@ -120,6 +128,10 @@ namespace MewtocolNet.RegisterBuilding.BuilderPatterns {
|
|||||||
|
|
||||||
public class MultTypedArr3D<T> : TypedArr3D<T> {
|
public class MultTypedArr3D<T> : TypedArr3D<T> {
|
||||||
|
|
||||||
|
public void Build() => builder.Assemble(this);
|
||||||
|
|
||||||
|
public void Build(out IArrayRegister3D<T> reference) => reference = (IArrayRegister3D<T>)builder.Assemble(this);
|
||||||
|
|
||||||
public MultTypedArr3DOut<T> PollLevel(int level) {
|
public MultTypedArr3DOut<T> PollLevel(int level) {
|
||||||
|
|
||||||
Data.pollLevel = level;
|
Data.pollLevel = level;
|
||||||
@@ -131,7 +143,7 @@ namespace MewtocolNet.RegisterBuilding.BuilderPatterns {
|
|||||||
|
|
||||||
public class MultTypedArr3DOut<T> : TypedArr3DOut<T> {
|
public class MultTypedArr3DOut<T> : TypedArr3DOut<T> {
|
||||||
|
|
||||||
public IArrayRegister3D<T> Build() => (IArrayRegister3D<T>)builder.Assemble(this);
|
public void Build() => builder.Assemble(this);
|
||||||
|
|
||||||
public void Build(out IArrayRegister3D<T> reference) => reference = (IArrayRegister3D<T>)builder.Assemble(this);
|
public void Build(out IArrayRegister3D<T> reference) => reference = (IArrayRegister3D<T>)builder.Assemble(this);
|
||||||
|
|
||||||
|
|||||||
@@ -52,6 +52,18 @@ namespace MewtocolNet.UnderlyingRegisters {
|
|||||||
mewInterface = mewIf;
|
mewInterface = mewIf;
|
||||||
Setup(wrSize, dtSize);
|
Setup(wrSize, dtSize);
|
||||||
|
|
||||||
|
mewInterface.Connected += (s, e) => {
|
||||||
|
|
||||||
|
pollIteration = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
mewInterface.Reconnected += (s, e) => {
|
||||||
|
|
||||||
|
pollIteration = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Later on pass memory area sizes here
|
// Later on pass memory area sizes here
|
||||||
@@ -63,11 +75,7 @@ namespace MewtocolNet.UnderlyingRegisters {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task OnPlcConnected () {
|
internal async Task OnPlcConnected () => await Task.CompletedTask;
|
||||||
|
|
||||||
await Task.CompletedTask;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void LinkAndMergeRegisters(List<Register> registers = null) {
|
internal void LinkAndMergeRegisters(List<Register> registers = 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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<T> (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<T>(expectAddr, expectByteSize) {
|
|
||||||
attachedInterface = interf,
|
|
||||||
pollLevel = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
//test building to the internal list
|
|
||||||
builder.Struct<T>(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<T>(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<T>(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<T>(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<short>("DT100", 100, 2);
|
|
||||||
|
|
||||||
[Fact(DisplayName = "[16 Bit] ushort")]
|
|
||||||
public void TestStruct_2() => TestStruct<ushort>("DT101", 101, 2);
|
|
||||||
|
|
||||||
[Fact(DisplayName = "[16 Bit] Word")]
|
|
||||||
public void TestStruct_3() => TestStruct<Word>("DT102", 102, 2);
|
|
||||||
|
|
||||||
[Fact(DisplayName = "[16 Bit] Enum")]
|
|
||||||
public void TestStruct_4() => TestStruct<CurrentState16>("DT103", 103, 2);
|
|
||||||
|
|
||||||
//32 bit structs
|
|
||||||
|
|
||||||
[Fact(DisplayName = "[32 Bit] int")]
|
|
||||||
public void TestStruct_5() => TestStruct<int>("DT104", 104, 4);
|
|
||||||
|
|
||||||
[Fact(DisplayName = "[32 Bit] uint")]
|
|
||||||
public void TestStruct_6() => TestStruct<uint>("DT105", 105, 4);
|
|
||||||
|
|
||||||
[Fact(DisplayName = "[32 Bit] DWord")]
|
|
||||||
public void TestStruct_7() => TestStruct<DWord>("DT106", 106, 4);
|
|
||||||
|
|
||||||
[Fact(DisplayName = "[32 Bit] Enum")]
|
|
||||||
public void TestStruct_8() => TestStruct<CurrentState32>("DT107", 107, 4);
|
|
||||||
|
|
||||||
[Fact(DisplayName = "[32 Bit] TimeSpan")]
|
|
||||||
public void TestStruct_9() => TestStruct<TimeSpan>("DT108", 108, 4);
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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<T> (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<T>(expectAddr, expectByteSize, new int[] { indices1 }) {
|
|
||||||
attachedInterface = interf,
|
|
||||||
pollLevel = 1
|
|
||||||
};
|
|
||||||
|
|
||||||
//test building to the internal list
|
|
||||||
builder.Struct<T>(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<T>(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<T>(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<T>(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
|
|
||||||
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user