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.Hosting;
|
||||||
using ZB.MOM.WW.OtOpcUa.Core.OpcUa;
|
using ZB.MOM.WW.OtOpcUa.Core.OpcUa;
|
||||||
using ZB.MOM.WW.OtOpcUa.Core.Resilience;
|
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.Observability;
|
||||||
using ZB.MOM.WW.OtOpcUa.Server.Security;
|
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? _virtualReadable;
|
||||||
private ZB.MOM.WW.OtOpcUa.Core.Abstractions.IReadable? _scriptedAlarmReadable;
|
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 ILoggerFactory _loggerFactory;
|
||||||
private readonly ILogger<OpcUaApplicationHost> _logger;
|
private readonly ILogger<OpcUaApplicationHost> _logger;
|
||||||
private ApplicationInstance? _application;
|
private ApplicationInstance? _application;
|
||||||
@@ -57,7 +65,9 @@ public sealed class OpcUaApplicationHost : IAsyncDisposable
|
|||||||
Func<string, string?>? resilienceConfigLookup = null,
|
Func<string, string?>? resilienceConfigLookup = null,
|
||||||
Func<string, ZB.MOM.WW.OtOpcUa.Core.OpcUa.EquipmentNamespaceContent?>? equipmentContentLookup = 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? 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;
|
_options = options;
|
||||||
_driverHost = driverHost;
|
_driverHost = driverHost;
|
||||||
@@ -71,6 +81,8 @@ public sealed class OpcUaApplicationHost : IAsyncDisposable
|
|||||||
_equipmentContentLookup = equipmentContentLookup;
|
_equipmentContentLookup = equipmentContentLookup;
|
||||||
_virtualReadable = virtualReadable;
|
_virtualReadable = virtualReadable;
|
||||||
_scriptedAlarmReadable = scriptedAlarmReadable;
|
_scriptedAlarmReadable = scriptedAlarmReadable;
|
||||||
|
_historyRouter = historyRouter;
|
||||||
|
_alarmConditionService = alarmConditionService;
|
||||||
_loggerFactory = loggerFactory;
|
_loggerFactory = loggerFactory;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
@@ -136,7 +148,8 @@ public sealed class OpcUaApplicationHost : IAsyncDisposable
|
|||||||
authzGate: _authzGate, scopeResolver: _scopeResolver,
|
authzGate: _authzGate, scopeResolver: _scopeResolver,
|
||||||
tierLookup: _tierLookup, resilienceConfigLookup: _resilienceConfigLookup,
|
tierLookup: _tierLookup, resilienceConfigLookup: _resilienceConfigLookup,
|
||||||
virtualReadable: _virtualReadable, scriptedAlarmReadable: _scriptedAlarmReadable,
|
virtualReadable: _virtualReadable, scriptedAlarmReadable: _scriptedAlarmReadable,
|
||||||
anonymousRoles: _options.AnonymousRoles);
|
anonymousRoles: _options.AnonymousRoles,
|
||||||
|
historyRouter: _historyRouter, alarmConditionService: _alarmConditionService);
|
||||||
await _application.Start(_server).ConfigureAwait(false);
|
await _application.Start(_server).ConfigureAwait(false);
|
||||||
|
|
||||||
_logger.LogInformation("OPC UA server started — endpoint={Endpoint} driverCount={Count}",
|
_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.Hosting;
|
||||||
using ZB.MOM.WW.OtOpcUa.Core.OpcUa;
|
using ZB.MOM.WW.OtOpcUa.Core.OpcUa;
|
||||||
using ZB.MOM.WW.OtOpcUa.Core.Resilience;
|
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;
|
using ZB.MOM.WW.OtOpcUa.Server.Security;
|
||||||
|
|
||||||
namespace ZB.MOM.WW.OtOpcUa.Server.OpcUa;
|
namespace ZB.MOM.WW.OtOpcUa.Server.OpcUa;
|
||||||
@@ -34,6 +36,13 @@ public sealed class OtOpcUaServer : StandardServer
|
|||||||
private readonly IReadable? _virtualReadable;
|
private readonly IReadable? _virtualReadable;
|
||||||
private readonly IReadable? _scriptedAlarmReadable;
|
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>
|
/// <summary>
|
||||||
/// Roles granted to anonymous sessions. When non-empty, <see cref="OnImpersonateUser"/>
|
/// Roles granted to anonymous sessions. When non-empty, <see cref="OnImpersonateUser"/>
|
||||||
/// wraps <c>AnonymousIdentityToken</c> in a <see cref="RoleBasedIdentity"/> carrying
|
/// wraps <c>AnonymousIdentityToken</c> in a <see cref="RoleBasedIdentity"/> carrying
|
||||||
@@ -57,7 +66,9 @@ public sealed class OtOpcUaServer : StandardServer
|
|||||||
Func<string, string?>? resilienceConfigLookup = null,
|
Func<string, string?>? resilienceConfigLookup = null,
|
||||||
IReadable? virtualReadable = null,
|
IReadable? virtualReadable = null,
|
||||||
IReadable? scriptedAlarmReadable = null,
|
IReadable? scriptedAlarmReadable = null,
|
||||||
IReadOnlyList<string>? anonymousRoles = null)
|
IReadOnlyList<string>? anonymousRoles = null,
|
||||||
|
IHistoryRouter? historyRouter = null,
|
||||||
|
AlarmConditionService? alarmConditionService = null)
|
||||||
{
|
{
|
||||||
_driverHost = driverHost;
|
_driverHost = driverHost;
|
||||||
_authenticator = authenticator;
|
_authenticator = authenticator;
|
||||||
@@ -69,6 +80,8 @@ public sealed class OtOpcUaServer : StandardServer
|
|||||||
_virtualReadable = virtualReadable;
|
_virtualReadable = virtualReadable;
|
||||||
_scriptedAlarmReadable = scriptedAlarmReadable;
|
_scriptedAlarmReadable = scriptedAlarmReadable;
|
||||||
_anonymousRoles = anonymousRoles ?? [];
|
_anonymousRoles = anonymousRoles ?? [];
|
||||||
|
_historyRouter = historyRouter;
|
||||||
|
_alarmConditionService = alarmConditionService;
|
||||||
_loggerFactory = loggerFactory;
|
_loggerFactory = loggerFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,7 +115,14 @@ public sealed class OtOpcUaServer : StandardServer
|
|||||||
var invoker = new CapabilityInvoker(_pipelineBuilder, driver.DriverInstanceId, () => options, driver.DriverType);
|
var invoker = new CapabilityInvoker(_pipelineBuilder, driver.DriverInstanceId, () => options, driver.DriverType);
|
||||||
var manager = new DriverNodeManager(server, configuration, driver, invoker, logger,
|
var manager = new DriverNodeManager(server, configuration, driver, invoker, logger,
|
||||||
authzGate: _authzGate, scopeResolver: _scopeResolver,
|
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);
|
_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.S7;
|
||||||
using ZB.MOM.WW.OtOpcUa.Driver.TwinCAT;
|
using ZB.MOM.WW.OtOpcUa.Driver.TwinCAT;
|
||||||
using ZB.MOM.WW.OtOpcUa.Server;
|
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.Hosting;
|
||||||
using ZB.MOM.WW.OtOpcUa.Server.OpcUa;
|
using ZB.MOM.WW.OtOpcUa.Server.OpcUa;
|
||||||
using ZB.MOM.WW.OtOpcUa.Server.Phase7;
|
using ZB.MOM.WW.OtOpcUa.Server.Phase7;
|
||||||
@@ -137,6 +139,14 @@ builder.Services.AddScoped<EquipmentNamespaceContentLoader>();
|
|||||||
// to ACL enforcement accidentally on upgrade.
|
// to ACL enforcement accidentally on upgrade.
|
||||||
builder.Services.AddSingleton<AuthorizationBootstrap>();
|
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 =>
|
builder.Services.AddSingleton<OpcUaApplicationHost>(sp =>
|
||||||
{
|
{
|
||||||
var registry = sp.GetRequiredService<DriverEquipmentContentRegistry>();
|
var registry = sp.GetRequiredService<DriverEquipmentContentRegistry>();
|
||||||
@@ -146,7 +156,9 @@ builder.Services.AddSingleton<OpcUaApplicationHost>(sp =>
|
|||||||
sp.GetRequiredService<IUserAuthenticator>(),
|
sp.GetRequiredService<IUserAuthenticator>(),
|
||||||
sp.GetRequiredService<ILoggerFactory>(),
|
sp.GetRequiredService<ILoggerFactory>(),
|
||||||
sp.GetRequiredService<ILogger<OpcUaApplicationHost>>(),
|
sp.GetRequiredService<ILogger<OpcUaApplicationHost>>(),
|
||||||
equipmentContentLookup: registry.Get);
|
equipmentContentLookup: registry.Get,
|
||||||
|
historyRouter: sp.GetRequiredService<IHistoryRouter>(),
|
||||||
|
alarmConditionService: sp.GetRequiredService<AlarmConditionService>());
|
||||||
});
|
});
|
||||||
builder.Services.AddHostedService<OpcUaServerService>();
|
builder.Services.AddHostedService<OpcUaServerService>();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user