Projektdateien hinzufügen.

This commit is contained in:
Felix Weiß
2022-06-16 09:57:15 +02:00
parent 7595a3f0ce
commit 3911bb9707
19 changed files with 1981 additions and 0 deletions

26
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,26 @@
{
"version": "0.2.0",
"configurations": [
{
// Use IntelliSense to find out which attributes exist for C# debugging
// Use hover for the description of the existing attributes
// For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
"name": ".NET Core Launch (console)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/Examples/bin/Debug/net5.0/Examples.dll",
"args": [],
"cwd": "${workspaceFolder}/Examples",
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
"console": "internalConsole",
"stopAtEntry": false
},
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach"
}
]
}

42
.vscode/tasks.json vendored Normal file
View File

@@ -0,0 +1,42 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/Examples/Examples.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "publish",
"command": "dotnet",
"type": "process",
"args": [
"publish",
"${workspaceFolder}/Examples/Examples.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "watch",
"command": "dotnet",
"type": "process",
"args": [
"watch",
"run",
"${workspaceFolder}/Examples/Examples.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
}
]
}

193
Builds/MewtocolNet.xml Normal file
View File

@@ -0,0 +1,193 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>MewtocolNet</name>
</assembly>
<members>
<member name="M:MewtocolNet.MewtocolInterface.ConnectAsync(System.Action{MewtocolNet.Responses.PLCInfo},System.Action)">
<summary>
Trys to connect to the PLC by the IP given in the constructor
</summary>
<param name="OnConnected">Gets called when a connection with a PLC was established</param>
<param name="OnFailed">Gets called when an error or timeout during connection occurs</param>
<returns></returns>
</member>
<member name="M:MewtocolNet.MewtocolInterface.AttachContinousReader(System.Int32)">
<summary>
Attaches a continous reader that reads back the Registers and Contacts
</summary>
</member>
<member name="P:MewtocolNet.MewtocolInterface.PlcInfo">
<summary>
Generic information about the connected PLC
</summary>
</member>
<member name="M:MewtocolNet.MewtocolInterface.#ctor(System.String,System.Int32)">
<summary>
Builds a new Interfacer for a PLC
</summary>
<param name="_ip"></param>
<param name="_port"></param>
</member>
<member name="M:MewtocolNet.MewtocolInterface.SendCommandAsync(System.String)">
<summary>
Sends a command to the PLC and awaits results
</summary>
<param name="_msg">MEWTOCOL Formatted request string ex: %01#RT</param>
<param name="_close">Auto close of frame [true]%01#RT01\r [false]%01#RT</param>
<returns>Returns the result</returns>
</member>
<member name="M:MewtocolNet.MewtocolInterface.GetPLCInfoAsync">
<summary>
Gets generic information about the PLC
</summary>
<returns>A PLCInfo class</returns>
</member>
<member name="M:MewtocolNet.MewtocolInterface.ReadBoolContacts(System.Collections.Generic.List{MewtocolNet.Responses.Contact},System.Int32)">
<summary>
Reads bool values from the plc by the given <c>Contact</c> List
</summary>
<param name="_contactsToRead">A list of contacts</param>
<param name="_stationNumber">The PLCs station number</param>
<returns>List of IBoolContact with unique copys of the given contacts</returns>
</member>
<member name="M:MewtocolNet.MewtocolInterface.WriteContact(MewtocolNet.Responses.Contact,System.Boolean,System.Int32)">
<summary>
Writes a boolen value to the given contact
</summary>
<param name="_contact">The contact to write</param>
<param name="_value">The boolean state to write</param>
<param name="_stationNumber">Station Number (optional)</param>
<returns>A result struct</returns>
</member>
<member name="M:MewtocolNet.MewtocolInterface.ReadNumRegister``1(MewtocolNet.Responses.NRegister{``0},System.Int32)">
<summary>
Reads the given numeric register from PLC
</summary>
<typeparam name="T">Type of number (short, ushort, int, uint, float)</typeparam>
<param name="_toRead">The register to read</param>
<param name="_stationNumber">Station number to access</param>
<returns>A result with the given NumberRegister containing the readback value and a result struct</returns>
</member>
<member name="M:MewtocolNet.MewtocolInterface.WriteNumRegister``1(MewtocolNet.Responses.NRegister{``0},``0,System.Int32)">
<summary>
Reads the given numeric register from PLC
</summary>
<typeparam name="T">Type of number (short, ushort, int, uint, float)</typeparam>
<param name="_toWrite">The register to write</param>
<param name="_stationNumber">Station number to access</param>
<returns>A result with the given NumberRegister and a result struct</returns>
</member>
<member name="E:MewtocolNet.Events.MewtocolContactListener.ContactsChangedValue">
<summary>
Gets fired whenever a contact of the observed list changes its value
</summary>
</member>
<member name="T:MewtocolNet.Responses.Register">
<summary>
A class describing a register
</summary>
</member>
<member name="E:MewtocolNet.Responses.Register.ValueChanged">
<summary>
Gets called whenever the value was changed
</summary>
</member>
<member name="T:MewtocolNet.Responses.NRegister`1">
<summary>
Defines a register containing a number
</summary>
<typeparam name="T">The type of the numeric value</typeparam>
</member>
<member name="P:MewtocolNet.Responses.NRegister`1.Value">
<summary>
The value of the register
</summary>
</member>
<member name="M:MewtocolNet.Responses.NRegister`1.#ctor(System.Int32,System.String)">
<summary>
Defines a register containing a number
</summary>
<param name="_adress">Memory start adress max 99999</param>
<param name="_format">The format in which the variable is stored</param>
</member>
<member name="T:MewtocolNet.Responses.NRegisterResult`1">
<summary>
Result for a read/write operation
</summary>
<typeparam name="T">The type of the numeric value</typeparam>
</member>
<member name="T:MewtocolNet.Responses.SRegister">
<summary>
Defines a register containing a string
</summary>
</member>
<member name="M:MewtocolNet.Responses.SRegister.#ctor(System.Int32,System.Int32,System.String)">
<summary>
Defines a register containing a string
</summary>
</member>
<member name="T:MewtocolNet.Responses.CommandResult">
<summary>
The formatted result of a ascii command
</summary>
</member>
<member name="T:MewtocolNet.Responses.PLCInfo">
<summary>
Contains generic information about the plc
</summary>
</member>
<member name="M:MewtocolNet.Responses.PLCInfo.PLCMode.BuildFromHex(System.String)">
<summary>
Gets operation mode from 2 digit hex number
</summary>
</member>
<member name="T:MewtocolNet.Responses.IBoolContact">
<summary>
Contact as bool contact
</summary>
</member>
<member name="T:MewtocolNet.Responses.Contact">
<summary>
A class describing a PLC contact
</summary>
</member>
<member name="M:MewtocolNet.Responses.Contact.#ctor(MewtocolNet.Responses.Contact.PFX,System.Int32,System.String)">
<summary>
Creates a new base Contact
</summary>
<param name="_prefix">A prefix identifier eg. X,Y,R,L</param>
<param name="_number">The number of the PLC contact</param>
</member>
<member name="M:MewtocolNet.Responses.Contact.#ctor(System.String,System.Int32,System.String)">
<summary>
Creates a new base Contact
</summary>
<param name="_prefix">A prefix identifier eg. X,Y,R,L</param>
<param name="_number">The number of the PLC contact</param>
</member>
<member name="M:MewtocolNet.Responses.Contact.#ctor(System.String,System.String)">
<summary>
Build contact from complete contact name
</summary>
<param name="_contactName">Complete contact name e.g. Y1C, Y3D or X1</param>
</member>
<member name="M:MewtocolNet.Responses.Contact.BuildMewtocolIdent">
<summary>
Builds the mewtocol ascii contact identifier
</summary>
<returns>The identifier e.g. Y0001 or Y000A or X001C</returns>
</member>
<member name="M:MewtocolNet.Responses.Contact.ToGenericObject">
<summary>
Converts the class to a generic json compatible object
</summary>
<returns></returns>
</member>
<member name="M:MewtocolNet.Responses.Contact.ShallowCopy">
<summary>
Creates a copy of the contact
</summary>
</member>
</members>
</doc>

