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

View File

@@ -48,7 +48,7 @@ namespace MewtocolNet {
/// <summary>
/// Tries to establish a connection with the device asynchronously
/// </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/>
public event PropertyChangedEventHandler PropertyChanged;
/// <inheritdoc/>
public event PlcModeChangedEventHandler ModeChanged;
#endregion
#region Private fields
@@ -85,6 +88,7 @@ namespace MewtocolNet {
internal protected System.Timers.Timer cyclicGenericUpdateCounter;
internal event Action PolledCycle;
internal volatile bool pollerTaskStopped = true;
internal volatile bool pollerFirstCycle;
internal MemoryAreaManager memoryManager;
@@ -184,7 +188,7 @@ namespace MewtocolNet {
public int ConnectTimeout { get; set; } = 3000;
#endregion
#region Methods
private protected MewtocolInterface() {
@@ -211,6 +215,7 @@ namespace MewtocolNet {
memoryManager.MemoryLayoutChanged += () => {
OnPropChange(nameof(MemoryAreas));
OnPropChange(nameof(Registers));
};
@@ -260,7 +265,7 @@ namespace MewtocolNet {
}
/// <inheritdoc/>
public virtual async Task ConnectAsync(Func<Task> callBack = null) {
public virtual async Task<ConnectResult> ConnectAsync(Func<Task> callBack = null) {
isConnectingStage = false;
@@ -296,17 +301,19 @@ namespace MewtocolNet {
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) {
}
Logger.Log($">> OnConnected register collections run complete <<", LogLevel.Verbose, this);
await col.OnInterfaceLinkedAndOnline(this);
}
Logger.Log($">> OnConnected register collections run complete <<", LogLevel.Verbose, this);
return ConnectResult.Unknown;
}
/// <inheritdoc/>
@@ -315,7 +322,7 @@ namespace MewtocolNet {
/// <inheritdoc/>
public async Task AwaitFirstDataCycleAsync() {
if(firstPollTask != null && !firstPollTask.IsCompleted)
if(firstPollTask != null && !firstPollTask.IsCompleted && IsConnected)
await firstPollTask;
await Task.CompletedTask;
@@ -430,6 +437,7 @@ namespace MewtocolNet {
if (regularSendTask != null && !regularSendTask.IsCompleted) {
//queue self
Logger.LogCritical($"Queued {_msg}...", this);
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) {
@@ -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
/// <inheritdoc/>

View File

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

View File

@@ -49,7 +49,7 @@ namespace MewtocolNet {
PLCInfo plcInf;
//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");

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/>
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();
if (!portnames.Any(x => x == PortName))
@@ -149,6 +149,7 @@ namespace MewtocolNet {
IsConnected = true;
await base.ConnectAsync(callBack);
OnConnected(gotInfo);
return ConnectResult.Connected;
} else {
@@ -169,6 +170,8 @@ namespace MewtocolNet {
tryingSerialConfig -= OnTryConfig;
return ConnectResult.MewtocolError;
}
private async Task<PLCInfo> TryConnectAsyncMulti() {

View File

@@ -69,7 +69,7 @@ namespace MewtocolNet {
}
/// <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 () {
@@ -107,7 +107,7 @@ namespace MewtocolNet {
}
private async Task ConnectAsyncPriv(Func<Task> callBack = null) {
private async Task<ConnectResult> ConnectAsyncPriv(Func<Task> callBack = null) {
try {
@@ -118,6 +118,13 @@ namespace MewtocolNet {
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 success = result.AsyncWaitHandle.WaitOne(TimeSpan.FromMilliseconds(ConnectTimeout));
@@ -125,17 +132,12 @@ namespace MewtocolNet {
Logger.Log("The PLC connection timed out", LogLevel.Error, this);
OnMajorSocketExceptionWhileConnecting();
return;
return ConnectResult.Timeout;
}
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
stream = client.GetStream();
stream.ReadTimeout = 1000;
@@ -150,6 +152,7 @@ namespace MewtocolNet {
IsConnected = true;
await base.ConnectAsync(callBack);
OnConnected(plcinf);
return ConnectResult.Connected;
} else {
@@ -167,6 +170,8 @@ namespace MewtocolNet {
}
return ConnectResult.MewtocolError;
}
protected override async Task ReconnectAsync (int conTimeout) {

View File

@@ -13,6 +13,8 @@ namespace MewtocolNet {
/// </summary>
public class PLCInfo : INotifyPropertyChanged {
private MewtocolInterface plc;
private PlcType typeCode;
private string typeName;
private OPMode operationMode;
@@ -55,10 +57,17 @@ namespace MewtocolNet {
public OPMode OperationMode {
get => operationMode;
internal set {
var lastModeFlags = operationMode;
operationMode = value;
OnPropChange();
OnPropChange(nameof(IsRunMode));
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;
internal PLCInfo (MewtocolInterface onInterface) {
plc = onInterface;
}
internal bool TryExtendFromEXRT(string msg) {
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 match = regexRT.Match(msg);
@@ -191,7 +206,7 @@ namespace MewtocolNet {
}
inf = new PLCInfo {
inf = new PLCInfo (onInterface) {
TypeCode = typeCodeFull,
CpuVersion = match.Groups["cpuver"].Value.Insert(1, "."),
ProgramCapacity = definedProgCapacity,
@@ -211,7 +226,7 @@ namespace MewtocolNet {
/// <summary>
/// Plc info when its not connected
/// </summary>
public static PLCInfo None => new PLCInfo() {
public static PLCInfo None => new PLCInfo(null) {
SelfDiagnosticError = "",
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>
string Name { get; }
/// <summary>
/// Names of the bound properties, comma seperated
/// </summary>
string BoundPropertyNamesString { get; }
/// <summary>
/// The poll level this register is attached to
/// </summary>

View File

@@ -72,6 +72,9 @@ namespace MewtocolNet.Registers {
/// <inheritdoc/>
public string Name => name;
/// <inheritdoc/>
public string BoundPropertyNamesString => string.Join(", ", boundProperties.Select(x => $"{x.ContainedCollection.GetType().Name}.{x.BoundProperty.Name}"));
/// <inheritdoc/>
public string PLCAddressName => GetMewName();
@@ -263,13 +266,20 @@ namespace MewtocolNet.Registers {
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) {
foreach (var item in propInfos.ToArray())
boundProperties.Add(item);
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(BoundPropertyNamesString)));
}
#region Default accessors

View File

@@ -15,6 +15,8 @@ namespace MewtocolNet.Registers {
/// </summary>
public class StringRegister : Register, IStringRegister {
internal int sizeWarning = 0;
internal int reservedStringLength;
internal uint byteLength;
@@ -120,15 +122,6 @@ namespace MewtocolNet.Registers {
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();
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 System;
using System.Collections.Generic;
@@ -310,10 +311,12 @@ namespace MewtocolNet.UnderlyingRegisters {
}
//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
await dtArea.RequestByteReadAsync(dtArea.AddressStart, dtArea.AddressEnd);
await area.RequestByteReadAsync(area.AddressStart, area.AddressEnd);
await inbetweenCallback();