namespace ZB.MOM.WW.OtOpcUa.Driver.FOCAS;
///
/// FOCAS driver configuration. One instance supports N CNC devices. Per plan decision #144
/// each device gets its own (DriverInstanceId, HostAddress) bulkhead key at the
/// Phase 6.1 resilience layer.
///
public sealed class FocasDriverOptions
{
public IReadOnlyList Devices { get; init; } = [];
public IReadOnlyList Tags { get; init; } = [];
public FocasProbeOptions Probe { get; init; } = new();
public TimeSpan Timeout { get; init; } = TimeSpan.FromSeconds(2);
public FocasAlarmProjectionOptions AlarmProjection { get; init; } = new();
public FocasHandleRecycleOptions HandleRecycle { get; init; } = new();
public FocasFixedTreeOptions FixedTree { get; init; } = new();
}
///
/// Fixed-node tree exposed by FOCAS per docs/v2/driver-specs.md §7 —
/// Identity/, Axes/{name}/, etc. populated from
/// cnc_sysinfo / cnc_rdaxisname / cnc_rddynamic2. Disabled by
/// default so existing configs that only use user-authored tags don't grow new
/// nodes on upgrade.
///
public sealed class FocasFixedTreeOptions
{
/// Enable the fixed-node tree for every configured device.
public bool Enabled { get; init; } = false;
///
/// Poll cadence for cnc_rddynamic2. Each tick calls the API once per
/// configured axis + publishes OnDataChange for the axis subtree. Real CNCs
/// serve ~100ms loops comfortably; the default is conservative.
///
public TimeSpan PollInterval { get; init; } = TimeSpan.FromMilliseconds(250);
///
/// Poll cadence for program + operation-mode info. Slower than the axis
/// poll because program / mode transitions happen on operator timescales.
/// Zero / negative disables the program poll entirely.
///
public TimeSpan ProgramPollInterval { get; init; } = TimeSpan.FromSeconds(1);
///
/// Poll cadence for timers (power-on / operating / cutting / cycle).
/// These change at human timescales — default is 30s. Zero / negative
/// disables the timer poll entirely.
///
public TimeSpan TimerPollInterval { get; init; } = TimeSpan.FromSeconds(30);
}
///
/// Proactive session-recycle cadence. Fanuc CNCs have a finite FWLIB handle pool
/// (~5–10 concurrent connections) and certain series have documented handle-leak bugs
/// that manifest after long uptime. When is true the
/// driver closes + reopens each device's session on the cadence,
/// forcing FWLIB to release its handle slot back to the pool. Reads / writes during
/// recycle wait for the reconnect rather than failing — worst case an operator sees a
/// brief read latency spike once per cadence.
///
///
/// Disabled by default because a healthy CNC + driver doesn't need it. Enable when
/// field experience shows handle exhaustion against a specific series / firmware.
/// Typical tuning: 30 min for sites running multiple OtOpcUa instances against the
/// same CNC (they share the pool); 6 h for a single-client deployment.
///
public sealed class FocasHandleRecycleOptions
{
public bool Enabled { get; init; } = false;
public TimeSpan Interval { get; init; } = TimeSpan.FromHours(1);
}
///
/// Controls the CNC active-alarm polling projection that surfaces FOCAS alarms via
/// IAlarmSource. Disabled by default — operators opt in by setting
/// in appsettings.json.
///
public sealed class FocasAlarmProjectionOptions
{
public bool Enabled { get; init; } = false;
/// Poll cadence. One cnc_rdalmmsg2 call per device per tick.
public TimeSpan PollInterval { get; init; } = TimeSpan.FromSeconds(2);
}
///
/// One CNC the driver talks to. enables per-series
/// address validation at ; leave as
/// to skip validation (legacy behaviour).
///
public sealed record FocasDeviceOptions(
string HostAddress,
string? DeviceName = null,
FocasCncSeries Series = FocasCncSeries.Unknown);
///
/// One FOCAS-backed OPC UA variable. is the canonical FOCAS
/// address string that parses via —
/// X0.0 / R100 / PARAM:1815/0 / MACRO:500.
///
public sealed record FocasTagDefinition(
string Name,
string DeviceHostAddress,
string Address,
FocasDataType DataType,
bool Writable = true,
bool WriteIdempotent = false);
public sealed class FocasProbeOptions
{
public bool Enabled { get; init; } = true;
public TimeSpan Interval { get; init; } = TimeSpan.FromSeconds(5);
public TimeSpan Timeout { get; init; } = TimeSpan.FromSeconds(2);
}