From 614426207c75d7d1593cd604758e6285e7c15ba3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Wei=C3=9F?= <72068105+Sandoun@users.noreply.github.com> Date: Tue, 21 Jun 2022 14:15:07 +0200 Subject: [PATCH] Added missing raw byte array writing reading - added ability to change run/prog mode --- Examples/Program.cs | 3 + MewtocolNet/Mewtocol/CpuInfo.cs | 44 +----- MewtocolNet/Mewtocol/DynamicInterface.cs | 18 +-- MewtocolNet/Mewtocol/MewtocolHelpers.cs | 31 ++++- MewtocolNet/Mewtocol/MewtocolInterface.cs | 18 +-- .../Mewtocol/MewtocolInterfaceRequests.cs | 126 +++++++++++++++--- MewtocolNet/Mewtocol/PLCEnums/CpuType.cs | 46 +++++++ MewtocolNet/Mewtocol/PLCEnums/OPMode.cs | 21 +++ MewtocolNet/MewtocolNet.csproj | 9 +- 9 files changed, 236 insertions(+), 80 deletions(-) create mode 100644 MewtocolNet/Mewtocol/PLCEnums/CpuType.cs create mode 100644 MewtocolNet/Mewtocol/PLCEnums/OPMode.cs diff --git a/Examples/Program.cs b/Examples/Program.cs index de1c005..f142896 100644 --- a/Examples/Program.cs +++ b/Examples/Program.cs @@ -33,6 +33,9 @@ namespace Examples { //writing a value to the registers Task.Factory.StartNew(async () => { + //set plc to run mode if not already + await interf.SetOperationMode(OPMode.Run); + await Task.Delay(2000); //inverts the boolean register diff --git a/MewtocolNet/Mewtocol/CpuInfo.cs b/MewtocolNet/Mewtocol/CpuInfo.cs index ff6ecc8..124ce2a 100644 --- a/MewtocolNet/Mewtocol/CpuInfo.cs +++ b/MewtocolNet/Mewtocol/CpuInfo.cs @@ -1,49 +1,7 @@ using System; namespace MewtocolNet.Registers { - public class CpuInfo { - /// - /// CPU type of the PLC - /// - public enum CpuType { - /// - /// FP 0 / FP 2.7K - /// - FP0_FP1_2_7K, - /// - /// FP0 / FP1, 5K / 10K - /// - FP0_FP1_5K_10K, - /// - /// FP1 M 0.9K - /// - FP1_M_0_9K, - /// - /// FP2 16k / 32k - /// - FP2_16K_32K, - /// - /// FP3 C 10K - /// - FP3_C_10K, - /// - /// FP3 C 16K - /// - FP3_C_16K, - /// - /// FP5 16K - /// - FP5_16K, - /// - /// FP 5 24K - /// - FP5_24K, - /// - /// Includes panasonic FPX, FPX-H, Sigma - /// - FP_Sigma_X_H_30K_60K_120K - - } + public partial class CpuInfo { public CpuType Cputype { get; set; } public int ProgramCapacity { get; set; } diff --git a/MewtocolNet/Mewtocol/DynamicInterface.cs b/MewtocolNet/Mewtocol/DynamicInterface.cs index 266fee9..03880ef 100644 --- a/MewtocolNet/Mewtocol/DynamicInterface.cs +++ b/MewtocolNet/Mewtocol/DynamicInterface.cs @@ -24,7 +24,7 @@ namespace MewtocolNet { internal void KillPoller () { ContinousReaderRunning = false; - cTokenAutoUpdater.Cancel(); + cTokenAutoUpdater?.Cancel(); } @@ -86,7 +86,7 @@ namespace MewtocolNet { if (reg is NRegister shortReg) { var lastVal = shortReg.Value; - var readout = (await ReadNumRegister(shortReg, stationNumber)).Register.Value; + var readout = (await ReadNumRegister(shortReg)).Register.Value; if (lastVal != readout) { shortReg.LastValue = readout; InvokeRegisterChanged(shortReg); @@ -95,7 +95,7 @@ namespace MewtocolNet { } if (reg is NRegister ushortReg) { var lastVal = ushortReg.Value; - var readout = (await ReadNumRegister(ushortReg, stationNumber)).Register.Value; + var readout = (await ReadNumRegister(ushortReg)).Register.Value; if (lastVal != readout) { ushortReg.LastValue = readout; InvokeRegisterChanged(ushortReg); @@ -104,7 +104,7 @@ namespace MewtocolNet { } if (reg is NRegister intReg) { var lastVal = intReg.Value; - var readout = (await ReadNumRegister(intReg, stationNumber)).Register.Value; + var readout = (await ReadNumRegister(intReg)).Register.Value; if (lastVal != readout) { intReg.LastValue = readout; InvokeRegisterChanged(intReg); @@ -113,7 +113,7 @@ namespace MewtocolNet { } if (reg is NRegister uintReg) { var lastVal = uintReg.Value; - var readout = (await ReadNumRegister(uintReg, stationNumber)).Register.Value; + var readout = (await ReadNumRegister(uintReg)).Register.Value; if (lastVal != readout) { uintReg.LastValue = readout; InvokeRegisterChanged(uintReg); @@ -122,7 +122,7 @@ namespace MewtocolNet { } if (reg is NRegister floatReg) { var lastVal = floatReg.Value; - var readout = (await ReadNumRegister(floatReg, stationNumber)).Register.Value; + var readout = (await ReadNumRegister(floatReg)).Register.Value; if (lastVal != readout) { floatReg.LastValue = readout; InvokeRegisterChanged(floatReg); @@ -131,7 +131,7 @@ namespace MewtocolNet { } if (reg is NRegister tsReg) { var lastVal = tsReg.Value; - var readout = (await ReadNumRegister(tsReg, stationNumber)).Register.Value; + var readout = (await ReadNumRegister(tsReg)).Register.Value; if (lastVal != readout) { tsReg.LastValue = readout; InvokeRegisterChanged(tsReg); @@ -140,7 +140,7 @@ namespace MewtocolNet { } if (reg is BRegister boolReg) { var lastVal = boolReg.Value; - var readout = (await ReadBoolRegister(boolReg, stationNumber)).Register.Value; + var readout = (await ReadBoolRegister(boolReg)).Register.Value; if (lastVal != readout) { boolReg.LastValue = readout; InvokeRegisterChanged(boolReg); @@ -149,7 +149,7 @@ namespace MewtocolNet { } if (reg is SRegister stringReg) { var lastVal = stringReg.Value; - var readout = (await ReadStringRegister(stringReg, stationNumber)).Register.Value; + var readout = (await ReadStringRegister(stringReg)).Register.Value; if (lastVal != readout) { InvokeRegisterChanged(stringReg); stringReg.TriggerNotifyChange(); diff --git a/MewtocolNet/Mewtocol/MewtocolHelpers.cs b/MewtocolNet/Mewtocol/MewtocolHelpers.cs index ae1dbb1..5868c63 100644 --- a/MewtocolNet/Mewtocol/MewtocolHelpers.cs +++ b/MewtocolNet/Mewtocol/MewtocolHelpers.cs @@ -159,19 +159,42 @@ namespace MewtocolNet { internal 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(); + .Where(x => x % 2 == 0) + .Select(x => Convert.ToByte(hex.Substring(x, 2), 16)) + .ToArray(); } internal static string ToHexString (this byte[] arr) { StringBuilder sb = new StringBuilder(); - foreach (var b in arr) { + for (int i = 0; i < arr.Length; i++) { + byte b = arr[i]; sb.Append(b.ToString("X2")); } return sb.ToString(); } + internal static byte[] BigToMixedEndian (this byte[] arr) { + + List oldBL = new List(arr); + + List tempL = new List(); + + //make the input list even + if(arr.Length % 2 != 0) + oldBL.Add((byte)0); + + for (int i = 0; i < oldBL.Count; i+=2) { + byte firstByte = oldBL[i]; + byte lastByte = oldBL[i + 1]; + tempL.Add(lastByte); + tempL.Add(firstByte); + + } + + return tempL.ToArray(); + + } + } } \ No newline at end of file diff --git a/MewtocolNet/Mewtocol/MewtocolInterface.cs b/MewtocolNet/Mewtocol/MewtocolInterface.cs index 5092e07..cf330ad 100644 --- a/MewtocolNet/Mewtocol/MewtocolInterface.cs +++ b/MewtocolNet/Mewtocol/MewtocolInterface.cs @@ -115,6 +115,8 @@ namespace MewtocolNet { /// public async Task ConnectAsync (Action OnConnected = null, Action OnFailed = null) { + Logger.Log("Connecting to PLC...", LogLevel.Info, this); + var plcinf = await GetPLCInfoAsync(); if (plcinf != null) { @@ -394,49 +396,49 @@ namespace MewtocolNet { if (foundRegister.GetType() == typeof(BRegister)) { - return await WriteBoolRegister((BRegister)foundRegister, (bool)value, StationNumber); + return await WriteBoolRegister((BRegister)foundRegister, (bool)value); } if (foundRegister.GetType() == typeof(NRegister)) { - return await WriteNumRegister((NRegister)foundRegister, (short)value, StationNumber); + return await WriteNumRegister((NRegister)foundRegister, (short)value); } if (foundRegister.GetType() == typeof(NRegister)) { - return await WriteNumRegister((NRegister)foundRegister, (ushort)value, StationNumber); + return await WriteNumRegister((NRegister)foundRegister, (ushort)value); } if (foundRegister.GetType() == typeof(NRegister)) { - return await WriteNumRegister((NRegister)foundRegister, (int)value, StationNumber); + return await WriteNumRegister((NRegister)foundRegister, (int)value); } if (foundRegister.GetType() == typeof(NRegister)) { - return await WriteNumRegister((NRegister)foundRegister, (uint)value, StationNumber); + return await WriteNumRegister((NRegister)foundRegister, (uint)value); } if (foundRegister.GetType() == typeof(NRegister)) { - return await WriteNumRegister((NRegister)foundRegister, (float)value, StationNumber); + return await WriteNumRegister((NRegister)foundRegister, (float)value); } if (foundRegister.GetType() == typeof(NRegister)) { - return await WriteNumRegister((NRegister)foundRegister, (TimeSpan)value, StationNumber); + return await WriteNumRegister((NRegister)foundRegister, (TimeSpan)value); } if (foundRegister.GetType() == typeof(SRegister)) { - return await WriteStringRegister((SRegister)foundRegister, (string)value, StationNumber); + return await WriteStringRegister((SRegister)foundRegister, (string)value); } diff --git a/MewtocolNet/Mewtocol/MewtocolInterfaceRequests.cs b/MewtocolNet/Mewtocol/MewtocolInterfaceRequests.cs index 12ced99..9f5cc76 100644 --- a/MewtocolNet/Mewtocol/MewtocolInterfaceRequests.cs +++ b/MewtocolNet/Mewtocol/MewtocolInterfaceRequests.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using MewtocolNet.Registers; using System.Linq; using System.Globalization; +using MewtocolNet.Logging; namespace MewtocolNet { @@ -51,16 +52,101 @@ namespace MewtocolNet { #endregion + #region Operation mode changing + + /// + /// Changes the PLCs operation mode to the given one + /// + /// The mode to change to + /// The success state of the write operation + public async Task SetOperationMode (OPMode mode) { + + string modeChar = mode == OPMode.Prog ? "P" : "R"; + + string requeststring = $"%{GetStationNumber()}#RM{modeChar}"; + var result = await SendCommandAsync(requeststring); + + if (result.Success) { + Logger.Log($"operation mode was changed to {mode}", LogLevel.Info, this); + } else { + Logger.Log("Operation mode change failed", LogLevel.Error, this); + } + + return result.Success; + + } + + #endregion + + #region Byte range writingv / reading to registers + + /// + /// Writes a byte array to a span over multiple registers at once, + /// Rembember the plc can only store word so in order to write to a word array + /// your byte array should be double the size + /// + /// /// start address of the array + /// + /// + public async Task WriteByteRange (int start, byte[] byteArr) { + + string byteString = byteArr.BigToMixedEndian().ToHexString(); + var wordLength = byteArr.Length / 2; + if (byteArr.Length % 2 != 0) + wordLength++; + + string startStr = start.ToString().PadLeft(5, '0'); + string endStr = (start + wordLength - 1).ToString().PadLeft(5, '0'); + + string requeststring = $"%{GetStationNumber()}#WDD{startStr}{endStr}{byteString}"; + var result = await SendCommandAsync(requeststring); + + return result.Success; + + } + + /// + /// Reads the bytes from the start adress for counts byte length + /// + /// Start adress + /// Number of bytes to get + /// A byte array or null of there was an error + public async Task ReadByteRange (int start, int count) { + + string startStr = start.ToString().PadLeft(5, '0'); + var wordLength = count / 2; + bool wasOdd = false; + if (count % 2 != 0) + wordLength++; + + string endStr = (start + wordLength - 1).ToString().PadLeft(5, '0'); + + string requeststring = $"%{GetStationNumber()}#RDD{startStr}{endStr}"; + var result = await SendCommandAsync(requeststring); + + if(result.Success && !string.IsNullOrEmpty(result.Response)) { + + var bytes = result.Response.ParseDTByteString(wordLength * 4).HexStringToByteArray(); + + return bytes.BigToMixedEndian().Take(count).ToArray(); + + } + + return null; + + } + + #endregion + #region Bool register reading / writing /// /// Reads the given boolean register from the PLC /// /// The register to read - /// Station number to access - public async Task ReadBoolRegister (BRegister _toRead, int _stationNumber = 1) { + public async Task ReadBoolRegister (BRegister _toRead) { - string requeststring = $"%{_stationNumber.ToString().PadLeft(2, '0')}#RCS{_toRead.BuildMewtocolIdent()}"; + string requeststring = $"%{GetStationNumber()}#RCS{_toRead.BuildMewtocolIdent()}"; var result = await SendCommandAsync(requeststring); if(!result.Success) { @@ -88,15 +174,14 @@ namespace MewtocolNet { /// Writes to the given bool register on the PLC /// /// The register to write to - /// Station number to access /// The success state of the write operation - public async Task WriteBoolRegister (BRegister _toWrite, bool value, int _stationNumber = 1) { + public async Task WriteBoolRegister (BRegister _toWrite, bool value) { - string requeststring = $"%{_stationNumber.ToString().PadLeft(2, '0')}#WCS{_toWrite.BuildMewtocolIdent()}{(value ? "1" : "0")}"; + string requeststring = $"%{GetStationNumber()}#WCS{_toWrite.BuildMewtocolIdent()}{(value ? "1" : "0")}"; var result = await SendCommandAsync(requeststring); - return result.Success && result.Response.StartsWith($"%{ _stationNumber.ToString().PadLeft(2, '0')}#WC"); + return result.Success && result.Response.StartsWith($"%{ GetStationNumber()}#WC"); } @@ -111,11 +196,11 @@ namespace MewtocolNet { /// The register to read /// Station number to access /// A result with the given NumberRegister containing the readback value and a result struct - public async Task> ReadNumRegister (NRegister _toRead, int _stationNumber = 1) { + public async Task> ReadNumRegister (NRegister _toRead) { Type numType = typeof(T); - string requeststring = $"%{_stationNumber.ToString().PadLeft(2, '0')}#RD{_toRead.BuildMewtocolIdent()}"; + string requeststring = $"%{GetStationNumber()}#RD{_toRead.BuildMewtocolIdent()}"; var result = await SendCommandAsync(requeststring); if(!result.Success || string.IsNullOrEmpty(result.Response)) { @@ -188,7 +273,7 @@ namespace MewtocolNet { /// The register to write /// Station number to access /// The success state of the write operation - public async Task WriteNumRegister (NRegister _toWrite, T _value, int _stationNumber = 1) { + public async Task WriteNumRegister (NRegister _toWrite, T _value) { byte[] toWriteVal; Type numType = typeof(T); @@ -222,11 +307,11 @@ namespace MewtocolNet { toWriteVal = null; } - string requeststring = $"%{_stationNumber.ToString().PadLeft(2, '0')}#WD{_toWrite.BuildMewtocolIdent()}{toWriteVal.ToHexString()}"; + string requeststring = $"%{GetStationNumber()}#WD{_toWrite.BuildMewtocolIdent()}{toWriteVal.ToHexString()}"; var result = await SendCommandAsync(requeststring); - return result.Success && result.Response.StartsWith($"%{ _stationNumber.ToString().PadLeft(2, '0')}#WD"); + return result.Success && result.Response.StartsWith($"%{ GetStationNumber()}#WD"); } @@ -249,7 +334,7 @@ namespace MewtocolNet { /// public async Task ReadStringRegister (SRegister _toRead, int _stationNumber = 1) { - string requeststring = $"%{_stationNumber.ToString().PadLeft(2, '0')}#RD{_toRead.BuildMewtocolIdent()}"; + string requeststring = $"%{GetStationNumber()}#RD{_toRead.BuildMewtocolIdent()}"; var result = await SendCommandAsync(requeststring); if (result.Success) _toRead.SetValueFromPLC(result.Response.ParseDTString()); @@ -273,7 +358,7 @@ namespace MewtocolNet { throw new ArgumentException("Write string size cannot be longer than reserved string size"); } - string stationNum = _stationNumber.ToString().PadLeft(2, '0'); + string stationNum = GetStationNumber(); string dataString = _value.BuildDTString(_toWrite.ReservedSize); string dataArea = _toWrite.BuildCustomIdent(dataString.Length / 4); @@ -282,7 +367,18 @@ namespace MewtocolNet { var result = await SendCommandAsync(requeststring); - return result.Success && result.Response.StartsWith($"%{ _stationNumber.ToString().PadLeft(2, '0')}#WD"); + return result.Success && result.Response.StartsWith($"%{ GetStationNumber()}#WD"); + } + + #endregion + + #region Helpers + + internal string GetStationNumber () { + + return StationNumber.ToString().PadLeft(2, '0'); + + } #endregion diff --git a/MewtocolNet/Mewtocol/PLCEnums/CpuType.cs b/MewtocolNet/Mewtocol/PLCEnums/CpuType.cs new file mode 100644 index 0000000..9312177 --- /dev/null +++ b/MewtocolNet/Mewtocol/PLCEnums/CpuType.cs @@ -0,0 +1,46 @@ +namespace MewtocolNet { + + /// + /// CPU type of the PLC + /// + public enum CpuType { + /// + /// FP 0 / FP 2.7K + /// + FP0_FP1_2_7K, + /// + /// FP0 / FP1, 5K / 10K + /// + FP0_FP1_5K_10K, + /// + /// FP1 M 0.9K + /// + FP1_M_0_9K, + /// + /// FP2 16k / 32k + /// + FP2_16K_32K, + /// + /// FP3 C 10K + /// + FP3_C_10K, + /// + /// FP3 C 16K + /// + FP3_C_16K, + /// + /// FP5 16K + /// + FP5_16K, + /// + /// FP 5 24K + /// + FP5_24K, + /// + /// Includes panasonic FPX, FPX-H, Sigma + /// + FP_Sigma_X_H_30K_60K_120K + + } + +} \ No newline at end of file diff --git a/MewtocolNet/Mewtocol/PLCEnums/OPMode.cs b/MewtocolNet/Mewtocol/PLCEnums/OPMode.cs new file mode 100644 index 0000000..150338c --- /dev/null +++ b/MewtocolNet/Mewtocol/PLCEnums/OPMode.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MewtocolNet { + + /// + /// CPU type of the PLC + /// + public enum OPMode { + /// + /// PLC run mode + /// + Run, + /// + /// PLC programming mode + /// + Prog, + } + +} \ No newline at end of file diff --git a/MewtocolNet/MewtocolNet.csproj b/MewtocolNet/MewtocolNet.csproj index 9e3221a..368dd07 100644 --- a/MewtocolNet/MewtocolNet.csproj +++ b/MewtocolNet/MewtocolNet.csproj @@ -1,11 +1,18 @@  netstandard2.0 - AppLogger + MewtocolNet 0.2.5 Felix Weiss Womed true + A Mewtocol protocol library to interface with Panasonic PLCs over TCP/Serial. + Copyright (c) 2022 WOLF Medizintechnik GmbH + https://github.com/WOmed/MewtocolNet + https://github.com/WOmed/MewtocolNet + plc;panasonic;mewtocol;automation; + MIT + 2ccdcc9b-94a3-4e76-8827-453ab889ea33 C:\Users\Felix Weiß\source\repos\WOmed\MewtocolNet\Builds\MewtocolNet.xml