View File

@@ -0,0 +1,23 @@
{
"runtimeTarget": {
"name": ".NETCoreApp,Version=v5.0",
"signature": ""
},
"compilationOptions": {},
"targets": {
".NETCoreApp,Version=v5.0": {
"MewtocolNet/0.1.5": {
"runtime": {
"MewtocolNet.dll": {}
}
}
}
},
"libraries": {
"MewtocolNet/0.1.5": {
"type": "project",
"serviceable": false,
"sha512": ""
}
}
}

Binary file not shown.

View File

@@ -0,0 +1,193 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>MewtocolNet</name>
</assembly>
<members>
<member name="M:MewtocolNet.MewtocolInterface.ConnectAsync(System.Action{MewtocolNet.Responses.PLCInfo},System.Action)">
<summary>
Trys to connect to the PLC by the IP given in the constructor
</summary>
<param name="OnConnected">Gets called when a connection with a PLC was established</param>
<param name="OnFailed">Gets called when an error or timeout during connection occurs</param>
<returns></returns>
</member>
<member name="M:MewtocolNet.MewtocolInterface.AttachContinousReader(System.Int32)">
<summary>
Attaches a continous reader that reads back the Registers and Contacts
</summary>
</member>
<member name="P:MewtocolNet.MewtocolInterface.PlcInfo">
<summary>
Generic information about the connected PLC
</summary>
</member>
<member name="M:MewtocolNet.MewtocolInterface.#ctor(System.String,System.Int32)">
<summary>
Builds a new Interfacer for a PLC
</summary>
<param name="_ip"></param>
<param name="_port"></param>
</member>
<member name="M:MewtocolNet.MewtocolInterface.SendCommandAsync(System.String)">
<summary>
Sends a command to the PLC and awaits results
</summary>
<param name="_msg">MEWTOCOL Formatted request string ex: %01#RT</param>
<param name="_close">Auto close of frame [true]%01#RT01\r [false]%01#RT</param>
<returns>Returns the result</returns>
</member>
<member name="M:MewtocolNet.MewtocolInterface.GetPLCInfoAsync">
<summary>
Gets generic information about the PLC
</summary>
<returns>A PLCInfo class</returns>
</member>
<member name="M:MewtocolNet.MewtocolInterface.ReadBoolContacts(System.Collections.Generic.List{MewtocolNet.Responses.Contact},System.Int32)">
<summary>
Reads bool values from the plc by the given <c>Contact</c> List
</summary>
<param name="_contactsToRead">A list of contacts</param>
<param name="_stationNumber">The PLCs station number</param>
<returns>List of IBoolContact with unique copys of the given contacts</returns>
</member>
<member name="M:MewtocolNet.MewtocolInterface.WriteContact(MewtocolNet.Responses.Contact,System.Boolean,System.Int32)">
<summary>
Writes a boolen value to the given contact
</summary>
<param name="_contact">The contact to write</param>
<param name="_value">The boolean state to write</param>
<param name="_stationNumber">Station Number (optional)</param>
<returns>A result struct</returns>
</member>
<member name="M:MewtocolNet.MewtocolInterface.ReadNumRegister``1(MewtocolNet.Responses.NRegister{``0},System.Int32)">
<summary>
Reads the given numeric register from PLC
</summary>
<typeparam name="T">Type of number (short, ushort, int, uint, float)</typeparam>
<param name="_toRead">The register to read</param>
<param name="_stationNumber">Station number to access</param>
<returns>A result with the given NumberRegister containing the readback value and a result struct</returns>
</member>
<member name="M:MewtocolNet.MewtocolInterface.WriteNumRegister``1(MewtocolNet.Responses.NRegister{``0},``0,System.Int32)">
<summary>
Reads the given numeric register from PLC
</summary>
<typeparam name="T">Type of number (short, ushort, int, uint, float)</typeparam>
<param name="_toWrite">The register to write</param>
<param name="_stationNumber">Station number to access</param>
<returns>A result with the given NumberRegister and a result struct</returns>
</member>
<member name="E:MewtocolNet.Events.MewtocolContactListener.ContactsChangedValue">
<summary>
Gets fired whenever a contact of the observed list changes its value
</summary>
</member>
<member name="T:MewtocolNet.Responses.Register">
<summary>
A class describing a register
</summary>
</member>
<member name="E:MewtocolNet.Responses.Register.ValueChanged">
<summary>
Gets called whenever the value was changed
</summary>
</member>
<member name="T:MewtocolNet.Responses.NRegister`1">
<summary>
Defines a register containing a number
</summary>
<typeparam name="T">The type of the numeric value</typeparam>
</member>
<member name="P:MewtocolNet.Responses.NRegister`1.Value">
<summary>
The value of the register
</summary>
</member>
<member name="M:MewtocolNet.Responses.NRegister`1.#ctor(System.Int32,System.String)">
<summary>
Defines a register containing a number
</summary>
<param name="_adress">Memory start adress max 99999</param>
<param name="_format">The format in which the variable is stored</param>
</member>
<member name="T:MewtocolNet.Responses.NRegisterResult`1">
<summary>
Result for a read/write operation
</summary>
<typeparam name="T">The type of the numeric value</typeparam>
</member>
<member name="T:MewtocolNet.Responses.SRegister">
<summary>
Defines a register containing a string
</summary>
</member>
<member name="M:MewtocolNet.Responses.SRegister.#ctor(System.Int32,System.Int32,System.String)">
<summary>
Defines a register containing a string
</summary>
</member>
<member name="T:MewtocolNet.Responses.CommandResult">
<summary>
The formatted result of a ascii command
</summary>
</member>
<member name="T:MewtocolNet.Responses.PLCInfo">
<summary>
Contains generic information about the plc
</summary>
</member>
<member name="M:MewtocolNet.Responses.PLCInfo.PLCMode.BuildFromHex(System.String)">
<summary>
Gets operation mode from 2 digit hex number
</summary>
</member>
<member name="T:MewtocolNet.Responses.IBoolContact">
<summary>
Contact as bool contact
</summary>
</member>
<member name="T:MewtocolNet.Responses.Contact">
<summary>
A class describing a PLC contact
</summary>
</member>
<member name="M:MewtocolNet.Responses.Contact.#ctor(MewtocolNet.Responses.Contact.PFX,System.Int32,System.String)">
<summary>
Creates a new base Contact
</summary>
<param name="_prefix">A prefix identifier eg. X,Y,R,L</param>
<param name="_number">The number of the PLC contact</param>
</member>
<member name="M:MewtocolNet.Responses.Contact.#ctor(System.String,System.Int32,System.String)">
<summary>
Creates a new base Contact
</summary>
<param name="_prefix">A prefix identifier eg. X,Y,R,L</param>
<param name="_number">The number of the PLC contact</param>
</member>
<member name="M:MewtocolNet.Responses.Contact.#ctor(System.String,System.String)">
<summary>
Build contact from complete contact name
</summary>
<param name="_contactName">Complete contact name e.g. Y1C, Y3D or X1</param>
</member>
<member name="M:MewtocolNet.Responses.Contact.BuildMewtocolIdent">
<summary>
Builds the mewtocol ascii contact identifier
</summary>
<returns>The identifier e.g. Y0001 or Y000A or X001C</returns>
</member>
<member name="M:MewtocolNet.Responses.Contact.ToGenericObject">
<summary>
Converts the class to a generic json compatible object
</summary>
<returns></returns>
</member>
<member name="M:MewtocolNet.Responses.Contact.ShallowCopy">
<summary>
Creates a copy of the contact
</summary>
</member>
</members>
</doc>

