From 55893b9d29b6faeb98151202eb780ea21e351bb6 Mon Sep 17 00:00:00 2001 From: Cedric Franke Date: Tue, 14 Jul 2026 08:38:48 +0200 Subject: [PATCH] fixed problems with errormessages and import --- src/S7CommPlusDriver/ClientApi/Browser.cs | 6 +++ src/S7CommPlusDriver/IS7CommPlusSession.cs | 1 + src/S7CommPlusDriver/S7CommPlusClient.cs | 40 +++++++++++++++---- .../S7CommPlusClientOptions.cs | 3 ++ src/S7CommPlusDriver/S7CommPlusDefaults.cs | 1 + .../S7CommPlusProtocolSession.Session.cs | 2 + .../S7CommPlusAlarmSubscriptionService.cs | 6 ++- 7 files changed, 50 insertions(+), 9 deletions(-) diff --git a/src/S7CommPlusDriver/ClientApi/Browser.cs b/src/S7CommPlusDriver/ClientApi/Browser.cs index 3b7ef2a..3d20abb 100644 --- a/src/S7CommPlusDriver/ClientApi/Browser.cs +++ b/src/S7CommPlusDriver/ClientApi/Browser.cs @@ -110,6 +110,10 @@ private void AddFlatSubnodes(Node node, string names, string accessIds, uint Opt Softdatatype = node.Softdatatype, SymbolCrc = S7CommPlusSymbolCrc.ComputeFromSegments(nextCrcPath), SymbolCrcPath = nextCrcPath, + // For array elements the Vte comes from the parent array base element, so its + // HMI attributes still apply. Default to true when unknown so nothing is dropped. + HmiVisible = node.Vte?.GetAttributeFlagHmiVisible() ?? true, + HmiAccessible = node.Vte?.GetAttributeFlagHmiAccessible() ?? true, }; // If an Array element of basic datatype, the Vte is here from the parent array base element and offsets not valid here. if (node.NodeType == eNodeType.Array) @@ -732,6 +736,8 @@ public class VarInfo public int OptBitoffset; // Optimized access: Bit-Offset where the value is located when reading a complete DB content. public UInt32 NonOptAddress; // NonOptimized access: Byte-Offset where the value is located when reading a complete DB content. public int NonOptBitoffset; // NonOptimized access: Bit-Offset where the value is located when reading a complete DB content. + public bool HmiVisible = true; // TIA "Visible in HMI engineering" attribute. + public bool HmiAccessible = true; // TIA "Accessible from HMI/OPC UA/Web server" attribute. internal List SymbolCrcPath; } diff --git a/src/S7CommPlusDriver/IS7CommPlusSession.cs b/src/S7CommPlusDriver/IS7CommPlusSession.cs index 174c41d..dc6f1c8 100644 --- a/src/S7CommPlusDriver/IS7CommPlusSession.cs +++ b/src/S7CommPlusDriver/IS7CommPlusSession.cs @@ -38,6 +38,7 @@ internal interface IS7CommPlusSession int CreateTisWatchSubscription(S7CommPlusTisWatchRequest request, out uint subscriptionObjectId); int WaitForTisWatchNotifications(uint subscriptionObjectId, int timeoutMilliseconds, out List notifications); string LastTisWatchDiagnostic { get; } + string LastAlarmSubscriptionDiagnostic { get; } int DeleteTisWatchSubscription(uint subscriptionObjectId); } } diff --git a/src/S7CommPlusDriver/S7CommPlusClient.cs b/src/S7CommPlusDriver/S7CommPlusClient.cs index 54dfd14..1c02c02 100644 --- a/src/S7CommPlusDriver/S7CommPlusClient.cs +++ b/src/S7CommPlusDriver/S7CommPlusClient.cs @@ -79,7 +79,7 @@ public Task> BrowseAsync(CancellationToken cancellationTo var error = session.BrowseVariables(out var vars); ThrowIfError("Browse", error); return (IReadOnlyList)(vars ?? new List()); - }, cancellationToken); + }, _options.BrowseTimeout, cancellationToken); } public Task> BrowseBlocksAsync(CancellationToken cancellationToken = default) @@ -541,7 +541,7 @@ public async Task SubscribeAlarmsAsync(IEnumerable< () => _session.CreateAlarmSubscription(languageIdsUint, subscriptionOptions.InitialCreditLimit, out subscriptionObjectId), _options.RequestTimeout, cancellationToken).ConfigureAwait(false); - ThrowIfError("CreateAlarmSubscription", error); + ThrowIfAlarmSubscriptionError("CreateAlarmSubscription", error); subscription.Start(token => RunAlarmSubscriptionLoopAsync(subscription, subscriptionOptions, subscriptionObjectId, token)); return subscription; @@ -736,12 +736,17 @@ public async ValueTask DisposeAsync() private Task ExecuteReadOperationAsync(string operation, Func operationFunc, CancellationToken cancellationToken) { - return ExecuteOperationAsync(operation, allowReconnect: true, operationFunc, cancellationToken); + return ExecuteReadOperationAsync(operation, operationFunc, _options.RequestTimeout, cancellationToken); + } + + private Task ExecuteReadOperationAsync(string operation, Func operationFunc, TimeSpan timeout, CancellationToken cancellationToken) + { + return ExecuteOperationAsync(operation, allowReconnect: true, operationFunc, timeout, cancellationToken); } private Task ExecuteSessionOperationAsync(string operation, Func operationFunc, CancellationToken cancellationToken) { - return ExecuteOperationAsync(operation, allowReconnect: false, operationFunc, cancellationToken); + return ExecuteOperationAsync(operation, allowReconnect: false, operationFunc, _options.RequestTimeout, cancellationToken); } private async Task SetCpuOperatingStateAsync(string operation, int operatingStateRequest, CancellationToken cancellationToken) @@ -760,10 +765,10 @@ private Task ExecuteWriteOperationAsync(string operation, Func ExecuteOperationAsync(string operation, bool allowReconnect, Func operationFunc, CancellationToken cancellationToken) + private async Task ExecuteOperationAsync(string operation, bool allowReconnect, Func operationFunc, TimeSpan timeout, CancellationToken cancellationToken) { ThrowIfDisposed(); await _operationGate.WaitAsync(cancellationToken).ConfigureAwait(false); @@ -772,14 +777,14 @@ private async Task ExecuteOperationAsync(string operation, bool allowRecon await EnsureConnectedCoreAsync(cancellationToken).ConfigureAwait(false); try { - return await RunWithTimeoutAsync(operation, () => operationFunc(_session), _options.RequestTimeout, cancellationToken).ConfigureAwait(false); + return await RunWithTimeoutAsync(operation, () => operationFunc(_session), timeout, cancellationToken).ConfigureAwait(false); } catch (S7CommPlusException ex) when (allowReconnect && _options.AutoReconnect && ex.IsTransient) { RaiseCommunicationError(ex); _options.Logger.LogWarning(ex, "Transient {Operation} failure for {Endpoint}; reconnecting and retrying once.", operation, Endpoint); await ReconnectCoreAsync(cancellationToken).ConfigureAwait(false); - return await RunWithTimeoutAsync(operation, () => operationFunc(_session), _options.RequestTimeout, cancellationToken).ConfigureAwait(false); + return await RunWithTimeoutAsync(operation, () => operationFunc(_session), timeout, cancellationToken).ConfigureAwait(false); } } catch (S7CommPlusException ex) @@ -1077,6 +1082,25 @@ private S7CommPlusException CreateTisWatchException(string operation, int errorC return CreateException(effectiveOperation, errorCode); } + private void ThrowIfAlarmSubscriptionError(string operation, int errorCode) + { + if (errorCode == 0) + { + return; + } + + throw CreateAlarmSubscriptionException(operation, errorCode); + } + + private S7CommPlusException CreateAlarmSubscriptionException(string operation, int errorCode) + { + var diagnostic = _session?.LastAlarmSubscriptionDiagnostic; + var effectiveOperation = string.IsNullOrWhiteSpace(diagnostic) + ? operation + : $"{operation}: {diagnostic}"; + return CreateException(effectiveOperation, errorCode); + } + private async Task TryDeleteSubscriptionAsync(string operation, S7CommPlusSubscription subscription, S7CommPlusSubscriptionOptions subscriptionOptions, Func deleteFunc) { if (!subscriptionOptions.DeleteOnStop || _session == null) diff --git a/src/S7CommPlusDriver/S7CommPlusClientOptions.cs b/src/S7CommPlusDriver/S7CommPlusClientOptions.cs index bf97c4f..fc4e55a 100644 --- a/src/S7CommPlusDriver/S7CommPlusClientOptions.cs +++ b/src/S7CommPlusDriver/S7CommPlusClientOptions.cs @@ -17,6 +17,7 @@ public sealed class S7CommPlusClientOptions public TimeSpan ConnectTimeout { get; set; } = S7CommPlusDefaults.ConnectTimeout; public TimeSpan RequestTimeout { get; set; } = S7CommPlusDefaults.RequestTimeout; public TimeSpan DisconnectTimeout { get; set; } = S7CommPlusDefaults.DisconnectTimeout; + public TimeSpan BrowseTimeout { get; set; } = S7CommPlusDefaults.BrowseTimeout; public bool AutoReconnect { get; set; } = true; public bool WriteEnabled { get; set; } = false; public S7CommPlusSecurityMode SecurityMode { get; set; } = S7CommPlusSecurityMode.Tls; @@ -28,6 +29,7 @@ public sealed class S7CommPlusClientOptions internal int ConnectTimeoutMilliseconds => ToPositiveMilliseconds(ConnectTimeout, nameof(ConnectTimeout)); internal int RequestTimeoutMilliseconds => ToPositiveMilliseconds(RequestTimeout, nameof(RequestTimeout)); internal int DisconnectTimeoutMilliseconds => ToPositiveMilliseconds(DisconnectTimeout, nameof(DisconnectTimeout)); + internal int BrowseTimeoutMilliseconds => ToPositiveMilliseconds(BrowseTimeout, nameof(BrowseTimeout)); internal byte[] RemoteTsapBytes => Encoding.ASCII.GetBytes(RemoteTsap ?? string.Empty); internal S7CommPlusClientOptions Clone() @@ -80,6 +82,7 @@ internal void Validate() _ = ConnectTimeoutMilliseconds; _ = RequestTimeoutMilliseconds; _ = DisconnectTimeoutMilliseconds; + _ = BrowseTimeoutMilliseconds; Logger ??= NullLogger.Instance; } diff --git a/src/S7CommPlusDriver/S7CommPlusDefaults.cs b/src/S7CommPlusDriver/S7CommPlusDefaults.cs index bca1aeb..ab68874 100644 --- a/src/S7CommPlusDriver/S7CommPlusDefaults.cs +++ b/src/S7CommPlusDriver/S7CommPlusDefaults.cs @@ -12,5 +12,6 @@ public static class S7CommPlusDefaults public static readonly TimeSpan ConnectTimeout = TimeSpan.FromSeconds(5); public static readonly TimeSpan RequestTimeout = TimeSpan.FromSeconds(5); public static readonly TimeSpan DisconnectTimeout = TimeSpan.FromSeconds(2); + public static readonly TimeSpan BrowseTimeout = TimeSpan.FromSeconds(120); } } diff --git a/src/S7CommPlusDriver/S7CommPlusProtocolSession.Session.cs b/src/S7CommPlusDriver/S7CommPlusProtocolSession.Session.cs index f020d70..38ec564 100644 --- a/src/S7CommPlusDriver/S7CommPlusProtocolSession.Session.cs +++ b/src/S7CommPlusDriver/S7CommPlusProtocolSession.Session.cs @@ -169,6 +169,8 @@ int IS7CommPlusSession.WaitForTisWatchNotifications(uint subscriptionObjectId, i string IS7CommPlusSession.LastTisWatchDiagnostic => TisWatchSubscriptions.LastDiagnostic; + string IS7CommPlusSession.LastAlarmSubscriptionDiagnostic => AlarmSubscriptions.LastDiagnostic; + int IS7CommPlusSession.DeleteTisWatchSubscription(uint subscriptionObjectId) { return TisWatchSubscriptions.Delete(subscriptionObjectId); diff --git a/src/S7CommPlusDriver/Services/S7CommPlusAlarmSubscriptionService.cs b/src/S7CommPlusDriver/Services/S7CommPlusAlarmSubscriptionService.cs index 7e5bd5c..5fc3dc8 100644 --- a/src/S7CommPlusDriver/Services/S7CommPlusAlarmSubscriptionService.cs +++ b/src/S7CommPlusDriver/Services/S7CommPlusAlarmSubscriptionService.cs @@ -25,6 +25,8 @@ internal sealed class S7CommPlusAlarmSubscriptionService private readonly IS7CommPlusProtocolSession _session; private readonly S7CommPlusProtocolRequests _requests; + public string LastDiagnostic { get; private set; } = ""; + public S7CommPlusAlarmSubscriptionService(IS7CommPlusProtocolSession session) { _session = session; @@ -54,6 +56,7 @@ public int Create(uint[] languageIds) public int Create(uint[] languageIds, short initialCreditLimit, out uint subscriptionObjectId) { subscriptionObjectId = 0; + LastDiagnostic = ""; int res; languageIds ??= Array.Empty(); var state = new AlarmSubscriptionState { NextCreditLimit = initialCreditLimit }; @@ -116,7 +119,8 @@ public int Create(uint[] languageIds, short initialCreditLimit, out uint subscri { // If creating a subscription fails, the object is still created and should be deleted. // At least deleting it, gives no error. - System.Diagnostics.Trace.WriteLine(String.Format("AlarmSubscription - Create: Failed with Returnvalue = 0x{0:X8}", createObjRes.ReturnValue)); + LastDiagnostic = String.Format("Create failed with Returnvalue = 0x{0:X8}", createObjRes.ReturnValue); + System.Diagnostics.Trace.WriteLine("AlarmSubscription - " + LastDiagnostic); res = S7Consts.errCliInvalidParams; }