Files
lmxopcua/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS/FocasDriverOptions.cs
Joseph Doherty a25593a9c6 chore: organize solution into module folders (Core/Server/Drivers/Client/Tooling)
Group all 69 projects into category subfolders under src/ and tests/ so the
Rider Solution Explorer mirrors the module structure. Folders: Core, Server,
Drivers (with a nested Driver CLIs subfolder), Client, Tooling.

- Move every project folder on disk with git mv (history preserved as renames).
- Recompute relative paths in 57 .csproj files: cross-category ProjectReferences,
  the lib/ HintPath+None refs in Driver.Historian.Wonderware, and the external
  mxaccessgw refs in Driver.Galaxy and its test project.
- Rebuild ZB.MOM.WW.OtOpcUa.slnx with nested solution folders.
- Re-prefix project paths in functional scripts (e2e, compliance, smoke SQL,
  integration, install).

Build green (0 errors); unit tests pass. Docs left for a separate pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 01:55:28 -04:00

116 lines
5.1 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
namespace ZB.MOM.WW.OtOpcUa.Driver.FOCAS;
/// <summary>
/// FOCAS driver configuration. One instance supports N CNC devices. Per plan decision #144
/// each device gets its own <c>(DriverInstanceId, HostAddress)</c> bulkhead key at the
/// Phase 6.1 resilience layer.
/// </summary>
public sealed class FocasDriverOptions
{
public IReadOnlyList<FocasDeviceOptions> Devices { get; init; } = [];
public IReadOnlyList<FocasTagDefinition> 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();
}
/// <summary>
/// Fixed-node tree exposed by FOCAS per <c>docs/v2/driver-specs.md §7</c> —
/// <c>Identity/</c>, <c>Axes/{name}/</c>, etc. populated from
/// <c>cnc_sysinfo</c> / <c>cnc_rdaxisname</c> / <c>cnc_rddynamic2</c>. Disabled by
/// default so existing configs that only use user-authored tags don't grow new
/// nodes on upgrade.
/// </summary>
public sealed class FocasFixedTreeOptions
{
/// <summary>Enable the fixed-node tree for every configured device.</summary>
public bool Enabled { get; init; } = false;
/// <summary>
/// Poll cadence for <c>cnc_rddynamic2</c>. 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.
/// </summary>
public TimeSpan PollInterval { get; init; } = TimeSpan.FromMilliseconds(250);
/// <summary>
/// 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.
/// </summary>
public TimeSpan ProgramPollInterval { get; init; } = TimeSpan.FromSeconds(1);
/// <summary>
/// Poll cadence for timers (power-on / operating / cutting / cycle).
/// These change at human timescales — default is 30s. Zero / negative
/// disables the timer poll entirely.
/// </summary>
public TimeSpan TimerPollInterval { get; init; } = TimeSpan.FromSeconds(30);
}
/// <summary>
/// Proactive session-recycle cadence. Fanuc CNCs have a finite FWLIB handle pool
/// (~510 concurrent connections) and certain series have documented handle-leak bugs
/// that manifest after long uptime. When <see cref="Enabled"/> is <c>true</c> the
/// driver closes + reopens each device's session on the <see cref="Interval"/> 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.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
public sealed class FocasHandleRecycleOptions
{
public bool Enabled { get; init; } = false;
public TimeSpan Interval { get; init; } = TimeSpan.FromHours(1);
}
/// <summary>
/// Controls the CNC active-alarm polling projection that surfaces FOCAS alarms via
/// <c>IAlarmSource</c>. Disabled by default — operators opt in by setting
/// <see cref="Enabled"/> in <c>appsettings.json</c>.
/// </summary>
public sealed class FocasAlarmProjectionOptions
{
public bool Enabled { get; init; } = false;
/// <summary>Poll cadence. One <c>cnc_rdalmmsg2</c> call per device per tick.</summary>
public TimeSpan PollInterval { get; init; } = TimeSpan.FromSeconds(2);
}
/// <summary>
/// One CNC the driver talks to. <paramref name="Series"/> enables per-series
/// address validation at <see cref="FocasDriver.InitializeAsync"/>; leave as
/// <see cref="FocasCncSeries.Unknown"/> to skip validation (legacy behaviour).
/// </summary>
public sealed record FocasDeviceOptions(
string HostAddress,
string? DeviceName = null,
FocasCncSeries Series = FocasCncSeries.Unknown);
/// <summary>
/// One FOCAS-backed OPC UA variable. <paramref name="Address"/> is the canonical FOCAS
/// address string that parses via <see cref="FocasAddress.TryParse"/> —
/// <c>X0.0</c> / <c>R100</c> / <c>PARAM:1815/0</c> / <c>MACRO:500</c>.
/// </summary>
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);
}