9b51dbca60
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).