12
Examples/Examples.csproj Normal file
View File

@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\MewtocolNet\MewtocolNet.csproj" />
</ItemGroup>
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
</Project>

39
Examples/Program.cs Normal file
View File

@@ -0,0 +1,39 @@
using System;
using System.Threading.Tasks;
using System.Text.Json;
using MewtocolNet;
namespace Examples {
class Program {
static void Main(string[] args) {
Console.WriteLine("Starting test");
Task.Factory.StartNew(async () => {
MewtocolInterface interf = new MewtocolInterface("10.237.191.3");
interf.AddRegister<short>("Cooler Status",1204);
interf.AddRegister<string>(1101, 4);
interf.RegisterChanged += (o) => {
Console.WriteLine($"DT{o.MemoryAdress} {(o.Name != null ? $"({o.Name}) " : "")}changed to {o.GetValueString()}");
};
await interf.ConnectAsync(
(plcinf) => {
Console.WriteLine("Connected to PLC:\n" + plcinf.ToString());
},
() => {
Console.WriteLine("Failed connection");
}
).AttachContinousReader(50);
});
Console.ReadLine();
}
}
}

48
MewtocolNet.sln Normal file
View File

@@ -0,0 +1,48 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30114.105
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MewtocolNet", "MewtocolNet\MewtocolNet.csproj", "{8B7863E7-5E82-4990-9138-2C0C24629982}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Examples", "Examples\Examples.csproj", "{D1F2FA26-3752-44BA-9DCB-4BC2436C5957}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{8B7863E7-5E82-4990-9138-2C0C24629982}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8B7863E7-5E82-4990-9138-2C0C24629982}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8B7863E7-5E82-4990-9138-2C0C24629982}.Debug|x64.ActiveCfg = Debug|Any CPU
{8B7863E7-5E82-4990-9138-2C0C24629982}.Debug|x64.Build.0 = Debug|Any CPU
{8B7863E7-5E82-4990-9138-2C0C24629982}.Debug|x86.ActiveCfg = Debug|Any CPU
{8B7863E7-5E82-4990-9138-2C0C24629982}.Debug|x86.Build.0 = Debug|Any CPU
{8B7863E7-5E82-4990-9138-2C0C24629982}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8B7863E7-5E82-4990-9138-2C0C24629982}.Release|Any CPU.Build.0 = Release|Any CPU
{8B7863E7-5E82-4990-9138-2C0C24629982}.Release|x64.ActiveCfg = Release|Any CPU
{8B7863E7-5E82-4990-9138-2C0C24629982}.Release|x64.Build.0 = Release|Any CPU
{8B7863E7-5E82-4990-9138-2C0C24629982}.Release|x86.ActiveCfg = Release|Any CPU
{8B7863E7-5E82-4990-9138-2C0C24629982}.Release|x86.Build.0 = Release|Any CPU
{D1F2FA26-3752-44BA-9DCB-4BC2436C5957}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D1F2FA26-3752-44BA-9DCB-4BC2436C5957}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D1F2FA26-3752-44BA-9DCB-4BC2436C5957}.Debug|x64.ActiveCfg = Debug|Any CPU
{D1F2FA26-3752-44BA-9DCB-4BC2436C5957}.Debug|x64.Build.0 = Debug|Any CPU
{D1F2FA26-3752-44BA-9DCB-4BC2436C5957}.Debug|x86.ActiveCfg = Debug|Any CPU
{D1F2FA26-3752-44BA-9DCB-4BC2436C5957}.Debug|x86.Build.0 = Debug|Any CPU
{D1F2FA26-3752-44BA-9DCB-4BC2436C5957}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D1F2FA26-3752-44BA-9DCB-4BC2436C5957}.Release|Any CPU.Build.0 = Release|Any CPU
{D1F2FA26-3752-44BA-9DCB-4BC2436C5957}.Release|x64.ActiveCfg = Release|Any CPU
{D1F2FA26-3752-44BA-9DCB-4BC2436C5957}.Release|x64.Build.0 = Release|Any CPU
{D1F2FA26-3752-44BA-9DCB-4BC2436C5957}.Release|x86.ActiveCfg = Release|Any CPU
{D1F2FA26-3752-44BA-9DCB-4BC2436C5957}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,140 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using MewtocolNet.Responses;
namespace MewtocolNet {
public partial class MewtocolInterface {
public event Action<Register> RegisterChanged;
internal CancellationTokenSource cTokenAutoUpdater;
protected internal bool isWriting = false;
public bool ContinousReaderRunning { get; set; }
public List<Register> Registers { get; set; } = new List<Register>();
public List<Contact> Contacts { get; set; } = new List<Contact>();
/// <summary>
/// Trys to connect to the PLC by the IP given in the constructor
/// </summary>
/// <param name="OnConnected">Gets called when a connection with a PLC was established</param>
/// <param name="OnFailed">Gets called when an error or timeout during connection occurs</param>
/// <returns></returns>
public async Task<MewtocolInterface> ConnectAsync (Action<PLCInfo> OnConnected = null, Action OnFailed = null) {
var plcinf = await GetPLCInfoAsync();
if(plcinf is not null) {
if(OnConnected != null) OnConnected(plcinf);
} else {
if(OnFailed != null) OnFailed();
}
return this;
}
#region Register Adding
/// <summary>
/// Adds a PLC memory register to the watchlist <para/>
/// The registers can be read back by attaching <see cref="MewtocolInterfaceExtensions.AttachContinousReader(Task{MewtocolInterface}, int)"/>
/// <para/>
/// to the end of a <see cref="MewtocolInterface.ConnectAsync(Action{PLCInfo}, Action)"/> method
/// </summary>
/// <typeparam name="T">
/// The type of the register translated from C# to IEC 61131-3 types
/// <para>C# ------ IEC</para>
/// <para>short => INT/WORD</para>
/// <para>ushort => UINT</para>
/// <para>int => DOUBLE</para>
/// <para>uint => UDOUBLE</para>
/// <para>float => REAL</para>
/// <para>string => STRING</para>
/// </typeparam>
/// <param name="_address">The address of the register in the PLCs memory</param>
/// <param name="_length">The length of the string (Can be ignored for other types)</param>
public void AddRegister<T> (int _address, int _length = 1) {
Type regType = typeof(T);
if (regType == typeof(short)) {
Registers.Add(new NRegister<short>(_address));
} else if (regType == typeof(ushort)) {
Registers.Add(new NRegister<ushort>(_address));
} else if (regType == typeof(int)) {
Registers.Add(new NRegister<int>(_address));
} else if (regType == typeof(uint)) {
Registers.Add(new NRegister<uint>(_address));
} else if (regType == typeof(float)) {
Registers.Add(new NRegister<float>(_address));
} else if (regType == typeof(string)) {
Registers.Add(new SRegister(_address, _length));
} else {
throw new NotSupportedException($"The type {regType} is not allowed for Registers \n" +
$"Allowed are: short, ushort, int, uint, float and string");
}
}
/// <summary>
/// Adds a PLC memory register to the watchlist <para/>
/// The registers can be read back by attaching <see cref="MewtocolInterfaceExtensions.AttachContinousReader(Task{MewtocolInterface}, int)"/>
/// <para/>
/// to the end of a <see cref="MewtocolInterface.ConnectAsync(Action{PLCInfo}, Action)"/> method
/// </summary>
/// <typeparam name="T">
/// The type of the register translated from C# to IEC 61131-3 types
/// <para>C# ------ IEC</para>
/// <para>short => INT/WORD</para>
/// <para>ushort => UINT</para>
/// <para>int => DOUBLE</para>
/// <para>uint => UDOUBLE</para>
/// <para>float => REAL</para>
/// <para>string => STRING</para>
/// </typeparam>
/// <param name="_name">A naming definition for QOL, doesn't effect PLC and is optional</param>
/// <param name="_address">The address of the register in the PLCs memory</param>
/// <param name="_length">The length of the string (Can be ignored for other types)</param>
public void AddRegister<T>(string _name, int _address, int _length = 1) {
Type regType = typeof(T);
if (regType == typeof(short)) {
Registers.Add(new NRegister<short>(_address, _name));
} else if (regType == typeof(ushort)) {
Registers.Add(new NRegister<ushort>(_address, _name));
} else if (regType == typeof(int)) {
Registers.Add(new NRegister<int>(_address, _name));
} else if (regType == typeof(uint)) {
Registers.Add(new NRegister<uint>(_address, _name));
} else if (regType == typeof(float)) {
Registers.Add(new NRegister<float>(_address, _name));
} else if (regType == typeof(string)) {
Registers.Add(new SRegister(_address, _length, _name));
} else {
throw new NotSupportedException($"The type {regType} is not allowed for Registers \n" +
$"Allowed are: short, ushort, int, uint, float and string");
}
}
#endregion
internal void InvokeRegisterChanged (Register reg) {
RegisterChanged?.Invoke (reg);
}
}
}

