namespace ZB.MOM.WW.OtOpcUa.Core.Abstractions; /// /// Required capability for every driver instance. Owns lifecycle, metadata, health. /// Other capabilities (, , /// , , , /// , , /// ) are composable — a driver implements only what its /// backend actually supports. /// public interface IDriver { /// Stable logical ID of this driver instance, sourced from the central config DB. string DriverInstanceId { get; } /// Driver type name (e.g. "Galaxy", "Modbus", "AbCip"). Matches DriverInstance.DriverType. string DriverType { get; } /// Initialize the driver from its DriverConfig JSON; open connections; prepare for first use. /// The driver configuration as JSON. /// Cancellation token for the operation. /// A task that represents the asynchronous operation. Task InitializeAsync(string driverConfigJson, CancellationToken cancellationToken); /// /// Apply a config change in place without tearing down the driver process. /// Invoked by the v2 DriverInstanceActor when ApplyDelta reports that only this /// driver's config changed in the new deployment. /// /// /// Per docs/v2/driver-stability.md §"In-process only (Tier A/B)" — Reinitialize is the /// only Core-initiated recovery path for in-process drivers; if it fails, the driver instance /// is marked Faulted and its nodes go Bad quality, but the server process keeps running. /// /// The driver configuration as JSON. /// Cancellation token for the operation. /// A task that represents the asynchronous operation. Task ReinitializeAsync(string driverConfigJson, CancellationToken cancellationToken); /// Stop the driver, close connections, release resources. Called on shutdown or driver removal. /// /// Land the terminal health value AFTER the teardown, never only before it (Gitea #517). /// Teardown drains in-flight poll / probe loops, and those loops write health as they unwind — so a /// driver that wrote first and then tore down could have a late /// loop overwrite it with and leave THAT as the resting state, /// with every connection already closed. This is why the fleet writes last, and it is the reason /// #517's original suggestion — "write the transitional value before disposing" — is only safe as an /// ADDITIONAL write, bracketing the teardown, rather than as a move. /// Bracketing matters wherever a can land mid-teardown. It cannot on /// this path today: DriverInstanceActor calls this from PostStop, on the actor thread, /// after the mailbox has drained, so its health poll is serialised against it. It CAN on the /// initialize path, which the same actor runs on a fire-and-forget Task.Run while the health /// tick keeps firing — so a driver whose disposes anything on its /// failure path must write the failed state before disposing as well as after. /// /// Cancellation token for the operation. /// A task that represents the asynchronous operation. Task ShutdownAsync(CancellationToken cancellationToken); /// Current health snapshot, polled by Core for the status dashboard and ServiceLevel. /// Must reflect the driver's ACTUAL state rather than a constant — a hard-coded /// reports a healthy driver after shutdown and before initialize /// alike, which is what Gitea #517 found in CalculationDriver. A driver with no transport still /// has a running / not-running distinction worth reporting. /// The driver's current health snapshot. DriverHealth GetHealth(); }