feat(clients)+test(interop): C++ client + Java validation of the daemon (B7)
tests / build-and-test (push) Successful in 2m54s
tests / thread-sanitizer (push) Successful in 3m48s
tests / tshark-dissector (push) Successful in 2m24s
tests / secs4j-interop (push) Successful in 1m44s
tests / python-interop (push) Successful in 3m10s
tests / libfuzzer (push) Successful in 3m38s

B7 — the daemon's HSMS face under the Java reference: Dockerfile.server now
bakes secs_gemd alongside secs_server (grpc deps in both stages), and
secs4j_validate.sh gains TARGET=gemd to point the 55-check secs4java8 suite
at the daemon instead. Result: 55/55 green. With secsgem-py already
validating both faces, byte-identical GEM between secs_server and secs_gemd
is now proven by both reference implementations, not inferred from shared
code. CI runs the daemon target as an extra step (image layers shared).

Second client — clients/cpp: a header-only C++ twin of the Python client
over the same proto. eq.set("ChamberPressure", 2.5) with bare literals
(integral/floating dispatch avoids variant ambiguity), get/fire/alarm/
clear, control_state/request_control_state/health, on("START", fn) +
listen()/listen_async()/stop() with auto-CompleteCommand, SecsGemError
carrying the daemon's message. cpp_mini_tool (~30 lines) mirrors the
Python mini_tool. Tested end-to-end over real loopback TCP against the
service inside secs_gemd_tests — now 4 cases / 141 assertions — including
set/get round-trips, error text, alarm-by-name into the model, health,
and the full HCACK-4 command loop with parameters.

(Build note: two grpc-heavy TUs at -O3 OOM even at -j2 on Docker Desktop;
built -j1. Known environment limitation, roadmap-documented.)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-10 23:37:14 +02:00
parent 4f3031aeb9
commit b30443089f
9 changed files with 515 additions and 13 deletions
+17 -5
View File
@@ -27,7 +27,7 @@ host and stays conformant while the tool software restarts/upgrades/crashes.
| gRPC/protobuf toolchain (Dockerfile + CMake codegen) | ✅ | grpc++ 1.51 / protoc 3.21; opt-in `SECSGEM_DAEMON`, graceful skip without grpc |
| `secs_gemd`: `SetVariables` / `FireEvent` / `GetControlState` / `GetVariables` | ✅ | **format-aware** both directions (declared SECS-II formats on write, `from_item` on read) and thread-safe (snapshot maps + posted writes + `read_sync` reads). In-process gRPC tests incl. run_async production mode (`test_daemon_service.cpp`, 61 assertions) |
| Daemon interop vs **secsgem-py** reference host | ✅ | `interop/daemon_interop.py` (via `gemd` compose service): gRPC `SetVariables(ChamberPressure=2.5)` + `FireEvent` → host receives `S6F11 CEID 300` carrying `<F4 2.5>` — value *and declared format* flow gRPC→engine→HSMS→host |
| Daemon interop vs **secs4j** (Java) | | mirror the secsgem-py harness against `interop/secs4j` |
| Daemon interop vs **secs4j** (Java) | | `TARGET=gemd interop/secs4j_validate.sh` — 55/55 against the daemon's HSMS face; in CI |
| `Subscribe` host→tool command stream + `CompleteCommand` | ✅ | HCACK-4 contract implemented + tested in-process AND live vs secsgem-py (full loop: S2F41 → stream → complete → S6F11) |
| Universal RPC surface complete (vars/events/alarms/control-state/health) | ✅ | Phase A done; daemon tests 101 assertions, interop 15 checks |
| Python client package (the "beautiful API") | ✅ | `clients/python` (`secsgem-client`); 13-check interop green via the published API |
@@ -170,7 +170,12 @@ debts tax every later phase, and the most valuable tests aren't automated.
START` → `S2F42 HCACK=4` → tool receives Command(name=START, id) on the
stream → `CompleteCommand` → tool fires the event → host receives `S6F11`.
(interop now 20 checks.)
7. Java interop: `secs4j` host variant of the same scenario.
7. Java interop: `TARGET=gemd interop/secs4j_validate.sh` runs the full
55-check secs4java8 suite against the DAEMON's HSMS face — 55/55 green
(secs_gemd and secs_server sit on the same register_default_handlers, so
byte-identical GEM is now proven, not assumed). CI step added. The
command loop with a live subscriber is covered by the python harnesses
(a Java *tool-side* gRPC client remains possible future work).
### Phase C — the beautiful Python client
8. ✅ `clients/python/` — pip-installable `secsgem-client`, pure Python,
@@ -185,9 +190,16 @@ debts tax every later phase, and the most valuable tests aren't automated.
S5F1 set+clear on the wire, HCACK-4 command loop through the decorator,
operator offline). Conversion layer unit-tested (bool-before-int etc).
Wired into tools/run_interop.sh as the `pyclient` step.
9. 🚧 `clients/python/examples/mini_tool.py` — a complete GEM tool in ~25
lines ✅. Migrating the C++ `pvd_tool` to EquipmentRuntime + capability
registration + `set_handler` ⬜.
9. `clients/python/examples/mini_tool.py` (~25 lines) and the C++
`pvd_tool` migrated to EquipmentRuntime + register_default_handlers +
`set_handler` (1093 -> 570 lines; now serves all 56 handlers; boots
verified). Chapter 42 teaches the daemon path.
10. ✅ **C++ client** (`clients/cpp`): header-only twin of the Python
client — `eq.set("ChamberPressure", 2.5)`, `eq.on("START", fn)` +
`listen_async()`, alarms/health/control-state, SecsGemError. Tested
end-to-end over loopback TCP against the real service inside
`secs_gemd_tests` (141 assertions total), incl. the HCACK-4 loop.
`cpp_mini_tool` is the worked example.
### Phase D — GEM300 in-the-loop (process/carrier tools)
10. ⬜ Settle job/carrier semantics (who acks S16F5/S3F17, gate vs observe —