View File

@@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
namespace MewtocolNet.Links {
public class LinkedData {
public static Dictionary<int, string> ErrorCodes = new System.Collections.Generic.Dictionary<int, string> {
{21, "NACK error"},
{22, "WACK error"},
{23, "Station number overlap"},
{24, "Transmission error"},
{25, "Hardware error"},
{26, "Station number setting error"},
{27, "Frame over error"},
{28, "No response error"},
{29, "Buffer close error"},
{30, "Timeout error"},
{32, "Transmission impossible"},
{33, "Communication stop"},
{36, "No local station"},
{38, "Other com error"},
{40, "BCC error"},
{41, "Format error"},
{42, "Not supported error"},
{43, "Procedure error"},
{50, "Link setting error"},
{51, "Simultanious operation error"},
{52, "Sending disable error"},
{53, "Busy error"},
{60, "Paramter error"},
{61, "Data error"},
{62, "Registration error"},
{63, "Mode error"},
{66, "Adress error"},
{67, "No data error"},
{72, "Timeout"},
{73, "Timeout"},
};
}
}

View File

@@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Linq;
using MewtocolNet.Responses;
namespace MewtocolNet.Events {
public class MewtocolContactListener : IDisposable {
/// <summary>
/// Gets fired whenever a contact of the observed list changes its value
/// </summary>
public event Action<List<IBoolContact>> ContactsChangedValue;
//privates
private List<IBoolContact> lastContacts = new List<IBoolContact>();
private CancellationTokenSource cToken = new CancellationTokenSource();
public static MewtocolContactListener ListenContactChanges (MewtocolInterface _interFace, List<Contact> _observeContacts, int _refreshMS = 100, int _stationNumber = 1) {
MewtocolContactListener listener = new MewtocolContactListener();
_ = Task.Factory.StartNew( async () => {
//get contacts first time
listener.lastContacts = (List<IBoolContact>) await _interFace.ReadBoolContacts(_observeContacts, _stationNumber);
while(!listener.cToken.Token.IsCancellationRequested) {
//compare and update
var newContactData = (List<IBoolContact>) await _interFace.ReadBoolContacts(_observeContacts, _stationNumber);
var difference = newContactData.Where(p => listener.lastContacts.Any(l => p.Value != l.Value && p.Identifier == l.Identifier));
if(difference.Count() > 0) {
listener.ContactsChangedValue?.Invoke(difference.ToList());
listener.lastContacts = newContactData;
} else {
}
await Task.Delay(_refreshMS);
}
});
return listener;
}
public void Dispose() {
cToken.Cancel();
}
}
}

View File

