This commit is contained in:
Felix Weiß
2023-08-21 16:10:17 +02:00

197
README.md
View File

@@ -1,28 +1,47 @@
[![Publish pipeline](https://github.com/WOmed/MewtocolNet/actions/workflows/publish-pipeline.yml/badge.svg)](https://github.com/WOmed/MewtocolNet/actions/workflows/publish-pipeline.yml)
[![Test pipeline](https://github.com/WOmed/MewtocolNet/actions/workflows/test-pipeline.yml/badge.svg)](https://github.com/WOmed/MewtocolNet/actions/workflows/test-pipeline.yml)
[![GitHub tag](https://img.shields.io/github/v/tag/WOmed/MewtocolNet?label=Package%20Version)](https://github.com/WOmed/MewtocolNet/pkgs/nuget/Mewtocol.NET)
[![gencov](https://github.com/WOmed/MewtocolNet/blob/badges/Builds/TestResults/badge_combined_master.svg)](https://htmlpreview.github.io/?https://github.com/WOmed/MewtocolNet/blob/badges/Builds/TestResults/summary_master.html)
[![gencov](../badges/Builds/TestResults/badge_combined_master.svg)](https://htmlpreview.github.io/?https://github.com/WOmed/MewtocolNet/blob/badges/Builds/TestResults/summary_master.html)
![GitHub](https://img.shields.io/github/license/WOmed/MewtocolNet?label=License)
![Status](https://img.shields.io/badge/Status-In%20dev-orange)
![Status](https://img.shields.io/badge/Status-Stable-orange)
# MewtocolNet
An easy to use Mewtocol protocol library to interface with Panasonic PLCs over TCP/Serial.
## Disclaimer
This library is not an official panasonic product nor does panasonic provide financial support or limitations in any form.
This software was written by WOLF Medizintechnik GmbH (@WOmed/dev).
> ⚠️ This library is not an official panasonic product nor does panasonic provide financial support or limitations in any form.
> This software was written by WOLF Medizintechnik GmbH (@WOmed/dev).
# PLC Support
## For a full list check [this table](../master_auto_docs/plctypes.md)
> This library was only tested with a few PLCs, other types that support the Panasonic Mewtocol protocol might work.
> Use at your own risk, others might follow with community feedback
# Features
> Features that are not checked still need implementation
## Fully implemented
- [x] Read out stats from your PLC
- [x] TCP/IP and Serial Port support
- [x] Get type and hardware information of PLCs
- [x] Get PLC program metadata such as program version and IDs
- [x] Read and write registers in real time
- [x] Dynamic register type casting from properties
- [x] Change run / prog modes
- [x] Write / read byte blocks in a whole chain
- [x] Basic data types / structures support
- [x] Fast readback cycles due to a MemoryManager that optimizes TCP / Serial frames by combining areas
- [x] Fully customizable heartbeats and polling levels (tell the interface when you need register updates)
- [x] Easy to use builder patterns for interface and register generation
- [x] Register type casting from property attributes
- [x] Change RUN / PROG modes
- [x] Delete Programs
- [x] Write / read low level byte blocks to areas
- [x] Scanning for network devices and change network settings (WDConfigurator features)
# Planned
- [ ] Upload / Download programs to the PLC
- [ ] Reading / writing PLC system registers
- [ ] Advanced data structures like SDTs and SDT Arrays
- [ ] Custom open source program compiler for PLC cpus
# Support
@@ -32,18 +51,7 @@ This library was written in **netstandard2.0** and should be compatible with a l
For a full list of supported .NET clrs see [this page](https://docs.microsoft.com/de-de/dotnet/standard/net-standard?tabs=net-standard-2-0#select-net-standard-version)
## PLC Support
> This library was only tested with a few PLCs, other types that support the Panasonic Mewtocol protocol might work.
> Use at your own risk, others might follow with community feedback
For a **Support List** check [this table](AutoTools.DocBuilder/Docs/plctypes.md)
Where is the RS232/Serial support?
> Support for the serial protocol will be added soon, feel free to contribute
# Installing
# Installation
Use the dotnet CLI and run
```Shell
@@ -57,51 +65,56 @@ Refer to this site if you want to see the general functionality or add / report
> This library is at the time not feature complete, but all essential features are provided
# Usage
# Examples
See [More examples](/Examples) here
To see a full list of examples [click here](/Examples).
## Connecting to a PLC
Connecting to a PLC is as simple as
```C#
//attaching a logger
Logger.LogLevel = LogLevel.Verbose;
Logger.OnNewLogMessage((date, msg) => {
Console.WriteLine($"{date.ToString("HH:mm:ss")} {msg}");
});
using MewtocolNet;
//setting up a new PLC interface
MewtocolInterface plc = new MewtocolInterface("192.168.115.5");
using (var plc = Mewtocol.Ethernet("192.168.178.55").Build()) {
await plc.ConnectAsync();
await plc.ConnectAsync();
if (!plc.IsConnected) {
Console.WriteLine("Failed to connect to the plc...");
} else {
Console.WriteLine(plc.PlcInfo);
}
}
```
## Reading data registers / contacts
[Detailed instructions](https://github.com/WOmed/MewtocolNet/wiki/Attribute-handled-reading)
- Create a new class that inherits from `RegisterCollectionBase`
- Create a new class that inherits from `RegisterCollection`
```C#
public class TestRegisters : RegisterCollectionBase {
using MewtocolNet;
using MewtocolNet.RegisterAttributes;
public class TestRegisters : RegisterCollection {
//corresponds to a R100 boolean register in the PLC
[Register(100, RegisterType.R)]
[Register("R100")]
public bool TestBool1 { get; private set; }
//corresponds to a XD input of the PLC
[Register(RegisterType.X, SpecialAddress.D)]
[Register("XD")]
public bool TestBoolInputXD { get; private set; }
//corresponds to a DT7012 - DT7013 as a 32bit time value that gets parsed as a timespan (TIME)
//corresponds to a DDT7012 - DDT7013 as a 32bit time value that gets parsed as a timespan (TIME)
//the smallest value to communicate to the PLC is 10ms
[Register(7012)]
[Register("DDT7012")]
public TimeSpan TestTime { get; private set; }
//corresponds to a DT1101 - DT1104 string register in the PLC with (STRING[4])
[Register(1101, 4)]
[Register("DT1101", "STRING[4]")]
public string TestString1 { get; private set; }
}
@@ -111,80 +124,68 @@ public class TestRegisters : RegisterCollectionBase {
- attach an automatic poller by chaining `.WithPoller()` after the register attachment
```C#
//setting up a new PLC interface and register collection
MewtocolInterface plc = new MewtocolInterface("192.168.115.5");
TestRegisters registers = new TestRegisters();
TestRegisters registers = null;
//attaching the register collection and an automatic poller
plc.WithRegisterCollection(registers).WithPoller();
//setting up a new PLC serial interface and tell it to use the register collection
var plc = Mewtocol.Serial("COM4", BaudRate._19200)
.WithPoller()
.WithRegisterCollections(c => {
registers = c.AddCollection<TestRegisters>();
// or use
// c.AddCollection(new TestRegisters());
// if you want to pass data to a constructor
})
.Build();
//connect to it
await plc.ConnectAsync(async () => {
//restart the plc program during the connection process
await plc.RestartProgramAsync();
});
//wait for the first data cycle of the poller module
//otherwise the property value might still be unset or null
await App.ViewModel.Plc.AwaitFirstDataCycleAsync();
if (App.ViewModel.Plc.IsConnected) {
Console.WriteLine(registers.TestBool1);
}
await plc.ConnectAsync(
(plcinf) => {
//reading a value from the register collection
Console.WriteLine($"Time Value is: {registers.TestTime}");
}
);
```
- Your properties are getting automatically updated after the initial connection
> Note! this is not your only option to read registers, see here
## Writing data registers / contacts
## Reading & Writing
Registers are stored in an underlying layer for automatic handling, each register has a unique name and address.
In addition to the automatic property binding you can use these patterns:
Classes that derive from `RegisterCollectionBase` reference these registers automatically using attributes.
All the heavy lifting is done automatically for you, setting this up is described [here](https://github.com/WOmed/MewtocolNet/wiki/Attribute-handled-reading)
### Asynchronous
This operations awaits a task to make sure the register was actually set to your desired value before progressing
### Reading & Writing by using the anonymous builder pattern
```C#
//sets the register to false
await plc.SetRegisterAsync(nameof(registers.TestBool1), false);
await plc.Register.Struct<short>("DT100").WriteAsync(100);
//set the current second to the PLCs TIME register
await plc.SetRegisterAsync(nameof(registers.TestTime), TimeSpan.FromSeconds(DateTime.Now.Second));
var value = await plc.Register.Struct<short>("DT100").ReadAsync();
```
### Synchronous
Sets the register without feedback if it was set
You can use the method to set a register
### Reading & Writing by using the direct reference from the builder pattern
```C#
//inverts the boolean register
plc.SetRegister(nameof(registers.TestBool1), !registers.TestBool1);
//set the current second to the PLCs TIME register
plc.SetRegister(nameof(registers.TestTime), TimeSpan.FromSeconds(DateTime.Now.Second));
IRegister<bool> outputContactReference;
//writes 'Test' to the PLCs string register
plc.SetRegister(nameof(registers.TestString1), "Test");
```
or write to a register in your `RegisterCollectionBase` directly (you need to attach a register collection to your interface beforehand)
```C#
//inverts the boolean register
registers.TestBool1 = true;
```
You can also set a register by calling its name directly (Must be either in an attached register collection or added to the list manually)
Adding registers to a manual list
```C#
plc.AddRegister<bool>(105, _name: "ManualBoolRegister");
```
Reading the value of the manually added register
```C#
//get the value as a string
string value = plc.GetRegister("ManualBoolRegister").GetValueString();
//get the value by casting
bool value2 = plc.GetRegister<BRegister>("ManualBoolRegister").Value;
//for double casted ones like numbers
var value2 = plc.GetRegister<NRegister<short>>("NumberRegister").Value;
var plc = Mewtocol.Ethernet("127.0.0.1")
.WithRegisters(b => {
b.Bool("Y4").Build(out outputContactReference);
})
.Build();
await plc.ConnectAsync();
await outputContactReference.WriteAsync(true);
```