PR 1+2.W — Wire HistoryRouter + AlarmConditionService into DI
Server-side singletons threaded through OpcUaApplicationHost → OtOpcUaServer → DriverNodeManager construction. New ctor parameters are last-position optional with null defaults so every existing test construction site (OpcUaServerIntegrationTests, AlarmSubscribeIntegrationTests, etc.) keeps working unchanged. Program.cs: AddSingleton<IHistoryRouter, HistoryRouter>(); AddSingleton<AlarmConditionService>(); The router stays empty after this PR. DriverNodeManager's internal LegacyDriverHistoryAdapter handles every driver that still implements IHistoryProvider; PR 3.W will register the Wonderware sidecar as a router source; PR 7.2 retires the legacy fallback entirely. 44 alarm + history + integration tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,8 @@ using ZB.MOM.WW.OtOpcUa.Configuration.LocalCache;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.Hosting;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.OpcUa;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.Resilience;
|
||||
using ZB.MOM.WW.OtOpcUa.Server.Alarms;
|
||||
using ZB.MOM.WW.OtOpcUa.Server.History;
|
||||
using ZB.MOM.WW.OtOpcUa.Server.Observability;
|
||||
using ZB.MOM.WW.OtOpcUa.Server.Security;
|
||||
|
||||
@@ -40,6 +42,12 @@ public sealed class OpcUaApplicationHost : IAsyncDisposable
|
||||
private ZB.MOM.WW.OtOpcUa.Core.Abstractions.IReadable? _virtualReadable;
|
||||
private ZB.MOM.WW.OtOpcUa.Core.Abstractions.IReadable? _scriptedAlarmReadable;
|
||||
|
||||
// PR 1+2.W — server-level singletons. Threaded through to OtOpcUaServer + every
|
||||
// DriverNodeManager. Default null preserves existing test construction sites that
|
||||
// don't opt into the new server-side history routing or alarm-condition state machine.
|
||||
private readonly IHistoryRouter? _historyRouter;
|
||||
private readonly AlarmConditionService? _alarmConditionService;
|
||||
|
||||
private readonly ILoggerFactory _loggerFactory;
|
||||
private readonly ILogger<OpcUaApplicationHost> _logger;
|
||||
private ApplicationInstance? _application;
|
||||
@@ -57,7 +65,9 @@ public sealed class OpcUaApplicationHost : IAsyncDisposable
|
||||
Func<string, string?>? resilienceConfigLookup = null,
|
||||
Func<string, ZB.MOM.WW.OtOpcUa.Core.OpcUa.EquipmentNamespaceContent?>? equipmentContentLookup = null,
|
||||
ZB.MOM.WW.OtOpcUa.Core.Abstractions.IReadable? virtualReadable = null,
|
||||
ZB.MOM.WW.OtOpcUa.Core.Abstractions.IReadable? scriptedAlarmReadable = null)
|
||||
ZB.MOM.WW.OtOpcUa.Core.Abstractions.IReadable? scriptedAlarmReadable = null,
|
||||
IHistoryRouter? historyRouter = null,
|
||||
AlarmConditionService? alarmConditionService = null)
|
||||
{
|
||||
_options = options;
|
||||
_driverHost = driverHost;
|
||||
@@ -71,6 +81,8 @@ public sealed class OpcUaApplicationHost : IAsyncDisposable
|
||||
_equipmentContentLookup = equipmentContentLookup;
|
||||
_virtualReadable = virtualReadable;
|
||||
_scriptedAlarmReadable = scriptedAlarmReadable;
|
||||
_historyRouter = historyRouter;
|
||||
_alarmConditionService = alarmConditionService;
|
||||
_loggerFactory = loggerFactory;
|
||||
_logger = logger;
|
||||
}
|
||||
@@ -136,7 +148,8 @@ public sealed class OpcUaApplicationHost : IAsyncDisposable
|
||||
authzGate: _authzGate, scopeResolver: _scopeResolver,
|
||||
tierLookup: _tierLookup, resilienceConfigLookup: _resilienceConfigLookup,
|
||||
virtualReadable: _virtualReadable, scriptedAlarmReadable: _scriptedAlarmReadable,
|
||||
anonymousRoles: _options.AnonymousRoles);
|
||||
anonymousRoles: _options.AnonymousRoles,
|
||||
historyRouter: _historyRouter, alarmConditionService: _alarmConditionService);
|
||||
await _application.Start(_server).ConfigureAwait(false);
|
||||
|
||||
_logger.LogInformation("OPC UA server started — endpoint={Endpoint} driverCount={Count}",
|
||||
|
||||
@@ -6,6 +6,8 @@ using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.Hosting;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.OpcUa;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.Resilience;
|
||||
using ZB.MOM.WW.OtOpcUa.Server.Alarms;
|
||||
using ZB.MOM.WW.OtOpcUa.Server.History;
|
||||
using ZB.MOM.WW.OtOpcUa.Server.Security;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.Server.OpcUa;
|
||||
@@ -34,6 +36,13 @@ public sealed class OtOpcUaServer : StandardServer
|
||||
private readonly IReadable? _virtualReadable;
|
||||
private readonly IReadable? _scriptedAlarmReadable;
|
||||
|
||||
// PR 1+2.W — server-level singletons shared across every DriverNodeManager.
|
||||
// Null when the deployment hasn't opted into the new server-side history routing /
|
||||
// server-side alarm-condition state machine; DriverNodeManager falls back to the
|
||||
// legacy per-driver IHistoryProvider + IAlarmSource paths in that case.
|
||||
private readonly IHistoryRouter? _historyRouter;
|
||||
private readonly AlarmConditionService? _alarmConditionService;
|
||||
|
||||
/// <summary>
|
||||
/// Roles granted to anonymous sessions. When non-empty, <see cref="OnImpersonateUser"/>
|
||||
/// wraps <c>AnonymousIdentityToken</c> in a <see cref="RoleBasedIdentity"/> carrying
|
||||
@@ -57,7 +66,9 @@ public sealed class OtOpcUaServer : StandardServer
|
||||
Func<string, string?>? resilienceConfigLookup = null,
|
||||
IReadable? virtualReadable = null,
|
||||
IReadable? scriptedAlarmReadable = null,
|
||||
IReadOnlyList<string>? anonymousRoles = null)
|
||||
IReadOnlyList<string>? anonymousRoles = null,
|
||||
IHistoryRouter? historyRouter = null,
|
||||
AlarmConditionService? alarmConditionService = null)
|
||||
{
|
||||
_driverHost = driverHost;
|
||||
_authenticator = authenticator;
|
||||
@@ -69,6 +80,8 @@ public sealed class OtOpcUaServer : StandardServer
|
||||
_virtualReadable = virtualReadable;
|
||||
_scriptedAlarmReadable = scriptedAlarmReadable;
|
||||
_anonymousRoles = anonymousRoles ?? [];
|
||||
_historyRouter = historyRouter;
|
||||
_alarmConditionService = alarmConditionService;
|
||||
_loggerFactory = loggerFactory;
|
||||
}
|
||||
|
||||
@@ -102,7 +115,14 @@ public sealed class OtOpcUaServer : StandardServer
|
||||
var invoker = new CapabilityInvoker(_pipelineBuilder, driver.DriverInstanceId, () => options, driver.DriverType);
|
||||
var manager = new DriverNodeManager(server, configuration, driver, invoker, logger,
|
||||
authzGate: _authzGate, scopeResolver: _scopeResolver,
|
||||
virtualReadable: _virtualReadable, scriptedAlarmReadable: _scriptedAlarmReadable);
|
||||
virtualReadable: _virtualReadable, scriptedAlarmReadable: _scriptedAlarmReadable,
|
||||
historyRouter: _historyRouter, alarmService: _alarmConditionService);
|
||||
|
||||
// The router stays empty after PR 1+2.W — DriverNodeManager's internal
|
||||
// LegacyDriverHistoryAdapter handles every driver that still implements
|
||||
// IHistoryProvider. PR 3.W will register the Wonderware sidecar as a router
|
||||
// source; PR 7.2 retires the legacy fallback entirely.
|
||||
|
||||
_driverNodeManagers.Add(manager);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ using ZB.MOM.WW.OtOpcUa.Driver.Modbus;
|
||||
using ZB.MOM.WW.OtOpcUa.Driver.S7;
|
||||
using ZB.MOM.WW.OtOpcUa.Driver.TwinCAT;
|
||||
using ZB.MOM.WW.OtOpcUa.Server;
|
||||
using ZB.MOM.WW.OtOpcUa.Server.Alarms;
|
||||
using ZB.MOM.WW.OtOpcUa.Server.History;
|
||||
using ZB.MOM.WW.OtOpcUa.Server.Hosting;
|
||||
using ZB.MOM.WW.OtOpcUa.Server.OpcUa;
|
||||
using ZB.MOM.WW.OtOpcUa.Server.Phase7;
|
||||
@@ -137,6 +139,14 @@ builder.Services.AddScoped<EquipmentNamespaceContentLoader>();
|
||||
// to ACL enforcement accidentally on upgrade.
|
||||
builder.Services.AddSingleton<AuthorizationBootstrap>();
|
||||
|
||||
// PR 1+2.W — server-level history routing + alarm-condition state machine. Singletons
|
||||
// shared across every DriverNodeManager. The router stays empty after this PR;
|
||||
// PR 3.W registers the Wonderware historian sidecar as a router source. The alarm
|
||||
// service runs the Active/Acknowledged/Inactive state machine for any driver that
|
||||
// declares alarms via AlarmConditionInfo's sub-attribute refs.
|
||||
builder.Services.AddSingleton<IHistoryRouter, HistoryRouter>();
|
||||
builder.Services.AddSingleton<AlarmConditionService>();
|
||||
|
||||
builder.Services.AddSingleton<OpcUaApplicationHost>(sp =>
|
||||
{
|
||||
var registry = sp.GetRequiredService<DriverEquipmentContentRegistry>();
|
||||
@@ -146,7 +156,9 @@ builder.Services.AddSingleton<OpcUaApplicationHost>(sp =>
|
||||
sp.GetRequiredService<IUserAuthenticator>(),
|
||||
sp.GetRequiredService<ILoggerFactory>(),
|
||||
sp.GetRequiredService<ILogger<OpcUaApplicationHost>>(),
|
||||
equipmentContentLookup: registry.Get);
|
||||
equipmentContentLookup: registry.Get,
|
||||
historyRouter: sp.GetRequiredService<IHistoryRouter>(),
|
||||
alarmConditionService: sp.GetRequiredService<AlarmConditionService>());
|
||||
});
|
||||
builder.Services.AddHostedService<OpcUaServerService>();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user