diff --git a/Examples/ExampleScenarios.cs b/Examples/ExampleScenarios.cs
index d4c82d6..24dcd95 100644
--- a/Examples/ExampleScenarios.cs
+++ b/Examples/ExampleScenarios.cs
@@ -9,6 +9,7 @@ using System.Collections.Generic;
using MewtocolNet.Registers;
using System.Diagnostics;
using System.Text;
+using Microsoft.Win32;
namespace Examples;
@@ -17,7 +18,7 @@ public class ExampleScenarios {
public void SetupLogger () {
//attaching the logger
- Logger.LogLevel = LogLevel.Info;
+ Logger.LogLevel = LogLevel.Error;
Logger.OnNewLogMessage((date, level, msg) => {
if (level == LogLevel.Error) Console.ForegroundColor = ConsoleColor.Red;
@@ -46,6 +47,7 @@ public class ExampleScenarios {
interf.WithRegisterCollection(registers).WithPoller();
await interf.ConnectAsync();
+ await interf.AwaitFirstDataAsync();
_ = Task.Factory.StartNew(async () => {
@@ -54,22 +56,18 @@ public class ExampleScenarios {
//flip the bool register each tick and wait for it to be registered
//await interf.SetRegisterAsync(nameof(registers.TestBool1), !registers.TestBool1);
- Console.Title = $"Polling Paused: {interf.PollingPaused}, " +
- $"Poller active: {interf.PollerActive}, " +
+ Console.Title =
$"Speed UP: {interf.BytesPerSecondUpstream} B/s, " +
$"Speed DOWN: {interf.BytesPerSecondDownstream} B/s, " +
- $"Poll delay: {interf.PollerDelayMs} ms, " +
+ $"Poll cycle: {interf.PollerCycleDurationMs} ms, " +
$"Queued MSGs: {interf.QueuedMessages}";
Console.Clear();
Console.WriteLine("Underlying registers on tick: \n");
- foreach (var register in interf.Registers) {
-
+ foreach (var register in interf.Registers)
Console.WriteLine($"{register.ToString(true)}");
- }
-
Console.WriteLine($"{registers.TestBool1}");
Console.WriteLine($"{registers.TestDuplicate}");
@@ -192,12 +190,12 @@ public class ExampleScenarios {
public async Task RunReadTest () {
//setting up a new PLC interface and register collection
- MewtocolInterface interf = new MewtocolInterface("192.168.115.210");
+ MewtocolInterface interf = new MewtocolInterface("192.168.115.210").WithPoller();
//auto add all built registers to the interface
var builder = RegBuilder.ForInterface(interf);
var r0reg = builder.FromPlcRegName("R0").Build();
- builder.FromPlcRegName("R1").Build();
+ builder.FromPlcRegName("R1", "Testname").Build();
builder.FromPlcRegName("R1F").Build();
builder.FromPlcRegName("R101A").Build();
@@ -205,35 +203,57 @@ public class ExampleScenarios {
builder.FromPlcRegName("DDT36").AsPlcType(PlcVarType.DINT).Build();
builder.FromPlcRegName("DT200").AsBytes(30).Build();
- builder.FromPlcRegName("DDT38").AsPlcType(PlcVarType.TIME).Build();
- //builder.FromPlcRegName("DT40").AsPlcType(PlcVarType.STRING).Build();
+ var timeReg = builder.FromPlcRegName("DDT38").AsPlcType(PlcVarType.TIME).Build();
+ var stringReg = builder.FromPlcRegName("DT40").AsPlcType(PlcVarType.STRING).Build();
//connect
await interf.ConnectAsync();
- //var res = await interf.SendCommandAsync("%01#RCSR000F");
+ //await first register data
+ await interf.AwaitFirstDataAsync();
- while(true) {
+ _ = Task.Factory.StartNew(async () => {
- await interf.SetRegisterAsync(r0reg, !(bool)r0reg.Value);
- await interf.SetRegisterAsync(shortReg, (short)new Random().Next(0, 100));
+ void setTitle () {
- var sw = Stopwatch.StartNew();
-
- foreach (var reg in interf.Registers) {
-
- await reg.ReadAsync();
-
- Console.WriteLine($"Register {reg.ToString()}");
+ Console.Title =
+ $"Speed UP: {interf.BytesPerSecondUpstream} B/s, " +
+ $"Speed DOWN: {interf.BytesPerSecondDownstream} B/s, " +
+ $"Poll cycle: {interf.PollerCycleDurationMs} ms, " +
+ $"Queued MSGs: {interf.QueuedMessages}";
}
+ while (interf.IsConnected) {
+ setTitle();
+ await Task.Delay(1000);
+ }
+
+ setTitle();
+
+ });
+
+ while (interf.IsConnected) {
+
+ var sw = Stopwatch.StartNew();
+
+ //set bool
+ await r0reg.WriteAsync(!(bool)r0reg.Value);
+
+ //set random num
+ await shortReg.WriteAsync((short)new Random().Next(0, 100));
+ await stringReg.WriteAsync($"_{DateTime.Now.Second}s_");
+
sw.Stop();
- Console.WriteLine($"Total read time for registers: {sw.Elapsed.TotalMilliseconds:N0}ms");
+ foreach (var reg in interf.Registers)
+ Console.WriteLine(reg.ToString());
+
+ Console.WriteLine($"Total write time for registers: {sw.Elapsed.TotalMilliseconds:N0}ms");
Console.WriteLine();
+ //await Task.Delay(new Random().Next(0, 10000));
await Task.Delay(1000);
}
@@ -244,28 +264,24 @@ public class ExampleScenarios {
public async Task MultiFrameTest() {
var preLogLevel = Logger.LogLevel;
- Logger.LogLevel = LogLevel.Error;
+ Logger.LogLevel = LogLevel.Critical;
//setting up a new PLC interface and register collection
- MewtocolInterface interf = new MewtocolInterface("192.168.115.210");
+ MewtocolInterface interf = new MewtocolInterface("192.168.115.210") {
+ ConnectTimeout = 3000,
+ };
//auto add all built registers to the interface
var builder = RegBuilder.ForInterface(interf);
var r0reg = builder.FromPlcRegName("R0").Build();
builder.FromPlcRegName("R1").Build();
- builder.FromPlcRegName("R1F").Build();
- builder.FromPlcRegName("R60A").Build();
- builder.FromPlcRegName("R61A").Build();
- builder.FromPlcRegName("R62A").Build();
- builder.FromPlcRegName("R63A").Build();
- builder.FromPlcRegName("R64A").Build();
- builder.FromPlcRegName("R65A").Build();
- builder.FromPlcRegName("R66A").Build();
- builder.FromPlcRegName("R67A").Build();
- builder.FromPlcRegName("R68A").Build();
- builder.FromPlcRegName("R69A").Build();
- builder.FromPlcRegName("R70A").Build();
- builder.FromPlcRegName("R71A").Build();
+ builder.FromPlcRegName("DT0").AsBytes(100).Build();
+
+ for (int i = 1; i < 100; i++) {
+
+ builder.FromPlcRegName($"R{i}A").Build();
+
+ }
//connect
await interf.ConnectAsync();
@@ -273,36 +289,18 @@ public class ExampleScenarios {
Console.WriteLine("Poller cycle started");
var sw = Stopwatch.StartNew();
- await interf.RunPollerCylceManual(false);
+ int cmdCount = await interf.RunPollerCylceManual();
sw.Stop();
Console.WriteLine("Poller cycle finished");
- Console.WriteLine($"Single frame excec time: {sw.ElapsedMilliseconds:N0}ms");
+ Console.WriteLine($"Single frame excec time: {sw.ElapsedMilliseconds:N0}ms for {cmdCount} commands");
interf.Disconnect();
await Task.Delay(1000);
- await interf.ConnectAsync();
-
- sw = Stopwatch.StartNew();
-
- Console.WriteLine("Poller cycle started");
-
- await interf.RunPollerCylceManual(true);
-
- sw.Stop();
-
- Console.WriteLine("Poller cycle finished");
-
- Console.WriteLine($"Multi frame excec time: {sw.ElapsedMilliseconds:N0}ms");
-
- Logger.LogLevel = preLogLevel;
-
- await Task.Delay(10000);
-
}
}
diff --git a/MewtocolNet/DynamicInterface.cs b/MewtocolNet/DynamicInterface.cs
index 357bd52..2344bba 100644
--- a/MewtocolNet/DynamicInterface.cs
+++ b/MewtocolNet/DynamicInterface.cs
@@ -5,6 +5,7 @@ using MewtocolNet.Registers;
using System;
using System.Collections;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
@@ -19,25 +20,32 @@ namespace MewtocolNet
///
public partial class MewtocolInterface {
- ///
- /// True if the auto poller is currently paused
- ///
- public bool PollingPaused => pollerIsPaused;
+ internal event Action PolledCycle;
+
+ internal volatile bool pollerTaskStopped = true;
+ internal volatile bool pollerFirstCycle;
+
+ internal bool usePoller = false;
+
+ private int tcpMessagesSentThisCycle = 0;
+
+ private int pollerCycleDurationMs;
///
/// True if the poller is actvice (can be paused)
///
public bool PollerActive => !pollerTaskStopped;
- internal event Action PolledCycle;
-
- internal volatile bool pollerTaskRunning;
- internal volatile bool pollerTaskStopped;
- internal volatile bool pollerIsPaused;
- internal volatile bool pollerFirstCycle = false;
-
- internal bool usePoller = false;
- internal bool pollerUseMultiFrame = false;
+ ///
+ /// Current poller cycle duration
+ ///
+ public int PollerCycleDurationMs {
+ get => pollerCycleDurationMs;
+ private set {
+ pollerCycleDurationMs = value;
+ OnPropChange();
+ }
+ }
#region Register Polling
@@ -46,54 +54,20 @@ namespace MewtocolNet
///
internal void KillPoller() {
- pollerTaskRunning = false;
pollerTaskStopped = true;
-
ClearRegisterVals();
}
- ///
- /// Pauses the polling and waits for the last message to be sent
- ///
- ///
- public async Task PausePollingAsync() {
-
- if (!pollerTaskRunning)
- return;
-
- pollerTaskRunning = false;
-
- while (!pollerIsPaused) {
-
- if (pollerIsPaused)
- break;
-
- await Task.Delay(10);
-
- }
-
- pollerTaskRunning = false;
-
- }
-
- ///
- /// Resumes the polling
- ///
- public void ResumePolling() {
-
- pollerTaskRunning = true;
-
- }
-
///
/// Attaches a continous reader that reads back the Registers and Contacts
///
internal void AttachPoller() {
- if (pollerTaskRunning)
+ if (!pollerTaskStopped)
return;
+ PollerCycleDurationMs = 0;
pollerFirstCycle = true;
Task.Run(Poll);
@@ -104,14 +78,18 @@ namespace MewtocolNet
/// Runs a single poller cycle manually,
/// useful if you want to use a custom update frequency
///
- ///
- public async Task RunPollerCylceManual (bool useMultiFrame = false) {
+ /// The number of inidvidual mewtocol commands sent
+ public async Task RunPollerCylceManual () {
- if (useMultiFrame) {
- await OnMultiFrameCycle();
- } else {
- await OnSingleFrameCycle();
- }
+ if (!pollerTaskStopped)
+ throw new NotSupportedException($"The poller is already running, " +
+ $"please make sure there is no polling active before calling {nameof(RunPollerCylceManual)}");
+
+ tcpMessagesSentThisCycle = 0;
+
+ await OnMultiFrameCycle();
+
+ return tcpMessagesSentThisCycle;
}
@@ -120,78 +98,49 @@ namespace MewtocolNet
Logger.Log("Poller is attaching", LogLevel.Info, this);
- int iteration = 0;
-
pollerTaskStopped = false;
- pollerTaskRunning = true;
- pollerIsPaused = false;
while (!pollerTaskStopped) {
- if (iteration >= Registers.Count + 1) {
- iteration = 0;
- //invoke cycle polled event
- InvokePolledCycleDone();
- continue;
- }
+ tcpMessagesSentThisCycle = 0;
- if(pollerUseMultiFrame) {
- await OnMultiFrameCycle();
- } else {
- await OnSingleFrameCycle();
+ await OnMultiFrameCycle();
+
+ if(!IsConnected) {
+ pollerTaskStopped = true;
+ return;
}
pollerFirstCycle = false;
-
- iteration++;
-
- pollerIsPaused = !pollerTaskRunning;
+ InvokePolledCycleDone();
}
- pollerIsPaused = false;
-
- }
-
- private async Task OnSingleFrameCycle () {
-
- foreach (var reg in Registers) {
-
- if (reg.IsAllowedRegisterGenericType()) {
-
- var lastVal = reg.Value;
-
- var rwReg = (IRegisterInternal)reg;
-
- var readout = await rwReg.ReadAsync();
-
- if (lastVal != readout) {
-
- rwReg.SetValueFromPLC(readout);
- InvokeRegisterChanged(reg);
-
- }
-
- }
-
- }
-
- await GetPLCInfoAsync();
-
}
private async Task OnMultiFrameCycle () {
+ var sw = Stopwatch.StartNew();
+
await UpdateRCPRegisters();
+ await UpdateDTRegisters();
+
await GetPLCInfoAsync();
+ sw.Stop();
+ PollerCycleDurationMs = (int)sw.ElapsedMilliseconds;
+
}
+ #endregion
+
+ #region Smart register polling methods
+
private async Task UpdateRCPRegisters () {
//build booleans
- var rcpList = Registers.Where(x => x.GetType() == typeof(BoolRegister))
+ var rcpList = RegistersUnderlying.Where(x => x.GetType() == typeof(BoolRegister))
.Select(x => (BoolRegister)x)
.ToArray();
@@ -216,6 +165,8 @@ namespace MewtocolNet
string rcpRequest = rcpString.ToString();
var result = await SendCommandAsync(rcpRequest);
+ if (!result.Success) return;
+
var resultBitArray = result.Response.ParseRCMultiBit();
for (int k = 0; k < resultBitArray.Length; k++) {
@@ -233,9 +184,27 @@ namespace MewtocolNet
}
- internal void PropertyRegisterWasSet(string propName, object value) {
+ private async Task UpdateDTRegisters () {
- _ = SetRegisterAsync(GetRegister(propName), value);
+ foreach (var reg in RegistersUnderlying) {
+
+ var type = reg.GetType();
+
+ if(reg.RegisterType.IsNumericDTDDT() || reg.RegisterType == RegisterType.DT_BYTE_RANGE) {
+
+ var lastVal = reg.Value;
+ var rwReg = (IRegisterInternal)reg;
+ var readout = await rwReg.ReadAsync();
+ if (readout == null) return;
+
+ if (lastVal != readout) {
+ rwReg.SetValueFromPLC(readout);
+ InvokeRegisterChanged(reg);
+ }
+
+ }
+
+ }
}
@@ -409,7 +378,7 @@ namespace MewtocolNet
throw MewtocolException.DupeNameRegister(builtRegister);
builtRegister.attachedInterface = this;
- Registers.Add(builtRegister);
+ RegistersUnderlying.Add(builtRegister);
}
@@ -422,7 +391,7 @@ namespace MewtocolNet
throw MewtocolException.DupeNameRegister(register);
register.attachedInterface = this;
- Registers.Add(register);
+ RegistersUnderlying.Add(register);
}
@@ -458,7 +427,7 @@ namespace MewtocolNet
///
public IRegister GetRegister(string name) {
- return Registers.FirstOrDefault(x => x.Name == name);
+ return RegistersUnderlying.FirstOrDefault(x => x.Name == name);
}
@@ -471,7 +440,7 @@ namespace MewtocolNet
///
public IEnumerable GetAllRegisters() {
- return Registers.Cast();
+ return RegistersUnderlying.Cast();
}
@@ -479,6 +448,12 @@ namespace MewtocolNet
#region Event Invoking
+ internal void PropertyRegisterWasSet(string propName, object value) {
+
+ _ = SetRegisterAsync(GetRegister(propName), value);
+
+ }
+
internal void InvokeRegisterChanged(IRegister reg) {
RegisterChanged?.Invoke(reg);
diff --git a/MewtocolNet/IRegister.cs b/MewtocolNet/IRegister.cs
index 6bd6bca..18aa8d8 100644
--- a/MewtocolNet/IRegister.cs
+++ b/MewtocolNet/IRegister.cs
@@ -1,4 +1,5 @@
using System;
+using System.Text;
using System.Threading.Tasks;
namespace MewtocolNet {
@@ -38,6 +39,12 @@ namespace MewtocolNet {
///
int MemoryAddress { get; }
+ ///
+ /// Gets the value of the register as the plc representation string
+ ///
+ ///
+ string GetAsPLC();
+
///
/// Builds a readable string with all important register informations
///
@@ -49,16 +56,17 @@ namespace MewtocolNet {
string ToString(bool detailed);
///
- /// Sets the register value in the plc async
+ /// Reads the register value async from the plc
///
- /// True if successful
- Task SetValueAsync();
+ /// The register value
+ Task