Files
lmxopcua/docs/drivers/FOCAS.md
2026-04-26 04:32:43 -04:00

4.9 KiB

FOCAS driver

Fanuc CNC driver for the FS 0i / 16i / 18i / 21i / 30i / 31i / 32i / 35i / Power Mate i families. Talks to the controller via the licensed Fwlib32.dll (Tier C, process-isolated per docs/v2/driver-stability.md).

For range-validation and per-series capability surface see docs/v2/focas-version-matrix.md.

Alarm history (cnc_rdalmhistry) — issue #267, plan PR F3-a

FocasAlarmProjection exposes two modes via FocasDriverOptions.AlarmProjection:

Mode Behaviour
ActiveOnly (default) Subscribe / unsubscribe / acknowledge wire up so capability negotiation works, but no history poll runs. Back-compat with every pre-F3-a deployment.
ActivePlusHistory On subscribe (== "on connect") and on every HistoryPollInterval tick, the projection issues cnc_rdalmhistry for the most recent HistoryDepth entries. Each previously-unseen entry fires an OnAlarmEvent with SourceTimestampUtc set from the CNC's reported timestamp — OPC UA dashboards see the real occurrence time, not the moment the projection polled.

Config knobs

{
  "AlarmProjection": {
    "Mode": "ActivePlusHistory",        // "ActiveOnly" (default) | "ActivePlusHistory"
    "HistoryPollInterval": "00:05:00",  // default 5 min
    "HistoryDepth": 100                 // default 100, capped at 250
  }
}

Dedup key

(OccurrenceTime, AlarmNumber, AlarmType). The same triple across two polls only emits once. The dedup set is in-memory and resets on reconnect — first poll after reconnect re-emits everything in the ring buffer. OPC UA clients that need exactly-once semantics dedupe client-side on the same triple (the timestamp + type + number tuple is stable across the boundary).

HistoryDepth cap

Capped at FocasAlarmProjectionOptions.MaxHistoryDepth = 250 so an operator who types 10000 by accident can't blast the wire session with a giant request. Typical FANUC ring buffers cap at ~100 entries; the default HistoryDepth = 100 matches the most common ring-buffer size.

Wire surface

  • Wire-protocol command id: 0x0F1A (see docs/v2/implementation/focas-wire-protocol.md).
  • ODBALMHIS struct decoder: Wire/FocasAlarmHistoryDecoder.cs.
  • Tier-C Fwlib32 backend short-circuits the packed-buffer decoder by surfacing the FWLIB struct fields directly into FocasAlarmHistoryEntry.

Writes (opt-in, off by default) — issue #268, plan PR F4-a

Writes ship behind two independent opt-ins. Both default off so a freshly deployed FOCAS driver is read-only until the deployment makes a deliberate choice. Decision record: docs/v2/decisions.md → "FOCAS write-path opt-in".

Knob Default Effect when off
FocasDriverOptions.Writes.Enabled (driver-level master switch) false Every entry in a WriteAsync batch short-circuits to BadNotWritable with status text writes disabled at driver level. Wire client never gets touched.
FocasTagDefinition.Writable (per-tag opt-in) false The per-tag check returns BadNotWritable for that tag even when the driver-level flag is on.

Config shape

{
  "Writes": { "Enabled": true },
  "Tags": [
    { "Name": "RPM", "Address": "PARAM:1815", "DataType": "Int32",
      "Writable": true, "WriteIdempotent": false }
  ]
}

WriteIdempotent is plumbed through Polly retry by the server-layer CapabilityInvoker.ExecuteWriteAsync. When false (default), failed writes are NOT auto-retried per plan decisions #44/#45 — a timeout that fires after the CNC already accepted the write would otherwise risk a duplicate non-idempotent action (alarm acks, M-code pulses, recipe steps). Flip WriteIdempotent on per tag for genuinely-idempotent writes (a parameter value that the operator simply wants forced to a target).

Status-code semantics post-F4-a

  • BadNotWritable — driver-level Writes.Enabled = false, OR per-tag Writable = false. Two distinct paths, same status code.
  • BadNotSupported — both opt-ins flipped on, but the wire client doesn't yet implement the kind being written. F4-a wires the dispatch surface; F4-b/c land the actual macro / parameter / PMC writes for unimplemented kinds, replacing those BadNotSupported responses with real wire calls.
  • BadNodeIdUnknown — full-reference doesn't match any configured FocasTagDefinition.Name.
  • BadCommunicationError — wire failure (DLL not loaded, IPC peer dead, etc.).

CLI bypass

otopcua-focas-cli write (docs/Driver.FOCAS.Cli.md) sets Writes.Enabled=true locally for the lifetime of one invocation because the CLI is a per-operator tool — not a long-lived process bound to the central config DB. The server-side flag is untouched; configure-the- server code paths remain safer-by-default.