diff --git a/DocBuilder/Program.cs b/DocBuilder/Program.cs
index bbb7263..fb0a63c 100644
--- a/DocBuilder/Program.cs
+++ b/DocBuilder/Program.cs
@@ -7,7 +7,6 @@ using System.Reflection;
using System.Reflection.PortableExecutable;
using System.Text;
using MewtocolNet;
-using MewtocolNet.DocAttributes;
Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;
Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
diff --git a/Examples.BasicEthernet/Examples.BasicEthernet.csproj b/Examples.BasicEthernet/Examples.BasicEthernet.csproj
new file mode 100644
index 0000000..c7c4dd3
--- /dev/null
+++ b/Examples.BasicEthernet/Examples.BasicEthernet.csproj
@@ -0,0 +1,14 @@
+
+
+
+ Exe
+ net7.0
+ enable
+ enable
+
+
+
+
+
+
+
diff --git a/Examples.BasicEthernet/Program.cs b/Examples.BasicEthernet/Program.cs
new file mode 100644
index 0000000..cb3d1f3
--- /dev/null
+++ b/Examples.BasicEthernet/Program.cs
@@ -0,0 +1,72 @@
+using MewtocolNet;
+using MewtocolNet.Logging;
+
+namespace Examples.BasicUsage;
+
+internal class Program {
+
+ const string IP = "192.168.115.210";
+ const int PORT = 9094;
+
+ static void Main(string[] args) => Task.Run(AsyncMain).Wait();
+
+ static async Task AsyncMain () {
+
+ //the library provides a logging tool, comment this out if needed
+ Logger.LogLevel = LogLevel.Verbose;
+ Logger.OnNewLogMessage((d, l, m) => Console.WriteLine($"{d}: {m}"));
+
+ //create a new interface to the plc using ethernet / tcp ip
+ //the using keyword is optional, if you want to use your PLC instance
+ //globally leave it
+
+ //you can also specify the source ip with:
+ //Mewtocol.Ethernet(IP, PORT).FromSource("192.168.113.2", 46040).Build()
+
+ using (var plc = Mewtocol.Ethernet(IP, PORT).Build()) {
+
+ //connect async to the plc
+ await plc.ConnectAsync();
+
+ //check if the connection was established
+ if (!plc.IsConnected) {
+ Console.WriteLine("Failed to connect to the plc...");
+ Environment.Exit(1);
+ }
+
+ //print basic plc info
+ Console.WriteLine(plc.PlcInfo);
+
+ //check if the plc is not in RUN mode, change to run
+ if(!plc.PlcInfo.IsRunMode) await plc.SetOperationModeAsync(true);
+
+ //get information about the plc
+ Console.WriteLine($"PLC type: {plc.PlcInfo.TypeName}");
+ Console.WriteLine($"Capacity: {plc.PlcInfo.ProgramCapacity}k");
+ Console.WriteLine($"Error: {plc.PlcInfo.SelfDiagnosticError}k");
+
+ //set the plc to prog mode
+ //await plc.SetOperationModeAsync(false);
+
+ //do disconnect use
+ plc.Disconnect();
+
+ //or
+ //await plc.DisconnectAsync();
+
+ //you can then change the connection settings for example to another PLC
+ plc.ConfigureConnection("192.168.115.212", 9094);
+ await plc.ConnectAsync();
+
+ }
+
+ //you can also find any applicable source endpoints by using:
+ foreach (var endpoint in Mewtocol.GetSourceEndpoints()) {
+
+ Console.WriteLine($"Usable endpoint: {endpoint}");
+
+ }
+
+ }
+
+}
diff --git a/Examples.BasicRegisterReadWrite/Examples.BasicRegisterReadWrite.csproj b/Examples.BasicRegisterReadWrite/Examples.BasicRegisterReadWrite.csproj
new file mode 100644
index 0000000..b8f82d1
--- /dev/null
+++ b/Examples.BasicRegisterReadWrite/Examples.BasicRegisterReadWrite.csproj
@@ -0,0 +1,14 @@
+
+
+
+ Exe
+ net7.0
+ enable
+ enable
+
+
+
+
+
+
+
diff --git a/Examples.BasicRegisterReadWrite/Program.cs b/Examples.BasicRegisterReadWrite/Program.cs
new file mode 100644
index 0000000..2cdff82
--- /dev/null
+++ b/Examples.BasicRegisterReadWrite/Program.cs
@@ -0,0 +1,145 @@
+using MewtocolNet;
+using MewtocolNet.Logging;
+using MewtocolNet.Registers;
+
+namespace Examples.BasicRegisterReadWrite;
+
+internal class Program {
+
+ const string PLC_IP = "192.168.178.55";
+
+ static void Main(string[] args) => Task.Run(AsyncMain).Wait();
+
+ static async Task AsyncMain() {
+
+ Console.Clear();
+
+ //the library provides a logging tool, comment this out if needed
+ Logger.LogLevel = LogLevel.Critical;
+
+ //here we collect our built registers
+ IRegister simpleNumberRegister = null!;
+ IRegister simpleNumberRegister2 = null!;
+
+ IRegister simpleWordRegister = null!;
+ IArrayRegister overlapWordRegister = null!;
+
+ IStringRegister stringRegister = null!;
+ IArrayRegister stringArrayRegister = null!;
+
+ IArrayRegister2D simpleNumberRegister2Dim = null!;
+
+ //create a new interface to the plc using ethernet / tcp ip
+ using var plc = Mewtocol.Ethernet(PLC_IP)
+ .WithRegisters(b => {
+
+ //a simple INT at register address DT1000
+ b.Struct("DT1000").Build(out simpleNumberRegister);
+ b.Struct("DT1001").Build(out simpleNumberRegister2);
+
+ //you can also read the same array as an other data type
+ //not how they are at the same address, that means their values are linked
+ b.Struct("DT1000").Build(out simpleWordRegister);
+
+ //same link feature is also true for arrays that overlap their addresses
+ //this will go from DT999 to DT1001 because its a 3 Word array
+ b.Struct("DT999").AsArray(3).Build(out overlapWordRegister);
+
+ //strings area not stacially sized, they use a different constructor
+ b.String("DT1024", 32).Build(out stringRegister);
+
+ //string can also be arrayed
+ b.String("DT2030", 5).AsArray(3).Build(out stringArrayRegister);
+
+ //a simple 2 dimensional ARRAY [0..2, 0..2] OF INT at DT2003
+ b.Struct("DT2003").AsArray(3, 3).Build(out simpleNumberRegister2Dim);
+
+ })
+ .Build();
+
+ //you can explain the internal register layout and which ones are linked by
+ Console.WriteLine(plc.Explain());
+
+ //connect async to the plc
+ await plc.ConnectAsync();
+
+ //check if the connection was established
+ if (!plc.IsConnected) {
+ Console.WriteLine("Failed to connect to the plc...");
+ Environment.Exit(1);
+ }
+
+ //restarts the program, this will make sure the global registers of the plc get reset each run
+ await plc.RestartProgramAsync();
+
+ //from this point on we can modify our registers
+
+ //read the value of the the register
+ short readValue = await simpleNumberRegister.ReadAsync();
+ ushort readValue2 = await simpleNumberRegister2.ReadAsync();
+
+ //show the value
+ Console.WriteLine($"Read value for {nameof(simpleNumberRegister)}: {readValue}");
+ Console.WriteLine($"Read value for {nameof(simpleNumberRegister2)}: {readValue2}");
+
+ //write the value
+ await simpleNumberRegister.WriteAsync(30);
+
+ //show the value
+ Console.WriteLine($"Value of {nameof(simpleNumberRegister)}: {simpleNumberRegister.Value}");
+
+ //because the two registers at DT1000 are linked by their memory address in the plc,
+ //both of their values got updated
+ Console.WriteLine($"Value of {nameof(simpleWordRegister)}: {simpleWordRegister.Value}");
+
+ //also the overlapping word array register value got updated, but only at the DT positions that were read
+ int i = 0;
+ overlapWordRegister.Value.ToList().ForEach(x => {
+ Console.WriteLine($"Value of {nameof(overlapWordRegister)}[{i}]: {x}");
+ i++;
+ });
+
+ //you can even read out a word bitwise
+ Console.WriteLine($"Bits of {nameof(simpleWordRegister)}: {simpleWordRegister.Value?.ToStringBits()}");
+ //or as a single bit
+ Console.WriteLine($"Bit at index 3 of {nameof(simpleWordRegister)}: {simpleWordRegister.Value?[3]}");
+
+ //reading / writing the string register
+ //await stringRegister.WriteAsync("Lorem ipsum dolor sit amet, cons");
+ await stringRegister.ReadAsync();
+ Console.WriteLine($"Value of {nameof(stringRegister)}: {stringRegister.Value}");
+
+ //reading writing a string array register
+ await stringArrayRegister.ReadAsync();
+
+ i = 0;
+ stringArrayRegister.Value.ToList().ForEach(x => {
+ Console.WriteLine($"Value of {nameof(stringArrayRegister)}[{i}]: {x}");
+ i++;
+ });
+
+ //you can either set the whole array at once,
+ //this will be slow if you only want to update one item
+ await stringArrayRegister.WriteAsync(new string[] {
+ "Test1",
+ "Test2",
+ "Test3",
+ });
+
+ //or update just one
+
+ //COMING LATER
+
+ //same thing also works for 2 dim arrays
+ await simpleNumberRegister2Dim.ReadAsync();
+
+ //the array is multi dimensional but can also be iterated per element
+ foreach (var item in simpleNumberRegister2Dim)
+ Console.WriteLine($"Element of {nameof(simpleNumberRegister2Dim)}: {item}");
+
+ //you can also use the array indexer accessors
+ Console.WriteLine($"Element [1, 2] of {nameof(simpleNumberRegister2Dim)}: {simpleNumberRegister2Dim[1, 2]}");
+
+ }
+
+}
diff --git a/Examples.Polling/Examples.Polling.csproj b/Examples.Polling/Examples.Polling.csproj
new file mode 100644
index 0000000..c7c4dd3
--- /dev/null
+++ b/Examples.Polling/Examples.Polling.csproj
@@ -0,0 +1,14 @@
+
+
+
+ Exe
+ net7.0
+ enable
+ enable
+
+
+
+
+
+
+
diff --git a/Examples.Polling/Program.cs b/Examples.Polling/Program.cs
new file mode 100644
index 0000000..4386c7c
--- /dev/null
+++ b/Examples.Polling/Program.cs
@@ -0,0 +1,90 @@
+using MewtocolNet.Logging;
+using MewtocolNet.Registers;
+using MewtocolNet;
+
+namespace Examples.Polling;
+
+internal class Program {
+
+ const string PLC_IP = "192.168.178.55";
+
+ static void Main(string[] args) => Task.Run(AsyncMain).Wait();
+
+ static async Task AsyncMain() {
+
+ Console.Clear();
+
+ //the library provides a logging tool, comment this out if needed
+ Logger.LogLevel = LogLevel.Change;
+
+ //create a new interface to the plc using ethernet / tcp ip
+ using var plc = Mewtocol.Ethernet(PLC_IP)
+ .WithPoller() // <= use this in the builder pattern to automatically poll registers
+ .WithInterfaceSettings(s => {
+
+ //this means registers at the same address
+ //or that are contained by overlapping arrays
+ //always get assigned the same poll level
+ s.PollLevelOverwriteMode = PollLevelOverwriteMode.Highest;
+
+ //this means combine all registers that are not farther
+ //than 8 words apart from another into one tcp message,
+ //this safes message frames but a to large number can block read write traffic
+ s.MaxOptimizationDistance = 8;
+
+ })
+ .WithCustomPollLevels(l => {
+
+ //this makes registers at polllevel 2 only run all 3 iterations
+ l.SetLevel(2, 3);
+
+ //this makes registers at polllevel 3 only run all 5 seconds
+ l.SetLevel(3, TimeSpan.FromSeconds(5));
+
+ })
+ .WithRegisters(b => {
+
+ b.Struct("DT1000").Build();
+ b.Struct("DT1000").Build();
+ b.Struct("DT1001").Build();
+ b.Struct("DT999").AsArray(3).Build();
+
+ //we dont want to poll the string register as fast as we can
+ //so we assign it to level 2 to run only all 3 iterations
+ b.String("DT1024", 32).PollLevel(2).Build();
+
+ //we want to poll this array only at the first iteration,
+ //and after this only if we need the data
+ b.Struct("DT2000").AsArray(3).PollLevel(PollLevel.FirstIteration).Build();
+
+ //we want to poll this string array all 5 seconds
+ b.String("DT2030", 5).AsArray(3).PollLevel(3).Build();
+
+ //this is a fairly large array, so we never poll it automatically
+ //only if we need the data
+ b.Struct("DT2003").AsArray(3, 3).PollLevel(PollLevel.Never).Build();
+
+ })
+ .Build();
+
+ //this explains the underlying data structure for the poller
+ Console.WriteLine(plc.Explain());
+
+ await plc.ConnectAsync(async () => {
+
+ //we want to restart the program before the poller starts
+ await plc.RestartProgramAsync();
+
+ });
+
+ if (!plc.IsConnected) {
+ Console.WriteLine("Failed to connect to the plc...");
+ Environment.Exit(1);
+ }
+
+ Console.ReadLine();
+
+ }
+
+
+}
diff --git a/MewTerminal/Commands/OnlineCommands/GetRegisterCommand.cs b/MewTerminal/Commands/OnlineCommands/GetRegisterCommand.cs
deleted file mode 100644
index d7094f6..0000000
--- a/MewTerminal/Commands/OnlineCommands/GetRegisterCommand.cs
+++ /dev/null
@@ -1,53 +0,0 @@
-using System;
-using System.Collections.Generic;
-using CommandLine;
-using MewtocolNet;
-using MewtocolNet.ComCassette;
-using MewtocolNet.Logging;
-using MewtocolNet.RegisterBuilding;
-using Spectre.Console;
-
-namespace MewTerminal.Commands.OnlineCommands;
-
-[Verb("rget", HelpText = "Gets the values of the given PLC registers")]
-internal class GetRegisterCommand : OnlineCommand {
-
- [Value(0, MetaName = "registers", Default = "DT0", HelpText = "The registers to read formatted as (DT0:INT)")]
- public IEnumerable Registers { get; set; }
-
- internal override async Task AfterSetup(IPlc plc) {
-
- var builder = RegBuilder.ForInterface(plc);
-
- foreach (var reg in Registers) {
-
- var split = reg.Split(":");
-
- if (split.Length <= 1) {
- throw new FormatException($"Register name was not formatted correctly: {reg}, missing :PlcVarType");
- }
-
- var mewtocolName = split[0];
- var mewtocolType = split[1];
-
- if (Enum.TryParse(mewtocolType, out var parsedT)) {
-
- builder.FromPlcRegName(mewtocolName).AsPlcType(parsedT).Build();
-
- }
-
- }
-
- await plc.ConnectAsync();
-
- foreach (var reg in plc.GetAllRegisters()) {
-
- await reg.ReadAsync();
-
- }
-
- AnsiConsole.Write(plc.GetAllRegisters().ToTable());
-
- }
-
-}
diff --git a/MewTerminal/Commands/OnlineCommands/SetRegisterCommand.cs b/MewTerminal/Commands/OnlineCommands/SetRegisterCommand.cs
deleted file mode 100644
index 44a6077..0000000
--- a/MewTerminal/Commands/OnlineCommands/SetRegisterCommand.cs
+++ /dev/null
@@ -1,64 +0,0 @@
-using CommandLine;
-using MewtocolNet;
-using MewtocolNet.RegisterBuilding;
-using MewtocolNet.Registers;
-using Spectre.Console;
-
-namespace MewTerminal.Commands.OnlineCommands;
-
-[Verb("rset", HelpText = "Sets the values of the given PLC registers")]
-internal class SetRegisterCommand : OnlineCommand {
-
- [Value(0, MetaName = "registers", Default = "DT0", HelpText = "The registers to write formatted as (DT0:INT:VALUE)")]
- public IEnumerable Registers { get; set; }
-
- internal override async Task AfterSetup(IPlc plc) {
-
- var builder = RegBuilder.ForInterface(plc);
-
- var toWriteVals = new List();
-
- foreach (var reg in Registers) {
-
- var split = reg.Split(":");
-
- if (split.Length <= 2) {
- throw new FormatException($"Register name was not formatted correctly: {reg}, missing :PlcVarType:Value");
- }
-
- var mewtocolName = split[0];
- var mewtocolType = split[1];
- var value = split[2];
-
- if (Enum.TryParse(mewtocolType, out var parsedT)) {
-
- var built = builder.FromPlcRegName(mewtocolName).AsPlcType(parsedT).Build();
-
- if(built is BoolRegister) toWriteVals.Add(bool.Parse(value));
- else if(built is NumberRegister) toWriteVals.Add(short.Parse(value));
- else if(built is NumberRegister) toWriteVals.Add(ushort.Parse(value));
- else if(built is NumberRegister) toWriteVals.Add(int.Parse(value));
- else if(built is NumberRegister) toWriteVals.Add(uint.Parse(value));
- else if(built is NumberRegister) toWriteVals.Add(float.Parse(value));
- else if(built is NumberRegister) toWriteVals.Add(TimeSpan.Parse(value));
-
- }
-
- }
-
- await plc.ConnectAsync();
-
- int i = 0;
- foreach (var reg in plc.GetAllRegisters()) {
-
- await reg.WriteAsync(toWriteVals[i]);
-
- i++;
-
- }
-
- AnsiConsole.WriteLine("All registers written");
-
- }
-
-}
\ No newline at end of file
diff --git a/MewtocolNet.sln b/MewtocolNet.sln
index a957c67..8a98cbe 100644
--- a/MewtocolNet.sln
+++ b/MewtocolNet.sln
@@ -11,6 +11,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DocBuilder", "DocBuilder\Do
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MewTerminal", "MewTerminal\MewTerminal.csproj", "{D1E751C6-296F-4CF1-AE28-C6D4388C7CF1}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{323729B0-5FB2-4592-9FA6-220C46BBF84C}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Examples.BasicEthernet", "Examples.BasicEthernet\Examples.BasicEthernet.csproj", "{80806065-163D-43F3-90CD-9221F391793F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Examples.BasicRegisterReadWrite", "Examples.BasicRegisterReadWrite\Examples.BasicRegisterReadWrite.csproj", "{A24444C8-691D-44CB-8DE8-19618C6DE94B}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Examples.Polling", "Examples.Polling\Examples.Polling.csproj", "{9A36F2B1-FF9E-47BF-9931-3D3EB3C46B61}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -69,10 +77,51 @@ Global
{D1E751C6-296F-4CF1-AE28-C6D4388C7CF1}.Release|x64.Build.0 = Release|Any CPU
{D1E751C6-296F-4CF1-AE28-C6D4388C7CF1}.Release|x86.ActiveCfg = Release|Any CPU
{D1E751C6-296F-4CF1-AE28-C6D4388C7CF1}.Release|x86.Build.0 = Release|Any CPU
+ {80806065-163D-43F3-90CD-9221F391793F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {80806065-163D-43F3-90CD-9221F391793F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {80806065-163D-43F3-90CD-9221F391793F}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {80806065-163D-43F3-90CD-9221F391793F}.Debug|x64.Build.0 = Debug|Any CPU
+ {80806065-163D-43F3-90CD-9221F391793F}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {80806065-163D-43F3-90CD-9221F391793F}.Debug|x86.Build.0 = Debug|Any CPU
+ {80806065-163D-43F3-90CD-9221F391793F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {80806065-163D-43F3-90CD-9221F391793F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {80806065-163D-43F3-90CD-9221F391793F}.Release|x64.ActiveCfg = Release|Any CPU
+ {80806065-163D-43F3-90CD-9221F391793F}.Release|x64.Build.0 = Release|Any CPU
+ {80806065-163D-43F3-90CD-9221F391793F}.Release|x86.ActiveCfg = Release|Any CPU
+ {80806065-163D-43F3-90CD-9221F391793F}.Release|x86.Build.0 = Release|Any CPU
+ {A24444C8-691D-44CB-8DE8-19618C6DE94B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A24444C8-691D-44CB-8DE8-19618C6DE94B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A24444C8-691D-44CB-8DE8-19618C6DE94B}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {A24444C8-691D-44CB-8DE8-19618C6DE94B}.Debug|x64.Build.0 = Debug|Any CPU
+ {A24444C8-691D-44CB-8DE8-19618C6DE94B}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {A24444C8-691D-44CB-8DE8-19618C6DE94B}.Debug|x86.Build.0 = Debug|Any CPU
+ {A24444C8-691D-44CB-8DE8-19618C6DE94B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A24444C8-691D-44CB-8DE8-19618C6DE94B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A24444C8-691D-44CB-8DE8-19618C6DE94B}.Release|x64.ActiveCfg = Release|Any CPU
+ {A24444C8-691D-44CB-8DE8-19618C6DE94B}.Release|x64.Build.0 = Release|Any CPU
+ {A24444C8-691D-44CB-8DE8-19618C6DE94B}.Release|x86.ActiveCfg = Release|Any CPU
+ {A24444C8-691D-44CB-8DE8-19618C6DE94B}.Release|x86.Build.0 = Release|Any CPU
+ {9A36F2B1-FF9E-47BF-9931-3D3EB3C46B61}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9A36F2B1-FF9E-47BF-9931-3D3EB3C46B61}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9A36F2B1-FF9E-47BF-9931-3D3EB3C46B61}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {9A36F2B1-FF9E-47BF-9931-3D3EB3C46B61}.Debug|x64.Build.0 = Debug|Any CPU
+ {9A36F2B1-FF9E-47BF-9931-3D3EB3C46B61}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {9A36F2B1-FF9E-47BF-9931-3D3EB3C46B61}.Debug|x86.Build.0 = Debug|Any CPU
+ {9A36F2B1-FF9E-47BF-9931-3D3EB3C46B61}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9A36F2B1-FF9E-47BF-9931-3D3EB3C46B61}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9A36F2B1-FF9E-47BF-9931-3D3EB3C46B61}.Release|x64.ActiveCfg = Release|Any CPU
+ {9A36F2B1-FF9E-47BF-9931-3D3EB3C46B61}.Release|x64.Build.0 = Release|Any CPU
+ {9A36F2B1-FF9E-47BF-9931-3D3EB3C46B61}.Release|x86.ActiveCfg = Release|Any CPU
+ {9A36F2B1-FF9E-47BF-9931-3D3EB3C46B61}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {80806065-163D-43F3-90CD-9221F391793F} = {323729B0-5FB2-4592-9FA6-220C46BBF84C}
+ {A24444C8-691D-44CB-8DE8-19618C6DE94B} = {323729B0-5FB2-4592-9FA6-220C46BBF84C}
+ {9A36F2B1-FF9E-47BF-9931-3D3EB3C46B61} = {323729B0-5FB2-4592-9FA6-220C46BBF84C}
+ EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {4ABB8137-CD8F-4691-9802-9ED371012F47}
EndGlobalSection
diff --git a/MewtocolNet/ComCassette/CassetteFinder.cs b/MewtocolNet/ComCassette/CassetteFinder.cs
index d779168..7079bab 100644
--- a/MewtocolNet/ComCassette/CassetteFinder.cs
+++ b/MewtocolNet/ComCassette/CassetteFinder.cs
@@ -20,7 +20,7 @@ namespace MewtocolNet.ComCassette {
var interfacesTasks = new List>>();
- var usableInterfaces = GetUseableNetInterfaces();
+ var usableInterfaces = Mewtocol.GetUseableNetInterfaces();
if (ipSource == null) {
@@ -71,36 +71,6 @@ namespace MewtocolNet.ComCassette {
}
- private static IEnumerable GetUseableNetInterfaces() {
-
- foreach (NetworkInterface netInterface in NetworkInterface.GetAllNetworkInterfaces()) {
-
- bool isEthernet =
- netInterface.NetworkInterfaceType == NetworkInterfaceType.Ethernet ||
- netInterface.NetworkInterfaceType == NetworkInterfaceType.Ethernet3Megabit ||
- netInterface.NetworkInterfaceType == NetworkInterfaceType.FastEthernetFx ||
- netInterface.NetworkInterfaceType == NetworkInterfaceType.FastEthernetT ||
- netInterface.NetworkInterfaceType == NetworkInterfaceType.GigabitEthernet;
-
- bool isWlan = netInterface.NetworkInterfaceType == NetworkInterfaceType.Wireless80211;
-
- bool isUsable = netInterface.OperationalStatus == OperationalStatus.Up;
-
- if (!isUsable) continue;
- if (!(isWlan || isEthernet)) continue;
-
- IPInterfaceProperties ipProps = netInterface.GetIPProperties();
- var hasUnicastInfo = ipProps.UnicastAddresses
- .Any(x => x.Address.AddressFamily == AddressFamily.InterNetwork);
-
- if (!hasUnicastInfo) continue;
-
- yield return netInterface;
-
- }
-
- }
-
private static async Task> FindClientsForEndpoint(IPEndPoint from, int timeoutMs, string ipEndpointName) {
var cassettesFound = new List();
diff --git a/MewtocolNet/CustomTypes/DWord.cs b/MewtocolNet/CustomTypes/DWord.cs
index fd31914..d7c10ea 100644
--- a/MewtocolNet/CustomTypes/DWord.cs
+++ b/MewtocolNet/CustomTypes/DWord.cs
@@ -13,17 +13,13 @@ namespace MewtocolNet {
internal uint value;
- public uint Value {
- get => value;
- set {
- this.value = value;
- }
- }
+ public uint Value => value;
public DWord(uint bytes) {
value = bytes;
bitLength = Marshal.SizeOf(value) * 8;
}
+
public DWord(byte[] bytes) {
bytes = bytes.Take(4).ToArray();
value = BitConverter.ToUInt32(bytes, 0);
@@ -62,17 +58,8 @@ namespace MewtocolNet {
return (value & (1 << bitIndex)) != 0;
}
- set {
- if (bitIndex > bitLength - 1)
- throw new IndexOutOfRangeException($"The DWord bit index was out of range ({bitIndex}/{bitLength - 1})");
-
- int mask = 1 << bitIndex;
- this.value = value ? this.value |= (uint)mask : this.value &= (uint)~mask;
- }
}
- public void ClearBits() => this.value = 0;
-
public override bool Equals(object obj) {
if ((obj == null) || !this.GetType().Equals(obj.GetType())) {
@@ -107,8 +94,6 @@ namespace MewtocolNet {
}
- public int GetIntialPlcByteSize() => 4;
-
}
}
diff --git a/MewtocolNet/CustomTypes/Word.cs b/MewtocolNet/CustomTypes/Word.cs
index b10bd0d..d53e6b6 100644
--- a/MewtocolNet/CustomTypes/Word.cs
+++ b/MewtocolNet/CustomTypes/Word.cs
@@ -13,17 +13,13 @@ namespace MewtocolNet {
internal ushort value;
- public ushort Value {
- get => value;
- set {
- this.value = value;
- }
- }
+ public ushort Value => value;
public Word(ushort bytes) {
value = bytes;
bitLength = Marshal.SizeOf(value) * 8;
}
+
public Word(byte[] bytes) {
bytes = bytes.Take(2).ToArray();
value = BitConverter.ToUInt16(bytes, 0);
@@ -53,7 +49,7 @@ namespace MewtocolNet {
public static bool operator !=(Word a, Word b) => a.value != b.value;
///
- /// Gets or sets the bit value at the given position
+ /// Gets the bit value at the given position
///
public bool this[int bitIndex] {
get {
@@ -62,17 +58,8 @@ namespace MewtocolNet {
return (value & (1 << bitIndex)) != 0;
}
- set {
- if (bitIndex > bitLength - 1)
- throw new IndexOutOfRangeException($"The word bit index was out of range ({bitIndex}/{bitLength - 1})");
-
- int mask = 1 << bitIndex;
- this.value = value ? this.value |= (ushort)mask : this.value &= (ushort)~mask;
- }
}
- public void ClearBits() => this.value = 0;
-
public override bool Equals(object obj) {
if ((obj == null) || !this.GetType().Equals(obj.GetType())) {
diff --git a/MewtocolNet/IPlc.cs b/MewtocolNet/IPlc.cs
index 64bc17d..1b8b310 100644
--- a/MewtocolNet/IPlc.cs
+++ b/MewtocolNet/IPlc.cs
@@ -57,11 +57,6 @@ namespace MewtocolNet {
///
int ConnectTimeout { get; set; }
- ///
- /// Provides an anonymous interface for register reading and writing without memory management
- ///
- RBuildAnon Register { get; }
-
///
/// Tries to establish a connection with the device asynchronously
///
diff --git a/MewtocolNet/IPlcEthernet.cs b/MewtocolNet/IPlcEthernet.cs
index cebb130..fd4add0 100644
--- a/MewtocolNet/IPlcEthernet.cs
+++ b/MewtocolNet/IPlcEthernet.cs
@@ -22,7 +22,7 @@ namespace MewtocolNet {
///
/// The host ip endpoint, leave it null to use an automatic interface
///
- IPEndPoint HostEndpoint { get; set; }
+ IPEndPoint HostEndpoint { get; }
///
/// Configures the serial interface
diff --git a/MewtocolNet/Logging/Logger.cs b/MewtocolNet/Logging/Logger.cs
index 7e5ae3b..48f6509 100644
--- a/MewtocolNet/Logging/Logger.cs
+++ b/MewtocolNet/Logging/Logger.cs
@@ -1,4 +1,5 @@
using System;
+using System.Diagnostics;
namespace MewtocolNet.Logging {
@@ -12,8 +13,58 @@ namespace MewtocolNet.Logging {
///
public static LogLevel LogLevel { get; set; }
+ ///
+ /// Defines the default output logger targets
+ ///
+ public static LoggerTargets DefaultTargets { get; set; } = LoggerTargets.Console;
+
internal static Action LogInvoked;
+ static Logger () {
+
+ OnNewLogMessage((d, l, m) => {
+
+ if(DefaultTargets.HasFlag(LoggerTargets.Console)) {
+
+ switch (l) {
+ case LogLevel.Error:
+ Console.ForegroundColor = ConsoleColor.Red;
+ Console.WriteLine($"{d:hh:mm:ss:ff} {m}");
+ break;
+ case LogLevel.Info:
+ Console.WriteLine($"{d:hh:mm:ss:ff} {m}");
+ break;
+ case LogLevel.Change:
+ Console.ForegroundColor = ConsoleColor.DarkBlue;
+ Console.WriteLine($"{d:hh:mm:ss:ff} {m}");
+ break;
+ case LogLevel.Verbose:
+ Console.ForegroundColor = ConsoleColor.DarkYellow;
+ Console.WriteLine($"{d:hh:mm:ss:ff} {m}");
+ break;
+ case LogLevel.Critical:
+ Console.ForegroundColor = ConsoleColor.DarkGray;
+ Console.WriteLine($"{d:hh:mm:ss:ff} {m}");
+ break;
+ }
+
+ Console.ResetColor();
+
+ }
+
+ if (DefaultTargets.HasFlag(LoggerTargets.Trace)) {
+
+ Trace.WriteLine($"{d:hh:mm:ss:ff} {m}");
+
+ }
+
+ });
+
+ }
+
+ //for static calling purposes only
+ internal static void Start() { }
+
///
/// Gets invoked whenever a new log message is ready
///
diff --git a/MewtocolNet/Logging/LoggerTargets.cs b/MewtocolNet/Logging/LoggerTargets.cs
new file mode 100644
index 0000000..b77007c
--- /dev/null
+++ b/MewtocolNet/Logging/LoggerTargets.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace MewtocolNet.Logging {
+ [Flags]
+ public enum LoggerTargets {
+
+ None = 0,
+ Console = 1,
+ Trace = 2,
+
+ }
+}
diff --git a/MewtocolNet/Mewtocol.cs b/MewtocolNet/Mewtocol.cs
index 3057d37..dc3177b 100644
--- a/MewtocolNet/Mewtocol.cs
+++ b/MewtocolNet/Mewtocol.cs
@@ -1,14 +1,18 @@
using MewtocolNet.RegisterAttributes;
using MewtocolNet.RegisterBuilding;
+using MewtocolNet.RegisterBuilding.BuilderPatterns;
using MewtocolNet.SetupClasses;
using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Net;
+using System.Net.NetworkInformation;
+using System.Net.Sockets;
using System.Threading.Tasks;
-namespace MewtocolNet {
+namespace MewtocolNet
+{
///
/// Builder helper for mewtocol interfaces
@@ -24,11 +28,11 @@ namespace MewtocolNet {
///
/// Plc station number 0xEE for direct communication
///
- public static PostInit Ethernet(string ip, int port = 9094, int station = 0xEE) {
+ public static PostInitEth Ethernet(string ip, int port = 9094, int station = 0xEE) {
var instance = new MewtocolInterfaceTcp();
instance.ConfigureConnection(ip, port, station);
- return new PostInit {
+ return new PostInitEth {
intf = instance
};
@@ -41,11 +45,11 @@ namespace MewtocolNet {
///
/// Plc station number 0xEE for direct communication
///
- public static PostInit Ethernet(IPAddress ip, int port = 9094, int station = 0xEE) {
+ public static PostInitEth Ethernet(IPAddress ip, int port = 9094, int station = 0xEE) {
var instance = new MewtocolInterfaceTcp();
instance.ConfigureConnection(ip, port, station);
- return new PostInit {
+ return new PostInitEth {
intf = instance
};
@@ -63,8 +67,6 @@ namespace MewtocolNet {
///
public static PostInit Serial(string portName, BaudRate baudRate = BaudRate._19200, DataBits dataBits = DataBits.Eight, Parity parity = Parity.Odd, StopBits stopBits = StopBits.One, int station = 0xEE) {
- TestPortName(portName);
-
var instance = new MewtocolInterfaceSerial();
instance.ConfigureConnection(portName, (int)baudRate, (int)dataBits, parity, stopBits, station);
return new PostInit {
@@ -81,8 +83,6 @@ namespace MewtocolNet {
///
public static PostInit SerialAuto(string portName, int station = 0xEE) {
- TestPortName(portName);
-
var instance = new MewtocolInterfaceSerial();
instance.ConfigureConnection(portName, station);
instance.ConfigureConnectionAuto();
@@ -92,12 +92,52 @@ namespace MewtocolNet {
}
- private static void TestPortName(string portName) {
+ ///
+ /// Lists all useable source endpoints of the device this is running on for usage with PLCs
+ ///
+ public static IEnumerable GetSourceEndpoints () {
- var portnames = SerialPort.GetPortNames();
+ foreach (var netIf in GetUseableNetInterfaces()) {
- if (!portnames.Any(x => x == portName))
- throw new NotSupportedException($"The port {portName} is no valid port");
+ var addressInfo = netIf.GetIPProperties().UnicastAddresses
+ .FirstOrDefault(x => x.Address.AddressFamily == AddressFamily.InterNetwork);
+
+ yield return new IPEndPoint(addressInfo.Address, 9094);
+
+ }
+
+ }
+
+ ///
+ /// Lists all useable network interfaces of the device this is running on for usage with PLCs
+ ///
+ public static IEnumerable GetUseableNetInterfaces () {
+
+ foreach (NetworkInterface netInterface in NetworkInterface.GetAllNetworkInterfaces()) {
+
+ bool isEthernet =
+ netInterface.NetworkInterfaceType == NetworkInterfaceType.Ethernet ||
+ netInterface.NetworkInterfaceType == NetworkInterfaceType.Ethernet3Megabit ||
+ netInterface.NetworkInterfaceType == NetworkInterfaceType.FastEthernetFx ||
+ netInterface.NetworkInterfaceType == NetworkInterfaceType.FastEthernetT ||
+ netInterface.NetworkInterfaceType == NetworkInterfaceType.GigabitEthernet;
+
+ bool isWlan = netInterface.NetworkInterfaceType == NetworkInterfaceType.Wireless80211;
+
+ bool isUsable = netInterface.OperationalStatus == OperationalStatus.Up;
+
+ if (!isUsable) continue;
+ if (!(isWlan || isEthernet)) continue;
+
+ IPInterfaceProperties ipProps = netInterface.GetIPProperties();
+ var hasUnicastInfo = ipProps.UnicastAddresses
+ .Any(x => x.Address.AddressFamily == AddressFamily.InterNetwork);
+
+ if (!hasUnicastInfo) continue;
+
+ yield return netInterface;
+
+ }
}
@@ -116,8 +156,8 @@ namespace MewtocolNet {
/// Delay between poll requests
public PollLevelConfigurator SetLevel(int level, TimeSpan interval) {
- if (level <= 1)
- throw new NotSupportedException($"The poll level {level} is not configurable");
+ if (level == PollLevel.Always || level == PollLevel.Never || level == PollLevel.FirstIteration)
+ throw new NotSupportedException("The poll level is reserved for the library");
if (!levelConfigs.ContainsKey(level)) {
levelConfigs.Add(level, new PollLevelConfig {
@@ -133,8 +173,8 @@ namespace MewtocolNet {
public PollLevelConfigurator SetLevel(int level, int skipNth) {
- if (level <= 1)
- throw new NotSupportedException($"The poll level {level} is not configurable");
+ if (level == PollLevel.Always || level == PollLevel.Never || level == PollLevel.FirstIteration)
+ throw new NotSupportedException("The poll level is reserved for the library");
if (!levelConfigs.ContainsKey(level)) {
levelConfigs.Add(level, new PollLevelConfig {
@@ -174,6 +214,56 @@ namespace MewtocolNet {
}
+ public class PostInitEth : PostInit {
+
+ ///
+ /// Sets the source of the outgoing ethernet connection
+ ///
+ public PostInit FromSource (IPEndPoint endpoint) {
+
+ if(endpoint == null)
+ throw new ArgumentNullException("Endpoint can't be null", nameof(endpoint));
+
+ if(intf is MewtocolInterfaceTcp imew) {
+
+ imew.HostEndpoint = endpoint;
+
+ }
+
+ return this;
+
+ }
+
+ ///
+ /// Sets the source of the outgoing ethernet connection
+ ///
+ /// IP address of the source interface (Format: 127.0.0.1)
+ /// Port of the source interface
+ ///
+ ///
+ public PostInit FromSource(string ip, int port) {
+
+ if (intf is MewtocolInterfaceTcp imew) {
+
+ if(port < IPEndPoint.MinPort)
+ throw new ArgumentException($"Source port cant be smaller than {IPEndPoint.MinPort}", nameof(port));
+
+ if (port > IPEndPoint.MaxPort)
+ throw new ArgumentException($"Source port cant be larger than {IPEndPoint.MaxPort}", nameof(port));
+
+ if (!IPAddress.TryParse(ip, out var ipParsed))
+ throw new ArgumentException("Failed to parse the source IP", nameof(ip));
+
+ imew.HostEndpoint = new IPEndPoint(ipParsed, port);
+
+ }
+
+ return this;
+
+ }
+
+ }
+
public class PostInit {
internal T intf;
@@ -269,18 +359,16 @@ namespace MewtocolNet {
///
/// A builder for attaching register collections
///
- public PostInit WithRegisters(Action builder) {
+ public PostInit WithRegisters(Action builder) {
try {
var plc = (MewtocolInterface)(object)intf;
- var assembler = new RegisterAssembler(plc);
- var regBuilder = new RBuildMult(plc);
+ var regBuilder = new RBuild(plc);
builder.Invoke(regBuilder);
- var registers = assembler.AssembleAll(regBuilder);
- plc.AddRegisters(registers.ToArray());
+ plc.AddRegisters(regBuilder.assembler.assembled.ToArray());
return this;
diff --git a/MewtocolNet/MewtocolInterface.cs b/MewtocolNet/MewtocolInterface.cs
index 0b2bcca..914f4be 100644
--- a/MewtocolNet/MewtocolInterface.cs
+++ b/MewtocolNet/MewtocolInterface.cs
@@ -55,7 +55,7 @@ namespace MewtocolNet {
private protected AsyncQueue queue = new AsyncQueue();
private protected Stopwatch speedStopwatchUpstr;
private protected Stopwatch speedStopwatchDownstr;
- private protected Task firstPollTask = new Task(() => { });
+ private protected Task firstPollTask;
private protected bool wasInitialStatusReceived;
private protected MewtocolVersion mewtocolVersion;
@@ -144,6 +144,8 @@ namespace MewtocolNet {
private protected MewtocolInterface() {
+ Logger.Start();
+
memoryManager = new MemoryAreaManager(this);
Connected += MewtocolInterface_Connected;
diff --git a/MewtocolNet/MewtocolInterfaceRegisterHandling.cs b/MewtocolNet/MewtocolInterfaceRegisterHandling.cs
index d4a3e3c..0840b71 100644
--- a/MewtocolNet/MewtocolInterfaceRegisterHandling.cs
+++ b/MewtocolNet/MewtocolInterfaceRegisterHandling.cs
@@ -2,6 +2,7 @@
using MewtocolNet.Logging;
using MewtocolNet.RegisterAttributes;
using MewtocolNet.RegisterBuilding;
+using MewtocolNet.RegisterBuilding.BuilderPatterns;
using MewtocolNet.Registers;
using System;
using System.Collections.Generic;
@@ -38,9 +39,6 @@ namespace MewtocolNet {
}
}
- ///
- public RBuildAnon Register => new RBuildAnon(this);
-
#region Register Polling
///
@@ -191,7 +189,7 @@ namespace MewtocolNet {
if (registerCollections.Count != 0)
throw new NotSupportedException("Register collections can only be build once");
- var regBuild = new RBuildMult(this);
+ var regBuild = new RBuildFromAttributes(this);
foreach (var collection in collections) {
@@ -218,7 +216,7 @@ namespace MewtocolNet {
if (pollFreqAttr != null) pollLevel = pollFreqAttr.pollLevel;
//add builder item
- var stp1 = regBuild
+ regBuild
.AddressFromAttribute(cAttribute.MewAddress, cAttribute.TypeDef, collection, prop, byteHint)
.AsType(dotnetType.IsEnum ? dotnetType.UnderlyingSystemType : dotnetType)
.PollLevel(pollLevel);
@@ -242,8 +240,8 @@ namespace MewtocolNet {
}
var assembler = new RegisterAssembler(this);
- var registers = assembler.AssembleAll(regBuild, true);
- AddRegisters(registers.ToArray());
+
+ AddRegisters(assembler.assembled.ToArray());
}
diff --git a/MewtocolNet/MewtocolInterfaceSerial.cs b/MewtocolNet/MewtocolInterfaceSerial.cs
index a561a94..83f826b 100644
--- a/MewtocolNet/MewtocolInterfaceSerial.cs
+++ b/MewtocolNet/MewtocolInterfaceSerial.cs
@@ -78,6 +78,9 @@ namespace MewtocolNet {
///
public void ConfigureConnection(string _portName, int _baudRate = 19200, int _dataBits = 8, Parity _parity = Parity.Odd, StopBits _stopBits = StopBits.One, int _station = 0xEE) {
+ if (IsConnected)
+ throw new NotSupportedException("Can't change the connection settings while the PLC is connected");
+
PortName = _portName;
SerialBaudRate = _baudRate;
SerialDataBits = _dataBits;
@@ -95,6 +98,9 @@ namespace MewtocolNet {
internal void ConfigureConnectionAuto() {
+ if (IsConnected)
+ throw new NotSupportedException("Can't change the connection settings while the PLC is connected");
+
autoSerial = true;
}
@@ -106,6 +112,10 @@ namespace MewtocolNet {
///
private async Task ConnectAsyncPriv(Func callBack, Action onTryingConfig = null) {
+ var portnames = SerialPort.GetPortNames();
+ if (!portnames.Any(x => x == PortName))
+ throw new NotSupportedException($"The port {PortName} is no valid port");
+
void OnTryConfig() {
onTryingConfig();
}
@@ -115,6 +125,8 @@ namespace MewtocolNet {
try {
+ firstPollTask = new Task(() => { });
+
Logger.Log($">> Intial connection start <<", LogLevel.Verbose, this);
isConnectingStage = true;
diff --git a/MewtocolNet/MewtocolInterfaceTcp.cs b/MewtocolNet/MewtocolInterfaceTcp.cs
index e527784..fa00aa8 100644
--- a/MewtocolNet/MewtocolInterfaceTcp.cs
+++ b/MewtocolNet/MewtocolInterfaceTcp.cs
@@ -1,5 +1,6 @@
using MewtocolNet.Logging;
using System;
+using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
@@ -23,7 +24,7 @@ namespace MewtocolNet {
public int Port { get; private set; }
///
- public IPEndPoint HostEndpoint { get; set; }
+ public IPEndPoint HostEndpoint { get; internal set; }
internal MewtocolInterfaceTcp() : base() { }
@@ -32,6 +33,9 @@ namespace MewtocolNet {
///
public void ConfigureConnection(string ip, int port = 9094, int station = 0xEE) {
+ if (IsConnected)
+ throw new NotSupportedException("Can't change the connection settings while the PLC is connected");
+
if (!IPAddress.TryParse(ip, out ipAddr))
throw new NotSupportedException($"The ip: {ip} is no valid ip address");
@@ -48,6 +52,9 @@ namespace MewtocolNet {
///
public void ConfigureConnection(IPAddress ip, int port = 9094, int station = 0xEE) {
+ if (IsConnected)
+ throw new NotSupportedException("Can't change the connection settings while the PLC is connected");
+
ipAddr = ip;
Port = port;
@@ -65,11 +72,22 @@ namespace MewtocolNet {
try {
+ firstPollTask = new Task(() => { });
+
Logger.Log($">> Intial connection start <<", LogLevel.Verbose, this);
isConnectingStage = true;
if (HostEndpoint != null) {
+ var hasEndpoint = Mewtocol
+ .GetSourceEndpoints()
+ .Any(x => x.Address.ToString() == HostEndpoint.Address.ToString());
+
+ if (!hasEndpoint)
+ throw new NotSupportedException($"The specified source endpoint: " +
+ $"{HostEndpoint}, doesn't exist on the device, " +
+ $"use 'Mewtocol.GetSourceEndpoints()' to find applicable ones");
+
client = new TcpClient(HostEndpoint) {
ReceiveBufferSize = RecBufferSize,
NoDelay = false,
diff --git a/MewtocolNet/MewtocolNet.csproj b/MewtocolNet/MewtocolNet.csproj
index 89d2219..7a357ce 100644
--- a/MewtocolNet/MewtocolNet.csproj
+++ b/MewtocolNet/MewtocolNet.csproj
@@ -37,7 +37,7 @@
-
+
diff --git a/MewtocolNet/PollLevel.cs b/MewtocolNet/PollLevel.cs
new file mode 100644
index 0000000..9e23018
--- /dev/null
+++ b/MewtocolNet/PollLevel.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace MewtocolNet {
+
+ public static class PollLevel {
+
+ public const int Always = 1;
+
+ public const int FirstIteration = 254;
+
+ public const int Never = 255;
+
+ }
+
+}
diff --git a/MewtocolNet/RegisterBuilding/AddressTools.cs b/MewtocolNet/RegisterBuilding/AddressTools.cs
new file mode 100644
index 0000000..ae07479
--- /dev/null
+++ b/MewtocolNet/RegisterBuilding/AddressTools.cs
@@ -0,0 +1,266 @@
+using MewtocolNet.PublicEnums;
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Text.RegularExpressions;
+
+namespace MewtocolNet.RegisterBuilding {
+
+ internal static class AddressTools {
+
+ internal protected struct ParseResult {
+
+ internal ParseResultState state;
+
+ internal string hardFailReason;
+
+ internal StepData stepData;
+
+ }
+
+ //methods to test the input string on
+ static List> parseMethods = new List>() {
+
+ (x) => TryBuildBoolean(x),
+ (x) => TryBuildNumericBased(x),
+ (x) => TryBuildByteRangeBased(x),
+
+ };
+
+ //bool registers
+ private static ParseResult TryBuildBoolean(string plcAddrName) {
+
+ //regex to find special register values
+ var patternBool = new Regex(@"(?X|Y|R)(? [0-9]{0,3})(?(?:[0-9]|[A-F]){1})?");
+
+ var match = patternBool.Match(plcAddrName);
+
+ if (!match.Success)
+ return new ParseResult {
+ state = ParseResultState.FailedSoft
+ };
+
+ string prefix = match.Groups["prefix"].Value;
+ string area = match.Groups["area"].Value;
+ string special = match.Groups["special"].Value;
+
+ IOType regType;
+ uint areaAdd = 0;
+ byte specialAdd = 0x0;
+
+ //try cast the prefix
+ if (!Enum.TryParse(prefix, out regType)) {
+
+ return new ParseResult {
+ state = ParseResultState.FailedHard,
+ hardFailReason = $"Cannot parse '{plcAddrName}', the prefix is not allowed for boolean registers"
+ };
+
+ }
+
+ if (!string.IsNullOrEmpty(area) && !uint.TryParse(area, out areaAdd)) {
+
+ return new ParseResult {
+ state = ParseResultState.FailedHard,
+ hardFailReason = $"Cannot parse '{plcAddrName}', the area address: '{area}' is wrong"
+ };
+
+ }
+
+ //special address not given
+ if (string.IsNullOrEmpty(special) && !string.IsNullOrEmpty(area)) {
+
+ var isAreaInt = uint.TryParse(area, NumberStyles.Number, CultureInfo.InvariantCulture, out var areaInt);
+
+ if (isAreaInt && areaInt >= 0 && areaInt <= 9) {
+
+ //area address is actually meant as special address but 0-9
+ specialAdd = (byte)areaInt;
+ areaAdd = 0;
+
+
+ } else if (isAreaInt && areaInt > 9) {
+
+ //area adress is meant to be the actual area address
+ areaAdd = areaInt;
+ specialAdd = 0;
+
+ } else {
+
+ return new ParseResult {
+ state = ParseResultState.FailedHard,
+ hardFailReason = $"Cannot parse '{plcAddrName}', the special address: '{special}' is wrong 1",
+ };
+
+ }
+
+ } else {
+
+ //special address parsed as hex num
+ if (!byte.TryParse(special, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out specialAdd)) {
+
+ return new ParseResult {
+ state = ParseResultState.FailedHard,
+ hardFailReason = $"Cannot parse '{plcAddrName}', the special address: '{special}' is wrong 2",
+ };
+
+ }
+
+ }
+
+ return new ParseResult {
+ state = ParseResultState.Success,
+ stepData = new StepData {
+ regType = (RegisterType)(int)regType,
+ memAddress = areaAdd,
+ specialAddress = specialAdd,
+ }
+ };
+
+ }
+
+ // one to two word registers
+ private static ParseResult TryBuildNumericBased(string plcAddrName) {
+
+ var patternByte = new Regex(@"^(?DT|DDT)(? [0-9]{1,5})$");
+
+ var match = patternByte.Match(plcAddrName);
+
+ if (!match.Success)
+ return new ParseResult {
+ state = ParseResultState.FailedSoft
+ };
+
+ string prefix = match.Groups["prefix"].Value;
+ string area = match.Groups["area"].Value;
+
+ RegisterType regType;
+ uint areaAdd = 0;
+
+ //try cast the prefix
+ if (!Enum.TryParse(prefix, out regType)) {
+
+ return new ParseResult {
+ state = ParseResultState.FailedHard,
+ hardFailReason = $"Cannot parse '{plcAddrName}', the prefix is not allowed for numeric registers"
+ };
+
+ }
+
+ if (!string.IsNullOrEmpty(area) && !uint.TryParse(area, out areaAdd)) {
+
+ return new ParseResult {
+ state = ParseResultState.FailedHard,
+ hardFailReason = $"Cannot parse '{plcAddrName}', the area address: '{area}' is wrong"
+ };
+
+ }
+
+ return new ParseResult {
+ state = ParseResultState.Success,
+ stepData = new StepData {
+ regType = regType,
+ memAddress = areaAdd,
+ }
+ };
+
+ }
+
+ // one to two word registers
+ private static ParseResult TryBuildByteRangeBased(string plcAddrName) {
+
+ var split = plcAddrName.Split('-');
+
+ if (split.Length > 2)
+ return new ParseResult {
+ state = ParseResultState.FailedHard,
+ hardFailReason = $"Cannot parse '{plcAddrName}', to many delimters '-'"
+ };
+
+ uint[] addresses = new uint[2];
+
+ for (int i = 0; i < split.Length; i++) {
+
+ string addr = split[i];
+ var patternByte = new Regex(@"(?DT|DDT)(? [0-9]{1,5})");
+
+ var match = patternByte.Match(addr);
+
+ if (!match.Success)
+ return new ParseResult {
+ state = ParseResultState.FailedSoft
+ };
+
+ string prefix = match.Groups["prefix"].Value;
+ string area = match.Groups["area"].Value;
+
+ RegisterType regType;
+ uint areaAdd = 0;
+
+ //try cast the prefix
+ if (!Enum.TryParse(prefix, out regType)) {
+
+ return new ParseResult {
+ state = ParseResultState.FailedHard,
+ hardFailReason = $"Cannot parse '{plcAddrName}', the prefix is not allowed for word range registers"
+ };
+
+ }
+
+ if (!string.IsNullOrEmpty(area) && !uint.TryParse(area, out areaAdd)) {
+
+ return new ParseResult {
+ state = ParseResultState.FailedHard,
+ hardFailReason = $"Cannot parse '{plcAddrName}', the area address: '{area}' is wrong"
+ };
+
+ }
+
+ addresses[i] = areaAdd;
+
+ }
+
+ return new ParseResult {
+ state = ParseResultState.Success,
+ stepData = new StepData {
+ regType = RegisterType.DT_BYTE_RANGE,
+ wasAddressStringRangeBased = true,
+ dotnetVarType = typeof(byte[]),
+ memAddress = addresses[0],
+ byteSizeHint = (addresses[1] - addresses[0] + 1) * 2
+ }
+ };
+
+ }
+
+ internal static StepData ParseAddress(string plcAddrName, string name = null) {
+
+ foreach (var method in parseMethods) {
+
+ var res = method.Invoke(plcAddrName);
+
+ if (res.state == ParseResultState.Success) {
+
+ if (!string.IsNullOrEmpty(name)) res.stepData.name = name;
+
+ res.stepData.originalParseStr = plcAddrName;
+ res.stepData.buildSource = RegisterBuildSource.Manual;
+
+ return new StepData().Map(res.stepData);
+
+ } else if (res.state == ParseResultState.FailedHard) {
+
+ throw new Exception(res.hardFailReason);
+
+ }
+
+ }
+
+ throw new Exception("Wrong input format");
+
+ }
+
+ }
+
+
+}
diff --git a/MewtocolNet/RegisterBuilding/BuilderPatterns/RBuild.cs b/MewtocolNet/RegisterBuilding/BuilderPatterns/RBuild.cs
new file mode 100644
index 0000000..444404d
--- /dev/null
+++ b/MewtocolNet/RegisterBuilding/BuilderPatterns/RBuild.cs
@@ -0,0 +1,253 @@
+using MewtocolNet;
+using MewtocolNet.PublicEnums;
+using MewtocolNet.RegisterAttributes;
+using MewtocolNet.Registers;
+using System;
+using System.Linq;
+using System.Reflection;
+using static MewtocolNet.RegisterBuilding.BuilderPatterns.RBuild;
+
+namespace MewtocolNet.RegisterBuilding.BuilderPatterns {
+
+ ///
+ /// Contains useful tools for bunch register creation
+ ///
+ public class RBuild {
+
+ internal RegisterAssembler assembler;
+
+ public RBuild(MewtocolInterface plc) {
+
+ assembler = new RegisterAssembler(plc);
+
+ }
+
+ public class SBaseRB : StepBase {
+
+ internal RBuild builder;
+
+ }
+
+ #region String parse stage
+
+ internal Register Assemble(StepBase stp) => assembler.Assemble(stp.Data);
+
+ //struct constructor
+
+ public StructStp Struct(string fpAddr, string name = null) where T : struct {
+
+ var data = AddressTools.ParseAddress(fpAddr, name);
+
+ data.dotnetVarType = typeof(T);
+
+ return new StructStp(data) {
+ builder = this,
+ };
+
+ }
+
+ //string constructor
+
+ public StringStp String(string fpAddr, int sizeHint, string name = null) {
+
+ var data = AddressTools.ParseAddress(fpAddr, name);
+
+ data.dotnetVarType = typeof(string);
+ data.byteSizeHint = (uint)sizeHint;
+
+ return new StringStp(data) {
+ builder = this,
+ };
+
+ }
+
+ #endregion
+
+ #region Typing stage
+
+ //structs can lead to arrays
+ public class StructStp : ArrayStp where T : struct {
+
+ internal StructStp(StepData data) {
+
+ this.Data = data;
+ this.Map(StepBaseTyper.AsType(this, typeof(T)));
+
+ }
+
+ public void Build() => builder.Assemble(this);
+
+ public void Build(out IRegister reference) => reference = (IRegister)builder.Assemble(this);
+
+ public StructStpOut PollLevel(int level) {
+
+ Data.pollLevel = level;
+ return new StructStpOut().Map(this);
+
+ }
+
+ }
+
+ public class StructStpOut : SBaseRB where T : struct {
+
+ public void Build() => builder.Assemble(this);
+
+ public void Build(out IRegister reference) => reference = (IRegister)builder.Assemble(this);
+
+ }
+
+ //strings can lead to arrays
+ public class StringStp : ArrayStp where T : class {
+
+ internal StringStp(StepData data) {
+
+ this.Data = data;
+ this.Map(StepBaseTyper.AsType(this, typeof(T)));
+
+ }
+
+ public void Build() => builder.Assemble(this);
+
+ public void Build(out IStringRegister reference) => reference = (IStringRegister)builder.Assemble(this);
+
+ public StringOutStp PollLevel(int level) {
+
+ Data.pollLevel = level;
+ return new StringOutStp().Map(this);
+
+ }
+
+ }
+
+ public class StringOutStp : SBaseRB {
+
+ public void Build() => builder.Assemble(this);
+
+ public void Build(out IStringRegister reference) => reference = (IStringRegister)builder.Assemble(this);
+
+ }
+
+ //arrays
+ public class ArrayStp : SBaseRB {
+
+ public TypedArr1D AsArray(int i) {
+
+ Data.arrayIndicies = new int[] { i };
+ SetSizing();
+ return new TypedArr1D().Map(this);
+
+ }
+
+ public TypedArr2D AsArray(int i1, int i2) {
+
+ Data.arrayIndicies = new int[] { i1, i2 };
+ SetSizing();
+ return new TypedArr2D().Map(this);
+
+ }
+
+ public TypedArr3D AsArray(int i1, int i2, int i3) {
+
+ Data.arrayIndicies = new int[] { i1, i2, i3 };
+ SetSizing();
+ return new TypedArr3D().Map(this);
+
+ }
+
+ private void SetSizing() {
+
+ var arr = Array.CreateInstance(Data.dotnetVarType, Data.arrayIndicies.ToArray());
+
+ Data.dotnetVarType = arr.GetType();
+
+ var itemCount = (uint)Data.arrayIndicies.Aggregate((a, x) => a * x);
+
+ if (typeof(T) == typeof(string)) {
+
+ var byteSize = Data.byteSizeHint.Value;
+ if (byteSize % 2 != 0) byteSize++;
+ Data.byteSizeHint = itemCount * (byteSize + 4);
+
+ } else {
+
+ var byteSize = (uint)typeof(T).DetermineTypeByteIntialSize();
+ Data.byteSizeHint = itemCount * byteSize;
+
+ }
+
+ }
+
+ }
+
+ #endregion
+
+ #region Typing size hint
+
+ //1D array
+
+ public class TypedArr1D : TypedArr1DOut {
+
+ public TypedArr1DOut PollLevel(int level) {
+
+ Data.pollLevel = level;
+ return new TypedArr1DOut().Map(this);
+
+ }
+
+ }
+
+ public class TypedArr1DOut : SBaseRB {
+
+ public IArrayRegister Build() => (IArrayRegister)builder.Assemble(this);
+
+ public void Build(out IArrayRegister reference) => reference = (IArrayRegister)builder.Assemble(this);
+
+ }
+
+ //2D array
+
+ public class TypedArr2D : TypedArr2DOut {
+
+ public TypedArr2DOut PollLevel(int level) {
+
+ Data.pollLevel = level;
+ return new TypedArr2DOut().Map(this);
+
+ }
+
+ }
+
+ public class TypedArr2DOut : SBaseRB {
+
+ public IArrayRegister2D Build() => (IArrayRegister2D)builder.Assemble(this);
+
+ public void Build(out IArrayRegister2D reference) => reference = (IArrayRegister2D)builder.Assemble(this);
+
+ }
+
+ //3D array
+
+ public class TypedArr3D : SBaseRB {
+
+ public TypedArr3DOut PollLevel(int level) {
+
+ Data.pollLevel = level;
+ return new TypedArr3DOut().Map(this);
+
+ }
+
+ }
+
+ public class TypedArr3DOut : SBaseRB {
+
+ public IArrayRegister3D Build() => (IArrayRegister3D)builder.Assemble(this);
+
+ public void Build(out IArrayRegister3D reference) => reference = (IArrayRegister3D)builder.Assemble(this);
+
+ }
+
+ #endregion
+
+ }
+
+}
diff --git a/MewtocolNet/RegisterBuilding/BuilderPatterns/RBuildFromAttributes.cs b/MewtocolNet/RegisterBuilding/BuilderPatterns/RBuildFromAttributes.cs
new file mode 100644
index 0000000..d2b8279
--- /dev/null
+++ b/MewtocolNet/RegisterBuilding/BuilderPatterns/RBuildFromAttributes.cs
@@ -0,0 +1,70 @@
+using MewtocolNet.PublicEnums;
+using MewtocolNet.RegisterAttributes;
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Text;
+
+namespace MewtocolNet.RegisterBuilding.BuilderPatterns {
+
+ internal class RBuildFromAttributes {
+
+ internal RegisterAssembler assembler;
+
+ public RBuildFromAttributes(MewtocolInterface plc) {
+
+ assembler = new RegisterAssembler(plc);
+
+ }
+
+ public class SBaseRBDyn : StepBase {
+
+ internal RBuildFromAttributes builder;
+
+ }
+
+ //internal use only, adds a type definition (for use when building from attibute)
+ internal DynamicStp AddressFromAttribute(string dtAddr, string typeDef, RegisterCollection regCol, PropertyInfo prop, uint? bytesizeHint = null) {
+
+ var stpData = AddressTools.ParseAddress(dtAddr);
+
+ stpData.typeDef = typeDef;
+ stpData.buildSource = RegisterBuildSource.Attribute;
+ stpData.regCollection = regCol;
+ stpData.boundProperty = prop;
+ stpData.byteSizeHint = bytesizeHint;
+
+ return new DynamicStp {
+ builder = this,
+ Data = stpData,
+ };
+
+ }
+
+ //non generic
+ internal class DynamicStp : SBaseRBDyn {
+
+ public DynamicRegister AsType() => new DynamicRegister().Map(StepBaseTyper.AsType(this));
+
+ public DynamicRegister AsType(Type type) => new DynamicRegister().Map(StepBaseTyper.AsType(this, type));
+
+ public DynamicRegister AsType(PlcVarType type) => new DynamicRegister().Map(StepBaseTyper.AsType(this, type));
+
+ public DynamicRegister AsType(string type) => new DynamicRegister().Map(StepBaseTyper.AsType(this, type));
+
+ public DynamicRegister AsTypeArray(params int[] indicies) => new DynamicRegister().Map(StepBaseTyper.AsTypeArray(this, indicies));
+
+ }
+
+ internal class DynamicRegister : SBaseRBDyn {
+
+ public void PollLevel (int level) {
+
+ Data.pollLevel = level;
+
+ }
+
+ }
+
+ }
+}
diff --git a/MewtocolNet/RegisterBuilding/RBuildAnon.cs b/MewtocolNet/RegisterBuilding/RBuildAnon.cs
deleted file mode 100644
index 2c0198c..0000000
--- a/MewtocolNet/RegisterBuilding/RBuildAnon.cs
+++ /dev/null
@@ -1,104 +0,0 @@
-using System;
-using MewtocolNet.Registers;
-using System.Threading.Tasks;
-
-namespace MewtocolNet.RegisterBuilding {
-
- ///
- /// Anonymous register builder
- ///
- public class RBuildAnon : RBuildBase {
-
- public RBuildAnon(MewtocolInterface plc) : base(plc) { }
-
- ///
- public SAddress Address(string plcAddrName) {
-
- return new SAddress {
- attachedPlc = this.attachedPLC,
- addrString = plcAddrName
- };
-
- }
-
- public new class SAddress {
-
- protected internal MewtocolInterface attachedPlc;
- protected internal string addrString;
- protected internal string name;
-
- ///
- /// Writes data to the register and bypasses the memory manager
- ///
- /// The value to write
- /// True if success
- public async Task WriteToAsync(T value) {
-
- throw new NotImplementedException();
-
- //try {
-
- // var tempRegister = AssembleTemporaryRegister();
- // return await tempRegister.WriteAsync(value);
-
- //} catch {
-
- // throw;
-
- //}
-
- }
-
- ///
- /// Reads data from the register and bypasses the memory manager
- ///
- /// The value read or null if failed
- public async Task ReadFromAsync() {
-
- throw new NotImplementedException();
-
- //try {
-
- // var tempRegister = AssembleTemporaryRegister();
- // return (T)await tempRegister.ReadAsync();
-
- //} catch {
-
- // throw;
-
- //}
-
- }
-
- private Register AssembleTemporaryRegister() {
-
- var temp = new RBuildMult(attachedPlc).Address(addrString).AsType();
-
- var assembler = new RegisterAssembler(attachedPlc);
- return assembler.Assemble(temp.Data);
-
- }
-
- }
-
- }
-
- public class RBuildSingle : RBuildBase {
-
- public RBuildSingle(MewtocolInterface plc) : base(plc) { }
-
- ///
- public SAddress Address(string plcAddrName, string name = null) {
-
- var data = ParseAddress(plcAddrName, name);
-
- return new SAddress {
- Data = data,
- builder = this,
- };
-
- }
-
- }
-
-}
diff --git a/MewtocolNet/RegisterBuilding/RBuildBase.cs b/MewtocolNet/RegisterBuilding/RBuildBase.cs
deleted file mode 100644
index be25c6d..0000000
--- a/MewtocolNet/RegisterBuilding/RBuildBase.cs
+++ /dev/null
@@ -1,607 +0,0 @@
-using MewtocolNet.PublicEnums;
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Linq;
-using System.Reflection;
-using System.Text.RegularExpressions;
-
-namespace MewtocolNet.RegisterBuilding {
-
- public class RBuildBase {
-
- protected internal MewtocolInterface attachedPLC;
-
- public RBuildBase() { }
-
- internal RBuildBase(MewtocolInterface plc) => attachedPLC = plc;
-
- internal List unfinishedList = new List();
-
- #region Parser stage
-
- //methods to test the input string on
- protected static List> parseMethods = new List>() {
-
- (x) => TryBuildBoolean(x),
- (x) => TryBuildNumericBased(x),
- (x) => TryBuildByteRangeBased(x),
-
- };
-
- public class SBase {
-
- public SBase() { }
-
- internal SBase(StepData data, RBuildBase bldr) {
- Data = data;
- builder = bldr;
- }
-
- internal StepData Data;
-
- internal RBuildBase builder;
-
- }
-
- internal protected struct ParseResult {
-
- internal ParseResultState state;
-
- internal string hardFailReason;
-
- internal BaseStepData stepData;
-
- }
-
- //bool registers
- private static ParseResult TryBuildBoolean(string plcAddrName) {
-
- //regex to find special register values
- var patternBool = new Regex(@"(?X|Y|R)(? [0-9]{0,3})(?(?:[0-9]|[A-F]){1})?");
-
- var match = patternBool.Match(plcAddrName);
-
- if (!match.Success)
- return new ParseResult {
- state = ParseResultState.FailedSoft
- };
-
- string prefix = match.Groups["prefix"].Value;
- string area = match.Groups["area"].Value;
- string special = match.Groups["special"].Value;
-
- IOType regType;
- uint areaAdd = 0;
- byte specialAdd = 0x0;
-
- //try cast the prefix
- if (!Enum.TryParse(prefix, out regType)) {
-
- return new ParseResult {
- state = ParseResultState.FailedHard,
- hardFailReason = $"Cannot parse '{plcAddrName}', the prefix is not allowed for boolean registers"
- };
-
- }
-
- if (!string.IsNullOrEmpty(area) && !uint.TryParse(area, out areaAdd)) {
-
- return new ParseResult {
- state = ParseResultState.FailedHard,
- hardFailReason = $"Cannot parse '{plcAddrName}', the area address: '{area}' is wrong"
- };
-
- }
-
- //special address not given
- if (string.IsNullOrEmpty(special) && !string.IsNullOrEmpty(area)) {
-
- var isAreaInt = uint.TryParse(area, NumberStyles.Number, CultureInfo.InvariantCulture, out var areaInt);
-
- if (isAreaInt && areaInt >= 0 && areaInt <= 9) {
-
- //area address is actually meant as special address but 0-9
- specialAdd = (byte)areaInt;
- areaAdd = 0;
-
-
- } else if (isAreaInt && areaInt > 9) {
-
- //area adress is meant to be the actual area address
- areaAdd = areaInt;
- specialAdd = 0;
-
- } else {
-
- return new ParseResult {
- state = ParseResultState.FailedHard,
- hardFailReason = $"Cannot parse '{plcAddrName}', the special address: '{special}' is wrong 1",
- };
-
- }
-
- } else {
-
- //special address parsed as hex num
- if (!byte.TryParse(special, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out specialAdd)) {
-
- return new ParseResult {
- state = ParseResultState.FailedHard,
- hardFailReason = $"Cannot parse '{plcAddrName}', the special address: '{special}' is wrong 2",
- };
-
- }
-
- }
-
- return new ParseResult {
- state = ParseResultState.Success,
- stepData = new StepData {
- regType = (RegisterType)(int)regType,
- memAddress = areaAdd,
- specialAddress = specialAdd,
- }
- };
-
- }
-
- // one to two word registers
- private static ParseResult TryBuildNumericBased(string plcAddrName) {
-
- var patternByte = new Regex(@"^(?DT|DDT)(? [0-9]{1,5})$");
-
- var match = patternByte.Match(plcAddrName);
-
- if (!match.Success)
- return new ParseResult {
- state = ParseResultState.FailedSoft
- };
-
- string prefix = match.Groups["prefix"].Value;
- string area = match.Groups["area"].Value;
-
- RegisterType regType;
- uint areaAdd = 0;
-
- //try cast the prefix
- if (!Enum.TryParse(prefix, out regType)) {
-
- return new ParseResult {
- state = ParseResultState.FailedHard,
- hardFailReason = $"Cannot parse '{plcAddrName}', the prefix is not allowed for numeric registers"
- };
-
- }
-
- if (!string.IsNullOrEmpty(area) && !uint.TryParse(area, out areaAdd)) {
-
- return new ParseResult {
- state = ParseResultState.FailedHard,
- hardFailReason = $"Cannot parse '{plcAddrName}', the area address: '{area}' is wrong"
- };
-
- }
-
- return new ParseResult {
- state = ParseResultState.Success,
- stepData = new StepData {
- regType = regType,
- memAddress = areaAdd,
- }
- };
-
- }
-
- // one to two word registers
- private static ParseResult TryBuildByteRangeBased(string plcAddrName) {
-
- var split = plcAddrName.Split('-');
-
- if (split.Length > 2)
- return new ParseResult {
- state = ParseResultState.FailedHard,
- hardFailReason = $"Cannot parse '{plcAddrName}', to many delimters '-'"
- };
-
- uint[] addresses = new uint[2];
-
- for (int i = 0; i < split.Length; i++) {
-
- string addr = split[i];
- var patternByte = new Regex(@"(?DT|DDT)(? [0-9]{1,5})");
-
- var match = patternByte.Match(addr);
-
- if (!match.Success)
- return new ParseResult {
- state = ParseResultState.FailedSoft
- };
-
- string prefix = match.Groups["prefix"].Value;
- string area = match.Groups["area"].Value;
-
- RegisterType regType;
- uint areaAdd = 0;
-
- //try cast the prefix
- if (!Enum.TryParse(prefix, out regType)) {
-
- return new ParseResult {
- state = ParseResultState.FailedHard,
- hardFailReason = $"Cannot parse '{plcAddrName}', the prefix is not allowed for word range registers"
- };
-
- }
-
- if (!string.IsNullOrEmpty(area) && !uint.TryParse(area, out areaAdd)) {
-
- return new ParseResult {
- state = ParseResultState.FailedHard,
- hardFailReason = $"Cannot parse '{plcAddrName}', the area address: '{area}' is wrong"
- };
-
- }
-
- addresses[i] = areaAdd;
-
- }
-
- return new ParseResult {
- state = ParseResultState.Success,
- stepData = new StepData {
- regType = RegisterType.DT_BYTE_RANGE,
- wasAddressStringRangeBased = true,
- dotnetVarType = typeof(byte[]),
- memAddress = addresses[0],
- byteSizeHint = (addresses[1] - addresses[0] + 1) * 2
- }
- };
-
- }
-
- #endregion
-
- #region Addressing stage
-
- internal StepData ParseAddress(string plcAddrName, string name = null) {
-
- foreach (var method in parseMethods) {
-
- var res = method.Invoke(plcAddrName);
-
- if (res.state == ParseResultState.Success) {
-
- if (!string.IsNullOrEmpty(name)) res.stepData.name = name;
-
- res.stepData.originalParseStr = plcAddrName;
- res.stepData.buildSource = RegisterBuildSource.Manual;
-
- return new StepData().Map(res.stepData);
-
- } else if (res.state == ParseResultState.FailedHard) {
-
- throw new Exception(res.hardFailReason);
-
- }
-
- }
-
- throw new Exception("Wrong input format");
-
- }
-
- #endregion
-
- #region Typing stage
-
- public class SAddress : SBase {
-
- ///
- /// Sets the register as a dotnet type for direct conversion
- ///
- ///
- ///
- ///
- internal TypedRegister AsType() {
-
- if (!typeof(T).IsAllowedPlcCastingType()) {
-
- throw new NotSupportedException($"The dotnet type {typeof(T)}, is not supported for PLC type casting");
-
- }
-
- Data.dotnetVarType = typeof(T);
-
- return new TypedRegister().Map(this);
-
- }
-
- ///
- /// Sets the register as a dotnet type for direct conversion
- ///
- ///
- ///
- ///
- ///
- internal TypedRegister AsType(Type type) {
-
- //was ranged syntax array build
- if (Data.wasAddressStringRangeBased && type.IsArray && type.GetArrayRank() == 1) {
-
- //invoke generic AsTypeArray
- MethodInfo method = typeof(SAddress).GetMethod(nameof(AsTypeArray));
- MethodInfo generic = method.MakeGenericMethod(type);
-
- var elementType = type.GetElementType();
-
- if (type != typeof(byte[]) && !elementType.IsAllowedPlcCastingType()) {
-
- throw new NotSupportedException($"The dotnet type {elementType}, is not supported for PLC type casting");
-
- }
-
- int byteSizePerItem = elementType.DetermineTypeByteIntialSize();
-
- //check if it fits without remainder
- if (Data.byteSizeHint % byteSizePerItem != 0) {
- throw new NotSupportedException($"The array element type {elementType} doesn't fit into the adress range");
- }
-
- return (TypedRegister)generic.Invoke(this, new object[] {
- //element count
- new int[] { (int)((Data.byteSizeHint / byteSizePerItem)) }
- });
-
- } else if (Data.wasAddressStringRangeBased) {
-
- throw new NotSupportedException("DT range building is only allowed for 1 dimensional arrays");
-
- }
-
- //for internal only, relay to AsType from string
- if (Data.buildSource == RegisterBuildSource.Attribute) {
-
- if ((type.IsArray || type == typeof(string)) && Data.typeDef != null) {
-
- return AsType(Data.typeDef);
-
- } else if (type.IsArray && Data.typeDef == null) {
-
- throw new NotSupportedException("Typedef parameter is needed for array types");
-
- } else if (Data.typeDef != null) {
-
- throw new NotSupportedException("Can't use the typedef parameter on non array or string types");
-
- }
-
- }
-
- if (!type.IsAllowedPlcCastingType()) {
-
- throw new NotSupportedException($"The dotnet type {type}, is not supported for PLC type casting");
-
- }
-
- Data.dotnetVarType = type;
-
- return new TypedRegister().Map(this);
-
- }
-
- ///
- /// Sets the register type as a predefined
- ///
- internal TypedRegister AsType(PlcVarType type) {
-
- Data.dotnetVarType = type.GetDefaultDotnetType();
-
- return new TypedRegister().Map(this);
-
- }
-
- ///
- /// Sets the register type from the plc type string
- /// Supported types:
- ///
- /// BOOL Boolean R/X/Y registers
- /// INT 16 bit signed integer
- /// UINT 16 bit un-signed integer
- /// DINT 32 bit signed integer
- /// UDINT 32 bit un-signed integer
- /// REAL 32 bit floating point
- /// TIME 32 bit time interpreted as
- /// STRING String of chars, the interface will automatically get the length
- /// STRING[N] String of chars, pre capped to N
- /// WORD 16 bit word interpreted as
- /// DWORD 32 bit double word interpreted as
- ///
- ///
- internal TypedRegister AsType(string type) {
-
- var regexString = new Regex(@"^STRING *\[(?[0-9]*)\]$", RegexOptions.IgnoreCase);
- var regexArray = new Regex(@"^ARRAY *\[(?[0-9]*)..(?[0-9]*)(?:\,(?[0-9]*)..(?[0-9]*))?(?:\,(?[0-9]*)..(?[0-9]*))?\] *OF {1,}(?.*)$", RegexOptions.IgnoreCase);
-
- var stringMatch = regexString.Match(type);
- var arrayMatch = regexArray.Match(type);
-
- if (Enum.TryParse(type, out var parsed)) {
-
- Data.dotnetVarType = parsed.GetDefaultDotnetType();
-
- } else if (stringMatch.Success) {
-
- Data.dotnetVarType = typeof(string);
- Data.byteSizeHint = uint.Parse(stringMatch.Groups["len"].Value);
-
- } else if (arrayMatch.Success) {
-
- //invoke generic AsTypeArray
-
- string arrTypeString = arrayMatch.Groups["t"].Value;
- Type dotnetArrType = null;
-
- var stringMatchInArray = regexString.Match(arrTypeString);
-
- if (Enum.TryParse(arrTypeString, out var parsedArrType) && parsedArrType != PlcVarType.STRING) {
-
- dotnetArrType = parsedArrType.GetDefaultDotnetType();
-
-
- } else if (stringMatchInArray.Success) {
-
- dotnetArrType = typeof(string);
- //Data.byteSizeHint = uint.Parse(stringMatch.Groups["len"].Value);
-
- } else {
-
- throw new NotSupportedException($"The FP type '{arrTypeString}' was not recognized");
-
- }
-
- var indices = new List();
-
- for (int i = 1; i < 4; i++) {
-
- var arrStart = arrayMatch.Groups[$"S{i}"]?.Value;
- var arrEnd = arrayMatch.Groups[$"E{i}"]?.Value;
- if (string.IsNullOrEmpty(arrStart) || string.IsNullOrEmpty(arrEnd)) break;
-
- var arrStartInt = int.Parse(arrStart);
- var arrEndInt = int.Parse(arrEnd);
-
- indices.Add(arrEndInt - arrStartInt + 1);
-
- }
-
- var arr = Array.CreateInstance(dotnetArrType, indices.ToArray());
- var arrType = arr.GetType();
-
- MethodInfo method = typeof(SAddress).GetMethod(nameof(AsTypeArray));
- MethodInfo generic = method.MakeGenericMethod(arrType);
-
- var tmp = (TypedRegister)generic.Invoke(this, new object[] {
- indices.ToArray()
- });
-
- tmp.builder = builder;
- tmp.Data = Data;
-
- return tmp;
-
- } else {
-
- throw new NotSupportedException($"The FP type '{type}' was not recognized");
-
- }
-
- return new TypedRegister().Map(this);
-
- }
-
- ///
- /// Sets the register as a (multidimensional) array targeting a PLC array
- ///
- ///
- ///
- ///
- ///
- /// Indicies for multi dimensional arrays, for normal arrays just one INT
- ///
- ///
- /// One dimensional arrays:
- /// ARRAY [0..2] OF INT = AsTypeArray<short[]>(3)
- /// ARRAY [5..6] OF DWORD = AsTypeArray<DWord[]>(2)
- ///
- /// Multi dimensional arrays:
- /// ARRAY [0..2, 0..3, 0..4] OF INT = AsTypeArray<short[,,]>(3,4,5)
- /// ARRAY [5..6, 0..2] OF DWORD = AsTypeArray<DWord[,]>(2, 3)
- ///
- internal TypedRegister AsTypeArray(params int[] indicies) {
-
- if (!typeof(T).IsArray)
- throw new NotSupportedException($"The type {typeof(T)} was no array");
-
- var arrRank = typeof(T).GetArrayRank();
- var elBaseType = typeof(T).GetElementType();
-
- if (arrRank > 3)
- throw new NotSupportedException($"4+ dimensional arrays are not supported");
-
- if (typeof(T) != typeof(byte[]) && !elBaseType.IsAllowedPlcCastingType())
- throw new NotSupportedException($"The dotnet type {typeof(T)}, is not supported for PLC array type casting");
-
- if (arrRank != indicies.Length)
- throw new NotSupportedException($"All dimensional array indicies must be set");
-
- Data.dotnetVarType = typeof(T);
-
- int byteSizePerItem = elBaseType.DetermineTypeByteIntialSize();
- int calcedTotalByteSize = indicies.Aggregate((a, x) => a * x) * byteSizePerItem;
-
- Data.byteSizeHint = (uint)calcedTotalByteSize;
- Data.arrayIndicies = indicies;
-
- if (Data.byteSizeHint % byteSizePerItem != 0) {
- throw new NotSupportedException($"The array element type {elBaseType} doesn't fit into the adress range");
- }
-
- return new TypedRegister().Map(this);
-
- }
-
- }
-
- #endregion
-
- #region Typing size hint
-
- public class TypedRegister : SBase {
-
- internal OptionsRegister SizeHint(int hint) {
-
- Data.byteSizeHint = (uint)hint;
-
- return new OptionsRegister().Map(this);
-
- }
-
- internal OptionsRegister PollLevel(int level) {
-
- Data.pollLevel = level;
-
- return new OptionsRegister().Map(this);
-
- }
-
- }
-
- #endregion
-
- #region Options stage
-
- public class OptionsRegister : SBase {
-
- internal OptionsRegister() { }
-
- internal OptionsRegister(StepData data, RBuildBase bldr) : base(data, bldr) { }
-
- ///
- /// Sets the poll level of the register
- ///
- public OptionsRegister PollLevel(int level) {
-
- Data.pollLevel = level;
-
- return this;
-
- }
-
- }
-
- #endregion
-
- }
-
-}
diff --git a/MewtocolNet/RegisterBuilding/RBuildMult.cs b/MewtocolNet/RegisterBuilding/RBuildMult.cs
deleted file mode 100644
index c2683f7..0000000
--- a/MewtocolNet/RegisterBuilding/RBuildMult.cs
+++ /dev/null
@@ -1,375 +0,0 @@
-using MewtocolNet.PublicEnums;
-using MewtocolNet.RegisterAttributes;
-using MewtocolNet.Registers;
-using System;
-using System.Reflection;
-using static MewtocolNet.RegisterBuilding.RBuildMult;
-
-namespace MewtocolNet.RegisterBuilding {
-
- ///
- /// Contains useful tools for bunch register creation
- ///
- public class RBuildMult : RBuildBase {
-
- public RBuildMult(MewtocolInterface plc) : base(plc) { }
-
- #region String parse stage
-
- //at runtime constructor
-
- ///
- /// Starts the register builder for a new mewtocol address
- /// Examples:
- /// Address("DT100") | Address("R10A") | Address("DDT50", "MyRegisterName")
- ///
- /// Address name formatted as FP-Address like in FP-Winpro
- /// Custom name for the register to referr to it later
- public AddressStp Address(string dtAddr, string name = null) {
-
- var data = ParseAddress(dtAddr, name);
-
- unfinishedList.Add(data);
-
- return new AddressStp {
- Data = data,
- builder = this,
- };
-
- }
-
- //struct constructor
-
- public StructStp Struct(string dtAddr, string name = null) where T : struct {
-
- var data = ParseAddress(dtAddr, name);
-
- data.dotnetVarType = typeof(T);
-
- unfinishedList.Add(data);
-
- return new StructStp(data) {
- builder = this,
- };
-
- }
-
- public StringStp String(string dtAddr, int sizeHint, string name = null) where T : class {
-
- var data = ParseAddress(dtAddr, name);
-
- data.dotnetVarType = typeof(T);
- data.byteSizeHint = (uint)sizeHint;
-
- unfinishedList.Add(data);
-
- if (typeof(T).IsArray) {
-
- return new StringStp(data, true) {
- Data = data,
- builder = this,
- };
-
- }
-
- return new StringStp(data) {
- builder = this,
- };
-
- }
-
- public ArrayStp Array(string dtAddr, string name = null) where T : class {
-
- var data = ParseAddress(dtAddr, name);
-
- data.dotnetVarType = typeof(T);
-
- unfinishedList.Add(data);
-
- if (typeof(T).IsArray) {
-
- return new ArrayStp(data, true) {
- Data = data,
- builder = this,
- };
-
- }
-
- return new ArrayStp(data) {
- builder = this,
- };
-
- }
-
- //internal use only, adds a type definition (for use when building from attibute)
- internal AddressStp AddressFromAttribute(string dtAddr, string typeDef, RegisterCollection regCol, PropertyInfo prop, uint? bytesizeHint = null) {
-
- var built = Address(dtAddr);
-
- built.Data.typeDef = typeDef;
- built.Data.buildSource = RegisterBuildSource.Attribute;
- built.Data.regCollection = regCol;
- built.Data.boundProperty = prop;
- built.Data.byteSizeHint = bytesizeHint;
-
- return built;
-
- }
-
- #endregion
-
- #region Typing stage
-
- //non generic
- public new class AddressStp : RBuildBase.SAddress {
-
- public new TypedRegister AsType() => new TypedRegister().Map(base.AsType());
-
- public new TypedRegister AsType(Type type) => new TypedRegister().Map(base.AsType(type));
-
- public new TypedRegister AsType(PlcVarType type) => new TypedRegister().Map(base.AsType(type));
-
- public new TypedRegister AsType(string type) => new TypedRegister().Map(base.AsType(type));
-
- public new TypedRegister AsTypeArray(params int[] indicies) => new TypedRegister().Map(base.AsTypeArray(indicies));
-
- }
-
- //structs
- public class StructStp : RBuildBase.SAddress where T : struct {
-
- internal StructStp(StepData data) {
-
- this.Data = data;
-
- this.Map(AsType(typeof(T)));
-
- }
-
- internal StructStp(StepData data, bool arrayed) {
-
- this.Data = data;
-
- }
-
- ///
- /// Outputs the generated
- ///
- public void Out(Action> registerOut) {
-
- Data.registerOut = new Action(o => registerOut((IRegister)o));
-
- }
-
- public OutStruct PollLevel(int level) {
-
- Data.pollLevel = level;
-
- return new OutStruct().Map(this);
-
- }
-
- }
-
- //arrays
- public class ArrayStp : RBuildBase.SAddress {
-
- internal ArrayStp(StepData data) {
-
- Data = data;
-
- this.Map(AsType(typeof(T)));
-
- }
-
- internal ArrayStp(StepData data, bool arrayed) {
-
- Data = data;
-
- }
-
- public TypedRegisterArray Indices(params int[] indices) {
-
- if (typeof(T).GetElementType() == typeof(string) && Data.byteSizeHint == null) {
-
- throw new NotSupportedException($"For string arrays use {nameof(ArrayStp.StrHint)} before setting the indices");
-
- }
-
- Data.arrayIndicies = indices;
-
- return new TypedRegisterArray().Map(this);
-
- }
-
- public TypedRegisterStringArray StrHint(int hint) {
-
- Data.byteSizeHint = (uint)hint;
- return new TypedRegisterStringArray().Map(this);
-
- }
-
- }
-
- //strings
- public class StringStp : RBuildBase.SAddress where T : class {
-
- internal StringStp(StepData data) {
-
- this.Data = data;
-
- this.Map(AsType(typeof(T)));
-
- }
-
- internal StringStp(StepData data, bool arrayed) {
-
- this.Data = data;
-
- }
-
- ///
- /// Outputs the generated
- ///
- public void Out(Action> registerOut) {
-
- Data.registerOut = new Action(o => registerOut((IStringRegister)o));
-
- }
-
- }
-
- #endregion
-
- #region Typing size hint
-
- public new class TypedRegister : RBuildBase.TypedRegister {
-
- public new OptionsRegister SizeHint(int hint) => new OptionsRegister().Map(base.SizeHint(hint));
-
- ///
- public new OutRegister PollLevel(int level) => new OutRegister().Map(base.PollLevel(level));
-
- ///
- /// Outputs the generated
- ///
- public void Out(Action registerOut) {
-
- Data.registerOut = new Action(o => registerOut((IRegister)o));
-
- }
-
- }
-
- public class TypedRegisterString : RBuildBase.TypedRegister where T : class {
-
- public new OptionsRegister SizeHint(int hint) => new OptionsRegister().Map(base.SizeHint(hint));
-
- ///
- public new OutRegister PollLevel(int level) => new OutRegister().Map(base.PollLevel(level));
-
- ///
- /// Outputs the generated
- ///
- public void Out(Action> registerOut) {
-
- Data.registerOut = new Action(o => registerOut((IStringRegister)o));
-
- }
-
- }
-
- public class TypedRegisterArray : RBuildBase.TypedRegister {
-
- public new OutArray PollLevel(int level) => new OutArray().Map(base.PollLevel(level));
-
- public void Out(Action> registerOut) {
-
- Data.registerOut = new Action(o => registerOut((IArrayRegister)o));
-
- }
-
- }
-
- public class TypedRegisterStringArray : RBuildBase.TypedRegister {
-
- public OptionsRegisterArray Indices(params int[] indices) {
-
- Data.arrayIndicies = indices;
- return new OptionsRegisterArray().Map(this);
-
- }
-
- public new OutArray PollLevel(int level) => new OutArray().Map(base.PollLevel(level));
-
- }
-
- #endregion
-
- #region Options stage
-
- public new class OptionsRegister : RBuildBase.OptionsRegister {
-
- ///
- public new OutRegister PollLevel(int level) => new OutRegister().Map(base.PollLevel(level));
-
- ///
- /// Outputs the generated
- ///
- public void Out(Action registerOut) {
-
- Data.registerOut = new Action(o => registerOut((IRegister)o));
-
- }
-
- }
-
- public class OptionsRegisterArray : RBuildBase.OptionsRegister {
-
- ///
- public new OutArray PollLevel(int level) => new OutArray().Map(base.PollLevel(level));
-
- public void Out(Action> registerOut) {
-
- Data.registerOut = new Action(o => registerOut((IArrayRegister)o));
-
- }
-
- }
-
- #endregion
-
- public class OutRegister : SBase {
-
- public void Out(Action registerOut) {
-
- Data.registerOut = new Action(o => registerOut((IRegister)o));
-
- }
-
- }
-
- public class OutStruct : SBase where T : struct {
-
- public void Out(Action> registerOut) {
-
- Data.registerOut = new Action(o => registerOut((IRegister)o));
-
- }
-
- }
-
- public class OutArray : SBase {
-
- public void Out(Action> registerOut) {
-
- Data.registerOut = new Action(o => registerOut((IArrayRegister)o));
-
- }
-
- }
-
-
- }
-
-}
diff --git a/MewtocolNet/RegisterBuilding/RegBuilderExtensions.cs b/MewtocolNet/RegisterBuilding/RegBuilderExtensions.cs
index 3812246..d1486f3 100644
--- a/MewtocolNet/RegisterBuilding/RegBuilderExtensions.cs
+++ b/MewtocolNet/RegisterBuilding/RegBuilderExtensions.cs
@@ -1,4 +1,5 @@
-using MewtocolNet.Registers;
+using MewtocolNet.RegisterBuilding.BuilderPatterns;
+using MewtocolNet.Registers;
using System;
using System.Linq;
using System.Threading.Tasks;
@@ -12,22 +13,22 @@ namespace MewtocolNet.RegisterBuilding {
/// This waits for the memory manager to size all dynamic registers correctly
///
/// The generated
- public static IRegister AddRegister(this IPlc plc, Action builder) {
+ //public static IRegister AddRegister(this IPlc plc, Action builder) {
- var assembler = new RegisterAssembler((MewtocolInterface)plc);
- var regBuilder = new RBuildSingle((MewtocolInterface)plc);
+ // var assembler = new RegisterAssembler((MewtocolInterface)plc);
+ // var regBuilder = new RBuildSingle((MewtocolInterface)plc);
- builder.Invoke(regBuilder);
+ // builder.Invoke(regBuilder);
- var registers = assembler.AssembleAll(regBuilder);
+ // var registers = assembler.AssembleAll(regBuilder);
- var interf = (MewtocolInterface)plc;
+ // var interf = (MewtocolInterface)plc;
- interf.AddRegisters(registers.ToArray());
+ // interf.AddRegisters(registers.ToArray());
- return registers.First();
+ // return registers.First();
- }
+ //}
///
/// Adds multiple registers to the plc stack at once
@@ -38,22 +39,22 @@ namespace MewtocolNet.RegisterBuilding {
/// use
/// for this case
///
- public static IPlc AddRegisters (this IPlc plc, Action builder) {
+ //public static IPlc AddRegisters (this IPlc plc, Action builder) {
- var assembler = new RegisterAssembler((MewtocolInterface)plc);
- var regBuilder = new RBuildMult((MewtocolInterface)plc);
+ // var assembler = new RegisterAssembler((MewtocolInterface)plc);
+ // var regBuilder = new RBuild((MewtocolInterface)plc);
- builder.Invoke(regBuilder);
+ // builder.Invoke(regBuilder);
- var registers = assembler.AssembleAll(regBuilder);
+ // var registers = assembler.AssembleAll(regBuilder);
- var interf = (MewtocolInterface)plc;
+ // var interf = (MewtocolInterface)plc;
- interf.AddRegisters(registers.ToArray());
+ // interf.AddRegisters(registers.ToArray());
- return plc;
+ // return plc;
- }
+ //}
}
diff --git a/MewtocolNet/RegisterBuilding/RegisterAssembler.cs b/MewtocolNet/RegisterBuilding/RegisterAssembler.cs
index c70dbe3..7cdbe77 100644
--- a/MewtocolNet/RegisterBuilding/RegisterAssembler.cs
+++ b/MewtocolNet/RegisterBuilding/RegisterAssembler.cs
@@ -1,4 +1,5 @@
using MewtocolNet.RegisterAttributes;
+using MewtocolNet.RegisterBuilding.BuilderPatterns;
using MewtocolNet.Registers;
using System;
using System.Collections.Generic;
@@ -13,33 +14,15 @@ namespace MewtocolNet.RegisterBuilding {
internal MewtocolInterface onInterface;
+ internal List assembled = new List();
+
internal RegisterAssembler(MewtocolInterface interf) {
onInterface = interf;
}
- internal List AssembleAll(RBuildBase rBuildData, bool flagAutoGenerated = false) {
-
- List generatedInstances = new List();
-
- foreach (var data in rBuildData.unfinishedList) {
-
- var generatedInstance = Assemble(data);
-
- generatedInstance.autoGenerated = flagAutoGenerated;
-
- data?.InvokeBuilt(generatedInstance);
-
- generatedInstances.Add(generatedInstance);
-
- }
-
- return generatedInstances;
-
- }
-
- internal Register Assemble(BaseStepData data) {
+ internal Register Assemble(StepData data) {
Register generatedInstance = null;
@@ -69,7 +52,7 @@ namespace MewtocolNet.RegisterBuilding {
var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
- Type paramedClass = typeof(ArrayRegister<>).MakeGenericType(data.dotnetVarType);
+ Type paramedClass = typeof(ArrayRegister<>).MakeGenericType(data.dotnetVarType.GetElementType());
ConstructorInfo constr = paramedClass.GetConstructor(flags, null, new Type[] {
typeof(uint),
typeof(uint),
@@ -86,10 +69,10 @@ namespace MewtocolNet.RegisterBuilding {
generatedInstance = instance;
- } else if (!data.regType.IsBoolean() && data.dotnetVarType.IsAllowedPlcCastingType()) {
+ } else if (!data.regType.IsBoolean() && data.dotnetVarType.IsAllowedPlcCastingType() && data.dotnetVarType != typeof(string)) {
//-------------------------------------------
- //as single register
+ //as struct register
uint numericSize = (uint)data.dotnetVarType.DetermineTypeByteIntialSize();
@@ -100,18 +83,6 @@ namespace MewtocolNet.RegisterBuilding {
throw new NotSupportedException($"Enums not based on 16 or 32 bit numbers are not supported");
}
}
-
- if(data.dotnetVarType == typeof(string)) {
-
- if(data.byteSizeHint == null)
- throw new NotSupportedException($"Can't create a STRING register without a string size hint");
-
- if(data.byteSizeHint < 0)
- throw new NotSupportedException($"Can't create a STRING register with a string size hint < 0");
-
- numericSize = 4 + data.byteSizeHint.Value;
-
- }
var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
@@ -130,6 +101,29 @@ namespace MewtocolNet.RegisterBuilding {
generatedInstance = instance;
+ } else if (!data.regType.IsBoolean() && data.dotnetVarType.IsAllowedPlcCastingType()) {
+
+ //-------------------------------------------
+ //as string register
+
+ uint numericSize = 0;
+
+ if (data.dotnetVarType == typeof(string)) {
+
+ if (data.byteSizeHint == null)
+ throw new NotSupportedException($"Can't create a STRING register without a string size hint");
+
+ if (data.byteSizeHint < 0)
+ throw new NotSupportedException($"Can't create a STRING register with a string size hint < 0");
+
+ numericSize = data.byteSizeHint.Value;
+
+ }
+
+ var instance = (Register)new StringRegister(data.memAddress, numericSize, data.name);
+
+ generatedInstance = instance;
+
} else if (data.regType.IsBoolean()) {
//-------------------------------------------
@@ -162,6 +156,10 @@ namespace MewtocolNet.RegisterBuilding {
generatedInstance.underlyingSystemType = data.dotnetVarType;
generatedInstance.pollLevel = data.pollLevel;
+ if (data.regCollection != null)
+ generatedInstance.autoGenerated = true;
+
+ assembled.Add(generatedInstance);
return generatedInstance;
}
diff --git a/MewtocolNet/RegisterBuilding/StepBaseTyper.cs b/MewtocolNet/RegisterBuilding/StepBaseTyper.cs
new file mode 100644
index 0000000..6585c58
--- /dev/null
+++ b/MewtocolNet/RegisterBuilding/StepBaseTyper.cs
@@ -0,0 +1,234 @@
+using MewtocolNet.PublicEnums;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text.RegularExpressions;
+
+namespace MewtocolNet.RegisterBuilding {
+
+ internal static class StepBaseTyper {
+
+ ///
+ /// Sets the register as a dotnet type for direct conversion
+ ///
+ ///
+ ///
+ ///
+ internal static StepBase AsType(this StepBase b) {
+
+ if (!typeof(T).IsAllowedPlcCastingType()) {
+
+ throw new NotSupportedException($"The dotnet type {typeof(T)}, is not supported for PLC type casting");
+
+ }
+
+ b.Data.dotnetVarType = typeof(T);
+
+ return b;
+
+ }
+
+ ///
+ /// Sets the register as a dotnet type for direct conversion
+ ///
+ ///
+ ///
+ ///
+ ///
+ internal static StepBase AsType(this StepBase b, Type type) {
+
+ //for internal only, relay to AsType from string
+ if (b.Data.buildSource == RegisterBuildSource.Attribute) {
+
+ if ((type.IsArray || type == typeof(string)) && b.Data.typeDef != null) {
+
+ return b.AsType(b.Data.typeDef);
+
+ } else if (type.IsArray && b.Data.typeDef == null) {
+
+ throw new NotSupportedException("Typedef parameter is needed for array types");
+
+ } else if (b.Data.typeDef != null) {
+
+ throw new NotSupportedException("Can't use the typedef parameter on non array or string types");
+
+ }
+
+ }
+
+ if (!type.IsAllowedPlcCastingType()) {
+
+ throw new NotSupportedException($"The dotnet type {type}, is not supported for PLC type casting");
+
+ }
+
+ b.Data.dotnetVarType = type;
+
+ return b;
+
+ }
+
+ ///
+ /// Sets the register type as a predefined
+ ///
+ internal static StepBase AsType(this StepBase b, PlcVarType type) {
+
+ b.Data.dotnetVarType = type.GetDefaultDotnetType();
+
+ return b;
+
+ }
+
+ ///
+ /// Sets the register type from the plc type string
+ /// Supported types:
+ ///
+ /// BOOL Boolean R/X/Y registers
+ /// INT 16 bit signed integer
+ /// UINT 16 bit un-signed integer
+ /// DINT 32 bit signed integer
+ /// UDINT 32 bit un-signed integer
+ /// REAL 32 bit floating point
+ /// TIME 32 bit time interpreted as
+ /// STRING String of chars, the interface will automatically get the length
+ /// STRING[N] String of chars, pre capped to N
+ /// WORD 16 bit word interpreted as
+ /// DWORD 32 bit double word interpreted as
+ ///
+ ///
+ internal static StepBase AsType(this StepBase b, string type) {
+
+ var regexString = new Regex(@"^STRING *\[(?[0-9]*)\]$", RegexOptions.IgnoreCase);
+ var regexArray = new Regex(@"^ARRAY *\[(?[0-9]*)..(?[0-9]*)(?:\,(?[0-9]*)..(?[0-9]*))?(?:\,(?[0-9]*)..(?[0-9]*))?\] *OF {1,}(?.*)$", RegexOptions.IgnoreCase);
+
+ var stringMatch = regexString.Match(type);
+ var arrayMatch = regexArray.Match(type);
+
+ if (Enum.TryParse(type, out var parsed)) {
+
+ b.Data.dotnetVarType = parsed.GetDefaultDotnetType();
+
+ } else if (stringMatch.Success) {
+
+ b.Data.dotnetVarType = typeof(string);
+ b.Data.byteSizeHint = uint.Parse(stringMatch.Groups["len"].Value);
+
+ } else if (arrayMatch.Success) {
+
+ //invoke generic AsTypeArray
+
+ string arrTypeString = arrayMatch.Groups["t"].Value;
+ Type dotnetArrType = null;
+
+ var stringMatchInArray = regexString.Match(arrTypeString);
+
+ if (Enum.TryParse(arrTypeString, out var parsedArrType) && parsedArrType != PlcVarType.STRING) {
+
+ dotnetArrType = parsedArrType.GetDefaultDotnetType();
+
+
+ } else if (stringMatchInArray.Success) {
+
+ dotnetArrType = typeof(string);
+ //Data.byteSizeHint = uint.Parse(stringMatch.Groups["len"].Value);
+
+ } else {
+
+ throw new NotSupportedException($"The FP type '{arrTypeString}' was not recognized");
+
+ }
+
+ var indices = new List();
+
+ for (int i = 1; i < 4; i++) {
+
+ var arrStart = arrayMatch.Groups[$"S{i}"]?.Value;
+ var arrEnd = arrayMatch.Groups[$"E{i}"]?.Value;
+ if (string.IsNullOrEmpty(arrStart) || string.IsNullOrEmpty(arrEnd)) break;
+
+ var arrStartInt = int.Parse(arrStart);
+ var arrEndInt = int.Parse(arrEnd);
+
+ indices.Add(arrEndInt - arrStartInt + 1);
+
+ }
+
+ var arr = Array.CreateInstance(dotnetArrType, indices.ToArray());
+ var arrType = arr.GetType();
+
+ MethodInfo method = typeof(StepBaseTyper).GetMethod(nameof(AsTypeArray));
+ MethodInfo generic = method.MakeGenericMethod(arrType);
+
+ var tmp = (StepBase)generic.Invoke(null, new object[] {
+ b,
+ indices.ToArray()
+ });
+
+ return tmp;
+
+ } else {
+
+ throw new NotSupportedException($"The FP type '{type}' was not recognized");
+
+ }
+
+ return b;
+
+ }
+
+ ///
+ /// Sets the register as a (multidimensional) array targeting a PLC array
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// Indicies for multi dimensional arrays, for normal arrays just one INT
+ ///
+ ///
+ /// One dimensional arrays:
+ /// ARRAY [0..2] OF INT = AsTypeArray<short[]>(3)
+ /// ARRAY [5..6] OF DWORD = AsTypeArray<DWord[]>(2)
+ ///
+ /// Multi dimensional arrays:
+ /// ARRAY [0..2, 0..3, 0..4] OF INT = AsTypeArray<short[,,]>(3,4,5)
+ /// ARRAY [5..6, 0..2] OF DWORD = AsTypeArray<DWord[,]>(2, 3)
+ ///
+ internal static StepBase AsTypeArray(this StepBase b, params int[] indicies) {
+
+ if (!typeof(T).IsArray)
+ throw new NotSupportedException($"The type {typeof(T)} was no array");
+
+ var arrRank = typeof(T).GetArrayRank();
+ var elBaseType = typeof(T).GetElementType();
+
+ if (arrRank > 3)
+ throw new NotSupportedException($"4+ dimensional arrays are not supported");
+
+ if (typeof(T) != typeof(byte[]) && !elBaseType.IsAllowedPlcCastingType())
+ throw new NotSupportedException($"The dotnet type {typeof(T)}, is not supported for PLC array type casting");
+
+ if (arrRank != indicies.Length)
+ throw new NotSupportedException($"All dimensional array indicies must be set");
+
+ b.Data.dotnetVarType = typeof(T);
+
+ int byteSizePerItem = elBaseType.DetermineTypeByteIntialSize();
+ int calcedTotalByteSize = indicies.Aggregate((a, x) => a * x) * byteSizePerItem;
+
+ b.Data.byteSizeHint = (uint)calcedTotalByteSize;
+ b.Data.arrayIndicies = indicies;
+
+ if (b.Data.byteSizeHint % byteSizePerItem != 0) {
+ throw new NotSupportedException($"The array element type {elBaseType} doesn't fit into the adress range");
+ }
+
+ return b;
+
+ }
+
+ }
+
+
+}
diff --git a/MewtocolNet/RegisterBuilding/StepData.cs b/MewtocolNet/RegisterBuilding/StepData.cs
index edb2d80..9c258e1 100644
--- a/MewtocolNet/RegisterBuilding/StepData.cs
+++ b/MewtocolNet/RegisterBuilding/StepData.cs
@@ -6,9 +6,15 @@ using System.Reflection;
namespace MewtocolNet.RegisterBuilding {
- internal class BaseStepData {
+ public class StepBase {
- internal Action registerOut;
+ internal StepData Data;
+
+ }
+
+ public class StepBase : StepBase { }
+
+ internal class StepData {
internal RegisterBuildSource buildSource = RegisterBuildSource.Anonymous;
@@ -33,40 +39,6 @@ namespace MewtocolNet.RegisterBuilding {
internal string typeDef;
- internal void InvokeBuilt(Register reg) {
-
- registerOut?.Invoke(reg);
-
- //var selftype = this.GetType();
-
- //if ((selftype.IsGenericType && selftype.GetGenericTypeDefinition() == typeof(StepData<>))) {
-
- // var field = selftype.GetField("registerOut", BindingFlags.NonPublic | BindingFlags.Instance);
-
- // var generic = typeof(IRegister<>).MakeGenericType()
-
- // var action = Action.CreateDelegate(typeof(IRegister));
-
- // field.SetValue(this,);
-
- //}
-
- }
-
- }
-
- internal class StepData : BaseStepData {
-
- //for referencing the output at builder level
- //internal Action> registerOut;
-
- }
-
- internal class StepData : BaseStepData {
-
- //for referencing the output at builder level
- //internal Action registerOut;
-
}
}
diff --git a/MewtocolNet/Registers/Base/IStringRegister.cs b/MewtocolNet/Registers/Base/IStringRegister.cs
deleted file mode 100644
index 48da7a9..0000000
--- a/MewtocolNet/Registers/Base/IStringRegister.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using System.Threading.Tasks;
-
-namespace MewtocolNet.Registers {
- public interface IStringRegister : IRegister where T : class {
-
- ///
- /// The current value of the register
- ///
- T Value { get; }
-
- ///
- /// Reads the register value async from the plc
- ///
- /// The register value
- Task ReadAsync();
-
- ///
- /// Writes the register content async to the plc
- ///
- /// True if successfully set
- Task WriteAsync(T data);
-
- }
-
-}
diff --git a/MewtocolNet/Registers/Base/Register.cs b/MewtocolNet/Registers/Base/Register.cs
index 6ca98de..e5b114f 100644
--- a/MewtocolNet/Registers/Base/Register.cs
+++ b/MewtocolNet/Registers/Base/Register.cs
@@ -26,8 +26,9 @@ namespace MewtocolNet.Registers {
internal Type underlyingSystemType;
internal IMemoryArea underlyingMemory;
internal bool autoGenerated;
+ internal bool isAnonymous;
- internal object lastValue = null;
+ internal protected object lastValue = null;
internal string name;
internal uint memoryAddress;
internal int pollLevel = 0;
@@ -189,7 +190,7 @@ namespace MewtocolNet.Registers {
public override string ToString() {
var sb = new StringBuilder();
- sb.Append(GetMewName());
+ sb.Append(GetRegisterWordRangeString());
if (Name != null) sb.Append($" ({Name})");
sb.Append($" [{this.GetType().Name}({underlyingSystemType.Name})]");
if (ValueObj != null) sb.Append($" Val: {GetValueString()}");
diff --git a/MewtocolNet/Registers/ArrayRegister.cs b/MewtocolNet/Registers/Classes/ArrayRegister.cs
similarity index 50%
rename from MewtocolNet/Registers/ArrayRegister.cs
rename to MewtocolNet/Registers/Classes/ArrayRegister.cs
index 7413841..96a2afd 100644
--- a/MewtocolNet/Registers/ArrayRegister.cs
+++ b/MewtocolNet/Registers/Classes/ArrayRegister.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
+using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
@@ -10,7 +11,10 @@ namespace MewtocolNet.Registers {
///
/// Defines a register containing a string
///
- public class ArrayRegister : Register, IArrayRegister {
+ public class ArrayRegister : Register, IArrayRegister, IArrayRegister2D, IArrayRegister3D {
+
+ internal int byteSizePerItem;
+ internal uint reservedByteSize;
internal int[] indices;
@@ -21,87 +25,124 @@ namespace MewtocolNet.Registers {
///
public uint AddressLength => addressLength;
- public T[] Value => (T[])ValueObj;
+ T[] IArrayRegister.Value => (T[])ValueObj;
+
+ T[,] IArrayRegister2D.Value => (T[,])ValueObj;
+
+ T[,,] IArrayRegister3D.Value => (T[,,])ValueObj;
+
+ public int Count => ((Array)ValueObj).Length;
+
+ public T this[int i1, int i2, int i3] => (T)((Array)ValueObj).GetValue(i1, i2, i3);
+
+ public T this[int i1, int i2] => (T)((Array)ValueObj).GetValue(i1, i2);
+
+ public T this[int i] => (T)((Array)ValueObj).GetValue(i);
[Obsolete("Creating registers directly is not supported use IPlc.Register instead")]
public ArrayRegister() =>
throw new NotSupportedException("Direct register instancing is not supported, use the builder pattern");
- internal ArrayRegister(uint _address, uint _reservedByteSize, int[] _indicies, string _name = null) {
+ internal ArrayRegister(uint _address, uint _reservedByteSize, int[] _indices, string _name = null) {
+
+ if (_reservedByteSize % 2 != 0)
+ throw new ArgumentException(nameof(_reservedByteSize), "Reserved byte size must be even");
name = _name;
memoryAddress = _address;
- indices = _indicies;
+ indices = _indices;
- //calc mem length
- //because one register is always 1 word (2 bytes) long, if the bytecount is uneven we get the trailing word too
- var byteSize = _reservedByteSize;
- if (byteSize % 2 != 0) byteSize++;
+ int itemCount = _indices.Aggregate((a, x) => a * x);
+ byteSizePerItem = (int)_reservedByteSize / itemCount;
+ reservedByteSize = _reservedByteSize;
RegisterType = RegisterType.DT_BYTE_RANGE;
- addressLength = Math.Max((byteSize / 2), 1);
+ addressLength = Math.Max((_reservedByteSize / 2), 1);
CheckAddressOverflow(memoryAddress, addressLength);
+ underlyingSystemType = typeof(T).MakeArrayType(indices.Length);
+
lastValue = null;
-
}
- public override string GetValueString() {
+ public IEnumerator GetEnumerator() => ((Array)ValueObj).OfType().GetEnumerator();
- if (ValueObj == null) return "null";
+ IEnumerator IEnumerable.GetEnumerator() => ((Array)ValueObj).OfType().GetEnumerator();
- if(typeof(T) == typeof(byte[])) {
+ async Task IArrayRegister.ReadAsync() => (T[])(object)await ReadAsync();
- return ((byte[])ValueObj).ToHexString("-");
+ async Task IArrayRegister2D.ReadAsync() => (T[,])(object)await ReadAsync();
- }
+ async Task IArrayRegister3D.ReadAsync() => (T[,,])(object)await ReadAsync();
- StringBuilder sb = new StringBuilder();
- var valueIenum = (IEnumerable)ValueObj;
+ async Task IArrayRegister.WriteAsync(T[] data) => await WriteAsync(data);
- foreach (var el in valueIenum) {
+ async Task IArrayRegister2D.WriteAsync(T[,] data) => await WriteAsync(data);
- sb.Append($"{el}, ");
-
- }
-
- return ArrayToString((Array)ValueObj);
-
- }
+ async Task IArrayRegister3D.WriteAsync(T[,,] data) => await WriteAsync(data);
///
- public override string GetRegisterString() => "DT";
-
- ///
- public override uint GetRegisterAddressLen() => AddressLength;
-
- ///
- public async Task WriteAsync(T value) {
+ private async Task WriteAsync(object value) {
var encoded = PlcValueParser.EncodeArray(this, value);
+
var res = await attachedInterface.WriteByteRange((int)MemoryAddress, encoded);
if (res) {
- //find the underlying memory
- var matchingReg = attachedInterface.memoryManager.GetAllRegisters()
- .FirstOrDefault(x => x.IsSameAddressAndType(this));
+ if(isAnonymous) {
- if (matchingReg != null)
- matchingReg.underlyingMemory.SetUnderlyingBytes(matchingReg, encoded);
+ //find the underlying memory
+ var matchingReg = attachedInterface.memoryManager.GetAllRegisters()
+ .FirstOrDefault(x => x.IsSameAddressAndType(this));
+
+ if (matchingReg != null) matchingReg.underlyingMemory.SetUnderlyingBytes(matchingReg, encoded);
+
+ } else {
+
+ underlyingMemory.SetUnderlyingBytes(this, encoded);
+
+ }
AddSuccessWrite();
UpdateHoldingValue(value);
}
- return res;
+ }
+
+ private async Task WriteEntriesAsync(int start, object value) {
+
+ var encoded = PlcValueParser.EncodeArray(this, value);
+
+ var res = await attachedInterface.WriteByteRange((int)MemoryAddress, encoded);
+
+ if (res) {
+
+ if (isAnonymous) {
+
+ //find the underlying memory
+ var matchingReg = attachedInterface.memoryManager.GetAllRegisters()
+ .FirstOrDefault(x => x.IsSameAddressAndType(this));
+
+ if (matchingReg != null) matchingReg.underlyingMemory.SetUnderlyingBytes(matchingReg, encoded);
+
+ } else {
+
+ underlyingMemory.SetUnderlyingBytes(this, encoded);
+
+ }
+
+ AddSuccessWrite();
+ UpdateHoldingValue(value);
+
+ }
}
///
- async Task IArrayRegister.ReadAsync() {
+ private async Task ReadAsync() {
var res = await attachedInterface.ReadByteRangeNonBlocking((int)MemoryAddress, (int)GetRegisterAddressLen() * 2);
if (res == null) throw new Exception();
@@ -112,15 +153,18 @@ namespace MewtocolNet.Registers {
if (matchingReg != null)
matchingReg.underlyingMemory.SetUnderlyingBytes(matchingReg, res);
- return (T[])SetValueFromBytes(res);
+ return SetValueFromBytes(res);
}
internal override object SetValueFromBytes(byte[] bytes) {
+ if (bytes.Length != reservedByteSize)
+ throw new ArgumentException(nameof(bytes), "Bytes were not equal the size of registers reserved byte size");
+
AddSuccessRead();
- var parsed = PlcValueParser.ParseArray(this, indices, bytes);
+ var parsed = PlcValueParser.ParseArray(this, bytes);
UpdateHoldingValue(parsed);
return parsed;
@@ -130,13 +174,22 @@ namespace MewtocolNet.Registers {
///
internal override void UpdateHoldingValue(object val) {
- bool changeTriggerBitArr = val is BitArray bitArr &&
- lastValue is BitArray bitArr2 &&
- (bitArr.ToBitString() != bitArr2.ToBitString());
+ if (val == null && lastValue == null) return;
- bool changeTriggerGeneral = (lastValue?.ToString() != val?.ToString());
+ bool sequenceDifference = false;
- if (changeTriggerBitArr || changeTriggerGeneral) {
+ if(val == null && lastValue != null) sequenceDifference = true;
+ if(val != null && lastValue == null) sequenceDifference = true;
+ if (val != null && lastValue != null) {
+
+ var val1 = ((Array)val).OfType();
+ var val2 = ((Array)lastValue).OfType();
+
+ sequenceDifference = !Enumerable.SequenceEqual(val1, val2);
+
+ }
+
+ if (sequenceDifference) {
var beforeVal = lastValue;
var beforeValStr = GetValueString();
@@ -150,6 +203,25 @@ namespace MewtocolNet.Registers {
}
+ public override string GetValueString() {
+
+ if (ValueObj == null) return "null";
+
+ if (underlyingSystemType == typeof(byte[])) {
+
+ return ((byte[])ValueObj).ToHexString("-");
+
+ }
+
+ return ArrayToString((Array)ValueObj);
+
+ }
+
+ ///
+ public override string GetRegisterString() => "DT";
+
+ ///
+ public override uint GetRegisterAddressLen() => AddressLength;
private string ArrayToString(Array array) {
@@ -195,6 +267,7 @@ namespace MewtocolNet.Registers {
}
+
}
}
diff --git a/MewtocolNet/Registers/BoolRegister.cs b/MewtocolNet/Registers/Classes/BoolRegister.cs
similarity index 100%
rename from MewtocolNet/Registers/BoolRegister.cs
rename to MewtocolNet/Registers/Classes/BoolRegister.cs
diff --git a/MewtocolNet/Registers/Classes/StringRegister.cs b/MewtocolNet/Registers/Classes/StringRegister.cs
new file mode 100644
index 0000000..586f66b
--- /dev/null
+++ b/MewtocolNet/Registers/Classes/StringRegister.cs
@@ -0,0 +1,158 @@
+using MewtocolNet.Logging;
+using System;
+using System.Collections;
+using System.Drawing;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MewtocolNet.Registers {
+
+ ///
+ /// Defines a register containing a number
+ ///
+ public class StringRegister : Register, IStringRegister {
+
+ internal int reservedStringLength;
+ internal uint byteLength;
+
+ internal uint addressLength;
+
+ ///
+ /// The rgisters memory length
+ ///
+ public uint AddressLength => addressLength;
+
+ public string Value => (string)ValueObj;
+
+ [Obsolete("Creating registers directly is not supported use IPlc.Register instead")]
+ public StringRegister() =>
+ throw new NotSupportedException("Direct register instancing is not supported, use the builder pattern");
+
+ internal StringRegister(uint _address, uint _reservedByteSize, string _name = null) {
+
+ memoryAddress = _address;
+ name = _name;
+
+ reservedStringLength = (int)_reservedByteSize;
+ Resize(_reservedByteSize);
+
+ RegisterType = RegisterType.DT_BYTE_RANGE;
+
+ CheckAddressOverflow(memoryAddress, addressLength);
+
+ lastValue = null;
+
+ }
+
+ private void Resize (uint reservedByteSize) {
+
+ if (reservedByteSize % 2 != 0) reservedByteSize++;
+ reservedByteSize += 4;
+
+ addressLength = reservedByteSize / 2;
+ byteLength = reservedByteSize;
+
+ }
+
+ ///
+ public override string GetAsPLC() => Value;
+
+ ///
+ public override string GetValueString() => ValueObj?.ToString() ?? "null";
+
+ ///
+ public override uint GetRegisterAddressLen() => AddressLength;
+
+ ///
+ public async Task WriteAsync(string value) {
+
+ //trim the size if the input was larger
+ if(value.Length > reservedStringLength) {
+ value = value.Substring(0, reservedStringLength);
+ }
+
+ var encoded = PlcValueParser.Encode(this, value);
+ var res = await attachedInterface.WriteByteRange((int)MemoryAddress, encoded);
+
+ if (res) {
+
+ //find the underlying memory
+ var matchingReg = attachedInterface.memoryManager.GetAllRegisters()
+ .FirstOrDefault(x => x.IsSameAddressAndType(this));
+
+ if (matchingReg != null)
+ matchingReg.underlyingMemory.SetUnderlyingBytes(matchingReg, encoded);
+
+ AddSuccessWrite();
+ UpdateHoldingValue(value);
+
+ }
+
+ }
+
+ ///
+ public async Task ReadAsync() {
+
+ var res = await attachedInterface.ReadByteRangeNonBlocking((int)MemoryAddress, (int)GetRegisterAddressLen() * 2);
+ if (res == null) return null;
+
+ var matchingReg = attachedInterface.memoryManager.GetAllRegisters()
+ .FirstOrDefault(x => x.IsSameAddressAndType(this));
+
+ if (matchingReg != null) {
+
+ if (matchingReg is StringRegister sreg && this is StringRegister selfSreg) {
+
+ sreg.addressLength = selfSreg.addressLength;
+
+ }
+
+ matchingReg.underlyingMemory.SetUnderlyingBytes(matchingReg, res);
+
+ }
+
+ return (string)SetValueFromBytes(res);
+
+ }
+
+ internal override object SetValueFromBytes (byte[] bytes) {
+
+ //if string correct the sizing of the byte hint was wrong
+ var reservedSize = BitConverter.ToInt16(bytes, 0);
+ if (reservedSize != byteLength - 4)
+ throw new NotSupportedException(
+ $"The STRING register at {GetMewName()} is not correctly sized, " +
+ $"the size should be STRING[{reservedSize}] instead of STRING[{byteLength - 4}]"
+ );
+
+ AddSuccessRead();
+
+ var parsed = PlcValueParser.Parse(this, bytes);
+
+ UpdateHoldingValue(parsed);
+ return parsed;
+
+ }
+
+ internal override void UpdateHoldingValue(object val) {
+
+ if (lastValue?.ToString() != val?.ToString()) {
+
+ var beforeVal = lastValue;
+ var beforeValStr = GetValueString();
+
+ lastValue = val;
+
+ TriggerNotifyChange();
+ attachedInterface.InvokeRegisterChanged(this, beforeVal, beforeValStr);
+
+ }
+
+ }
+
+ }
+
+}
diff --git a/MewtocolNet/Registers/StructRegister.cs b/MewtocolNet/Registers/Classes/StructRegister.cs
similarity index 77%
rename from MewtocolNet/Registers/StructRegister.cs
rename to MewtocolNet/Registers/Classes/StructRegister.cs
index a4296f9..328662f 100644
--- a/MewtocolNet/Registers/StructRegister.cs
+++ b/MewtocolNet/Registers/Classes/StructRegister.cs
@@ -16,8 +16,6 @@ namespace MewtocolNet.Registers {
/// The type of the numeric value
public class StructRegister : Register, IRegister where T : struct {
- internal uint byteLength;
-
internal uint addressLength;
///
@@ -35,26 +33,20 @@ namespace MewtocolNet.Registers {
memoryAddress = _address;
name = _name;
- Resize(_reservedByteSize);
+
+ addressLength = _reservedByteSize / 2;
+ if (_reservedByteSize % 2 != 0) addressLength++;
if (_reservedByteSize == 2) RegisterType = RegisterType.DT;
if(_reservedByteSize == 4) RegisterType = RegisterType.DDT;
- if (typeof(T) == typeof(string)) RegisterType = RegisterType.DT_BYTE_RANGE;
CheckAddressOverflow(memoryAddress, addressLength);
+ underlyingSystemType = typeof(T);
lastValue = null;
}
- private void Resize (uint reservedByteSize) {
-
- addressLength = reservedByteSize / 2;
- if (reservedByteSize % 2 != 0) addressLength++;
- byteLength = reservedByteSize;
-
- }
-
///
public override string GetAsPLC() {
@@ -92,7 +84,7 @@ namespace MewtocolNet.Registers {
public override uint GetRegisterAddressLen() => AddressLength;
///
- public async Task WriteAsync(T value) {
+ public async Task WriteAsync(T value) {
var encoded = PlcValueParser.Encode(this, (T)value);
var res = await attachedInterface.WriteByteRange((int)MemoryAddress, encoded);
@@ -111,27 +103,19 @@ namespace MewtocolNet.Registers {
}
- return res;
-
}
///
- public async Task ReadAsync() {
+ public async Task ReadAsync() {
var res = await attachedInterface.ReadByteRangeNonBlocking((int)MemoryAddress, (int)GetRegisterAddressLen() * 2);
- if (res == null) return (T?)(object)null;
+ if (res == null) throw new Exception($"Failed to read the register {this}");
var matchingReg = attachedInterface.memoryManager.GetAllRegisters()
.FirstOrDefault(x => x.IsSameAddressAndType(this));
if (matchingReg != null) {
- //if (matchingReg is StructRegister sreg && this.GetType() == typeof(StructRegister)) {
-
- // sreg.addressLength = selfSreg.addressLength;
-
- //}
-
matchingReg.underlyingMemory.SetUnderlyingBytes(matchingReg, res);
}
@@ -142,16 +126,6 @@ namespace MewtocolNet.Registers {
internal override object SetValueFromBytes (byte[] bytes) {
- //if string correct the sizing of the byte hint was wrong
- if (typeof(T) == typeof(string)) {
- var reservedSize = BitConverter.ToInt16(bytes, 0);
- if (reservedSize != byteLength - 4)
- throw new NotSupportedException(
- $"The STRING register at {GetMewName()} is not correctly sized, " +
- $"the size should be STRING[{reservedSize}] instead of STRING[{byteLength - 4}]"
- );
- }
-
AddSuccessRead();
var parsed = PlcValueParser.Parse(this, bytes);
diff --git a/MewtocolNet/Registers/IArrayRegister.cs b/MewtocolNet/Registers/IArrayRegister.cs
new file mode 100644
index 0000000..64e283c
--- /dev/null
+++ b/MewtocolNet/Registers/IArrayRegister.cs
@@ -0,0 +1,43 @@
+using System.Collections;
+using System.Collections.ObjectModel;
+using System.Collections.Generic;
+using System.Collections.Concurrent;
+using System.Threading.Tasks;
+
+namespace MewtocolNet.Registers {
+
+ ///
+ /// Provides an abstruct enumerable interface for one dimensional array registers
+ ///
+ public interface IArrayRegister : IReadOnlyList, IRegister {
+
+ ///
+ /// Reads the register value async from the plc
+ ///
+ /// The register value
+ Task ReadAsync();
+
+ ///
+ /// Writes a whole array to the plc
+ ///
+ /// True if successfully set
+ Task WriteAsync(T[] data);
+
+ /////
+ ///// Writes a single item to the array, this saves bandwidth
+ /////
+ ///// Index of the element to write
+ ///// The value to overwrite
+ ///// True if successfully set
+ //Task WriteEntryAsync(int i, T data);
+
+ T[] Value { get; }
+
+ ///
+ /// The current value of the register at the position
+ ///
+ new T this[int i] { get; }
+
+ }
+
+}
diff --git a/MewtocolNet/Registers/IArrayRegister2D.cs b/MewtocolNet/Registers/IArrayRegister2D.cs
new file mode 100644
index 0000000..9e7f4c7
--- /dev/null
+++ b/MewtocolNet/Registers/IArrayRegister2D.cs
@@ -0,0 +1,32 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace MewtocolNet.Registers {
+
+ ///
+ /// Provides an abstruct enumerable interface for two dimensional array registers
+ ///
+ public interface IArrayRegister2D : IReadOnlyList, IRegister {
+
+ ///
+ /// Reads the register value async from the plc
+ ///
+ /// The register value
+ Task ReadAsync();
+
+ ///
+ /// Writes a whole array to the plc
+ ///
+ /// True if successfully set
+ Task WriteAsync(T[,] data);
+
+ T[,] Value { get; }
+
+ ///
+ /// The current value of the register at the position
+ ///
+ T this[int i1, int i2] { get; }
+
+ }
+
+}
diff --git a/MewtocolNet/Registers/IArrayRegister3D.cs b/MewtocolNet/Registers/IArrayRegister3D.cs
new file mode 100644
index 0000000..52cd431
--- /dev/null
+++ b/MewtocolNet/Registers/IArrayRegister3D.cs
@@ -0,0 +1,32 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace MewtocolNet.Registers {
+
+ ///
+ /// Provides an abstruct enumerable interface for three dimensional array registers
+ ///
+ public interface IArrayRegister3D : IReadOnlyList, IRegister {
+
+ ///
+ /// Reads the register value async from the plc
+ ///
+ /// The register value
+ Task ReadAsync();
+
+ ///
+ /// Writes a whole array to the plc
+ ///
+ /// True if successfully set
+ Task WriteAsync(T[,,] data);
+
+ T[,,] Value { get; }
+
+ ///
+ /// The current value of the register at the position
+ ///
+ T this[int i1, int i2, int i3] { get; }
+
+ }
+
+}
diff --git a/MewtocolNet/Registers/Base/IRegisterT.cs b/MewtocolNet/Registers/IRegister.cs
similarity index 85%
rename from MewtocolNet/Registers/Base/IRegisterT.cs
rename to MewtocolNet/Registers/IRegister.cs
index 6ffa350..5a4b85d 100644
--- a/MewtocolNet/Registers/Base/IRegisterT.cs
+++ b/MewtocolNet/Registers/IRegister.cs
@@ -5,7 +5,7 @@ using MewtocolNet.Events;
namespace MewtocolNet.Registers {
///
- /// An interface for all register types
+ /// An interface for all struct register types
///
public interface IRegister : IRegister where T : struct {
@@ -18,13 +18,13 @@ namespace MewtocolNet.Registers {
/// Reads the register value async from the plc
///
/// The register value
- Task ReadAsync();
+ Task ReadAsync();
///
/// Writes the register content async to the plc
///
/// True if successfully set
- Task WriteAsync(T data);
+ Task WriteAsync(T data);
}
diff --git a/MewtocolNet/Registers/Base/IArrayRegister.cs b/MewtocolNet/Registers/IStringRegister.cs
similarity index 75%
rename from MewtocolNet/Registers/Base/IArrayRegister.cs
rename to MewtocolNet/Registers/IStringRegister.cs
index 7cdb364..999df0c 100644
--- a/MewtocolNet/Registers/Base/IArrayRegister.cs
+++ b/MewtocolNet/Registers/IStringRegister.cs
@@ -2,24 +2,24 @@
namespace MewtocolNet.Registers {
- public interface IArrayRegister : IRegister {
+ public interface IStringRegister : IRegister {
///
/// The current value of the register
///
- T[] Value { get; }
+ string Value { get; }
///
/// Reads the register value async from the plc
///
/// The register value
- Task ReadAsync();
+ Task ReadAsync();
///
/// Writes the register content async to the plc
///
/// True if successfully set
- Task WriteAsync(T data);
+ Task WriteAsync(string data);
}
diff --git a/MewtocolNet/SetupClasses/PollLevelConfig.cs b/MewtocolNet/SetupClasses/PollLevelConfig.cs
index 9e44f10..1d15cb0 100644
--- a/MewtocolNet/SetupClasses/PollLevelConfig.cs
+++ b/MewtocolNet/SetupClasses/PollLevelConfig.cs
@@ -5,6 +5,10 @@ namespace MewtocolNet.SetupClasses {
internal class PollLevelConfig {
+ internal bool skipsAll;
+
+ internal bool skipAllButFirst;
+
internal TimeSpan? delay;
internal int? skipNth;
diff --git a/MewtocolNet/TypeConversion/Conversions.cs b/MewtocolNet/TypeConversion/Conversions.cs
index 1788939..31997bd 100644
--- a/MewtocolNet/TypeConversion/Conversions.cs
+++ b/MewtocolNet/TypeConversion/Conversions.cs
@@ -130,48 +130,48 @@ namespace MewtocolNet.TypeConversion {
//default string DT Range conversion Example bytes: (04 00 03 00 XX XX XX)
//first 4 bytes are reserved size (2 bytes) and used size (2 bytes)
//the remaining bytes are the ascii bytes for the string
- //new PlcTypeConversion(RegisterType.DT_BYTE_RANGE) {
- // HoldingRegisterType = typeof(StructRegister),
- // PlcVarType = PlcVarType.STRING,
- // FromRaw = (reg, bytes) => {
+ new PlcTypeConversion(RegisterType.DT_BYTE_RANGE) {
+ HoldingRegisterType = typeof(StringRegister),
+ PlcVarType = PlcVarType.STRING,
+ FromRaw = (reg, bytes) => {
- // if(bytes.Length == 4) return string.Empty;
+ if(bytes.Length == 4) return string.Empty;
- // if(bytes == null || bytes.Length < 4) {
+ if(bytes == null || bytes.Length < 4) {
- // throw new Exception("Failed to convert string bytes, response not long enough");
+ throw new Exception("Failed to convert string bytes, response not long enough");
- // }
+ }
- // //get actual showed size
- // short actualLen = BitConverter.ToInt16(bytes, 2);
+ //get actual showed size
+ short actualLen = BitConverter.ToInt16(bytes, 2);
- // //skip 4 bytes because they only describe the length
- // string gotVal = Encoding.UTF8.GetString(bytes.Skip(4).Take(actualLen).ToArray());
+ //skip 4 bytes because they only describe the length
+ string gotVal = Encoding.UTF8.GetString(bytes.Skip(4).Take(actualLen).ToArray());
- // return gotVal;
+ return gotVal;
- // },
- // ToRaw = (reg, value) => {
+ },
+ ToRaw = (reg, value) => {
- // int padLen = value.Length;
- // if(value.Length % 2 != 0) padLen++;
+ int padLen = value.Length;
+ if(value.Length % 2 != 0) padLen++;
- // var strBytes = Encoding.UTF8.GetBytes(value.PadRight(padLen, '\0'));
+ var strBytes = Encoding.UTF8.GetBytes(value.PadRight(padLen, '\0'));
- // List finalBytes = new List();
+ List finalBytes = new List();
- // short reserved = (short)(reg.GetRegisterAddressLen() * 2 - 4);
- // short used = (short)value.Length;
+ short reserved = (short)(reg.GetRegisterAddressLen() * 2 - 4);
+ short used = (short)value.Length;
- // finalBytes.AddRange(BitConverter.GetBytes(reserved));
- // finalBytes.AddRange(BitConverter.GetBytes(used));
- // finalBytes.AddRange(strBytes);
+ finalBytes.AddRange(BitConverter.GetBytes(reserved));
+ finalBytes.AddRange(BitConverter.GetBytes(used));
+ finalBytes.AddRange(strBytes);
- // return finalBytes.ToArray();
+ return finalBytes.ToArray();
- // },
- //},
+ },
+ },
};
diff --git a/MewtocolNet/TypeConversion/PlcValueParser.cs b/MewtocolNet/TypeConversion/PlcValueParser.cs
index f2dad34..2366bbe 100644
--- a/MewtocolNet/TypeConversion/PlcValueParser.cs
+++ b/MewtocolNet/TypeConversion/PlcValueParser.cs
@@ -4,6 +4,7 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
+using System.Text;
namespace MewtocolNet {
@@ -36,10 +37,10 @@ namespace MewtocolNet {
}
- internal static T ParseArray (Register register, int[] indices, byte[] bytes) {
+ internal static object ParseArray (ArrayRegister register, byte[] bytes) {
//if byte array directly return the bytes
- if (typeof(T) == typeof(byte[])) return (T)(object)bytes;
+ if (typeof(T) == typeof(byte[])) return bytes;
IPlcTypeConverter converter;
Type underlyingElementType;
@@ -47,11 +48,11 @@ namespace MewtocolNet {
//special case for enums
if (typeof(T).IsEnum) {
- underlyingElementType = typeof(T).GetElementType().GetEnumUnderlyingType();
+ underlyingElementType = typeof(T).GetEnumUnderlyingType();
} else {
- underlyingElementType = typeof(T).GetElementType();
+ underlyingElementType = typeof(T);
}
@@ -61,17 +62,18 @@ namespace MewtocolNet {
throw new Exception($"A converter for the type {underlyingElementType} doesn't exist");
//parse the array from one to n dimensions
- var outArray = Array.CreateInstance(underlyingElementType, indices);
+ var outArray = Array.CreateInstance(typeof(T), register.indices);
int sizePerItem = 0;
+
if(underlyingElementType == typeof(string)) {
- throw new NotImplementedException();
+ sizePerItem = register.byteSizePerItem;
} else {
sizePerItem = underlyingElementType.DetermineTypeByteIntialSize();
}
- var iterateItems = indices.Aggregate((a, x) => a * x);
- var indexer = new int[indices.Length];
+ var iterateItems = register.indices.Aggregate((a, x) => a * x);
+ var indexer = new int[register.indices.Length];
for (int i = 0; i < iterateItems; i++) {
int j = i * sizePerItem;
@@ -79,9 +81,9 @@ namespace MewtocolNet {
var currentItem = bytes.Skip(j).Take(sizePerItem).ToArray();
var value = converter.FromRawData(register, currentItem);
- for (int remainder = i, k = indices.Length - 1; k >= 0; k--) {
+ for (int remainder = i, k = register.indices.Length - 1; k >= 0; k--) {
- int currentDimension = indices[k];
+ int currentDimension = register.indices[k];
indexer[k] = remainder % currentDimension;
remainder = remainder / currentDimension;
@@ -91,7 +93,7 @@ namespace MewtocolNet {
}
- return (T)(object)outArray;
+ return (object)outArray;
}
@@ -120,10 +122,10 @@ namespace MewtocolNet {
}
- internal static byte[] EncodeArray(Register register, T value) {
+ internal static byte[] EncodeArray(ArrayRegister register, object value) {
//if byte array directly return the bytes
- if (typeof(T) == typeof(byte[])) return (byte[])(object)value;
+ if (value.GetType() == typeof(byte[])) return (byte[])value;
IPlcTypeConverter converter;
Type underlyingElementType;
@@ -131,11 +133,11 @@ namespace MewtocolNet {
//special case for enums
if (typeof(T).IsEnum) {
- underlyingElementType = typeof(T).GetElementType().GetEnumUnderlyingType();
+ underlyingElementType = typeof(T).GetEnumUnderlyingType();
} else {
- underlyingElementType = typeof(T).GetElementType();
+ underlyingElementType = typeof(T);
}
@@ -145,8 +147,9 @@ namespace MewtocolNet {
throw new Exception($"A converter for the type {underlyingElementType} doesn't exist");
int sizePerItem = 0;
+
if (underlyingElementType == typeof(string)) {
- throw new NotImplementedException();
+ sizePerItem = register.byteSizePerItem;
} else {
sizePerItem = underlyingElementType.DetermineTypeByteIntialSize();
}
@@ -158,12 +161,19 @@ namespace MewtocolNet {
var encoded = converter.ToRawData(register, item);
+ if(encoded.Length > register.byteSizePerItem)
+ throw new ArgumentOutOfRangeException(nameof(value), "Input mismatched register target size");
+
encoded.CopyTo(encodedData, i);
i += sizePerItem;
}
+
+ if (encodedData.Length != register.reservedByteSize)
+ throw new ArgumentOutOfRangeException(nameof(value), "Input mismatched register target size");
+
return encodedData;
}
diff --git a/MewtocolNet/UnderlyingRegisters/MemoryAreaManager.cs b/MewtocolNet/UnderlyingRegisters/MemoryAreaManager.cs
index d477e13..967f941 100644
--- a/MewtocolNet/UnderlyingRegisters/MemoryAreaManager.cs
+++ b/MewtocolNet/UnderlyingRegisters/MemoryAreaManager.cs
@@ -106,12 +106,24 @@ namespace MewtocolNet.UnderlyingRegisters {
private void TestPollLevelExistence(Register reg) {
- if (!pollLevelConfigs.ContainsKey(1)) {
- pollLevelConfigs.Add(1, new PollLevelConfig {
+ if (!pollLevelConfigs.ContainsKey(MewtocolNet.PollLevel.Always)) {
+ pollLevelConfigs.Add(MewtocolNet.PollLevel.Always, new PollLevelConfig {
skipNth = 1,
});
}
+ if (!pollLevelConfigs.ContainsKey(MewtocolNet.PollLevel.FirstIteration)) {
+ pollLevelConfigs.Add(MewtocolNet.PollLevel.FirstIteration, new PollLevelConfig {
+ skipAllButFirst = true
+ });
+ }
+
+ if (!pollLevelConfigs.ContainsKey(MewtocolNet.PollLevel.Never)) {
+ pollLevelConfigs.Add(MewtocolNet.PollLevel.Never, new PollLevelConfig {
+ skipsAll = true,
+ });
+ }
+
if (!pollLevels.Any(x => x.level == reg.pollLevel)) {
pollLevels.Add(new PollLevel(wrAreaSize, dtAreaSize) {
@@ -294,10 +306,14 @@ namespace MewtocolNet.UnderlyingRegisters {
var sw = Stopwatch.StartNew();
+ var lvlConfig = pollLevelConfigs[pollLevel.level];
+
+ if (lvlConfig.skipsAll) continue;
+ if (lvlConfig.skipAllButFirst && pollIteration > 0) continue;
+
//determine to skip poll levels, first iteration is always polled
if (pollIteration > 0 && pollLevel.level > 1) {
- var lvlConfig = pollLevelConfigs[pollLevel.level];
var skipIterations = lvlConfig.skipNth;
var skipDelay = lvlConfig.delay;
@@ -360,14 +376,32 @@ namespace MewtocolNet.UnderlyingRegisters {
foreach (var pollLevel in pollLevels) {
- sb.AppendLine($"\n> ==== Poll lvl {pollLevel.level} ====");
+ if (pollLevel.level == MewtocolNet.PollLevel.Always) {
+
+ sb.AppendLine($"\n> ==== Poll lvl ALWAYS ====\n");
+ sb.AppendLine($"> Poll each iteration");
+
+ }else if (pollLevel.level == MewtocolNet.PollLevel.FirstIteration) {
+
+ sb.AppendLine($"\n> ==== Poll lvl FIRST ITERATION ====\n");
+ sb.AppendLine($"> Poll only on the first iteration");
+
+ } else if (pollLevel.level == MewtocolNet.PollLevel.Never) {
+
+ sb.AppendLine($"\n> ==== Poll lvl NEVER ====\n");
+ sb.AppendLine($"> Poll never");
+
+ } else {
+
+ sb.AppendLine($"\n> ==== Poll lvl {pollLevel.level} ====\n");
+ if (pollLevelConfigs.ContainsKey(pollLevel.level) && pollLevelConfigs[pollLevel.level].delay != null) {
+ sb.AppendLine($"> Poll each {pollLevelConfigs[pollLevel.level].delay?.TotalMilliseconds}ms");
+ } else if (pollLevelConfigs.ContainsKey(pollLevel.level)) {
+ sb.AppendLine($"> Poll every {pollLevelConfigs[pollLevel.level].skipNth} iterations");
+ }
- sb.AppendLine();
- if (pollLevelConfigs.ContainsKey(pollLevel.level) && pollLevelConfigs[pollLevel.level].delay != null) {
- sb.AppendLine($"> Poll each {pollLevelConfigs[pollLevel.level].delay?.TotalMilliseconds}ms");
- } else if (pollLevelConfigs.ContainsKey(pollLevel.level)) {
- sb.AppendLine($"> Poll every {pollLevelConfigs[pollLevel.level].skipNth} iterations");
}
+
sb.AppendLine($"> Level read time: {pollLevel.lastReadTimeMs}ms");
sb.AppendLine($"> Optimization distance: {maxOptimizationDistance}");
sb.AppendLine();
diff --git a/MewtocolTests/AutomatedPropertyRegisters.cs b/MewtocolTests/AutomatedPropertyRegisters.cs
index 90ce676..d4188db 100644
--- a/MewtocolTests/AutomatedPropertyRegisters.cs
+++ b/MewtocolTests/AutomatedPropertyRegisters.cs
@@ -14,124 +14,6 @@ namespace MewtocolTests {
this.output = output;
}
- private void Test(IRegisterInternal reg, uint expectAddr, string expectPlcName) {
-
- Assert.NotNull(reg);
- Assert.StartsWith("auto_prop_register_", reg.Name);
- Assert.Null(reg.Value);
-
- Assert.Equal(expectAddr, reg.MemoryAddress);
- Assert.Equal(expectPlcName, reg.GetMewName());
-
- output.WriteLine(reg.ToString());
-
- }
-
- //actual tests
-
- [Fact(DisplayName = "Boolean generation")]
- public void BooleanGen() {
-
- var interf = Mewtocol.Ethernet("192.168.0.1")
- .WithRegisterCollections(x =>
- x.AddCollection(new TestBoolRegisters())
- ).Build();
-
- output.WriteLine(((MewtocolInterface)interf).memoryManager.ExplainLayout());
-
- var register1 = interf.GetRegister("auto_prop_register_1");
- var register2 = interf.GetRegister("auto_prop_register_2");
- var register3 = interf.GetRegister("auto_prop_register_3");
-
- Test((IRegisterInternal)register1, 0, "XD");
- Test((IRegisterInternal)register2, 85, "R85A");
- Test((IRegisterInternal)register3, 85, "R85B");
-
- }
-
- [Fact(DisplayName = "Number 16 bit generation")]
- public void N16BitGen () {
-
- var interf = Mewtocol.Ethernet("192.168.0.1")
- .WithRegisterCollections(x =>
- x.AddCollection(new Nums16Bit())
- ).Build();
-
- var register1 = interf.GetRegister("auto_prop_register_1");
- var register2 = interf.GetRegister("auto_prop_register_2");
- var register3 = interf.GetRegister("auto_prop_register_3");
-
- //test generic properties
- Test((IRegisterInternal)register1, 50, "DT50");
- Test((IRegisterInternal)register2, 342, "DT342");
- Test((IRegisterInternal)register3, 899, "DT899");
-
- }
-
- [Fact(DisplayName = "Number 32 bit generation")]
- public void N32BitGen () {
-
- var interf = Mewtocol.Ethernet("192.168.0.1")
- .WithRegisterCollections(x => x
- .AddCollection(new Nums32Bit())
- ).Build();
-
- output.WriteLine(((MewtocolInterface)interf).memoryManager.ExplainLayout());
-
- var register1 = interf.GetRegister("auto_prop_register_1");
- var register2 = interf.GetRegister("auto_prop_register_2");
- var register3 = interf.GetRegister("auto_prop_register_3");
-
- //only one generated because same type
- var register4 = interf.GetRegister("auto_prop_register_4");
-
- var register6 = interf.GetRegister("auto_prop_register_5");
- var register7 = interf.GetRegister("auto_prop_register_6");
-
- //test generic properties
- Test((IRegisterInternal)register1, 7000, "DDT7000");
- Test((IRegisterInternal)register2, 7002, "DDT7002");
- Test((IRegisterInternal)register3, 7004, "DDT7004");
-
- Test((IRegisterInternal)register4, 7006, "DDT7006");
-
- Test((IRegisterInternal)register6, 7008, "DDT7008");
- Test((IRegisterInternal)register7, 7010, "DDT7010");
-
- }
-
- [Fact(DisplayName = "String generation")]
- public void StringGen() {
-
- var interf = Mewtocol.Ethernet("192.168.0.1")
- .WithRegisterCollections(x =>
- x.AddCollection(new TestStringRegisters())
- ).Build();
-
- var register1 = interf.GetRegister("auto_prop_register_1");
-
- //test generic properties
- Test((IRegisterInternal)register1, 7005, "DT7005");
-
- }
-
- [Fact(DisplayName = "Byte Array generation")]
- public void ByteArrGen() {
-
- var interf = Mewtocol.Ethernet("192.168.0.1")
- .WithRegisterCollections(x =>
- x.AddCollection(new TestBitwiseRegisters())
- ).Build();
-
- var register1 = interf.GetRegister("auto_prop_register_1");
- //var register2 = interf.GetRegister("auto_prop_register_2");
-
- //test generic properties
- Test((IRegisterInternal)register1, 7000, "DT7000");
- //Test((IRegisterInternal)register2, 7001, "DT7001");
-
- }
-
}
}
\ No newline at end of file
diff --git a/MewtocolTests/EncapsulatedTests/RegisterReadWriteTest.cs b/MewtocolTests/EncapsulatedTests/RegisterReadWriteTest.cs
deleted file mode 100644
index 3ddf4a5..0000000
--- a/MewtocolTests/EncapsulatedTests/RegisterReadWriteTest.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using MewtocolNet;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace MewtocolTests.EncapsulatedTests;
-
-internal class RegisterReadWriteTest {
-
- public IRegister TargetRegister { get; set; }
-
- public object IntialValue { get; set; }
-
- public object IntermediateValue { get; set; }
-
- public object AfterWriteValue { get; set; }
-
- public string RegisterPlcAddressName { get; set; }
-
-}
diff --git a/MewtocolTests/EncapsulatedTests/TestRegisterCollection.cs b/MewtocolTests/EncapsulatedTests/TestRegisterCollection.cs
index 6558f2d..ebdfe01 100644
--- a/MewtocolTests/EncapsulatedTests/TestRegisterCollection.cs
+++ b/MewtocolTests/EncapsulatedTests/TestRegisterCollection.cs
@@ -4,7 +4,7 @@ using System.Collections;
namespace MewtocolTests.EncapsulatedTests {
- public enum CurrentState : short {
+ public enum CurrentState16 : short {
Undefined = 0,
State1 = 1,
State2 = 2,
@@ -51,7 +51,7 @@ namespace MewtocolTests.EncapsulatedTests {
public ushort UInt16Type { get; set; }
[Register("DT50")]
- public CurrentState Enum16Type { get; set; }
+ public CurrentState16 Enum16Type { get; set; }
}
@@ -95,8 +95,6 @@ namespace MewtocolTests.EncapsulatedTests {
public class TestBitwiseRegisters : RegisterCollection {
- [Register("DT7000")]
- public BitArray BitArr16 { get; set; }
//[Register("DT7001")]
//public BitArray BitArr32 { get; set; }
diff --git a/MewtocolTests/SkippedChecks.cs b/MewtocolTests/SkippedChecks.cs
deleted file mode 100644
index 20857ff..0000000
--- a/MewtocolTests/SkippedChecks.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using MewtocolNet;
-using MewtocolNet.DocAttributes;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using Xunit;
-using Xunit.Abstractions;
-
-namespace MewtocolTests;
-
-public class SkippedChecks {
-
- private readonly ITestOutputHelper output;
-
- public SkippedChecks(ITestOutputHelper output) {
- this.output = output;
- }
-
- [Fact]
- public void BuildBCCFrameGeneration() {
-
- var toSuccess = new List {
-
- typeof(PlcCodeTestedAttribute),
- typeof(PlcEXRTAttribute),
- typeof(PlcLegacyAttribute),
-
- };
-
- Assert.NotNull(toSuccess);
-
- }
-
-}
diff --git a/MewtocolTests/TestHelperExtensions.cs b/MewtocolTests/TestHelperExtensions.cs
index 556b409..7a18933 100644
--- a/MewtocolTests/TestHelperExtensions.cs
+++ b/MewtocolTests/TestHelperExtensions.cs
@@ -66,18 +66,18 @@ namespace MewtocolTests {
}
- [Fact(DisplayName = nameof(MewtocolHelpers.ParseDTByteString))]
+ [Fact(DisplayName = nameof(MewtocolHelpers.ParseDTRawStringAsBytes))]
public void ParseDTByteStringGeneration() {
- var testList = new List() {
- "1112",
- "1C2C",
- "FFFF",
+ var testList = new List() {
+ new byte[] {0x11, 0x12},
+ new byte[] {0x1C, 0x2C},
+ new byte[] {0xFF, 0xFF},
};
foreach (var item in testList) {
- Assert.Equal(item, $"%01$RD{item}".BCC_Mew().ParseDTByteString());
+ Assert.Equal(item, $"%01$RD{item.ToHexString()}".BCC_Mew().ParseDTRawStringAsBytes());
}
diff --git a/MewtocolTests/TestLivePLC.cs b/MewtocolTests/TestLivePLC.cs
index 16dfd65..5ffce23 100644
--- a/MewtocolTests/TestLivePLC.cs
+++ b/MewtocolTests/TestLivePLC.cs
@@ -37,59 +37,6 @@ namespace MewtocolTests
};
- private List testRegisterRW = new() {
-
- new RegisterReadWriteTest {
- TargetRegister = new BoolRegister(IOType.R, 0xA, 10),
- RegisterPlcAddressName = "R10A",
- IntermediateValue = false,
- AfterWriteValue = true,
- },
- new RegisterReadWriteTest {
- TargetRegister = new NumberRegister(3000),
- RegisterPlcAddressName = "DT3000",
- IntermediateValue = (short)0,
- AfterWriteValue = (short)-513,
- },
- new RegisterReadWriteTest {
- TargetRegister = new NumberRegister(3001),
- RegisterPlcAddressName = "DT3001",
- IntermediateValue = CurrentState.Undefined,
- AfterWriteValue = CurrentState.State4,
- },
- new RegisterReadWriteTest {
- TargetRegister = new NumberRegister(3002),
- RegisterPlcAddressName = "DDT3002",
- IntermediateValue = CurrentState32.Undefined,
- AfterWriteValue = CurrentState32.StateBetween,
- },
- new RegisterReadWriteTest {
- TargetRegister = new NumberRegister(3004),
- RegisterPlcAddressName = "DDT3004",
- IntermediateValue = TimeSpan.Zero,
- AfterWriteValue = TimeSpan.FromSeconds(11),
- },
- new RegisterReadWriteTest {
- TargetRegister = new NumberRegister(3006),
- RegisterPlcAddressName = "DDT3006",
- IntermediateValue = TimeSpan.Zero,
- AfterWriteValue = PlcFormat.ParsePlcTime("T#50m"),
- },
- new RegisterReadWriteTest {
- TargetRegister = new StringRegister(40),
- RegisterPlcAddressName = "DT40",
- IntermediateValue = "Hello",
- AfterWriteValue = "TestV",
- },
- new RegisterReadWriteTest {
- TargetRegister = RegBuilder.Factory.FromPlcRegName("DT3008").AsBits(5).Build(),
- RegisterPlcAddressName = "DT3008",
- IntermediateValue = new BitArray(new bool[] { false, false, false, false, false }),
- AfterWriteValue = new BitArray(new bool[] { false, true, false, false, false }),
- },
-
- };
-
public TestLivePLC(ITestOutputHelper output) {
this.output = output;
@@ -141,58 +88,6 @@ namespace MewtocolTests
}
- [Fact(DisplayName = "Reading / Writing registers from PLC (Ethernet)")]
- public async void TestRegisterReadWriteAsync() {
-
- Logger.LogLevel = LogLevel.Verbose;
- Logger.OnNewLogMessage((d, l, m) => {
-
- output.WriteLine($"{d:HH:mm:ss:fff} {m}");
-
- });
-
- var plc = testPlcInformationData[0];
-
- output.WriteLine($"\n\n --- Testing: {plc.PLCName} ---\n");
-
- var client = Mewtocol.Ethernet(plc.PLCIP, plc.PLCPort).Build();
-
- foreach (var testRW in testRegisterRW) {
-
- client.AddRegister(testRW.TargetRegister);
-
- }
-
- await client.ConnectAsync();
- Assert.True(client.IsConnected);
-
- //cycle run mode to reset registers to inital
- await client.SetOperationModeAsync(false);
- await client.SetOperationModeAsync(true);
-
- foreach (var testRW in testRegisterRW) {
-
- var testRegister = client.Registers.First(x => x.PLCAddressName == testRW.RegisterPlcAddressName);
-
- //test inital val
- Assert.Null(testRegister.Value);
-
- await testRegister.ReadAsync();
-
- Assert.Equal(testRW.IntermediateValue, testRegister.Value);
-
- await testRegister.WriteAsync(testRW.AfterWriteValue);
- await testRegister.ReadAsync();
-
- //test after write val
- Assert.Equal(testRW.AfterWriteValue, testRegister.Value);
-
- }
-
- client.Disconnect();
-
- }
-
}
}
diff --git a/MewtocolTests/TestPublicBuilderPattern.cs b/MewtocolTests/TestPublicBuilderPattern.cs
new file mode 100644
index 0000000..a7eb134
--- /dev/null
+++ b/MewtocolTests/TestPublicBuilderPattern.cs
@@ -0,0 +1,89 @@
+using MewtocolNet;
+using MewtocolNet.RegisterBuilding;
+using MewtocolNet.RegisterBuilding.BuilderPatterns;
+using MewtocolNet.Registers;
+using MewtocolTests.EncapsulatedTests;
+using System.Collections;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace MewtocolTests;
+
+public class TestPublicBuilderPattern {
+
+ private readonly ITestOutputHelper output;
+
+ public TestPublicBuilderPattern(ITestOutputHelper output) => this.output = output;
+
+ private void TestStruct (string buildAddr, uint expectAddr, uint expectByteSize) where T : struct {
+
+ using var interf = (MewtocolInterface)Mewtocol.Ethernet("192.168.115.210").Build();
+ var builder = new RBuild(interf);
+
+ var comparer = new StructRegister(expectAddr, expectByteSize) {
+ attachedInterface = interf,
+ pollLevel = 1,
+ };
+
+ //test building to the internal list
+ builder.Struct(buildAddr).Build();
+ var generated = builder.assembler.assembled.First();
+ Assert.Equivalent(comparer, generated);
+ builder.assembler.assembled.Clear();
+ output.WriteLine(generated.Explain());
+
+ //test building with direct out
+ builder.Struct(buildAddr).Build(out var testRef);
+ Assert.Equivalent(comparer, testRef);
+ builder.assembler.assembled.Clear();
+ output.WriteLine(((Register)testRef).Explain());
+
+ comparer.pollLevel++;
+
+ //test building to the internal list with poll level
+ builder.Struct(buildAddr).PollLevel(2).Build();
+ var generated2 = builder.assembler.assembled.First();
+ Assert.Equivalent(comparer, generated2);
+ builder.assembler.assembled.Clear();
+ output.WriteLine(generated2.Explain());
+
+ //test building direct out with poll level
+ builder.Struct(buildAddr).PollLevel(2).Build(out var testRef2);
+ Assert.Equivalent(comparer, testRef2);
+ builder.assembler.assembled.Clear();
+ output.WriteLine(((Register)testRef2).Explain());
+
+ }
+
+ //16 bit structs
+
+ [Fact(DisplayName = "[16 Bit] short")]
+ public void TestStruct_1() => TestStruct("DT100", 100, 2);
+
+ [Fact(DisplayName = "[16 Bit] ushort")]
+ public void TestStruct_2() => TestStruct("DT101", 101, 2);
+
+ [Fact(DisplayName = "[16 Bit] Word")]
+ public void TestStruct_3() => TestStruct("DT102", 102, 2);
+
+ [Fact(DisplayName = "[16 Bit] Enum")]
+ public void TestStruct_4() => TestStruct("DT103", 103, 2);
+
+ //32 bit structs
+
+ [Fact(DisplayName = "[32 Bit] int")]
+ public void TestStruct_5() => TestStruct("DT104", 104, 4);
+
+ [Fact(DisplayName = "[32 Bit] uint")]
+ public void TestStruct_6() => TestStruct("DT105", 105, 4);
+
+ [Fact(DisplayName = "[32 Bit] DWord")]
+ public void TestStruct_7() => TestStruct("DT106", 106, 4);
+
+ [Fact(DisplayName = "[32 Bit] Enum")]
+ public void TestStruct_8() => TestStruct("DT107", 107, 4);
+
+ [Fact(DisplayName = "[32 Bit] TimeSpan")]
+ public void TestStruct_9() => TestStruct("DT108", 108, 4);
+
+}
diff --git a/MewtocolTests/TestPublicBuilderPatternArrays.cs b/MewtocolTests/TestPublicBuilderPatternArrays.cs
new file mode 100644
index 0000000..e938b0f
--- /dev/null
+++ b/MewtocolTests/TestPublicBuilderPatternArrays.cs
@@ -0,0 +1,63 @@
+using MewtocolNet;
+using MewtocolNet.RegisterBuilding;
+using MewtocolNet.RegisterBuilding.BuilderPatterns;
+using MewtocolNet.Registers;
+using MewtocolTests.EncapsulatedTests;
+using System.Collections;
+using System.Collections.Generic;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace MewtocolTests;
+
+public class TestPublicBuilderPatternArray {
+
+ private readonly ITestOutputHelper output;
+
+ public TestPublicBuilderPatternArray(ITestOutputHelper output) => this.output = output;
+
+ private void TestArray1D (string buildAddr, int indices1, uint expectAddr, uint expectByteSize) where T : struct {
+
+ using var interf = (MewtocolInterface)Mewtocol.Ethernet("192.168.115.210").Build();
+ var builder = new RBuild(interf);
+
+ var comparer = new ArrayRegister(expectAddr, expectByteSize, new int[] { indices1 }) {
+ attachedInterface = interf,
+ pollLevel = 1
+ };
+
+ //test building to the internal list
+ builder.Struct(buildAddr).AsArray(indices1).Build();
+ var generated = builder.assembler.assembled.First();
+
+ Assert.Equivalent(comparer, generated);
+
+ builder.assembler.assembled.Clear();
+ output.WriteLine(generated.Explain());
+
+ ////test building with direct out
+ //builder.Struct(buildAddr).AsArray(indices1).Build(out var testRef);
+ //Assert.Equivalent(comparer, testRef);
+ //builder.assembler.assembled.Clear();
+ //output.WriteLine(((Register)testRef).Explain());
+
+ //comparer.pollLevel++;
+
+ ////test building to the internal list with poll level
+ //builder.Struct(buildAddr).AsArray(indices1).PollLevel(2).Build();
+ //var generated2 = builder.assembler.assembled.First();
+ //Assert.Equivalent(comparer, generated2);
+ //builder.assembler.assembled.Clear();
+ //output.WriteLine(generated2.Explain());
+
+ ////test building direct out with poll level
+ //builder.Struct(buildAddr).AsArray(indices1).PollLevel(2).Build(out var testRef2);
+ //Assert.Equivalent(comparer, testRef2);
+ //builder.assembler.assembled.Clear();
+ //output.WriteLine(((Register)testRef2).Explain());
+
+ }
+
+ //16 bit structs
+
+}
diff --git a/MewtocolTests/TestRegisterBuilder.cs b/MewtocolTests/TestRegisterBuilder.cs
index 729ac6c..64a99a6 100644
--- a/MewtocolTests/TestRegisterBuilder.cs
+++ b/MewtocolTests/TestRegisterBuilder.cs
@@ -1,5 +1,6 @@
using MewtocolNet;
using MewtocolNet.RegisterBuilding;
+using MewtocolNet.RegisterBuilding.BuilderPatterns;
using MewtocolNet.Registers;
using MewtocolTests.EncapsulatedTests;
using System.Collections;
@@ -123,137 +124,9 @@ public class TestRegisterBuilder {
foreach (var item in dict) {
- output.WriteLine($"Expected: {item.Key}");
-
- var built = RegBuilder.Factory.FromPlcRegName(item.Key).AsPlcType(PlcVarType.BOOL).Build();
-
- output.WriteLine($"{(built?.ToString(true) ?? "null")}\n");
- Assert.Equivalent(item.Value, built);
}
}
- [Fact(DisplayName = "Parsing as Bool Register (Casted)")]
- public void TestRegisterBuildingBoolCasted () {
-
- var expect = new BoolRegister(IOType.R, 0x1, 0);
- var expect2 = new BoolRegister(IOType.Y, 0xA, 103);
-
- Assert.Equivalent(expect, RegBuilder.Factory.FromPlcRegName("R1").AsPlcType(PlcVarType.BOOL).Build());
- Assert.Equivalent(expect, RegBuilder.Factory.FromPlcRegName("R1").AsType().Build());
-
- Assert.Equivalent(expect2, RegBuilder.Factory.FromPlcRegName("Y103A").AsPlcType(PlcVarType.BOOL).Build());
- Assert.Equivalent(expect2, RegBuilder.Factory.FromPlcRegName("Y103A").AsType().Build());
-
- }
-
- [Fact(DisplayName = "Parsing as Bool Register (Auto)")]
- public void TestRegisterBuildingBoolAuto () {
-
- var expect = new BoolRegister(IOType.R, 0x1, 0);
- var expect2 = new BoolRegister(IOType.Y, 0xA, 103);
-
- Assert.Equivalent(expect, RegBuilder.Factory.FromPlcRegName("R1").Build());
- Assert.Equivalent(expect, RegBuilder.Factory.FromPlcRegName("R1").Build());
-
- Assert.Equivalent(expect2, RegBuilder.Factory.FromPlcRegName("Y103A").Build());
- Assert.Equivalent(expect2, RegBuilder.Factory.FromPlcRegName("Y103A").Build());
-
- }
-
- [Fact(DisplayName = "Parsing as Number Register (Casted)")]
- public void TestRegisterBuildingNumericCasted () {
-
- var expect = new NumberRegister(303);
- var expect2 = new NumberRegister(10002);
- var expect3 = new NumberRegister(404);
- var expect4 = new NumberRegister(400);
- var expect5 = new NumberRegister(203);
- var expect6 = new NumberRegister(204);
-
- Assert.Equivalent(expect, RegBuilder.Factory.FromPlcRegName("DT303").AsPlcType(PlcVarType.INT).Build());
- Assert.Equivalent(expect, RegBuilder.Factory.FromPlcRegName("DT303").AsType().Build());
-
- Assert.Equivalent(expect2, RegBuilder.Factory.FromPlcRegName("DDT10002").AsPlcType(PlcVarType.DINT).Build());
- Assert.Equivalent(expect2, RegBuilder.Factory.FromPlcRegName("DDT10002").AsType().Build());
-
- Assert.Equivalent(expect3, RegBuilder.Factory.FromPlcRegName("DDT404").AsPlcType(PlcVarType.REAL).Build());
- Assert.Equivalent(expect3, RegBuilder.Factory.FromPlcRegName("DDT404").AsType().Build());
-
- Assert.Equivalent(expect4, RegBuilder.Factory.FromPlcRegName("DDT400").AsPlcType(PlcVarType.TIME).Build());
- Assert.Equivalent(expect4, RegBuilder.Factory.FromPlcRegName("DDT400").AsType().Build());
-
- Assert.Equivalent(expect5, RegBuilder.Factory.FromPlcRegName("DT203").AsType().Build());
- Assert.Equivalent(expect6, RegBuilder.Factory.FromPlcRegName("DT204").AsType().Build());
-
- }
-
- [Fact(DisplayName = "Parsing as Number Register (Auto)")]
- public void TestRegisterBuildingNumericAuto () {
-
- var expect = new NumberRegister(201);
- var expect2 = new NumberRegister(10002);
-
- Assert.Equivalent(expect, RegBuilder.Factory.FromPlcRegName("DT201").Build());
- Assert.Equivalent(expect2, RegBuilder.Factory.FromPlcRegName("DDT10002").Build());
-
- }
-
- [Fact(DisplayName = "Parsing as Bytes Register (Casted)")]
- public void TestRegisterBuildingByteRangeCasted () {
-
- var expect = new BytesRegister(305, (uint)35);
-
- Assert.Equal((uint)18, expect.AddressLength);
- Assert.Equivalent(expect, RegBuilder.Factory.FromPlcRegName("DT305").AsBytes(35).Build());
-
- }
-
- [Fact(DisplayName = "Parsing as Bytes Register (Auto)")]
- public void TestRegisterBuildingByteRangeAuto () {
-
- var expect = new BytesRegister(300, (uint)20 * 2);
- var actual = (BytesRegister)RegBuilder.Factory.FromPlcRegName("DT300-DT319").Build();
-
- Assert.Equal((uint)20, expect.AddressLength);
- Assert.Equivalent(expect, actual);
-
- }
-
- [Fact(DisplayName = "Parsing as Bit Array")]
- public void TestRegisterBuildingBitArray () {
-
- var expect1 = new BytesRegister(311, (ushort)5);
- var expect2 = new BytesRegister(312, (ushort)16);
- var expect3 = new BytesRegister(313, (ushort)32);
-
- var actual1 = (BytesRegister)RegBuilder.Factory.FromPlcRegName("DT311").AsBits(5).Build();
- var actual2 = (BytesRegister)RegBuilder.Factory.FromPlcRegName("DT312").AsBits(16).Build();
- var actual3 = (BytesRegister)RegBuilder.Factory.FromPlcRegName("DT313").AsBits(32).Build();
-
- Assert.Equivalent(expect1, actual1);
- Assert.Equivalent(expect2, actual2);
- Assert.Equivalent(expect3, actual3);
-
- Assert.Equal((uint)1, actual1.AddressLength);
- Assert.Equal((uint)1, actual2.AddressLength);
- Assert.Equal((uint)2, actual3.AddressLength);
-
- }
-
- [Fact(DisplayName = "Parsing as String Register")]
- public void TestRegisterBuildingString () {
-
- var expect1 = new StringRegister(314);
-
- var actual1 = (StringRegister)RegBuilder.Factory.FromPlcRegName("DT314").AsType().Build();
-
- Assert.Equivalent(expect1, actual1);
-
- Assert.Equal((uint)0, actual1.WordsSize);
-
- }
-
-
}
diff --git a/MewtocolTests/TestRegisterInterface.cs b/MewtocolTests/TestRegisterInterface.cs
index 674d7af..8e004ec 100644
--- a/MewtocolTests/TestRegisterInterface.cs
+++ b/MewtocolTests/TestRegisterInterface.cs
@@ -13,113 +13,22 @@ namespace MewtocolTests {
this.output = output;
}
- [Fact(DisplayName = "Numeric mewtocol query building")]
- public void NumericRegisterMewtocolIdentifiers() {
-
- List registers = new List {
- new NumberRegister(50),
- new NumberRegister(50),
- new NumberRegister(50),
- new NumberRegister(50),
- new NumberRegister(50),
- new NumberRegister(50),
- new BytesRegister(50, (uint)30),
- new BytesRegister(50, (uint)31),
- };
-
- List expectedIdents = new List {
- "D0005000050", //single word register
- "D0005000050", //single word register
- "D0005000051", //double word register
- "D0005000051", //double word register
- "D0005000051", //double word register
- "D0005000051", //double word register
- "D0005000064", //variable len register even bytes
- "D0005000065", //variable len register odd bytes
- };
-
- //test mewtocol idents
- for (int i = 0; i < registers.Count; i++) {
-
- IRegisterInternal? reg = registers[i];
- string expect = expectedIdents[i];
-
- Assert.Equal(expect, reg.BuildMewtocolQuery());
-
- }
-
- }
-
- [Fact(DisplayName = "PLC register naming convention test")]
- public void PLCRegisterIdentifiers() {
-
- List registers = new List {
- //numeric ones
- new NumberRegister(50, _name: null),
- new NumberRegister(60, _name : null),
- new NumberRegister(70, _name : null),
- new NumberRegister(80, _name : null),
- new NumberRegister(90, _name : null),
- new NumberRegister(100, _name : null),
-
- //boolean
- new BoolRegister(IOType.R, 0, 100),
- new BoolRegister(IOType.R, 0, 0),
- new BoolRegister(IOType.X, 5),
- new BoolRegister(IOType.X, 0xA),
- new BoolRegister(IOType.X, 0xF, 109),
- new BoolRegister(IOType.Y, 0xC, 75),
-
- //string
- new BytesRegister(999, 5),
- };
-
- List expcectedIdents = new List {
-
- //numeric ones
- "DT50",
- "DT60",
- "DDT70",
- "DDT80",
- "DDT90",
- "DDT100",
-
- //boolean
- "R100",
- "R0",
- "X5",
- "XA",
- "X109F",
- "Y75C",
-
- //string
- "DT999"
-
- };
-
- //test mewtocol idents
- for (int i = 0; i < registers.Count; i++) {
-
- IRegisterInternal? reg = registers[i];
- string expect = expcectedIdents[i];
-
- Assert.Equal(expect, reg.GetMewName());
-
- }
-
- }
-
- [Fact(DisplayName = "Non allowed (Overflow address)")]
- public void OverFlowRegisterAddress() {
+ [Fact(DisplayName = "Non allowed Struct Address (Overflow address)")]
+ public void OverFlowStructRegister() {
var ex = Assert.Throws(() => {
- new NumberRegister(100000, _name: null);
+ new StructRegister(100000, 2);
});
output.WriteLine(ex.Message.ToString());
+ }
+
+ [Fact(DisplayName = "Non allowed Boolean Address (Overflow address )")]
+ public void OverFlowBoolRegister() {
+
var ex1 = Assert.Throws(() => {
new BoolRegister(IOType.R, _areaAdress: 512);
@@ -136,27 +45,6 @@ namespace MewtocolTests {
output.WriteLine(ex2.Message.ToString());
- var ex3 = Assert.Throws(() => {
-
- new BytesRegister(100000, 5);
-
- });
-
- output.WriteLine(ex3.Message.ToString());
-
- }
-
- [Fact(DisplayName = "Non allowed (Wrong data type)")]
- public void WrongDataTypeRegister() {
-
- var ex = Assert.Throws(() => {
-
- new NumberRegister(100, _name: null);
-
- });
-
- output.WriteLine(ex.Message.ToString());
-
}
}