feat(client): the Python client — a GEM tool in plain Python (Phase C)

clients/python: pip-installable "secsgem-client", pure Python (stubs
pre-generated from equipment.proto, import made package-relative; no
compiled extension, no SEMI knowledge, no C++ toolchain). The API the whole
effort aimed at:

    eq = Equipment("localhost:50051")
    eq.set(ChamberPressure=2.5); eq["WaferCounter"] = 7
    eq.fire("ProcessStarted", ChamberPressure=2.75)
    eq.alarm("chiller_temp_high"); eq.clear("chiller_temp_high")
    @eq.on("START")
    def start(cmd): ...           # auto-CompleteCommand after return
    eq.listen(background=True)
    eq.control_state; eq.request_control_state("HOST_OFFLINE"); eq.health()

Errors raise SecsGemError carrying the daemon's message ("no variable named
..."). bool checked before int in conversion (isinstance(True, int)).
examples/mini_tool.py is a complete GEM tool in ~25 lines.

PROOF — interop/pyclient_interop.py drives the PUBLISHED package (not raw
stubs) against a live secs_gemd with secsgem-py as the fab host: 13 checks
all green on first run — set/get round-trips, item syntax, SecsGemError on
unknown names, control state, health, fire->S6F11 on the host's wire,
alarm/clear->S5F1 with correct set bit, the full command loop (host S2F41 ->
HCACK=4 -> @eq.on handler -> completion event back at the host), operator
offline. Conversion layer unit-tested standalone; both wired into
tools/run_interop.sh as the pyclient step.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-10 22:57:55 +02:00
parent 912304966f
commit 8686654b15
19 changed files with 1235 additions and 7 deletions
+16 -7
View File
@@ -30,7 +30,7 @@ host and stays conformant while the tool software restarts/upgrades/crashes.
| Daemon interop vs **secs4j** (Java) | ⬜ | mirror the secsgem-py harness against `interop/secs4j` |
| `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") | | thin wrapper over generated stubs |
| Python client package (the "beautiful API") | | `clients/python` (`secsgem-client`); 13-check interop green via the published API |
## Known issues (found in the 2026-06-10 audit; honest list)
@@ -173,12 +173,21 @@ debts tax every later phase, and the most valuable tests aren't automated.
7. ⬜ Java interop: `secs4j` host variant of the same scenario.
### Phase C — the beautiful Python client
8. `clients/python/` package (`pip install secsgem-client`): wraps generated
stubs in the agreed API — `eq.set(chamber_pressure=2.5)`, `eq.fire("wafer_complete", thickness=1.2)`,
`eq.alarm("pressure_high")`, `@eq.on("START")` consuming the stream,
`eq.health()`. Pure Python (no compiled ext). Ship stubs pre-generated.
9. ⬜ Example: rewrite a minimal `pvd_tool`-equivalent in ~40 lines of Python
against the daemon; also migrate the C++ `pvd_tool` to `set_handler`.
8. `clients/python/` pip-installable `secsgem-client`, pure Python,
stubs pre-generated (relative-import fixed). The full agreed API:
`eq.set(ChamberPressure=2.5)` kwargs + `eq["..."]` item syntax, `eq.get`,
`eq.fire(event, **data)`, `eq.alarm`/`eq.clear`, `eq.control_state`,
`eq.request_control_state`, `eq.health()`/`watch_health()`, and
`@eq.on("START")` + `eq.listen(background=...)` with auto-CompleteCommand.
Errors raise `SecsGemError` carrying the daemon's explanation.
PROOF: `interop/pyclient_interop.py` drives the PUBLISHED package against
a live daemon with secsgem-py as the host — 13 checks all green (S6F11/
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` ⬜.
### Phase D — GEM300 in-the-loop (process/carrier tools)
10. ⬜ Settle job/carrier semantics (who acks S16F5/S3F17, gate vs observe —