From f338acfe8a2c07392178c7730a8a98e3ca203175 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Wei=C3=9F?= <72068105+Sandoun@users.noreply.github.com> Date: Mon, 3 Jul 2023 01:25:29 +0200 Subject: [PATCH] Fixes --- Examples/ExampleScenarios.cs | 73 +++++++++++++++-- Examples/TestRegisters.cs | 2 +- Examples/TestRegistersEnumBitwise.cs | 2 +- MewExplorer/MewExplorer.csproj | 6 +- MewtocolNet/IPlc.cs | 28 ++++++- MewtocolNet/IPlcEthernet.cs | 28 ++++++- MewtocolNet/IPlcSerial.cs | 32 ++++++-- MewtocolNet/Mewtocol.cs | 66 +++++++++++---- MewtocolNet/MewtocolInterface.cs | 75 +++++++++++++++-- .../MewtocolInterfaceRegisterHandling.cs | 55 +++++-------- MewtocolNet/MewtocolInterfaceRequests.cs | 80 +++++++++++++++++++ MewtocolNet/MewtocolInterfaceSerial.cs | 51 +++++++++--- MewtocolNet/MewtocolInterfaceTcp.cs | 61 ++++++++------ ...ollectionBase.cs => RegisterCollection.cs} | 2 +- MewtocolNet/RegisterBuilding/RegBuilder.cs | 2 +- MewtocolTests/AutomatedPropertyRegisters.cs | 50 ++++-------- MewtocolTests/TestLivePLC.cs | 55 +++++++------ 17 files changed, 494 insertions(+), 174 deletions(-) rename MewtocolNet/RegisterAttributes/{RegisterCollectionBase.cs => RegisterCollection.cs} (96%) diff --git a/Examples/ExampleScenarios.cs b/Examples/ExampleScenarios.cs index 00c1a95..442844f 100644 --- a/Examples/ExampleScenarios.cs +++ b/Examples/ExampleScenarios.cs @@ -22,7 +22,7 @@ public class ExampleScenarios { public void SetupLogger () { //attaching the logger - Logger.LogLevel = LogLevel.Verbose; + Logger.LogLevel = LogLevel.Info; Logger.OnNewLogMessage((date, level, msg) => { if (level == LogLevel.Error) Console.ForegroundColor = ConsoleColor.Red; @@ -74,12 +74,28 @@ public class ExampleScenarios { } - [Scenario("Read all kinds of example registers")] - public async Task RunReadTest () { + [Scenario("Read all kinds of example registers over ethernet")] + public async Task RunReadTestEth () { //setting up a new PLC interface and register collection var interf = Mewtocol.Ethernet("192.168.115.210").WithPoller(); + await RunCyclicReadTest(interf); + + } + + [Scenario("Read all kinds of example registers over serial")] + public async Task RunReadTestSer () { + + //setting up a new PLC interface and register collection + var interf = Mewtocol.SerialAuto("COM4").WithPoller(); + + await RunCyclicReadTest(interf); + + } + + private async Task RunCyclicReadTest (IPlc interf) { + //auto add all built registers to the interface var builder = RegBuilder.ForInterface(interf); var r0reg = builder.FromPlcRegName("R0").Build(); @@ -95,14 +111,26 @@ public class ExampleScenarios { var stringReg = builder.FromPlcRegName("DT40").AsPlcType(PlcVarType.STRING).Build(); //connect - await interf.ConnectAsync(); + if(interf is IPlcSerial serialPlc) { + + await serialPlc.ConnectAsync(() => { + + Console.WriteLine($"Trying config: {serialPlc.ConnectionInfo}"); + + }); + + } else { + + await interf.ConnectAsync(); + + } //await first register data await interf.AwaitFirstDataAsync(); _ = Task.Factory.StartNew(async () => { - void setTitle () { + void setTitle() { Console.Title = $"Speed UP: {interf.BytesPerSecondUpstream} B/s, " + @@ -112,7 +140,7 @@ public class ExampleScenarios { } - while (interf.IsConnected) { + while (interf.IsConnected) { setTitle(); await Task.Delay(1000); } @@ -127,7 +155,7 @@ public class ExampleScenarios { //set bool await r0reg.WriteAsync(!(bool)r0reg.Value); - + //set random num await shortReg.WriteAsync((short)new Random().Next(0, 100)); await stringReg.WriteAsync($"_{DateTime.Now.Second}s_"); @@ -135,7 +163,7 @@ public class ExampleScenarios { sw.Stop(); foreach (var reg in interf.Registers) - Console.WriteLine(reg.ToString()); + Console.WriteLine(reg.ToString()); Console.WriteLine($"Total write time for registers: {sw.Elapsed.TotalMilliseconds:N0}ms"); @@ -287,4 +315,33 @@ public class ExampleScenarios { } + [Scenario("Test")] + public async Task Test () { + + Logger.LogLevel = LogLevel.Critical; + + //fpx c14 r + var plxFpx = Mewtocol.Ethernet("192.168.178.55"); + await plxFpx.ConnectAsync(); + await ((MewtocolInterface)plxFpx).GetSystemRegister(); + + //fpx-h c30 t + var plcFpxH = Mewtocol.Ethernet("192.168.115.210"); + await plcFpxH.ConnectAsync(); + await ((MewtocolInterface)plcFpxH).GetSystemRegister(); + + //fpx-h c14 r + var plcFpxHc14 = Mewtocol.Ethernet("192.168.115.212"); + await plcFpxHc14.ConnectAsync(); + await ((MewtocolInterface)plcFpxHc14).GetSystemRegister(); + + //fpx c30 t + var plcFpxc30T = Mewtocol.Ethernet("192.168.115.213"); + await plcFpxc30T.ConnectAsync(); + await ((MewtocolInterface)plcFpxc30T).GetSystemRegister(); + + await Task.Delay(-1); + + } + } diff --git a/Examples/TestRegisters.cs b/Examples/TestRegisters.cs index 4b13ca1..c2bbe9a 100644 --- a/Examples/TestRegisters.cs +++ b/Examples/TestRegisters.cs @@ -4,7 +4,7 @@ using System; using System.Collections; namespace Examples { - public class TestRegisters : RegisterCollectionBase { + public class TestRegisters : RegisterCollection { //corresponds to a R100 boolean register in the PLC [Register(IOType.R, 1000)] diff --git a/Examples/TestRegistersEnumBitwise.cs b/Examples/TestRegistersEnumBitwise.cs index 72f5ede..a3990b8 100644 --- a/Examples/TestRegistersEnumBitwise.cs +++ b/Examples/TestRegistersEnumBitwise.cs @@ -5,7 +5,7 @@ using System.Collections; namespace Examples { - public class TestRegistersEnumBitwise : RegisterCollectionBase { + public class TestRegistersEnumBitwise : RegisterCollection { private bool startCyclePLC; diff --git a/MewExplorer/MewExplorer.csproj b/MewExplorer/MewExplorer.csproj index 9b64231..9a8e14e 100644 --- a/MewExplorer/MewExplorer.csproj +++ b/MewExplorer/MewExplorer.csproj @@ -1,7 +1,7 @@  - net7.0-android;net7.0-ios;net7.0-maccatalyst + net7.0-android; $(TargetFrameworks);net7.0-windows10.0.19041.0 @@ -23,12 +23,10 @@ 1.0 1 - 14.2 - 14.0 24.0 10.0.17763.0 10.0.17763.0 - 6.5 + diff --git a/MewtocolNet/IPlc.cs b/MewtocolNet/IPlc.cs index 62c1c33..6bc1c1b 100644 --- a/MewtocolNet/IPlc.cs +++ b/MewtocolNet/IPlc.cs @@ -1,4 +1,5 @@ -using System; +using MewtocolNet.Registers; +using System; using System.Collections.Generic; using System.Threading.Tasks; @@ -49,6 +50,11 @@ namespace MewtocolNet { /// int StationNumber { get; } + /// + /// A connection info string + /// + string ConnectionInfo { get; } + /// /// The initial connection timeout in milliseconds /// @@ -91,6 +97,26 @@ namespace MewtocolNet { /// string GetConnectionInfo(); + /// + /// Adds a register to the plc + /// + void AddRegister(BaseRegister register); + + /// + /// Adds a register to the plc + /// + void AddRegister(IRegister register); + + /// + /// Gets a register from the plc by name + /// + IRegister GetRegister(string name); + + /// + /// Gets all registers from the plc + /// + IEnumerable GetAllRegisters(); + } } diff --git a/MewtocolNet/IPlcEthernet.cs b/MewtocolNet/IPlcEthernet.cs index f9032c1..dd95ddf 100644 --- a/MewtocolNet/IPlcEthernet.cs +++ b/MewtocolNet/IPlcEthernet.cs @@ -1,4 +1,9 @@ -namespace MewtocolNet { +using MewtocolNet.RegisterAttributes; +using System; +using System.Net; +using System.Threading.Tasks; + +namespace MewtocolNet { /// /// Provides a interface for Panasonic PLCs over a ethernet connection @@ -16,9 +21,14 @@ int Port { get; } /// - /// Attaches a poller to the interface + /// The host ip endpoint, leave it null to use an automatic interface /// - public IPlcEthernet WithPoller(); + IPEndPoint HostEndpoint { get; set; } + + /// + /// Tries to establish a connection with the device asynchronously + /// + Task ConnectAsync(); /// /// Configures the serial interface @@ -28,6 +38,18 @@ /// Station Number of the PLC void ConfigureConnection(string _ip, int _port = 9094, int _station = 1); + /// + /// Attaches a poller to the interface + /// + IPlcEthernet WithPoller(); + + /// + /// Attaches a register collection object to + /// the interface that can be updated automatically. + /// + /// The type of the collection base class + IPlcEthernet AddRegisterCollection(RegisterCollection collection); + } } diff --git a/MewtocolNet/IPlcSerial.cs b/MewtocolNet/IPlcSerial.cs index 09d2587..609bf40 100644 --- a/MewtocolNet/IPlcSerial.cs +++ b/MewtocolNet/IPlcSerial.cs @@ -1,4 +1,5 @@ -using System; +using MewtocolNet.RegisterAttributes; +using System; using System.Collections.Generic; using System.IO.Ports; using System.Text; @@ -36,11 +37,6 @@ namespace MewtocolNet { /// StopBits SerialStopBits { get; } - /// - /// Attaches a poller to the interface - /// - public IPlcSerial WithPoller(); - /// /// Sets up the connection settings for the device /// @@ -50,7 +46,29 @@ namespace MewtocolNet { /// The serial connection parity /// The serial connection stop bits /// The station number of the PLC - void ConfigureConnection(string _portName, int _baudRate = 19200, int _dataBits = 8, Parity _parity = Parity.Odd, StopBits _stopBits = StopBits.One, int _station = 1) + void ConfigureConnection(string _portName, int _baudRate = 19200, int _dataBits = 8, Parity _parity = Parity.Odd, StopBits _stopBits = StopBits.One, int _station = 1); + + /// + /// Tries to establish a connection with the device asynchronously + /// + Task ConnectAsync(); + + /// + /// Tries to establish a connection with the device asynchronously + /// + Task ConnectAsync(Action onTryingConfig); + + /// + /// Attaches a poller to the interface + /// + IPlcSerial WithPoller(); + + /// + /// Attaches a register collection object to + /// the interface that can be updated automatically. + /// + /// The type of the collection base class + IPlcSerial AddRegisterCollection(RegisterCollection collection); } diff --git a/MewtocolNet/Mewtocol.cs b/MewtocolNet/Mewtocol.cs index 7515cbf..ca88908 100644 --- a/MewtocolNet/Mewtocol.cs +++ b/MewtocolNet/Mewtocol.cs @@ -1,6 +1,9 @@ -using System; +using MewtocolNet.Exceptions; +using System; using System.Collections.Generic; using System.IO.Ports; +using System.Linq; +using System.Net; using System.Text; namespace MewtocolNet { @@ -13,14 +16,29 @@ namespace MewtocolNet { /// /// Builds a ethernet based Mewtocol Interface /// - /// - /// - /// + /// + /// + /// Plc station number /// - public static IPlcEthernet Ethernet (string _ip, int _port = 9094, int _station = 1) { + public static IPlcEthernet Ethernet (string ip, int port = 9094, int station = 1) { var instance = new MewtocolInterfaceTcp(); - instance.ConfigureConnection(_ip, _port, _station); + instance.ConfigureConnection(ip, port, station); + return instance; + + } + + /// + /// Builds a ethernet based Mewtocol Interface + /// + /// + /// + /// Plc station number + /// + public static IPlcEthernet Ethernet(IPAddress ip, int port = 9094, int station = 1) { + + var instance = new MewtocolInterfaceTcp(); + instance.ConfigureConnection(ip, port, station); return instance; } @@ -28,16 +46,19 @@ namespace MewtocolNet { /// /// Builds a serial port based Mewtocol Interface /// - /// - /// - /// - /// - /// + /// + /// + /// + /// + /// + /// /// - public static IPlcSerial Serial (string _portName, BaudRate _baudRate = BaudRate._19200, DataBits _dataBits = DataBits.Eight, Parity _parity = Parity.Odd, StopBits _stopBits = StopBits.One, int _station = 1) { + public static IPlcSerial Serial (string portName, BaudRate baudRate = BaudRate._19200, DataBits dataBits = DataBits.Eight, Parity parity = Parity.Odd, StopBits stopBits = StopBits.One, int station = 1) { + + TestPortName(portName); var instance = new MewtocolInterfaceSerial(); - instance.ConfigureConnection(_portName, (int)_baudRate, (int)_dataBits, _parity, _stopBits, _station); + instance.ConfigureConnection(portName, (int)baudRate, (int)dataBits, parity, stopBits, station); return instance; } @@ -45,18 +66,29 @@ namespace MewtocolNet { /// /// Builds a serial mewtocol interface that finds the correct settings for the given port name automatically /// - /// - /// + /// + /// /// - public static IPlcSerial SerialAuto (string _portName, int _station = 1) { + public static IPlcSerial SerialAuto (string portName, int station = 1) { + + TestPortName(portName); var instance = new MewtocolInterfaceSerial(); - instance.ConfigureConnection(_portName, _station); + instance.ConfigureConnection(portName, station); instance.ConfigureConnectionAuto(); return instance; } + private static void TestPortName (string portName) { + + var portnames = SerialPort.GetPortNames(); + + if (!portnames.Any(x => x == portName)) + throw new MewtocolException($"The port {portName} is no valid port"); + + } + } } diff --git a/MewtocolNet/MewtocolInterface.cs b/MewtocolNet/MewtocolInterface.cs index 340924f..da8e69b 100644 --- a/MewtocolNet/MewtocolInterface.cs +++ b/MewtocolNet/MewtocolInterface.cs @@ -115,6 +115,9 @@ namespace MewtocolNet { } } + /// + public string ConnectionInfo => GetConnectionInfo(); + #endregion #region Public read/write Properties / Fields @@ -151,9 +154,9 @@ namespace MewtocolNet { }; } - + /// - public virtual Task ConnectAsync () => throw new NotImplementedException(); + public virtual async Task ConnectAsync() => throw new NotImplementedException(); /// public async Task AwaitFirstDataAsync() => await firstPollTask; @@ -163,7 +166,8 @@ namespace MewtocolNet { if (!IsConnected) return; - pollCycleTask.Wait(); + if(pollCycleTask != null && !pollCycleTask.IsCompleted) + pollCycleTask.Wait(); OnMajorSocketExceptionWhileConnected(); @@ -212,10 +216,16 @@ namespace MewtocolNet { if (useCr) frame = $"{frame}\r"; + + SetUpstreamStopWatchStart(); + //write inital command byte[] writeBuffer = Encoding.UTF8.GetBytes(frame); stream.Write(writeBuffer, 0, writeBuffer.Length); + //calc upstream speed + CalcUpstreamSpeed(writeBuffer.Length); + Logger.Log($"[---------CMD START--------]", LogLevel.Critical, this); Logger.Log($"--> OUT MSG: {frame.Replace("\r", "(CR)")}", LogLevel.Critical, this); @@ -235,7 +245,9 @@ namespace MewtocolNet { //error response var gotErrorcode = CheckForErrorMsg(resString); if (gotErrorcode != 0) { - return new MewtocolFrameResponse(gotErrorcode); + var errResponse = new MewtocolFrameResponse(gotErrorcode); + Logger.Log($"Command error: {errResponse.Error}", LogLevel.Error, this); + return errResponse; } //was multiframed response @@ -281,9 +293,13 @@ namespace MewtocolNet { do { + SetDownstreamStopWatchStart(); + byte[] buffer = new byte[128]; int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length); + CalcDownstreamSpeed(bytesRead); + byte[] received = new byte[bytesRead]; Buffer.BlockCopy(buffer, 0, received, 0, bytesRead); @@ -338,7 +354,7 @@ namespace MewtocolNet { private protected int CheckForErrorMsg (string msg) { //error catching - Regex errorcheck = new Regex(@"\%[0-9]{2}\!([0-9]{2})", RegexOptions.IgnoreCase); + Regex errorcheck = new Regex(@"\%..\!([0-9]{2})", RegexOptions.IgnoreCase); Match m = errorcheck.Match(msg); if (m.Success) { @@ -401,7 +417,7 @@ namespace MewtocolNet { BytesPerSecondDownstream = 0; BytesPerSecondUpstream = 0; - CycleTimeMs = 0; + PollerCycleDurationMs = 0; IsConnected = false; ClearRegisterVals(); @@ -422,6 +438,53 @@ namespace MewtocolNet { } + private void SetUpstreamStopWatchStart () { + + if (speedStopwatchUpstr == null) { + speedStopwatchUpstr = Stopwatch.StartNew(); + } + + if (speedStopwatchUpstr.Elapsed.TotalSeconds >= 1) { + speedStopwatchUpstr.Restart(); + bytesTotalCountedUpstream = 0; + } + + } + + private void SetDownstreamStopWatchStart () { + + if (speedStopwatchDownstr == null) { + speedStopwatchDownstr = Stopwatch.StartNew(); + } + + if (speedStopwatchDownstr.Elapsed.TotalSeconds >= 1) { + speedStopwatchDownstr.Restart(); + bytesTotalCountedDownstream = 0; + } + + } + + private void CalcUpstreamSpeed (int byteCount) { + + bytesTotalCountedUpstream += byteCount; + + var perSecUpstream = (double)((bytesTotalCountedUpstream / speedStopwatchUpstr.Elapsed.TotalMilliseconds) * 1000); + if (perSecUpstream <= 10000) + BytesPerSecondUpstream = (int)Math.Round(perSecUpstream, MidpointRounding.AwayFromZero); + + } + + private void CalcDownstreamSpeed (int byteCount) { + + bytesTotalCountedDownstream += byteCount; + + var perSecDownstream = (double)((bytesTotalCountedDownstream / speedStopwatchDownstr.Elapsed.TotalMilliseconds) * 1000); + + if (perSecDownstream <= 10000) + BytesPerSecondDownstream = (int)Math.Round(perSecDownstream, MidpointRounding.AwayFromZero); + + } + private protected void OnPropChange([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); diff --git a/MewtocolNet/MewtocolInterfaceRegisterHandling.cs b/MewtocolNet/MewtocolInterfaceRegisterHandling.cs index f86a029..51bb07f 100644 --- a/MewtocolNet/MewtocolInterfaceRegisterHandling.cs +++ b/MewtocolNet/MewtocolInterfaceRegisterHandling.cs @@ -205,15 +205,7 @@ namespace MewtocolNet { #region Register Colleciton adding - /// - /// Attaches a register collection object to - /// the interface that can be updated automatically. - /// - /// Just create a class inheriting from - /// and assert some propertys with the custom . - /// - /// A collection inherting the class - public MewtocolInterface WithRegisterCollection(RegisterCollectionBase collection) { + internal MewtocolInterface WithRegisterCollection (RegisterCollection collection) { collection.PLCInterface = this; @@ -353,6 +345,23 @@ namespace MewtocolNet { #region Register Adding + /// + public void AddRegister(BaseRegister register) { + + if (CheckDuplicateRegister(register)) + throw MewtocolException.DupeRegister(register); + + if (CheckDuplicateNameRegister(register)) + throw MewtocolException.DupeNameRegister(register); + + register.attachedInterface = this; + RegistersUnderlying.Add(register); + + } + + /// + public void AddRegister(IRegister register) => AddRegister(register as BaseRegister); + internal void AddRegister (RegisterBuildInfo buildInfo) { var builtRegister = buildInfo.Build(); @@ -375,19 +384,6 @@ namespace MewtocolNet { } - public void AddRegister (BaseRegister register) { - - if (CheckDuplicateRegister(register)) - throw MewtocolException.DupeRegister(register); - - if (CheckDuplicateNameRegister(register)) - throw MewtocolException.DupeNameRegister(register); - - register.attachedInterface = this; - RegistersUnderlying.Add(register); - - } - private bool CheckDuplicateRegister (IRegisterInternal instance, out IRegisterInternal foundDupe) { foundDupe = RegistersInternal.FirstOrDefault(x => x.CompareIsDuplicate(instance)); @@ -414,24 +410,15 @@ namespace MewtocolNet { #region Register accessing - /// - /// Gets a register that was added by its name - /// - /// + /// > public IRegister GetRegister(string name) { return RegistersUnderlying.FirstOrDefault(x => x.Name == name); } - #endregion - - #region Register Reading - - /// - /// Gets a list of all added registers - /// - public IEnumerable GetAllRegisters() { + /// + public IEnumerable GetAllRegisters () { return RegistersUnderlying.Cast(); diff --git a/MewtocolNet/MewtocolInterfaceRequests.cs b/MewtocolNet/MewtocolInterfaceRequests.cs index 7b35869..75e8906 100644 --- a/MewtocolNet/MewtocolInterfaceRequests.cs +++ b/MewtocolNet/MewtocolInterfaceRequests.cs @@ -289,6 +289,86 @@ namespace MewtocolNet { #endregion + #region Reading / Writing Plc program + + public async Task ReadPLCProgramAsync () { + + var cmd = SendCommandAsync($""); + + + } + + public async Task GetSystemRegister () { + + //the "." means CR or \r + + await SendCommandAsync("%EE#RT"); + + //then get plc status extended? gets polled all time + // %EE#EX00RT00 + await SendCommandAsync("%EE#EX00RT00"); + + //fpx C14 r + + //%EE$EX00 RT + //00 Extended mode + //32 Data item count + //70 Machine type + //00 Version (Fixed to 00) + //16 Prog capacity in K + //81 Operation mode / status + //00 Link unit + //60 Error flag + //0000 Self diag error + //50 Version + //02 Hardware information + //0 Number of programs + //4100 Program size BCD + //1600 Header size (no. of words) bcd + //1604 System register size + //96230000001480004 ?? + // + + // PLC TYPE | Machine Code | HW Information + // FPX C14 R | 70 | 02 + // FPX C30 T | 77 | 02 + + // FPX-H C14 R | A0 | 01 + // FPX-H C30 T | A5 | 01 + + + //then a sequence of these is sent + + // Specifiy register for monitoring + // %EE#MDFFFFFF + //await SendCommandAsync("%EE#MDFFFFFF"); + + // reset monitor registers + // %EE#MCFFFFF -> gets ackn + //await SendCommandAsync("%EE#MCFFFFF"); + + // maybe some special registers? + // %EE#MCR9029R0000R0000R0000R0000R0000R0000R0000 -> gets ackn + //await SendCommandAsync("%EE#MCR9029R0000R0000R0000R0000R0000R0000R0000"); + + // gets requested when opening plc status + // %EE#MG + // has a response like: + + //await SendCommandAsync("%EE#MG"); + + + //var res = cmd.Response.Replace("%01$RR", ""); + + //var parts = res.SplitInParts(4); + + //foreach (var part in parts) + // Console.WriteLine(part); + + } + + #endregion + #region Helpers internal string GetStationNumber() { diff --git a/MewtocolNet/MewtocolInterfaceSerial.cs b/MewtocolNet/MewtocolInterfaceSerial.cs index afe148a..8d692a0 100644 --- a/MewtocolNet/MewtocolInterfaceSerial.cs +++ b/MewtocolNet/MewtocolInterfaceSerial.cs @@ -10,6 +10,7 @@ using System.Net; using System.Text; using System.Threading.Tasks; using System.Threading; +using MewtocolNet.RegisterAttributes; namespace MewtocolNet { @@ -17,11 +18,23 @@ namespace MewtocolNet { private bool autoSerial; + private event Action tryingSerialConfig; + //serial config - public string PortName { get; private set; } - public int SerialBaudRate { get; private set; } - public int SerialDataBits { get; private set; } - public Parity SerialParity { get; private set; } + + /// + public string PortName { get; private set; } + + /// + public int SerialBaudRate { get; private set; } + + /// + public int SerialDataBits { get; private set; } + + /// + public Parity SerialParity { get; private set; } + + /// public StopBits SerialStopBits { get; private set; } //Serial @@ -29,7 +42,6 @@ namespace MewtocolNet { internal MewtocolInterfaceSerial () : base() { } - /// public IPlcSerial WithPoller () { @@ -38,6 +50,13 @@ namespace MewtocolNet { } + public IPlcSerial AddRegisterCollection (RegisterCollection collection) { + + WithRegisterCollection(collection); + return this; + + } + /// public override string GetConnectionInfo() { @@ -89,8 +108,17 @@ namespace MewtocolNet { } + public override async Task ConnectAsync() => await ConnectAsync(null); + /// - public override async Task ConnectAsync () { + public async Task ConnectAsync (Action onTryingConfig = null) { + + void OnTryConfig() { + onTryingConfig(); + } + + if (onTryingConfig != null) + tryingSerialConfig += OnTryConfig; try { @@ -98,10 +126,12 @@ namespace MewtocolNet { if(autoSerial) { + Logger.Log($"Connecting [AUTO CONFIGURE]: {PortName}", LogLevel.Info, this); gotInfo = await TryConnectAsyncMulti(); } else { + Logger.Log($"Connecting [MAN]: {PortName}", LogLevel.Info, this); gotInfo = await TryConnectAsyncSingle(PortName, SerialBaudRate, SerialDataBits, SerialParity, SerialStopBits); } @@ -112,7 +142,7 @@ namespace MewtocolNet { } else { - Logger.Log("Initial connection failed", LogLevel.Info, this); + Logger.Log("Initial connection failed", LogLevel.Error, this); OnMajorSocketExceptionWhileConnecting(); } @@ -125,6 +155,8 @@ namespace MewtocolNet { } + tryingSerialConfig -= OnTryConfig; + } private async Task TryConnectAsyncMulti () { @@ -193,19 +225,20 @@ namespace MewtocolNet { SerialParity = par; SerialStopBits = sbits; OnSerialPropsChanged(); + tryingSerialConfig?.Invoke(); serialClient.Open(); if (!serialClient.IsOpen) { - Logger.Log($"Failed to open [SERIAL]: {GetConnectionInfo()}", LogLevel.Verbose, this); + Logger.Log($"Failed to open [SERIAL]: {GetConnectionInfo()}", LogLevel.Critical, this); return null; } stream = serialClient.BaseStream; - Logger.Log($"Opened [SERIAL]: {GetConnectionInfo()}", LogLevel.Verbose, this); + Logger.Log($"Opened [SERIAL]: {GetConnectionInfo()}", LogLevel.Critical, this); var plcinf = await GetPLCInfoAsync(100); diff --git a/MewtocolNet/MewtocolInterfaceTcp.cs b/MewtocolNet/MewtocolInterfaceTcp.cs index 9a4f60e..3d25c16 100644 --- a/MewtocolNet/MewtocolInterfaceTcp.cs +++ b/MewtocolNet/MewtocolInterfaceTcp.cs @@ -26,23 +26,19 @@ namespace MewtocolNet { /// public class MewtocolInterfaceTcp : MewtocolInterface, IPlcEthernet { - /// - /// The host ip endpoint, leave it null to use an automatic interface - /// - public IPEndPoint HostEndpoint { get; set; } - //TCP internal TcpClient client; - //tcp/ip config - private string ip; - private int port; + private IPAddress ipAddr; /// - public string IpAddress => ip; + public string IpAddress => ipAddr.ToString(); /// - public int Port => port; + public int Port { get; private set; } + + /// + public IPEndPoint HostEndpoint { get; set; } internal MewtocolInterfaceTcp () : base() { } @@ -54,14 +50,35 @@ namespace MewtocolNet { } + /// + public IPlcEthernet AddRegisterCollection (RegisterCollection collection) { + + WithRegisterCollection(collection); + return this; + + } + #region TCP connection state handling /// - public void ConfigureConnection (string _ip, int _port = 9094, int _station = 1) { + public void ConfigureConnection (string ip, int port = 9094, int station = 1) { - ip = _ip; - port = _port; - stationNumber = _station; + if (!IPAddress.TryParse(ip, out ipAddr)) + throw new MewtocolException($"The ip: {ip} is no valid ip address"); + + Port = port; + stationNumber = station; + + Disconnect(); + + } + + /// + public void ConfigureConnection(IPAddress ip, int port = 9094, int station = 1) { + + ipAddr = ip; + Port = port; + stationNumber = station; Disconnect(); @@ -70,10 +87,6 @@ namespace MewtocolNet { /// public override async Task ConnectAsync () { - if (!IPAddress.TryParse(ip, out var targetIP)) { - throw new ArgumentException("The IP adress of the PLC was no valid format"); - } - try { if (HostEndpoint != null) { @@ -83,29 +96,31 @@ namespace MewtocolNet { NoDelay = false, }; var ep = (IPEndPoint)client.Client.LocalEndPoint; - Logger.Log($"Connecting [MAN] endpoint: {ep.Address}:{ep.Port}", LogLevel.Verbose, this); + Logger.Log($"Connecting [MAN] endpoint: {ep.Address}:{ep.Port}", LogLevel.Info, this); } else { client = new TcpClient() { ReceiveBufferSize = RecBufferSize, NoDelay = false, - ExclusiveAddressUse = true, + //ExclusiveAddressUse = true, }; } - var result = client.BeginConnect(targetIP, port, null, null); + var result = client.BeginConnect(ipAddr, Port, null, null); var success = result.AsyncWaitHandle.WaitOne(TimeSpan.FromMilliseconds(ConnectTimeout)); if (!success || !client.Connected) { + + Logger.Log("The PLC connection timed out", LogLevel.Error, this); OnMajorSocketExceptionWhileConnecting(); return; } if (HostEndpoint == null) { var ep = (IPEndPoint)client.Client.LocalEndPoint; - Logger.Log($"Connecting [AUTO] endpoint: {ep.Address.MapToIPv4()}:{ep.Port}", LogLevel.Verbose, this); + Logger.Log($"Connecting [AUTO] endpoint: {ep.Address.MapToIPv4()}:{ep.Port}", LogLevel.Info, this); } //get the stream @@ -121,7 +136,7 @@ namespace MewtocolNet { } else { - Logger.Log("Initial connection failed", LogLevel.Info, this); + Logger.Log("Initial connection failed", LogLevel.Error, this); OnDisconnect(); } diff --git a/MewtocolNet/RegisterAttributes/RegisterCollectionBase.cs b/MewtocolNet/RegisterAttributes/RegisterCollection.cs similarity index 96% rename from MewtocolNet/RegisterAttributes/RegisterCollectionBase.cs rename to MewtocolNet/RegisterAttributes/RegisterCollection.cs index 66d38d8..497abf1 100644 --- a/MewtocolNet/RegisterAttributes/RegisterCollectionBase.cs +++ b/MewtocolNet/RegisterAttributes/RegisterCollection.cs @@ -6,7 +6,7 @@ namespace MewtocolNet.RegisterAttributes { /// /// A register collection base with full auto read and notification support built in /// - public class RegisterCollectionBase : INotifyPropertyChanged { + public class RegisterCollection : INotifyPropertyChanged { /// /// Reference to its bound interface diff --git a/MewtocolNet/RegisterBuilding/RegBuilder.cs b/MewtocolNet/RegisterBuilding/RegBuilder.cs index 5879645..6994662 100644 --- a/MewtocolNet/RegisterBuilding/RegBuilder.cs +++ b/MewtocolNet/RegisterBuilding/RegBuilder.cs @@ -21,7 +21,7 @@ namespace MewtocolNet.RegisterBuilding { }; - public static RegBuilder ForInterface (IPlcEthernet interf) { + public static RegBuilder ForInterface (IPlc interf) { var rb = new RegBuilder(); rb.forInterface = interf as MewtocolInterface; diff --git a/MewtocolTests/AutomatedPropertyRegisters.cs b/MewtocolTests/AutomatedPropertyRegisters.cs index 6a002b7..9a30f15 100644 --- a/MewtocolTests/AutomatedPropertyRegisters.cs +++ b/MewtocolTests/AutomatedPropertyRegisters.cs @@ -15,7 +15,7 @@ namespace MewtocolTests { this.output = output; } - public class TestRegisterCollection : RegisterCollectionBase { + public class TestRegisterCollection : RegisterCollection { //corresponds to a R100 boolean register in the PLC //can also be written as R1000 because the last one is a special address @@ -109,8 +109,8 @@ namespace MewtocolTests { [Fact(DisplayName = "Boolean R generation")] public void BooleanGen() { - var interf = new MewtocolInterfaceShared("192.168.0.1"); - interf.WithRegisterCollection(new TestRegisterCollection()).WithPoller(); + var interf = Mewtocol.Ethernet("192.168.0.1"); + interf.AddRegisterCollection(new TestRegisterCollection()).WithPoller(); var register = interf.GetRegister(nameof(TestRegisterCollection.TestBool1)); @@ -122,8 +122,8 @@ namespace MewtocolTests { [Fact(DisplayName = "Boolean input XD generation")] public void BooleanInputGen() { - var interf = new MewtocolInterfaceShared("192.168.0.1"); - interf.WithRegisterCollection(new TestRegisterCollection()).WithPoller(); + var interf = Mewtocol.Ethernet("192.168.0.1"); + interf.AddRegisterCollection(new TestRegisterCollection()).WithPoller(); var register = interf.GetRegister(nameof(TestRegisterCollection.TestBoolInputXD)); @@ -135,8 +135,8 @@ namespace MewtocolTests { [Fact(DisplayName = "Int16 generation")] public void Int16Gen() { - var interf = new MewtocolInterfaceShared("192.168.0.1"); - interf.WithRegisterCollection(new TestRegisterCollection()).WithPoller(); + var interf = Mewtocol.Ethernet("192.168.0.1"); + interf.AddRegisterCollection(new TestRegisterCollection()).WithPoller(); var register = interf.GetRegister(nameof(TestRegisterCollection.TestInt16)); @@ -148,8 +148,8 @@ namespace MewtocolTests { [Fact(DisplayName = "UInt16 generation")] public void UInt16Gen() { - var interf = new MewtocolInterfaceShared("192.168.0.1"); - interf.WithRegisterCollection(new TestRegisterCollection()).WithPoller(); + var interf = Mewtocol.Ethernet("192.168.0.1"); + interf.AddRegisterCollection(new TestRegisterCollection()).WithPoller(); var register = interf.GetRegister(nameof(TestRegisterCollection.TestUInt16)); @@ -161,8 +161,8 @@ namespace MewtocolTests { [Fact(DisplayName = "Int32 generation")] public void Int32Gen() { - var interf = new MewtocolInterfaceShared("192.168.0.1"); - interf.WithRegisterCollection(new TestRegisterCollection()).WithPoller(); + var interf = Mewtocol.Ethernet("192.168.0.1"); + interf.AddRegisterCollection(new TestRegisterCollection()).WithPoller(); var register = interf.GetRegister(nameof(TestRegisterCollection.TestInt32)); @@ -174,8 +174,8 @@ namespace MewtocolTests { [Fact(DisplayName = "UInt32 generation")] public void UInt32Gen() { - var interf = new MewtocolInterfaceShared("192.168.0.1"); - interf.WithRegisterCollection(new TestRegisterCollection()).WithPoller(); + var interf = Mewtocol.Ethernet("192.168.0.1"); + interf.AddRegisterCollection(new TestRegisterCollection()).WithPoller(); var register = interf.GetRegister(nameof(TestRegisterCollection.TestUInt32)); @@ -187,8 +187,8 @@ namespace MewtocolTests { [Fact(DisplayName = "Float32 generation")] public void Float32Gen() { - var interf = new MewtocolInterfaceShared("192.168.0.1"); - interf.WithRegisterCollection(new TestRegisterCollection()).WithPoller(); + var interf = Mewtocol.Ethernet("192.168.0.1"); + interf.AddRegisterCollection(new TestRegisterCollection()).WithPoller(); var register = interf.GetRegister(nameof(TestRegisterCollection.TestFloat32)); @@ -200,8 +200,8 @@ namespace MewtocolTests { [Fact(DisplayName = "TimeSpan generation")] public void TimespanGen() { - var interf = new MewtocolInterfaceShared("192.168.0.1"); - interf.WithRegisterCollection(new TestRegisterCollection()).WithPoller(); + var interf = Mewtocol.Ethernet("192.168.0.1"); + interf.AddRegisterCollection(new TestRegisterCollection()).WithPoller(); var register = interf.GetRegister(nameof(TestRegisterCollection.TestTime)); @@ -210,22 +210,6 @@ namespace MewtocolTests { } - //[Fact(DisplayName = "String generation")] - //public void StringGen() { - - // var interf = new MewtocolInterface("192.168.0.1"); - // interf.WithRegisterCollection(new TestRegisterCollection()).WithPoller(); - - // var register = interf.GetRegister(nameof(TestRegisterCollection.TestString2)); - - // //test generic properties - // TestBasicGeneration(register, nameof(TestRegisterCollection.TestString2), null!, 7005, "DT7005"); - - // Assert.Equal(5, ((BytesRegister)register).ReservedSize); - // Assert.Equal(4, ((BytesRegister)register).MemoryLength); - - //} - } } \ No newline at end of file diff --git a/MewtocolTests/TestLivePLC.cs b/MewtocolTests/TestLivePLC.cs index 540a451..a8bdaef 100644 --- a/MewtocolTests/TestLivePLC.cs +++ b/MewtocolTests/TestLivePLC.cs @@ -45,10 +45,10 @@ namespace MewtocolTests AfterWriteValue = true, }, new RegisterReadWriteTest { - TargetRegister = new NumberRegister(3000), + TargetRegister = new NumberRegister(3000), RegisterPlcAddressName = "DT3000", - IntialValue = (int)0, - AfterWriteValue = (int)-513, + IntialValue = (short)0, + AfterWriteValue = (short)-513, }, }; @@ -73,9 +73,9 @@ namespace MewtocolTests output.WriteLine($"Testing: {plc.PLCName}"); - var cycleClient = new MewtocolInterfaceShared(plc.PLCIP, plc.PLCPort); + var cycleClient = Mewtocol.Ethernet(plc.PLCIP, plc.PLCPort); - await cycleClient.ConnectAsyncOld(); + await cycleClient.ConnectAsync(); Assert.True(cycleClient.IsConnected); @@ -94,9 +94,9 @@ namespace MewtocolTests output.WriteLine($"Testing: {plc.PLCName}\n"); - var client = new MewtocolInterfaceShared(plc.PLCIP, plc.PLCPort); + var client = Mewtocol.Ethernet(plc.PLCIP, plc.PLCPort); - await client.ConnectAsyncOld(); + await client.ConnectAsync(); output.WriteLine($"{client.PlcInfo}\n"); @@ -111,38 +111,43 @@ namespace MewtocolTests } - //[Fact(DisplayName = "Reading basic information from PLC")] - //public async void TestRegisterReadWriteAsync () { + [Fact(DisplayName = "Reading basic information from PLC")] + public async void TestRegisterReadWriteAsync() { - // foreach (var plc in testPlcInformationData) { + foreach (var plc in testPlcInformationData) { - // output.WriteLine($"Testing: {plc.PLCName}\n"); + output.WriteLine($"Testing: {plc.PLCName}\n"); - // var client = new MewtocolInterface(plc.PLCIP, plc.PLCPort); + var client = Mewtocol.Ethernet(plc.PLCIP, plc.PLCPort); - // foreach (var testRW in testRegisterRW) { + foreach (var testRW in testRegisterRW) { - // client.AddRegister(testRW.TargetRegister); + client.AddRegister(testRW.TargetRegister); - // } + } - // await client.ConnectAsync(); - // Assert.True(client.IsConnected); + await client.ConnectAsync(); + Assert.True(client.IsConnected); - // foreach (var testRW in testRegisterRW) { + foreach (var testRW in testRegisterRW) { - // client.AddRegister(testRW.TargetRegister); + var testRegister = client.Registers.First(x => x.PLCAddressName == testRW.RegisterPlcAddressName); - // } + //test inital val + Assert.Equal(testRW.IntialValue, testRegister.Value); - // Assert.Equal(client.PlcInfo.CpuInformation.Cputype, plc.Type); - // Assert.Equal(client.PlcInfo.CpuInformation.ProgramCapacity, plc.ProgCapacity); + await testRegister.WriteAsync(testRW.AfterWriteValue); - // client.Disconnect(); + //test after write val + Assert.Equal(testRW.AfterWriteValue, testRegister.Value); - // } + } - //} + client.Disconnect(); + + } + + } }