Made the interface disposable and refactoring

This commit is contained in:
Felix Weiß
2022-07-18 09:55:36 +02:00
parent 6c411d7318
commit fe2d2b9fb9
3 changed files with 209 additions and 87 deletions

View File

@@ -4,16 +4,35 @@ 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) {
Console.WriteLine("Enter your scenario number:\n" +
"1 = Permanent connection\n" +
"2 = Dispose connection");
var line = Console.ReadLine();
if(line == "1") {
Scenario1();
}
if (line == "2") {
Scenario2();
}
Console.ReadLine();
}
static void Scenario1 () {
Task.Factory.StartNew(async () => { Task.Factory.StartNew(async () => {
//attaching the logger //attaching the logger
Logger.LogLevel = LogLevel.Verbose; Logger.LogLevel = LogLevel.Critical;
Logger.OnNewLogMessage((date, msg) => { Logger.OnNewLogMessage((date, msg) => {
Console.WriteLine($"{date.ToString("HH:mm:ss")} {msg}"); Console.WriteLine($"{date.ToString("HH:mm:ss")} {msg}");
}); });
@@ -42,28 +61,16 @@ namespace Examples {
await interf.SetRegisterAsync(nameof(registers.TestInt32), 100); 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 //adds 10 each time the plc connects to the PLCs INT regíster
//interf.SetRegister(nameof(registers.TestInt16), (short)(registers.TestInt16 + 10)); interf.SetRegister(nameof(registers.TestInt16), (short)(registers.TestInt16 + 10));
//adds 1 each time the plc connects to the PLCs DINT regíster //adds 1 each time the plc connects to the PLCs DINT regíster
//interf.SetRegister(nameof(registers.TestInt32), (registers.TestInt32 + 1)); interf.SetRegister(nameof(registers.TestInt32), (registers.TestInt32 + 1));
//adds 11.11 each time the plc connects to the PLCs REAL regíster //adds 11.11 each time the plc connects to the PLCs REAL regíster
//interf.SetRegister(nameof(registers.TestFloat32), (float)(registers.TestFloat32 + 11.11)); interf.SetRegister(nameof(registers.TestFloat32), (float)(registers.TestFloat32 + 11.11));
//writes 'Hello' to the PLCs string register //writes 'Hello' to the PLCs string register
//interf.SetRegister(nameof(registers.TestString2), "Hello"); interf.SetRegister(nameof(registers.TestString2), "Hello");
//set the current second to the PLCs TIME register //set the current second to the PLCs TIME register
//interf.SetRegister(nameof(registers.TestTime), TimeSpan.FromSeconds(DateTime.Now.Second)); interf.SetRegister(nameof(registers.TestTime), TimeSpan.FromSeconds(DateTime.Now.Second));
}); });
@@ -72,7 +79,51 @@ namespace Examples {
}); });
Console.ReadLine();
} }
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();
}
});
}
} }

View File

@@ -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) {

View File

@@ -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;
} }