This commit is contained in:
Felix Weiß
2023-07-03 01:25:29 +02:00
parent 8c2ba1f68f
commit f338acfe8a
17 changed files with 494 additions and 174 deletions

View File

@@ -22,7 +22,7 @@ public class ExampleScenarios {
public void SetupLogger () { public void SetupLogger () {
//attaching the logger //attaching the logger
Logger.LogLevel = LogLevel.Verbose; Logger.LogLevel = LogLevel.Info;
Logger.OnNewLogMessage((date, level, msg) => { Logger.OnNewLogMessage((date, level, msg) => {
if (level == LogLevel.Error) Console.ForegroundColor = ConsoleColor.Red; if (level == LogLevel.Error) Console.ForegroundColor = ConsoleColor.Red;
@@ -74,12 +74,28 @@ public class ExampleScenarios {
} }
[Scenario("Read all kinds of example registers")] [Scenario("Read all kinds of example registers over ethernet")]
public async Task RunReadTest () { public async Task RunReadTestEth () {
//setting up a new PLC interface and register collection //setting up a new PLC interface and register collection
var interf = Mewtocol.Ethernet("192.168.115.210").WithPoller(); 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 //auto add all built registers to the interface
var builder = RegBuilder.ForInterface(interf); var builder = RegBuilder.ForInterface(interf);
var r0reg = builder.FromPlcRegName("R0").Build(); var r0reg = builder.FromPlcRegName("R0").Build();
@@ -95,8 +111,20 @@ public class ExampleScenarios {
var stringReg = builder.FromPlcRegName("DT40").AsPlcType(PlcVarType.STRING).Build(); var stringReg = builder.FromPlcRegName("DT40").AsPlcType(PlcVarType.STRING).Build();
//connect //connect
if(interf is IPlcSerial serialPlc) {
await serialPlc.ConnectAsync(() => {
Console.WriteLine($"Trying config: {serialPlc.ConnectionInfo}");
});
} else {
await interf.ConnectAsync(); await interf.ConnectAsync();
}
//await first register data //await first register data
await interf.AwaitFirstDataAsync(); await interf.AwaitFirstDataAsync();
@@ -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);
}
} }

View File

@@ -4,7 +4,7 @@ using System;
using System.Collections; using System.Collections;
namespace Examples { namespace Examples {
public class TestRegisters : RegisterCollectionBase { public class TestRegisters : RegisterCollection {
//corresponds to a R100 boolean register in the PLC //corresponds to a R100 boolean register in the PLC
[Register(IOType.R, 1000)] [Register(IOType.R, 1000)]

View File

@@ -5,7 +5,7 @@ using System.Collections;
namespace Examples { namespace Examples {
public class TestRegistersEnumBitwise : RegisterCollectionBase { public class TestRegistersEnumBitwise : RegisterCollection {
private bool startCyclePLC; private bool startCyclePLC;

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Razor"> <Project Sdk="Microsoft.NET.Sdk.Razor">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net7.0-android;net7.0-ios;net7.0-maccatalyst</TargetFrameworks> <TargetFrameworks>net7.0-android;</TargetFrameworks>
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net7.0-windows10.0.19041.0</TargetFrameworks> <TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net7.0-windows10.0.19041.0</TargetFrameworks>
<!-- Uncomment to also build the tizen app. You will need to install tizen by following this: https://github.com/Samsung/Tizen.NET --> <!-- Uncomment to also build the tizen app. You will need to install tizen by following this: https://github.com/Samsung/Tizen.NET -->
<!-- <TargetFrameworks>$(TargetFrameworks);net7.0-tizen</TargetFrameworks> --> <!-- <TargetFrameworks>$(TargetFrameworks);net7.0-tizen</TargetFrameworks> -->
@@ -23,12 +23,10 @@
<ApplicationDisplayVersion>1.0</ApplicationDisplayVersion> <ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
<ApplicationVersion>1</ApplicationVersion> <ApplicationVersion>1</ApplicationVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">14.2</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'">14.0</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">24.0</SupportedOSPlatformVersion> <SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">24.0</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</SupportedOSPlatformVersion> <SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</SupportedOSPlatformVersion>
<TargetPlatformMinVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</TargetPlatformMinVersion> <TargetPlatformMinVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</TargetPlatformMinVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'tizen'">6.5</SupportedOSPlatformVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@@ -1,4 +1,5 @@
using System; using MewtocolNet.Registers;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -49,6 +50,11 @@ namespace MewtocolNet {
/// </summary> /// </summary>
int StationNumber { get; } int StationNumber { get; }
/// <summary>
/// A connection info string
/// </summary>
string ConnectionInfo { get; }
/// <summary> /// <summary>
/// The initial connection timeout in milliseconds /// The initial connection timeout in milliseconds
/// </summary> /// </summary>
@@ -91,6 +97,26 @@ namespace MewtocolNet {
/// </summary> /// </summary>
string GetConnectionInfo(); string GetConnectionInfo();
/// <summary>
/// Adds a register to the plc
/// </summary>
void AddRegister(BaseRegister register);
/// <summary>
/// Adds a register to the plc
/// </summary>
void AddRegister(IRegister register);
/// <summary>
/// Gets a register from the plc by name
/// </summary>
IRegister GetRegister(string name);
/// <summary>
/// Gets all registers from the plc
/// </summary>
IEnumerable<IRegister> GetAllRegisters();
} }
} }

View File

@@ -1,4 +1,9 @@
namespace MewtocolNet { using MewtocolNet.RegisterAttributes;
using System;
using System.Net;
using System.Threading.Tasks;
namespace MewtocolNet {
/// <summary> /// <summary>
/// Provides a interface for Panasonic PLCs over a ethernet connection /// Provides a interface for Panasonic PLCs over a ethernet connection
@@ -16,9 +21,14 @@
int Port { get; } int Port { get; }
/// <summary> /// <summary>
/// Attaches a poller to the interface /// The host ip endpoint, leave it null to use an automatic interface
/// </summary> /// </summary>
public IPlcEthernet WithPoller(); IPEndPoint HostEndpoint { get; set; }
/// <summary>
/// Tries to establish a connection with the device asynchronously
/// </summary>
Task ConnectAsync();
/// <summary> /// <summary>
/// Configures the serial interface /// Configures the serial interface
@@ -28,6 +38,18 @@
/// <param name="_station">Station Number of the PLC</param> /// <param name="_station">Station Number of the PLC</param>
void ConfigureConnection(string _ip, int _port = 9094, int _station = 1); void ConfigureConnection(string _ip, int _port = 9094, int _station = 1);
/// <summary>
/// Attaches a poller to the interface
/// </summary>
IPlcEthernet WithPoller();
/// <summary>
/// Attaches a register collection object to
/// the interface that can be updated automatically.
/// </summary>
/// <param name="collection">The type of the collection base class</param>
IPlcEthernet AddRegisterCollection(RegisterCollection collection);
} }
} }

View File

@@ -1,4 +1,5 @@
using System; using MewtocolNet.RegisterAttributes;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO.Ports; using System.IO.Ports;
using System.Text; using System.Text;
@@ -36,11 +37,6 @@ namespace MewtocolNet {
/// </summary> /// </summary>
StopBits SerialStopBits { get; } StopBits SerialStopBits { get; }
/// <summary>
/// Attaches a poller to the interface
/// </summary>
public IPlcSerial WithPoller();
/// <summary> /// <summary>
/// Sets up the connection settings for the device /// Sets up the connection settings for the device
/// </summary> /// </summary>
@@ -50,7 +46,29 @@ namespace MewtocolNet {
/// <param name="_parity">The serial connection parity</param> /// <param name="_parity">The serial connection parity</param>
/// <param name="_stopBits">The serial connection stop bits</param> /// <param name="_stopBits">The serial connection stop bits</param>
/// <param name="_station">The station number of the PLC</param> /// <param name="_station">The station number of the PLC</param>
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);
/// <summary>
/// Tries to establish a connection with the device asynchronously
/// </summary>
Task ConnectAsync();
/// <summary>
/// Tries to establish a connection with the device asynchronously
/// </summary>
Task ConnectAsync(Action onTryingConfig);
/// <summary>
/// Attaches a poller to the interface
/// </summary>
IPlcSerial WithPoller();
/// <summary>
/// Attaches a register collection object to
/// the interface that can be updated automatically.
/// </summary>
/// <param name="collection">The type of the collection base class</param>
IPlcSerial AddRegisterCollection(RegisterCollection collection);
} }

View File

@@ -1,6 +1,9 @@
using System; using MewtocolNet.Exceptions;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO.Ports; using System.IO.Ports;
using System.Linq;
using System.Net;
using System.Text; using System.Text;
namespace MewtocolNet { namespace MewtocolNet {
@@ -13,14 +16,29 @@ namespace MewtocolNet {
/// <summary> /// <summary>
/// Builds a ethernet based Mewtocol Interface /// Builds a ethernet based Mewtocol Interface
/// </summary> /// </summary>
/// <param name="_ip"></param> /// <param name="ip"></param>
/// <param name="_port"></param> /// <param name="port"></param>
/// <param name="_station"></param> /// <param name="station">Plc station number</param>
/// <returns></returns> /// <returns></returns>
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(); var instance = new MewtocolInterfaceTcp();
instance.ConfigureConnection(_ip, _port, _station); instance.ConfigureConnection(ip, port, station);
return instance;
}
/// <summary>
/// Builds a ethernet based Mewtocol Interface
/// </summary>
/// <param name="ip"></param>
/// <param name="port"></param>
/// <param name="station">Plc station number</param>
/// <returns></returns>
public static IPlcEthernet Ethernet(IPAddress ip, int port = 9094, int station = 1) {
var instance = new MewtocolInterfaceTcp();
instance.ConfigureConnection(ip, port, station);
return instance; return instance;
} }
@@ -28,16 +46,19 @@ namespace MewtocolNet {
/// <summary> /// <summary>
/// Builds a serial port based Mewtocol Interface /// Builds a serial port based Mewtocol Interface
/// </summary> /// </summary>
/// <param name="_portName"></param> /// <param name="portName"></param>
/// <param name="_baudRate"></param> /// <param name="baudRate"></param>
/// <param name="_dataBits"></param> /// <param name="dataBits"></param>
/// <param name="_parity"></param> /// <param name="parity"></param>
/// <param name="_stopBits"></param> /// <param name="stopBits"></param>
/// <param name="station"></param>
/// <returns></returns> /// <returns></returns>
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(); 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; return instance;
} }
@@ -45,18 +66,29 @@ namespace MewtocolNet {
/// <summary> /// <summary>
/// Builds a serial mewtocol interface that finds the correct settings for the given port name automatically /// Builds a serial mewtocol interface that finds the correct settings for the given port name automatically
/// </summary> /// </summary>
/// <param name="_portName"></param> /// <param name="portName"></param>
/// <param name="_station"></param> /// <param name="station"></param>
/// <returns></returns> /// <returns></returns>
public static IPlcSerial SerialAuto (string _portName, int _station = 1) { public static IPlcSerial SerialAuto (string portName, int station = 1) {
TestPortName(portName);
var instance = new MewtocolInterfaceSerial(); var instance = new MewtocolInterfaceSerial();
instance.ConfigureConnection(_portName, _station); instance.ConfigureConnection(portName, station);
instance.ConfigureConnectionAuto(); instance.ConfigureConnectionAuto();
return instance; 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");
}
} }
} }

View File

@@ -115,6 +115,9 @@ namespace MewtocolNet {
} }
} }
/// <inheritdoc/>
public string ConnectionInfo => GetConnectionInfo();
#endregion #endregion
#region Public read/write Properties / Fields #region Public read/write Properties / Fields
@@ -153,7 +156,7 @@ namespace MewtocolNet {
} }
/// <inheritdoc/> /// <inheritdoc/>
public virtual Task ConnectAsync () => throw new NotImplementedException(); public virtual async Task ConnectAsync() => throw new NotImplementedException();
/// <inheritdoc/> /// <inheritdoc/>
public async Task AwaitFirstDataAsync() => await firstPollTask; public async Task AwaitFirstDataAsync() => await firstPollTask;
@@ -163,6 +166,7 @@ namespace MewtocolNet {
if (!IsConnected) return; if (!IsConnected) return;
if(pollCycleTask != null && !pollCycleTask.IsCompleted)
pollCycleTask.Wait(); pollCycleTask.Wait();
OnMajorSocketExceptionWhileConnected(); OnMajorSocketExceptionWhileConnected();
@@ -212,10 +216,16 @@ namespace MewtocolNet {
if (useCr) if (useCr)
frame = $"{frame}\r"; frame = $"{frame}\r";
SetUpstreamStopWatchStart();
//write inital command //write inital command
byte[] writeBuffer = Encoding.UTF8.GetBytes(frame); byte[] writeBuffer = Encoding.UTF8.GetBytes(frame);
stream.Write(writeBuffer, 0, writeBuffer.Length); stream.Write(writeBuffer, 0, writeBuffer.Length);
//calc upstream speed
CalcUpstreamSpeed(writeBuffer.Length);
Logger.Log($"[---------CMD START--------]", LogLevel.Critical, this); Logger.Log($"[---------CMD START--------]", LogLevel.Critical, this);
Logger.Log($"--> OUT MSG: {frame.Replace("\r", "(CR)")}", LogLevel.Critical, this); Logger.Log($"--> OUT MSG: {frame.Replace("\r", "(CR)")}", LogLevel.Critical, this);
@@ -235,7 +245,9 @@ namespace MewtocolNet {
//error response //error response
var gotErrorcode = CheckForErrorMsg(resString); var gotErrorcode = CheckForErrorMsg(resString);
if (gotErrorcode != 0) { 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 //was multiframed response
@@ -281,9 +293,13 @@ namespace MewtocolNet {
do { do {
SetDownstreamStopWatchStart();
byte[] buffer = new byte[128]; byte[] buffer = new byte[128];
int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length); int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
CalcDownstreamSpeed(bytesRead);
byte[] received = new byte[bytesRead]; byte[] received = new byte[bytesRead];
Buffer.BlockCopy(buffer, 0, received, 0, bytesRead); Buffer.BlockCopy(buffer, 0, received, 0, bytesRead);
@@ -338,7 +354,7 @@ namespace MewtocolNet {
private protected int CheckForErrorMsg (string msg) { private protected int CheckForErrorMsg (string msg) {
//error catching //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); Match m = errorcheck.Match(msg);
if (m.Success) { if (m.Success) {
@@ -401,7 +417,7 @@ namespace MewtocolNet {
BytesPerSecondDownstream = 0; BytesPerSecondDownstream = 0;
BytesPerSecondUpstream = 0; BytesPerSecondUpstream = 0;
CycleTimeMs = 0; PollerCycleDurationMs = 0;
IsConnected = false; IsConnected = false;
ClearRegisterVals(); 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) { private protected void OnPropChange([CallerMemberName] string propertyName = null) {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

View File

@@ -205,15 +205,7 @@ namespace MewtocolNet {
#region Register Colleciton adding #region Register Colleciton adding
/// <summary> internal MewtocolInterface WithRegisterCollection (RegisterCollection collection) {
/// Attaches a register collection object to
/// the interface that can be updated automatically.
/// <para/>
/// Just create a class inheriting from <see cref="RegisterCollectionBase"/>
/// and assert some propertys with the custom <see cref="RegisterAttribute"/>.
/// </summary>
/// <param name="collection">A collection inherting the <see cref="RegisterCollectionBase"/> class</param>
public MewtocolInterface WithRegisterCollection(RegisterCollectionBase collection) {
collection.PLCInterface = this; collection.PLCInterface = this;
@@ -353,6 +345,23 @@ namespace MewtocolNet {
#region Register Adding #region Register Adding
/// <inheritdoc/>
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);
}
/// <inheritdoc/>
public void AddRegister(IRegister register) => AddRegister(register as BaseRegister);
internal void AddRegister (RegisterBuildInfo buildInfo) { internal void AddRegister (RegisterBuildInfo buildInfo) {
var builtRegister = buildInfo.Build(); 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) { private bool CheckDuplicateRegister (IRegisterInternal instance, out IRegisterInternal foundDupe) {
foundDupe = RegistersInternal.FirstOrDefault(x => x.CompareIsDuplicate(instance)); foundDupe = RegistersInternal.FirstOrDefault(x => x.CompareIsDuplicate(instance));
@@ -414,23 +410,14 @@ namespace MewtocolNet {
#region Register accessing #region Register accessing
/// <summary> /// <inheritdoc/>>
/// Gets a register that was added by its name
/// </summary>
/// <returns></returns>
public IRegister GetRegister(string name) { public IRegister GetRegister(string name) {
return RegistersUnderlying.FirstOrDefault(x => x.Name == name); return RegistersUnderlying.FirstOrDefault(x => x.Name == name);
} }
#endregion /// <inheritdoc/>
#region Register Reading
/// <summary>
/// Gets a list of all added registers
/// </summary>
public IEnumerable<IRegister> GetAllRegisters () { public IEnumerable<IRegister> GetAllRegisters () {
return RegistersUnderlying.Cast<IRegister>(); return RegistersUnderlying.Cast<IRegister>();

View File

@@ -289,6 +289,86 @@ namespace MewtocolNet {
#endregion #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 #region Helpers
internal string GetStationNumber() { internal string GetStationNumber() {

View File

@@ -10,6 +10,7 @@ using System.Net;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Threading; using System.Threading;
using MewtocolNet.RegisterAttributes;
namespace MewtocolNet { namespace MewtocolNet {
@@ -17,11 +18,23 @@ namespace MewtocolNet {
private bool autoSerial; private bool autoSerial;
private event Action tryingSerialConfig;
//serial config //serial config
/// <inheritdoc/>
public string PortName { get; private set; } public string PortName { get; private set; }
/// <inheritdoc/>
public int SerialBaudRate { get; private set; } public int SerialBaudRate { get; private set; }
/// <inheritdoc/>
public int SerialDataBits { get; private set; } public int SerialDataBits { get; private set; }
/// <inheritdoc/>
public Parity SerialParity { get; private set; } public Parity SerialParity { get; private set; }
/// <inheritdoc/>
public StopBits SerialStopBits { get; private set; } public StopBits SerialStopBits { get; private set; }
//Serial //Serial
@@ -29,7 +42,6 @@ namespace MewtocolNet {
internal MewtocolInterfaceSerial () : base() { } internal MewtocolInterfaceSerial () : base() { }
/// <inheritdoc/> /// <inheritdoc/>
public IPlcSerial WithPoller () { public IPlcSerial WithPoller () {
@@ -38,6 +50,13 @@ namespace MewtocolNet {
} }
public IPlcSerial AddRegisterCollection (RegisterCollection collection) {
WithRegisterCollection(collection);
return this;
}
/// <inheritdoc/> /// <inheritdoc/>
public override string GetConnectionInfo() { public override string GetConnectionInfo() {
@@ -89,8 +108,17 @@ namespace MewtocolNet {
} }
public override async Task ConnectAsync() => await ConnectAsync(null);
/// <inheritdoc/> /// <inheritdoc/>
public override async Task ConnectAsync () { public async Task ConnectAsync (Action onTryingConfig = null) {
void OnTryConfig() {
onTryingConfig();
}
if (onTryingConfig != null)
tryingSerialConfig += OnTryConfig;
try { try {
@@ -98,10 +126,12 @@ namespace MewtocolNet {
if(autoSerial) { if(autoSerial) {
Logger.Log($"Connecting [AUTO CONFIGURE]: {PortName}", LogLevel.Info, this);
gotInfo = await TryConnectAsyncMulti(); gotInfo = await TryConnectAsyncMulti();
} else { } else {
Logger.Log($"Connecting [MAN]: {PortName}", LogLevel.Info, this);
gotInfo = await TryConnectAsyncSingle(PortName, SerialBaudRate, SerialDataBits, SerialParity, SerialStopBits); gotInfo = await TryConnectAsyncSingle(PortName, SerialBaudRate, SerialDataBits, SerialParity, SerialStopBits);
} }
@@ -112,7 +142,7 @@ namespace MewtocolNet {
} else { } else {
Logger.Log("Initial connection failed", LogLevel.Info, this); Logger.Log("Initial connection failed", LogLevel.Error, this);
OnMajorSocketExceptionWhileConnecting(); OnMajorSocketExceptionWhileConnecting();
} }
@@ -125,6 +155,8 @@ namespace MewtocolNet {
} }
tryingSerialConfig -= OnTryConfig;
} }
private async Task<PLCInfo> TryConnectAsyncMulti () { private async Task<PLCInfo> TryConnectAsyncMulti () {
@@ -193,19 +225,20 @@ namespace MewtocolNet {
SerialParity = par; SerialParity = par;
SerialStopBits = sbits; SerialStopBits = sbits;
OnSerialPropsChanged(); OnSerialPropsChanged();
tryingSerialConfig?.Invoke();
serialClient.Open(); serialClient.Open();
if (!serialClient.IsOpen) { 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; return null;
} }
stream = serialClient.BaseStream; stream = serialClient.BaseStream;
Logger.Log($"Opened [SERIAL]: {GetConnectionInfo()}", LogLevel.Verbose, this); Logger.Log($"Opened [SERIAL]: {GetConnectionInfo()}", LogLevel.Critical, this);
var plcinf = await GetPLCInfoAsync(100); var plcinf = await GetPLCInfoAsync(100);

View File

@@ -26,23 +26,19 @@ namespace MewtocolNet {
/// </summary> /// </summary>
public class MewtocolInterfaceTcp : MewtocolInterface, IPlcEthernet { public class MewtocolInterfaceTcp : MewtocolInterface, IPlcEthernet {
/// <summary>
/// The host ip endpoint, leave it null to use an automatic interface
/// </summary>
public IPEndPoint HostEndpoint { get; set; }
//TCP //TCP
internal TcpClient client; internal TcpClient client;
//tcp/ip config private IPAddress ipAddr;
private string ip;
private int port;
/// <inheritdoc/> /// <inheritdoc/>
public string IpAddress => ip; public string IpAddress => ipAddr.ToString();
/// <inheritdoc/> /// <inheritdoc/>
public int Port => port; public int Port { get; private set; }
/// <inheritdoc/>
public IPEndPoint HostEndpoint { get; set; }
internal MewtocolInterfaceTcp () : base() { } internal MewtocolInterfaceTcp () : base() { }
@@ -54,14 +50,35 @@ namespace MewtocolNet {
} }
/// <inheritdoc/>
public IPlcEthernet AddRegisterCollection (RegisterCollection collection) {
WithRegisterCollection(collection);
return this;
}
#region TCP connection state handling #region TCP connection state handling
/// <inheritdoc/> /// <inheritdoc/>
public void ConfigureConnection (string _ip, int _port = 9094, int _station = 1) { public void ConfigureConnection (string ip, int port = 9094, int station = 1) {
ip = _ip; if (!IPAddress.TryParse(ip, out ipAddr))
port = _port; throw new MewtocolException($"The ip: {ip} is no valid ip address");
stationNumber = _station;
Port = port;
stationNumber = station;
Disconnect();
}
/// <inheritdoc/>
public void ConfigureConnection(IPAddress ip, int port = 9094, int station = 1) {
ipAddr = ip;
Port = port;
stationNumber = station;
Disconnect(); Disconnect();
@@ -70,10 +87,6 @@ namespace MewtocolNet {
/// <inheritdoc/> /// <inheritdoc/>
public override async Task ConnectAsync () { 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 { try {
if (HostEndpoint != null) { if (HostEndpoint != null) {
@@ -83,29 +96,31 @@ namespace MewtocolNet {
NoDelay = false, NoDelay = false,
}; };
var ep = (IPEndPoint)client.Client.LocalEndPoint; 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 { } else {
client = new TcpClient() { client = new TcpClient() {
ReceiveBufferSize = RecBufferSize, ReceiveBufferSize = RecBufferSize,
NoDelay = false, 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)); var success = result.AsyncWaitHandle.WaitOne(TimeSpan.FromMilliseconds(ConnectTimeout));
if (!success || !client.Connected) { if (!success || !client.Connected) {
Logger.Log("The PLC connection timed out", LogLevel.Error, this);
OnMajorSocketExceptionWhileConnecting(); OnMajorSocketExceptionWhileConnecting();
return; return;
} }
if (HostEndpoint == null) { if (HostEndpoint == null) {
var ep = (IPEndPoint)client.Client.LocalEndPoint; 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 //get the stream
@@ -121,7 +136,7 @@ namespace MewtocolNet {
} else { } else {
Logger.Log("Initial connection failed", LogLevel.Info, this); Logger.Log("Initial connection failed", LogLevel.Error, this);
OnDisconnect(); OnDisconnect();
} }

View File

@@ -6,7 +6,7 @@ namespace MewtocolNet.RegisterAttributes {
/// <summary> /// <summary>
/// A register collection base with full auto read and notification support built in /// A register collection base with full auto read and notification support built in
/// </summary> /// </summary>
public class RegisterCollectionBase : INotifyPropertyChanged { public class RegisterCollection : INotifyPropertyChanged {
/// <summary> /// <summary>
/// Reference to its bound interface /// Reference to its bound interface

View File

@@ -21,7 +21,7 @@ namespace MewtocolNet.RegisterBuilding {
}; };
public static RegBuilder ForInterface (IPlcEthernet interf) { public static RegBuilder ForInterface (IPlc interf) {
var rb = new RegBuilder(); var rb = new RegBuilder();
rb.forInterface = interf as MewtocolInterface; rb.forInterface = interf as MewtocolInterface;

View File

@@ -15,7 +15,7 @@ namespace MewtocolTests {
this.output = output; this.output = output;
} }
public class TestRegisterCollection : RegisterCollectionBase { public class TestRegisterCollection : RegisterCollection {
//corresponds to a R100 boolean register in the PLC //corresponds to a R100 boolean register in the PLC
//can also be written as R1000 because the last one is a special address //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")] [Fact(DisplayName = "Boolean R generation")]
public void BooleanGen() { public void BooleanGen() {
var interf = new MewtocolInterfaceShared("192.168.0.1"); var interf = Mewtocol.Ethernet("192.168.0.1");
interf.WithRegisterCollection(new TestRegisterCollection()).WithPoller(); interf.AddRegisterCollection(new TestRegisterCollection()).WithPoller();
var register = interf.GetRegister(nameof(TestRegisterCollection.TestBool1)); var register = interf.GetRegister(nameof(TestRegisterCollection.TestBool1));
@@ -122,8 +122,8 @@ namespace MewtocolTests {
[Fact(DisplayName = "Boolean input XD generation")] [Fact(DisplayName = "Boolean input XD generation")]
public void BooleanInputGen() { public void BooleanInputGen() {
var interf = new MewtocolInterfaceShared("192.168.0.1"); var interf = Mewtocol.Ethernet("192.168.0.1");
interf.WithRegisterCollection(new TestRegisterCollection()).WithPoller(); interf.AddRegisterCollection(new TestRegisterCollection()).WithPoller();
var register = interf.GetRegister(nameof(TestRegisterCollection.TestBoolInputXD)); var register = interf.GetRegister(nameof(TestRegisterCollection.TestBoolInputXD));
@@ -135,8 +135,8 @@ namespace MewtocolTests {
[Fact(DisplayName = "Int16 generation")] [Fact(DisplayName = "Int16 generation")]
public void Int16Gen() { public void Int16Gen() {
var interf = new MewtocolInterfaceShared("192.168.0.1"); var interf = Mewtocol.Ethernet("192.168.0.1");
interf.WithRegisterCollection(new TestRegisterCollection()).WithPoller(); interf.AddRegisterCollection(new TestRegisterCollection()).WithPoller();
var register = interf.GetRegister(nameof(TestRegisterCollection.TestInt16)); var register = interf.GetRegister(nameof(TestRegisterCollection.TestInt16));
@@ -148,8 +148,8 @@ namespace MewtocolTests {
[Fact(DisplayName = "UInt16 generation")] [Fact(DisplayName = "UInt16 generation")]
public void UInt16Gen() { public void UInt16Gen() {
var interf = new MewtocolInterfaceShared("192.168.0.1"); var interf = Mewtocol.Ethernet("192.168.0.1");
interf.WithRegisterCollection(new TestRegisterCollection()).WithPoller(); interf.AddRegisterCollection(new TestRegisterCollection()).WithPoller();
var register = interf.GetRegister(nameof(TestRegisterCollection.TestUInt16)); var register = interf.GetRegister(nameof(TestRegisterCollection.TestUInt16));
@@ -161,8 +161,8 @@ namespace MewtocolTests {
[Fact(DisplayName = "Int32 generation")] [Fact(DisplayName = "Int32 generation")]
public void Int32Gen() { public void Int32Gen() {
var interf = new MewtocolInterfaceShared("192.168.0.1"); var interf = Mewtocol.Ethernet("192.168.0.1");
interf.WithRegisterCollection(new TestRegisterCollection()).WithPoller(); interf.AddRegisterCollection(new TestRegisterCollection()).WithPoller();
var register = interf.GetRegister(nameof(TestRegisterCollection.TestInt32)); var register = interf.GetRegister(nameof(TestRegisterCollection.TestInt32));
@@ -174,8 +174,8 @@ namespace MewtocolTests {
[Fact(DisplayName = "UInt32 generation")] [Fact(DisplayName = "UInt32 generation")]
public void UInt32Gen() { public void UInt32Gen() {
var interf = new MewtocolInterfaceShared("192.168.0.1"); var interf = Mewtocol.Ethernet("192.168.0.1");
interf.WithRegisterCollection(new TestRegisterCollection()).WithPoller(); interf.AddRegisterCollection(new TestRegisterCollection()).WithPoller();
var register = interf.GetRegister(nameof(TestRegisterCollection.TestUInt32)); var register = interf.GetRegister(nameof(TestRegisterCollection.TestUInt32));
@@ -187,8 +187,8 @@ namespace MewtocolTests {
[Fact(DisplayName = "Float32 generation")] [Fact(DisplayName = "Float32 generation")]
public void Float32Gen() { public void Float32Gen() {
var interf = new MewtocolInterfaceShared("192.168.0.1"); var interf = Mewtocol.Ethernet("192.168.0.1");
interf.WithRegisterCollection(new TestRegisterCollection()).WithPoller(); interf.AddRegisterCollection(new TestRegisterCollection()).WithPoller();
var register = interf.GetRegister(nameof(TestRegisterCollection.TestFloat32)); var register = interf.GetRegister(nameof(TestRegisterCollection.TestFloat32));
@@ -200,8 +200,8 @@ namespace MewtocolTests {
[Fact(DisplayName = "TimeSpan generation")] [Fact(DisplayName = "TimeSpan generation")]
public void TimespanGen() { public void TimespanGen() {
var interf = new MewtocolInterfaceShared("192.168.0.1"); var interf = Mewtocol.Ethernet("192.168.0.1");
interf.WithRegisterCollection(new TestRegisterCollection()).WithPoller(); interf.AddRegisterCollection(new TestRegisterCollection()).WithPoller();
var register = interf.GetRegister(nameof(TestRegisterCollection.TestTime)); 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<string>)register).ReservedSize);
// Assert.Equal(4, ((BytesRegister<string>)register).MemoryLength);
//}
} }
} }

View File

@@ -45,10 +45,10 @@ namespace MewtocolTests
AfterWriteValue = true, AfterWriteValue = true,
}, },
new RegisterReadWriteTest { new RegisterReadWriteTest {
TargetRegister = new NumberRegister<int>(3000), TargetRegister = new NumberRegister<short>(3000),
RegisterPlcAddressName = "DT3000", RegisterPlcAddressName = "DT3000",
IntialValue = (int)0, IntialValue = (short)0,
AfterWriteValue = (int)-513, AfterWriteValue = (short)-513,
}, },
}; };
@@ -73,9 +73,9 @@ namespace MewtocolTests
output.WriteLine($"Testing: {plc.PLCName}"); 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); Assert.True(cycleClient.IsConnected);
@@ -94,9 +94,9 @@ namespace MewtocolTests
output.WriteLine($"Testing: {plc.PLCName}\n"); 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"); output.WriteLine($"{client.PlcInfo}\n");
@@ -111,38 +111,43 @@ namespace MewtocolTests
} }
//[Fact(DisplayName = "Reading basic information from PLC")] [Fact(DisplayName = "Reading basic information from PLC")]
//public async void TestRegisterReadWriteAsync () { 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(); await client.ConnectAsync();
// Assert.True(client.IsConnected); 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); await testRegister.WriteAsync(testRW.AfterWriteValue);
// Assert.Equal(client.PlcInfo.CpuInformation.ProgramCapacity, plc.ProgCapacity);
// client.Disconnect(); //test after write val
Assert.Equal(testRW.AfterWriteValue, testRegister.Value);
// } }
//} client.Disconnect();
}
}
} }