Files
secs-gem/clients/python/secsgem_client/_enums.py
T
raphael 8a55137e57 feat(client): typo-safe protocol enums + context manager; add wafer_tool example
Interface cleanup so the report_* family matches the typo-safe ethos of
eq.names instead of leaking raw protobuf errors on a misspelled value.

- Milestone / ModuleState / JobState: importable str-enums (member == its
  wire name, so plain strings still work) — autocomplete + a typo-checked
  happy path. The clean rule: equipment-specific *names* live on eq.names;
  fixed protocol *value-sets* are enums.
- _enum_value(): resolves an enum-or-string arg client-side and, on a bad
  value, raises ValueError with a close-match hint *before* the wire. Wired
  into report_job / report_substrate / report_module / request_control_state
  (all previously raised a raw protobuf ValueError).
- Equipment is now a context manager (with Equipment(...) as eq: ...).
- examples/wafer_tool.py: a cluster tool tracking one wafer through one
  module end-to-end (E90 + E157), showing the enums + context manager.
- tests/test_enums.py: asserts the enums stay in lockstep with the proto and
  that the typo path is helpful. Wired into run_interop.sh (pyclient step).
- Interop drives both the enum and string forms on the wire + the ValueError
  typo path. Docs (ch16/ch42) updated; names-vs-enums rule documented.

All Python unit tests + 25 pyclient interop checks pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:09:48 +02:00

49 lines
1.8 KiB
Python

"""The fixed protocol enums you pass to the report_* / control methods.
Unlike the equipment's *names* (events, alarms, commands — those vary per
tool and live on ``eq.names``), these value sets are fixed by the SEMI
standards, so they ship as importable enums:
from secsgem_client import Equipment, Milestone, ModuleState
eq.report_substrate("WFR-1", Milestone.ARRIVED) # autocomplete + typo-safe
eq.report_substrate("WFR-1", "ARRIVED") # plain string also works
Each member *is* its wire name (``Milestone.ARRIVED == "ARRIVED"``), so the
two forms are interchangeable; the enums simply give you IDE autocomplete and
a typo-checked happy path. A wrong string raises ``ValueError`` with a
close-match suggestion (see ``_client._enum_value``).
"""
from __future__ import annotations
import enum
class Milestone(str, enum.Enum):
"""E90 substrate (wafer) lifecycle milestones — for ``eq.report_substrate``."""
ARRIVED = "ARRIVED" # created at its source carrier slot
AT_WORK = "AT_WORK" # picked up for processing
PROCESSING = "PROCESSING" # recipe step started
PROCESSED = "PROCESSED" # recipe step finished
AT_DESTINATION = "AT_DESTINATION" # returned / deposited
class ModuleState(str, enum.Enum):
"""E157 module execution states — for ``eq.report_module``."""
NOT_EXECUTING = "NOT_EXECUTING" # idle (also resets the module)
GENERAL_EXECUTING = "GENERAL_EXECUTING" # setup / pre- / post-process
STEP_EXECUTING = "STEP_EXECUTING" # actively running a recipe step
STEP_COMPLETED = "STEP_COMPLETED"
class JobState(str, enum.Enum):
"""E40 process-job progress — for ``eq.report_job``."""
SETTING_UP = "SETTING_UP"
PROCESSING = "PROCESSING"
COMPLETE = "COMPLETE"
ABORTED = "ABORTED"