diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 4f7f693ff..5d4f7c956 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "AXSharp.ixc": { - "version": "0.47.0-alpha.439", + "version": "0.47.0-alpha.452", "commands": [ "ixc" ], @@ -17,14 +17,14 @@ "rollForward": false }, "AXSharp.ixd": { - "version": "0.47.0-alpha.439", + "version": "0.47.0-alpha.452", "commands": [ "ixd" ], "rollForward": false }, "AXSharp.ixr": { - "version": "0.47.0-alpha.439", + "version": "0.47.0-alpha.452", "commands": [ "ixr" ], diff --git a/CHANGELOG.md b/CHANGELOG.md index 06f2eaf7d..d7af799ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,41 @@ +### [CORE] Controller logger updates ([#1054](https://github.com/Inxton/AXOpen/pull/1054)) + +**Note:** Enhanced logging and messaging capabilities with new message categories and requalification features. + +- feat: Added new `Potential` message category (severity level 150) for messages that may escalate to warnings or errors +- feat: Introduced message requalification system via `RequalifyDownstreamMessages()` to allow downstream message category promotion +- feat: Added `_messageCode` parameter to logger methods for improved message tracking and identification +- feat: Implemented step timeout detection in `AxoSequencer` with automatic error message generation +- feat: Enhanced `AxoMessageProvider` and `Flattener` to support configurable observation depth +- refactor: Standardized severity localization keys (simplified from "SeverityInfo" to "Info", etc.) +- refactor: Updated AxoMessenger logging signatures to use rise/fall signature markers for clarity +- chore: Bumped AXSharp packages to 0.47.0-alpha.452 and Siemens.Simatic.S7.Webserver.API to 3.3.24 + +**Impact:** +- Enables intermediate message categorization before escalation to warnings or errors +- Improves diagnostics through message code tracking and step timeout detection +- Provides better control over message severity in distributed systems +- Simplifies localization maintenance with consistent key naming + +**New Message Categories:** +- `None` (0): No category; ignore non-critical messages +- `Info` (100): Informative messages with minimal impact +- `Potential` (150): Potential problems that may escalate (automatically requalified if configured) +- `Warning` (200): Possible problems affecting the process +- `Error` (300): Failures requiring intervention +- `Critical` (400): Critical system failures +- `ProgrammingError` (500): Implementation/configuration errors + +**Risks/Review:** +- Existing code using old severity localization keys should be updated to use new simplified keys +- Message requalification logic should be tested in environments with coordinated components +- Step timeout thresholds should be validated for application-specific timing requirements + +**Testing:** +- Unit tests for message requalification across all categories +- Integration tests for step timeout scenarios in sequencers +- Localization verification for all supported languages + ### [INTEGRATIONS] Additional alignments with application template ([#768](https://github.com/Inxton/AXOpen/pull/768)) **Note:** Namespace and component renames require consumers to update imports, templates, and generated UI bindings before upgrading. diff --git a/Directory.Packages.props b/Directory.Packages.props index 5583a1954..06503b581 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -11,12 +11,12 @@ - - - - - - + + + + + + @@ -40,7 +40,7 @@ - + diff --git a/docfx/articles/guidelines/components.md b/docfx/articles/guidelines/components.md index d0671c045..0a3e3cb89 100644 --- a/docfx/articles/guidelines/components.md +++ b/docfx/articles/guidelines/components.md @@ -123,6 +123,30 @@ Components should surface diagnostic and alarm information consistently: For UI auto‑rendering the alarm level icons provided by `AxoComponent` will reflect the highest active severity; ensure warnings are deactivated when condition clears to avoid stale visualization. +### Message Categories + +The framework supports the following message categories (in order of severity): + +* **None** (0): No category; used to clear requalification directives and ignore non-critical messages +* **Info** (100): Informative messages with minimal impact; do not require operator intervention +* **Potential** (150): Potential problems that may escalate to Warning or Error states; these messages can be automatically requalified based on system configuration when downstream conditions are detected +* **Warning** (200): Possible problems that may adversely affect a process; information to help identify problems but does not necessarily stop the process +* **Error** (300): Failures that cannot be immediately recovered; intervention is needed +* **Critical** (400): Critical system failures +* **ProgrammingError** (500): Implementation or configuration errors in the application + +When using the `Potential` category, ensure that the parent context or sequencer is configured with appropriate requalification rules to escalate potential issues to `Warning` or `Error` when conditions persist. + +### Message Requalification + +For coordinated components and sequencers, message requalification allows upstream components to influence downstream message severity. Use `GetMessengerService().RequalifyDownstreamMessages(category)` to set the requalification category. This is particularly useful for: + +* Escalating `Potential` messages to `Warning` or `Error` based on elapsed time +* Implementing hierarchical error response strategies in multi-step workflows +* Ensuring consistent severity handling across component hierarchies + +Note: Only `Potential` messages are subject to requalification; other categories retain their configured severity. + ## Documentation requirements diff --git a/src/abstractions/ctrl/src/AxoLogger/IAxoLogger.st b/src/abstractions/ctrl/src/AxoLogger/IAxoLogger.st index 17a5aa0ef..5f132d4ad 100644 --- a/src/abstractions/ctrl/src/AxoLogger/IAxoLogger.st +++ b/src/abstractions/ctrl/src/AxoLogger/IAxoLogger.st @@ -25,7 +25,7 @@ NAMESPACE AXOpen.Logging /// _message is a string of maximum 80 characters that represents the log message /// _message : STRING[80]; - + /// /// _level is an instance of eLogLevel that represents the level of the log /// @@ -35,6 +35,11 @@ NAMESPACE AXOpen.Logging /// _sender is an instance of IAxoObject that represents the sender of the log /// _sender : IAxoObject; + + /// + /// _messageCode is an unsigned long integer that represents the code of the log message + /// + _messageCode : ULINT; END_VAR END_METHOD diff --git a/src/abstractions/ctrl/src/AxoMessenger/IAxoMessengerService.st b/src/abstractions/ctrl/src/AxoMessenger/IAxoMessengerService.st index 5ba71e029..25ffd3405 100644 --- a/src/abstractions/ctrl/src/AxoMessenger/IAxoMessengerService.st +++ b/src/abstractions/ctrl/src/AxoMessenger/IAxoMessengerService.st @@ -5,8 +5,37 @@ NAMESPACE AXOpen.Core inMessegenger : IAxoMessenger; END_VAR END_METHOD + /// + /// Suspends the messaging system. When suspended, messages will not be sent to subscribers until the system is resumed. + /// This is useful for scenarios where you want to temporarily prevent messages from being processed. + /// METHOD SuspendMessaging END_METHOD + + /// + /// Resumes the messaging system. When resumed, messages that arose while the system was suspended will NOT be sent to subscribers. + /// METHOD ResumeMessaging END_METHOD + + /// + /// Checks if the messaging system is currently suspended. + /// Returns true if the messaging system is suspended, false otherwise. + /// METHOD IsSuspended : BOOL END_METHOD + + /// + /// Requalifies the message category for downstream messages. + /// This is used to ensure that messages that are sent from a downstream component are categorized correctly, + /// even if the upstream component has a different message category. + /// + METHOD RequalifyDownstreamMessages + VAR_INPUT + inMessageCategory : AXOpen.Messaging.eAxoMessageCategory; + END_VAR + END_METHOD + + /// + /// Gets the requalified message category for downstream messages. + /// + METHOD GetRequalifiedMessageCategory : AXOpen.Messaging.eAxoMessageCategory END_METHOD END_INTERFACE END_NAMESPACE diff --git a/src/core/ctrl/src/AxoMessaging/eAxoMessageCategory.st b/src/abstractions/ctrl/src/AxoMessenger/eAxoMessageCategory.st similarity index 65% rename from src/core/ctrl/src/AxoMessaging/eAxoMessageCategory.st rename to src/abstractions/ctrl/src/AxoMessenger/eAxoMessageCategory.st index d390a55af..8e6c783f8 100644 --- a/src/core/ctrl/src/AxoMessaging/eAxoMessageCategory.st +++ b/src/abstractions/ctrl/src/AxoMessenger/eAxoMessageCategory.st @@ -5,20 +5,36 @@ NAMESPACE AXOpen.Messaging /// ATTENTION: Values must be multiply of 100 /// eAxoMessageCategory : INT - ( + ( + /// + /// No category. + /// This is used to indicate that the message does not have any importance and should be ignored by the system. + /// Do not use this category for messages that have to be delivered to the user, even if such information is not critical. + /// + None := 0, + + /// /// Info message. /// Use when you want to deliver information to the user, that has only informative character and does not adversely affect a process. /// Info := 100, - + + /// + /// Potential problem message. + /// Use this category when you want to report to the user an information about a potential problem, that does not necessarily affect a process + /// but may cause a problem. This is typically used when certain conditions is expected to happen within a process that did not happen yet, + /// this category is aimed to be later automatically requalified to Warning or Error by coordination primitive if the expected conditions did not happen within expected time. + /// + Potential := 150, + /// /// Warning message. /// Use this category when you want to report to the user an information about a possible problem, that may adversly affect a process. /// Information in this cateogory aims to help the user to identify a problem, the cause of such problem does not necessarily stops the process. /// Warning := 200, - + /// /// Error message. /// Use this category when there is a failure that cannot be immediately recovered and an intervention is needed. This is typically a situation when a device fails diff --git a/src/components.elements/ctrl/test/Mock.st b/src/components.elements/ctrl/test/Mock.st index f9100040d..731815b1b 100644 --- a/src/components.elements/ctrl/test/Mock.st +++ b/src/components.elements/ctrl/test/Mock.st @@ -67,5 +67,16 @@ NAMESPACE AXOpen.Components.Elements.Tests END_VAR ; END_METHOD + + METHOD PUBLIC Log + VAR_INPUT + _message : STRING[80]; + _level : eLogLevel; + _sender : IAxoObject; + _messageCode : ULINT; + END_VAR + + ; + END_METHOD END_CLASS END_NAMESPACE diff --git a/src/components.pneumatics/ctrl/src/AxoCylinder.st b/src/components.pneumatics/ctrl/src/AxoCylinder.st index 68af1bd77..8c7b4fb61 100644 --- a/src/components.pneumatics/ctrl/src/AxoCylinder.st +++ b/src/components.pneumatics/ctrl/src/AxoCylinder.st @@ -115,7 +115,7 @@ NAMESPACE AXOpen.Components.Pneumatics _Messenger.Serve(THIS); - _Messenger.ActivateOnCondition(UINT#3, _OutSensor AND _InSensor, eAxoMessageCategory#Error); + _Messenger.ActivateOnCondition(UINT#3, _OutSensor AND _InSensor, eAxoMessageCategory#Warning); IF(_StopTask.Execute(THIS)) THEN _MoveInSignal := FALSE; @@ -136,9 +136,10 @@ NAMESPACE AXOpen.Components.Pneumatics _MoveToOutIsBusy := _MoveOutTask.IsBusy(); - _Messenger.ActivateOnCondition(UINT#1, (_MoveToOutIsBusy AND NOT _MoveToOutIsSuspended AND _MoveOutTask.Duration >= T#10S), eAxoMessageCategory#Error); - _Messenger.ActivateOnCondition(UINT#9, (_MoveOutTask.IsDone() AND NOT _OutSensor), eAxoMessageCategory#Warning); - + IF(NOT _MoveToOutIsSuspended AND NOT THIS._MoveOutTask.IsDisabled) THEN + _Messenger.ActivateOnCondition(UINT#1, (_MoveToOutIsBusy AND NOT _MoveToOutIsSuspended), eAxoMessageCategory#Potential); + _Messenger.ActivateOnCondition(UINT#9, (_MoveOutTask.IsDone() AND NOT _OutSensor), eAxoMessageCategory#Potential); + END_IF; IF(_MoveInTask.Execute(THIS)) THEN IF(_MoveInTask.IsFirstExecutionCycle()) THEN @@ -151,8 +152,10 @@ NAMESPACE AXOpen.Components.Pneumatics _MoveToInIsBusy := _MoveInTask.IsBusy(); - _Messenger.ActivateOnCondition(UINT#2, (_MoveToInIsBusy AND NOT _MoveToInIsSuspended AND _MoveInTask.Duration >= T#10S), eAxoMessageCategory#Error); - _Messenger.ActivateOnCondition(UINT#8, (_MoveInTask.IsDone() AND NOT _InSensor), eAxoMessageCategory#Warning); + IF(NOT _MoveToInIsSuspended AND NOT THIS._MoveInTask.IsDisabled) THEN + _Messenger.ActivateOnCondition(UINT#2, (_MoveToInIsBusy AND NOT _MoveToInIsSuspended), eAxoMessageCategory#Potential); + _Messenger.ActivateOnCondition(UINT#8, (_MoveInTask.IsDone() AND NOT _InSensor), eAxoMessageCategory#Potential); + END_IF; outMoveInSignal := _MoveInSignal AND NOT _MoveToInIsSuspended; diff --git a/src/components.pneumatics/ctrl/test/CylinderTests.st b/src/components.pneumatics/ctrl/test/CylinderTests.st index 44c9a8258..b4fd2d468 100644 --- a/src/components.pneumatics/ctrl/test/CylinderTests.st +++ b/src/components.pneumatics/ctrl/test/CylinderTests.st @@ -205,7 +205,7 @@ NAMESPACE Cylinder.Tests AxUnit.Assert.Equal(TRUE, cylinder._MoveOutTask.IsBusy()); AxUnit.Assert.Equal(TRUE, cylinder.MoveOut().IsBusy()); // AxUnit.Assert.Equal(FALSE, cylinder._Messenger.IsActive); - AxUnit.Assert.Equal(FALSE, cylinder._Messenger.MessengerState = eAxoMessengerState#ActiveAcknowledgeNotRequired); + AxUnit.Assert.Equal(FALSE, cylinder._Messenger.MessageCode = ULINT#4 && cylinder._Messenger.MessengerState = eAxoMessengerState#ActiveAcknowledgeNotRequired); context.Open(); context.InitializeRootObject(cylinderParent); @@ -240,7 +240,7 @@ NAMESPACE Cylinder.Tests AxUnit.Assert.Equal(TRUE, cylinder._MoveOutTask.IsBusy()); AxUnit.Assert.Equal(TRUE, cylinder.MoveOut().IsBusy()); // AxUnit.Assert.Equal(FALSE, cylinder._Messenger.IsActive); - AxUnit.Assert.Equal(FALSE, cylinder._Messenger.MessengerState = eAxoMessengerState#ActiveAcknowledgeNotRequired); + AxUnit.Assert.Equal(FALSE, cylinder._Messenger.MessageCode = ULINT#4 && cylinder._Messenger.MessengerState = eAxoMessengerState#ActiveAcknowledgeNotRequired); context.Open(); _homeSensor := FALSE; @@ -278,7 +278,7 @@ NAMESPACE Cylinder.Tests AxUnit.Assert.Equal(TRUE, cylinder._MoveInTask.IsBusy()); AxUnit.Assert.Equal(TRUE, cylinder.MoveIn().IsBusy()); // AxUnit.Assert.Equal(FALSE, cylinder._Messenger.IsActive); - AxUnit.Assert.Equal(FALSE, cylinder._Messenger.MessengerState = eAxoMessengerState#ActiveAcknowledgeNotRequired); + AxUnit.Assert.Equal(FALSE, cylinder._Messenger.MessageCode = ULINT#5 && cylinder._Messenger.MessengerState = eAxoMessengerState#ActiveAcknowledgeNotRequired); context.Open(); cylinder.SuspendMoveToInWhile(TRUE); @@ -313,7 +313,7 @@ NAMESPACE Cylinder.Tests AxUnit.Assert.Equal(TRUE, cylinder._MoveInTask.IsBusy()); AxUnit.Assert.Equal(TRUE, cylinder.MoveIn().IsBusy()); // AxUnit.Assert.Equal(FALSE, cylinder._Messenger.IsActive); - AxUnit.Assert.Equal(FALSE, cylinder._Messenger.MessengerState = eAxoMessengerState#ActiveAcknowledgeNotRequired); + AxUnit.Assert.Equal(FALSE, cylinder._Messenger.MessageCode = ULINT#5 && cylinder._Messenger.MessengerState = eAxoMessengerState#ActiveAcknowledgeNotRequired); context.Open(); context.InitializeRootObject(cylinderParent); cylinder.SuspendMoveToInWhile(TRUE); @@ -419,6 +419,7 @@ NAMESPACE Cylinder.Tests AxUnit.Assert.Equal(FALSE, cylinder.MoveInSignal); AxUnit.Assert.Equal(TRUE, cylinder._MoveOutTask.IsBusy()); AxUnit.Assert.Equal(TRUE, cylinder.MoveOut().IsBusy()); + AxUnit.Assert.Equal(FALSE, cylinder._Messenger.MessageCode = ULINT#6 && cylinder._Messenger.MessengerState = eAxoMessengerState#ActiveAcknowledgeNotRequired); context.Open(); cylinder.AbortMoveToWorkWhen(TRUE); @@ -432,7 +433,7 @@ NAMESPACE Cylinder.Tests AxUnit.Assert.Equal(FALSE, cylinder.MoveOut().IsBusy()); // AxUnit.Assert.Equal(TRUE, cylinder._Messenger.IsActive); AxUnit.Assert.Equal(TRUE, cylinder._Messenger.MessengerState = eAxoMessengerState#ActiveAcknowledgeNotRequired); - AxUnit.Assert.Equal(TRUE,THIS.AreEqual(dt, cylinder._Messenger.Risen)); + AxUnit.Assert.Equal(cylinder._Messenger.MessageCode, ULINT#6); END_METHOD {Test} @@ -453,7 +454,7 @@ NAMESPACE Cylinder.Tests AxUnit.Assert.Equal(TRUE, cylinder._MoveOutTask.IsBusy()); AxUnit.Assert.Equal(TRUE, cylinder.MoveOut().IsBusy()); // AxUnit.Assert.Equal(FALSE, cylinder._Messenger.IsActive); - AxUnit.Assert.Equal(FALSE, cylinder._Messenger.MessengerState = eAxoMessengerState#ActiveAcknowledgeNotRequired); + AxUnit.Assert.Equal(FALSE, cylinder._Messenger.MessageCode = ULINT#6 && cylinder._Messenger.MessengerState = eAxoMessengerState#ActiveAcknowledgeNotRequired); context.Open(); context.InitializeRootObject(cylinderParent); @@ -467,6 +468,7 @@ NAMESPACE Cylinder.Tests AxUnit.Assert.Equal(FALSE, cylinder.MoveOut().IsBusy()); // AxUnit.Assert.Equal(TRUE, cylinder._Messenger.IsActive); AxUnit.Assert.Equal(TRUE, cylinder._Messenger.MessengerState = eAxoMessengerState#ActiveAcknowledgeNotRequired); + AxUnit.Assert.Equal(cylinder._Messenger.MessageCode, ULINT#6); AxUnit.Assert.Equal(TRUE,THIS.AreEqual(dt, cylinder._Messenger.Risen)); END_METHOD @@ -488,7 +490,7 @@ NAMESPACE Cylinder.Tests AxUnit.Assert.Equal(TRUE, cylinder._MoveInTask.IsBusy()); AxUnit.Assert.Equal(TRUE, cylinder.MoveIn().IsBusy()); // AxUnit.Assert.Equal(FALSE, cylinder._Messenger.IsActive); - AxUnit.Assert.Equal(FALSE, cylinder._Messenger.MessengerState = eAxoMessengerState#ActiveAcknowledgeNotRequired); + AxUnit.Assert.Equal(FALSE, cylinder._Messenger.MessageCode = ULINT#7 && cylinder._Messenger.MessengerState = eAxoMessengerState#ActiveAcknowledgeNotRequired); context.Open(); cylinder.AbortMoveToHomeWhen(TRUE); @@ -502,6 +504,7 @@ NAMESPACE Cylinder.Tests AxUnit.Assert.Equal(FALSE, cylinder.MoveIn().IsBusy()); // AxUnit.Assert.Equal(TRUE, cylinder._Messenger.IsActive); AxUnit.Assert.Equal(TRUE, cylinder._Messenger.MessengerState = eAxoMessengerState#ActiveAcknowledgeNotRequired); + AxUnit.Assert.Equal(cylinder._Messenger.MessageCode, ULINT#7); AxUnit.Assert.Equal(TRUE,THIS.AreEqual(dt, cylinder._Messenger.Risen)); END_METHOD @@ -523,7 +526,7 @@ NAMESPACE Cylinder.Tests AxUnit.Assert.Equal(TRUE, cylinder._MoveInTask.IsBusy()); AxUnit.Assert.Equal(TRUE, cylinder.MoveIn().IsBusy()); // AxUnit.Assert.Equal(FALSE, cylinder._Messenger.IsActive); - AxUnit.Assert.Equal(FALSE, cylinder._Messenger.MessengerState = eAxoMessengerState#ActiveAcknowledgeNotRequired); + AxUnit.Assert.Equal(FALSE, cylinder._Messenger.MessageCode = ULINT#7 && cylinder._Messenger.MessengerState = eAxoMessengerState#ActiveAcknowledgeNotRequired); context.Open(); context.InitializeRootObject(cylinderParent); @@ -537,6 +540,7 @@ NAMESPACE Cylinder.Tests AxUnit.Assert.Equal(FALSE, cylinder.MoveIn().IsBusy()); // AxUnit.Assert.Equal(TRUE, cylinder._Messenger.IsActive); AxUnit.Assert.Equal(TRUE, cylinder._Messenger.MessengerState = eAxoMessengerState#ActiveAcknowledgeNotRequired); + AxUnit.Assert.Equal(cylinder._Messenger.MessageCode, ULINT#7); AxUnit.Assert.Equal(TRUE,THIS.AreEqual(dt, cylinder._Messenger.Risen)); END_METHOD diff --git a/src/core/ctrl/src/AxoCoordination/AxoSequencer/AxoSequencer.st b/src/core/ctrl/src/AxoCoordination/AxoSequencer/AxoSequencer.st index 455bf584e..69f6c8063 100644 --- a/src/core/ctrl/src/AxoCoordination/AxoSequencer/AxoSequencer.st +++ b/src/core/ctrl/src/AxoCoordination/AxoSequencer/AxoSequencer.st @@ -1,3 +1,5 @@ +USING AXOpen.Messaging; +USING AXOpen.Messaging.Static; NAMESPACE AXOpen.Core USING System.Strings; {S7.extern=ReadWrite} @@ -49,7 +51,6 @@ NAMESPACE AXOpen.Core END_IF; Open := FALSE; - SUPER.SetSuspendMultipleExecuteCallCheck(TRUE); StepForwardCommand.SetSuspendMultipleExecuteCallCheck(TRUE); @@ -57,6 +58,9 @@ NAMESPACE AXOpen.Core StepBackwardCommand.SetSuspendMultipleExecuteCallCheck(TRUE); IF SUPER.Execute() THEN + // Resets the message category requalification to default value of None at the beginning of each sequence execution to avoid unexpected message category requalifications caused by previous sequence executions. + _context.GetMessengerService().RequalifyDownstreamMessages(eAxoMessageCategory#None); + IF _context = NULL THEN RETURN; END_IF; @@ -83,6 +87,10 @@ NAMESPACE AXOpen.Core END_IF; END_METHOD + VAR PUBLIC + _msgStepTimedOut : AxoStepTimedOutMessenger; + END_VAR + METHOD INTERNAL Execute : BOOL VAR_INPUT step : IAxoStep; @@ -98,7 +106,6 @@ NAMESPACE AXOpen.Core END_IF; _step := step; - IF(_coordinatorState = AxoCoordinatorStates#Configuring) THEN THIS.DetermineOrder(step); END_IF; @@ -210,6 +217,21 @@ NAMESPACE AXOpen.Core CurrentStep := _refStep^; END_IF; + IF(THIS.MaxSequenceDuration = LT#0s) THEN + THIS.MaxSequenceDuration := LT#10s; // Default value for max sequence duration to 10 seconds if not set + END_IF; + + // We will only report step timeout when the step duration exceeds the MaxSequenceDuration, + // which is set for the whole sequence, to avoid too much noise in the logs. + IF(THIS.CurrentStep.Duration > THIS.MaxSequenceDuration) THEN + IF(Execute) THEN + _context.GetMessengerService().RequalifyDownstreamMessages(eAxoMessageCategory#Error); + _msgStepTimedOut.Activate(THIS, _refStep); + END_IF; + ELSE + _msgStepTimedOut.Deactivate(THIS, _refStep); + END_IF; + // IF(THIS.CurrentOrder + ULINT#1 = step.GetStepOrder() OR (THIS.CurrentOrder = THIS.GetNumberOfConfiguredSteps() AND step.GetStepOrder() = UINT#1)) THEN // _refStep ?= step; // AfterStep := _refStep^; @@ -443,6 +465,78 @@ NAMESPACE AXOpen.Core END_VAR SteppingMode := inSteppingMode; END_METHOD + END_CLASS + + {S7.extern=ReadWrite} + CLASS PUBLIC AxoStepTimedOutMessenger EXTENDS AxoMessenger + + VAR CONSTANT + MSG_STEP_TIMED_OUT : STRING[254] := '<#Step timed out: #>'; + END_VAR + + VAR PRIVATE + _refObservedStep : REF_TO AxoStep; + END_VAR + + METHOD INTERNAL Activate + VAR_INPUT + inParent : IAxoObject; + inStep : REF_TO AXOpen.Core.AxoStep; + END_VAR + + VAR + _context : IAxoContext; + END_VAR + + IF(inStep = NULL) THEN + RETURN; + END_IF; + + IF(THIS.MessengerState = eAxoMessengerState#Idle && _refObservedStep <> inStep) THEN + THIS.MessengerState := eAxoMessengerState#ActiveAcknowledgeNotRequired; + _context := inParent.GetContext(); + Risen := _context.GetRtc().NowUTC(); + Fallen := LDATE_AND_TIME#1970-01-01-00:00:00.000; + Acknowledged := LDATE_AND_TIME#1970-01-01-00:00:00.000; + Category := eAxoMessageCategory#Error; + Message := System.Strings.Concat(MSG_STEP_TIMED_OUT, inStep^.GetStepDescription()); + MessageCode := ULINT#18446744073709551615; // No specific message code for step timeout + _context.GetLogger().Log(System.Strings.Concat(RISE_SIGNATURE, Message), AXOpen.Logging.eLogLevel#Error, THIS); + _refObservedStep := inStep; + END_IF; + + IF(THIS.MessengerState <> eAxoMessengerState#Idle && _refObservedStep = inStep) THEN + inParent.AggregateMessage(1); + END_IF; + END_METHOD + + + METHOD INTERNAL Deactivate + VAR_INPUT + inParent : IAxoObject; + inStep : REF_TO AXOpen.Core.AxoStep; + END_VAR + VAR + _context : IAxoContext; + END_VAR + + IF(_refObservedStep = NULL) THEN + RETURN; + END_IF; + + + IF(THIS.MessengerState = eAxoMessengerState#ActiveAcknowledgeNotRequired) THEN + _context := inParent.GetContext(); + Fallen := _context.GetRtc().NowUTC(); + Message :=System.Strings.Concat(MSG_STEP_TIMED_OUT, _refObservedStep^.GetStepDescription()); + MessageCode := ULINT#18446744073709551615; // No specific message code for step timeout + _context.GetLogger().Log(System.Strings.Concat(FALL_SIGNATURE, Message), AXOpen.Logging.eLogLevel#Error, THIS); + THIS.MessengerState := eAxoMessengerState#Idle; + END_IF; + + _refObservedStep := NULL; + + END_METHOD + END_CLASS - END_CLASS END_NAMESPACE diff --git a/src/core/ctrl/src/AxoCoordination/AxoStep/AxoStep.st b/src/core/ctrl/src/AxoCoordination/AxoStep/AxoStep.st index 5401779fc..ef3445f21 100644 --- a/src/core/ctrl/src/AxoCoordination/AxoStep/AxoStep.st +++ b/src/core/ctrl/src/AxoCoordination/AxoStep/AxoStep.st @@ -1,3 +1,4 @@ +USING AXOpen.Messaging.Static; NAMESPACE AXOpen.Core {#ix-prop: public string Description} {S7.extern=ReadWrite} @@ -152,9 +153,6 @@ NAMESPACE AXOpen.Core ELSIF (SequencerContainer <> NULL) THEN ExecuteInternal := SequencerContainer^.Execute(THIS, Enable); END_IF; - - - // Selector ?= coord; // IF(coord <> NULL) THEN Sequencer^.ExecuteWithSelector(THIS, Enable); END_IF; diff --git a/src/core/ctrl/src/AxoLogger/AxoLogEntry.st b/src/core/ctrl/src/AxoLogger/AxoLogEntry.st index 0c58d7e90..48c2c6df4 100644 --- a/src/core/ctrl/src/AxoLogger/AxoLogEntry.st +++ b/src/core/ctrl/src/AxoLogger/AxoLogEntry.st @@ -10,6 +10,12 @@ NAMESPACE AXOpen.Logging /// Message : STRING[80]; + + /// + /// Message code. The code will provide information about the text of the message from message tables of respective messenger. + /// + MessageCode : ULINT; + /// /// The severity level of the log entry. /// diff --git a/src/core/ctrl/src/AxoLogger/AxoLogger.st b/src/core/ctrl/src/AxoLogger/AxoLogger.st index 9b3e5a283..c4b6752db 100644 --- a/src/core/ctrl/src/AxoLogger/AxoLogger.st +++ b/src/core/ctrl/src/AxoLogger/AxoLogger.st @@ -31,11 +31,13 @@ NAMESPACE AXOpen.Logging /// The message to be logged. /// The severity level of the log. /// The object that sends the log. + /// The code of the log message. METHOD PUBLIC Log VAR_INPUT _message : STRING[80]; _level : eLogLevel; - _sender : IAxoObject; + _sender : IAxoObject; + _messageCode : ULINT; END_VAR IF(_level >= THIS.MinimumLevel) THEN @@ -45,6 +47,7 @@ NAMESPACE AXOpen.Logging LogEntries[Carret].Message := _message; LogEntries[Carret].Level := _level; + LogEntries[Carret].MessageCode := _messageCode; IF(_sender <> NULL) THEN LogEntries[Carret].Sender := _sender.GetIdentity(); @@ -75,6 +78,7 @@ NAMESPACE AXOpen.Logging LogEntries[Carret].Level := _level; LogEntries[Carret].ToDequeue := TRUE; LogEntries[Carret].Sender := ULINT#0; + LogEntries[Carret].MessageCode := ULINT#0; Carret := Carret + 1; END_IF; END_METHOD diff --git a/src/core/ctrl/src/AxoMessaging/Static/AxoMessenger.st b/src/core/ctrl/src/AxoMessaging/Static/AxoMessenger.st index 786e8eed7..e827a26c7 100644 --- a/src/core/ctrl/src/AxoMessaging/Static/AxoMessenger.st +++ b/src/core/ctrl/src/AxoMessaging/Static/AxoMessenger.st @@ -32,6 +32,17 @@ NAMESPACE AXOpen.Messaging.Static VAR PRIVATE _context : IAxoContext; END_VAR + + VAR PRIVATE + {#ix-attr:[ReadOnly()]} + _requalifiedToCategory : eAxoMessageCategory := eAxoMessageCategory#None; // This property is used to store the new category of the message in case it gets requalified by the messenger service configuration. + END_VAR + + VAR PROTECTED + RISE_SIGNATURE : STRING[4] := '^ : '; + FALL_SIGNATURE : STRING[4] := 'v : '; + END_VAR + /// /// Ensures the inicialization of the instance, so as the deactivation of the message. Must be called cyclically. @@ -41,15 +52,12 @@ NAMESPACE AXOpen.Messaging.Static inObject : IAxoObject; END_VAR - IF _context = NULL THEN - IF inObject.GetContext() = NULL THEN - RETURN; - END_IF; - - SUPER.Run(inObject); - _context := inObject.GetContext(); - END_IF; - + SUPER.Run(inObject); + _context := THIS.GetContext(); + IF(NOT THIS.IsValid()) THEN + RETURN; + END_IF; + IF THIS.MessengerState = eAxoMessengerState#ActiveAcknowledgeRequired AND NOT AcknowledgementRequired THEN THIS.MessengerState := eAxoMessengerState#ActiveAcknowledgeNotRequired; END_IF; @@ -58,13 +66,15 @@ NAMESPACE AXOpen.Messaging.Static THIS.MessengerState := eAxoMessengerState#ActiveAcknowledgeRequired; END_IF; - IF THIS.MessengerState = eAxoMessengerState#ActiveAcknowledgeRequired OR THIS.MessengerState = eAxoMessengerState#ActiveAcknowledgeNotRequired OR THIS.MessengerState = eAxoMessengerState#ActiveAlreadyAcknowledged THEN + IF THIS.MessengerState = eAxoMessengerState#ActiveAcknowledgeRequired OR + THIS.MessengerState = eAxoMessengerState#ActiveAcknowledgeNotRequired OR + THIS.MessengerState = eAxoMessengerState#ActiveAlreadyAcknowledged THEN IF _context.OpenCycleCount() = ULINT#0 OR _context.GetRtc().NowUTC() = LDATE_AND_TIME#1970-01-01-00:00:00.000 THEN THIS.MessengerState := eAxoMessengerState#InvalidImplementation; THIS.Category := eAxoMessageCategory#ProgrammingError; END_IF; IF _context.OpenCycleCount() - ActiveContextCount > UINT#1 THEN - THIS.Deactivate(MessageCode); + THIS.Deactivate(MessageCode, THIS.ToLogLevel(Category)); END_IF; END_IF; @@ -89,8 +99,14 @@ NAMESPACE AXOpen.Messaging.Static METHOD PUBLIC Activate : AXOpen.Messaging.Static.IAxoMessageProperties VAR_INPUT _messageCode : ULINT; + inCategory : eAxoMessageCategory; + END_VAR + VAR _category : eAxoMessageCategory; END_VAR + + _category := inCategory; + IF _context = NULL THEN _context := THIS.GetContext(); END_IF; @@ -108,24 +124,43 @@ NAMESPACE AXOpen.Messaging.Static IF _messageCode = ULINT#0 THEN RETURN; END_IF; + + // "Complete new" activation - IF THIS.MessengerState = eAxoMessengerState#Idle AND MessageCode = ULINT#0 AND ActiveContextCount = ULINT#0 THEN + IF THIS.MessengerState = eAxoMessengerState#Idle AND MessageCode = ULINT#0 + AND ActiveContextCount = ULINT#0 THEN Risen := _context.GetRtc().NowUTC(); Fallen := LDATE_AND_TIME#1970-01-01-00:00:00.000; Acknowledged := LDATE_AND_TIME#1970-01-01-00:00:00.000; - _context.GetLogger().Log('<#Risen#>', THIS.ToLogLevel(_category), THIS); + _context.GetLogger().Log(RISE_SIGNATURE, THIS.ToLogLevel(_category), THIS, _messageCode); MessageCode := _messageCode; Category := _category; + _requalifiedToCategory := eAxoMessageCategory#None; // AcknowledgementRequired := Category >= eAxoMessageCategory#Error; END_IF; + + // Activation with different message code without deactivation of the previous one. + // This can only happen with non-acknowledgeable messages, as acknowledgeable messages require deactivation or acknowledgement + // before being able to be activated again with a different message code. + IF THIS.MessengerState = eAxoMessengerState#ActiveAcknowledgeNotRequired && MessageCode <> _messageCode THEN + Risen := _context.GetRtc().NowUTC(); + Fallen := LDATE_AND_TIME#1970-01-01-00:00:00.000; + Acknowledged := LDATE_AND_TIME#1970-01-01-00:00:00.000; + _context.GetLogger().Log(RISE_SIGNATURE, THIS.ToLogLevel(_category), THIS, _messageCode); + MessageCode := _messageCode; + Category := _category; + _requalifiedToCategory := eAxoMessageCategory#None; + END_IF; // "Reactivation" in case of acknowledgable alarm rised, falled and rised again without acknowledgement - IF THIS.MessengerState = eAxoMessengerState#InactiveWaitingForAcknowledge AND MessageCode = _messageCode AND ActiveContextCount = ULINT#0 THEN + IF THIS.MessengerState = eAxoMessengerState#InactiveWaitingForAcknowledge AND + MessageCode = _messageCode AND + ActiveContextCount = ULINT#0 THEN Risen := _context.GetRtc().NowUTC(); Fallen := LDATE_AND_TIME#1970-01-01-00:00:00.000; - Acknowledged := LDATE_AND_TIME#1970-01-01-00:00:00.000; - _context.GetLogger().Log('<#Risen again before acknowledgement#>', THIS.ToLogLevel(_category), THIS); + Acknowledged := LDATE_AND_TIME#1970-01-01-00:00:00.000; + _requalifiedToCategory := eAxoMessageCategory#None; END_IF; IF MessageCode = _messageCode THEN @@ -133,6 +168,7 @@ NAMESPACE AXOpen.Messaging.Static IF ActiveContextCount = ULINT#0 OR Risen = LDATE_AND_TIME#1970-01-01-00:00:00.000 THEN THIS.MessengerState := eAxoMessengerState#InvalidImplementation; THIS.Category := eAxoMessageCategory#ProgrammingError; + RETURN; END_IF; IF THIS.MessengerState <> eAxoMessengerState#ActiveAlreadyAcknowledged THEN IF AcknowledgementRequired THEN @@ -143,6 +179,21 @@ NAMESPACE AXOpen.Messaging.Static END_IF; END_IF; + // This will requalify the message category in case the messenger service is configured to do so. + // Only potential messages can be requalified to Warning or Error, any other category will not be requalified and will be logged as configured by the user. + IF(_category = eAxoMessageCategory#Potential + AND _context.GetMessengerService().GetRequalifiedMessageCategory() <> eAxoMessageCategory#None + AND _requalifiedToCategory = eAxoMessageCategory#None) THEN + Risen := _context.GetRtc().NowUTC(); + Fallen := LDATE_AND_TIME#1970-01-01-00:00:00.000; + Acknowledged := LDATE_AND_TIME#1970-01-01-00:00:00.000; + MessageCode := _messageCode; + Category := _context.GetMessengerService().GetRequalifiedMessageCategory(); + _requalifiedToCategory := Category; + _context.GetLogger().Log(RISE_SIGNATURE, THIS.ToLogLevel(Category), THIS, _messageCode); + END_IF; + + Activate := THIS; END_METHOD @@ -161,7 +212,7 @@ NAMESPACE AXOpen.Messaging.Static IF _condition THEN ActivateOnCondition := THIS.Activate(_messageCode,_category); ELSE - THIS.Deactivate(_messageCode); + THIS.Deactivate(_messageCode, THIS.ToLogLevel(_category)); END_IF; END_METHOD @@ -184,9 +235,10 @@ NAMESPACE AXOpen.Messaging.Static END_IF; END_METHOD - METHOD PRIVATE Deactivate + METHOD PROTECTED Deactivate VAR_INPUT _messageCode : ULINT; + _logLevel : eLogLevel; END_VAR IF _context = NULL THEN _context := THIS.GetContext(); @@ -195,16 +247,18 @@ NAMESPACE AXOpen.Messaging.Static IF _context = NULL THEN RETURN; END_IF; - + IF _messageCode = ULINT#0 THEN RETURN; END_IF; - - IF THIS.MessengerState = eAxoMessengerState#ActiveAcknowledgeRequired OR THIS.MessengerState = eAxoMessengerState#ActiveAcknowledgeNotRequired OR THIS.MessengerState = eAxoMessengerState#ActiveAlreadyAcknowledged THEN + + IF THIS.MessengerState = eAxoMessengerState#ActiveAcknowledgeRequired OR + THIS.MessengerState = eAxoMessengerState#ActiveAcknowledgeNotRequired OR + THIS.MessengerState = eAxoMessengerState#ActiveAlreadyAcknowledged THEN IF MessageCode = _messageCode THEN ActiveContextCount := ULINT#0; - Fallen := _context.GetRtc().NowUTC(); - _context.GetLogger().Log('<#Fallen#>', eLogLevel#Information, THIS); + Fallen := _context.GetRtc().NowUTC(); + _context.GetLogger().Log(FALL_SIGNATURE, _logLevel, THIS, _messageCode); IF THIS.MessengerState = eAxoMessengerState#ActiveAcknowledgeRequired THEN THIS.MessengerState := eAxoMessengerState#InactiveWaitingForAcknowledge; ELSIF THIS.MessengerState = eAxoMessengerState#ActiveAcknowledgeNotRequired THEN diff --git a/src/core/ctrl/src/AxoMessaging/Static/AxoMessengerService.st b/src/core/ctrl/src/AxoMessaging/Static/AxoMessengerService.st index fd547b2ef..1fbaf58d1 100644 --- a/src/core/ctrl/src/AxoMessaging/Static/AxoMessengerService.st +++ b/src/core/ctrl/src/AxoMessaging/Static/AxoMessengerService.st @@ -95,6 +95,20 @@ NAMESPACE AXOpen.Messaging IsSuspended := _isSuspended; END_METHOD + VAR PRIVATE + _requalificationCategory : eAxoMessageCategory := eAxoMessageCategory#None; + END_VAR + + METHOD PUBLIC RequalifyDownstreamMessages + VAR_INPUT + inMessageCategory : eAxoMessageCategory; + END_VAR + _requalificationCategory := inMessageCategory; + END_METHOD + + METHOD PUBLIC GetRequalifiedMessageCategory : eAxoMessageCategory + GetRequalifiedMessageCategory := _requalificationCategory; + END_METHOD END_CLASS diff --git a/src/core/ctrl/src/abstractions/NULLs.st b/src/core/ctrl/src/abstractions/NULLs.st index a335d74b3..6ef161f31 100644 --- a/src/core/ctrl/src/abstractions/NULLs.st +++ b/src/core/ctrl/src/abstractions/NULLs.st @@ -122,6 +122,7 @@ NAMESPACE AXOpen.Core _message : STRING[80]; _level : eLogLevel; _sender : IAxoObject; + _messageCode : ULINT; END_VAR ; END_METHOD @@ -184,6 +185,17 @@ NAMESPACE AXOpen.Core METHOD PUBLIC IsSuspended : BOOL IsSuspended := _isSuspended; END_METHOD + + METHOD PUBLIC RequalifyDownstreamMessages + VAR_INPUT + inMessageCategory : eAxoMessageCategory; + END_VAR + ; + END_METHOD + + METHOD PUBLIC GetRequalifiedMessageCategory : eAxoMessageCategory + ; + END_METHOD END_CLASS END_NAMESPACE diff --git a/src/core/ctrl/test/AxoMessaging/Static/AxoMessaging_UnitTests.st b/src/core/ctrl/test/AxoMessaging/Static/AxoMessaging_UnitTests.st index ed0b7bc41..01dbbd635 100644 --- a/src/core/ctrl/test/AxoMessaging/Static/AxoMessaging_UnitTests.st +++ b/src/core/ctrl/test/AxoMessaging/Static/AxoMessaging_UnitTests.st @@ -225,6 +225,7 @@ NAMESPACE AXOpen.AxoMessagingStatic_UnitTests VAR PRIVATE _IsSuspended : BOOL := FALSE; + _requalificationCategory : eAxoMessageCategory := eAxoMessageCategory#None; END_VAR METHOD PUBLIC QueueMessage : BOOL @@ -251,6 +252,17 @@ NAMESPACE AXOpen.AxoMessagingStatic_UnitTests METHOD PUBLIC IsSuspended : BOOL IsSuspended :=_IsSuspended; END_METHOD + + METHOD PUBLIC RequalifyDownstreamMessages + VAR_INPUT + inMessageCategory : eAxoMessageCategory; + END_VAR + _requalificationCategory := inMessageCategory; + END_METHOD + + METHOD PUBLIC GetRequalifiedMessageCategory : eAxoMessageCategory + GetRequalifiedMessageCategory := _requalificationCategory; + END_METHOD END_CLASS {S7.extern=ReadWrite} @@ -400,6 +412,27 @@ NAMESPACE AXOpen.AxoMessagingStatic_UnitTests END_METHOD + {Test} + METHOD PUBLIC category_should_be_requalified_when_requalification_changes_while_message_is_active + _suti.InjectMessengerService(_serviceSpy); + + _suti.Open(); + _suti.InitializeRootObject(_suti._object); + _suti._object._messenger.Serve(_suti._object); + _serviceSpy.RequalifyDownstreamMessages(eAxoMessageCategory#None); + _suti._object._messenger.Activate(UINT#1, eAxoMessageCategory#Potential); + Assert.Equal(TRUE, _suti._object._messenger.Category = eAxoMessageCategory#Potential); + _suti.Close(); + + _suti.Open(); + _suti._object._messenger.Serve(_suti._object); + _serviceSpy.RequalifyDownstreamMessages(eAxoMessageCategory#Error); + _suti._object._messenger.Activate(UINT#1, eAxoMessageCategory#Potential); + Assert.Equal(TRUE, _suti._object._messenger.IsActive()); + Assert.Equal(TRUE, _suti._object._messenger.Category = eAxoMessageCategory#Error); + _suti.Close(); + END_METHOD + {Test} METHOD PUBLIC post_should_enqueue_message_with_sender_and_text @@ -1356,6 +1389,247 @@ NAMESPACE AXOpen.AxoMessagingStatic_UnitTests _suti._object._messenger.ActivateOnCondition(UINT#1,FALSE,expMessage.Category).RequireAcknowledgement(); _suti.Close(); END_METHOD + + // ========== MESSAGE REQUALIFICATION TESTS ========== + + {Test} + METHOD PUBLIC potential_message_should_be_requalified_to_warning + VAR + expectedRisen : LDATE_AND_TIME; + END_VAR + //--Arrange + _suti.InjectMessengerService(_serviceSpy); + _serviceSpy.RequalifyDownstreamMessages(eAxoMessageCategory#Warning); + expectedRisen := LDATE_AND_TIME#2023-01-12-17:58:12.123; + _rtc.SetNowUTC(expectedRisen); + _suti.InjectRtc(_rtc); + + //--Act + _suti.Open(); + _suti.InitializeRootObject(_suti._object); + _suti._object._messenger.Serve(_suti._object); + _suti._object._messenger.Activate(UINT#1, eAxoMessageCategory#Potential); + _suti.Close(); + + //--Assert + Assert.Equal(TRUE, _suti._object._messenger.Category = eAxoMessageCategory#Warning); + Assert.Equal(TRUE, _suti._object._messenger.MessageCode = UINT#1); + Assert.Equal(TRUE, THIS.AreEqual(_suti._object._messenger.Risen, expectedRisen)); + Assert.Equal(TRUE, THIS.AreEqual(_suti._object._messenger.Fallen, LDATE_AND_TIME#1970-01-01-00:00:00.000)); + Assert.Equal(TRUE, THIS.AreEqual(_suti._object._messenger.Acknowledged, LDATE_AND_TIME#1970-01-01-00:00:00.000)); + END_METHOD + + {Test} + METHOD PUBLIC potential_message_should_be_requalified_to_error + VAR + expectedRisen : LDATE_AND_TIME; + END_VAR + //--Arrange + _suti.InjectMessengerService(_serviceSpy); + _serviceSpy.RequalifyDownstreamMessages(eAxoMessageCategory#Error); + expectedRisen := LDATE_AND_TIME#2023-01-12-17:58:12.123; + _rtc.SetNowUTC(expectedRisen); + _suti.InjectRtc(_rtc); + + //--Act + _suti.Open(); + _suti.InitializeRootObject(_suti._object); + _suti._object._messenger.Serve(_suti._object); + _suti._object._messenger.Activate(UINT#1, eAxoMessageCategory#Potential); + _suti.Close(); + + //--Assert + Assert.Equal(TRUE, _suti._object._messenger.Category = eAxoMessageCategory#Error); + Assert.Equal(TRUE, _suti._object._messenger.MessageCode = UINT#1); + Assert.Equal(TRUE, THIS.AreEqual(_suti._object._messenger.Risen, expectedRisen)); + END_METHOD + + {Test} + METHOD PUBLIC info_message_should_not_be_requalified + //--Arrange + _suti.InjectMessengerService(_serviceSpy); + _serviceSpy.RequalifyDownstreamMessages(eAxoMessageCategory#Error); + + //--Act + _suti.Open(); + _suti.InitializeRootObject(_suti._object); + _suti._object._messenger.Serve(_suti._object); + _suti._object._messenger.Activate(UINT#1, eAxoMessageCategory#Info); + _suti.Close(); + + //--Assert + Assert.Equal(TRUE, _suti._object._messenger.Category = eAxoMessageCategory#Info); + END_METHOD + + {Test} + METHOD PUBLIC warning_message_should_not_be_requalified + //--Arrange + _suti.InjectMessengerService(_serviceSpy); + _serviceSpy.RequalifyDownstreamMessages(eAxoMessageCategory#Error); + + //--Act + _suti.Open(); + _suti.InitializeRootObject(_suti._object); + _suti._object._messenger.Serve(_suti._object); + _suti._object._messenger.Activate(UINT#1, eAxoMessageCategory#Warning); + _suti.Close(); + + //--Assert + Assert.Equal(TRUE, _suti._object._messenger.Category = eAxoMessageCategory#Warning); + END_METHOD + + {Test} + METHOD PUBLIC error_message_should_not_be_requalified + //--Arrange + _suti.InjectMessengerService(_serviceSpy); + _serviceSpy.RequalifyDownstreamMessages(eAxoMessageCategory#Warning); + + //--Act + _suti.Open(); + _suti.InitializeRootObject(_suti._object); + _suti._object._messenger.Serve(_suti._object); + _suti._object._messenger.Activate(UINT#1, eAxoMessageCategory#Error); + _suti.Close(); + + //--Assert + Assert.Equal(TRUE, _suti._object._messenger.Category = eAxoMessageCategory#Error); + END_METHOD + + {Test} + METHOD PUBLIC critical_message_should_not_be_requalified + //--Arrange + _suti.InjectMessengerService(_serviceSpy); + _serviceSpy.RequalifyDownstreamMessages(eAxoMessageCategory#Error); + + //--Act + _suti.Open(); + _suti.InitializeRootObject(_suti._object); + _suti._object._messenger.Serve(_suti._object); + _suti._object._messenger.Activate(UINT#1, eAxoMessageCategory#Critical); + _suti.Close(); + + //--Assert + Assert.Equal(TRUE, _suti._object._messenger.Category = eAxoMessageCategory#Critical); + END_METHOD + + {Test} + METHOD PUBLIC requalification_should_happen_only_once + //--Arrange + _suti.InjectMessengerService(_serviceSpy); + _serviceSpy.RequalifyDownstreamMessages(eAxoMessageCategory#Warning); + + //--Act - First activation + _suti.Open(); + _suti.InitializeRootObject(_suti._object); + _suti._object._messenger.Serve(_suti._object); + _suti._object._messenger.Activate(UINT#1, eAxoMessageCategory#Potential); + _suti.Close(); + + //--Assert after first activation + Assert.Equal(TRUE, _suti._object._messenger.Category = eAxoMessageCategory#Warning); + + //--Act - Second activation with same message code + _suti.Open(); + _suti._object._messenger.Serve(_suti._object); + _serviceSpy.RequalifyDownstreamMessages(eAxoMessageCategory#Error); // Change requalification to Error + _suti._object._messenger.Activate(UINT#1, eAxoMessageCategory#Potential); + _suti.Close(); + + //--Assert after second activation - should remain Warning, not change to Error + Assert.Equal(TRUE, _suti._object._messenger.Category = eAxoMessageCategory#Warning); + END_METHOD + + {Test} + METHOD PUBLIC requalification_should_not_happen_when_service_is_not_configured_to_requalify + //--Arrange + _suti.InjectMessengerService(_serviceSpy); + _serviceSpy.RequalifyDownstreamMessages(eAxoMessageCategory#None); + + //--Act + _suti.Open(); + _suti.InitializeRootObject(_suti._object); + _suti._object._messenger.Serve(_suti._object); + _suti._object._messenger.Activate(UINT#1, eAxoMessageCategory#Potential); + _suti.Close(); + + //--Assert + Assert.Equal(TRUE, _suti._object._messenger.Category = eAxoMessageCategory#Potential); + END_METHOD + + {Test} + METHOD PUBLIC requalified_message_should_remain_active + //--Arrange + _suti.InjectMessengerService(_serviceSpy); + _serviceSpy.RequalifyDownstreamMessages(eAxoMessageCategory#Error); + + //--Act + _suti.Open(); + _suti.InitializeRootObject(_suti._object); + _suti._object._messenger.Serve(_suti._object); + _suti._object._messenger.Activate(UINT#1, eAxoMessageCategory#Potential); + _suti.Close(); + + //--Assert + Assert.Equal(TRUE, _suti._object._messenger.IsActive()); + Assert.Equal(TRUE, _suti._object._messenger.Category = eAxoMessageCategory#Error); + END_METHOD + + {Test} + METHOD PUBLIC requalification_should_preserve_message_code + VAR + messageCode : ULINT; + END_VAR + //--Arrange + _suti.InjectMessengerService(_serviceSpy); + _serviceSpy.RequalifyDownstreamMessages(eAxoMessageCategory#Warning); + messageCode := ULINT#12345; + + //--Act + _suti.Open(); + _suti.InitializeRootObject(_suti._object); + _suti._object._messenger.Serve(_suti._object); + _suti._object._messenger.Activate(messageCode, eAxoMessageCategory#Potential); + _suti.Close(); + + //--Assert + Assert.Equal(TRUE, _suti._object._messenger.MessageCode = messageCode); + Assert.Equal(TRUE, _suti._object._messenger.Category = eAxoMessageCategory#Warning); + END_METHOD + + {Test} + METHOD PUBLIC requalified_potential_to_warning_should_log_correctly + //--Arrange + _suti.InjectMessengerService(_serviceSpy); + _serviceSpy.RequalifyDownstreamMessages(eAxoMessageCategory#Warning); + + //--Act + _suti.Open(); + _suti.InitializeRootObject(_suti._object); + _suti._object._messenger.Serve(_suti._object); + _suti._object._messenger.Activate(UINT#1, eAxoMessageCategory#Potential); + _suti.Close(); + + //--Assert + Assert.Equal(TRUE, _suti._object._messenger.Category = eAxoMessageCategory#Warning); + Assert.Equal(TRUE, _suti._object._messenger.IsActive()); + END_METHOD + + {Test} + METHOD PUBLIC requalified_potential_to_error_should_require_acknowledgement + //--Arrange + _suti.InjectMessengerService(_serviceSpy); + _serviceSpy.RequalifyDownstreamMessages(eAxoMessageCategory#Error); + + //--Act + _suti.Open(); + _suti.InitializeRootObject(_suti._object); + _suti._object._messenger.Serve(_suti._object); + _suti._object._messenger.Activate(UINT#1, eAxoMessageCategory#Potential); + _suti.Close(); + + //--Assert + Assert.Equal(TRUE, _suti._object._messenger.Category = eAxoMessageCategory#Error); + END_METHOD END_CLASS END_NAMESPACE diff --git a/src/core/docs/AXOLOGGER.md b/src/core/docs/AXOLOGGER.md index 2f80420ba..1217a8cc4 100644 --- a/src/core/docs/AXOLOGGER.md +++ b/src/core/docs/AXOLOGGER.md @@ -155,17 +155,22 @@ This example showcases how to initialize a logger in a .NET application using th --- ## AxoLogger and AxoMessenger -AxoMessenger uses Context AxoLogger to log the rising and falling of an alarm. There is no particular need for the configuration fo this behaviour. +AxoMessenger uses Context AxoLogger to log the rising and falling of an alarm. There is no particular need for the configuration of this behaviour. Here are the mappings between eAxoMessageCategory and eLogLevel as per the code: - Trace messages are logged as Verbose. - Debug messages are logged as Debug. -- Info, TimedOut, and Notification messages are logged as - Information. +- Info and Notification messages are logged as Information. +- **Potential messages are logged as Information** (These are potential problems that may escalate to Warning or Error based on system requalification configuration). - Warning messages are logged as Warning. - Error and ProgrammingError messages are logged as Error. - Critical, Fatal, and Catastrophic messages are logged as Fatal. +### Message Requalification and Logging + +When a `Potential` message is requalified by the downstream `AxoMessengerService` to a `Warning` or `Error` category, the log level will be updated accordingly at the requalification point. This allows for intermediate detection of issues that may escalate during execution of coordinated components and sequencers. + diff --git a/src/core/docs/AXOMESSENGER.md b/src/core/docs/AXOMESSENGER.md index 0a03ca30c..28c1d7faf 100644 --- a/src/core/docs/AXOMESSENGER.md +++ b/src/core/docs/AXOMESSENGER.md @@ -27,9 +27,19 @@ Static `AxoMessenger` is a class that provides a mechanism for delivering static Depending on the [eAxoMessageCategory](../../../docs/apictrl/abstractions/plc.AXOpen.Messaging.eAxoMessageCategory.html) the messenger should require the acknowledgement. -By default the acknowledgement is not required for the levels `Trace`,`Debug`,`Info`,`TimedOut`,`Notification` and `Warning`. +By default the acknowledgement is not required for the levels `Trace`, `Debug`, `Info`, `Notification`, `Potential` and `Warning`. This could be overwritten by calling the `RequireAcknowledgement()` method. -Contrariwise, the acknowledgement is required for the levels `Error`,`ProgrammingError`,`Critical`,`Fatal` and `Catastrophic` by default. This could be overwritten by calling the `DoNotRequireAcknowledgement()` method. +Contrariwise, the acknowledgement is required for the levels `Error`, `ProgrammingError`, `Critical`, `Fatal` and `Catastrophic` by default. This could be overwritten by calling the `DoNotRequireAcknowledgement()` method. + +### Potential Message Category and Requalification + +The `Potential` message category (severity level 150) represents potential problems that may escalate to `Warning` or `Error` states depending on system configuration. This category is useful for: + +- Identifying emerging issues that may require escalation +- Implementing hierarchical error handling in complex workflows +- Allowing downstream components to determine severity based on context + +A `Potential` message can be automatically requalified to `Warning` or `Error` by configuring the messenger service through the `RequalifyDownstreamMessages(category)` method. This is particularly useful in sequencers and multi-step processes where conditions may escalate over time (e.g., a step taking longer than expected may be initially reported as `Potential` and then escalated to `Error` if it exceeds the configured time threshold). **Attributes `MessageText`, `Help` and `PlcTextList`** diff --git a/src/core/src/AXOpen.Core.Blazor/AxoComponent/AxoCompnentsBase/AxoComponentContainerView.razor.cs b/src/core/src/AXOpen.Core.Blazor/AxoComponent/AxoCompnentsBase/AxoComponentContainerView.razor.cs index 3937acf8b..3f8c7d3f3 100644 --- a/src/core/src/AXOpen.Core.Blazor/AxoComponent/AxoCompnentsBase/AxoComponentContainerView.razor.cs +++ b/src/core/src/AXOpen.Core.Blazor/AxoComponent/AxoCompnentsBase/AxoComponentContainerView.razor.cs @@ -1,4 +1,4 @@ -using AXOpen.Messaging; +using AXOpen.Messaging; using AXOpen.Messaging.Static; using AXOpen.VisualComposer.Components.VisualComposerItem; using AXSharp.Presentation.Blazor.Controls.RenderableContent; @@ -62,6 +62,7 @@ public AxoMessageProvider? MessageProvider eAlarmLevel.NoAlarms => "", eAlarmLevel.Unacknowledged => "animate-pulse-danger badge-warning", eAlarmLevel.ActiveInfo => "animate-pulse-danger badge-info", + eAlarmLevel.ActivePotential => "animate-pulse-danger badge-info", eAlarmLevel.ActiveWarnings => "animate-pulse-danger badge-warning", eAlarmLevel.ActiveErrors => "animate-pulse-danger badge-danger", _ => "" @@ -106,6 +107,7 @@ private eAlarmLevel _alarmLevel return seriousness switch { eAxoMessageCategory.Info => eAlarmLevel.ActiveInfo, + eAxoMessageCategory.Potential => eAlarmLevel.ActivePotential, eAxoMessageCategory.Warning => eAlarmLevel.ActiveWarnings, eAxoMessageCategory.Error or eAxoMessageCategory.ProgrammingError or eAxoMessageCategory.Critical => eAlarmLevel.ActiveErrors, _ => eAlarmLevel.NoAlarms @@ -126,6 +128,7 @@ private eAlarmLevel _alarmLevel eAlarmLevel.NoAlarms => "", eAlarmLevel.Unacknowledged => "border-warning", eAlarmLevel.ActiveInfo => "border-info", + eAlarmLevel.ActivePotential => "border-info shadow-glow-info", eAlarmLevel.ActiveWarnings => "border-warning/20! shadow-glow-warning", eAlarmLevel.ActiveErrors => "border-danger/20! shadow-glow-danger", _ => "" @@ -137,6 +140,7 @@ private eAlarmLevel _alarmLevel eAlarmLevel.NoAlarms => "", eAlarmLevel.Unacknowledged => "bg-warning", eAlarmLevel.ActiveInfo => "bg-info", + eAlarmLevel.ActivePotential => "bg-info shadow-glow-info", eAlarmLevel.ActiveWarnings => "bg-warning/20! shadow-glow-warning", eAlarmLevel.ActiveErrors => "bg-danger/20! shadow-glow-danger", _ => "" @@ -148,6 +152,7 @@ private eAlarmLevel _alarmLevel eAlarmLevel.NoAlarms => "bg-background/80", eAlarmLevel.Unacknowledged => "bg-warning", eAlarmLevel.ActiveInfo => "bg-info", + eAlarmLevel.ActivePotential => "bg-info", eAlarmLevel.ActiveWarnings => "bg-warning/20! shadow-glow-warning", eAlarmLevel.ActiveErrors => "bg-danger/20! shadow-glow-danger", _ => "bg-background/80" diff --git a/src/core/src/AXOpen.Core.Blazor/AxoComponent/AxoComponentView.razor.cs b/src/core/src/AXOpen.Core.Blazor/AxoComponent/AxoComponentView.razor.cs index 1d6812741..4da19bd97 100644 --- a/src/core/src/AXOpen.Core.Blazor/AxoComponent/AxoComponentView.razor.cs +++ b/src/core/src/AXOpen.Core.Blazor/AxoComponent/AxoComponentView.razor.cs @@ -1,4 +1,4 @@ -using AXOpen.Messaging; +using AXOpen.Messaging; using AXOpen.Messaging.Static; using AXSharp.Connector; using AXOpen.Core; @@ -51,6 +51,8 @@ private eAlarmLevel _alarmLevel { case eAxoMessageCategory.Info: return eAlarmLevel.ActiveInfo; + case eAxoMessageCategory.Potential: + return eAlarmLevel.ActiveInfo; case eAxoMessageCategory.Warning: return eAlarmLevel.ActiveWarnings; case eAxoMessageCategory.Error: @@ -229,6 +231,7 @@ public enum eAlarmLevel NoAlarms, Unacknowledged, ActiveInfo, + ActivePotential, ActiveWarnings, ActiveErrors } @@ -248,4 +251,4 @@ public AxoComponentStatusView() IsControllable = false; } } -} \ No newline at end of file +} diff --git a/src/core/src/AXOpen.Core.Blazor/AxoDialogs/Locator/AxoDialogLocator.razor.cs b/src/core/src/AXOpen.Core.Blazor/AxoDialogs/Locator/AxoDialogLocator.razor.cs index cab3c9ac2..11e46e7b0 100644 --- a/src/core/src/AXOpen.Core.Blazor/AxoDialogs/Locator/AxoDialogLocator.razor.cs +++ b/src/core/src/AXOpen.Core.Blazor/AxoDialogs/Locator/AxoDialogLocator.razor.cs @@ -1,4 +1,4 @@ -using AXOpen.Core.Blazor.AxoDialogs.Hubs; +using AXOpen.Core.Blazor.AxoDialogs.Hubs; using AXOpen.Core.Blazor.Dialogs; using AXSharp.Connector; using Microsoft.AspNetCore.Components; @@ -88,27 +88,27 @@ protected override async Task InitializeObservationHandling() private async void OnSignalRClient_DialogOpen(object sender, SignalRClientReceivedMessageArgs e) { - Log.Logger.Information($"AxoDialogLocator by SignalR Opening : {e.SymbolOfDialogInstance}"); + Log.Logger.Verbose($"AxoDialogLocator by SignalR Opening : {e.SymbolOfDialogInstance}"); // this message is no supported and required at this moment. await Refresh(); } private async void OnSignalRClient_DialogClose(object sender, SignalRClientReceivedMessageArgs e) { - Log.Logger.Information($"AxoDialogLocator by SignalR Closing: {e.SymbolOfDialogInstance}"); + Log.Logger.Verbose($"AxoDialogLocator by SignalR Closing: {e.SymbolOfDialogInstance}"); _dialogProxyService.RemoveDisplayedDialog(e.SymbolOfDialogInstance); await Refresh(); } private async void OnPlc_DialogInvoked(object? sender, AxoDialogEventArgs e) { - Log.Logger.Information($"AxoDialogLocator by PLC Opening: {e.SymbolOfDialogInstance}"); + Log.Logger.Verbose($"AxoDialogLocator by PLC Opening: {e.SymbolOfDialogInstance}"); await Refresh(); } private async void OnPlc_DialogRemoved(object? sender, AxoDialogEventArgs e) { - Log.Logger.Information($"AxoDialogLocator by PLC Closing: {e.SymbolOfDialogInstance}"); + Log.Logger.Verbose($"AxoDialogLocator by PLC Closing: {e.SymbolOfDialogInstance}"); await Refresh(); } diff --git a/src/core/src/AXOpen.Core.Blazor/AxoDialogs/Locator/AxoDialogLocatorService.cs b/src/core/src/AXOpen.Core.Blazor/AxoDialogs/Locator/AxoDialogLocatorService.cs index bb1596947..f95aaebdb 100644 --- a/src/core/src/AXOpen.Core.Blazor/AxoDialogs/Locator/AxoDialogLocatorService.cs +++ b/src/core/src/AXOpen.Core.Blazor/AxoDialogs/Locator/AxoDialogLocatorService.cs @@ -1,4 +1,4 @@ -using AXOpen.Base.Dialogs; +using AXOpen.Base.Dialogs; using AXSharp.Connector; using Serilog; @@ -140,7 +140,7 @@ protected async void HandleDialogInvocation_FromPlc(object? sender, AxoDialogEve { lock (_lockObject) { - Log.Logger.Information($"AxoDialogLocatorService invoke dialog (from Plc): {senderAsDialogMonitor.Dialog.Symbol}"); + Log.Logger.Verbose($"AxoDialogLocatorService invoke dialog (from Plc): {senderAsDialogMonitor.Dialog.Symbol}"); var exist = DisplayedDialogs.Any(p => p.Symbol == e.SymbolOfDialogInstance); if (!exist) @@ -164,7 +164,7 @@ public void HandleDialogClosing_FromPlc(object? sender, AxoDialogEventArgs e) { lock (_lockObject) { - Log.Logger.Information($"AxoDialogLocatorService remove displayed dialog (from Plc): {senderAsDialogMonitor.Dialog.Symbol}"); + Log.Logger.Verbose($"AxoDialogLocatorService remove displayed dialog (from Plc): {senderAsDialogMonitor.Dialog.Symbol}"); var exist = DisplayedDialogs.Any(p => p.Symbol == senderAsDialogMonitor.Dialog.Symbol); if (exist) @@ -186,7 +186,7 @@ public void RemoveDisplayedDialog(string dialogSymbol) { lock (_lockObject) { - Log.Logger.Information($"AxoDialogLocatorService removing displayed dialog: {dialogSymbol}"); + Log.Logger.Verbose($"AxoDialogLocatorService removing displayed dialog: {dialogSymbol}"); var exist = DisplayedDialogs.Any(p => p.Symbol == dialogSymbol); if (exist) @@ -235,4 +235,4 @@ public void Dispose() _observedDialogs.Clear(); } } -} \ No newline at end of file +} diff --git a/src/core/src/AXOpen.Core.Blazor/AxoMessenger/Static/AxoMessengerView.razor b/src/core/src/AXOpen.Core.Blazor/AxoMessenger/Static/AxoMessengerView.razor index 04fa94053..8ad925b30 100644 --- a/src/core/src/AXOpen.Core.Blazor/AxoMessenger/Static/AxoMessengerView.razor +++ b/src/core/src/AXOpen.Core.Blazor/AxoMessenger/Static/AxoMessengerView.razor @@ -1,4 +1,4 @@ -@namespace AXOpen.Messaging.Static +@namespace AXOpen.Messaging.Static @inherits RenderableComplexComponentBase @using System.Security.Principal @using Operon.Icons @@ -9,7 +9,7 @@
-
+
@ConvertToInternalSeverityName((eAxoMessageCategory)Component.Category.LastValue)
@@ -18,7 +18,7 @@ @Component.GetMessageText() }
-
@StateLabel
+ @*
@StateLabel
*@
@@ -98,15 +98,17 @@ switch (category) { case eAxoMessageCategory.Info: - return Localizer["SeverityInfo"]; + return Localizer["Info"]; + case eAxoMessageCategory.Potential: + return Localizer["Potential"]; case eAxoMessageCategory.Warning: - return Localizer["SeverityWarning"]; + return Localizer["Warning"]; case eAxoMessageCategory.Error: - return Localizer["SeverityMinorFault"]; + return Localizer["Error"]; case eAxoMessageCategory.ProgrammingError: - return Localizer["SeverityMajorFault"]; + return Localizer["System"]; case eAxoMessageCategory.Critical: - return Localizer["SeverityCritical"]; + return Localizer["Critical"]; } return string.Empty; } @@ -117,6 +119,8 @@ { case eAxoMessageCategory.Info: return "bg-linear-to-br from-info/20! from-0% to-background-light! to-50%"; + case eAxoMessageCategory.Potential: + return "bg-linear-to-br from-info/20! from-0% to-background-light! to-50%"; case eAxoMessageCategory.Warning: return "bg-linear-to-br from-warning/20! from-0% to-background-light! to-50%"; case eAxoMessageCategory.Error: @@ -129,14 +133,36 @@ return string.Empty; } + private static string ConvertToBadge(eAxoMessageCategory category) + { + switch (category) + { + case eAxoMessageCategory.Info: + return "badge badge-primary"; + case eAxoMessageCategory.Potential: + return "badge badge-primary"; + case eAxoMessageCategory.Warning: + return "badge badge-warning"; + case eAxoMessageCategory.Error: + return "badge badge-danger"; + case eAxoMessageCategory.ProgrammingError: + return "badge badge-danger"; + case eAxoMessageCategory.Critical: + return "badge badge-danger"; + } + return string.Empty; + } + private static string ConvertToInternalShadowGlowSeverityName(eAxoMessageCategory category) { switch (category) { case eAxoMessageCategory.Info: return "shadow-glow-info"; + case eAxoMessageCategory.Potential: + return "shadow-glow-info"; case eAxoMessageCategory.Warning: - return "shadow-glow-warning"; + return "shadow-glow-warning"; case eAxoMessageCategory.Error: return "shadow-glow-danger"; case eAxoMessageCategory.ProgrammingError: @@ -153,6 +179,8 @@ { case eAxoMessageCategory.Info: return "bg-info"; + case eAxoMessageCategory.Potential: + return "bg-info"; case eAxoMessageCategory.Warning: return "bg-warning"; case eAxoMessageCategory.Error: @@ -181,4 +209,4 @@ _isDetailsExpanded = !_isDetailsExpanded; StateHasChanged(); } -} \ No newline at end of file +} diff --git a/src/core/src/AXOpen.Core.Blazor/AxoObject/AxoObjectDiagnosticsView.razor b/src/core/src/AXOpen.Core.Blazor/AxoObject/AxoObjectDiagnosticsView.razor index 969e71e3b..462e29d34 100644 --- a/src/core/src/AXOpen.Core.Blazor/AxoObject/AxoObjectDiagnosticsView.razor +++ b/src/core/src/AXOpen.Core.Blazor/AxoObject/AxoObjectDiagnosticsView.razor @@ -1,4 +1,4 @@ -@namespace AXOpen.Core +@namespace AXOpen.Core @inherits RenderableComplexComponentBase @using AXOpen.Core @using AXOpen.Messaging.Static @@ -270,11 +270,12 @@ { return category switch { - eAxoMessageCategory.Info => Localizer["SeverityInfo"], - eAxoMessageCategory.Warning => Localizer["SeverityWarning"], - eAxoMessageCategory.Error => Localizer["SeverityError"], - eAxoMessageCategory.ProgrammingError => Localizer["SeverityProgrammingError"], - eAxoMessageCategory.Critical => Localizer["SeverityCritical"], + eAxoMessageCategory.Info => Localizer["Info"], + eAxoMessageCategory.Potential => Localizer["Potential"], + eAxoMessageCategory.Warning => Localizer["Warning"], + eAxoMessageCategory.Error => Localizer["Error"], + eAxoMessageCategory.ProgrammingError => Localizer["System"], + eAxoMessageCategory.Critical => Localizer["Critical"], _ => string.Empty }; } @@ -285,6 +286,8 @@ { case eAxoMessageCategory.Info: return "badge-info"; + case eAxoMessageCategory.Potential: + return "badge-info"; case eAxoMessageCategory.Warning: return "badge-warning"; case eAxoMessageCategory.Error: @@ -305,6 +308,8 @@ { case eAxoMessageCategory.Info: return "bg-info/10!"; + case eAxoMessageCategory.Potential: + return "bg-info/10!"; case eAxoMessageCategory.Warning: return "bg-warning/10!"; case eAxoMessageCategory.Error: @@ -423,7 +428,7 @@ private IJSObjectReference? module; private IEnumerable observedObjects = new List(); private AxoMessageProvider provider; - private static readonly eAxoMessageCategory[] SeverityOptions = Enum.GetValues().ToArray(); + private static readonly eAxoMessageCategory[] SeverityOptions = Enum.GetValues().Skip(1).ToArray(); private readonly HashSet _selectedSeverities = new(SeverityOptions); private bool _shouldBounce = false; private System.Threading.Timer? _bounceTimer; diff --git a/src/core/src/AXOpen.Core.Blazor/Properties/AxOpenCoreResources.de-DE.resx b/src/core/src/AXOpen.Core.Blazor/Properties/AxOpenCoreResources.de-DE.resx index 15db6ae52..660893a39 100644 --- a/src/core/src/AXOpen.Core.Blazor/Properties/AxOpenCoreResources.de-DE.resx +++ b/src/core/src/AXOpen.Core.Blazor/Properties/AxOpenCoreResources.de-DE.resx @@ -189,14 +189,14 @@ Aufgabe zurücksetzen - + Infos - + Warnung - - Kleinere Störung + + Fehler Major Fault @@ -204,10 +204,10 @@ Fehler - + Systemfehler - + Kritisch @@ -260,10 +260,10 @@ Keine Alarme anzuzeigen. - + Details ausblenden - + Details anzeigen diff --git a/src/core/src/AXOpen.Core.Blazor/Properties/AxOpenCoreResources.de.resx b/src/core/src/AXOpen.Core.Blazor/Properties/AxOpenCoreResources.de.resx index 0938b76df..279c9f59e 100644 --- a/src/core/src/AXOpen.Core.Blazor/Properties/AxOpenCoreResources.de.resx +++ b/src/core/src/AXOpen.Core.Blazor/Properties/AxOpenCoreResources.de.resx @@ -133,19 +133,19 @@ Aufgabe zurücksetzen - + Info - + Warnung - - Kleiner Fehler + + Fehler Großer Fehler - + Kritisch @@ -171,4 +171,88 @@ aktiv - + + Fehler + + + Systemfehler + + + Schweregrad + + + Titel + + + Staat + + + Aktionen + + + Alarme suchen + + + Titel oder Ausrüstung suchen + + + Auffrischen + + + Meldungen aktualisieren + + + Es werden keine Alarme angezeigt. + + + Details ausblenden + + + Details anzeigen + + + Alarm quittieren + + + ACKN + + + Ack + + + Anhängig + + + Aktiv - Ack + + + Alarme + + + Alarm + + + Dienstansicht umschalten + + + Normal + + + Dienst + + + KONFIGURATION + + + STAAT + + + Daten + + + Schweregrad + + + Alarm-Code + + \ No newline at end of file diff --git a/src/core/src/AXOpen.Core.Blazor/Properties/AxOpenCoreResources.es-ES.resx b/src/core/src/AXOpen.Core.Blazor/Properties/AxOpenCoreResources.es-ES.resx index 5fa26fad3..d38bdcd05 100644 --- a/src/core/src/AXOpen.Core.Blazor/Properties/AxOpenCoreResources.es-ES.resx +++ b/src/core/src/AXOpen.Core.Blazor/Properties/AxOpenCoreResources.es-ES.resx @@ -189,14 +189,14 @@ Reiniciar tarea - + Información - + Advertencia - - Fallo menor + + Fallo Fallo grave @@ -204,10 +204,10 @@ Error - + Error del sistema - + Crítica @@ -260,10 +260,10 @@ No hay alarmas para mostrar. - + Ocultar detalles - + Mostrar detalles diff --git a/src/core/src/AXOpen.Core.Blazor/Properties/AxOpenCoreResources.es.resx b/src/core/src/AXOpen.Core.Blazor/Properties/AxOpenCoreResources.es.resx index 5e52a9c75..96d4fe46c 100644 --- a/src/core/src/AXOpen.Core.Blazor/Properties/AxOpenCoreResources.es.resx +++ b/src/core/src/AXOpen.Core.Blazor/Properties/AxOpenCoreResources.es.resx @@ -133,19 +133,19 @@ Restablecer tarea - + Info - + Advertencia - - Fallo menor + + Fallo Fallo mayor - + Crítico @@ -171,4 +171,88 @@ activo - + + Error + + + Error del sistema + + + Gravedad + + + Título + + + Estado + + + Acciones + + + Buscar alarmas + + + Buscar título o equipo + + + Actualizar + + + Actualizar mensajes + + + No hay alarmas que mostrar. + + + Ocultar detalles + + + Mostrar detalles + + + Confirmar alarma + + + ACKN + + + Ack + + + Pendiente + + + Activo - Ack + + + alarmas + + + alarma + + + Alternar vista de servicio + + + Normal + + + Servicio + + + CONFIGURACIÓN + + + ESTADO + + + Datos + + + Gravedad + + + Código de alarma + + \ No newline at end of file diff --git a/src/core/src/AXOpen.Core.Blazor/Properties/AxOpenCoreResources.hu-HU.resx b/src/core/src/AXOpen.Core.Blazor/Properties/AxOpenCoreResources.hu-HU.resx index 64eb644c1..1997bb0de 100644 --- a/src/core/src/AXOpen.Core.Blazor/Properties/AxOpenCoreResources.hu-HU.resx +++ b/src/core/src/AXOpen.Core.Blazor/Properties/AxOpenCoreResources.hu-HU.resx @@ -189,14 +189,14 @@ Feladat alaphelyzetbe állítása - + Info - + Figyelmeztetés - - Kisebb hiba + + Hiba Fő hiba @@ -204,10 +204,10 @@ Hiba - + Rendszerhiba - + Kritikus @@ -260,10 +260,10 @@ Nincsenek megjelenítendő riasztások. - + Részletek elrejtése - + Részletek megjelenítése diff --git a/src/core/src/AXOpen.Core.Blazor/Properties/AxOpenCoreResources.pl-PL.resx b/src/core/src/AXOpen.Core.Blazor/Properties/AxOpenCoreResources.pl-PL.resx index 473578a71..eef4cea96 100644 --- a/src/core/src/AXOpen.Core.Blazor/Properties/AxOpenCoreResources.pl-PL.resx +++ b/src/core/src/AXOpen.Core.Blazor/Properties/AxOpenCoreResources.pl-PL.resx @@ -189,14 +189,14 @@ Resetowanie zadania - + Info - + Ostrzeżenie - - Drobna usterka + + Usterka Główny błąd @@ -204,10 +204,10 @@ Błąd - + Błąd systemu - + Krytyczny @@ -260,10 +260,10 @@ Brak alarmów do wyświetlenia. - + Ukryj szczegóły - + Pokaż szczegóły diff --git a/src/core/src/AXOpen.Core.Blazor/Properties/AxOpenCoreResources.resx b/src/core/src/AXOpen.Core.Blazor/Properties/AxOpenCoreResources.resx index a1870407a..e6d6783a3 100644 --- a/src/core/src/AXOpen.Core.Blazor/Properties/AxOpenCoreResources.resx +++ b/src/core/src/AXOpen.Core.Blazor/Properties/AxOpenCoreResources.resx @@ -133,14 +133,14 @@ Reset task - + Info - + Warning - - Minor Fault + + Error Major Fault @@ -148,10 +148,10 @@ Error - + System Error - + Critical @@ -206,10 +206,10 @@ No alarms to display. - + Hide details - + Show details diff --git a/src/core/src/AXOpen.Core.Blazor/Properties/AxOpenCoreResources.sk-SK.resx b/src/core/src/AXOpen.Core.Blazor/Properties/AxOpenCoreResources.sk-SK.resx index 3c7f02390..7ce98b6ff 100644 --- a/src/core/src/AXOpen.Core.Blazor/Properties/AxOpenCoreResources.sk-SK.resx +++ b/src/core/src/AXOpen.Core.Blazor/Properties/AxOpenCoreResources.sk-SK.resx @@ -189,14 +189,14 @@ Obnovenie úlohy - + Informácie - + Upozornenie - - Menšia porucha + + Chyba Hlavná porucha @@ -204,10 +204,10 @@ Chyba - + Chyba systému - + Kritické @@ -260,10 +260,10 @@ Žiadne alarmy na zobrazenie. - + Skryť podrobnosti - + Zobraziť podrobnosti diff --git a/src/core/src/AXOpen.Core.Blazor/Properties/AxOpenCoreResources.sk.resx b/src/core/src/AXOpen.Core.Blazor/Properties/AxOpenCoreResources.sk.resx index e37e0a33e..6bc6b9d08 100644 --- a/src/core/src/AXOpen.Core.Blazor/Properties/AxOpenCoreResources.sk.resx +++ b/src/core/src/AXOpen.Core.Blazor/Properties/AxOpenCoreResources.sk.resx @@ -133,19 +133,19 @@ Resetovať úlohu - + Info - + Varovanie - - Menšia chyba + + Chyba Väčšia chyba - + Kritický @@ -171,4 +171,88 @@ aktívny - + + Chyba + + + Chyba systému + + + Závažnosť + + + Názov + + + Štát + + + Činnosti + + + Vyhľadávanie alarmov + + + Vyhľadávanie názvu alebo zariadenia + + + Obnoviť + + + Obnovenie správ + + + Nezobrazujú sa žiadne alarmy. + + + Skryť podrobnosti + + + Zobraziť podrobnosti + + + Potvrdenie alarmu + + + ACKN + + + Ack + + + Čaká sa na + + + Aktívne - Ack + + + alarmy + + + alarm + + + Prepnutie zobrazenia služby + + + Normálne + + + Služba + + + KONFIGURÁCIA + + + ŠTÁT + + + Údaje + + + Závažnosť + + + Kód alarmu + + \ No newline at end of file diff --git a/src/core/src/AXOpen.Core/AxoLogger/AxoLogger.cs b/src/core/src/AXOpen.Core/AxoLogger/AxoLogger.cs index 03aa3256b..071837289 100644 --- a/src/core/src/AXOpen.Core/AxoLogger/AxoLogger.cs +++ b/src/core/src/AXOpen.Core/AxoLogger/AxoLogger.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Security.Principal; @@ -58,9 +58,13 @@ await Task.Run(async () => switch (sender) { + case AxoStepTimedOutMessenger timedOutMessenger: + level = (eLogLevel)entry.Level.LastValue; + message = $"{entry.Message.LastValue}"; + break; case AxoMessenger messenger: await messenger.ReadAsync(); - message = $"{entry.Message.LastValue} : {messenger.GetMessageText()}"; + message = $"{entry.Message.LastValue} {messenger.GetMessageText(messenger.MessageCode.LastValue)}"; break; case AxoStep step: await step.ReadAsync(); diff --git a/src/core/src/AXOpen.Core/AxoMessenger/Static/AxoMessageProvider.cs b/src/core/src/AXOpen.Core/AxoMessenger/Static/AxoMessageProvider.cs index e5c9783cb..21988b4cb 100644 --- a/src/core/src/AXOpen.Core/AxoMessenger/Static/AxoMessageProvider.cs +++ b/src/core/src/AXOpen.Core/AxoMessenger/Static/AxoMessageProvider.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Reflection.Metadata; @@ -16,12 +16,15 @@ namespace AXOpen.Messaging.Static ///
public class AxoMessageProvider { - private AxoMessageProvider(IEnumerable observedObjects) + private AxoMessageProvider(IEnumerable observedObjects, int observedDepth = int.MaxValue) { this.ObservedObjects = observedObjects; + this.ObservedDepth = observedDepth; } public IEnumerable ObservedObjects { get; } + + public int ObservedDepth { get; } private int? _cachedActiveMessagesCount; private int? _maxActiveMessagesCount; @@ -192,7 +195,7 @@ public AxoMessenger[]? Messengers var retVal = new List(); foreach (var observedObject in ObservedObjects.Where(p => p != null)) { - retVal.AddRange(observedObject.GetChildren().Flatten(p => p.GetChildren()) + retVal.AddRange(observedObject.GetChildren().Flatten(p => p.GetChildren(), this.ObservedDepth) .OfType()); } @@ -208,9 +211,9 @@ public AxoMessenger[]? Messengers /// /// The collection of observed objects. /// A new instance of the AxoMessageProvider class. - public static AxoMessageProvider Create(IEnumerable observedObjects) + public static AxoMessageProvider Create(IEnumerable observedObjects, int observedDepth = int.MaxValue) { - return new AxoMessageProvider(observedObjects); + return new AxoMessageProvider(observedObjects, observedDepth); } diff --git a/src/core/src/AXOpen.Core/AxoMessenger/Static/AxoMessenger.cs b/src/core/src/AXOpen.Core/AxoMessenger/Static/AxoMessenger.cs index cf9687b90..af368f270 100644 --- a/src/core/src/AXOpen.Core/AxoMessenger/Static/AxoMessenger.cs +++ b/src/core/src/AXOpen.Core/AxoMessenger/Static/AxoMessenger.cs @@ -197,21 +197,24 @@ public async Task ReadDetailsAsync() return FindParentOfType(node.GetParent(), depth++); } + + public string GetMessageText() + { + return GetMessageText(this.MessageCode.LastValue); + } + /// /// Retrieves the message text based on the message code. /// /// The message text string. - public string GetMessageText() - { + public string GetMessageText(ulong messageCode) + { //18446744073709551615 - - ulong messageCode = this.MessageCode.LastValue; - if (messageCode == ulong.MaxValue) - { - return this.Message.LastValue; + { + return this.Message.GetCyclic(); } - + string retVal = ""; string prefix = ""; if (this.MessengerState.Equals(eAxoMessengerState.InvalidImplementation) || this.MessengerState.LastValue.Equals((short)eAxoMessengerState.InvalidImplementation)) @@ -345,4 +348,4 @@ private void ChekIfHelpTextDefined() public class EmptyMessenger { public string MessageText { get; set; } = "All good here."; -} \ No newline at end of file +} diff --git a/src/core/src/AXOpen.Core/Resources/PlcStringResources.de-DE.resx b/src/core/src/AXOpen.Core/Resources/PlcStringResources.de-DE.resx index 390626be9..9e8c0113e 100644 --- a/src/core/src/AXOpen.Core/Resources/PlcStringResources.de-DE.resx +++ b/src/core/src/AXOpen.Core/Resources/PlcStringResources.de-DE.resx @@ -118,4 +118,28 @@ Gefallen + + Ist manuell steuerbar + + + Messenger 1 - Aktivierung mit der Methode "Activate", die innerhalb der if-Anweisung aufgerufen wird + + + RemoteTaskMessenger + + + Bote + + + Befehlsstatus + + + Schrittweiser Betrieb + + + Sequenz-Modus + + + Schritt ausführen + \ No newline at end of file diff --git a/src/core/src/AXOpen.Core/Resources/PlcStringResources.de.resx b/src/core/src/AXOpen.Core/Resources/PlcStringResources.de.resx index 801b17917..355b32082 100644 --- a/src/core/src/AXOpen.Core/Resources/PlcStringResources.de.resx +++ b/src/core/src/AXOpen.Core/Resources/PlcStringResources.de.resx @@ -192,4 +192,13 @@ Gefallen + + Messenger 1 - Aktivierung mit der Methode "Activate", die innerhalb der if-Anweisung aufgerufen wird + + + RemoteTaskMessenger + + + Bote + \ No newline at end of file diff --git a/src/core/src/AXOpen.Core/Resources/PlcStringResources.es-ES.resx b/src/core/src/AXOpen.Core/Resources/PlcStringResources.es-ES.resx index 2741f1283..579fe37bf 100644 --- a/src/core/src/AXOpen.Core/Resources/PlcStringResources.es-ES.resx +++ b/src/core/src/AXOpen.Core/Resources/PlcStringResources.es-ES.resx @@ -118,4 +118,28 @@ Caídos + + Se puede controlar manualmente + + + Mensajero 1 - activar usando el método 'Activate' llamado dentro de la sentencia if + + + RemoteTaskMessenger + + + Mensajero + + + Estado de mando + + + Modo escalonado + + + Modo secuencia + + + Ejecutar paso + \ No newline at end of file diff --git a/src/core/src/AXOpen.Core/Resources/PlcStringResources.es.resx b/src/core/src/AXOpen.Core/Resources/PlcStringResources.es.resx index 7aa7c3a27..4fbdd491b 100644 --- a/src/core/src/AXOpen.Core/Resources/PlcStringResources.es.resx +++ b/src/core/src/AXOpen.Core/Resources/PlcStringResources.es.resx @@ -192,4 +192,13 @@ Caídos + + Mensajero 1 - activar usando el método 'Activate' llamado dentro de la sentencia if + + + RemoteTaskMessenger + + + Mensajero + \ No newline at end of file diff --git a/src/core/src/AXOpen.Core/Resources/PlcStringResources.hu-HU.resx b/src/core/src/AXOpen.Core/Resources/PlcStringResources.hu-HU.resx index e96f90876..6d6ed328e 100644 --- a/src/core/src/AXOpen.Core/Resources/PlcStringResources.hu-HU.resx +++ b/src/core/src/AXOpen.Core/Resources/PlcStringResources.hu-HU.resx @@ -118,4 +118,28 @@ Fallen + + Kézi vezérlésű + + + Messenger 1 - aktiválás az if utasításon belül meghívott 'Activate' módszerrel + + + RemoteTaskMessenger + + + Messenger + + + Parancsállapot + + + Lépcső üzemmód + + + Szekvencia üzemmód + + + Futtatási lépés + \ No newline at end of file diff --git a/src/core/src/AXOpen.Core/Resources/PlcStringResources.pl-PL.resx b/src/core/src/AXOpen.Core/Resources/PlcStringResources.pl-PL.resx index 637617c75..3dd5873ca 100644 --- a/src/core/src/AXOpen.Core/Resources/PlcStringResources.pl-PL.resx +++ b/src/core/src/AXOpen.Core/Resources/PlcStringResources.pl-PL.resx @@ -118,4 +118,28 @@ Upadły + + Możliwość sterowania ręcznego + + + Messenger 1 - aktywacja przy użyciu metody "Activate" wywołanej wewnątrz instrukcji if + + + RemoteTaskMessenger + + + Posłaniec + + + Stan polecenia + + + Tryb krokowy + + + Tryb sekwencji + + + Krok działania + \ No newline at end of file diff --git a/src/core/src/AXOpen.Core/Resources/PlcStringResources.sk.resx b/src/core/src/AXOpen.Core/Resources/PlcStringResources.sk.resx index 58c837d61..d6cc36784 100644 --- a/src/core/src/AXOpen.Core/Resources/PlcStringResources.sk.resx +++ b/src/core/src/AXOpen.Core/Resources/PlcStringResources.sk.resx @@ -192,4 +192,13 @@ Fallen + + Messenger 1 - aktivácia pomocou metódy 'Activate' volanej vo vnútri príkazu if + + + RemoteTaskMessenger + + + Messenger + \ No newline at end of file diff --git a/src/inspectors/src/AXOpen.Inspectors.blazor/AxoInspectorDialog/AxoInspectorDialogDialogView.razor b/src/inspectors/src/AXOpen.Inspectors.blazor/AxoInspectorDialog/AxoInspectorDialogDialogView.razor index ac31b4da5..629d0838d 100644 --- a/src/inspectors/src/AXOpen.Inspectors.blazor/AxoInspectorDialog/AxoInspectorDialogDialogView.razor +++ b/src/inspectors/src/AXOpen.Inspectors.blazor/AxoInspectorDialog/AxoInspectorDialogDialogView.razor @@ -1,4 +1,4 @@ -@namespace AXOpen.Inspectors +@namespace AXOpen.Inspectors @using System.Globalization @using AXOpen.Core.Blazor.AxoDialogs @using AXSharp.Connector.Localizations @@ -57,10 +57,13 @@ @code { + private string? currentUrl; string InspectionDescription { get; set; } + string InspectionId { get; set; } + eInspectorType InspectorType { get; set; } async Task GetInspectorType() @@ -72,8 +75,9 @@ { currentUrl = NavigationManager.ToBaseRelativePath(NavigationManager.Uri); NavigationManager.LocationChanged += OnLocationChanged; - + InspectionDescription = await Component._inspectionDetails.GetAsync(CultureInfo.CurrentCulture); + InspectionId = Component._inspectionDetails.LastValue; InspectorType = await GetInspectorType(); return base.OnInitializedAsync(); } diff --git a/src/inspectors/src/AXOpen.Inspectors.blazor/AxoInspectorDialog/AxoInspectorDialogDialogView.razor.cs b/src/inspectors/src/AXOpen.Inspectors.blazor/AxoInspectorDialog/AxoInspectorDialogDialogView.razor.cs index 35658b9b5..a4d42796d 100644 --- a/src/inspectors/src/AXOpen.Inspectors.blazor/AxoInspectorDialog/AxoInspectorDialogDialogView.razor.cs +++ b/src/inspectors/src/AXOpen.Inspectors.blazor/AxoInspectorDialog/AxoInspectorDialogDialogView.razor.cs @@ -1,4 +1,4 @@ -using AXOpen.Core; +using AXOpen.Core; using AXOpen.Core.Blazor.AxoDialogs; using AXOpen.Logging; using AXSharp.Connector; @@ -17,6 +17,11 @@ namespace AXOpen.Inspectors public partial class AxoInspectorDialogDialogView : AxoDialogBaseView, IDisposable { + private string? InspectorDescription() + { + return InspectionId; + } + public bool RetryDisabled { get; set; } = false; protected override void OnAfterRender(bool firstRender) @@ -56,7 +61,7 @@ public async Task Retry() base.Component._dialogueRetry.Edit = true; await base.CloseDialogsWithSignalR(); var identity = (await _asp.GetAuthenticationStateAsync()).User.Identity; - AxoApplication.Current.Logger.Information($"{nameof(Retry)} of '{Component.HumanReadable}' was executed.", identity); + AxoApplication.Current.Logger.Information($"{nameof(Retry)} of '{InspectorDescription()}' was executed.", identity); } else { @@ -69,14 +74,14 @@ public async Task Terminate() base.Component._dialogueTerminate.Edit = true; await base.CloseDialogsWithSignalR(); var identity = (await _asp.GetAuthenticationStateAsync()).User.Identity; - AxoApplication.Current.Logger.Information($"{nameof(Terminate)} of '{Component.HumanReadable}' was executed.", identity); + AxoApplication.Current.Logger.Information($"{nameof(Terminate)} of '{InspectorDescription()}' was executed.", identity); } public async Task Override() { base.Component._dialogueOverride.Edit = true; await base.CloseDialogsWithSignalR(); var identity = (await _asp.GetAuthenticationStateAsync()).User.Identity; - AxoApplication.Current.Logger.Information($"{nameof(Override)} of '{Component.HumanReadable}' was executed.", identity); + AxoApplication.Current.Logger.Information($"{nameof(Override)} of '{InspectorDescription()}' was executed.", identity); } diff --git a/src/inspectors/src/AxOpen.Inspectors.Blazor/AxoInspectorDialog/AxoInspectorDialogDialogView.razor b/src/inspectors/src/AxOpen.Inspectors.Blazor/AxoInspectorDialog/AxoInspectorDialogDialogView.razor deleted file mode 100644 index ac31b4da5..000000000 --- a/src/inspectors/src/AxOpen.Inspectors.Blazor/AxoInspectorDialog/AxoInspectorDialogDialogView.razor +++ /dev/null @@ -1,86 +0,0 @@ -@namespace AXOpen.Inspectors -@using System.Globalization -@using AXOpen.Core.Blazor.AxoDialogs -@using AXSharp.Connector.Localizations -@using Microsoft.AspNetCore.Components.Authorization; -@using Microsoft.AspNetCore.Components.Routing -@using AXSharp.Presentation.Blazor.Controls.RenderableContent -@inherits AxoDialogBaseView -@inject AuthenticationStateProvider _asp -@inject NavigationManager NavigationManager - -
-
-

@InspectionDescription

-
-
- @switch (InspectorType) - { - case eInspectorType.Digital: - - break; - case eInspectorType.Analogue: - - break; - case eInspectorType.Data: - - break; - } - -
- - - - - - - - - - - - - - - -
-
- - @if (!_asp.GetAuthenticationStateAsync().Result.User.Identity.IsAuthenticated) - { -
-

@Localizer["NotAuthorizedPleaseLogIn"] @Localizer["LogIn"].

- @Localizer["LogIn"] -
- } -
- -@code -{ - private string? currentUrl; - - string InspectionDescription { get; set; } - - eInspectorType InspectorType { get; set; } - - async Task GetInspectorType() - { - return (eInspectorType)await Component._inspectorType.GetAsync(); - } - - protected override async Task OnInitializedAsync() - { - currentUrl = NavigationManager.ToBaseRelativePath(NavigationManager.Uri); - NavigationManager.LocationChanged += OnLocationChanged; - - InspectionDescription = await Component._inspectionDetails.GetAsync(CultureInfo.CurrentCulture); - InspectorType = await GetInspectorType(); - return base.OnInitializedAsync(); - } - - private void OnLocationChanged(object? sender, LocationChangedEventArgs e) - { - currentUrl = NavigationManager.ToBaseRelativePath(e.Location); - StateHasChanged(); - } -} diff --git a/src/inspectors/src/AxOpen.Inspectors.Blazor/AxoInspectorDialog/AxoInspectorDialogDialogView.razor.cs b/src/inspectors/src/AxOpen.Inspectors.Blazor/AxoInspectorDialog/AxoInspectorDialogDialogView.razor.cs deleted file mode 100644 index 35658b9b5..000000000 --- a/src/inspectors/src/AxOpen.Inspectors.Blazor/AxoInspectorDialog/AxoInspectorDialogDialogView.razor.cs +++ /dev/null @@ -1,109 +0,0 @@ -using AXOpen.Core; -using AXOpen.Core.Blazor.AxoDialogs; -using AXOpen.Logging; -using AXSharp.Connector; -using Pocos.AXOpen.Inspectors; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; -using AXSharp.Connector.Localizations; - -namespace AXOpen.Inspectors -{ - public partial class AxoInspectorDialogDialogView : AxoDialogBaseView, IDisposable - { - - public bool RetryDisabled { get; set; } = false; - - protected override void OnAfterRender(bool firstRender) - { - _inspector = null; - try - { - // _inspectorIndentity property is subscribed in the method base.ConfigurePolling() - var parent = Component.GetParent(); - - if (parent != null) - { - if (parent is AxoInspector i) - { - _inspector = i; - } - } - } - catch (Exception) - { - } - - if (base.Component._isOverInspected.Cyclic) - RetryDisabled = true; - } - - public override void ConfigurePolling() - { - StartPolling(this.Component, 250); - } - - public async Task Retry() - { - if (base.Component != null && !base.Component._isOverInspected.Cyclic) - { - RetryDisabled = false; - base.Component._dialogueRetry.Edit = true; - await base.CloseDialogsWithSignalR(); - var identity = (await _asp.GetAuthenticationStateAsync()).User.Identity; - AxoApplication.Current.Logger.Information($"{nameof(Retry)} of '{Component.HumanReadable}' was executed.", identity); - } - else - { - RetryDisabled = true; - } - - } - public async Task Terminate() - { - base.Component._dialogueTerminate.Edit = true; - await base.CloseDialogsWithSignalR(); - var identity = (await _asp.GetAuthenticationStateAsync()).User.Identity; - AxoApplication.Current.Logger.Information($"{nameof(Terminate)} of '{Component.HumanReadable}' was executed.", identity); - } - public async Task Override() - { - base.Component._dialogueOverride.Edit = true; - await base.CloseDialogsWithSignalR(); - var identity = (await _asp.GetAuthenticationStateAsync()).User.Identity; - AxoApplication.Current.Logger.Information($"{nameof(Override)} of '{Component.HumanReadable}' was executed.", identity); - } - - - public string Description - { - get => string.IsNullOrEmpty(base.Component.AttributeName) ? base.Component.GetSymbolTail() : base.Component.GetAttributeName(CultureInfo.CurrentUICulture); - - } - - private ITwinObject _inspector; - public ITwinObject Inspector - { - get - { - - return _inspector; - } - - } - - - - public override void Dispose() - { - base.Dispose(); - } - - } - -} diff --git a/src/timers/ctrl/test/Abstractions.st b/src/timers/ctrl/test/Abstractions.st index 649697ee6..da807c659 100644 --- a/src/timers/ctrl/test/Abstractions.st +++ b/src/timers/ctrl/test/Abstractions.st @@ -34,6 +34,17 @@ NAMESPACE AXOpen.Timers_tests END_VAR ; END_METHOD + + METHOD PUBLIC Log + VAR_INPUT + _message : STRING[80]; + _level : eLogLevel; + _sender : IAxoObject; + _messageCode : ULINT; + END_VAR + + ; + END_METHOD END_CLASS {S7.extern=ReadWrite} diff --git a/src/toolbox/src/AXOpen.ToolBox/Flattener.cs b/src/toolbox/src/AXOpen.ToolBox/Flattener.cs index 55d2a1b0b..1acce469f 100644 --- a/src/toolbox/src/AXOpen.ToolBox/Flattener.cs +++ b/src/toolbox/src/AXOpen.ToolBox/Flattener.cs @@ -9,10 +9,21 @@ public static class Flattener /// /// /// + /// Optional maximum depth for flattening. Defaults to no limit. /// public static IEnumerable Flatten( this IEnumerable e , Func> f - ) => e.SelectMany(c => f(c).Flatten(f)).Concat(e); + , int maxDepth = int.MaxValue + ) => FlattenCore(e, f, maxDepth, 0); + + private static IEnumerable FlattenCore( + IEnumerable e + , Func> f + , int maxDepth + , int depth + ) => depth < maxDepth + ? e.SelectMany(c => FlattenCore(f(c), f, maxDepth, depth + 1)).Concat(e) + : e; } } \ No newline at end of file