mirror of
https://github.com/OpenLogics/MewtocolNet.git
synced 2025-12-06 03:01:24 +00:00
Changed poller structure and attachment method
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
using System.Threading.Tasks;
|
||||
using System.Text.Json;
|
||||
using MewtocolNet;
|
||||
using MewtocolNet.Responses;
|
||||
|
||||
namespace Examples {
|
||||
class Program {
|
||||
@@ -16,6 +17,8 @@ namespace Examples {
|
||||
interf.AddRegister<short>("Cooler Status",1204);
|
||||
interf.AddRegister<string>(1101, 4);
|
||||
|
||||
interf.WithPoller();
|
||||
|
||||
interf.RegisterChanged += (o) => {
|
||||
Console.WriteLine($"DT{o.MemoryAdress} {(o.Name != null ? $"({o.Name}) " : "")}changed to {o.GetValueString()}");
|
||||
};
|
||||
@@ -24,11 +27,16 @@ namespace Examples {
|
||||
(plcinf) => {
|
||||
|
||||
Console.WriteLine("Connected to PLC:\n" + plcinf.ToString());
|
||||
|
||||
//read back a register value
|
||||
var statusNum = (NRegister<short>)interf.Registers[1204];
|
||||
Console.WriteLine($"Status num is: {statusNum.Value}");
|
||||
|
||||
},
|
||||
() => {
|
||||
Console.WriteLine("Failed connection");
|
||||
}
|
||||
).AttachContinousReader(50);
|
||||
);
|
||||
|
||||
|
||||
});
|
||||
|
||||
@@ -7,49 +7,129 @@ using System.Threading.Tasks;
|
||||
using MewtocolNet.Responses;
|
||||
|
||||
namespace MewtocolNet {
|
||||
public partial class MewtocolInterface {
|
||||
|
||||
public event Action<Register> RegisterChanged;
|
||||
|
||||
internal CancellationTokenSource cTokenAutoUpdater;
|
||||
protected internal bool isWriting = false;
|
||||
|
||||
|
||||
public bool ContinousReaderRunning { get; set; }
|
||||
public List<Register> Registers { get; set; } = new List<Register>();
|
||||
public List<Contact> Contacts { get; set; } = new List<Contact>();
|
||||
|
||||
/// <summary>
|
||||
/// Trys to connect to the PLC by the IP given in the constructor
|
||||
/// The PLC com interface class
|
||||
/// </summary>
|
||||
/// <param name="OnConnected">Gets called when a connection with a PLC was established</param>
|
||||
/// <param name="OnFailed">Gets called when an error or timeout during connection occurs</param>
|
||||
/// <returns></returns>
|
||||
public async Task<MewtocolInterface> ConnectAsync (Action<PLCInfo> OnConnected = null, Action OnFailed = null) {
|
||||
public partial class MewtocolInterface {
|
||||
|
||||
internal event Action PolledCycle;
|
||||
internal CancellationTokenSource cTokenAutoUpdater;
|
||||
internal bool isWriting;
|
||||
internal bool ContinousReaderRunning;
|
||||
internal bool usePoller = false;
|
||||
|
||||
#region Register Polling
|
||||
|
||||
/// <summary>
|
||||
/// Attaches a continous reader that reads back the Registers and Contacts
|
||||
/// </summary>
|
||||
internal void AttachPoller () {
|
||||
|
||||
if (ContinousReaderRunning) return;
|
||||
|
||||
cTokenAutoUpdater = new CancellationTokenSource();
|
||||
|
||||
Console.WriteLine("Attaching cont reader");
|
||||
|
||||
Task.Factory.StartNew(async () => {
|
||||
|
||||
var plcinf = await GetPLCInfoAsync();
|
||||
if (plcinf == null) {
|
||||
Console.WriteLine("PLC is not reachable");
|
||||
throw new Exception("PLC is not reachable");
|
||||
}
|
||||
if (!plcinf.OperationMode.RunMode) {
|
||||
Console.WriteLine("PLC is not running");
|
||||
throw new Exception("PLC is not running");
|
||||
}
|
||||
|
||||
if(plcinf is not null) {
|
||||
ContinousReaderRunning = true;
|
||||
|
||||
if(OnConnected != null) OnConnected(plcinf);
|
||||
while (true) {
|
||||
|
||||
} else {
|
||||
//dont update when currently writing a var
|
||||
if (isWriting) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(OnFailed != null) OnFailed();
|
||||
await Task.Delay(pollingDelayMs);
|
||||
foreach (var registerPair in Registers) {
|
||||
|
||||
var reg = registerPair.Value;
|
||||
|
||||
if (reg is NRegister<short> shortReg) {
|
||||
var lastVal = shortReg.Value;
|
||||
var readout = (await ReadNumRegister(shortReg, stationNumber)).Register.Value;
|
||||
if (lastVal != readout) {
|
||||
shortReg.LastValue = readout;
|
||||
InvokeRegisterChanged(shortReg);
|
||||
shortReg.TriggerNotifyChange();
|
||||
}
|
||||
}
|
||||
if (reg is NRegister<ushort> ushortReg) {
|
||||
var lastVal = ushortReg.Value;
|
||||
var readout = (await ReadNumRegister(ushortReg, stationNumber)).Register.Value;
|
||||
if (lastVal != readout) {
|
||||
ushortReg.LastValue = readout;
|
||||
InvokeRegisterChanged(ushortReg);
|
||||
ushortReg.TriggerNotifyChange();
|
||||
}
|
||||
}
|
||||
if (reg is NRegister<int> intReg) {
|
||||
var lastVal = intReg.Value;
|
||||
var readout = (await ReadNumRegister(intReg, stationNumber)).Register.Value;
|
||||
if (lastVal != readout) {
|
||||
intReg.LastValue = readout;
|
||||
InvokeRegisterChanged(intReg);
|
||||
intReg.TriggerNotifyChange();
|
||||
}
|
||||
}
|
||||
if (reg is NRegister<uint> uintReg) {
|
||||
var lastVal = uintReg.Value;
|
||||
var readout = (await ReadNumRegister(uintReg, stationNumber)).Register.Value;
|
||||
if (lastVal != readout) {
|
||||
uintReg.LastValue = readout;
|
||||
InvokeRegisterChanged(uintReg);
|
||||
uintReg.TriggerNotifyChange();
|
||||
}
|
||||
}
|
||||
if (reg is NRegister<float> floatReg) {
|
||||
var lastVal = floatReg.Value;
|
||||
var readout = (await ReadNumRegister(floatReg, stationNumber)).Register.Value;
|
||||
if (lastVal != readout) {
|
||||
floatReg.LastValue = readout;
|
||||
InvokeRegisterChanged(floatReg);
|
||||
floatReg.TriggerNotifyChange();
|
||||
}
|
||||
} else if (reg is SRegister stringReg) {
|
||||
var lastVal = stringReg.Value;
|
||||
var readout = (await ReadStringRegister(stringReg, stationNumber)).Register.Value;
|
||||
if (lastVal != readout) {
|
||||
InvokeRegisterChanged(stringReg);
|
||||
stringReg.TriggerNotifyChange();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
//invoke cycle polled event
|
||||
InvokePolledCycleDone();
|
||||
|
||||
}
|
||||
|
||||
}, cTokenAutoUpdater.Token);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Register Adding
|
||||
|
||||
/// <summary>
|
||||
/// Adds a PLC memory register to the watchlist <para/>
|
||||
/// The registers can be read back by attaching <see cref="MewtocolInterfaceExtensions.AttachContinousReader(Task{MewtocolInterface}, int)"/>
|
||||
/// <para/>
|
||||
/// to the end of a <see cref="MewtocolInterface.ConnectAsync(Action{PLCInfo}, Action)"/> method
|
||||
/// The registers can be read back by attaching <see cref="WithPoller"/>
|
||||
/// </summary>
|
||||
/// <typeparam name="T">
|
||||
/// The type of the register translated from C# to IEC 61131-3 types
|
||||
@@ -68,17 +148,17 @@ namespace MewtocolNet {
|
||||
Type regType = typeof(T);
|
||||
|
||||
if (regType == typeof(short)) {
|
||||
Registers.Add(new NRegister<short>(_address));
|
||||
Registers.Add(_address, new NRegister<short>(_address));
|
||||
} else if (regType == typeof(ushort)) {
|
||||
Registers.Add(new NRegister<ushort>(_address));
|
||||
Registers.Add(_address, new NRegister<ushort>(_address));
|
||||
} else if (regType == typeof(int)) {
|
||||
Registers.Add(new NRegister<int>(_address));
|
||||
Registers.Add(_address, new NRegister<int>(_address));
|
||||
} else if (regType == typeof(uint)) {
|
||||
Registers.Add(new NRegister<uint>(_address));
|
||||
Registers.Add(_address, new NRegister<uint>(_address));
|
||||
} else if (regType == typeof(float)) {
|
||||
Registers.Add(new NRegister<float>(_address));
|
||||
Registers.Add(_address, new NRegister<float>(_address));
|
||||
} else if (regType == typeof(string)) {
|
||||
Registers.Add(new SRegister(_address, _length));
|
||||
Registers.Add(_address, new SRegister(_address, _length));
|
||||
} else {
|
||||
throw new NotSupportedException($"The type {regType} is not allowed for Registers \n" +
|
||||
$"Allowed are: short, ushort, int, uint, float and string");
|
||||
@@ -88,9 +168,7 @@ namespace MewtocolNet {
|
||||
|
||||
/// <summary>
|
||||
/// Adds a PLC memory register to the watchlist <para/>
|
||||
/// The registers can be read back by attaching <see cref="MewtocolInterfaceExtensions.AttachContinousReader(Task{MewtocolInterface}, int)"/>
|
||||
/// <para/>
|
||||
/// to the end of a <see cref="MewtocolInterface.ConnectAsync(Action{PLCInfo}, Action)"/> method
|
||||
/// The registers can be read back by attaching <see cref="WithPoller"/>
|
||||
/// </summary>
|
||||
/// <typeparam name="T">
|
||||
/// The type of the register translated from C# to IEC 61131-3 types
|
||||
@@ -110,17 +188,17 @@ namespace MewtocolNet {
|
||||
Type regType = typeof(T);
|
||||
|
||||
if (regType == typeof(short)) {
|
||||
Registers.Add(new NRegister<short>(_address, _name));
|
||||
Registers.Add(_address, new NRegister<short>(_address, _name));
|
||||
} else if (regType == typeof(ushort)) {
|
||||
Registers.Add(new NRegister<ushort>(_address, _name));
|
||||
Registers.Add(_address, new NRegister<ushort>(_address, _name));
|
||||
} else if (regType == typeof(int)) {
|
||||
Registers.Add(new NRegister<int>(_address, _name));
|
||||
Registers.Add(_address, new NRegister<int>(_address, _name));
|
||||
} else if (regType == typeof(uint)) {
|
||||
Registers.Add(new NRegister<uint>(_address, _name));
|
||||
Registers.Add(_address, new NRegister<uint>(_address, _name));
|
||||
} else if (regType == typeof(float)) {
|
||||
Registers.Add(new NRegister<float>(_address, _name));
|
||||
Registers.Add(_address, new NRegister<float>(_address, _name));
|
||||
} else if (regType == typeof(string)) {
|
||||
Registers.Add(new SRegister(_address, _length, _name));
|
||||
Registers.Add(_address, new SRegister(_address, _length, _name));
|
||||
} else {
|
||||
throw new NotSupportedException($"The type {regType} is not allowed for Registers \n" +
|
||||
$"Allowed are: short, ushort, int, uint, float and string");
|
||||
@@ -136,5 +214,11 @@ namespace MewtocolNet {
|
||||
|
||||
}
|
||||
|
||||
internal void InvokePolledCycleDone () {
|
||||
|
||||
PolledCycle?.Invoke();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,32 +10,140 @@ using MewtocolNet.Responses;
|
||||
|
||||
namespace MewtocolNet {
|
||||
|
||||
/// <summary>
|
||||
/// The PLC com interface class
|
||||
/// </summary>
|
||||
public partial class MewtocolInterface {
|
||||
|
||||
/// <summary>
|
||||
/// Gets triggered when the PLC connection was established
|
||||
/// </summary>
|
||||
public event Action<PLCInfo> Connected;
|
||||
|
||||
/// <summary>
|
||||
/// Gets triggered when a registered data register changes its value
|
||||
/// </summary>
|
||||
public event Action<Register> RegisterChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Generic information about the connected PLC
|
||||
/// </summary>
|
||||
public PLCInfo PlcInfo {get;private set;}
|
||||
|
||||
/// <summary>
|
||||
/// The registered data registers of the PLC
|
||||
/// </summary>
|
||||
public Dictionary<int, Register> Registers { get; set; } = new();
|
||||
|
||||
private CancellationTokenSource tokenSource;
|
||||
|
||||
private string ip {get;set;}
|
||||
private int port {get;set;}
|
||||
public int ConnectionTimeout {get;set;} = 2000;
|
||||
private int stationNumber {get;set;}
|
||||
private int pollingDelayMs {get;set;}
|
||||
|
||||
/// <summary>
|
||||
/// The current IP of the PLC connection
|
||||
/// </summary>
|
||||
public string IpAddress => ip;
|
||||
/// <summary>
|
||||
/// The current port of the PLC connection
|
||||
/// </summary>
|
||||
public int Port => port;
|
||||
/// <summary>
|
||||
/// The station number of the PLC
|
||||
/// </summary>
|
||||
public int StationNumber => stationNumber;
|
||||
|
||||
|
||||
#region Initialization
|
||||
|
||||
/// <summary>
|
||||
/// Builds a new Interfacer for a PLC
|
||||
/// </summary>
|
||||
/// <param name="_ip"></param>
|
||||
/// <param name="_port"></param>
|
||||
public MewtocolInterface (string _ip, int _port = 9094) {
|
||||
/// <param name="_ip">IP adress of the PLC</param>
|
||||
/// <param name="_port">Port of the PLC</param>
|
||||
/// <param name="_station">Station Number of the PLC</param>
|
||||
public MewtocolInterface (string _ip, int _port = 9094, int _station = 1) {
|
||||
|
||||
ip = _ip;
|
||||
port = _port;
|
||||
stationNumber = _station;
|
||||
|
||||
Connected += MewtocolInterface_Connected;
|
||||
|
||||
void MewtocolInterface_Connected (PLCInfo obj) {
|
||||
|
||||
if (usePoller)
|
||||
AttachPoller();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Setup
|
||||
|
||||
/// <summary>
|
||||
/// Trys to connect to the PLC by the IP given in the constructor
|
||||
/// </summary>
|
||||
/// <param name="OnConnected">
|
||||
/// Gets called when a connection with a PLC was established
|
||||
/// <para/>
|
||||
/// If <see cref="WithPoller"/> is used it waits for the first data receive cycle to complete
|
||||
/// </param>
|
||||
/// <param name="OnFailed">Gets called when an error or timeout during connection occurs</param>
|
||||
/// <returns></returns>
|
||||
public async Task<MewtocolInterface> ConnectAsync (Action<PLCInfo> OnConnected = null, Action OnFailed = null) {
|
||||
|
||||
var plcinf = await GetPLCInfoAsync();
|
||||
|
||||
if (plcinf is not null) {
|
||||
|
||||
Connected?.Invoke(plcinf);
|
||||
|
||||
if (OnConnected != null) {
|
||||
|
||||
if (!usePoller) {
|
||||
OnConnected(plcinf);
|
||||
return this;
|
||||
}
|
||||
|
||||
PolledCycle += OnPollCycleDone;
|
||||
void OnPollCycleDone () {
|
||||
OnConnected(plcinf);
|
||||
PolledCycle -= OnPollCycleDone;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (OnFailed != null)
|
||||
OnFailed();
|
||||
|
||||
}
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attaches a poller to the interface that continously
|
||||
/// polls the registered data registers and writes the values to them
|
||||
/// </summary>
|
||||
public MewtocolInterface WithPoller (int pollerDelayMs = 50) {
|
||||
|
||||
pollingDelayMs = pollerDelayMs;
|
||||
usePoller = true;
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Low level command handling
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -10,110 +10,6 @@ namespace MewtocolNet {
|
||||
|
||||
public static class MewtocolInterfaceExtensions {
|
||||
|
||||
/// <summary>
|
||||
/// Attaches a continous reader that reads back the Registers and Contacts
|
||||
/// </summary>
|
||||
public static Task AttachContinousReader (this Task<MewtocolInterface> interfaceTask, int _refreshTimeMS = 200) {
|
||||
|
||||
interfaceTask.Wait(-1);
|
||||
|
||||
var interf = interfaceTask.Result;
|
||||
|
||||
if (interf.ContinousReaderRunning)
|
||||
return Task.CompletedTask;
|
||||
|
||||
interf.cTokenAutoUpdater = new CancellationTokenSource();
|
||||
|
||||
Console.WriteLine("Attaching cont reader");
|
||||
|
||||
Task.Factory.StartNew(async () => {
|
||||
|
||||
var plcinf = await interf.GetPLCInfoAsync();
|
||||
if (plcinf == null) {
|
||||
Console.WriteLine("PLC is not reachable");
|
||||
throw new Exception("PLC is not reachable");
|
||||
}
|
||||
if (!plcinf.OperationMode.RunMode) {
|
||||
Console.WriteLine("PLC is not running");
|
||||
throw new Exception("PLC is not running");
|
||||
}
|
||||
|
||||
interf.ContinousReaderRunning = true;
|
||||
|
||||
while (true) {
|
||||
|
||||
//dont update when currently writing a var
|
||||
if (interf.isWriting) {
|
||||
continue;
|
||||
}
|
||||
|
||||
await Task.Delay(_refreshTimeMS);
|
||||
foreach (var reg in interf.Registers) {
|
||||
|
||||
if (reg is NRegister<short> shortReg) {
|
||||
var lastVal = shortReg.Value;
|
||||
var readout = (await interf.ReadNumRegister(shortReg)).Register.Value;
|
||||
if (lastVal != readout) {
|
||||
shortReg.LastValue = readout;
|
||||
interf.InvokeRegisterChanged(shortReg);
|
||||
shortReg.TriggerNotifyChange();
|
||||
}
|
||||
}
|
||||
if (reg is NRegister<ushort> ushortReg) {
|
||||
var lastVal = ushortReg.Value;
|
||||
var readout = (await interf.ReadNumRegister(ushortReg)).Register.Value;
|
||||
if (lastVal != readout) {
|
||||
ushortReg.LastValue = readout;
|
||||
interf.InvokeRegisterChanged(ushortReg);
|
||||
ushortReg.TriggerNotifyChange();
|
||||
}
|
||||
}
|
||||
if (reg is NRegister<int> intReg) {
|
||||
var lastVal = intReg.Value;
|
||||
var readout = (await interf.ReadNumRegister(intReg)).Register.Value;
|
||||
if (lastVal != readout) {
|
||||
intReg.LastValue = readout;
|
||||
interf.InvokeRegisterChanged(intReg);
|
||||
intReg.TriggerNotifyChange();
|
||||
}
|
||||
}
|
||||
if (reg is NRegister<uint> uintReg) {
|
||||
var lastVal = uintReg.Value;
|
||||
var readout = (await interf.ReadNumRegister(uintReg)).Register.Value;
|
||||
if (lastVal != readout) {
|
||||
uintReg.LastValue = readout;
|
||||
interf.InvokeRegisterChanged(uintReg);
|
||||
uintReg.TriggerNotifyChange();
|
||||
}
|
||||
}
|
||||
if (reg is NRegister<float> floatReg) {
|
||||
var lastVal = floatReg.Value;
|
||||
var readout = (await interf.ReadNumRegister(floatReg)).Register.Value;
|
||||
if (lastVal != readout) {
|
||||
floatReg.LastValue = readout;
|
||||
interf.InvokeRegisterChanged(floatReg);
|
||||
floatReg.TriggerNotifyChange();
|
||||
}
|
||||
} else if (reg is SRegister stringReg) {
|
||||
var lastVal = stringReg.Value;
|
||||
var readout = (await interf.ReadStringRegister(stringReg)).Register.Value;
|
||||
if (lastVal != readout) {
|
||||
interf.InvokeRegisterChanged(stringReg);
|
||||
stringReg.TriggerNotifyChange();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}, interf.cTokenAutoUpdater.Token);
|
||||
|
||||
return Task.CompletedTask;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user