186 lines
8.9 KiB
Markdown
186 lines
8.9 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 (PARAM/MACRO, F4-b) + issue #270 (PMC, F4-c)
|
|
|
|
The FOCAS driver supports `cnc_wrparam`, `cnc_wrmacro`, and `pmc_wrpmcrng`
|
|
writes behind multiple independent opt-ins. A misdirected parameter write
|
|
can put the CNC in a bad state; a misdirected PMC write can move motion or
|
|
latch a feedhold. The runbook below MUST be followed before flipping any
|
|
of 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.
|
|
|
|
### PMC pre-checks (in addition to the above) — F4-c
|
|
|
|
PMC writes have a higher blast radius than PARAM/MACRO writes because PMC
|
|
is the ladder's working memory — bits in R/G/F/D directly drive servo
|
|
enables, feedhold latches, and safety interlocks. Before flipping
|
|
`Writes.AllowPmc` on:
|
|
|
|
1. **E-stop verified live + reachable.** The first PMC write of a session
|
|
should be issued with the operator's hand on the e-stop. PMC writes
|
|
bypass the ladder's normal MDI-mode protections; a misdirected bit can
|
|
move motion the moment it lands on the wire.
|
|
2. **Machine in JOG mode (or equivalent low-energy mode).** Auto / MEM
|
|
modes interpret PMC state immediately; JOG / MDI surface symptoms
|
|
slowly enough that the e-stop is the recovery path. **Never issue the
|
|
first PMC write of a deployment in Auto.**
|
|
3. **Audit the PMC tag list against the ladder print-out.** `R100.3` on
|
|
one machine is "homing complete"; on another it's "feedhold released".
|
|
The driver has no way to distinguish — the ladder source is the only
|
|
ground truth.
|
|
4. **Bit writes are read-modify-write — see
|
|
[`docs/drivers/FOCAS.md`](../drivers/FOCAS.md) "PMC bit-write read-modify-write semantics".**
|
|
`pmc_wrpmcrng` is byte-addressed; the driver reads the parent byte
|
|
first, masks the target bit, and writes the byte back. Concurrent
|
|
ladder writes to the same byte create a small race window. Coordinate
|
|
through a ladder-side handshake when this matters.
|
|
5. **Dry run with `Writable = true` but `Writes.AllowPmc = false`.** Same
|
|
staged-opt-in pattern as PARAM/MACRO — confirm tag mapping before any
|
|
PMC byte hits the wire.
|
|
|
|
### 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.
|
|
|
|
**Audit PMC writes specifically.** Because PMC writes have the highest blast
|
|
radius of the three write kinds, ops should set up a saved-search /
|
|
dashboard query for `Driver=FOCAS` + `Address` matching the PMC letter
|
|
prefixes (`R*`, `G*`, `F*`, `D*`, `Y*`, etc.) and review on the same
|
|
cadence as ladder change reviews. A spike in PMC write rate or a write
|
|
to an address outside the audited tag list is the leading indicator of a
|
|
misconfigured client or compromised credential.
|
|
|
|
### 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
|
|
"AllowPmc": false // PMC writes — keep locked unless the deployment specifically needs them
|
|
},
|
|
"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 */ },
|
|
{ "Name": "OperatorRequest", "DeviceHostAddress": "focas://10.0.0.5:8193",
|
|
"Address": "R100.3", "DataType": "Bit",
|
|
"Writable": false /* keep PMC read-only until ladder handshake reviewed */ }
|
|
]
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
Flipping `AllowParameter` / `AllowPmc` on for the commissioning window
|
|
(and back off afterward) is the recommended deployment cadence — the
|
|
granular kill switches are lightweight runtime toggles, not config-DB
|
|
redeploys. PMC in particular should default OFF in production and only
|
|
flip on for windows where the ladder team has signed off on the write
|
|
path.
|