@@ -147,6 +147,64 @@ 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).
|
||||
|
||||
### FOCAS password — issue #271 (F4-d)
|
||||
|
||||
Some controllers — notably 16i and certain 30i firmwares with the
|
||||
parameter-protect switch on — gate `cnc_wrparam` and a handful of reads
|
||||
behind a connection-level password. Without unlocking the session, every
|
||||
gated wire call returns `EW_PASSWD`, which the F4-b mapping surfaces as
|
||||
`BadUserAccessDenied`.
|
||||
|
||||
`FocasDeviceOptions.Password` plumbs the password through the device config:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"Devices": [
|
||||
{
|
||||
"HostAddress": "focas://10.0.0.5:8193",
|
||||
"Password": "1234" // F4-d — optional CNC password
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
When set, the driver:
|
||||
|
||||
1. **On connect**, calls `IFocasClient.UnlockAsync(password, ct)` after
|
||||
the FWLIB handle opens but before any read/write fires. The FWLIB-backed
|
||||
client emits `cnc_wrunlockparam` with the password ASCII-encoded into
|
||||
the 4-byte FOCAS password slot (right-padded with `0x00`, truncated at
|
||||
4 bytes — that's the shape the public Fanuc samples document).
|
||||
2. **On `BadUserAccessDenied` from any gated read or write**, re-issues
|
||||
`UnlockAsync` and retries the call **exactly once**. A second
|
||||
`EW_PASSWD` propagates unchanged so a wrong password doesn't loop
|
||||
forever on the wire.
|
||||
3. **Reset on reconnect** — FWLIB unlock state lives on the handle, so
|
||||
any reconnect path (planned or unplanned) re-runs unlock automatically
|
||||
via `EnsureConnectedAsync`.
|
||||
|
||||
**No-log invariant.** The password is a secret. The driver MUST NOT log
|
||||
it. Specifically:
|
||||
|
||||
- `FocasDeviceOptions` overrides the record's auto-generated `ToString`
|
||||
to print `Password = ***` when the field is non-null. Any Serilog
|
||||
destructure that flows the device options through `{Device}` gets the
|
||||
redaction for free.
|
||||
- `FwlibFocasClient.UnlockAsync` does not include the password in any
|
||||
exception message — only the FWLIB return code (`EW_PASSWD`,
|
||||
`EW_HANDLE`, etc.) makes it into the surface.
|
||||
- `FocasDriver` logs only `"FOCAS unlock applied for {host}"` when the
|
||||
unlock succeeds — no password.
|
||||
- The Driver.FOCAS.Cli `--cnc-password` flag is also redacted at the
|
||||
same `FocasDeviceOptions` choke point.
|
||||
- See [`docs/v2/focas-deployment.md`](../v2/focas-deployment.md)
|
||||
§ "FOCAS password handling" for the storage/rotation runbook + the
|
||||
cross-link to [`docs/Security.md`](../Security.md).
|
||||
|
||||
When the controller does **not** need a password, leave `Password`
|
||||
unset (`null`) and the driver short-circuits the unlock call entirely —
|
||||
no wire-level cost.
|
||||
|
||||
### Status-code semantics post-F4-b
|
||||
|
||||
- `BadNotWritable` — one of: driver-level `Writes.Enabled = false`; per-tag
|
||||
@@ -155,9 +213,13 @@ value that the operator simply wants forced to a target).
|
||||
**`Writes.AllowPmc = false` for a PMC tag (F4-c)**. Same status code,
|
||||
five distinct paths — operators distinguish by checking the knobs.
|
||||
- `BadUserAccessDenied` — **F4-b** — the CNC reported `EW_PASSWD`
|
||||
(parameter-write switch off / unlock required). F4-d will land the
|
||||
unlock workflow on top of this surface; today the deployment instructs
|
||||
the operator to flip the parameter-write switch on the CNC pendant.
|
||||
(parameter-write switch off / unlock required). **F4-d** wires the
|
||||
`cnc_wrunlockparam` retry path on top: when `Password` is configured
|
||||
the driver re-issues unlock + retries the gated call once before
|
||||
surfacing this status. A persistent `BadUserAccessDenied` after F4-d
|
||||
means either (a) the password doesn't match the controller, or (b)
|
||||
the parameter-write switch on the pendant is still off and the
|
||||
controller wants both the switch + the password.
|
||||
- `BadNotSupported` — both opt-ins flipped on, but the wire client doesn't
|
||||
implement the kind being written (e.g. older transport variant). F4-a
|
||||
wired the generic dispatch; F4-b adds typed `WriteParameterAsync` /
|
||||
|
||||
Reference in New Issue
Block a user