a2ebbf7c65
Python client:
- eq.names.event.* / .alarm.* / .command.* / .var.* / .constant.* —
autocomplete-able, typo-safe name lookup backed by the Describe RPC
(lazy, cached; AttributeError on bad name with close-match hints)
- @eq.command decorator — binds a handler by function name, validated
against the equipment's real command set at decoration time
- eq.report_substrate() — E90 wafer milestone reporting
- eq.report_module() — E157 module state reporting (auto-create)
Daemon (C++ service):
- ReportSubstrate RPC — drives E90 location + processing FSMs
- ReportModule RPC — drives E157 module FSM (auto-create on first report)
- ack_from_outcome() helper — consistent Ack mapping for read_sync results
Proto: SubstrateReport, ModuleReport, EquipmentDescription,
SpoolFlushRequest, TerminalMessage; Describe, FlushSpool,
SendTerminalMessage RPCs
Tests: C++ FSM test (journey + ghost rejection + E157 illegal jump);
interop coverage for names API and E90/E157 round-trip
Docs: ch42 RPC table + Python example updated; ch16 daemon-path section added
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
33 lines
753 B
Python
33 lines
753 B
Python
#!/usr/bin/env python3
|
|
"""A complete GEM tool in ~25 lines.
|
|
|
|
Run secs_gemd, then this. The fab host can poll ChamberPressure, receive
|
|
ProcessStarted events, get alarms above the threshold, and START the tool —
|
|
all SEMI plumbing handled by the daemon.
|
|
"""
|
|
import random
|
|
import time
|
|
|
|
from secsgem_client import Equipment
|
|
|
|
eq = Equipment("localhost:50051")
|
|
|
|
|
|
@eq.command
|
|
def START(cmd):
|
|
print("host says START", cmd.params)
|
|
eq.fire(eq.names.event.ProcessStarted)
|
|
|
|
|
|
eq.listen(background=True)
|
|
|
|
while True:
|
|
pressure = round(random.uniform(1.0, 3.0), 3)
|
|
eq.set(ChamberPressure=pressure)
|
|
if pressure > 2.9:
|
|
eq.alarm(eq.names.alarm.chiller_temp_high)
|
|
else:
|
|
eq.clear(eq.names.alarm.chiller_temp_high)
|
|
time.sleep(1)
|
|
|