# FOCAS Docker simulator — focas-mock + shim DLL Hardware-free FOCAS fixture for OtOpcUa's integration test matrix. Runs the vendored [`focas-mock`](focas-mock/VENDORED.md) Python server under Docker and pairs it with the [shim DLL](../Shim/VENDORED.md) that masquerades as `Fwlib64.dll` inside the .NET test process. ## Architecture ``` ┌────────────────────────────┐ cnc_allclibhndl3 / cnc_rdparam / ... │ xunit test process │ (P/Invoke, __stdcall) │ ├── Driver.FOCAS │ │ │ └── FwlibNative.cs ─┼─┐ │ └── FocasSimFixture │ │ resolves to... └────────────────────────────┘ │ ▼ ┌────────────────────────────┐ │ Fwlib64.dll (shim) │ JSON over TCP │ tests/.../Shim/focas_ │──────────────────────┐ │ shim.c compiled here │ │ └────────────────────────────┘ │ ▼ ┌─────────────────────────────┐ │ focas-mock (Docker) │ │ python:3.11-slim │ │ profile-aware responses │ │ mock_load_profile / │ │ mock_patch admin methods │ └─────────────────────────────┘ ``` The shim bridges the binary ABI (C `__stdcall` exports with FOCAS struct shapes) to the mock's newline-delimited JSON protocol. OtOpcUa's `FocasSimFixture` seeds per-test state by sending `mock_load_profile` + `mock_patch` admin calls on the same socket. Tests assert the managed driver sees the seeded values through its normal P/Invoke path. ## Running Pick one compose profile (they all publish 8193 — only one at a time): ```powershell docker compose -f Docker/docker-compose.yml --profile thirtyone up -d dotnet test tests/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.IntegrationTests docker compose -f Docker/docker-compose.yml --profile thirtyone down ``` Available profiles + their focas-mock target: | compose --profile | focas-mock profile | Covers | |---|---|---| | `thirtyone` / `thirty` / `thirtytwo` | `fwlib30i64` | 30i / 31i / 32i series | | `sixteen` | `FWLIB64` | 16i / 18i / 21i legacy family | | `zerod` / `zerof` / `zeromf` / `zerotf` | `fwlib0iD64` | 0i-D / 0i-F / 0i-MF / 0i-TF | | `powermotion` | `fwlib0DN64` | Power Motion i | | `ethernet` | `fwlibe64` | Ethernet-variant DLL | | `ncguide` | `fwlibNCG64` | NC Guide PC simulator | ## What this covers — and what it doesn't **Covered:** - All 10 FOCAS functions `FwlibNative.cs` P/Invokes - Read-after-write round-trip for parameters, macros, PMC ranges - PMC bit read-modify-write path (via the `pmc_wrpmcrng` seam) - `IAlarmSource` raise + clear transitions (via `mock_schedule_alarms`) - Per-series profile selection — tests can pin one and assert series-gated behaviour **Not covered** (still hardware-gated): - Real FOCAS2 TCP wire protocol (this is a JSON mock; the shim hides the real protocol entirely) - CNC-specific firmware quirks (position scaling across power cycles, edit-mode session locks, MTB custom screens) - Concurrent-read behaviour on the real `Fwlib64.dll` — the shim is single-threaded per connection See [`docs/drivers/FOCAS-Test-Fixture.md`](../../../docs/drivers/FOCAS-Test-Fixture.md) for the full coverage map. ## Skip behaviour `FocasSimFixture` probes the mock at collection init time: - Mock unreachable → tests skip with the compose-up command to run - Mock reachable but shim DLL not loaded → tests skip with a pointer at `Shim/build.ps1` - Both available → tests run This lets the same test assembly be green on a fresh CI box without docker, green on a dev box with just the docker compose up, and exercise the full wire path when the shim is built.