|
|
|
@@ -0,0 +1,152 @@
|
|
|
|
|
using System.ComponentModel.DataAnnotations;
|
|
|
|
|
|
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Driver.Mqtt;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// MQTT / Sparkplug B driver configuration. Bound from <c>DriverConfig</c> JSON at
|
|
|
|
|
/// driver-host registration time. Models the settings documented in
|
|
|
|
|
/// <c>docs/plans/2026-07-15-mqtt-sparkplug-driver-design.md</c> §5.1.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <remarks>
|
|
|
|
|
/// A record (not a plain class) so a future secret-resolution seam can produce a
|
|
|
|
|
/// credential-resolved copy with a <c>with</c> expression — mirrors
|
|
|
|
|
/// <c>OpcUaClientDriverOptions</c>. <see cref="Mode"/> selects the ingest shape;
|
|
|
|
|
/// <see cref="Sparkplug"/> and <see cref="Plain"/> are nullable sub-objects — only the one
|
|
|
|
|
/// matching <see cref="Mode"/> is populated.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
public sealed record MqttDriverOptions
|
|
|
|
|
{
|
|
|
|
|
/// <summary>Broker hostname or IP address.</summary>
|
|
|
|
|
public string Host { get; init; } = "localhost";
|
|
|
|
|
|
|
|
|
|
/// <summary>Broker TCP port.</summary>
|
|
|
|
|
[Range(1, 65535)]
|
|
|
|
|
public int Port { get; init; } = 8883;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// MQTT client identifier sent at CONNECT. Leave unset to let the driver generate one
|
|
|
|
|
/// (a stable per-instance id is recommended so broker-side ACLs / session state persist
|
|
|
|
|
/// across reconnects).
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string? ClientId { get; init; }
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// When <c>true</c>, connect over TLS. Default <c>true</c> — this driver never ships an
|
|
|
|
|
/// anonymous/plaintext-by-default posture; a deployment must opt into <c>false</c> for an
|
|
|
|
|
/// on-prem/dev broker with no TLS listener.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool UseTls { get; init; } = true;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// When <c>true</c>, accept any self-signed / untrusted broker certificate. Dev/on-prem
|
|
|
|
|
/// escape hatch only — mirrors the <c>ServerHistorian</c> TLS knobs. Must stay
|
|
|
|
|
/// <c>false</c> in production so MITM attacks against the broker connection fail closed.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool AllowUntrustedServerCertificate { get; init; } = false;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// PEM CA file pinning the broker's TLS chain. <c>null</c>/empty uses the OS trust
|
|
|
|
|
/// store.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string? CaCertificatePath { get; init; }
|
|
|
|
|
|
|
|
|
|
/// <summary>Username for broker authentication. <c>null</c> connects without a username.</summary>
|
|
|
|
|
public string? Username { get; init; }
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Password for broker authentication. Blank in committed JSON — supply via env, never
|
|
|
|
|
/// commit or log.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string? Password { get; init; } = "";
|
|
|
|
|
|
|
|
|
|
/// <summary>MQTT protocol version to negotiate at CONNECT.</summary>
|
|
|
|
|
public MqttProtocolVersion ProtocolVersion { get; init; } = MqttProtocolVersion.V500;
|
|
|
|
|
|
|
|
|
|
/// <summary>Whether to request a clean session (v3.1.1) / clean start (v5.0) at CONNECT.</summary>
|
|
|
|
|
public bool CleanSession { get; init; } = true;
|
|
|
|
|
|
|
|
|
|
/// <summary>Keep-alive interval sent at CONNECT.</summary>
|
|
|
|
|
[Range(1, int.MaxValue)]
|
|
|
|
|
public int KeepAliveSeconds { get; init; } = 30;
|
|
|
|
|
|
|
|
|
|
/// <summary>Bounded connect deadline (see design §8) — the driver never hangs past this.</summary>
|
|
|
|
|
[Range(1, int.MaxValue)]
|
|
|
|
|
public int ConnectTimeoutSeconds { get; init; } = 15;
|
|
|
|
|
|
|
|
|
|
/// <summary>Initial reconnect backoff after a connection drop.</summary>
|
|
|
|
|
[Range(1, int.MaxValue)]
|
|
|
|
|
public int ReconnectMinBackoffSeconds { get; init; } = 1;
|
|
|
|
|
|
|
|
|
|
/// <summary>Cap on the exponential reconnect backoff.</summary>
|
|
|
|
|
[Range(1, int.MaxValue)]
|
|
|
|
|
public int ReconnectMaxBackoffSeconds { get; init; } = 30;
|
|
|
|
|
|
|
|
|
|
/// <summary>Selects the ingest shape — Plain topics or Sparkplug B.</summary>
|
|
|
|
|
public MqttMode Mode { get; init; } = MqttMode.Plain;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Sparkplug-only settings. Populated when <see cref="Mode"/> is
|
|
|
|
|
/// <see cref="MqttMode.SparkplugB"/>; <c>null</c> in Plain mode.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public MqttSparkplugOptions? Sparkplug { get; init; }
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Plain-mode-only settings. Populated when <see cref="Mode"/> is
|
|
|
|
|
/// <see cref="MqttMode.Plain"/>; <c>null</c> in Sparkplug B mode.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public MqttPlainOptions? Plain { get; init; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Sparkplug B settings for an <see cref="MqttDriverOptions"/> instance in
|
|
|
|
|
/// <see cref="MqttMode.SparkplugB"/> mode. See
|
|
|
|
|
/// <c>docs/plans/2026-07-15-mqtt-sparkplug-driver-design.md</c> §5.1.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public sealed record MqttSparkplugOptions
|
|
|
|
|
{
|
|
|
|
|
/// <summary>Sparkplug group id — the driver subscribes <c>spBv1.0/{GroupId}/#</c>.</summary>
|
|
|
|
|
public string GroupId { get; init; } = "";
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Sparkplug Host Application identity — published as <c>spBv1.0/STATE/{HostId}</c>
|
|
|
|
|
/// (Sparkplug v3.0).
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string HostId { get; init; } = "";
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// When <c>true</c>, this driver instance acts as the Sparkplug Primary Host
|
|
|
|
|
/// Application. At most one primary host may exist per host-id per broker.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool ActAsPrimaryHost { get; init; } = false;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// When <c>true</c>, a detected sequence-number gap triggers a Sparkplug rebirth
|
|
|
|
|
/// request (NCMD) rather than silently continuing with stale metric state.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool RequestRebirthOnGap { get; init; } = true;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// How long, in seconds, browse/discovery collects NBIRTH/DBIRTH traffic before
|
|
|
|
|
/// considering the observed metric set stable.
|
|
|
|
|
/// </summary>
|
|
|
|
|
[Range(1, int.MaxValue)]
|
|
|
|
|
public int BirthObservationWindowSeconds { get; init; } = 15;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Plain-MQTT-mode settings for an <see cref="MqttDriverOptions"/> instance in
|
|
|
|
|
/// <see cref="MqttMode.Plain"/> mode. See
|
|
|
|
|
/// <c>docs/plans/2026-07-15-mqtt-sparkplug-driver-design.md</c> §5.1.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public sealed record MqttPlainOptions
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Optional prefix prepended when the driver needs to compose a topic (e.g. discovery
|
|
|
|
|
/// scoping); per-tag topics are authored explicitly and are unaffected.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string TopicPrefix { get; init; } = "";
|
|
|
|
|
|
|
|
|
|
/// <summary>Default QoS used when a tag's own <c>qos</c> is unset.</summary>
|
|
|
|
|
[Range(0, 2)]
|
|
|
|
|
public int DefaultQos { get; init; } = 1;
|
|
|
|
|
}
|