AxoCauseAnalyzer + AxoIncidentBarView: probable-cause ranking over AxoMessenger#1156
Merged
Conversation
- Implemented AxoCauseAnalyzer to analyze and rank probable causes of incidents based on severity, acknowledgment status, and age. - Introduced AxoIncidentBarPresenter to manage the display state of incident bars, including visibility, severity, and acknowledgment status. - Created IRankableMessage interface and AxoMessengerRankableAdapter for message adaptation. - Added unit tests for AxoCauseAnalyzer, AxoIncidentBarPresenter, and AxoMessengerRankableAdapter to ensure functionality and correctness. - Defined AxoCauseAnalyzerOptions for configurable parameters like burst window and hold duration. - Established event handling for changes in top causes to trigger UI updates.
- New axopen/src/core/docs/AxoIncidentBar.md describing ranking formula, severity floor, Blazor mount pattern - toc.yml entry under Messengers (Alarms) - Per-lib CHANGELOG 0.56.0 entry; GitVersion next-version 0.55.1 -> 0.56.0 - Central CHANGELOG PR-style entry covering analyzer/bar/presenter/adapter + showcase + template wiring + topology fix + 28 new tests - Showcase razor BarMount + ProviderCreate tagged regions so docs reference real code via [!code-*[]] directives Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| private static string SafeMessageText(AxoMessenger m) | ||
| { | ||
| try { return m.GetMessageText(); } | ||
| catch { return string.Empty; } |
| ExpireStaleAckPending(); | ||
| _presenter.Refresh(); | ||
| } | ||
| catch { /* swallow: a bad cycle should not crash the bar */ } |
Comment on lines
+172
to
+177
| catch | ||
| { | ||
| _ackPendingSince.Remove(symbol); | ||
| _presenter.NotifyAckResolved(_state.TopCause.Message); | ||
| await InvokeAsync(StateHasChanged); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
AxoCauseAnalyzerranks active Error+AxoMessengerinstances by severity, burst-root (sliding 8 s window), twin-tree topology (container-Symbol prefix), ack state, and age decay;Changedevent fires only on top-cause symbol flip.AxoIncidentBarViewBlazor component renders the top probable cause as a persistent severity-colored bar — zero height when idle,animate-pulsefor Critical/ProgrammingError, click-to-expand panel with top-5 candidates, optimistic Acknowledge, admin-gated Restore.AxoMessengerABI change. Application mounts one component inMainLayout.razorand creates one provider per circuit.AXOpen.Core.Tests/Messaging/). Showcase page + nested topology example + docs + central/per-lib CHANGELOG. GitVersion bumped0.55.1→0.56.0.Changes from CHANGELOG
Workspace
Note: Additive change in
src/core/src/AXOpen.Core/AxoMessenger/Static/andsrc/core/src/AXOpen.Core.Blazor/AxoMessenger/Static/. No PLC source change required for application opt-in; the analyzer reads only fieldsAxoMessengeralready exposes. Branch:feat-most-probable-failure-cause.AxoCauseAnalyzer(AXOpen.Messaging.Static) — heuristic ranking layered onAxoMessageProvider. Scores each Error-or-above active messenger by severity (operator-actionability map: Critical=1.0, Error=0.9, ProgrammingError=0.85, Warning=0.6, Potential=0.4, Info=0.1), burst-root (earliestRisenwithin slidingBurstWindow, default 8 s, anchored on latestRisen), twin-tree topology (container-Symbol-prefixDownstreamCount), acknowledgement state, and age decay.Changedevent fires only on top-cause symbol flip. Anti-strobe hold (HoldDurationdefault 2 s) suppresses PLC-cycle mid-read empty publishes. Static factoryAxoCauseAnalyzer.Create(AxoMessageProvider, options?, nowUtc?)wires the adapter pipeline.AxoIncidentBarView(AXOpen.Core.Blazor, namespaceAXOpen.Messaging.Static) — sticky in-flow Blazor component (zero height when idle, no layout reflow) that renders the top probable cause as a severity-colored bar (shadow-glow-{danger|warning|info}+ flatbg-{token}/15tint — color token matches the glow).animate-pulsefor Critical/ProgrammingError until acknowledged. Click-to-expand panel shows top-5 candidates with score, evidence (burst-root, downstream count), optimistic Acknowledge, admin-gated Restore (<AuthorizeView Roles="Administrator">). Adaptive polling — 750 ms when any Error+ active, 2500 ms idle — using two-tier batch reads via the existing provider.aria-live="polite"for accessibility.AxoIncidentBarPresenter— pure-logic seam. ExposesCurrentState(visibility, severity bucket, pulse flag, rows, ack-pending markers) and static Tailwind class mappers (GlowClass,BadgeClass,BackgroundClass,ToSeverityBucket). All decision logic is xUnit-testable without rendering or twin scaffolding.IncidentBarSeverityenum split intoCritical/Error/Warning/Info/None.IRankableMessage+AxoMessengerRankableAdapter— delegate-driven projection (Func<string>,Func<DateTime>, etc.) so tests fake fields without standing up anAxoMessenger. Delegates re-invoke on each access so the analyzer sees the latest batch-read value.CauseSeverityFloor = eAxoMessageCategory.Error. Warning/Potential/Info still count inActiveCount/PeakSeverity(global indicators stay accurate) but never enter the cause ranking. Configurable viaAxoCauseAnalyzerOptions.Topology_uses_container_prefix_not_messenger_symbol_prefixlocks the realistic twin-tree shape.AxoIncidentBarExample.st(nestedStation→Drive→Encoder+Conveyor→Sensor, each with its ownAxoMessengerand condition flag) plusPages/core/AxoIncidentBar.razor(Live Bar tab with operator controls, Topology code tab with snippet refs, Heuristic tab with ranking formula). NavMenu entry under Core; search registry entry.axopen.template.simple—MainLayout.razormounts<AxoIncidentBarView Provider="@_alarmProvider" />once per layout, cascades the provider soGeneralAlarms.razorconsumes the same provider instance instead of walking the twin tree a second time.tailwind.cssextended with@sourcepaths foraxopen/src/core/src/AXOpen.Core/**/*.csandAXOpen.Core.Blazor/**/*.razorso future bar class additions get JIT-compiled.src/core/docs/AxoIncidentBar.md(ranking formula, severity floor, Blazor mount pattern). Cross-linked fromsrc/core/docs/toc.ymlunder "Messengers (Alarms)". Appended0.56.0entry tosrc/core/docs/CHANGELOG.md(minor bump from0.55.1, GitVersionnext-versionupdated).src/core/tests/AXOpen.Core.Tests/Messaging/—AxoCauseAnalyzerTests(15: severity-floor, severity map, burst window, sliding burst, topology container prefix, ack de-prio, TopN clamp, anti-strobe hold, Changed event semantics, age decay),AxoMessengerRankableAdapterTests(2: projection + delegate re-invocation),AxoCauseAnalyzerFactoryTests(2: null guard + empty provider),AxoIncidentBarPresenterTests(26: visibility, severity bucket, pulse, additional count, rows, ack-pending, idle hysteresis, CSS class mapping, background-color-matches-glow invariant). Total: 69/69 green.Impact:
MainLayout.razorand creating one provider. No ST source changes; noAxoMessengerAPI change.AxoCauseAnalyzerdirectly (events +TopCause/ProbableCausesproperties) without the Blazor view.Risks/Review:
Error. Warning-only incidents do NOT raise the bar (intentional: operator noise reduction). Override viaAxoCauseAnalyzerOptions.CauseSeverityFloorif a deployment needs Warning-level surfacing.bg-{token}/15andshadow-glow-{token}Tailwind classes must be present in the host app's compiledmomentum.css. The template app'stailwind.css@sourceglob now covers the relevant axopen paths — rebuild required (tailwind.ps1) on first integration.System.Threading.Timer;IAsyncDisposablecleans it up. If hosted in a Blazor Server circuit with frequent reconnects, monitor for orphaned timers under stress.AxoIncidentBarViewconsumesAuthenticationStateProvidercascaded parameter for the admin-gated Restore button. If the host app routes the bar outside anAuthorizeRouteViewboundary, the cascading parameter isnulland Restore degrades to no-op (no exception).Testing:
dotnet test src/core/tests/AXOpen.Core.Tests/— 69/69 green (17 pre-existing + 52 messaging).dotnet build src/core/src/AXOpen.Core.Blazor/— Razor compiles clean.dotnet build src/showcase/app/ix-blazor/showcase.blazor/afterapax ib— 0 errors. Showcase page navigates to/core/AxoIncidentBarand renders the live bar with the topology controls.dotnet build axopen.template.simple/axpansion/server/— 0 errors.MainLayoutcascades the provider;GeneralAlarms.razorconsumes it without creating a second instance.Commits
feat:
ed7eaa3dfAdd AxoCauseAnalyzer and AxoIncidentBarPresenter for incident analysis47b41fd03Implement AxoIncidentBar for incident analysis and visualizationdocs:
7035b4071docs: add AxoIncidentBar.md + central/per-lib CHANGELOG + showcase tagsOther:
bf64be7b8Refactor code structure for improved readability and maintainabilityDiff stat
Test plan
dotnet test src/core/tests/AXOpen.Core.Tests/— 69/69 green (15 analyzer, 26 presenter, 2 adapter, 2 factory, 17 pre-existing core).apax ibfromsrc/showcase/app/regenerates twins cleanly afterAxoIncidentBarExample.st+ShowcaseContext.stchange.dotnet build src/showcase/app/ix-blazor/showcase.blazor/— 0 errors. Showcase razor page compiles./core/AxoIncidentBar, toggle_station_error+_drive_errortogether; confirm Station ranks above Drive viaDownstreamCount, bar pulses for Critical until acknowledged, collapses withinIdleHysteresis(2 s) after all flags cleared.docfx buildfromdocfx/—[!code-pascal[]](TopologyDeclaration,StationActivate) and[!code-csharp[]]/[!code-html[]](ProviderCreate,BarMount) directives resolve;AxoIncidentBar.mdrenders under TOC "Messengers (Alarms)".src/core/docs/CHANGELOG.md) covers all branch commits and flags the topology fix.Generated by
/pr-description-update. Last regenerated: 2026-05-27.