feat: add JoeAppEngine OPC UA nodes, fix DCL auto-reconnect and quality push

- Add JoeAppEngine folder to OPC UA nodes.json (BTCS, AlarmCntsBySeverity, Scheduler/ScanTime)
- Fix DataConnectionActor: capture Self in PreStart for use from non-actor threads,
  preventing Self.Tell failure in Disconnected event handler
- Implement InstanceActor.HandleConnectionQualityChanged to mark attributes Bad on disconnect
- Fix LmxFakeProxy TagMapper to serialize arrays as JSON instead of "System.Int32[]"
- Allow DataType and DataSourceReference updates in TemplateService.UpdateAttributeAsync
- Update test_infra_opcua.md with JoeAppEngine documentation
This commit is contained in:
Joseph Doherty
2026-03-19 13:27:54 -04:00
parent ffdda51990
commit 7740a3bcf9
70 changed files with 2684 additions and 541 deletions

View File

@@ -18,19 +18,15 @@ Central cluster only. Sites have no user interface.
- A **load balancer** sits in front of the central cluster and routes to the active node.
- On central failover, the Blazor Server SignalR circuit is interrupted. The browser automatically attempts to reconnect via SignalR's built-in reconnection logic.
- Since sessions use **JWT tokens** (not server-side state), the user's authentication survives failover — the new active node validates the same JWT. No re-login required if the token is still valid.
- Active debug view streams and in-progress real-time subscriptions are lost on failover and must be re-opened by the user.
- Since sessions use **authentication cookies** carrying an embedded JWT (not server-side state), the user's authentication survives failover — the new active node validates the same cookie-embedded JWT. No re-login required if the token is still valid.
- Active debug view polling and in-progress deployment status subscriptions are lost on failover and must be re-opened by the user.
- Both central nodes share the same **ASP.NET Data Protection keys** (stored in the configuration database or shared configuration) so that tokens and anti-forgery tokens remain valid across failover.
## Real-Time Updates
All real-time features use **server push via SignalR** (built into Blazor Server):
- **Debug view**: Attribute value and alarm state changes streamed live from sites.
- **Health dashboard**: Site status, connection health, error rates, and buffer depths update automatically when new health reports arrive.
- **Deployment status**: Pending/in-progress/success/failed transitions push to the UI immediately.
No manual refresh or polling is required for any of these features.
- **Debug view**: Near-real-time display of attribute values and alarm states, updated via a **2-second polling timer**. This avoids the complexity of cross-cluster streaming while providing responsive feedback — 2s latency is imperceptible for debugging purposes.
- **Health dashboard**: Site status, connection health, error rates, and buffer depths update via a **10-second auto-refresh timer**. Since health reports arrive from sites every 30 seconds, a 10s poll interval catches updates within one reporting cycle without unnecessary overhead.
- **Deployment status**: Pending/in-progress/success/failed transitions **push to the UI immediately** via SignalR (built into Blazor Server). No polling required for deployment tracking.
## Responsibilities
@@ -104,8 +100,8 @@ No manual refresh or polling is required for any of these features.
### Debug View (Deployment Role)
- Select a deployed instance and open a live debug view.
- Real-time streaming of all attribute values (with quality and timestamp) and alarm states for that instance.
- Initial snapshot of current state followed by streaming updates via the site-wide Akka stream.
- Near-real-time polling (2s interval) of all attribute values (with quality and timestamp) and alarm states for that instance.
- Initial snapshot of current state followed by periodic polling for updates.
- Stream includes attribute values formatted as `[InstanceUniqueName].[AttributePath].[AttributeName]` and alarm states formatted as `[InstanceUniqueName].[AlarmName]`.
- Subscribe-on-demand — stream starts when opened, stops when closed.