Made registers use the IRegister interface

- cleanup and refactoring
- fully implemented auto prop register generator unit tests #4
- added plc test program c30 fpx-h
- fixed bitarray setback
- cleaned up examples and added new ones with addition of attributes for later additions
This commit is contained in:
Felix Weiß
2023-06-15 20:04:38 +02:00
parent 6ca8e9de96
commit 09f4da54a9
23 changed files with 1478 additions and 811 deletions

View File

@@ -0,0 +1,173 @@
using MewtocolNet.Logging;
using MewtocolNet;
using System;
using System.Reflection;
using System.Threading.Tasks;
using System.Collections;
namespace Examples;
public class ExampleScenarios {
public static bool MewtocolLoggerEnabled = false;
public void SetupLogger () {
//attaching the logger
Logger.LogLevel = LogLevel.Verbose;
Logger.OnNewLogMessage((date, msg) => {
if(MewtocolLoggerEnabled)
Console.WriteLine($"{date.ToString("HH:mm:ss")} {msg}");
});
}
[Scenario("Permament connection with poller")]
public async Task RunCyclicPollerAsync () {
Console.WriteLine("Starting poller scenario");
int runTime = 10000;
int remainingTime = runTime;
//setting up a new PLC interface and register collection
MewtocolInterface interf = new MewtocolInterface("192.168.115.210");
TestRegisters registers = new TestRegisters();
//attaching the register collection and an automatic poller
interf.WithRegisterCollection(registers).WithPoller();
await interf.ConnectAsync();
_ = Task.Factory.StartNew(async () => {
while (interf.IsConnected) {
//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}, " +
$"Speed UP: {interf.BytesPerSecondUpstream} B/s, " +
$"Speed DOWN: {interf.BytesPerSecondDownstream} B/s, " +
$"Poll delay: {interf.PollerDelayMs} ms, " +
$"Queued MSGs: {interf.QueuedMessages}";
Console.Clear();
Console.WriteLine("Underlying registers on tick: \n");
foreach (var register in interf.Registers) {
Console.WriteLine($"{register.GetCombinedName()} / {register.GetRegisterPLCName()} - Value: {register.GetValueString()}");
}
Console.WriteLine($"{registers.TestBool1}");
Console.WriteLine($"{registers.TestDuplicate}");
remainingTime -= 1000;
Console.WriteLine($"\nStopping in: {remainingTime}ms");
await Task.Delay(1000);
}
});
await Task.Delay(runTime);
interf.Disconnect();
}
[Scenario("Dispose and disconnect connection")]
public async Task RunDisposalAndDisconnectAsync () {
//automatic disposal
using (var interf = new MewtocolInterface("192.168.115.210")) {
await interf.ConnectAsync();
if (interf.IsConnected) {
Console.WriteLine("Opened connection");
await Task.Delay(5000);
}
}
Console.WriteLine("Disposed, closed connection");
//manual close
var interf2 = new MewtocolInterface("192.168.115.210");
await interf2.ConnectAsync();
if (interf2.IsConnected) {
Console.WriteLine("Opened connection");
await Task.Delay(5000);
}
interf2.Disconnect();
Console.WriteLine("Disconnected, closed connection");
}
[Scenario("Test auto enums and bitwise, needs the example program from MewtocolNet/PLC_Test")]
public async Task RunEnumsBitwiseAsync () {
Console.WriteLine("Starting auto enums and bitwise");
//setting up a new PLC interface and register collection
MewtocolInterface interf = new MewtocolInterface("192.168.115.210");
TestRegistersEnumBitwise registers = new TestRegistersEnumBitwise();
//attaching the register collection and an automatic poller
interf.WithRegisterCollection(registers).WithPoller();
registers.PropertyChanged += (s, e) => {
Console.Clear();
var props = registers.GetType().GetProperties();
foreach (var prop in props) {
var val = prop.GetValue(registers);
string printVal = val?.ToString() ?? "null";
if (val is BitArray bitarr) {
printVal = bitarr.ToBitString();
}
Console.Write($"{prop.Name} - ");
if(printVal == "True") {
Console.ForegroundColor = ConsoleColor.Green;
}
Console.Write($"{printVal}");
Console.ResetColor();
Console.WriteLine();
}
};
await interf.ConnectAsync();
await interf.SetRegisterAsync(nameof(registers.StartCyclePLC), true);
await Task.Delay(-1);
}
}

View File

