From a06a69be3f6a71d7bf4968f8b351189eb6a6be73 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Felix=20Wei=C3=9F?=
<72068105+Sandoun@users.noreply.github.com>
Date: Tue, 8 Aug 2023 17:41:18 +0200
Subject: [PATCH] Generized memory areas
---
Examples.WPF/App.xaml | 1 +
Examples.WPF/Converters/ColorHashConverter.cs | 62 ++++++++
.../ViewModels/PlcDataViewViewModel.cs | 19 +++
Examples.WPF/Views/ConnectView.xaml.cs | 7 +-
Examples.WPF/Views/PlcDataView.xaml | 68 +++++++-
MewtocolNet/Events/ReconnectArgs.cs | 84 ++++++++++
MewtocolNet/Helpers/MewtocolHelpers.cs | 17 ++
MewtocolNet/IPlc.cs | 28 +++-
MewtocolNet/MewtocolInterface.cs | 150 +++++++++++-------
.../MewtocolInterfaceRegisterHandling.cs | 23 ++-
MewtocolNet/MewtocolInterfaceRequests.cs | 5 +
MewtocolNet/MewtocolInterfaceTcp.cs | 26 +--
MewtocolNet/MewtocolNet.csproj | 1 +
.../BuilderPatterns/RBuild.cs | 16 +-
MewtocolNet/Registers/Base/IRegister.cs | 7 +
MewtocolNet/Registers/Base/Register.cs | 5 +-
MewtocolNet/Registers/Classes/BoolRegister.cs | 11 ++
.../Registers/Classes/StringRegister.cs | 4 +-
.../{DTArea.cs => Areas/AreaBase.cs} | 27 +---
.../UnderlyingRegisters/Areas/DTArea.cs | 19 +++
.../{ => Areas}/IMemoryArea.cs | 2 +
.../UnderlyingRegisters/Areas/WRArea.cs | 19 +++
.../UnderlyingRegisters/MemoryAreaManager.cs | 103 +++---------
MewtocolNet/UnderlyingRegisters/PollLevel.cs | 39 +++--
MewtocolNet/UnderlyingRegisters/WRArea.cs | 92 -----------
25 files changed, 531 insertions(+), 304 deletions(-)
create mode 100644 Examples.WPF/Converters/ColorHashConverter.cs
create mode 100644 MewtocolNet/Events/ReconnectArgs.cs
rename MewtocolNet/UnderlyingRegisters/{DTArea.cs => Areas/AreaBase.cs} (81%)
create mode 100644 MewtocolNet/UnderlyingRegisters/Areas/DTArea.cs
rename MewtocolNet/UnderlyingRegisters/{ => Areas}/IMemoryArea.cs (91%)
create mode 100644 MewtocolNet/UnderlyingRegisters/Areas/WRArea.cs
delete mode 100644 MewtocolNet/UnderlyingRegisters/WRArea.cs
diff --git a/Examples.WPF/App.xaml b/Examples.WPF/App.xaml
index fe9a706..4b030de 100644
--- a/Examples.WPF/App.xaml
+++ b/Examples.WPF/App.xaml
@@ -6,5 +6,6 @@
StartupUri="MainWindow.xaml">
+
diff --git a/Examples.WPF/Converters/ColorHashConverter.cs b/Examples.WPF/Converters/ColorHashConverter.cs
new file mode 100644
index 0000000..8692d54
--- /dev/null
+++ b/Examples.WPF/Converters/ColorHashConverter.cs
@@ -0,0 +1,62 @@
+using System;
+using System.Drawing;
+using System.Globalization;
+using System.Windows.Data;
+
+
+namespace Examples.WPF.Converters;
+
+[ValueConversion(typeof(bool), typeof(bool))]
+public class ColorHashConverter : IValueConverter {
+
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
+
+ var hashCode = value.GetHashCode();
+ var randColor = GenerateRandomVibrantColor(new Random(hashCode));
+
+ System.Windows.Media.Brush outBrush = new System.Windows.Media.SolidColorBrush(new System.Windows.Media.Color {
+ R = randColor.R,
+ G = randColor.G,
+ B = randColor.B,
+ A = 255,
+ });
+
+ return outBrush;
+
+ }
+
+ private Color GenerateRandomVibrantColor(Random random) {
+
+ byte red = (byte)random.Next(256);
+ byte green = (byte)random.Next(256);
+ byte blue = (byte)random.Next(256);
+
+ Color color = Color.FromArgb(255, red, green, blue);
+
+ // Ensure the color is vibrant and colorful
+ while (!IsVibrantColor(color)) {
+ red = (byte)random.Next(256);
+ green = (byte)random.Next(256);
+ blue = (byte)random.Next(256);
+ color = Color.FromArgb(255,red, green, blue);
+ }
+
+ return color;
+ }
+
+ private bool IsVibrantColor(Color color) {
+
+ int minBrightness = 100;
+ int maxBrightness = 200;
+ int minSaturation = 150;
+
+ int brightness = (int)(color.GetBrightness() * 255);
+ int saturation = (int)(color.GetSaturation() * 255);
+
+ return brightness >= minBrightness && brightness <= maxBrightness && saturation >= minSaturation;
+
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new NotImplementedException();
+
+}
diff --git a/Examples.WPF/ViewModels/PlcDataViewViewModel.cs b/Examples.WPF/ViewModels/PlcDataViewViewModel.cs
index 174aabf..7cacd07 100644
--- a/Examples.WPF/ViewModels/PlcDataViewViewModel.cs
+++ b/Examples.WPF/ViewModels/PlcDataViewViewModel.cs
@@ -1,4 +1,5 @@
using MewtocolNet;
+using MewtocolNet.Events;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -9,6 +10,24 @@ namespace Examples.WPF.ViewModels;
public class PlcDataViewViewModel : ViewModelBase {
+ private ReconnectArgs plcCurrentReconnectArgs = null!;
+
public IPlc Plc => App.ViewModel.Plc!;
+ public ReconnectArgs PlcCurrentReconnectArgs {
+ get => plcCurrentReconnectArgs;
+ set {
+ plcCurrentReconnectArgs = value;
+ OnPropChange();
+ }
+ }
+
+ public PlcDataViewViewModel () {
+
+ Plc.ReconnectTryStarted += (s, e) => PlcCurrentReconnectArgs = e;
+ Plc.Reconnected += (s, e) => PlcCurrentReconnectArgs = null!;
+ Plc.Disconnected += (s, e) => PlcCurrentReconnectArgs = null!;
+
+ }
+
}
\ No newline at end of file
diff --git a/Examples.WPF/Views/ConnectView.xaml.cs b/Examples.WPF/Views/ConnectView.xaml.cs
index 6cc631a..adc4f35 100644
--- a/Examples.WPF/Views/ConnectView.xaml.cs
+++ b/Examples.WPF/Views/ConnectView.xaml.cs
@@ -59,9 +59,12 @@ public partial class ConnectView : UserControl {
App.ViewModel.Plc = Mewtocol.Ethernet(viewModel.SelectedIP, parsedInt)
.WithPoller()
.WithInterfaceSettings(setting => {
- setting.TryReconnectAttempts = 30;
+ setting.TryReconnectAttempts = 0;
setting.TryReconnectDelayMs = 2000;
+ setting.SendReceiveTimeoutMs = 1000;
setting.HeartbeatIntervalMs = 3000;
+ setting.MaxDataBlocksPerWrite = 12;
+ setting.MaxOptimizationDistance = 10;
})
.WithCustomPollLevels(lvl => {
lvl.SetLevel(2, 3);
@@ -73,6 +76,8 @@ public partial class ConnectView : UserControl {
//b.Struct("DT0").Build();
//b.Struct("DT0").AsArray(30).Build();
+ b.Bool("R10A").Build();
+
b.Struct("DT1000").Build(out heartbeatSetter);
b.Struct("DT1000").Build();
diff --git a/Examples.WPF/Views/PlcDataView.xaml b/Examples.WPF/Views/PlcDataView.xaml
index c82fa9a..d0ca91c 100644
--- a/Examples.WPF/Views/PlcDataView.xaml
+++ b/Examples.WPF/Views/PlcDataView.xaml
@@ -33,16 +33,56 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ in
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MewtocolNet/Events/ReconnectArgs.cs b/MewtocolNet/Events/ReconnectArgs.cs
new file mode 100644
index 0000000..969d559
--- /dev/null
+++ b/MewtocolNet/Events/ReconnectArgs.cs
@@ -0,0 +1,84 @@
+using MewtocolNet.Registers;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+using System.Text;
+
+namespace MewtocolNet.Events {
+
+ public delegate void PlcReconnectEventHandler(object sender, ReconnectArgs e);
+
+ public class ReconnectArgs : EventArgs, INotifyPropertyChanged {
+
+ private TimeSpan retryCountDownRemaining;
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ public int ReconnectTry { get; internal set; }
+
+ public int MaxAttempts { get; internal set; }
+
+ public TimeSpan RetryCountDownTime { get; internal set; }
+
+ public TimeSpan RetryCountDownRemaining {
+ get => retryCountDownRemaining;
+ private set {
+ retryCountDownRemaining = value;
+ OnPropChange();
+ }
+ }
+
+ private System.Timers.Timer countDownTimer;
+
+ internal ReconnectArgs(int currentAttempt, int totalAttempts, TimeSpan delayBetween) {
+
+ ReconnectTry = currentAttempt;
+ MaxAttempts = totalAttempts;
+ RetryCountDownTime = delayBetween;
+
+ //start countdown timer
+ RetryCountDownRemaining = RetryCountDownTime;
+
+ var interval = 100;
+ var intervalTS = TimeSpan.FromMilliseconds(interval);
+
+ countDownTimer = new System.Timers.Timer(100);
+
+ countDownTimer.Elapsed += (s, e) => {
+
+ if (RetryCountDownRemaining <= TimeSpan.Zero) {
+ StopTimer();
+ return;
+ }
+
+ RetryCountDownRemaining -= intervalTS;
+
+ };
+
+ countDownTimer.Start();
+
+ }
+
+ internal void ConnectionSuccess () {
+
+ StopTimer();
+
+ }
+
+ private void StopTimer () {
+
+ countDownTimer?.Stop();
+ RetryCountDownRemaining = TimeSpan.Zero;
+
+ }
+
+ private void OnPropChange([CallerMemberName] string propertyName = null) {
+
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+
+ }
+
+ }
+
+}
diff --git a/MewtocolNet/Helpers/MewtocolHelpers.cs b/MewtocolNet/Helpers/MewtocolHelpers.cs
index c63bc36..c0878fd 100644
--- a/MewtocolNet/Helpers/MewtocolHelpers.cs
+++ b/MewtocolNet/Helpers/MewtocolHelpers.cs
@@ -20,6 +20,23 @@ namespace MewtocolNet {
#region Byte and string operation helpers
+ public static T SetFlag(this Enum value, T flag, bool set) {
+
+ Type underlyingType = Enum.GetUnderlyingType(value.GetType());
+
+ dynamic valueAsInt = Convert.ChangeType(value, underlyingType);
+ dynamic flagAsInt = Convert.ChangeType(flag, underlyingType);
+
+ if (set) {
+ valueAsInt |= flagAsInt;
+ } else {
+ valueAsInt &= ~flagAsInt;
+ }
+
+ return (T)valueAsInt;
+
+ }
+
public static int DetermineTypeByteIntialSize(this Type type) {
//enums can only be of numeric types
diff --git a/MewtocolNet/IPlc.cs b/MewtocolNet/IPlc.cs
index 590bc9d..e0b430d 100644
--- a/MewtocolNet/IPlc.cs
+++ b/MewtocolNet/IPlc.cs
@@ -1,4 +1,5 @@
-using MewtocolNet.ProgramParsing;
+using MewtocolNet.Events;
+using MewtocolNet.ProgramParsing;
using MewtocolNet.RegisterBuilding;
using MewtocolNet.Registers;
using System;
@@ -12,6 +13,31 @@ namespace MewtocolNet {
/// Provides a interface for Panasonic PLCs
///
public interface IPlc : IDisposable, INotifyPropertyChanged {
+
+ ///
+ /// Fires when the interface is fully connected to a PLC
+ ///
+ event PlcConnectionEventHandler Connected;
+
+ ///
+ /// Fires when a reconnect attempt was successfull
+ ///
+ event PlcConnectionEventHandler Reconnected;
+
+ ///
+ /// Fires when the interfaces makes a reconnect try to the PLC
+ ///
+ event PlcReconnectEventHandler ReconnectTryStarted;
+
+ ///
+ /// Fires when the plc/interface connection was fully closed
+ ///
+ event PlcConnectionEventHandler Disconnected;
+
+ ///
+ /// Fires when the value of a register changes
+ ///
+ event RegisterChangedEventHandler RegisterChanged;
///
/// The current connection state of the interface
diff --git a/MewtocolNet/MewtocolInterface.cs b/MewtocolNet/MewtocolInterface.cs
index d534b37..45de15e 100644
--- a/MewtocolNet/MewtocolInterface.cs
+++ b/MewtocolNet/MewtocolInterface.cs
@@ -29,6 +29,9 @@ namespace MewtocolNet {
///
public event PlcConnectionEventHandler Reconnected;
+ ///
+ public event PlcReconnectEventHandler ReconnectTryStarted;
+
///
public event PlcConnectionEventHandler Disconnected;
@@ -139,13 +142,7 @@ namespace MewtocolNet {
}
///
- public int BytesPerSecondUpstream {
- get { return bytesPerSecondUpstream; }
- private protected set {
- bytesPerSecondUpstream = value;
- OnPropChange();
- }
- }
+ public int BytesPerSecondUpstream => bytesPerSecondUpstream;
///
public bool IsReceiving {
@@ -157,13 +154,7 @@ namespace MewtocolNet {
}
///
- public int BytesPerSecondDownstream {
- get { return bytesPerSecondDownstream; }
- private protected set {
- bytesPerSecondDownstream = value;
- OnPropChange();
- }
- }
+ public int BytesPerSecondDownstream => bytesPerSecondDownstream;
///
public MewtocolVersion MewtocolVersion {
@@ -302,7 +293,15 @@ namespace MewtocolNet {
if (pollCycleTask != null && !pollCycleTask.IsCompleted) pollCycleTask.Wait();
- OnMajorSocketExceptionWhileConnected();
+ if (IsConnected) {
+
+ tSource.Cancel();
+ isMessageLocked = false;
+
+ Logger.Log("The PLC connection was closed manually", LogLevel.Error, this);
+ OnDisconnect();
+
+ }
}
@@ -348,8 +347,16 @@ namespace MewtocolNet {
//stop the heartbeat timer for the time of retries
StopHeartBeat();
+ var eArgs = new ReconnectArgs(retryCount, tryReconnectAttempts, TimeSpan.FromMilliseconds(tryReconnectDelayMs));
+ ReconnectTryStarted?.Invoke(this, eArgs);
+
+ Reconnected += (s, e) => eArgs.ConnectionSuccess();
+
await ReconnectAsync(tryReconnectDelayMs);
- await Task.Delay(2000);
+
+ if (IsConnected) return;
+
+ await Task.Delay(tryReconnectDelayMs);
retryCount++;
@@ -378,17 +385,7 @@ namespace MewtocolNet {
if (regularSendTask != null && !regularSendTask.IsCompleted) {
//queue self
-
- var t = new Task(() => SendCommandInternalAsync(_msg, onReceiveProgress).Result);
- userInputSendTasks.Enqueue(t);
-
- OnPropChange(nameof(QueuedMessages));
-
- await t;
-
- OnPropChange(nameof(QueuedMessages));
-
- return t.Result;
+ return await EnqueueMessage(_msg, onReceiveProgress);
}
@@ -399,17 +396,12 @@ namespace MewtocolNet {
isMessageLocked = true;
- //wait for the last send task to complete
- //if (regularSendTask != null && !regularSendTask.IsCompleted) await regularSendTask;
-
//send request
regularSendTask = SendTwoDirectionalFrameAsync(_msg, onReceiveProgress);
- //if (regularSendTask == null) return MewtocolFrameResponse.Canceled;
-
try {
- var timeoutAwaiter = await Task.WhenAny(regularSendTask, Task.Delay(2000, tSource.Token));
+ var timeoutAwaiter = await Task.WhenAny(regularSendTask, Task.Delay(sendReceiveTimeoutMs, tSource.Token));
if (timeoutAwaiter != regularSendTask) {
@@ -444,10 +436,52 @@ namespace MewtocolNet {
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;
+
+ }
+
+ }
+
+ 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 {
@@ -763,17 +797,17 @@ namespace MewtocolNet {
}
- private protected void OnMajorSocketExceptionWhileConnected() {
+ private protected void OnSocketExceptionWhileConnected() {
- if (IsConnected) {
+ tSource.Cancel();
- tSource.Cancel();
- isMessageLocked = false;
+ bytesPerSecondDownstream = 0;
+ bytesPerSecondUpstream = 0;
- Logger.Log("The PLC connection was closed", LogLevel.Error, this);
- OnDisconnect();
+ isMessageLocked = false;
+ IsConnected = false;
- }
+ if (reconnectTask == null) StartReconnectTask();
}
@@ -785,16 +819,6 @@ namespace MewtocolNet {
}
- private protected void OnSocketExceptionWhileConnected () {
-
- tSource.Cancel();
- isMessageLocked = false;
- IsConnected = false;
-
- if (reconnectTask == null) StartReconnectTask();
-
- }
-
private protected virtual void OnConnected(PLCInfo plcinf) {
Logger.Log("Connected to PLC", LogLevel.Info, this);
@@ -807,18 +831,23 @@ namespace MewtocolNet {
//notify the registers
GetAllRegisters().Cast().ToList().ForEach(x => x.OnPlcConnected());
+ reconnectTask = null;
IsConnected = true;
-
- Connected?.Invoke(this, new PlcConnectionArgs());
+ isReconnectingStage = false;
+ isConnectingStage = false;
if (!usePoller) {
firstPollTask.RunSynchronously();
}
+ Connected?.Invoke(this, new PlcConnectionArgs());
+
PolledCycle += OnPollCycleDone;
void OnPollCycleDone() {
- firstPollTask.RunSynchronously();
+ if(firstPollTask != null && !firstPollTask.IsCompleted)
+ firstPollTask.RunSynchronously();
+
PolledCycle -= OnPollCycleDone;
}
@@ -829,8 +858,8 @@ namespace MewtocolNet {
IsReceiving = false;
IsSending = false;
- BytesPerSecondDownstream = 0;
- BytesPerSecondUpstream = 0;
+ bytesPerSecondDownstream = 0;
+ bytesPerSecondUpstream = 0;
PollerCycleDurationMs = 0;
isMessageLocked = false;
@@ -856,8 +885,8 @@ namespace MewtocolNet {
IsReceiving = false;
IsSending = false;
- BytesPerSecondDownstream = 0;
- BytesPerSecondUpstream = 0;
+ bytesPerSecondDownstream = 0;
+ bytesPerSecondUpstream = 0;
PollerCycleDurationMs = 0;
PlcInfo = null;
@@ -887,6 +916,9 @@ namespace MewtocolNet {
GetAllRegisters().Cast()
.ToList().ForEach(x => x.OnInterfaceCyclicTimerUpdate((int)cyclicGenericUpdateCounter.Interval));
+ OnPropChange(nameof(BytesPerSecondUpstream));
+ OnPropChange(nameof(BytesPerSecondDownstream));
+
}
private void SetUpstreamStopWatchStart() {
@@ -921,7 +953,7 @@ namespace MewtocolNet {
var perSecUpstream = (double)((bytesTotalCountedUpstream / speedStopwatchUpstr.Elapsed.TotalMilliseconds) * 1000);
if (perSecUpstream <= 10000)
- BytesPerSecondUpstream = (int)Math.Round(perSecUpstream, MidpointRounding.AwayFromZero);
+ bytesPerSecondUpstream = (int)Math.Round(perSecUpstream, MidpointRounding.AwayFromZero);
}
@@ -932,7 +964,7 @@ namespace MewtocolNet {
var perSecDownstream = (double)((bytesTotalCountedDownstream / speedStopwatchDownstr.Elapsed.TotalMilliseconds) * 1000);
if (perSecDownstream <= 10000)
- BytesPerSecondDownstream = (int)Math.Round(perSecDownstream, MidpointRounding.AwayFromZero);
+ bytesPerSecondDownstream = (int)Math.Round(perSecDownstream, MidpointRounding.AwayFromZero);
}
diff --git a/MewtocolNet/MewtocolInterfaceRegisterHandling.cs b/MewtocolNet/MewtocolInterfaceRegisterHandling.cs
index 1cd0e57..0fb4d56 100644
--- a/MewtocolNet/MewtocolInterfaceRegisterHandling.cs
+++ b/MewtocolNet/MewtocolInterfaceRegisterHandling.cs
@@ -17,6 +17,8 @@ namespace MewtocolNet {
///
public abstract partial class MewtocolInterface {
+ private bool heartbeatNeedsRun = false;
+
internal Task heartbeatTask = Task.CompletedTask;
internal Func heartbeatCallbackTask;
@@ -127,9 +129,13 @@ namespace MewtocolNet {
heartbeatNeedsRun = true;
- }
+ if(!PollerActive) {
- private bool heartbeatNeedsRun = false;
+ Task.Run(HeartbeatTickTask);
+
+ }
+
+ }
private async Task HeartbeatTickTask () {
@@ -141,8 +147,7 @@ namespace MewtocolNet {
Logger.LogError("Heartbeat timed out", this);
- //OnSocketExceptionWhileConnected();
- //StartReconnectTask();
+ OnSocketExceptionWhileConnected();
return;
@@ -224,15 +229,7 @@ namespace MewtocolNet {
await memoryManager.PollAllAreasAsync(async () => {
- if (userInputSendTasks != null && userInputSendTasks.Count > 0) {
-
- var t = userInputSendTasks.Dequeue();
-
- t.Start();
-
- await t;
-
- }
+ await RunOneOpenQueuedTask();
});
diff --git a/MewtocolNet/MewtocolInterfaceRequests.cs b/MewtocolNet/MewtocolInterfaceRequests.cs
index 94a0760..3c9bde9 100644
--- a/MewtocolNet/MewtocolInterfaceRequests.cs
+++ b/MewtocolNet/MewtocolInterfaceRequests.cs
@@ -138,7 +138,12 @@ namespace MewtocolNet {
var result = await SendCommandInternalAsync(requeststring);
if (result.Success) {
+
Logger.Log($"Operation mode was changed to {(setRun ? "Run" : "Prog")}", LogLevel.Info, this);
+
+ //directily update the op mode
+ PlcInfo.OperationMode = PlcInfo.OperationMode.SetFlag(OPMode.RunMode, setRun);
+
} else {
Logger.Log("Operation mode change failed", LogLevel.Error, this);
}
diff --git a/MewtocolNet/MewtocolInterfaceTcp.cs b/MewtocolNet/MewtocolInterfaceTcp.cs
index 4e10e69..1f65115 100644
--- a/MewtocolNet/MewtocolInterfaceTcp.cs
+++ b/MewtocolNet/MewtocolInterfaceTcp.cs
@@ -211,30 +211,12 @@ namespace MewtocolNet {
regularSendTask = null;
reconnectTask = Task.CompletedTask;
- //try to abort any non read message
- //await SendNoResponseCommandAsync($"%{GetStationNumber()}#AB");
+ if (await SendCommandInternalAsync($"%{GetStationNumber()}#RT") != null) {
- //get plc info 2 times to clear old stuff from the buffer
+ Logger.Log("Reconnect successfull");
+ OnReconnected();
- OnReconnected();
-
- //var plcinf = await SendCommandAsync($"%{GetStationNumber()}#RT");
-
- //if (plcinf != null) {
-
- // Logger.Log("Reconnect successfull");
-
- // OnReconnected();
-
- // //await base.ConnectAsync();
- // //OnConnected(plcinf);
-
- //} else {
-
- // Logger.Log("Initial connection failed", LogLevel.Error, this);
- // OnDisconnect();
-
- //}
+ }
await Task.CompletedTask;
diff --git a/MewtocolNet/MewtocolNet.csproj b/MewtocolNet/MewtocolNet.csproj
index 7318b21..d87d056 100644
--- a/MewtocolNet/MewtocolNet.csproj
+++ b/MewtocolNet/MewtocolNet.csproj
@@ -41,6 +41,7 @@
+
diff --git a/MewtocolNet/RegisterBuilding/BuilderPatterns/RBuild.cs b/MewtocolNet/RegisterBuilding/BuilderPatterns/RBuild.cs
index 444404d..f4da17f 100644
--- a/MewtocolNet/RegisterBuilding/BuilderPatterns/RBuild.cs
+++ b/MewtocolNet/RegisterBuilding/BuilderPatterns/RBuild.cs
@@ -32,7 +32,21 @@ namespace MewtocolNet.RegisterBuilding.BuilderPatterns {
internal Register Assemble(StepBase stp) => assembler.Assemble(stp.Data);
- //struct constructor
+ //bool constructor
+
+ public StructStp Bool(string fpAddr, string name = null) {
+
+ var data = AddressTools.ParseAddress(fpAddr, name);
+
+ data.dotnetVarType = typeof(bool);
+
+ return new StructStp(data) {
+ builder = this,
+ };
+
+ }
+
+ //struct constructor
public StructStp Struct(string fpAddr, string name = null) where T : struct {
diff --git a/MewtocolNet/Registers/Base/IRegister.cs b/MewtocolNet/Registers/Base/IRegister.cs
index 44380db..fe589e6 100644
--- a/MewtocolNet/Registers/Base/IRegister.cs
+++ b/MewtocolNet/Registers/Base/IRegister.cs
@@ -29,6 +29,11 @@ namespace MewtocolNet.Registers {
///
int PollLevel { get; }
+ ///
+ /// Info string about the memory area
+ ///
+ string MemoryAreaInfo { get; }
+
///
/// The update frequency of the register in Hz
///
@@ -59,6 +64,8 @@ namespace MewtocolNet.Registers {
///
uint MemoryAddress { get; }
+ string MemoryAreaHash { get; }
+
///
/// Gets the value of the register as the plc representation string
///
diff --git a/MewtocolNet/Registers/Base/Register.cs b/MewtocolNet/Registers/Base/Register.cs
index f448195..f1598a3 100644
--- a/MewtocolNet/Registers/Base/Register.cs
+++ b/MewtocolNet/Registers/Base/Register.cs
@@ -27,7 +27,7 @@ namespace MewtocolNet.Registers {
internal List boundProperties = new List();
internal Type underlyingSystemType;
- internal IMemoryArea underlyingMemory;
+ internal AreaBase underlyingMemory;
internal bool autoGenerated;
internal bool isAnonymous;
@@ -94,6 +94,9 @@ namespace MewtocolNet.Registers {
}
}
+ public string MemoryAreaInfo => underlyingMemory.GetName();
+
+ public string MemoryAreaHash => underlyingMemory.GetHashCode().ToString();
internal Register() { }
diff --git a/MewtocolNet/Registers/Classes/BoolRegister.cs b/MewtocolNet/Registers/Classes/BoolRegister.cs
index 59fa41c..0665066 100644
--- a/MewtocolNet/Registers/Classes/BoolRegister.cs
+++ b/MewtocolNet/Registers/Classes/BoolRegister.cs
@@ -74,6 +74,17 @@ namespace MewtocolNet.Registers {
///
public override uint GetRegisterAddressLen() => 1;
+ internal override object SetValueFromBytes(byte[] bytes) {
+
+ AddSuccessRead();
+
+ var parsed = PlcValueParser.Parse(this, bytes);
+
+ UpdateHoldingValue(parsed);
+ return parsed;
+
+ }
+
}
}
diff --git a/MewtocolNet/Registers/Classes/StringRegister.cs b/MewtocolNet/Registers/Classes/StringRegister.cs
index 40b7cbf..81dd5fe 100644
--- a/MewtocolNet/Registers/Classes/StringRegister.cs
+++ b/MewtocolNet/Registers/Classes/StringRegister.cs
@@ -123,9 +123,7 @@ namespace MewtocolNet.Registers {
//if string correct the sizing of the byte hint was wrong
var reservedSize = BitConverter.ToInt16(bytes, 0);
- if (reservedStringLength != reservedSize &&
- attachedInterface.PlcInfo.IsRunMode &&
- (attachedInterface.pollerTaskStopped || attachedInterface.pollerFirstCycleCompleted))
+ if (reservedStringLength != reservedSize && attachedInterface.PlcInfo.IsRunMode)
throw new NotSupportedException(
$"The STRING register at {GetMewName()} is not correctly sized, " +
$"the size should be STRING[{reservedSize}] instead of STRING[{reservedStringLength}]"
diff --git a/MewtocolNet/UnderlyingRegisters/DTArea.cs b/MewtocolNet/UnderlyingRegisters/Areas/AreaBase.cs
similarity index 81%
rename from MewtocolNet/UnderlyingRegisters/DTArea.cs
rename to MewtocolNet/UnderlyingRegisters/Areas/AreaBase.cs
index d745547..eb9215a 100644
--- a/MewtocolNet/UnderlyingRegisters/DTArea.cs
+++ b/MewtocolNet/UnderlyingRegisters/Areas/AreaBase.cs
@@ -5,7 +5,8 @@ using System.Text;
using System.Threading.Tasks;
namespace MewtocolNet.UnderlyingRegisters {
- public class DTArea : IMemoryArea {
+
+ public abstract class AreaBase {
private MewtocolInterface mewInterface;
@@ -23,7 +24,7 @@ namespace MewtocolNet.UnderlyingRegisters {
public ulong AddressStart => addressStart;
public ulong AddressEnd => addressEnd;
- internal DTArea(MewtocolInterface mewIf) {
+ internal AreaBase(MewtocolInterface mewIf) {
mewInterface = mewIf;
@@ -116,26 +117,12 @@ namespace MewtocolNet.UnderlyingRegisters {
}
- private string GetMewtocolIdent() {
-
- StringBuilder asciistring = new StringBuilder("D");
- asciistring.Append(AddressStart.ToString().PadLeft(5, '0'));
- asciistring.Append(AddressEnd.ToString().PadLeft(5, '0'));
- return asciistring.ToString();
-
- }
-
- private string GetMewtocolIdent(ulong addStart, ulong addEnd) {
-
- StringBuilder asciistring = new StringBuilder("D");
- asciistring.Append(addStart.ToString().PadLeft(5, '0'));
- asciistring.Append(addEnd.ToString().PadLeft(5, '0'));
- return asciistring.ToString();
-
- }
-
public override string ToString() => $"DT{AddressStart}-{AddressEnd}";
+ public virtual string GetName() => $"{ToString()} ({managedRegisters.Count} Registers)";
+
+ public string GetHash() => GetHashCode().ToString();
+
}
}
diff --git a/MewtocolNet/UnderlyingRegisters/Areas/DTArea.cs b/MewtocolNet/UnderlyingRegisters/Areas/DTArea.cs
new file mode 100644
index 0000000..e6a567e
--- /dev/null
+++ b/MewtocolNet/UnderlyingRegisters/Areas/DTArea.cs
@@ -0,0 +1,19 @@
+using MewtocolNet.Registers;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MewtocolNet.UnderlyingRegisters {
+
+ public class DTArea : AreaBase, IMemoryArea {
+
+ internal DTArea(MewtocolInterface mewIf) : base(mewIf) { }
+
+ public override string ToString() => $"DT{AddressStart}-{AddressEnd}";
+
+ public override string GetName() => $"{ToString()} ({managedRegisters.Count} Registers)";
+
+ }
+
+}
diff --git a/MewtocolNet/UnderlyingRegisters/IMemoryArea.cs b/MewtocolNet/UnderlyingRegisters/Areas/IMemoryArea.cs
similarity index 91%
rename from MewtocolNet/UnderlyingRegisters/IMemoryArea.cs
rename to MewtocolNet/UnderlyingRegisters/Areas/IMemoryArea.cs
index e593fa4..3bc41a2 100644
--- a/MewtocolNet/UnderlyingRegisters/IMemoryArea.cs
+++ b/MewtocolNet/UnderlyingRegisters/Areas/IMemoryArea.cs
@@ -4,6 +4,8 @@ namespace MewtocolNet.UnderlyingRegisters {
internal interface IMemoryArea {
+ string GetName();
+
byte[] GetUnderlyingBytes(Register reg);
void SetUnderlyingBytes(Register reg, byte[] bytes);
diff --git a/MewtocolNet/UnderlyingRegisters/Areas/WRArea.cs b/MewtocolNet/UnderlyingRegisters/Areas/WRArea.cs
new file mode 100644
index 0000000..d980672
--- /dev/null
+++ b/MewtocolNet/UnderlyingRegisters/Areas/WRArea.cs
@@ -0,0 +1,19 @@
+using MewtocolNet.Registers;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MewtocolNet.UnderlyingRegisters {
+
+ public class WRArea : AreaBase, IMemoryArea {
+
+ internal WRArea(MewtocolInterface mewIf) : base(mewIf) { }
+
+ public override string ToString() => $"DT{AddressStart}-{AddressEnd}";
+
+ public override string GetName() => $"{ToString()} ({managedRegisters.Count} Registers)";
+
+ }
+
+}
diff --git a/MewtocolNet/UnderlyingRegisters/MemoryAreaManager.cs b/MewtocolNet/UnderlyingRegisters/MemoryAreaManager.cs
index 5d63054..a1f5e44 100644
--- a/MewtocolNet/UnderlyingRegisters/MemoryAreaManager.cs
+++ b/MewtocolNet/UnderlyingRegisters/MemoryAreaManager.cs
@@ -92,17 +92,7 @@ namespace MewtocolNet.UnderlyingRegisters {
TestPollLevelExistence(reg);
- switch (reg.RegisterType) {
- case RegisterPrefix.X:
- case RegisterPrefix.Y:
- case RegisterPrefix.R:
- AddToWRArea(reg);
- break;
- case RegisterPrefix.DT:
- case RegisterPrefix.DDT:
- AddToDTArea(reg);
- break;
- }
+ AddToArea(reg, reg.RegisterType);
}
@@ -155,73 +145,33 @@ namespace MewtocolNet.UnderlyingRegisters {
}
- private bool AddToWRArea(Register insertReg) {
-
- var pollLevelFound = pollLevels.FirstOrDefault(x => x.level == insertReg.pollLevel);
-
- List collection = null;
-
- switch (insertReg.RegisterType) {
- case RegisterPrefix.X:
- collection = pollLevelFound.externalRelayInAreas;
- break;
- case RegisterPrefix.Y:
- collection = pollLevelFound.externalRelayOutAreas;
- break;
- case RegisterPrefix.R:
- collection = pollLevelFound.internalRelayAreas;
- break;
- }
-
- WRArea area = collection.FirstOrDefault(x => x.AddressStart == insertReg.MemoryAddress);
-
- if (area != null) {
-
- var existingLinkedRegister = area.linkedRegisters
- .FirstOrDefault(x => x.CompareIsDuplicate(insertReg));
-
- if (existingLinkedRegister != null) {
-
- return false;
-
- } else {
-
- insertReg.underlyingMemory = area;
- area.linkedRegisters.Add(insertReg);
- return true;
-
- }
-
- } else {
-
- area = new WRArea(mewInterface) {
- registerType = insertReg.RegisterType,
- addressStart = insertReg.MemoryAddress,
- };
-
- insertReg.underlyingMemory = area;
- area.linkedRegisters.Add(insertReg);
-
- collection.Add(area);
- collection = collection.OrderBy(x => x.AddressStart).ToList();
-
- return true;
-
- }
-
- }
-
- private void AddToDTArea(Register insertReg) {
+ private void AddToArea(Register insertReg, RegisterPrefix prefix) {
uint regInsAddStart = insertReg.MemoryAddress;
uint regInsAddEnd = insertReg.MemoryAddress + insertReg.GetRegisterAddressLen() - 1;
- DTArea targetArea = null;
+ AreaBase targetArea = null;
var pollLevelFound = pollLevels.FirstOrDefault(x => x.level == insertReg.pollLevel);
- var dataAreas = pollLevelFound.dataAreas;
+ List pollLevelAreas = null;
- foreach (var dtArea in dataAreas) {
+ switch (prefix) {
+ case RegisterPrefix.X:
+ pollLevelAreas = pollLevelFound.externalRelayInAreas;
+ break;
+ case RegisterPrefix.Y:
+ pollLevelAreas = pollLevelFound.externalRelayOutAreas;
+ break;
+ case RegisterPrefix.R:
+ pollLevelAreas = pollLevelFound.internalRelayAreas;
+ break;
+ case RegisterPrefix.DT:
+ case RegisterPrefix.DDT:
+ pollLevelAreas = pollLevelFound.dataAreas;
+ break;
+ }
+
+ foreach (var dtArea in pollLevelAreas) {
bool addressInsideArea = regInsAddStart >= dtArea.AddressStart &&
regInsAddEnd <= dtArea.AddressEnd;
@@ -278,7 +228,7 @@ namespace MewtocolNet.UnderlyingRegisters {
};
targetArea.BoundaryUdpdate();
- dataAreas.Add(targetArea);
+ pollLevelAreas.Add(targetArea);
}
@@ -358,7 +308,7 @@ namespace MewtocolNet.UnderlyingRegisters {
}
//update registers in poll level
- foreach (var dtArea in pollLevel.dataAreas.ToArray()) {
+ foreach (var dtArea in pollLevel.GetAllAreas().ToArray()) {
//set the whole memory area at once
await dtArea.RequestByteReadAsync(dtArea.AddressStart, dtArea.AddressEnd);
@@ -493,10 +443,9 @@ namespace MewtocolNet.UnderlyingRegisters {
foreach (var lvl in pollLevels) {
registers.AddRange(lvl.dataAreas.SelectMany(x => x.managedRegisters).SelectMany(x => x.Linked));
-
- registers.AddRange(lvl.internalRelayAreas.SelectMany(x => x.linkedRegisters));
- registers.AddRange(lvl.externalRelayInAreas.SelectMany(x => x.linkedRegisters));
- registers.AddRange(lvl.externalRelayOutAreas.SelectMany(x => x.linkedRegisters));
+ registers.AddRange(lvl.internalRelayAreas.SelectMany(x => x.managedRegisters).SelectMany(x => x.Linked));
+ registers.AddRange(lvl.externalRelayInAreas.SelectMany(x => x.managedRegisters).SelectMany(x => x.Linked));
+ registers.AddRange(lvl.externalRelayOutAreas.SelectMany(x => x.managedRegisters).SelectMany(x => x.Linked));
}
diff --git a/MewtocolNet/UnderlyingRegisters/PollLevel.cs b/MewtocolNet/UnderlyingRegisters/PollLevel.cs
index 25fe5c5..d70186d 100644
--- a/MewtocolNet/UnderlyingRegisters/PollLevel.cs
+++ b/MewtocolNet/UnderlyingRegisters/PollLevel.cs
@@ -6,30 +6,43 @@ namespace MewtocolNet.UnderlyingRegisters {
internal int lastReadTimeMs = 0;
- internal PollLevel(int wrSize, int dtSize) {
-
- externalRelayInAreas = new List(wrSize * 16);
- externalRelayOutAreas = new List(wrSize * 16);
- internalRelayAreas = new List(wrSize * 16);
- dataAreas = new List(dtSize);
-
- }
-
internal int level;
// WR areas are n of words, each word has 2 bytes representing the "special address component"
//X WR
- internal List externalRelayInAreas;
+ internal List externalRelayInAreas;
//Y WR
- internal List externalRelayOutAreas;
+ internal List externalRelayOutAreas;
//R WR
- internal List internalRelayAreas;
+ internal List internalRelayAreas;
//DT
- internal List dataAreas;
+ internal List dataAreas;
+
+ internal PollLevel(int wrSize, int dtSize) {
+
+ externalRelayInAreas = new List(wrSize * 16);
+ externalRelayOutAreas = new List(wrSize * 16);
+ internalRelayAreas = new List(wrSize * 16);
+ dataAreas = new List(dtSize);
+
+ }
+
+ internal IEnumerable GetAllAreas () {
+
+ List combined = new List();
+
+ combined.AddRange(internalRelayAreas);
+ combined.AddRange(externalRelayInAreas);
+ combined.AddRange(externalRelayOutAreas);
+ combined.AddRange(dataAreas);
+
+ return combined;
+
+ }
}
diff --git a/MewtocolNet/UnderlyingRegisters/WRArea.cs b/MewtocolNet/UnderlyingRegisters/WRArea.cs
deleted file mode 100644
index a1ebfb5..0000000
--- a/MewtocolNet/UnderlyingRegisters/WRArea.cs
+++ /dev/null
@@ -1,92 +0,0 @@
-using MewtocolNet.Registers;
-using System.Collections.Generic;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace MewtocolNet.UnderlyingRegisters {
-
- public class WRArea : IMemoryArea {
-
- private MewtocolInterface mewInterface;
-
- internal RegisterPrefix registerType;
- internal ulong addressStart;
-
- internal byte[] wordData = new byte[2];
-
- internal List linkedRegisters = new List();
-
- public ulong AddressStart => addressStart;
-
- internal WRArea(MewtocolInterface mewIf) {
-
- mewInterface = mewIf;
-
- }
-
- public void UpdateAreaRegisterValues() {
-
-
-
- }
- public void SetUnderlyingBytes(Register reg, byte[] bytes) {
-
-
- }
-
- public byte[] GetUnderlyingBytes(Register reg) {
-
- return null;
-
- }
-
- public async Task ReadRegisterAsync(Register reg) {
-
- return true;
-
- }
-
- public async Task WriteRegisterAsync(Register reg, byte[] bytes) {
-
- return true;
-
- }
-
- public string GetMewtocolIdent() => GetMewtocolIdentsAllBits();
-
- public string GetMewtocolIdentsAllBits() {
-
- StringBuilder asciistring = new StringBuilder();
-
- for (byte i = 0; i < 16; i++) {
-
- asciistring.Append(GetMewtocolIdentSingleBit(i));
-
- }
-
- return asciistring.ToString();
-
- }
-
- public string GetMewtocolIdentSingleBit(byte specialAddress) {
-
- //(R|X|Y)(area add [3] + special add [1])
- StringBuilder asciistring = new StringBuilder();
-
- string prefix = registerType.ToString();
- string mem = AddressStart.ToString();
- string sp = specialAddress.ToString("X1");
-
- asciistring.Append(prefix);
- asciistring.Append(mem.PadLeft(3, '0'));
- asciistring.Append(sp);
-
- return asciistring.ToString();
-
- }
-
- public override string ToString() => $"{registerType}{AddressStart} 0-F";
-
- }
-
-}