8686654b15
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>
36 lines
1.1 KiB
Python
36 lines
1.1 KiB
Python
#!/usr/bin/env python3
|
|
"""Conversion round-trips for the Value layer. Plain asserts — run directly."""
|
|
import sys
|
|
|
|
from secsgem_client._proto import equipment_pb2 as pb
|
|
from secsgem_client._values import from_value, to_value
|
|
|
|
|
|
def roundtrip(v):
|
|
return from_value(to_value(v))
|
|
|
|
|
|
def main() -> int:
|
|
assert roundtrip(2.5) == 2.5
|
|
assert roundtrip(7) == 7
|
|
assert roundtrip(-3) == -3
|
|
assert roundtrip(True) is True # bool BEFORE int: must stay boolean
|
|
assert roundtrip(False) is False
|
|
assert to_value(True).WhichOneof("kind") == "boolean"
|
|
assert to_value(1).WhichOneof("kind") == "integer"
|
|
assert roundtrip("wafer-17") == "wafer-17"
|
|
assert roundtrip(b"\x01\x02") == b"\x01\x02"
|
|
assert roundtrip([1, 2.5, "x", [True]]) == [1, 2.5, "x", [True]]
|
|
assert from_value(pb.Value()) is None # unset oneof
|
|
try:
|
|
to_value(object())
|
|
raise SystemExit("expected TypeError for unconvertible type")
|
|
except TypeError:
|
|
pass
|
|
print("values: all conversion checks passed")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|