docs: chapters 11–13 — HSMS, SECS-I, GEM
Three more chapters of Part 2: 11 — E37 HSMS. 4-byte length prefix + 10-byte header (R-bit + session id + W-bit + stream + function + PType + SType + system_bytes), the 9 SType control messages, the NOT-SELECTED → SELECTED state machine, T3/T5/T6/T7/T8 with what each one bounds, the auto-S9 paths (S9F1/F3/F5/F7/F9/F11), HSMS-SS vs HSMS-GS, the asio single-threaded contract. 12 — E4 SECS-I. Half-duplex line turnaround (ENQ/EOT/ACK/NAK), the 10-byte block header bit-packing (R-bit / W-bit / E-bit / system bytes), the 244-byte block cap and multi-block split/assemble, the event-driven IO-free FSM with its Action / Event variants, T1/T2/T3/T4 with semantics + defaults, master/slave contention. Notes the deferred asio serial_port adapter; explains why this chapter matters even for HSMS-only readers. 13 — E30 GEM. Disambiguates the three state machines (HSMS transport vs GEM communication vs GEM control), walks the comm-state FSM (DISABLED → WAIT-CRA → COMMUNICATING with T_CRA / T_DELAY) and the control-state FSM (5 states + the YAML transition table). Lists every Fundamental and Additional capability with its messages, code locations, and store assignments. One worked Event-Notification scenario tracing seven on-wire steps to their EquipmentDataModel internals. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,259 @@
|
||||
# 13 — E30: GEM — the behavioural model
|
||||
|
||||
← [12 E4 — SECS-I](12_e4_secs_i.md) | [Back to index](00_index.md) | Next: [14 E40 + E94 — Process and control jobs](14_e40_e94_jobs.md) →
|
||||
|
||||
E5 (chapter 10) is the data encoding. E37 / E4 (chapters 11–12)
|
||||
move the encoded bytes. This chapter is the first one where
|
||||
*behaviour* enters the picture.
|
||||
|
||||
**SEMI E30 — Generic Equipment Model (GEM)**, published 1992,
|
||||
specifies what an equipment must *do* when its host sends specific
|
||||
messages. E5 says how to encode S1F13; E30 says what state must
|
||||
change when an S1F13 arrives, what reply must come back, and under
|
||||
what conditions either side may refuse.
|
||||
|
||||
E30 has two top-level concepts:
|
||||
|
||||
1. **Two state machines** — communication state (above HSMS) and
|
||||
control state (governs who's allowed to issue commands).
|
||||
2. **GEM Capabilities** — Fundamentals (mandatory) and Additionals
|
||||
(optional but de-facto required). Each capability defines its
|
||||
own scenarios + messages.
|
||||
|
||||
By the end of this chapter you'll know both state machines, the
|
||||
14 Fundamentals + Additionals, and where each one lives in code.
|
||||
|
||||
---
|
||||
|
||||
## The two GEM state machines
|
||||
|
||||
GEM has **two** state machines that live on top of HSMS's transport
|
||||
state machine. Read carefully — beginners conflate these all the
|
||||
time:
|
||||
|
||||
| State machine | Lives where | Concerns |
|
||||
|----------------------|---------------------------------------------------|-----------------------------------------------|
|
||||
| **HSMS transport** | `secsgem::hsms::Connection` | NOT-CONNECTED → NOT-SELECTED → SELECTED |
|
||||
| **GEM communication**| `secsgem::gem::CommunicationStateMachine` | DISABLED / WAIT-CRA / WAIT-DELAY / COMMUNICATING |
|
||||
| **GEM control** | `secsgem::gem::ControlStateMachine` | EquipmentOffline / OnlineLocal / OnlineRemote / … |
|
||||
|
||||
All three can be in independent states. `SELECTED` (HSMS) doesn't
|
||||
imply `COMMUNICATING` (GEM-comm); `COMMUNICATING` doesn't imply
|
||||
`OnlineRemote` (control).
|
||||
|
||||
### Communication state (E30 §6.5)
|
||||
|
||||
What it answers: **have host and equipment agreed they can talk to
|
||||
each other at the GEM level?** This is *above* HSMS — even after
|
||||
HSMS is SELECTED, GEM-comm starts at WAIT-CRA and only reaches
|
||||
COMMUNICATING after a successful `S1F13 / S1F14 (COMMACK=Accept)`
|
||||
exchange.
|
||||
|
||||
```
|
||||
wire: S1F13 →
|
||||
DISABLED ─enable──► WAIT-CRA ─────────────► COMMUNICATING
|
||||
│ ◄─ S1F14(Accept)
|
||||
│
|
||||
│ S1F14(Deny) or T_CRA expires
|
||||
▼
|
||||
WAIT-DELAY
|
||||
│ T_DELAY expires
|
||||
▼
|
||||
WAIT-CRA (retry)
|
||||
```
|
||||
|
||||
Two timers, both in `gem::CommunicationStateMachine`:
|
||||
|
||||
- **T_CRA** (default 45 s): how long to wait for the S1F14 reply
|
||||
after sending S1F13.
|
||||
- **T_DELAY** (default 10 s): how long to back off after a
|
||||
rejected S1F14 before retrying.
|
||||
|
||||
Code:
|
||||
[`include/secsgem/gem/communication_state.hpp`](../include/secsgem/gem/communication_state.hpp);
|
||||
tests in
|
||||
[`tests/test_communication_state.cpp`](../tests/test_communication_state.cpp)
|
||||
(12 cases — every transition, every timer expiry).
|
||||
|
||||
The state machine is **IO-free** — it raises actions (send S1F13,
|
||||
arm T_CRA, …) that the caller translates into asio work. This
|
||||
makes it unit-testable without spinning up a TCP socket. Same
|
||||
design pattern as `secsi::Protocol` from chapter 12.
|
||||
|
||||
### Control state (E30 §6.2)
|
||||
|
||||
What it answers: **who's allowed to issue commands right now?**
|
||||
|
||||
Five states:
|
||||
|
||||
| State | Meaning |
|
||||
|------------------|------------------------------------------------------------|
|
||||
| `EquipmentOffline` | Off-network. Both panel and host commands disabled. |
|
||||
| `AttemptOnline` | Transient: equipment is dialing host. Rare. |
|
||||
| `HostOffline` | Host disconnected (or never connected). Operator can act, host cannot. |
|
||||
| `OnlineLocal` | Operator at the local panel has control. Host can read, not act. |
|
||||
| `OnlineRemote` | Host has full control. |
|
||||
|
||||
Defined in
|
||||
[`include/secsgem/gem/control_state.hpp`](../include/secsgem/gem/control_state.hpp).
|
||||
|
||||
Transitions are driven by **events** (operator pressed Online,
|
||||
host sent S1F17, AttemptOnline succeeded or failed, …) and
|
||||
encoded as a transition table loaded from
|
||||
[`data/control_state.yaml`](../data/control_state.yaml):
|
||||
|
||||
```yaml
|
||||
# data/control_state.yaml
|
||||
transitions:
|
||||
- {from: EquipmentOffline, on: operator_switch_online, to: AttemptOnline, then: OnlineRemote}
|
||||
- {from: OnlineRemote, on: host_request_offline, to: HostOffline, ack: Accept}
|
||||
- {from: OnlineLocal, on: host_request_remote, ack: NotAccept}
|
||||
...
|
||||
```
|
||||
|
||||
The table is **pure data**. `ControlTransitionTable` looks up
|
||||
rows; `ControlStateMachine` applies them. No `if/else` ladders
|
||||
embedded in C++.
|
||||
|
||||
```cpp
|
||||
// include/secsgem/gem/control_state.hpp:53
|
||||
struct ControlTransition {
|
||||
ControlState from;
|
||||
ControlEvent on;
|
||||
std::optional<ControlState> to;
|
||||
std::optional<ControlState> then; // chain through AttemptOnline
|
||||
std::optional<uint8_t> ack_code;
|
||||
};
|
||||
```
|
||||
|
||||
This is **spec-as-data** in its purest form: the SEMI standard
|
||||
section 6.2 is one YAML file. Add a state, add a transition, edit
|
||||
the YAML — no recompile, no C++ change. See chapter
|
||||
[31](31_spec_as_data_and_codegen.md) for the wider story.
|
||||
|
||||
Tests: [`tests/test_control_state.cpp`](../tests/test_control_state.cpp)
|
||||
(15 cases — every YAML-defined transition, both ACK codes).
|
||||
|
||||
---
|
||||
|
||||
## GEM Fundamentals (E30 §5.2)
|
||||
|
||||
The **mandatory** capabilities. An equipment that doesn't ship
|
||||
these isn't GEM-compliant, end of story.
|
||||
|
||||
| Fundamental | Messages | Code |
|
||||
|---------------------------------------------|---------------------------------------------------------|---------------------------------------------------------------|
|
||||
| State models | — | `ControlStateMachine`, `CommunicationStateMachine` |
|
||||
| Equipment Processing States | — | `ControlTransitionTable` (vendor supplies concrete states) |
|
||||
| Host-Initiated S1F13/F14 | S1F13 / S1F14 | `gem::CommunicationStateMachine` |
|
||||
| Event Notification | S6F11 / S6F12 | `EventStore` + `EquipmentDataModel::compose_reports_for` |
|
||||
| On-Line Identification | S1F1 / S1F2 | Router handler in `apps/secs_server.cpp` |
|
||||
| Error Messages | S9F1/F3/F5/F7/F9/F11 | `Connection::emit_s9` + `Router::dispatch_with_s9` |
|
||||
| Documentation | S1F19/F20, S1F21/F22, S1F23/F24 | `gem::compliance` / namelist handlers |
|
||||
| Control (Operator-Initiated) | — | `ControlStateMachine::operator_online/offline/local/remote` |
|
||||
|
||||
Full per-capability accounting with status + spec section + code
|
||||
ref: [docs/COMPLIANCE.md](COMPLIANCE.md) §3.
|
||||
|
||||
---
|
||||
|
||||
## GEM Additionals (E30 §5.3)
|
||||
|
||||
The **optional** capabilities — but every commercial MES will
|
||||
require all of them. In practice "Additional" means "optional per
|
||||
the SEMI spec, but mandatory for procurement."
|
||||
|
||||
| Additional | Messages | Code |
|
||||
|---------------------------------------|---------------------------------------------------------------------------|-----------------------------------------------|
|
||||
| Establish Communications | S1F13/F14 | `CommunicationStateMachine` (also in Fundamentals) |
|
||||
| Dynamic Event Report Configuration | S2F33/F34, S2F35/F36, S2F37/F38 | `ReportStore`, `EventStore` |
|
||||
| Variable Data Collection | S1F21/F22 + DVID values via `vid_value` | `DataVariableStore` |
|
||||
| Trace Data Collection | S2F23/F24, S6F1/F2 | `TraceStore` |
|
||||
| Status Data Collection | S1F3/F4, S1F11/F12 | `SvidStore` |
|
||||
| Alarm Management | S5F1/F2, S5F3/F4, S5F5/F6, S5F7/F8 | `AlarmStore`, `AlarmDispatcher` |
|
||||
| Remote Control | S2F41/F42, S2F49/F50, S2F21/F22 | `HostCommandRegistry` |
|
||||
| Equipment Constants | S2F13/F14, S2F15/F16, S2F29/F30 | `EquipmentConstantStore` |
|
||||
| Process Program Management | S7F1–F6, S7F17–F20, S7F23–F26 | `RecipeStore` |
|
||||
| Material Movement | (handled by E40 + E94 + E87 + E90 + E157) | see chapters 14–16 |
|
||||
| Equipment Terminal Services | S10F1/F2, S10F3/F4, S10F5/F6 | `TerminalServiceStore` |
|
||||
| Clock | S2F17/F18, S2F31/F32 | `ClockStore` (+ E148 in chapter 19) |
|
||||
| Limits Monitoring | S2F45/F46, S2F47/F48 | `LimitMonitorStore` |
|
||||
| Spooling | S2F43/F44, S6F23/F24, S6F25/F26 | `SpoolStore` (persistent file-backed journal) |
|
||||
|
||||
Every capability has its **own store** (a namespace bundle of
|
||||
state + behaviour) and its **own Router handlers** for the messages
|
||||
that drive it. Stores compose into `EquipmentDataModel`. Chapter
|
||||
[32](32_stores_and_the_data_model.md) is the deep dive.
|
||||
|
||||
---
|
||||
|
||||
## How a typical scenario lands in code
|
||||
|
||||
Pick **Event Notification** — the canonical GEM scenario:
|
||||
|
||||
```
|
||||
1. Host sends S2F33 (DefineReport): "RPTID 100 = [SVID 1, SVID 5]"
|
||||
2. Equipment stores the definition in ReportStore; replies S2F34(DRACK=0).
|
||||
3. Host sends S2F35 (LinkEvent): "CEID 300 → RPTID 100"
|
||||
4. Equipment stores the link in EventStore; replies S2F36(LRACK=0).
|
||||
5. Host sends S2F37 (EnableEvent CEED=true, CEID=[300])
|
||||
6. Equipment marks CEID 300 enabled in EventStore; replies S2F38(ERACK=0).
|
||||
7. Later: some FSM transition decides to fire CEID 300.
|
||||
compose_reports_for(300) walks EventStore → ReportStore → SvidStore
|
||||
and assembles {RPTID=100, V=[svid1_val, svid5_val]}.
|
||||
8. Equipment emits S6F11 with the assembled body.
|
||||
9. Host replies S6F12(ACKC6=0).
|
||||
```
|
||||
|
||||
Steps 1, 3, 5 are inbound — `gem::Router` dispatches by
|
||||
`(stream, function)` to a registered handler. Steps 2, 4, 6, 8
|
||||
are outbound — the handler or the FSM hands a built `secs2::Message`
|
||||
to the Connection. Step 7 is *internal* — the EquipmentDataModel
|
||||
walks its own stores; nothing on the wire happens until step 8.
|
||||
|
||||
Router and dispatch is in chapter
|
||||
[35](35_state_machines_and_dispatch.md); store internals in
|
||||
chapter [32](32_stores_and_the_data_model.md).
|
||||
|
||||
---
|
||||
|
||||
## The host-side analogue
|
||||
|
||||
Everything above describes the equipment side. The host side has
|
||||
its own E30 state — every Additional capability has a host-side
|
||||
view too (the host can disable an alarm, change a host command,
|
||||
etc.). This codebase implements the host-side as a thin module:
|
||||
|
||||
```cpp
|
||||
// include/secsgem/gem/host_handler.hpp
|
||||
class HostHandler {
|
||||
// Decode equipment-initiated S5F1 / S6F11 / S9Fx.
|
||||
// Maintain the host's view of CEID enables, alarm enables, …
|
||||
};
|
||||
```
|
||||
|
||||
`apps/secs_client.cpp` is the canonical host binary. In the
|
||||
two-container demo it walks ~24 transactions against
|
||||
`apps/secs_server.cpp` — the host side mostly *reads* what the
|
||||
equipment reports and acknowledges. Driving an MES is a much
|
||||
bigger story (see chapter [41](41_integration_hardware_mes_production.md)).
|
||||
|
||||
---
|
||||
|
||||
## Where to go next
|
||||
|
||||
You now have:
|
||||
|
||||
- E5 codec.
|
||||
- E37/E4 transport.
|
||||
- E30 state machines and capabilities.
|
||||
|
||||
That's the complete **base GEM stack**. Modern fab automation
|
||||
needs more — process job lifecycles, carrier management, substrate
|
||||
tracking — and that's what **GEM 300** adds.
|
||||
|
||||
The next six chapters tackle the GEM 300 standards one family at a
|
||||
time. Each one fits on top of E30 in the same way: a state
|
||||
machine + a store + Router handlers + per-CEID emissions.
|
||||
|
||||
Next: [→ 14 E40 + E94 — Process and control jobs](14_e40_e94_jobs.md)
|
||||
Reference in New Issue
Block a user