@@ -0,0 +1,121 @@
using System;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
using MewtocolNet.Responses;
namespace MewtocolNet {
public static class MewtocolHelpers {
public static Byte[] ToHexASCIIBytes (this string _str) {
ASCIIEncoding ascii = new ASCIIEncoding();
Byte[] bytes = ascii.GetBytes(_str.ToUpper());
return bytes;
}
public static string BuildBCCFrame (this string asciiArr) {
Encoding ae = Encoding.ASCII;
byte[] b = ae.GetBytes(asciiArr);
byte xorTotalByte = 0;
for(int i = 0; i < b.Length; i++)
xorTotalByte ^= b[i];
return asciiArr.Insert(asciiArr.Length, xorTotalByte.ToString("X2"));
}
public static byte[] ParseDTBytes (this string _onString ,int _blockSize = 4) {
var res = new Regex(@"\%([0-9]{2})\$RD(.{"+_blockSize+"})").Match(_onString);
if(res.Success) {
string val = res.Groups[2].Value;
return val.HexStringToByteArray();
}
return null;
}
public static string ParseDTByteString (this string _onString, int _blockSize = 4) {
var res = new Regex(@"\%([0-9]{2})\$RD(.{" + _blockSize + "})").Match(_onString);
if (res.Success) {
string val = res.Groups[2].Value;
return val;
}
return null;
}
public static string ParseDTString (this string _onString) {
var res = new Regex(@"\%([0-9]{2})\$RD.{8}(.*)...").Match(_onString);
if(res.Success) {
string val = res.Groups[2].Value;
return val.GetStringFromAsciiHex();
}
return null;
}
public static string BuildDTString (this string _inString, short _stringReservedSize) {
StringBuilder sb = new StringBuilder();
//06000600
short stringSize = (short)_inString.Length;
var sizeBytes = BitConverter.GetBytes(stringSize).ToHexString();
var reservedSizeBytes = BitConverter.GetBytes(_stringReservedSize).ToHexString();
//reserved string count bytes
sb.Append(reservedSizeBytes);
//string count actual bytes
sb.Append(sizeBytes);
//actual string content
sb.Append(_inString.GetAsciiHexFromString().PadRight(_stringReservedSize * 2, '0'));
return sb.ToString();
}
public static string GetStringFromAsciiHex (this string input) {
if (input.Length % 2 != 0)
throw new ArgumentException("input not a hex string");
byte[] bytes = new byte[input.Length / 2];
for (int i = 0; i < input.Length; i += 2) {
String hex = input.Substring(i, 2);
bytes[i/2] = Convert.ToByte(hex, 16);
}
return Encoding.ASCII.GetString(bytes);
}
public static string GetAsciiHexFromString (this string input) {
var bytes = new ASCIIEncoding().GetBytes(input);
return bytes.ToHexString();
}
public static byte[] HexStringToByteArray(this string hex) {
return Enumerable.Range(0, hex.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
.ToArray();
}
public static string ToHexString (this byte[] arr) {
StringBuilder sb = new StringBuilder();
foreach (var b in arr) {
sb.Append(b.ToString("X2"));
}
return sb.ToString();
}
public static string ToJsonString (this IEnumerable<IBoolContact> _contacts, bool formatPretty = false) {
return JsonSerializer.Serialize(_contacts, new JsonSerializerOptions {
WriteIndented = formatPretty,
});
}
public static bool IsSubclassOfRawGeneric(Type generic, Type toCheck) {
while (toCheck != null && toCheck != typeof(object)) {
var cur = toCheck.IsGenericType ? toCheck.GetGenericTypeDefinition() : toCheck;
if (generic == cur) {
return true;
}
toCheck = toCheck.BaseType;
}
return false;
}
}
}

View File

@@ -0,0 +1,135 @@
using System;
using System.IO;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using MewtocolNet.Responses;
namespace MewtocolNet {
public partial class MewtocolInterface {
/// <summary>
/// Generic information about the connected PLC
/// </summary>
public PLCInfo PlcInfo {get;private set;}
private CancellationTokenSource tokenSource;
private string ip {get;set;}
private int port {get;set;}
public int ConnectionTimeout {get;set;} = 2000;
#region Initialization
/// <summary>
/// Builds a new Interfacer for a PLC
/// </summary>
/// <param name="_ip"></param>
/// <param name="_port"></param>
public MewtocolInterface (string _ip, int _port = 9094) {
ip = _ip;
port = _port;
}
#endregion
#region Low level command handling
/// <summary>
/// Sends a command to the PLC and awaits results
/// </summary>
/// <param name="_msg">MEWTOCOL Formatted request string ex: %01#RT</param>
/// <param name="_close">Auto close of frame [true]%01#RT01\r [false]%01#RT</param>
/// <returns>Returns the result</returns>
public async Task<CommandResult> SendCommandAsync (string _msg) {
_msg = _msg.BuildBCCFrame();
_msg += "\r";
//send request
var response = await SendSingleBlock(_msg);
if(response == null) {
return new CommandResult {
Success = false,
Error = "0000",
ErrorDescription = "null result"
};
}
//error catching
Regex errorcheck = new Regex(@"\%[0-9]{2}\!([0-9]{2})", RegexOptions.IgnoreCase);
Match m = errorcheck.Match(response.ToString());
if (m.Success) {
string eCode = m.Groups[1].Value;
string eDes = Links.LinkedData.ErrorCodes[Convert.ToInt32(eCode)];
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"Response is: {response}");
Console.WriteLine($"Error on command {_msg.Replace("\r", "")} the PLC returned error code: {eCode}, {eDes}");
Console.ResetColor();
return new CommandResult {
Success = false,
Error = eCode,
ErrorDescription = eDes
};
}
return new CommandResult {
Success = true,
Error = "0000",
Response = response.ToString()
};
}
private async Task<string> SendSingleBlock (string _blockString) {
if(isWriting) {
return null;
}
tokenSource = new CancellationTokenSource();
using (TcpClient client = new TcpClient() { ReceiveBufferSize = 64, NoDelay = true, ExclusiveAddressUse = true }) {
try {
await client.ConnectAsync(ip, port, tokenSource.Token);
} catch(SocketException) {
return null;
}
using (NetworkStream stream = client.GetStream()) {
var message = _blockString.ToHexASCIIBytes();
var messageAscii = BitConverter.ToString(message).Replace("-", " ");
//send request
isWriting = true;
using (var sendStream = new MemoryStream(message)) {
await sendStream.CopyToAsync(stream);
//log message sent
ASCIIEncoding enc = new ASCIIEncoding();
string characters = enc.GetString(message);
}
//await result
StringBuilder response = new StringBuilder();
byte[] responseBuffer = new byte[256];
do {
int bytes = stream.Read(responseBuffer, 0, responseBuffer.Length);
response.Append(Encoding.UTF8.GetString(responseBuffer, 0, bytes));
}
while (stream.DataAvailable);
isWriting = false;
return response.ToString();
}
}
}
#endregion
}
}

View File

@@ -0,0 +1,120 @@
using MewtocolNet.Responses;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace MewtocolNet {
public static class MewtocolInterfaceExtensions {
/// <summary>
/// Attaches a continous reader that reads back the Registers and Contacts
/// </summary>
public static Task AttachContinousReader (this Task<MewtocolInterface> interfaceTask, int _refreshTimeMS = 200) {
interfaceTask.Wait(-1);
var interf = interfaceTask.Result;
if (interf.ContinousReaderRunning)
return Task.CompletedTask;
interf.cTokenAutoUpdater = new CancellationTokenSource();
Console.WriteLine("Attaching cont reader");
Task.Factory.StartNew(async () => {
var plcinf = await interf.GetPLCInfoAsync();
if (plcinf == null) {
Console.WriteLine("PLC is not reachable");
throw new Exception("PLC is not reachable");
}
if (!plcinf.OperationMode.RunMode) {
Console.WriteLine("PLC is not running");
throw new Exception("PLC is not running");
}
interf.ContinousReaderRunning = true;
while (true) {
//dont update when currently writing a var
if (interf.isWriting) {
continue;
}
await Task.Delay(_refreshTimeMS);
foreach (var reg in interf.Registers) {
if (reg is NRegister<short> shortReg) {
var lastVal = shortReg.Value;
var readout = (await interf.ReadNumRegister(shortReg)).Register.Value;
if (lastVal != readout) {
shortReg.LastValue = readout;
interf.InvokeRegisterChanged(shortReg);
shortReg.TriggerNotifyChange();
}
}
if (reg is NRegister<ushort> ushortReg) {
var lastVal = ushortReg.Value;
var readout = (await interf.ReadNumRegister(ushortReg)).Register.Value;
if (lastVal != readout) {
ushortReg.LastValue = readout;
interf.InvokeRegisterChanged(ushortReg);
ushortReg.TriggerNotifyChange();
}
}
if (reg is NRegister<int> intReg) {
var lastVal = intReg.Value;
var readout = (await interf.ReadNumRegister(intReg)).Register.Value;
if (lastVal != readout) {
intReg.LastValue = readout;
interf.InvokeRegisterChanged(intReg);
intReg.TriggerNotifyChange();
}
}
if (reg is NRegister<uint> uintReg) {
var lastVal = uintReg.Value;
var readout = (await interf.ReadNumRegister(uintReg)).Register.Value;
if (lastVal != readout) {
uintReg.LastValue = readout;
interf.InvokeRegisterChanged(uintReg);
uintReg.TriggerNotifyChange();
}
}
if (reg is NRegister<float> floatReg) {
var lastVal = floatReg.Value;
var readout = (await interf.ReadNumRegister(floatReg)).Register.Value;
if (lastVal != readout) {
floatReg.LastValue = readout;
interf.InvokeRegisterChanged(floatReg);
floatReg.TriggerNotifyChange();
}
} else if (reg is SRegister stringReg) {
var lastVal = stringReg.Value;
var readout = (await interf.ReadStringRegister(stringReg)).Register.Value;
if (lastVal != readout) {
interf.InvokeRegisterChanged(stringReg);
stringReg.TriggerNotifyChange();
}
}
}
}
}, interf.cTokenAutoUpdater.Token);
return Task.CompletedTask;
}
}
}

View File

