using System.Text.Json;
using Microsoft.Extensions.Logging;
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
using ZB.MOM.WW.OtOpcUa.Core.Hosting;
namespace ZB.MOM.WW.OtOpcUa.Driver.Mqtt;
///
/// Registers the MQTT / Sparkplug B driver with the . The
/// Host's DriverFactoryBootstrap calls once at startup; the
/// driver-instance bootstrapper then materialises DriverInstance rows of type
/// into live instances.
///
///
///
/// Direct deserialization, no intermediate DTO. The DriverConfig blob binds
/// straight onto — the same shape
/// , MqttDriverBrowser and
/// already bind, and the shape the options record
/// was designed for (every knob carries its own default, and rawTags is a plain
/// list needing no translation). Mirrors
/// OpcUaClientDriverFactoryExtensions. ModbusDriverFactoryExtensions' separate
/// DTO exists to service a legacy nullable-everything blob plus string→enum parsing this
/// driver does with a converter instead; copying it here would add a fourth parse
/// shape for one config, which is precisely the divergence
/// exists to prevent.
///
///
/// Connection-free. parses and constructs only — the
/// constructor touches no network, and the registry contract
/// forbids the factory from calling InitializeAsync itself (the driver host owns the
/// retry semantics).
///
///
public static class MqttDriverFactoryExtensions
{
///
/// Driver type name — matches DriverInstance.DriverType values. Sourced from
/// so the registration key can never drift from the
/// constant the dispatch maps, the probe and the browser reference.
///
public const string DriverTypeName = DriverTypeNames.Mqtt;
///
/// Register the MQTT factory with the driver registry. The optional
/// is captured at registration time and used to construct an
/// per driver instance — without it the driver runs with no
/// logger (standalone/test callers stay unchanged).
///
/// The driver factory registry to register with.
/// Optional logger factory used to create per-instance loggers.
///
/// Gitea #511: the Primary data-plane verdict the Sparkplug primary-host claim is gated on,
/// captured at registration time like . Null falls back to
/// (always claims) — correct for a standalone
/// host or a test, and the reason DriverFactoryBootstrap resolves it with
/// GetRequiredService rather than letting a redundant deployment reach that fallback.
///
public static void Register(
DriverFactoryRegistry registry,
ILoggerFactory? loggerFactory = null,
IDriverPrimaryRoleSource? primaryRoleSource = null)
{
ArgumentNullException.ThrowIfNull(registry);
registry.Register(DriverTypeName, (id, json) => CreateInstance(id, json, loggerFactory, primaryRoleSource));
}
/// Public for the Server-side bootstrapper + test consumers.
/// Stable logical id of the driver instance.
/// The DriverConfig JSON blob for the instance.
/// Optional logger factory for the per-instance logger.
///
/// Gitea #511: the Primary data-plane verdict gating the Sparkplug primary-host claim; null
/// falls back to .
///
/// A configured, not-yet-connected .
///
/// or is blank.
///
/// The config blob deserialised to null.
/// The config blob is not valid JSON for the options shape.
public static MqttDriver CreateInstance(
string driverInstanceId,
string driverConfigJson,
ILoggerFactory? loggerFactory = null,
IDriverPrimaryRoleSource? primaryRoleSource = null)
{
ArgumentException.ThrowIfNullOrWhiteSpace(driverInstanceId);
ArgumentException.ThrowIfNullOrWhiteSpace(driverConfigJson);
// MqttJson.Options — the one shared instance (see its remarks). A local copy here is the
// repo's documented systemic enum bug in the making.
var options = JsonSerializer.Deserialize(driverConfigJson, MqttJson.Options)
?? throw new InvalidOperationException(
$"MQTT driver config for '{driverInstanceId}' deserialised to null");
return new MqttDriver(
options, driverInstanceId, loggerFactory?.CreateLogger(), primaryRoleSource);
}
}