Files
lmxopcua/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.IntegrationTests/Docker/focas-mock/VENDORED.md
T
Joseph Doherty 9b51dbca60 test(focas): bring the vendored focas-mock onto the real 31i-B wire shapes; integration tests follow
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).
2026-10-07 14:12:11 -04:00

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_rdtimer minute/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 with EW_FUNC; 0x0026 position blocks carrying their own base/exponent (position_figures is now that exponent) and a signed arg2; cnc_rdmacro as the 8-byte ODBM shape (dec_val at [6..7], 9-digit mantissa, unset macro = vacant); servo/spindle meter records with the base/exponent envelope, spindle meters in 1/16384-of-full-scale counts.