Files
secs-gem/data/equipment.yaml
T
raphael 912304966f refactor(gem): decompose default handlers per GEM capability + YAML role bindings
register_default_handlers was a relocated app main(): one 1086-line function,
all-or-nothing. It is now 15 per-capability registration functions along the
lines GEM itself defines (S1F19): identification, equipment constants, clock,
event reports, remote commands, trace/limits, spooling, alarms, exceptions,
material tracking (E90/E116/E157), carriers (E87), recipes, object services
(E39), jobs (E40/E94), terminal services. A sensor-class tool registers three
functions instead of carrying carrier/job handlers it doesn't have;
register_default_handlers composes all 15. Each function derives exactly the
runtime aliases its handlers use (generated programmatically from the moved
bodies with boundary/substitution guards — zero hand-retyping).

Magic constants are gone: the control-state/clock SVIDs (were hardcoded 1/2)
and the CJ Executing/Completed CEIDs (were 400/401) now come from a "roles:"
block in equipment.yaml via EquipmentDescriptor, with historical defaults
when absent, loader parsing, and validation (CEID roles must name declared
events). The coupling is now visible in ONE file instead of silently split
between YAML and C++ — the exact drift class this repo's spec-as-data
philosophy exists to kill.

Tests: capability subsetting, role-driven SVID refresh via S1F3, roles
loader (shipped/custom/absent). Battery: core 473/3087 incl. the 53-handler
conformance sweep, daemon 125/125, live GEM300 demo (client exit 0), daemon
interop 20/20 vs secsgem-py.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 22:44:04 +02:00

105 lines
4.7 KiB
YAML

# Equipment data dictionary loaded by secs_server at startup. Adding a
# capability (SVID, ECID, CEID, alarm, recipe, command) is a YAML edit;
# no code change is required as long as the underlying message handler
# already exists in the C++ Router.
device:
id: 0
model_name: "SECSGEM-SIM"
software_rev: "0.1.0"
equipment_type: "EQUIPMENT" # S1F20 EQPTYP string
# Reported on S1F19 -> S1F20 (Get GEM Compliance). CCODE values follow
# E30 Appendix A; CDESC is implementer-defined. Keep this list honest:
# it should reflect what the equipment actually implements (see
# docs/COMPLIANCE.md for the audit).
capabilities:
- {code: 1, name: "Establish Communications"}
- {code: 2, name: "Dynamic Event Report Configuration"}
- {code: 3, name: "Variable Data Collection"}
- {code: 5, name: "Status Data Collection"}
- {code: 6, name: "Alarm Management"}
- {code: 7, name: "Remote Control"}
- {code: 8, name: "Equipment Constants"}
- {code: 9, name: "Process Program Management"}
- {code: 11, name: "Equipment Terminal Services"}
- {code: 12, name: "Clock"}
- {code: 14, name: "Spooling (partial; S2F43/F44 + S6F23/F24)"}
- {code: 15, name: "Control (operator initiated)"}
# GEM300 extensions (declared so the host can probe with S1F19).
- {code: 40, name: "E40 Process Job Management"}
- {code: 94, name: "E94 Control Job Management"}
# Reported on S1F3 (values) and S1F11 (namelist). `value` is the initial.
# `type` controls the SECS-II Item format.
svids:
- {id: 1, name: ControlState, units: "", type: ASCII, value: ""}
- {id: 2, name: Clock, units: "", type: ASCII, value: ""}
- {id: 3, name: EventsEnabled, units: "", type: BOOLEAN, value: true}
# Reported on S1F21/F22 (DVID namelist). Same shape as SVIDs.
dvids:
- {id: 100, name: WaferCounter, units: "wafer", type: U4, value: 0}
- {id: 101, name: ChamberPressure, units: "Torr", type: F4, value: 0.000001}
# Reported on S2F13 (values) / S2F29 (namelist) / S2F15 (set).
ecids:
- {id: 10, name: TimeFormat, units: "code", type: U4, value: 1, min: "0", max: "1"}
- {id: 11, name: EstablishCommTimeout, units: "sec", type: U4, value: 10, min: "1", max: "60"}
# Reported on S6F11. The host links these to reports via S2F35 and
# enables them via S2F37.
ceids:
- {id: 100, name: ControlStateChanged}
- {id: 200, name: AlarmSetEvent}
- {id: 300, name: ProcessStarted}
- {id: 400, name: ControlJobExecuting} # E94 CJ entered Executing
- {id: 401, name: ControlJobCompleted} # E94 CJ entered Completed
# Reported on S5F5 / S5F1. `category` is the lower-7 of ALCD. `name` is an
# optional local key for the daemon's name-based API (gRPC SetAlarm/ClearAlarm);
# it never goes on the wire — SEMI only defines numeric ALID + freetext text.
alarms:
- {id: 1, name: chiller_temp_high, text: "Chiller Temp High", category: 4}
- {id: 2, name: door_open, text: "Door Open", category: 1}
# Reported on S7F19. Body served by S7F5/F6.
recipes:
- {id: "RECIPE-A", body: "STEP CHAMBER ARGON 30s\nSTEP CHAMBER NITROGEN 60s\nEND"}
- {id: "RECIPE-B", body: "STEP HEATER 800C 120s\nEND"}
# Dispatched by S2F41. `ack` is the HCACK enum value. Optional `emit_ceid`
# fires a CEID after dispatch (e.g. ProcessStarted on START), and optional
# `set_alarm` activates an alarm (e.g. FAULT -> alarm 1). `force_spool`
# (true/false) flips the spool store's test-force flag — for the demo this
# stands in for "the host link has gone down" without actually dropping
# TCP.
host_commands:
- {name: START, ack: Accept, emit_ceid: 300}
- {name: STOP, ack: Accept}
- {name: PAUSE, ack: CannotDoNow}
- {name: FAULT, ack: Accept, set_alarm: 1}
- {name: SPOOL_ON, ack: Accept, force_spool: true}
- {name: SPOOL_OFF, ack: Accept, force_spool: false}
# Spool (E30 §6.22). Streams listed here may be queued by the equipment
# when the host is unreachable; queued messages drain when the host sends
# S6F23 with RSDC=0. max_size caps the in-memory queue (FIFO eviction on
# overflow).
spool:
max_size: 100
spoolable_streams: [5, 6]
# CEID emitted automatically whenever the control state machine transitions
# (i.e. on every change-handler call). Set to null to disable.
emit_on_control_change: 100
# Role bindings: which of the ids declared above the engine's built-in
# behaviours target. Explicit here so the coupling is visible in ONE file
# instead of constants in C++ that silently had to match.
roles:
control_state_svid: 1 # refreshed with the control-state name on S1F3
clock_svid: 2 # refreshed with the clock string on S1F3
cj_executing_ceid: 400 # fired when a control job enters Executing
cj_completed_ceid: 401 # fired when a control job enters Completed