#!/usr/bin/env python3 """A cluster tool that tracks material — E90 wafers + E157 modules. Where mini_tool.py is the bare quickstart, this shows the *material-tracking* side: when the host issues START, we run one wafer through one process module and report every milestone. The daemon turns each report into the standard GEM 300 collection events the host has subscribed to — we never touch a CEID. build/secs_gemd --port 5000 --config-dir data # then run this The host sees, per wafer: SubstrateArrived, module GeneralExecuting, the wafer Acquired, the step running, processing start/stop, step complete, the wafer delivered, and the module back to idle — the complete in-flight trace. """ import time from secsgem_client import Equipment, Milestone, ModuleState MODULE = "CHAMBER-A" def run_wafer(eq: Equipment, wafer: str, carrier: str, slot: int) -> None: """One wafer's journey through one module. Milestone / ModuleState are importable enums — autocomplete-friendly and typo-checked — but plain strings ("ARRIVED", "STEP_EXECUTING") work identically if you prefer.""" eq.report_substrate(wafer, Milestone.ARRIVED, carrier_id=carrier, slot=slot) eq.report_module(MODULE, ModuleState.GENERAL_EXECUTING) # module spins up eq.report_substrate(wafer, Milestone.AT_WORK) # robot loads wafer eq.report_module(MODULE, ModuleState.STEP_EXECUTING) # recipe step begins eq.report_substrate(wafer, Milestone.PROCESSING) eq.fire(eq.names.event.ProcessStarted) time.sleep(1) # ...the actual process... eq.report_substrate(wafer, Milestone.PROCESSED) eq.report_module(MODULE, ModuleState.STEP_COMPLETED) eq.report_substrate(wafer, Milestone.AT_DESTINATION) # robot unloads wafer eq.report_module(MODULE, ModuleState.NOT_EXECUTING) # module idle again def main() -> None: # Context manager: the gRPC channel is closed for you on exit. with Equipment("localhost:50051") as eq: @eq.command # bound by name, validated against the daemon def START(cmd): # the host's S2F41 START lands here carrier = cmd.params.get("CARRIER", "FOUP-1") for slot in range(1, 4): # three wafers out of the FOUP run_wafer(eq, f"{carrier}-W{slot}", carrier, slot) eq.listen() # blocks, dispatching host commands if __name__ == "__main__": main()