Added intial connection result

This commit is contained in:
Felix Weiß
2023-08-14 19:42:55 +02:00
parent 4fb9910d54
commit a00f56074f
15 changed files with 160 additions and 54 deletions

View File

@@ -4,10 +4,6 @@ namespace MewtocolNet.Events {
public delegate void PlcConnectionEventHandler(object sender, PlcConnectionArgs e); public delegate void PlcConnectionEventHandler(object sender, PlcConnectionArgs e);
public class PlcConnectionArgs : EventArgs { public class PlcConnectionArgs : EventArgs { }
}
} }

View File

@@ -0,0 +1,19 @@
using System;
namespace MewtocolNet.Events {
public delegate void PlcModeChangedEventHandler(object sender, PlcModeArgs e);
public class PlcModeArgs : EventArgs {
public OPMode LastMode { get; internal set; }
public OPMode NowMode { get; internal set; }
public bool ProgToRun { get; internal set; }
public bool RunToProg { get; internal set; }
}
}

View File

@@ -41,6 +41,11 @@ namespace MewtocolNet {
/// </summary> /// </summary>
event RegisterChangedEventHandler RegisterChanged; event RegisterChangedEventHandler RegisterChanged;
/// <summary>
/// Plc mode was changed
/// </summary>
event PlcModeChangedEventHandler ModeChanged;
/// <summary> /// <summary>
/// The current connection state of the interface /// The current connection state of the interface
/// </summary> /// </summary>
@@ -110,7 +115,7 @@ namespace MewtocolNet {
/// </summary> /// </summary>
/// <param name="onConnected">A callback for excecuting something inside the plc connetion process</param> /// <param name="onConnected">A callback for excecuting something inside the plc connetion process</param>
/// <returns></returns> /// <returns></returns>
Task ConnectAsync(Func<Task> onConnected = null); Task<ConnectResult> ConnectAsync(Func<Task> onConnected = null);
/// <summary> /// <summary>
/// Disconnects the device from its current plc connection /// Disconnects the device from its current plc connection

View File

@@ -48,7 +48,7 @@ namespace MewtocolNet {
/// <summary> /// <summary>
/// Tries to establish a connection with the device asynchronously /// Tries to establish a connection with the device asynchronously
/// </summary> /// </summary>
Task ConnectAsync(Func<Task> callBack, Action onTryingConfig); Task<ConnectResult> ConnectAsync(Func<Task> callBack, Action onTryingConfig);
} }

View File

@@ -43,6 +43,9 @@ namespace MewtocolNet {
/// <inheritdoc/> /// <inheritdoc/>
public event PropertyChangedEventHandler PropertyChanged; public event PropertyChangedEventHandler PropertyChanged;
/// <inheritdoc/>
public event PlcModeChangedEventHandler ModeChanged;
#endregion #endregion
#region Private fields #region Private fields
@@ -85,6 +88,7 @@ namespace MewtocolNet {
internal protected System.Timers.Timer cyclicGenericUpdateCounter; internal protected System.Timers.Timer cyclicGenericUpdateCounter;
internal event Action PolledCycle; internal event Action PolledCycle;
internal volatile bool pollerTaskStopped = true; internal volatile bool pollerTaskStopped = true;
internal volatile bool pollerFirstCycle; internal volatile bool pollerFirstCycle;
internal MemoryAreaManager memoryManager; internal MemoryAreaManager memoryManager;
@@ -211,6 +215,7 @@ namespace MewtocolNet {
memoryManager.MemoryLayoutChanged += () => { memoryManager.MemoryLayoutChanged += () => {
OnPropChange(nameof(MemoryAreas)); OnPropChange(nameof(MemoryAreas));
OnPropChange(nameof(Registers));
}; };
@@ -260,7 +265,7 @@ namespace MewtocolNet {
} }
/// <inheritdoc/> /// <inheritdoc/>
public virtual async Task ConnectAsync(Func<Task> callBack = null) { public virtual async Task<ConnectResult> ConnectAsync(Func<Task> callBack = null) {
isConnectingStage = false; isConnectingStage = false;
@@ -296,17 +301,19 @@ namespace MewtocolNet {
Logger.Log($">> OnConnected run complete <<", LogLevel.Verbose, this); Logger.Log($">> OnConnected run complete <<", LogLevel.Verbose, this);
//run all register collection on online tasks }
foreach (var col in registerCollections) {
await col.OnInterfaceLinkedAndOnline(this); //run all register collection on online tasks
foreach (var col in registerCollections) {
} await col.OnInterfaceLinkedAndOnline(this);
Logger.Log($">> OnConnected register collections run complete <<", LogLevel.Verbose, this);
} }
Logger.Log($">> OnConnected register collections run complete <<", LogLevel.Verbose, this);
return ConnectResult.Unknown;
} }
/// <inheritdoc/> /// <inheritdoc/>
@@ -315,7 +322,7 @@ namespace MewtocolNet {
/// <inheritdoc/> /// <inheritdoc/>
public async Task AwaitFirstDataCycleAsync() { public async Task AwaitFirstDataCycleAsync() {
if(firstPollTask != null && !firstPollTask.IsCompleted) if(firstPollTask != null && !firstPollTask.IsCompleted && IsConnected)
await firstPollTask; await firstPollTask;
await Task.CompletedTask; await Task.CompletedTask;
@@ -430,6 +437,7 @@ namespace MewtocolNet {
if (regularSendTask != null && !regularSendTask.IsCompleted) { if (regularSendTask != null && !regularSendTask.IsCompleted) {
//queue self //queue self
Logger.LogCritical($"Queued {_msg}...", this);
return await EnqueueMessage(_msg, onReceiveProgress); return await EnqueueMessage(_msg, onReceiveProgress);
} }
@@ -510,6 +518,8 @@ namespace MewtocolNet {
} }
await Task.CompletedTask;
} }
protected async Task<MewtocolFrameResponse> EnqueueMessage(string _msg, Action<double> onReceiveProgress = null) { protected async Task<MewtocolFrameResponse> EnqueueMessage(string _msg, Action<double> onReceiveProgress = null) {
@@ -1005,6 +1015,17 @@ namespace MewtocolNet {
} }
internal void InvokeModeChanged(OPMode before, OPMode now) {
ModeChanged?.Invoke(this, new PlcModeArgs {
ProgToRun = !before.HasFlag(OPMode.RunMode) && now.HasFlag(OPMode.RunMode),
RunToProg = before.HasFlag(OPMode.RunMode) && !now.HasFlag(OPMode.RunMode),
LastMode = before,
NowMode = now,
});
}
#endregion #endregion
/// <inheritdoc/> /// <inheritdoc/>

View File

@@ -221,19 +221,19 @@ namespace MewtocolNet {
private async Task OnMultiFrameCycle() { private async Task OnMultiFrameCycle() {
//await the timed task before starting a new poller cycle
if (heartbeatNeedsRun) {
await HeartbeatTickTask();
heartbeatNeedsRun = false;
}
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
await memoryManager.PollAllAreasAsync(async () => { await memoryManager.PollAllAreasAsync(async () => {
//await the timed task before starting a new poller cycle
if (heartbeatNeedsRun || pollerFirstCycle) {
await HeartbeatTickTask();
heartbeatNeedsRun = false;
}
await RunOneOpenQueuedTask(); await RunOneOpenQueuedTask();
}); });

View File

@@ -49,7 +49,7 @@ namespace MewtocolNet {
PLCInfo plcInf; PLCInfo plcInf;
//dont overwrite, use first //dont overwrite, use first
if (!PLCInfo.TryFromRT(resRT.Response, out plcInf)) { if (!PLCInfo.TryFromRT(resRT.Response, this, out plcInf)) {
throw new Exception("The RT message could not be parsed"); throw new Exception("The RT message could not be parsed");

View File

@@ -105,12 +105,12 @@ namespace MewtocolNet {
} }
public override async Task ConnectAsync(Func<Task> callBack = null) => await ConnectAsyncPriv(callBack); public override async Task<ConnectResult> ConnectAsync(Func<Task> callBack = null) => await ConnectAsyncPriv(callBack);
public async Task ConnectAsync(Func<Task> callBack = null, Action onTryingConfig = null) => await ConnectAsyncPriv(callBack, onTryingConfig); public async Task<ConnectResult> ConnectAsync(Func<Task> callBack = null, Action onTryingConfig = null) => await ConnectAsyncPriv(callBack, onTryingConfig);
/// <inheritdoc/> /// <inheritdoc/>
private async Task ConnectAsyncPriv(Func<Task> callBack, Action onTryingConfig = null) { private async Task<ConnectResult> ConnectAsyncPriv(Func<Task> callBack, Action onTryingConfig = null) {
var portnames = SerialPort.GetPortNames(); var portnames = SerialPort.GetPortNames();
if (!portnames.Any(x => x == PortName)) if (!portnames.Any(x => x == PortName))
@@ -149,6 +149,7 @@ namespace MewtocolNet {
IsConnected = true; IsConnected = true;
await base.ConnectAsync(callBack); await base.ConnectAsync(callBack);
OnConnected(gotInfo); OnConnected(gotInfo);
return ConnectResult.Connected;
} else { } else {
@@ -169,6 +170,8 @@ namespace MewtocolNet {
tryingSerialConfig -= OnTryConfig; tryingSerialConfig -= OnTryConfig;
return ConnectResult.MewtocolError;
} }
private async Task<PLCInfo> TryConnectAsyncMulti() { private async Task<PLCInfo> TryConnectAsyncMulti() {

View File

@@ -69,7 +69,7 @@ namespace MewtocolNet {
} }
/// <inheritdoc/> /// <inheritdoc/>
public override async Task ConnectAsync(Func<Task> callBack = null) => await ConnectAsyncPriv(callBack); public override async Task<ConnectResult> ConnectAsync(Func<Task> callBack = null) => await ConnectAsyncPriv(callBack);
private void BuildTcpClient () { private void BuildTcpClient () {
@@ -107,7 +107,7 @@ namespace MewtocolNet {
} }
private async Task ConnectAsyncPriv(Func<Task> callBack = null) { private async Task<ConnectResult> ConnectAsyncPriv(Func<Task> callBack = null) {
try { try {
@@ -118,6 +118,13 @@ namespace MewtocolNet {
BuildTcpClient(); BuildTcpClient();
var ep = (IPEndPoint)client.Client.LocalEndPoint;
if(ep != null) {
Logger.Log($"Connecting from: {ep.Address.MapToIPv4()}:{ep.Port} to {GetConnectionInfo()}", LogLevel.Info, this);
} else {
Logger.Log($"Connecting to {GetConnectionInfo()}", LogLevel.Info, this);
}
var result = client.BeginConnect(ipAddr, 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));
@@ -125,17 +132,12 @@ namespace MewtocolNet {
Logger.Log("The PLC connection timed out", LogLevel.Error, this); Logger.Log("The PLC connection timed out", LogLevel.Error, this);
OnMajorSocketExceptionWhileConnecting(); OnMajorSocketExceptionWhileConnecting();
return; return ConnectResult.Timeout;
} }
Logger.LogVerbose("TCP/IP Client connected", this); Logger.LogVerbose("TCP/IP Client connected", this);
if (HostEndpoint == null) {
var ep = (IPEndPoint)client.Client.LocalEndPoint;
Logger.Log($"Connecting [AUTO] from: {ep.Address.MapToIPv4()}:{ep.Port} to {GetConnectionInfo()}", LogLevel.Info, this);
}
//get the stream //get the stream
stream = client.GetStream(); stream = client.GetStream();
stream.ReadTimeout = 1000; stream.ReadTimeout = 1000;
@@ -150,6 +152,7 @@ namespace MewtocolNet {
IsConnected = true; IsConnected = true;
await base.ConnectAsync(callBack); await base.ConnectAsync(callBack);
OnConnected(plcinf); OnConnected(plcinf);
return ConnectResult.Connected;
} else { } else {
@@ -167,6 +170,8 @@ namespace MewtocolNet {
} }
return ConnectResult.MewtocolError;
} }
protected override async Task ReconnectAsync (int conTimeout) { protected override async Task ReconnectAsync (int conTimeout) {

View File

@@ -13,6 +13,8 @@ namespace MewtocolNet {
/// </summary> /// </summary>
public class PLCInfo : INotifyPropertyChanged { public class PLCInfo : INotifyPropertyChanged {
private MewtocolInterface plc;
private PlcType typeCode; private PlcType typeCode;
private string typeName; private string typeName;
private OPMode operationMode; private OPMode operationMode;
@@ -55,10 +57,17 @@ namespace MewtocolNet {
public OPMode OperationMode { public OPMode OperationMode {
get => operationMode; get => operationMode;
internal set { internal set {
var lastModeFlags = operationMode;
operationMode = value; operationMode = value;
OnPropChange(); OnPropChange();
OnPropChange(nameof(IsRunMode)); OnPropChange(nameof(IsRunMode));
OnPropChange(nameof(OperationModeTags)); OnPropChange(nameof(OperationModeTags));
if (plc != null && plc.IsConnected && !plc.isConnectingStage && lastModeFlags != OPMode.None)
plc.InvokeModeChanged(lastModeFlags, value);
} }
} }
@@ -117,6 +126,12 @@ namespace MewtocolNet {
public event PropertyChangedEventHandler PropertyChanged; public event PropertyChangedEventHandler PropertyChanged;
internal PLCInfo (MewtocolInterface onInterface) {
plc = onInterface;
}
internal bool TryExtendFromEXRT(string msg) { internal bool TryExtendFromEXRT(string msg) {
var regexEXRT = new Regex(@"\%EE\$EX00RT00(?<icnt>..)(?<mc>..)..(?<cap>..)(?<op>..)..(?<flg>..)(?<sdiag>....)(?<ver>..)(?<hwif>..)(?<nprog>.)(?<csumpz>...)(?<psize>...).*", RegexOptions.IgnoreCase); var regexEXRT = new Regex(@"\%EE\$EX00RT00(?<icnt>..)(?<mc>..)..(?<cap>..)(?<op>..)..(?<flg>..)(?<sdiag>....)(?<ver>..)(?<hwif>..)(?<nprog>.)(?<csumpz>...)(?<psize>...).*", RegexOptions.IgnoreCase);
@@ -158,7 +173,7 @@ namespace MewtocolNet {
} }
internal static bool TryFromRT(string msg, out PLCInfo inf) { internal static bool TryFromRT(string msg, MewtocolInterface onInterface, out PLCInfo inf) {
var regexRT = new Regex(@"\%EE\$RT(?<cputype>..)(?<cpuver>..)(?<cap>..)(?<op>..)..(?<flg>..)(?<sdiag>....).*", RegexOptions.IgnoreCase); var regexRT = new Regex(@"\%EE\$RT(?<cputype>..)(?<cpuver>..)(?<cap>..)(?<op>..)..(?<flg>..)(?<sdiag>....).*", RegexOptions.IgnoreCase);
var match = regexRT.Match(msg); var match = regexRT.Match(msg);
@@ -191,7 +206,7 @@ namespace MewtocolNet {
} }
inf = new PLCInfo { inf = new PLCInfo (onInterface) {
TypeCode = typeCodeFull, TypeCode = typeCodeFull,
CpuVersion = match.Groups["cpuver"].Value.Insert(1, "."), CpuVersion = match.Groups["cpuver"].Value.Insert(1, "."),
ProgramCapacity = definedProgCapacity, ProgramCapacity = definedProgCapacity,
@@ -211,7 +226,7 @@ namespace MewtocolNet {
/// <summary> /// <summary>
/// Plc info when its not connected /// Plc info when its not connected
/// </summary> /// </summary>
public static PLCInfo None => new PLCInfo() { public static PLCInfo None => new PLCInfo(null) {
SelfDiagnosticError = "", SelfDiagnosticError = "",
CpuVersion = "", CpuVersion = "",

View File

@@ -0,0 +1,31 @@
namespace MewtocolNet {
/// <summary>
/// The result of an initial connection
/// </summary>
public enum ConnectResult {
/// <summary>
/// There is no known reason for the connection to fail
/// </summary>
Unknown = 0,
/// <summary>
/// The PLC did establish the connection as expected
/// </summary>
Connected = 1,
/// <summary>
/// The tcp/serial connection to the device timed out (PLC did not respond within time limits)
/// </summary>
Timeout = 2,
/// <summary>
/// The PLC sent a an error message on the type determination stage
/// </summary>
MewtocolError = 3,
/// <summary>
/// The metadata of the PLC did not match the required metadata for the interface
/// </summary>
MismatchMetadata = 4,
}
}

View File

@@ -36,6 +36,11 @@ namespace MewtocolNet.Registers {
/// </summary> /// </summary>
string Name { get; } string Name { get; }
/// <summary>
/// Names of the bound properties, comma seperated
/// </summary>
string BoundPropertyNamesString { get; }
/// <summary> /// <summary>
/// The poll level this register is attached to /// The poll level this register is attached to
/// </summary> /// </summary>

View File

@@ -72,6 +72,9 @@ namespace MewtocolNet.Registers {
/// <inheritdoc/> /// <inheritdoc/>
public string Name => name; public string Name => name;
/// <inheritdoc/>
public string BoundPropertyNamesString => string.Join(", ", boundProperties.Select(x => $"{x.ContainedCollection.GetType().Name}.{x.BoundProperty.Name}"));
/// <inheritdoc/> /// <inheritdoc/>
public string PLCAddressName => GetMewName(); public string PLCAddressName => GetMewName();
@@ -263,13 +266,20 @@ namespace MewtocolNet.Registers {
internal virtual object SetValueFromBytes(byte[] bytes) => throw new NotImplementedException(); internal virtual object SetValueFromBytes(byte[] bytes) => throw new NotImplementedException();
internal void WithBoundProperty(RegisterPropTarget propInfo) => boundProperties.Add(propInfo); internal void WithBoundProperty(RegisterPropTarget propInfo) {
boundProperties.Add(propInfo);
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(BoundPropertyNamesString)));
}
internal void WithBoundProperties(IEnumerable<RegisterPropTarget> propInfos) { internal void WithBoundProperties(IEnumerable<RegisterPropTarget> propInfos) {
foreach (var item in propInfos.ToArray()) foreach (var item in propInfos.ToArray())
boundProperties.Add(item); boundProperties.Add(item);
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(BoundPropertyNamesString)));
} }
#region Default accessors #region Default accessors

View File

@@ -15,6 +15,8 @@ namespace MewtocolNet.Registers {
/// </summary> /// </summary>
public class StringRegister : Register, IStringRegister { public class StringRegister : Register, IStringRegister {
internal int sizeWarning = 0;
internal int reservedStringLength; internal int reservedStringLength;
internal uint byteLength; internal uint byteLength;
@@ -120,15 +122,6 @@ namespace MewtocolNet.Registers {
internal override object SetValueFromBytes (byte[] bytes) { internal override object SetValueFromBytes (byte[] bytes) {
//if string correct the sizing of the byte hint was wrong
var reservedSize = BitConverter.ToInt16(bytes, 0);
if (reservedStringLength != reservedSize && attachedInterface.PlcInfo.IsRunMode && !attachedInterface.isConnectingStage)
throw new NotSupportedException(
$"The STRING register at {GetMewName()} is not correctly sized, " +
$"the size should be STRING[{reservedSize}] instead of STRING[{reservedStringLength}]"
);
AddSuccessRead(); AddSuccessRead();
var parsed = PlcValueParser.Parse<string>(this, bytes); var parsed = PlcValueParser.Parse<string>(this, bytes);

View File

@@ -1,4 +1,5 @@
using MewtocolNet.Registers; using MewtocolNet.Logging;
using MewtocolNet.Registers;
using MewtocolNet.SetupClasses; using MewtocolNet.SetupClasses;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@@ -310,10 +311,12 @@ namespace MewtocolNet.UnderlyingRegisters {
} }
//update registers in poll level //update registers in poll level
foreach (var dtArea in pollLevel.GetAllAreas().ToArray()) { foreach (var area in pollLevel.GetAllAreas().ToArray()) {
Logger.LogVerbose($"Polling: {area}", this.mewInterface);
//set the whole memory area at once //set the whole memory area at once
await dtArea.RequestByteReadAsync(dtArea.AddressStart, dtArea.AddressEnd); await area.RequestByteReadAsync(area.AddressStart, area.AddressEnd);
await inbetweenCallback(); await inbetweenCallback();