@@ -0,0 +1,255 @@
using System;
using System.IO;
using System.Net.Sockets;
using System.Text;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using MewtocolNet.Responses;
using System.Linq;
using System.Globalization;
namespace MewtocolNet {
public partial class MewtocolInterface {
#region High level command handling
/// <summary>
/// Gets generic information about the PLC
/// </summary>
/// <returns>A PLCInfo class</returns>
public async Task<PLCInfo> GetPLCInfoAsync () {
var resu = await SendCommandAsync("%01#RT");
if(!resu.Success) return null;
var reg = new Regex(@"\%([0-9]{2})\$RT([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{4})..", RegexOptions.IgnoreCase);
Match m = reg.Match(resu.Response);
if(m.Success) {
string station = m.Groups[1].Value;
string cpu = m.Groups[2].Value;
string version = m.Groups[3].Value;
string capacity = m.Groups[4].Value;
string operation = m.Groups[5].Value;
string errorflag = m.Groups[7].Value;
string error = m.Groups[8].Value;
PLCInfo retInfo = new PLCInfo {
CpuInformation = PLCInfo.CpuInfo.BuildFromHexString(cpu, version, capacity),
OperationMode = PLCInfo.PLCMode.BuildFromHex(operation),
ErrorCode = error,
StationNumber = int.Parse(station ?? "0"),
};
return retInfo;
}
return null;
}
/// <summary>
/// Reads bool values from the plc by the given <c>Contact</c> List
/// </summary>
/// <param name="_contactsToRead">A list of contacts</param>
/// <param name="_stationNumber">The PLCs station number</param>
/// <returns>List of IBoolContact with unique copys of the given contacts</returns>
public async Task<IEnumerable<IBoolContact>> ReadBoolContacts (List<Contact> _contactsToRead, int _stationNumber = 1) {
//re order by contact pfx for faster querying
_contactsToRead = _contactsToRead.OrderBy(x=>x.Prefix).ToList();
//return list
List<IBoolContact> returnContacts = new List<IBoolContact>();
//grouped by 8 each
List<List<Contact>> nestedContacts = new List<List<Contact>>();
//group into max 8 contacts list
List<Contact> tempGroup = new List<Contact>();
for (int i = 0; i < _contactsToRead.Count; i++) {
tempGroup.Add(_contactsToRead[i]);
//each 8 contacts make a new list
if(i % 7 == 0 && i != 0 && i != _contactsToRead.Count) {
nestedContacts.Add(tempGroup);
tempGroup = new List<Contact>();
}
//if end of list and contacts cannot be broke down to 8 each group
if(i == _contactsToRead.Count - 1 && _contactsToRead.Count % 8 != 0) {
nestedContacts.Add(tempGroup);
tempGroup = new List<Contact>();
}
}
//make task for each group
foreach (var group in nestedContacts) {
//regex for getting values
StringBuilder regexString = new StringBuilder(@"\%..\$RC");
//append start %01#RCP2
StringBuilder messageString = new StringBuilder();
messageString.Append($"%{_stationNumber.ToString().PadLeft(2, '0')}#RCP");
messageString.Append($"{group.Count}");
//append each contact of group Y0000 Y0001 etc
foreach (var cont in group) {
messageString.Append(cont.BuildMewtocolIdent());
regexString.Append(@"([0-9])");
}
regexString.Append(@"(..)");
//parse the result
var result = await SendCommandAsync(messageString.ToString());
Regex regCheck = new Regex(regexString.ToString(), RegexOptions.IgnoreCase);
if(result.Success && regCheck.IsMatch(result.Response)) {
//parse result string
Match regMatch = regCheck.Match(result.Response);
// add to return list
for (int i = 0; i < group.Count; i++) {
Contact cont = group[i].ShallowCopy();
Contact toadd = cont;
if( regMatch.Groups[i + 1].Value == "1" ) {
toadd.Value = true;
} else if( regMatch.Groups[i + 1].Value == "0" ) {
toadd.Value = false;
}
returnContacts.Add(toadd);
}
}
}
return returnContacts;
}
/// <summary>
/// Writes a boolen value to the given contact
/// </summary>
/// <param name="_contact">The contact to write</param>
/// <param name="_value">The boolean state to write</param>
/// <param name="_stationNumber">Station Number (optional)</param>
/// <returns>A result struct</returns>
public async Task<CommandResult> WriteContact (Contact _contact, bool _value, int _stationNumber = 1) {
string stationNum = _stationNumber.ToString().PadLeft(2, '0');
string dataArea = _contact.BuildMewtocolIdent();
string dataString = _value ? "1" : "0";
string requeststring = $"%{stationNum}#WCS{dataArea}{dataString}";
var res = await SendCommandAsync(requeststring);
return res;
}
/// <summary>
/// Reads the given numeric register from PLC
/// </summary>
/// <typeparam name="T">Type of number (short, ushort, int, uint, float)</typeparam>
/// <param name="_toRead">The register to read</param>
/// <param name="_stationNumber">Station number to access</param>
/// <returns>A result with the given NumberRegister containing the readback value and a result struct</returns>
public async Task<NRegisterResult<T>> ReadNumRegister<T> (NRegister<T> _toRead, int _stationNumber = 1) {
Type numType = typeof(T);
string requeststring = $"%{_stationNumber.ToString().PadLeft(2, '0')}#RD{_toRead.BuildMewtocolIdent()}";
var result = await SendCommandAsync(requeststring);
if (numType == typeof(short)) {
var resultBytes = result.Response.ParseDTByteString(4);
var val = short.Parse(resultBytes, NumberStyles.HexNumber);
(_toRead as NRegister<short>).LastValue = val;
} else if (numType == typeof(ushort)) {
var resultBytes = result.Response.ParseDTBytes(4);
var val = BitConverter.ToInt16(resultBytes);
_toRead.Value = (T)Convert.ChangeType(val, typeof(T));
} else if (numType == typeof(int)) {
var resultBytes = result.Response.ParseDTBytes(8);
var val = BitConverter.ToInt16(resultBytes);
_toRead.Value = (T)Convert.ChangeType(val, typeof(T));
} else if (numType == typeof(uint)) {
var resultBytes = result.Response.ParseDTBytes(8);
var val = BitConverter.ToInt16(resultBytes);
_toRead.Value = (T)Convert.ChangeType(val, typeof(T));
} else if (numType == typeof(float)) {
var resultBytes = result.Response.ParseDTBytes(8);
var val = BitConverter.ToSingle(resultBytes);
_toRead.Value = (T)Convert.ChangeType(val, typeof(T));
}
var finalRes = new NRegisterResult<T> {
Result = result,
Register = _toRead
};
return finalRes;
}
/// <summary>
/// Reads the given numeric register from PLC
/// </summary>
/// <typeparam name="T">Type of number (short, ushort, int, uint, float)</typeparam>
/// <param name="_toWrite">The register to write</param>
/// <param name="_stationNumber">Station number to access</param>
/// <returns>A result with the given NumberRegister and a result struct</returns>
public async Task<NRegisterResult<T>> WriteNumRegister<T>(NRegister<T> _toWrite, T _value, int _stationNumber = 1) {
byte[] toWriteVal;
Type numType = typeof(T);
if (numType == typeof(short)) {
toWriteVal = BitConverter.GetBytes(Convert.ToInt16(_value));
} else if (numType == typeof(ushort)) {
toWriteVal = BitConverter.GetBytes(Convert.ToUInt16(_value));
} else if (numType == typeof(int)) {
toWriteVal = BitConverter.GetBytes(Convert.ToInt32(_value));
} else if (numType == typeof(uint)) {
toWriteVal = BitConverter.GetBytes(Convert.ToUInt32(_value));
} else if (numType == typeof(float)) {
toWriteVal = BitConverter.GetBytes(Convert.ToUInt32(_value));
} else {
toWriteVal = null;
}
string requeststring = $"%{_stationNumber.ToString().PadLeft(2, '0')}#WD{_toWrite.BuildMewtocolIdent()}{toWriteVal.ToHexString()}";
var result = await SendCommandAsync(requeststring);
return new NRegisterResult<T> {
Result = result,
Register = _toWrite
};
}
public async Task<SRegisterResult> ReadStringRegister (SRegister _toRead, int _stationNumber = 1) {
string requeststring = $"%{_stationNumber.ToString().PadLeft(2, '0')}#RD{_toRead.BuildMewtocolIdent()}";
var result = await SendCommandAsync(requeststring);
if (result.Success)
_toRead.SetValueFromPLC(result.Response.ParseDTString());
return new SRegisterResult {
Result = result,
Register = _toRead
};
}
public async Task<SRegisterResult> WriteStringRegister(SRegister _toWrite, string _value, int _stationNumber = 1) {
if (_value == null) _value = "";
if(_value.Length > _toWrite.ReservedSize) {
throw new ArgumentException("Write string size cannot be longer than reserved string size");
}
string stationNum = _stationNumber.ToString().PadLeft(2, '0');
string dataArea = _toWrite.BuildMewtocolIdent();
string dataString = _value.BuildDTString(_toWrite.ReservedSize);
string requeststring = $"%{stationNum}#WD{dataArea}{dataString}";
Console.WriteLine($"reserved: {_toWrite.MemoryLength}, size: {_value.Length}");
var result = await SendCommandAsync(requeststring);
return new SRegisterResult {
Result = result,
Register = _toWrite
};
}
#endregion
}
}

