interop: secsgem-py cross-validation harness + lenient identifier parsing
Adds a Docker-based interop harness that drives the C++ server with
secsgem-py 0.3.0 as the active host and probes a secsgem-py-passive
equipment from a minimal C++ active client. Surfaces and fixes four
interoperability bugs uncovered by cross-testing:
* SEMI E5 identifier formatcodes are a U1|U2|U4|U8 wildcard;
secsgem-py picks the narrowest fitting width while our parsers
only accepted U4. `as_uN_scalar` / `as_iN_scalar` now accept
any unsigned/signed width and range-check the downcast.
* PPBODY (S7F3/F6) is "ASCII | Binary | List" per the spec;
secsgem-py defaults to ASCII. Added BINARY_OR_ASCII codegen
item type with `as_text_or_binary` accessor.
* S1F23/F24 Collection Event Namelist was unimplemented; added
schema + `vids_for(ceid)` accessor on EventReportSubscriptions
plus the dispatch handler.
* S10F1 was registered as a host->equipment handler, but per
SEMI E5 §12 S10F1 is equipment->host; S10F3 is the actual
host->equipment Terminal Display Single. Added an S10F3
handler alongside (we keep S10F1 too for backward compat).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
# secsgem-py interop harness
|
||||
|
||||
Cross-validates our C++ SECS-II / HSMS / GEM implementation against
|
||||
[secsgem-py](https://pypi.org/project/secsgem/) 0.3.0, the de-facto
|
||||
Python reference. Everything runs in Docker — no Python or secsgem-py
|
||||
on the host.
|
||||
|
||||
## What it tests
|
||||
|
||||
| Driver | Peer | Coverage |
|
||||
| ------------------------------ | --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `host_vs_cpp_server.py` | C++ `secs_server` (passive) | HSMS select/separate, S1F1/F3/F11/F17/F23, S2F13/F17/F29/F33/F35/F37/F41, S5F3/F5/F7, S5F1 unsolicited, S6F11 unsolicited, S7F3/F5/F19, S10F1/F3, S1F15 |
|
||||
| `secs_interop_probe` (C++) | `passive_equipment.py` (secsgem-py GemEquipmentHandler) | HSMS select, S1F13/F14, S1F1/F2, S1F3/F4, clean separate |
|
||||
| `raw_gem300_harness.py` | C++ `secs_server` (passive) | GEM 300 streams secsgem-py upstream doesn't ship: S3F17/F18 (E87 carrier action), S16F5/F6 (E40 PRJobCommand), S16F27/F28 (E94 CJobCommand) — built with custom `SecsStreamFunction` subclasses + registered custom `DataItem`s |
|
||||
|
||||
24 named checks on the C++-server side; 4 explicit checks on the
|
||||
C++-host side; 4 GEM-300 raw-frame checks. Implicit HSMS state-machine
|
||||
and wire-level framing validation everywhere.
|
||||
|
||||
## Running
|
||||
|
||||
```bash
|
||||
# Start C++ passive server, then drive it with secsgem-py host:
|
||||
docker compose up -d server
|
||||
docker compose run --rm interop python3 /app/interop/host_vs_cpp_server.py \
|
||||
--host server --port 5000 --session-id 0
|
||||
|
||||
# Start Python passive equipment, then probe it with the C++ host:
|
||||
docker compose up -d equipment_py
|
||||
docker compose run --rm builder /app/build/secs_interop_probe \
|
||||
--host equipment_py --port 5000 --device 0
|
||||
```
|
||||
|
||||
Both exit 0 on success.
|
||||
|
||||
## What this caught
|
||||
|
||||
Real bugs surfaced by interop (now fixed):
|
||||
|
||||
1. **Strict U4 parsing rejected U1-encoded identifiers.** SEMI E5
|
||||
declares DATAID, RPTID, VID, CEID, ALID, EXID, etc. as
|
||||
`U1 | U2 | U4 | U8`; secsgem-py picks the smallest width that fits.
|
||||
Our `as_u4_scalar`, `as_u2_scalar`, etc. were strict. Now lenient
|
||||
with range-checked downcasts (`messages_helpers.hpp::any_unsigned_first`).
|
||||
2. **PPBODY rejected when sent as ASCII.** SEMI lets PPBODY be
|
||||
`ASCII | Binary | List`; secsgem-py defaults to ASCII. Added the
|
||||
`BINARY_OR_ASCII` codegen item type plus a permissive
|
||||
`as_text_or_binary` accessor, used for S7F3/F6.
|
||||
3. **Missing S1F23 / S1F24 (Collection Event Namelist).** Added the
|
||||
wire schema in `data/messages.yaml`, a `vids_for(ceid)` accessor on
|
||||
the event-report store, and the dispatch handler in `secs_server.cpp`.
|
||||
4. **Missing S10F3 handler (Terminal Display Single, host→equipment).**
|
||||
Our server only registered S10F1; per SEMI E5, S10F1 is
|
||||
equipment→host and S10F3 is the host→equipment counterpart. Added
|
||||
the missing dispatch.
|
||||
|
||||
The C++ test suite still passes (278 cases / 1436 assertions) after
|
||||
each of these changes — the fixes are purely permissive widenings, no
|
||||
existing behaviour was broken.
|
||||
Reference in New Issue
Block a user