diff --git a/src/core/src/AXOpen.Core/AxoMessenger/Static/AxoMessageProvider.cs b/src/core/src/AXOpen.Core/AxoMessenger/Static/AxoMessageProvider.cs index e6f77c2d2..e5c9783cb 100644 --- a/src/core/src/AXOpen.Core/AxoMessenger/Static/AxoMessageProvider.cs +++ b/src/core/src/AXOpen.Core/AxoMessenger/Static/AxoMessageProvider.cs @@ -24,8 +24,11 @@ private AxoMessageProvider(IEnumerable observedObjects) public IEnumerable ObservedObjects { get; } private int? _cachedActiveMessagesCount; + private int? _maxActiveMessagesCount; private DateTime _lastActiveMessagesCountUpdate = DateTime.MinValue; - private readonly TimeSpan _activeMessagesCountCacheDuration = TimeSpan.FromMilliseconds(500); + private DateTime _maxActiveMessagesCountSetTime = DateTime.MinValue; + private readonly TimeSpan _activeMessagesCountCacheDuration = TimeSpan.FromMilliseconds(100); + private readonly TimeSpan _maxValueHoldDuration = TimeSpan.FromSeconds(2); private readonly object _activeMessagesCountLock = new object(); /// @@ -34,7 +37,9 @@ private AxoMessageProvider(IEnumerable observedObjects) /// /// This property counts the number of messages that are currently active. /// An active message is defined as a message belonging to a Messenger that has a state other than Idle or NotActiveWatingAckn. - /// The value is cached for a short period to prevent flickering due to asynchronous PLC communication. + /// The value uses a maximum-tracking mechanism to prevent flickering caused by PLC cycle timing, + /// where the message count is accumulated during each controller cycle starting from 0. + /// The maximum value seen is held for a period since the correct full count persists longer than partial counts. /// public int? ActiveMessagesCount { @@ -44,11 +49,11 @@ public int? ActiveMessagesCount { var now = DateTime.UtcNow; - // Return cached value if still valid + // Return cached value if we just read recently if (_cachedActiveMessagesCount.HasValue && (now - _lastActiveMessagesCountUpdate) < _activeMessagesCountCacheDuration) { - return _cachedActiveMessagesCount; + return _maxActiveMessagesCount ?? _cachedActiveMessagesCount; } try @@ -61,7 +66,20 @@ public int? ActiveMessagesCount _cachedActiveMessagesCount = count; _lastActiveMessagesCountUpdate = now; - return count; + // Track maximum value seen + if (!_maxActiveMessagesCount.HasValue || count > _maxActiveMessagesCount.Value) + { + _maxActiveMessagesCount = count; + _maxActiveMessagesCountSetTime = now; + } + // Reset max if hold duration has passed and current value is lower + else if ((now - _maxActiveMessagesCountSetTime) > _maxValueHoldDuration) + { + _maxActiveMessagesCount = count; + _maxActiveMessagesCountSetTime = now; + } + + return _maxActiveMessagesCount; } catch (Exception e) { @@ -73,8 +91,11 @@ public int? ActiveMessagesCount private int? _cachedRelevantMessagesCount; + private int? _maxRelevantMessagesCount; private DateTime _lastRelevantMessagesCountUpdate = DateTime.MinValue; - private readonly TimeSpan _relevantMessagesCountCacheDuration = TimeSpan.FromMilliseconds(500); + private DateTime _maxRelevantMessagesCountSetTime = DateTime.MinValue; + private readonly TimeSpan _relevantMessagesCountCacheDuration = TimeSpan.FromMilliseconds(100); + private readonly TimeSpan _maxRelevantValueHoldDuration = TimeSpan.FromSeconds(2); private readonly object _relevantMessagesCountLock = new object(); /// @@ -83,7 +104,9 @@ public int? ActiveMessagesCount /// /// The RelevantMessagesCount property will return the number of messengers that have a state greater than eAxoMessengerState.Idle. /// Messengers is a collection of objects that represents messengers. - /// The value is cached for a short period to prevent flickering due to asynchronous PLC communication. + /// The value uses a maximum-tracking mechanism to prevent flickering caused by PLC cycle timing, + /// where the message count is accumulated during each controller cycle starting from 0. + /// The maximum value seen is held for a period since the correct full count persists longer than partial counts. /// /// An integer that represents the count of relevant messages. public int? RelevantMessagesCount @@ -94,11 +117,11 @@ public int? RelevantMessagesCount { var now = DateTime.UtcNow; - // Return cached value if still valid + // Return cached value if we just read recently if (_cachedRelevantMessagesCount.HasValue && (now - _lastRelevantMessagesCountUpdate) < _relevantMessagesCountCacheDuration) { - return _cachedRelevantMessagesCount; + return _maxRelevantMessagesCount ?? _cachedRelevantMessagesCount; } try @@ -111,7 +134,20 @@ public int? RelevantMessagesCount _cachedRelevantMessagesCount = count; _lastRelevantMessagesCountUpdate = now; - return count; + // Track maximum value seen + if (!_maxRelevantMessagesCount.HasValue || count > _maxRelevantMessagesCount.Value) + { + _maxRelevantMessagesCount = count; + _maxRelevantMessagesCountSetTime = now; + } + // Reset max if hold duration has passed and current value is lower + else if ((now - _maxRelevantMessagesCountSetTime) > _maxRelevantValueHoldDuration) + { + _maxRelevantMessagesCount = count; + _maxRelevantMessagesCountSetTime = now; + } + + return _maxRelevantMessagesCount; } catch (Exception e) { @@ -243,13 +279,17 @@ public void InvalidateMessageCountCache() lock (_activeMessagesCountLock) { _cachedActiveMessagesCount = null; + _maxActiveMessagesCount = null; _lastActiveMessagesCountUpdate = DateTime.MinValue; + _maxActiveMessagesCountSetTime = DateTime.MinValue; } lock (_relevantMessagesCountLock) { _cachedRelevantMessagesCount = null; + _maxRelevantMessagesCount = null; _lastRelevantMessagesCountUpdate = DateTime.MinValue; + _maxRelevantMessagesCountSetTime = DateTime.MinValue; } } } diff --git a/src/inspectors/ctrl/src/AxoInspectorDialog.st b/src/inspectors/ctrl/src/AxoInspectorDialog.st index 5ebb81c94..d49585797 100644 --- a/src/inspectors/ctrl/src/AxoInspectorDialog.st +++ b/src/inspectors/ctrl/src/AxoInspectorDialog.st @@ -21,11 +21,14 @@ NAMESPACE AXOpen.Inspectors _inspectorIndentity : ULINT; //try display inspector inside dialog. _inspectionDetails : STRING[254]; _inspectorType : eInspectorType; - {#ix-set:AttributeName = "<#Data#>"} + {#ix-set:AttributeName = ""} + {#ix-set:Units = ""} _analogInspectorData : AxoAnalogueInspectorData; - {#ix-set:AttributeName = "<#Data#>"} + {#ix-set:AttributeName = ""} + {#ix-set:Units = ""} _digitalInspectorData : AxoDigitalInspectorData; - {#ix-set:AttributeName = "<#Data#>"} + {#ix-set:AttributeName = ""} + {#ix-set:Units = ""} _dataInspectorData : AxoDataInspectorData; END_VAR diff --git a/src/inspectors/src/AxOpen.Inspectors.Blazor/InspectorViewTemplate.razor b/src/inspectors/src/AxOpen.Inspectors.Blazor/InspectorViewTemplate.razor index adfe90ad5..81038df6f 100644 --- a/src/inspectors/src/AxOpen.Inspectors.Blazor/InspectorViewTemplate.razor +++ b/src/inspectors/src/AxOpen.Inspectors.Blazor/InspectorViewTemplate.razor @@ -64,10 +64,23 @@ [Parameter] public string Presentation { get; set; } + + private string _name; [Parameter] - public string Name { get; set; } + public string Name + { + get + { + return this._name == this.Data.GetSymbolTail() ? string.Empty : this._name; + } + + set + { + this._name = value; + } + } - private string NameWithUnit => string.IsNullOrEmpty(this.Data?.Units) ? this.Name : $"{Name} [{this.Data.Units}]"; + private string NameWithUnit => string.IsNullOrEmpty(this.Data?.Units) || this.Data?.Units == this.Data?.GetSymbolTail() ? this.Name : $"{Name} [{this.Data.Units}]"; private short Result