The mock had served the shapes the driver once agreed with it on, so it validated the very defects just fixed: it ANSWERED the non-existent 0x00D3 cnc_getfigure, wrote base/exponent 0/0 into position blocks, put the macro decimal at offset 4, and served spindle 'max rpm' as a raw constant. Ported from the sister ignitionoee mock (local divergence recorded in VENDORED.md; the LE timer and alarm cap are kept): - 0x00D3 refused with EW_FUNC, as the real control does. - 0x0026 blocks carry base 10 / position_figures exponent; signed arg2, arg2 = 0 or past the axis count refused EW_ATTRIB. - cnc_rdmacro is the 8-byte ODBM shape, 9-digit mantissa, unset = vacant. - servo/spindle meter records carry the envelope; spindle meters are 1/16384-of-full-scale counts (motor speed = speed / max_rpm). Integration tests: spindle test reads Load/MotorSpeedPct percentages; the position-scale test now proves the block's own scale wins; new Vacant_positions_and_macros_read_bad_over_the_wire. 12/12 pass against the mock run locally (python -m focas_mock.cli serve, OTOPCUA_FOCAS_SIM_ENDPOINT).
2.5 KiB
focas-mock — vendored snapshot
Source: C:\Users\dohertj2\Desktop\focas (sibling project in this dev environment).
Snapshot date: 2026-04-24 (second refresh — pulled the native FOCAS2 Ethernet responder work in).
Why vendored
OtOpcUa's FOCAS integration fixture runs against the Python mock server. The upstream lives in its own repo; this directory is a verbatim snapshot so CI can build the Docker image without network access to the source repo and so OtOpcUa's test matrix pins a known-good revision.
The managed WireFocasClient speaks the mock's native FOCAS2 Ethernet
binary protocol directly — there's no longer a companion shim DLL.
What's here
| Path | Purpose |
|---|---|
src/focas_mock/ |
Python package — TCP JSON/line-delimited mock server with 6 Fanuc CNC profiles |
pyproject.toml |
Package metadata; installs focas-mock CLI |
Dockerfile |
python:3.11-slim image built by the parent docker-compose.yml |
README.md |
Upstream README |
LICENSE |
MIT — permissive, vendoring allowed |
Refreshing the snapshot
When upstream ships changes worth pulling:
$src = "C:\Users\dohertj2\Desktop\focas"
$dest = "$PWD"
Remove-Item -Recurse -Force "$dest\src" 2>$null
Copy-Item -Recurse "$src\src" "$dest\src"
Copy-Item "$src\pyproject.toml" "$dest\"
Copy-Item "$src\README.md" "$dest\"
Copy-Item "$src\LICENSE" "$dest\"
Copy-Item "$src\Dockerfile" "$dest\"
Update the snapshot date at the top of this file afterward. No other files belong here — the Docker build context is just the Python package and its metadata.
Local divergence (do not lose on refresh)
src/focas_mock/server.py and defaults.py carry OtOpcUa-local edits that make the wire
shapes match a real FANUC 31i-B (Makino Z-34184) instead of shapes the driver and the mock had
agreed on between themselves. A refresh that overwrites src/ must re-apply them:
- the little-endian
cnc_rdtimerminute/msec encoding (2026-06-25, live-validated); - the alarm count cap on
cnc_rdalmmsg2; - (2026-10-07, ported from the sister ignitionoee FOCAS mock)
0x00D3("cnc_getfigure", not a real FOCAS wire command) refused withEW_FUNC;0x0026position blocks carrying their ownbase/exponent(position_figuresis now that exponent) and a signedarg2;cnc_rdmacroas the 8-byte ODBM shape (dec_valat[6..7], 9-digit mantissa, unset macro = vacant); servo/spindle meter records with thebase/exponentenvelope, spindle meters in 1/16384-of-full-scale counts.