Files
lmxopcua/docs/v2/focas-deployment.md
2026-04-26 04:54:28 -04:00

141 lines
6.3 KiB
Markdown

# FOCAS deployment guide
Per-driver runbook for deploying the FANUC FOCAS driver. See
[`docs/drivers/FOCAS.md`](../drivers/FOCAS.md) for the per-feature
reference and [`focas-version-matrix.md`](./focas-version-matrix.md) for
the per-CNC-series capability surface.
## Operator config-knob cheat sheet
| Knob | Where | Default | Notes |
| --- | --- | --- | --- |
| `Devices[].HostAddress` | `FocasDriverOptions.Devices` | — | `focas://{ip}[:{port}]` |
| `Devices[].Series` | `FocasDriverOptions.Devices` | `Unknown` | Drives per-series range validation in `FocasCapabilityMatrix`. |
| `Devices[].OverrideParameters` | `FocasDriverOptions.Devices` | `null` | MTB-specific parameter numbers for Feed/Rapid/Spindle/Jog overrides. `null` suppresses the `Override/` subtree. |
| `Probe.Enabled` | `FocasDriverOptions.Probe` | `true` | Background reachability probe. |
| `Probe.Interval` | `FocasDriverOptions.Probe` | `00:00:05` | Probe cadence. |
| `FixedTree.ApplyFigureScaling` | `FocasDriverOptions.FixedTree` | `true` | Divide position values by 10^decimal-places (issue #262). |
| **`AlarmProjection.Mode`** | **`FocasDriverOptions.AlarmProjection`** | **`ActiveOnly`** | **`ActiveOnly` keeps today's behaviour. `ActivePlusHistory` polls `cnc_rdalmhistry` on connect + on `HistoryPollInterval` ticks (issue #267, plan PR F3-a).** |
| **`AlarmProjection.HistoryPollInterval`** | **`FocasDriverOptions.AlarmProjection`** | **`00:05:00`** | **Cadence of the history poll. Operator dashboards run the default; high-frequency rigs can drop to 30 s.** |
| **`AlarmProjection.HistoryDepth`** | **`FocasDriverOptions.AlarmProjection`** | **`100`** | **Most-recent-N ring-buffer entries pulled per poll. Hard-capped at `250` so misconfigured values can't blast the wire session.** |
## Sample `appsettings.json` snippet for `ActivePlusHistory`
```jsonc
{
"Drivers": {
"FOCAS": {
"Devices": [
{ "HostAddress": "focas://10.0.0.5:8193", "Series": "Series30i" }
],
"AlarmProjection": {
"Mode": "ActivePlusHistory",
"HistoryPollInterval": "00:05:00",
"HistoryDepth": 100
}
}
}
}
```
The history projection emits each unseen entry through
`IAlarmSource.OnAlarmEvent` with `SourceTimestampUtc` set from the CNC's
reported wall-clock — keep CNC clocks on UTC so the dedup key
`(OccurrenceTime, AlarmNumber, AlarmType)` stays stable across DST
transitions.
## Write safety — issue #269, plan PR F4-b
The FOCAS driver supports `cnc_wrparam` and `cnc_wrmacro` writes behind
multiple independent opt-ins. A misdirected parameter write can put the
CNC in a bad state, so the runbook below MUST be followed before flipping
the granular kill switches on.
### Operator pre-checks (every deployment, every change)
1. **CNC must be in MDI mode.** Most parameter writes fail with `EW_PASSWD`
(surfaces as `BadUserAccessDenied`) unless the CNC is in MDI. The
server-side write returns immediately with the access-denied status; no
value reaches the wire.
2. **Parameter-write switch enabled on the CNC pendant.** Even in MDI mode
protected parameters require the operator to physically enable the
parameter-write switch. Without it `cnc_wrparam` returns `EW_PASSWD`.
Plan PR F4-d will land an OPC UA-side unlock workflow; today the only
path is the pendant.
3. **Verify each tag's address against the FANUC manual.** Ranges vary per
CNC series; the
[`focas-version-matrix`](./focas-version-matrix.md) capability matrix
rejects out-of-range numbers at startup, but address-vs-meaning is the
operator's job.
4. **Dry run with `Writable = true` but `Writes.AllowParameter = false`.**
Staged opt-in catches mis-mapped tags: every PARAM write returns
`BadNotWritable` until you flip the granular flag, so you can confirm
the tag list before any wire write fires.
### LDAP group requirements
Per [`docs/security.md`](../security.md) the server-layer ACL maps
`SecurityClassification` to LDAP groups. Post-F4-b:
| Tag kind | LDAP group required |
| --- | --- |
| `PARAM:N` (writable) | **`WriteConfigure`** — heaviest write tier; matches commissioning roles |
| `MACRO:N` (writable) | `WriteOperate` — standard HMI recipe / setpoint group |
| PMC R/G/F (writable) | `WriteOperate` |
| Read-only | `ReadOnly` |
Per the `feedback_acl_at_server_layer` design note, the FOCAS driver
declares the classification but does NOT enforce it; `DriverNodeManager`
applies the gate before the driver's `WriteAsync` ever runs. A user
without `WriteConfigure` who attempts a `PARAM:` write gets
`BadUserAccessDenied` from the server with no driver-level audit entry —
the OPC UA layer's audit log catches it.
### Audit-log expectations
Every successful write produces:
- An OPC UA AuditWriteEvent (server layer — see
[`docs/security.md`](../security.md) "Audit logging").
- A FOCAS driver-level Serilog entry tagged `Driver=FOCAS DriverInstanceId=...
TagName=... Address=... ResultStatus=...`.
- A `Writes/LastWriteAt` and `Writes/LastWriteStatus` diagnostic counter
refresh on the device's `Diagnostics/` fixed-tree node (planned;
populated as F4-c lands).
Failures to write (`BadUserAccessDenied`, `BadCommunicationError`, etc.)
produce the same audit entries with the failure status code so a
post-incident reviewer sees the same shape regardless of whether the write
succeeded.
### Granular config example
```jsonc
{
"Drivers": {
"FOCAS": {
"Devices": [
{ "HostAddress": "focas://10.0.0.5:8193", "Series": "Series30i" }
],
"Writes": {
"Enabled": true,
"AllowMacro": true, // recipe / setpoint writes — operator role
"AllowParameter": false // commissioning only — keep locked except during planned work
},
"Tags": [
{ "Name": "Recipe.PartCount", "DeviceHostAddress": "focas://10.0.0.5:8193",
"Address": "MACRO:500", "DataType": "Int32",
"Writable": true, "WriteIdempotent": true },
{ "Name": "MaxFeedrate", "DeviceHostAddress": "focas://10.0.0.5:8193",
"Address": "PARAM:1815", "DataType": "Int32",
"Writable": false /* keep read-only until commissioning window */ }
]
}
}
}
```
Flipping `AllowParameter` on for the commissioning window (and back off
afterward) is the recommended deployment cadence — the granular kill
switch is a lightweight runtime toggle, not a config-DB redeploy.