# 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`](../v2/driver-stability.md)).

For range-validation and per-series capability surface see
[`docs/v2/focas-version-matrix.md`](../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

```jsonc
{
  "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`](../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`.