View File

@@ -0,0 +1,183 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MewtocolNet.Responses {
/// <summary>
/// A class describing a register
/// </summary>
public abstract class Register : INotifyPropertyChanged {
/// <summary>
/// Gets called whenever the value was changed
/// </summary>
public event Action<object> ValueChanged;
public event PropertyChangedEventHandler PropertyChanged;
public string Name { get; set; }
public int MemoryAdress { get; set; }
public int MemoryLength { get; set; }
public virtual string BuildMewtocolIdent() {
StringBuilder asciistring = new StringBuilder("D");
asciistring.Append(MemoryAdress.ToString().PadLeft(5, '0'));
asciistring.Append((MemoryAdress + MemoryLength).ToString().PadLeft(5, '0'));
return asciistring.ToString();
}
protected void TriggerChangedEvnt(object changed) {
ValueChanged?.Invoke(changed);
}
public void TriggerNotifyChange () {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Value"));
}
public string GetValueString () {
if (this is NRegister<short> shortReg) {
return shortReg.Value.ToString();
}
if (this is NRegister<ushort> ushortReg) {
return ushortReg.Value.ToString();
}
if (this is NRegister<int> intReg) {
return intReg.Value.ToString();
}
if (this is NRegister<uint> uintReg) {
return uintReg.Value.ToString();
}
if (this is NRegister<float> floatReg) {
return floatReg.Value.ToString();
}
else if (this is SRegister stringReg) {
return stringReg.Value.ToString();
}
return "Type of the register is not supported.";
}
}
/// <summary>
/// Defines a register containing a number
/// </summary>
/// <typeparam name="T">The type of the numeric value</typeparam>
public class NRegister<T> : Register {
public T NeedValue;
public T LastValue;
/// <summary>
/// The value of the register
/// </summary>
public T Value {
get => LastValue;
set {
NeedValue = value;
TriggerChangedEvnt(this);
}
}
/// <summary>
/// Defines a register containing a number
/// </summary>
/// <param name="_adress">Memory start adress max 99999</param>
/// <param name="_format">The format in which the variable is stored</param>
public NRegister(int _adress, string _name = null) {
if (_adress > 99999) throw new NotSupportedException("Memory adresses cant be greater than 99999");
MemoryAdress = _adress;
Name = _name;
Type numType = typeof(T);
if (numType == typeof(short)) {
MemoryLength = 0;
} else if (numType == typeof(ushort)) {
MemoryLength = 0;
} else if (numType == typeof(int)) {
MemoryLength = 1;
} else if (numType == typeof(uint)) {
MemoryLength = 1;
} else if (numType == typeof(float)) {
MemoryLength = 1;
} else {
throw new NotSupportedException($"The type {numType} is not allowed for Number Registers");
}
}
public override string ToString() {
return $"Adress: {MemoryAdress} Val: {Value}";
}
}
/// <summary>
/// Result for a read/write operation
/// </summary>
/// <typeparam name="T">The type of the numeric value</typeparam>
public class NRegisterResult<T> {
public CommandResult Result { get; set; }
public NRegister<T> Register { get; set; }
public override string ToString() {
string errmsg = Result.Success ? "" : $", Error [{Result.ErrorDescription}]";
return $"Result [{Result.Success}], Register [{Register.ToString()}]{errmsg}";
}
}
/// <summary>
/// Defines a register containing a string
/// </summary>
public class SRegister : Register {
private string lastVal = "";
public string Value {
get => lastVal;
}
public short ReservedSize { get; set; }
/// <summary>
/// Defines a register containing a string
/// </summary>
public SRegister(int _adress, int _reservedStringSize, string _name = null) {
if (_adress > 99999) throw new NotSupportedException("Memory adresses cant be greater than 99999");
Name = _name;
MemoryAdress = _adress;
ReservedSize = (short)_reservedStringSize;
MemoryLength = 1 + (_reservedStringSize) / 2;
}
public override string ToString() {
return $"Adress: {MemoryAdress} Val: {Value}";
}
public override string BuildMewtocolIdent() {
StringBuilder asciistring = new StringBuilder("D");
asciistring.Append(MemoryAdress.ToString().PadLeft(5, '0'));
asciistring.Append((MemoryAdress + MemoryLength).ToString().PadLeft(5, '0'));
return asciistring.ToString();
}
public void SetValueFromPLC (string val) {
lastVal = val;
TriggerChangedEvnt(this);
TriggerNotifyChange();
}
}
public class SRegisterResult {
public CommandResult Result { get; set; }
public SRegister Register { get; set; }
public override string ToString() {
string errmsg = Result.Success ? "" : $", Error [{Result.ErrorDescription}]";
return $"Result [{Result.Success}], Register [{Register.ToString()}]{errmsg}";
}
}
}

View File

