feat(historian-gateway): wire ContinuousHistorizationRecorder into DI + hosted lifecycle + meters
Bind ContinuousHistorizationOptions (Enabled/OutboxPath/CommitMode/ CommitIntervalMs/DrainBatchSize/DrainIntervalSeconds/Capacity/backoff) with a warn-only Validate(); gated on Enabled AND the ServerHistorian gateway being configured, the Host registers the durable FasterLogHistorizationOutbox (container -disposed) + a gateway-backed GatewayHistorianValueWriter, and binds outbox depth/dropped observable gauges on the central scraped meter. WithOtOpcUaRuntimeActors spawns the recorder (over the same dependency-mux ref) when the options + writer + outbox resolve, registering ContinuousHistorizationRecorderKey. Spawned with an EMPTY historized-ref set: the deployed address space builds later, so ref population is a documented follow-on (a later SetHistorizedRefs feed) — T18 wires the actor + outbox + writer + meters; the ref feed is the known remaining gap. Claude-Session: https://claude.ai/code/session_012SDSQ3AcaXqPcBtDESBRii
This commit is contained in:
@@ -11,6 +11,7 @@ using ZB.MOM.WW.OtOpcUa.Commons.Interfaces;
|
||||
using ZB.MOM.WW.OtOpcUa.Commons.OpcUa;
|
||||
using ZB.MOM.WW.OtOpcUa.Configuration;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.Abstractions.Historian;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.AlarmHistorian;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.Scripting;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.VirtualTags;
|
||||
@@ -33,6 +34,7 @@ public static class ServiceCollectionExtensions
|
||||
public const string DependencyMuxActorName = "dependency-mux";
|
||||
public const string OpcUaPublishActorName = "opcua-publish";
|
||||
public const string PeerProbeSupervisorName = "peer-probe-supervisor";
|
||||
public const string ContinuousHistorizationRecorderActorName = "continuous-historization-recorder";
|
||||
|
||||
/// <summary>
|
||||
/// Registers shared runtime services. Currently binds <see cref="IAlarmHistorianSink"/>
|
||||
@@ -245,6 +247,46 @@ public static class ServiceCollectionExtensions
|
||||
HistorianAdapterActor.Props(historianSink, roleInfo.LocalNode),
|
||||
HistorianAdapterActorName);
|
||||
registry.Register<HistorianAdapterActorKey>(historian);
|
||||
|
||||
// Continuous-historization recorder — gated on ContinuousHistorization:Enabled AND the
|
||||
// gateway-backed IHistorianValueWriter + the durable IHistorizationOutbox being registered
|
||||
// (the Host registers both ONLY when historization is enabled and the ServerHistorian gateway
|
||||
// is configured). The recorder taps the dependency mux's value fan-out, so it is spawned after
|
||||
// (and fed) the same `mux` ref the DriverHostActor uses.
|
||||
//
|
||||
// HISTORIZED-REF SET — DOCUMENTED GAP (T18 minimal wiring). The deployed address space (and
|
||||
// thus the set of historized tag refs) is built later at deploy time, not here at actor-spawn
|
||||
// time, so there is no clean ref set to resolve in WithOtOpcUaRuntimeActors. Per the plan, T18
|
||||
// spawns the recorder with an EMPTY initial ref set and registers its key; populating the refs
|
||||
// (a later SetHistorizedRefs feed driven off the deployed composition) is the remaining wiring
|
||||
// and a tracked follow-on. With an empty set the recorder registers interest in nothing and
|
||||
// historizes nothing until that feed lands — the actor + outbox + writer + meters are wired.
|
||||
var continuousOptions = resolver.GetService<ContinuousHistorizationOptions>();
|
||||
if (continuousOptions is { Enabled: true })
|
||||
{
|
||||
var valueWriter = resolver.GetService<IHistorianValueWriter>();
|
||||
var outbox = resolver.GetService<IHistorizationOutbox>();
|
||||
if (valueWriter is not null && outbox is not null)
|
||||
{
|
||||
var recorder = system.ActorOf(
|
||||
ContinuousHistorizationRecorder.Props(
|
||||
dependencyMux: mux,
|
||||
writer: valueWriter,
|
||||
outbox: outbox,
|
||||
historizedRefs: Array.Empty<string>(),
|
||||
drainBatchSize: continuousOptions.DrainBatchSize,
|
||||
drainInterval: TimeSpan.FromSeconds(continuousOptions.DrainIntervalSeconds),
|
||||
minBackoff: TimeSpan.FromSeconds(continuousOptions.MinBackoffSeconds),
|
||||
maxBackoff: TimeSpan.FromSeconds(continuousOptions.MaxBackoffSeconds)),
|
||||
ContinuousHistorizationRecorderActorName);
|
||||
registry.Register<ContinuousHistorizationRecorderKey>(recorder);
|
||||
}
|
||||
else
|
||||
{
|
||||
loggerFactory.CreateLogger("ZB.MOM.WW.OtOpcUa.Runtime.ServiceCollectionExtensions")
|
||||
.LogWarning("ContinuousHistorization is enabled but IHistorianValueWriter and/or IHistorizationOutbox are not registered; the recorder will not be spawned. Expected only in misconfigured deployments or test harnesses.");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return builder;
|
||||
@@ -258,5 +300,9 @@ public sealed class HistorianAdapterActorKey { }
|
||||
public sealed class DependencyMuxActorKey { }
|
||||
public sealed class OpcUaPublishActorKey { }
|
||||
|
||||
/// <summary>Marker key for the per-node ContinuousHistorizationRecorder (spawned only when
|
||||
/// <c>ContinuousHistorization:Enabled=true</c> and the gateway value-writer + outbox are registered).</summary>
|
||||
public sealed class ContinuousHistorizationRecorderKey { }
|
||||
|
||||
/// <summary>Marker key for the per-node PeerProbeSupervisor.</summary>
|
||||
public sealed class PeerProbeSupervisorKey { }
|
||||
|
||||
Reference in New Issue
Block a user