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); }