@@ -0,0 +1,343 @@
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Linq;
using System.Text;
using System.ComponentModel;
namespace MewtocolNet.Responses {
/// <summary>
/// The formatted result of a ascii command
/// </summary>
public struct CommandResult {
public bool Success {get;set;}
public string Response {get;set;}
public string Error {get;set;}
public string ErrorDescription {get;set;}
public override string ToString() {
string errmsg = Success ? "" : ErrorDescription;
return $"Success: {Success}, Response: {Response} {errmsg}";
}
}
/// <summary>
/// Contains generic information about the plc
/// </summary>
public class PLCInfo {
public class PLCMode {
public bool RunMode {get;set;}
public bool TestRunMode {get;set;}
public bool BreakExcecuting {get;set;}
public bool BreakValid {get;set;}
public bool OutputEnabled {get;set;}
public bool StepRunMode {get;set;}
public bool MessageExecuting {get;set;}
public bool RemoteMode {get;set;}
/// <summary>
/// Gets operation mode from 2 digit hex number
/// </summary>
public static PLCMode BuildFromHex (string _hexString) {
string lower = Convert.ToString(Convert.ToInt32(_hexString.Substring(0, 1)), 2).PadLeft(4, '0');
string higher = Convert.ToString(Convert.ToInt32(_hexString.Substring(1, 1)), 2).PadLeft(4, '0');
string combined = lower + higher;
var retMode = new PLCMode();
for (int i = 0; i < 8; i++) {
char digit = combined[i];
bool state = false;
if(digit.ToString() == "1") state = true;
switch (i) {
case 0 :
retMode.RunMode = state;
break;
case 1 :
retMode.TestRunMode = state;
break;
case 2 :
retMode.BreakExcecuting = state;
break;
case 3 :
retMode.BreakValid = state;
break;
case 4 :
retMode.OutputEnabled = state;
break;
case 5 :
retMode.StepRunMode = state;
break;
case 6 :
retMode.MessageExecuting = state;
break;
case 7 :
retMode.RemoteMode = state;
break;
}
}
return retMode;
}
}
public class CpuInfo {
public enum CpuType {
FP0_FP1_2_7K,
FP0_FP1_5K_10K,
FP1_M_0_9K,
FP2_16K_32K,
FP3_C_10K,
FP3_C_16K,
FP5_16K,
FP5_24K,
FP_Sigma_X_H_30K_60K_120K
}
public CpuType Cputype {get;set;}
public int ProgramCapacity {get;set;}
public string CpuVersion {get;set;}
public static CpuInfo BuildFromHexString (string _cpuType, string _cpuVersion, string _progCapacity) {
CpuInfo retInf = new CpuInfo();
switch (_cpuType) {
case "02":
retInf.Cputype = CpuType.FP5_16K;
break;
case "03":
retInf.Cputype = CpuType.FP3_C_10K;
break;
case "04":
retInf.Cputype = CpuType.FP1_M_0_9K;
break;
case "05":
retInf.Cputype = CpuType.FP0_FP1_2_7K;
break;
case "06":
retInf.Cputype = CpuType.FP0_FP1_5K_10K;
break;
case "12":
retInf.Cputype = CpuType.FP5_24K;
break;
case "13":
retInf.Cputype = CpuType.FP3_C_16K;
break;
case "20":
retInf.Cputype = CpuType.FP_Sigma_X_H_30K_60K_120K;
break;
case "50":
retInf.Cputype = CpuType.FP2_16K_32K;
break;
}
retInf.ProgramCapacity = Convert.ToInt32(_progCapacity);
retInf.CpuVersion = _cpuVersion.Insert(1, ".");
return retInf;
}
}
public CpuInfo CpuInformation {get;set;}
public PLCMode OperationMode {get;set;}
public string ErrorCode {get;set;}
public int StationNumber { get;set;}
public override string ToString () {
return $"Type: {CpuInformation.Cputype},\n" +
$"Capacity: {CpuInformation.ProgramCapacity}k\n" +
$"CPU v: {CpuInformation.CpuVersion}\n" +
$"Station Num: {StationNumber}\n" +
$"--------------------------------\n" +
$"OP Mode: {(OperationMode.RunMode ? "Run" : "Prog")}\n" +
$"Error Code: {ErrorCode}";
}
}
/// <summary>
/// Contact as bool contact
/// </summary>
public interface IBoolContact {
public string Name {get;set;}
public string Identifier {get;}
public bool? Value {get;set;}
}
/// <summary>
/// A class describing a PLC contact
/// </summary>
public class Contact : IBoolContact {
public string Name {get;set;}
public PFX Prefix {get;set;}
public int Number {get;set;}
public ContactType Type {get;set;}
public string Endprefix {get;set;}
public string Asciistring {get => BuildMewtocolIdent();}
public string Identifier {get => Asciistring;}
public bool? Value {get;set;} = null;
public enum ContactType {
Unknown,
Input,
Output,
}
public enum PFX {
X,
Y,
R
}
/// <summary>
/// Creates a new base Contact
/// </summary>
/// <param name="_prefix">A prefix identifier eg. X,Y,R,L</param>
/// <param name="_number">The number of the PLC contact</param>
public Contact (PFX _prefix, int _number, string _name = "unknown") {
switch (_prefix) {
case PFX.X:
Type = ContactType.Input;
break;
case PFX.Y:
case PFX.R:
Type = ContactType.Output;
break;
}
Prefix = _prefix;
Number = _number;
Name = _name;
}
/// <summary>
/// Creates a new base Contact
/// </summary>
/// <param name="_prefix">A prefix identifier eg. X,Y,R,L</param>
/// <param name="_number">The number of the PLC contact</param>
public Contact (string _prefix, int _number, string _name = "unknown") {
PFX parsedPFX;
if(Enum.TryParse<PFX>(_prefix, true, out parsedPFX)) {
switch (parsedPFX) {
case PFX.X:
Type = ContactType.Input;
break;
case PFX.Y:
case PFX.R:
Type = ContactType.Output;
break;
}
Prefix = parsedPFX;
Number = _number;
Name = _name;
} else {
throw new ArgumentException($"The prefix {_prefix} is no valid contact prefix");
}
}
/// <summary>
/// Build contact from complete contact name
/// </summary>
/// <param name="_contactName">Complete contact name e.g. Y1C, Y3D or X1</param>
public Contact (string _contactName, string _name = "unknown") {
string prefix = "";
int number = 0;
string endpfx = null;
Match regcheck = new Regex(@"(Y|X|R|L)([0-9]{1,3})?(.)?", RegexOptions.IgnoreCase).Match(_contactName);
if(regcheck.Success) {
/* for (int i = 0; i < regcheck.Groups.Count; i++) {
var item = regcheck.Groups[i].Value;
Console.WriteLine(item);
} */
prefix = regcheck.Groups[1].Value;
number = regcheck.Groups[2]?.Value != string.Empty ? Convert.ToInt32(regcheck.Groups[2].Value) : -1;
endpfx = regcheck.Groups[3]?.Value;
} else {
throw new ArgumentException($"The contact {_contactName} is no valid contact");
}
PFX parsedPFX;
if(Enum.TryParse<PFX>(prefix, true, out parsedPFX)) {
switch (parsedPFX) {
case PFX.X:
Type = ContactType.Input;
break;
case PFX.Y:
case PFX.R:
Type = ContactType.Output;
break;
}
Prefix = parsedPFX;
Number = number;
Endprefix = endpfx;
Name = _name;
} else {
throw new ArgumentException($"The prefix {prefix} is no valid contact prefix");
}
Console.WriteLine(BuildMewtocolIdent());
}
/// <summary>
/// Builds the mewtocol ascii contact identifier
/// </summary>
/// <returns>The identifier e.g. Y0001 or Y000A or X001C</returns>
public string BuildMewtocolIdent () {
string contactstring = "";
if(Endprefix == null) {
contactstring += Prefix;
contactstring += Number.ToString().PadLeft(4, '0');
} else {
contactstring += Prefix;
if(Number == -1) {
contactstring += "000" + Endprefix;
} else {
contactstring += (Number.ToString() + Endprefix).PadLeft(4, '0');
}
}
if(string.IsNullOrEmpty(contactstring)) {
return null;
}
return contactstring;
}
/// <summary>
/// Converts the class to a generic json compatible object
/// </summary>
/// <returns></returns>
public object ToGenericObject () {
return new {
Name = this.Name,
Identifier = this.Asciistring,
Value = this.Value
};
}
/// <summary>
/// Creates a copy of the contact
/// </summary>
public Contact ShallowCopy() {
return (Contact) this.MemberwiseClone();
}
}
}

View File

@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<PackageId>AppLogger</PackageId>
<Version>0.1.5</Version>
<Authors>Felix Weiss</Authors>
<Company>Womed</Company>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DocumentationFile>P:\QUELLEN\PRODUKTAKTE\DESIGN_V_V\Software SPS\Projekt FP-XH\MewtocolNet\Builds\MewtocolNet.xml</DocumentationFile>
<OutputPath>P:\QUELLEN\PRODUKTAKTE\DESIGN_V_V\Software SPS\Projekt FP-XH\MewtocolNet\Builds\</OutputPath>
</PropertyGroup>
</Project>