Captures uncommitted work that lived in the working tree on
v2-mxgw-integration but was orthogonal to the migration. Stashed
during the v2-mxgw merge to master (2026-04-30) and replanted here on
a feature branch off master so it's git-visible rather than living in
the stash list.
Two distinct buckets:
1. Tracked fixture/config refinements (10 files, ~36 lines):
- scripts/e2e/test-opcuaclient.ps1
- src/ZB.MOM.WW.OtOpcUa.Admin/appsettings.json
- 5 docker-compose.yml under tests/.../IntegrationTests/Docker/
(AbCip, Modbus, OpcUaClient, S7)
- 4 fixture .cs files (AbServerFixture, ModbusSimulatorFixture,
OpcPlcFixture, Snap7ServerFixture)
2. Untracked driver-gaps queue artifacts (~8000 lines):
- docs/plans/{abcip,ablegacy,focas,opcuaclient,s7,twincat}-plan.md
— per-driver gap plans
- docs/featuregaps.md — cross-cutting analysis
- docs/v2/focas-deployment.md, docs/v2/implementation/focas-simulator-plan.md
- followup.md — auto/driver-gaps queue follow-ups
- scripts/queue/ — PR-queue automation tooling (12 files including
pr-manifest.yaml at 1473 lines)
This commit is a snapshot for recoverability — review and split into
focused PRs (or discard) before merging anywhere downstream.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
160 lines
9.2 KiB
Markdown
160 lines
9.2 KiB
Markdown
# FOCAS deployment guide
|
|
|
|
Operational reference for deploying the Fanuc FOCAS driver in production.
|
|
|
|
## Licence + DLL provisioning
|
|
|
|
Fanuc's FOCAS2 library is proprietary + closed-source. Two DLL variants exist:
|
|
|
|
| Variant | Bitness | OtOpcUa usage |
|
|
|---|---|---|
|
|
| **`Fwlib64.dll`** | x64 | **Default production binary.** Loaded by `Driver.FOCAS.Host` (net10.0 x64 Windows service) and by the `Driver.FOCAS.Cli` when running on an x64 server. |
|
|
| `Fwlib32.dll` | x86 | Historical — what the project was originally scaffolded against. Not used by any current binary post the 2026-04-23 Host retarget. Kept in the licence set for legacy deployments that insist on x86-only Hosts. |
|
|
|
|
Both are **licensed for this project** — this project has a valid Fanuc FOCAS developer-kit licence that grants redistribution for either variant internally.
|
|
|
|
### The DLLs now ship with the Host (2026-04-23)
|
|
|
|
As of the vendoring change, the Host csproj copies the licensed FOCAS binaries from [`vendor/fanuc/`](../../vendor/fanuc/README.md) to its build output automatically. So after a `dotnet build` / `dotnet publish`, the layout is:
|
|
|
|
```
|
|
<publish-root>\Driver.FOCAS.Host\
|
|
├── OtOpcUa.Driver.FOCAS.Host.exe
|
|
├── OtOpcUa.Driver.FOCAS.Host.dll
|
|
├── ... runtime deps ...
|
|
├── Fwlib64.dll ← master FOCAS runtime (generic x64)
|
|
├── fwlib0iD64.dll ← 0i-D series dispatch target
|
|
├── fwlib30i64.dll ← 30i / 31i / 32i series dispatch target
|
|
├── fwlibe64.dll ← Ethernet transport variant
|
|
├── fwlibNCG64.dll ← NC Guide (Fanuc PC simulator) target
|
|
└── fwlib0DN64.dll ← 0i-D Numeric-control thin variant
|
|
```
|
|
|
|
No operator step required to "drop Fwlib64.dll on PATH" anymore — the Host loads `Fwlib64.dll` via bare-name and Windows finds it in the exe's own directory first. Shipping the full set of series-specific siblings lets the Host work against any Fanuc CNC the deployment points it at; the master `Fwlib64.dll` dispatches to the right variant based on what the CNC reports during `cnc_allclibhndl3`.
|
|
|
|
The DLL loads lazily on the first `OpenSessionAsync` call. When somehow missing (deployment artefact surgery), `Fwlib64FocasBackend` returns a structured `Fwlib64DllMissing` error-code rather than crashing; the Proxy maps it to `BadCommunicationError` with a clear operator message.
|
|
|
|
### Repo confidentiality note
|
|
|
|
**The FOCAS runtime DLLs in `vendor/fanuc/` are licensed binaries — treat this repo accordingly.** Do not mirror / push / fork to any public forge without first confirming the redistribution is covered by whoever manages the Fanuc relationship. Internal / customer-licensed mirrors are fine. See [`vendor/fanuc/README.md`](../../vendor/fanuc/README.md) for the full provenance + licence context.
|
|
|
|
## Tier-C architecture recap
|
|
|
|
The FOCAS driver is **Tier-C** — out-of-process — for **blast-radius isolation**, not bitness. Fanuc's DLL has documented crash modes (network errors, malformed responses, handle-recycle bugs) that could take the main OPC UA server down if loaded in-process. Splitting the P/Invoke into a separate Host process means a Fwlib crash only loses FOCAS tags; every other driver keeps running, and the supervisor restarts the Host.
|
|
|
|
Galaxy has the same pattern but is **forced** by MXAccess's 32-bit-only COM — there's no x64 path. FOCAS would work in-process on x64 (Fwlib64 is licensed), but the blast-radius argument keeps it Tier-C anyway.
|
|
|
|
See [`implementation/focas-isolation-plan.md`](implementation/focas-isolation-plan.md) for the full topology.
|
|
|
|
## Installing the Host service
|
|
|
|
Use the NSSM wrapper script:
|
|
|
|
```powershell
|
|
.\scripts\install\Install-FocasHost.ps1 `
|
|
-InstallRoot 'C:\Program Files\OtOpcUa\Driver.FOCAS.Host' `
|
|
-ServiceAccount 'OTOPCUA\svc-otopcua' `
|
|
-FocasBackend fwlib64
|
|
```
|
|
|
|
Parameters:
|
|
|
|
| Parameter | Default | Purpose |
|
|
|---|---|---|
|
|
| `-InstallRoot` | **required** | Where the Host binaries + `Fwlib64.dll` live |
|
|
| `-ServiceAccount` | **required** | Must match the main OtOpcUa server account so the named-pipe ACL allows the Proxy to connect |
|
|
| `-FocasBackend` | `fwlib64` | `fwlib64` (production), `fake` (in-memory for Tier-C pipeline smoke without a CNC), `unconfigured` (returns BadDeviceFailure for every call) |
|
|
| `-FocasSharedSecret` | auto-gen | Per-process secret passed at service start so it never touches disk |
|
|
| `-FocasPipeName` | `OtOpcUaFocas` | Named pipe the Proxy connects to |
|
|
| `-ServiceName` | `OtOpcUaFocasHost` | Windows service display name |
|
|
|
|
`fwlib32` is accepted as a legacy alias but maps to `Fwlib64FocasBackend` internally — the Host is x64 post-2026-04-23, so 32-bit-only deployments would need to rebuild + retarget.
|
|
|
|
## Configuring a FOCAS driver instance
|
|
|
|
In the Admin UI's Drivers tab, create a `DriverInstance` with `DriverType = "FOCAS"` and a JSON config of the shape:
|
|
|
|
```json
|
|
{
|
|
"Backend": "ipc",
|
|
"PipeName": "OtOpcUaFocas",
|
|
"SharedSecret": "<matches OTOPCUA_FOCAS_SECRET env var on the Host>",
|
|
"Devices": [
|
|
{ "Name": "Mill-01", "HostAddress": "focas://192.168.1.50:8193", "Series": "ThirtyOne_i" }
|
|
],
|
|
"Tags": [
|
|
{ "Name": "SpindleLoad", "DeviceName": "Mill-01", "Address": "R100", "DataType": "Int16" },
|
|
{ "Name": "CycleRunning", "DeviceName": "Mill-01", "Address": "X0.0", "DataType": "Bit" },
|
|
{ "Name": "PartCount", "DeviceName": "Mill-01", "Address": "MACRO:500", "DataType": "Float64" }
|
|
],
|
|
"Probe": { "Enabled": true, "IntervalMs": 5000, "TimeoutMs": 2000 }
|
|
}
|
|
```
|
|
|
|
`Backend` selector (on the Proxy side — not to be confused with `OTOPCUA_FOCAS_BACKEND` on the Host):
|
|
|
|
| Value | Meaning |
|
|
|---|---|
|
|
| `ipc` (default) | Route through `Driver.FOCAS.Host` over the named pipe. **Production shape.** |
|
|
| `fwlib` | Direct in-process P/Invoke via `FwlibFocasClient`. Only valid on x64 servers that are willing to accept the blast-radius trade-off. |
|
|
| `unimplemented` | Throws at construction — used for scaffolding `DriverInstance` rows before the Host is deployed. |
|
|
|
|
## Smoke testing
|
|
|
|
**Without a CNC — pipeline only:**
|
|
|
|
```powershell
|
|
$env:OTOPCUA_FOCAS_BACKEND = "fake"
|
|
Start-Service OtOpcUaFocasHost
|
|
```
|
|
|
|
The `FakeFocasBackend` stores per-address values in-memory and survives read/write/subscribe exercising. Use `otopcua-focas-cli` (in-process, bypasses the Host) or the OtOpcUa server's own driver registration to exercise the pipeline.
|
|
|
|
**Version-aware fake** (Stream A of the simulator plan, shipped 2026-04-23) — set `OTOPCUA_FOCAS_SERIES` to simulate a specific Fanuc controller's capability matrix. Addresses outside the series' documented ranges get rejected with `BadOutOfRange` (matching what the real DLL returns as `EW_NUMBER` / `EW_PARAM`):
|
|
|
|
```powershell
|
|
$env:OTOPCUA_FOCAS_BACKEND = "fake"
|
|
$env:OTOPCUA_FOCAS_SERIES = "ThirtyOne_i" # or Zero_i_D / Zero_i_F / Sixteen_i / PowerMotion_i / ...
|
|
Start-Service OtOpcUaFocasHost
|
|
```
|
|
|
|
**Optional behavioural quirks** — `OTOPCUA_FOCAS_QUIRKS` is a comma-separated list:
|
|
|
|
| Token | Behaviour |
|
|
|---|---|
|
|
| `EditMode` | `OpenSessionAsync` refuses sessions with `ErrorCode=EditModeActive`, mimicking a CNC in Edit mode |
|
|
| `Emergency` | `ProbeAsync` reports the session as unhealthy with `emergency-stop active` error even after a clean open — exercises the driver's probe-surfaces-non-connectivity path |
|
|
| `SlowFirstConnect[=ms]` | First `OpenSessionAsync` blocks for `ms` (default 3000) milliseconds, mimicking the 16i-series slow-first-connect — subsequent opens are fast |
|
|
| `CrashAfterCycles=N` | After `N` session opens, the `N+1`-th returns `ErrorCode=Fwlib64Crashed` — mimics the documented Fanuc handle-leak |
|
|
|
|
Example combining several:
|
|
|
|
```powershell
|
|
$env:OTOPCUA_FOCAS_QUIRKS = "EditMode,CrashAfterCycles=5,SlowFirstConnect=500"
|
|
```
|
|
|
|
Unknown tokens log a warning but don't abort startup.
|
|
|
|
**With a real CNC:**
|
|
|
|
```powershell
|
|
$env:OTOPCUA_FOCAS_BACKEND = "fwlib64"
|
|
$env:FOCAS_TRUST_WIRE = "1"
|
|
Start-Service OtOpcUaFocasHost
|
|
.\scripts\e2e\test-focas.ps1 -CncHost 192.168.1.50 -BridgeNodeId 'ns=2;s=Focas/R100'
|
|
```
|
|
|
|
Requires `Fwlib64.dll` on `PATH` alongside the Host exe.
|
|
|
|
## Observability
|
|
|
|
- Host logs: `%ProgramData%\OtOpcUa\focas-host-*.log` (Serilog daily rolling)
|
|
- Post-mortem: `%ProgramData%\OtOpcUa\focas-post-mortem.mmf` — ring buffer of the last ~1000 IPC operations, survives a Host crash so the Proxy-side supervisor can read it during respawn diagnostic
|
|
- `DriverHostStatus` rows in the central Config DB under `HostName = <configured device host>` — `State` transitions + Polly resilience counters surface on the Admin `/hosts` page
|
|
|
|
## Known issues
|
|
|
|
- **No public simulator** — Fanuc FOCAS has no published emulator. Lab-rig validation (a real FANUC 0i-F / 30i controller or an FDK-licenced dev rig) is the only way to confirm wire-level correctness. Tracked under task #222.
|
|
- **32-bit-only deployments unsupported** — post the 2026-04-23 retarget, running the Host as net48 x86 is not a supported mode. If you genuinely need Fwlib32-only, revert the Host csproj + Program.cs changes from that commit.
|
|
- **Handle-recycling cadence** — documented Fanuc issue where long-lived FWLIB session handles can leak inside the DLL; the Host periodically cycles them. Currently on a fixed 60-minute cadence; future config knob tracked as a post-release follow-up.
|