mirror of
https://github.com/OpenLogics/MewtocolNet.git
synced 2025-12-06 03:01:24 +00:00
Made the interface disposable and refactoring
This commit is contained in:
@@ -4,75 +4,126 @@ using MewtocolNet;
|
|||||||
using MewtocolNet.Logging;
|
using MewtocolNet.Logging;
|
||||||
using MewtocolNet.Registers;
|
using MewtocolNet.Registers;
|
||||||
|
|
||||||
namespace Examples {
|
namespace Examples;
|
||||||
|
|
||||||
class Program {
|
class Program {
|
||||||
|
|
||||||
static void Main(string[] args) {
|
static void Main(string[] args) {
|
||||||
|
|
||||||
Task.Factory.StartNew(async () => {
|
Console.WriteLine("Enter your scenario number:\n" +
|
||||||
|
"1 = Permanent connection\n" +
|
||||||
|
"2 = Dispose connection");
|
||||||
|
|
||||||
//attaching the logger
|
var line = Console.ReadLine();
|
||||||
Logger.LogLevel = LogLevel.Verbose;
|
|
||||||
Logger.OnNewLogMessage((date, msg) => {
|
|
||||||
Console.WriteLine($"{date.ToString("HH:mm:ss")} {msg}");
|
|
||||||
});
|
|
||||||
|
|
||||||
//setting up a new PLC interface and register collection
|
if(line == "1") {
|
||||||
MewtocolInterface interf = new MewtocolInterface("10.237.191.3");
|
Scenario1();
|
||||||
TestRegisters registers = new TestRegisters();
|
}
|
||||||
|
|
||||||
//attaching the register collection and an automatic poller
|
if (line == "2") {
|
||||||
interf.WithRegisterCollection(registers).WithPoller();
|
Scenario2();
|
||||||
|
}
|
||||||
|
|
||||||
await interf.ConnectAsync(
|
Console.ReadLine();
|
||||||
(plcinf) => {
|
}
|
||||||
|
|
||||||
//reading a value from the register collection
|
static void Scenario1 () {
|
||||||
Console.WriteLine($"BitValue is: {registers.BitValue}");
|
|
||||||
Console.WriteLine($"TestEnum is: {registers.TestEnum}");
|
|
||||||
|
|
||||||
//writing a value to the registers
|
Task.Factory.StartNew(async () => {
|
||||||
Task.Factory.StartNew(async () => {
|
|
||||||
|
|
||||||
//set plc to run mode if not already
|
|
||||||
await interf.SetOperationMode(OPMode.Run);
|
|
||||||
|
|
||||||
await Task.Delay(2000);
|
|
||||||
|
|
||||||
await interf.SetRegisterAsync(nameof(registers.TestInt32), 100);
|
|
||||||
|
|
||||||
_ = Task.Factory.StartNew(async () => {
|
|
||||||
while(true) {
|
|
||||||
for (int i = 0; i < 5; i++) {
|
|
||||||
var bytes = await interf.ReadByteRange(1020, 20);
|
|
||||||
await interf.SetRegisterAsync(nameof(registers.TestBool1), !registers.TestBool1);
|
|
||||||
await interf.SetRegisterAsync(nameof(registers.TestInt32), registers.TestInt32 + 100);
|
|
||||||
await Task.Delay(1333);
|
|
||||||
}
|
|
||||||
await Task.Delay(10000);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
//adds 10 each time the plc connects to the PLCs INT regíster
|
|
||||||
//interf.SetRegister(nameof(registers.TestInt16), (short)(registers.TestInt16 + 10));
|
|
||||||
//adds 1 each time the plc connects to the PLCs DINT regíster
|
|
||||||
//interf.SetRegister(nameof(registers.TestInt32), (registers.TestInt32 + 1));
|
|
||||||
//adds 11.11 each time the plc connects to the PLCs REAL regíster
|
|
||||||
//interf.SetRegister(nameof(registers.TestFloat32), (float)(registers.TestFloat32 + 11.11));
|
|
||||||
//writes 'Hello' to the PLCs string register
|
|
||||||
//interf.SetRegister(nameof(registers.TestString2), "Hello");
|
|
||||||
//set the current second to the PLCs TIME register
|
|
||||||
//interf.SetRegister(nameof(registers.TestTime), TimeSpan.FromSeconds(DateTime.Now.Second));
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
|
//attaching the logger
|
||||||
|
Logger.LogLevel = LogLevel.Critical;
|
||||||
|
Logger.OnNewLogMessage((date, msg) => {
|
||||||
|
Console.WriteLine($"{date.ToString("HH:mm:ss")} {msg}");
|
||||||
});
|
});
|
||||||
|
|
||||||
Console.ReadLine();
|
//setting up a new PLC interface and register collection
|
||||||
}
|
MewtocolInterface interf = new MewtocolInterface("10.237.191.3");
|
||||||
|
TestRegisters registers = new TestRegisters();
|
||||||
|
|
||||||
|
//attaching the register collection and an automatic poller
|
||||||
|
interf.WithRegisterCollection(registers).WithPoller();
|
||||||
|
|
||||||
|
await interf.ConnectAsync(
|
||||||
|
(plcinf) => {
|
||||||
|
|
||||||
|
//reading a value from the register collection
|
||||||
|
Console.WriteLine($"BitValue is: {registers.BitValue}");
|
||||||
|
Console.WriteLine($"TestEnum is: {registers.TestEnum}");
|
||||||
|
|
||||||
|
//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);
|
||||||
|
|
||||||
|
await interf.SetRegisterAsync(nameof(registers.TestInt32), 100);
|
||||||
|
|
||||||
|
//adds 10 each time the plc connects to the PLCs INT regíster
|
||||||
|
interf.SetRegister(nameof(registers.TestInt16), (short)(registers.TestInt16 + 10));
|
||||||
|
//adds 1 each time the plc connects to the PLCs DINT regíster
|
||||||
|
interf.SetRegister(nameof(registers.TestInt32), (registers.TestInt32 + 1));
|
||||||
|
//adds 11.11 each time the plc connects to the PLCs REAL regíster
|
||||||
|
interf.SetRegister(nameof(registers.TestFloat32), (float)(registers.TestFloat32 + 11.11));
|
||||||
|
//writes 'Hello' to the PLCs string register
|
||||||
|
interf.SetRegister(nameof(registers.TestString2), "Hello");
|
||||||
|
//set the current second to the PLCs TIME register
|
||||||
|
interf.SetRegister(nameof(registers.TestTime), TimeSpan.FromSeconds(DateTime.Now.Second));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void Scenario2 () {
|
||||||
|
|
||||||
|
Logger.LogLevel = LogLevel.Critical;
|
||||||
|
Logger.OnNewLogMessage((date, msg) => {
|
||||||
|
Console.WriteLine($"{date.ToString("HH:mm:ss")} {msg}");
|
||||||
|
});
|
||||||
|
|
||||||
|
Task.Factory.StartNew(async () => {
|
||||||
|
|
||||||
|
using(var interf = new MewtocolInterface("10.237.191.3")) {
|
||||||
|
|
||||||
|
await interf.ConnectAsync();
|
||||||
|
|
||||||
|
if(interf.IsConnected) {
|
||||||
|
|
||||||
|
var plcInf = await interf.GetPLCInfoAsync();
|
||||||
|
Console.WriteLine(plcInf);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
interf.Disconnect();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
using (var interf = new MewtocolInterface("10.237.191.3")) {
|
||||||
|
|
||||||
|
await interf.ConnectAsync();
|
||||||
|
|
||||||
|
if (interf.IsConnected) {
|
||||||
|
|
||||||
|
var plcInf = await interf.GetPLCInfoAsync();
|
||||||
|
Console.WriteLine(plcInf);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
interf.Disconnect();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ namespace MewtocolNet {
|
|||||||
public partial class MewtocolInterface {
|
public partial class MewtocolInterface {
|
||||||
|
|
||||||
internal event Action PolledCycle;
|
internal event Action PolledCycle;
|
||||||
internal CancellationTokenSource cTokenAutoUpdater;
|
|
||||||
internal bool ContinousReaderRunning;
|
internal bool ContinousReaderRunning;
|
||||||
internal bool usePoller = false;
|
internal bool usePoller = false;
|
||||||
|
|
||||||
@@ -24,7 +23,6 @@ namespace MewtocolNet {
|
|||||||
internal void KillPoller () {
|
internal void KillPoller () {
|
||||||
|
|
||||||
ContinousReaderRunning = false;
|
ContinousReaderRunning = false;
|
||||||
cTokenAutoUpdater?.Cancel();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,21 +36,26 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
Task.Factory.StartNew(async () => {
|
Task.Factory.StartNew(async () => {
|
||||||
|
|
||||||
cTokenAutoUpdater = new CancellationTokenSource();
|
|
||||||
|
|
||||||
Logger.Log("Poller is attaching", LogLevel.Info, this);
|
Logger.Log("Poller is attaching", LogLevel.Info, this);
|
||||||
|
|
||||||
int it = 0;
|
int it = 0;
|
||||||
|
ContinousReaderRunning = true;
|
||||||
|
|
||||||
while (it < Registers.Count + 1) {
|
while (ContinousReaderRunning) {
|
||||||
|
|
||||||
if (it >= Registers.Count) {
|
if (it >= Registers.Count + 1) {
|
||||||
it = 0;
|
it = 0;
|
||||||
//invoke cycle polled event
|
//invoke cycle polled event
|
||||||
InvokePolledCycleDone();
|
InvokePolledCycleDone();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (it >= Registers.Count) {
|
||||||
|
await GetPLCInfoAsync();
|
||||||
|
it++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
var reg = Registers[it];
|
var reg = Registers[it];
|
||||||
|
|
||||||
if (reg is NRegister<short> shortReg) {
|
if (reg is NRegister<short> shortReg) {
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ namespace MewtocolNet {
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The PLC com interface class
|
/// The PLC com interface class
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class MewtocolInterface : INotifyPropertyChanged {
|
public partial class MewtocolInterface : INotifyPropertyChanged, IDisposable {
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets triggered when the PLC connection was established
|
/// Gets triggered when the PLC connection was established
|
||||||
@@ -43,6 +43,15 @@ namespace MewtocolNet {
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public event PropertyChangedEventHandler PropertyChanged;
|
public event PropertyChangedEventHandler PropertyChanged;
|
||||||
|
|
||||||
|
private int connectTimeout = 1000;
|
||||||
|
/// <summary>
|
||||||
|
/// The initial connection timeout in milliseconds
|
||||||
|
/// </summary>
|
||||||
|
public int ConnectTimeout {
|
||||||
|
get { return connectTimeout; }
|
||||||
|
set { connectTimeout = value; }
|
||||||
|
}
|
||||||
|
|
||||||
private bool isConnected;
|
private bool isConnected;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The current connection state of the interface
|
/// The current connection state of the interface
|
||||||
@@ -55,6 +64,16 @@ namespace MewtocolNet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool disposed;
|
||||||
|
/// <summary>
|
||||||
|
/// True if the current interface was disposed
|
||||||
|
/// </summary>
|
||||||
|
public bool Disposed {
|
||||||
|
get { return disposed; }
|
||||||
|
private set { disposed = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private PLCInfo plcInfo;
|
private PLCInfo plcInfo;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generic information about the connected PLC
|
/// Generic information about the connected PLC
|
||||||
@@ -75,6 +94,7 @@ namespace MewtocolNet {
|
|||||||
private string ip;
|
private string ip;
|
||||||
private int port;
|
private int port;
|
||||||
private int stationNumber;
|
private int stationNumber;
|
||||||
|
private int cycleTimeMs = 25;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The current IP of the PLC connection
|
/// The current IP of the PLC connection
|
||||||
@@ -89,7 +109,6 @@ namespace MewtocolNet {
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public int StationNumber => stationNumber;
|
public int StationNumber => stationNumber;
|
||||||
|
|
||||||
private int cycleTimeMs = 25;
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The duration of the last message cycle
|
/// The duration of the last message cycle
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -135,7 +154,6 @@ namespace MewtocolNet {
|
|||||||
RegisterChanged += (o) => {
|
RegisterChanged += (o) => {
|
||||||
|
|
||||||
string address = $"{o.GetRegisterString()}{o.MemoryAdress}".PadRight(5, (char)32);
|
string address = $"{o.GetRegisterString()}{o.MemoryAdress}".PadRight(5, (char)32);
|
||||||
;
|
|
||||||
|
|
||||||
Logger.Log($"{address} " +
|
Logger.Log($"{address} " +
|
||||||
$"{(o.Name != null ? $"({o.Name}) " : "")}" +
|
$"{(o.Name != null ? $"({o.Name}) " : "")}" +
|
||||||
@@ -200,14 +218,14 @@ namespace MewtocolNet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Closes all permanent polling
|
/// Closes the connection all cyclic polling
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Disconnect () {
|
public void Disconnect () {
|
||||||
|
|
||||||
if (!IsConnected)
|
if (!IsConnected)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
OnMajorSocketException();
|
OnMajorSocketExceptionWhileConnected();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,6 +243,70 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region TCP connection state handling
|
||||||
|
|
||||||
|
private async Task ConnectTCP () {
|
||||||
|
|
||||||
|
if (!IPAddress.TryParse(ip, out var targetIP)) {
|
||||||
|
throw new ArgumentException("The IP adress of the PLC was no valid format");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
client = new TcpClient() {
|
||||||
|
ReceiveBufferSize = RecBufferSize,
|
||||||
|
NoDelay = false,
|
||||||
|
ExclusiveAddressUse = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = client.BeginConnect(targetIP, port, null, null);
|
||||||
|
var success = result.AsyncWaitHandle.WaitOne(TimeSpan.FromMilliseconds(ConnectTimeout));
|
||||||
|
|
||||||
|
if(!success) {
|
||||||
|
OnMajorSocketExceptionWhileConnecting();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream = client.GetStream();
|
||||||
|
stream.ReadTimeout = 1000;
|
||||||
|
|
||||||
|
Console.WriteLine($"Connected {client.Connected}");
|
||||||
|
await Task.CompletedTask;
|
||||||
|
|
||||||
|
} catch (SocketException) {
|
||||||
|
|
||||||
|
OnMajorSocketExceptionWhileConnecting();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnMajorSocketExceptionWhileConnecting () {
|
||||||
|
|
||||||
|
Logger.Log("The PLC connection timed out", LogLevel.Error, this);
|
||||||
|
CycleTimeMs = 0;
|
||||||
|
IsConnected = false;
|
||||||
|
KillPoller();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnMajorSocketExceptionWhileConnected () {
|
||||||
|
|
||||||
|
if (IsConnected) {
|
||||||
|
|
||||||
|
Logger.Log("The PLC connection was closed", LogLevel.Error, this);
|
||||||
|
CycleTimeMs = 0;
|
||||||
|
IsConnected = false;
|
||||||
|
Disconnected?.Invoke();
|
||||||
|
KillPoller();
|
||||||
|
client.Close();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region Register Collection
|
#region Register Collection
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -547,25 +629,6 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
#region Low level command handling
|
#region Low level command handling
|
||||||
|
|
||||||
private async Task ConnectTCP () {
|
|
||||||
|
|
||||||
var targetIP = IPAddress.Parse(ip);
|
|
||||||
|
|
||||||
client = new TcpClient() {
|
|
||||||
ReceiveBufferSize = RecBufferSize,
|
|
||||||
NoDelay = false,
|
|
||||||
ExclusiveAddressUse = true
|
|
||||||
};
|
|
||||||
|
|
||||||
await client.ConnectAsync(targetIP, port);
|
|
||||||
|
|
||||||
stream = client.GetStream();
|
|
||||||
stream.ReadTimeout = 1000;
|
|
||||||
|
|
||||||
Console.WriteLine($"Connected {client.Connected}");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Calculates checksum and sends a command to the PLC then awaits results
|
/// Calculates checksum and sends a command to the PLC then awaits results
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -668,12 +731,12 @@ namespace MewtocolNet {
|
|||||||
Logger.Log($"Critical IO exception on receive", LogLevel.Critical, this);
|
Logger.Log($"Critical IO exception on receive", LogLevel.Critical, this);
|
||||||
return null;
|
return null;
|
||||||
} catch (SocketException) {
|
} catch (SocketException) {
|
||||||
OnMajorSocketException();
|
OnMajorSocketExceptionWhileConnected();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!string.IsNullOrEmpty(response.ToString())) {
|
if(!string.IsNullOrEmpty(response.ToString())) {
|
||||||
Logger.Log($"<-- IN MSG (TXT): {response} ({response.Length} bytes)", LogLevel.Critical, this);
|
Logger.Log($"<-- IN MSG: {response}", LogLevel.Critical, this);
|
||||||
return response.ToString();
|
return response.ToString();
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
@@ -681,17 +744,22 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnMajorSocketException () {
|
#endregion
|
||||||
|
|
||||||
if (IsConnected) {
|
#region Disposing
|
||||||
|
|
||||||
Logger.Log("The PLC connection was closed", LogLevel.Error, this);
|
/// <summary>
|
||||||
CycleTimeMs = 0;
|
/// Disposes the current interface and clears all its members
|
||||||
IsConnected = false;
|
/// </summary>
|
||||||
Disconnected?.Invoke();
|
public void Dispose () {
|
||||||
KillPoller();
|
|
||||||
|
|
||||||
}
|
if (Disposed) return;
|
||||||
|
|
||||||
|
Disconnect();
|
||||||
|
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
|
||||||
|
Disposed = true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user