@@ -1,14 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using MewtocolNet;
using MewtocolNet.Logging;
using MewtocolNet.Registers;
using static System.Net.Mime.MediaTypeNames;
namespace Examples;
class Program {
static ExampleScenarios ExampleSzenarios = new ExampleScenarios();
static void Main(string[] args) {
AppDomain.CurrentDomain.UnhandledException += (s,e) => {
@@ -19,212 +20,81 @@ class Program {
Console.WriteLine(e.Exception.ToString());
};
Console.WriteLine("Enter your scenario number:\n" +
"1 = Permanent connection\n" +
"2 = Dispose connection");
ExampleSzenarios.SetupLogger();
LoopInput();
}
private static void LoopInput () {
Console.WriteLine("All available scenarios\n");
var methods = ExampleSzenarios.GetType().GetMethods();
var invokeableMethods = new List<MethodInfo>();
for (int i = 0, j = 0; i < methods.Length; i++) {
MethodInfo method = methods[i];
var foundAtt = method.GetCustomAttribute(typeof(ScenarioAttribute));
if(foundAtt != null && foundAtt is ScenarioAttribute att) {
Console.WriteLine($"[{j + 1}] {method.Name}() - {att.Description}");
invokeableMethods.Add(method);
j++;
}
}
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("\nEnter a number to excecute a example");
Console.ResetColor();
Console.WriteLine("\nOther possible commands: \n\n" +
"'toggle logger' - toggle the built in mewtocol logger on/off\n" +
"'exit' - to close this program \n" +
"'clear' - to clear the output \n");
Console.Write("> ");
var line = Console.ReadLine();
if(line == "1") {
Scenario1();
if (line == "toggle logger") {
ExampleScenarios.MewtocolLoggerEnabled = !ExampleScenarios.MewtocolLoggerEnabled;
Console.WriteLine(ExampleScenarios.MewtocolLoggerEnabled ? "Logger enabled" : "Logger disabled");
} else if (line == "exit") {
Environment.Exit(0);
} else if (line == "clear") {
Console.Clear();
} else if (int.TryParse(line, out var lineNum)) {
var index = Math.Clamp(lineNum - 1, 0, invokeableMethods.Count - 1);
var task = (Task)invokeableMethods.ElementAt(index).Invoke(ExampleSzenarios, null);
task.Wait();
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("The program ran to completition");
Console.ResetColor();
} else {
Console.WriteLine("Wrong input");
}
if (line == "2") {
Scenario2();
}
Console.ReadLine();
}
private static bool isProgressReadout = false;
static void Scenario1 () {
Task.Factory.StartNew(async () => {
//attaching the logger
Logger.LogLevel = LogLevel.Verbose;
Logger.OnNewLogMessage((date, msg) => {
Console.WriteLine($"{date.ToString("HH:mm:ss")} {msg}");
});
//setting up a new PLC interface and register collection
MewtocolInterface interf = new MewtocolInterface("10.237.191.3");
TestRegisters registers = new TestRegisters();
//attaching the register collection and an automatic poller
interf.WithRegisterCollection(registers).WithPoller();
_ = Task.Factory.StartNew(async () => {
while (true) {
if (isProgressReadout) continue;
Console.Title = $"Polling Paused: {interf.PollingPaused}, " +
$"Poller active: {interf.PollerActive}, " +
$"Speed UP: {interf.BytesPerSecondUpstream} B/s, " +
$"Speed DOWN: {interf.BytesPerSecondDownstream} B/s, " +
$"Poll delay: {interf.PollerDelayMs} ms, " +
$"Queued MSGs: {interf.QueuedMessages}";
await Task.Delay(1000);
}
});
await interf.ConnectAsync();
//bool flip = false;
//while(true) {
// if(!flip) {
// await interf.ConnectAsync();
// } else {
// interf.Disconnect();
// }
// flip = !flip;
// await Task.Delay(5000);
//}
});
}
static void AfterConnect (MewtocolInterface interf, TestRegisters registers) {
//reading a value from the register collection
Console.WriteLine($"BitValue is: {registers.BitValue}");
Console.WriteLine($"TestEnum is: {registers.TestEnum}");
_ = Task.Factory.StartNew(async () => {
while(true) {
isProgressReadout = true;
await interf.ReadByteRange(1000, 2000, (p) => {
var totSteps = 10;
var cSteps = totSteps * p;
string progBar = "";
for (int i = 0; i < totSteps; i++) {
if(i < (int)cSteps) {
progBar += "⬛";
} else {
progBar += "⬜";
}
}
Console.Title = $"Prog read range: {(p * 100).ToString("N1")}% {progBar} Queued MSGs: {interf.QueuedMessages}";
});
isProgressReadout = false;
await Task.Delay(3000);
}
});
//writing a value to the registers
_ = Task.Factory.StartNew(async () => {
//set plc to run mode if not already
await interf.SetOperationMode(OPMode.Run);
int startAdress = 10000;
int entryByteSize = 20 * 20;
var bytes = await interf.ReadByteRange(startAdress, entryByteSize);
Console.WriteLine($"Bytes: {string.Join('-', bytes)}");
await Task.Delay(2000);
await interf.SetRegisterAsync(nameof(registers.TestInt32), 100);
//adds 10 each time the plc connects to the PLCs INT regíster
interf.SetRegister(nameof(registers.TestInt16), (short)(registers.TestInt16 + 10));
//adds 1 each time the plc connects to the PLCs DINT regíster
interf.SetRegister(nameof(registers.TestInt32), (registers.TestInt32 + 1));
//adds 11.11 each time the plc connects to the PLCs REAL regíster
interf.SetRegister(nameof(registers.TestFloat32), (float)(registers.TestFloat32 + 11.11));
//writes 'Hello' to the PLCs string register
interf.SetRegister(nameof(registers.TestString2), "Hello");
//set the current second to the PLCs TIME register
interf.SetRegister(nameof(registers.TestTime), TimeSpan.FromSeconds(DateTime.Now.Second));
//test pausing poller
bool pollerPaused = false;
while (true) {
await Task.Delay(5000);
pollerPaused = !pollerPaused;
if (pollerPaused) {
Console.WriteLine("Pausing poller");
await interf.PausePollingAsync();
//interf.PollerDelayMs += 10;
Console.WriteLine("Paused poller");
} else {
interf.ResumePolling();
Console.WriteLine("Resumed poller");
}
}
});
}
static void Scenario2 () {
Logger.LogLevel = LogLevel.Critical;
Logger.OnNewLogMessage((date, msg) => {
Console.WriteLine($"{date.ToString("HH:mm:ss")} {msg}");
});
Task.Factory.StartNew(async () => {
//automatic endpoint
using (var interf = new MewtocolInterface("10.237.191.3")) {
await interf.ConnectAsync();
if (interf.IsConnected) {
await Task.Delay(5000);
}
interf.Disconnect();
}
//manual endpoint
using (var interf = new MewtocolInterface("10.237.191.3")) {
interf.HostEndpoint = new System.Net.IPEndPoint(System.Net.IPAddress.Parse("10.237.191.77"), 0);
await interf.ConnectAsync();
if(interf.IsConnected) {
await Task.Delay(5000);
}
interf.Disconnect();
}
});
LoopInput();
}

View File

@@ -0,0 +1,15 @@
using System;
namespace Examples;
public class ScenarioAttribute : Attribute {
public string Description { get; private set; }
public ScenarioAttribute(string description) {
Description = description;
}
}

View File

@@ -10,8 +10,13 @@ namespace Examples {
[Register(1000, RegisterType.R)]
public bool TestBool1 { get; private set; }
private int testDuplicate;
[Register(1000)]
public int TestDuplicate { get; private set; }
public int TestDuplicate {
get => testDuplicate;
set => AutoSetter(value, ref testDuplicate);
}
//corresponds to a XD input of the PLC
[Register(RegisterType.X, SpecialAddress.D)]

View File

@@ -0,0 +1,106 @@
using MewtocolNet;
using MewtocolNet.RegisterAttributes;
using System;
using System.Collections;
namespace Examples {
public class TestRegistersEnumBitwise : RegisterCollectionBase {
private bool startCyclePLC;
[Register(50, RegisterType.R)]
public bool StartCyclePLC {
get => startCyclePLC;
set => AutoSetter(value, ref startCyclePLC);
}
//the enum you want to read out
public enum CurrentState {
Undefined = 0,
State1 = 1,
State2 = 2,
//If you leave an enum empty it still works
//State3 = 3,
State4 = 4,
State5 = 5,
State6 = 6,
State7 = 7,
State8 = 8,
State9 = 9,
State10 = 10,
State11 = 11,
State12 = 12,
State13 = 13,
State14 = 14,
State15 = 15,
}
//automatically convert the short (PLC int) register to an enum
[Register(500)]
public CurrentState TestEnum16 { get; private set; }
//also works for 32bit registers
[Register(501, BitCount.B32)]
public CurrentState TestEnum32 { get; private set; }
//get the whole bit array from DT503
[Register(503)]
public BitArray TestBitRegister16 { get; private set; }
//you can also extract single bits from DT503
[Register(503, 0, BitCount.B16)]
public bool BitValue0 { get; private set; }
[Register(503, 1, BitCount.B16)]
public bool BitValue1 { get; private set; }
[Register(503, 2, BitCount.B16)]
public bool BitValue2 { get; private set; }
[Register(503, 3, BitCount.B16)]
public bool BitValue3 { get; private set; }
[Register(503, 4, BitCount.B16)]
public bool BitValue4 { get; private set; }
[Register(503, 5, BitCount.B16)]
public bool BitValue5 { get; private set; }
[Register(503, 6, BitCount.B16)]
public bool BitValue6 { get; private set; }
[Register(503, 7, BitCount.B16)]
public bool BitValue7 { get; private set; }
[Register(503, 8, BitCount.B16)]
public bool BitValue8 { get; private set; }
[Register(503, 9, BitCount.B16)]
public bool BitValue9 { get; private set; }
[Register(503, 10, BitCount.B16)]
public bool BitValue10 { get; private set; }
[Register(503, 11, BitCount.B16)]
public bool BitValue11 { get; private set; }
[Register(503, 12, BitCount.B16)]
public bool BitValue12 { get; private set; }
[Register(503, 13, BitCount.B16)]
public bool BitValue13 { get; private set; }
[Register(503, 14, BitCount.B16)]
public bool BitValue14 { get; private set; }
[Register(503, 15, BitCount.B16)]
public bool BitValue15 { get; private set; }
}
}