Files
secs-gem/clients/python
raphael d22bbc4ab2 fix(daemon)+fix(client): close four fool-proofing gaps
C++ (equipment_service.hpp):
- ReportSubstrate ARRIVED: check CreateResult and return INVALID_OBJECT
  when the substrate ID already exists, instead of silently doing nothing
- ReportSubstrate/ReportModule default switch branches: return false
  (→ CANNOT_DO_NOW) for unknown enum values instead of silently accepting

Python (_client.py):
- @eq.command: raise NameError (client-side name validation) instead of
  SecsGemError (which means "daemon declined a request") — wrong type
- Module docstring: update example to show @eq.command / eq.names API

Test (test_daemon_service.cpp):
- Add duplicate-ARRIVED check (expects INVALID_OBJECT)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 21:48:29 +02:00
..

secsgem-client

A complete GEM tool integration in plain Python. The secs_gemd daemon owns everything SEMI — the HSMS link to the host, the GEM state machines, formats, timers, spooling; this client tells it about your tool and reacts to the host.

from secsgem_client import Equipment

eq = Equipment("localhost:50051")

eq.set(ChamberPressure=2.5)        # host sees it on its next poll
eq.fire("ProcessStarted")          # S6F11 to the host, report auto-assembled
eq.alarm("chiller_temp_high")      # S5F1 (set), eq.clear(...) for clear

@eq.on("START")                    # host remote commands -> your function
def start(cmd):
    run_recipe(cmd.params.get("PPID"))
    eq.fire("ProcessStarted")      # the host's real completion signal

eq.listen()                        # block and dispatch (background=True for a thread)

Names are the ones from your equipment.yaml; values are plain Python (float, int, bool, str, bytes, lists). Errors raise SecsGemError with the daemon's explanation ("no variable named ...").

No compiled extension, no SEMI knowledge, no C++ toolchain — pip install and a running daemon is the whole setup.