Files
secs-gem/docs/19_e42_e148_s9_misc.md
raphael 40df3067a4 docs: chapters 14–19 — GEM 300 standards (Part 2 complete)
Six more chapters finishing Part 2.  Together with chapters 10–13
they document every SEMI standard this codebase implements.

14 — E40 + E94: process jobs (8-state lifecycle, S16F11/F5/F7/F9
on the wire) and control jobs (CJ wraps PJs with batch policy,
S14F9/S16F27 messages).  Worked cascade showing how CJSTART
propagates through the PJ FSM and triggers S6F11 CEIDs at each
transition.

15 — E87 carriers: three orthogonal sub-machines (CarrierID,
SlotMap, CarrierAccess) per carrier and three more (Transfer,
Reservation, Association) per load port.  S3F17 CarrierAction
strings + CAACK codes, S3F19 SlotMap verify, the 5-state slot
encoding, multi-port concurrency.

16 — E90 + E157: substrate tracking via three orthogonal axes
(STS / SPS / SubstrateIDStatus) and module process tracking
(NotExecuting / GeneralExecuting / StepExecuting / StepCompleted).
End-to-end PVD example showing E40 + E157 + E90 transitions
cascading into CEIDs.

17 — E116 + E120 + E39: equipment performance time-buckets across
six states, common equipment model object hierarchy, S14F1/F3
GetAttr/SetAttr as the uniform wire access for any object type
across multiple standards.

18 — E84 parallel I/O: ten signal lines, the 9-state handshake
FSM, the three TA1/TA2/TA3 timing-critical timers, why a physical
handshake gets modeled in software (testability, timer enforcement,
CEID emission, multi-port concurrency), the pure-FSM + asio-adapter
split.

19 — E42 + E148 + S5F9–F18: formatted recipes (S7F23/F25 typed
PPBODY), time synchronization with 16-char + 14-char accepted on
set, exception recovery as a persistent multi-step host-supervised
FSM (Posted → Recovering → Cleared with abort/retry).  Revisits
the auto-S9 family and contrasts S9 (transport) vs S5F9
(application).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-09 20:14:42 +02:00

265 lines
10 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 19 — E42 + E148 + S9 + exception recovery
← [18 E84 — Parallel I/O handoff](18_e84_parallel_io.md) | [Back to index](00_index.md) | Next: [30 Repository tour](30_repository_tour.md) →
Three remaining standards-shaped concerns to round out Part 2:
- **E42** — Formatted (enhanced) Process Programs.
- **E148** — Time synchronization.
- **S5F9S5F18** — Exception recovery (E5 §13 + GEM Additional).
Each is narrow enough that a half-chapter would do. Together they
round out the GEM 300 picture.
---
## E42 — Formatted Process Programs
### What it is
E30's Process Program Management (chapter 13) covers **unformatted**
recipes: the PPBODY is opaque bytes that only the equipment knows
how to parse. E42 adds **formatted** PPs — the recipe has a typed
SECS-II structure the host can introspect.
In practice, formatted recipes are a fab-internal standard rather
than a SEMI-defined schema. E42 just gives the wire shape; the
fab agrees what the structure means.
### The messages
| S/F | Direction | Purpose |
|-------|-----------|---------------------------------------------------------------|
| S7F23 | H → E | Formatted PP Send. Body: PPID + typed SECS-II body. |
| S7F24 | E → H | ACKC7 reply. |
| S7F25 | H → E | Formatted PP Request. Body: PPID. |
| S7F26 | E → H | Formatted PP Send (back). Body: PPID + typed body. |
Compare to unformatted S7F3 / S7F5: same direction pattern, just a
typed body instead of opaque bytes.
### Implementation
[`RecipeStore`](../include/secsgem/gem/store/recipes.hpp) carries
**both** views per recipe: an unformatted PPBODY (opaque bytes)
and an optional formatted body (a `secs2::Item` tree). S7F3 sends
the unformatted; S7F23 sends the formatted; both are stored side
by side and the host can request either via S7F5 (unformatted) or
S7F25 (formatted).
Tests:
[`tests/test_e42_formatted_pp.cpp`](../tests/test_e42_formatted_pp.cpp)
(6 cases — send formatted, request back, round-trip integrity,
ACKC7 error paths).
### Why both?
Some MES — and some equipment — only speak unformatted PPs.
Coexistence lets a vendor ship one EAP that handles both.
COMPLIANCE §4j has the audit detail.
---
## E148 — Time synchronization
### What it does
In a multi-tool fab, the host and the equipment need a **common
notion of time** for timestamp correlation. If tool A logs an
alarm at 14:32:01 and tool B logs a related alarm at 14:31:58, did
B precede A or did the clocks drift?
E148 defines the wire mechanism for keeping equipment clocks in
sync with a host-authoritative time source — typically NTP behind
the scenes — and lets equipment **report clock quality** so the
host knows how much to trust the timestamps coming off the tool.
### The messages
E148 doesn't add new streams; it specialises the existing E30
clock messages:
| S/F | Direction | Purpose |
|-------|-----------|----------------------------------------------------------|
| S2F17 | H → E | Read clock. |
| S2F18 | E → H | Reply with current time string. |
| S2F31 | H → E | Set clock to specified time string. |
| S2F32 | E → H | TIACK reply. |
The time string is **16 ASCII chars `YYYYMMDDhhmmsscc`** (E148
extended form, including hundredths). 14-char `YYYYMMDDhhmmss`
(without hundredths) is the older E30 form; the codebase **accepts
both on set** but emits 16 chars on read by default. See
[`docs/COMPLIANCE.md`](COMPLIANCE.md) §4g.
### Clock store
[`include/secsgem/gem/store/clock.hpp`](../include/secsgem/gem/store/clock.hpp)
holds the wall-clock plus a **drift / quality** indicator:
- `Drift_ms`: cumulative drift since last set.
- `Quality`: enum from {Authoritative, GoodNTP, FreeRunning,
Unreliable}.
A host can read both via E120/E39 attribute access (chapter 17) or
via DVID exposures (the EAP wires them).
### Why this matters
Without clock sync, **alarm root-cause analysis is impossible**.
SPC charts get the X-axis wrong. Yield correlations across tools
fall apart. Most modern fabs run NTP on every tool's
control-plane host; E148 is the mechanism for *reporting* clock
state, not for synchronising it (that's NTP's job).
---
## Exception recovery — S5F9S5F18
### What it adds beyond base alarms
E5 §13 + E30 Alarm Management (covered in chapter 13) handles
alarms as **set/clear** events: an alarm goes active, the
equipment fires S5F1; later it clears, equipment fires another
S5F1. Simple.
But some alarms aren't simple to clear. A vacuum leak that
required a chamber vent + manual seal replacement can't just
"go away" — the equipment has to run a recovery procedure with
the host's involvement. **S5F9S5F18** is the **exception
recovery** family that handles that.
### The exception lifecycle
Defined in
[`include/secsgem/gem/exception_state.hpp`](../include/secsgem/gem/exception_state.hpp):
```cpp
enum class ExceptionState : uint8_t {
Posted = 0, // S5F9 sent; awaiting host action
Recovering = 1, // S5F13 accepted; recovery in progress
RecoverFailed = 2, // S5F15 reported failure; retry possible
Cleared = 3, // resolved; terminal
};
```
Events: `Created` (NoState → Posted), `Recover` (host's S5F13),
`RecoveryComplete`, `RecoveryFailed`, `RecoveryAbort` (host's
S5F17), `Clear`.
### The messages
| S/F | Direction | Purpose |
|-------|-----------|--------------------------------------------------------------|
| S5F9 | E → H | Exception Post. Equipment-initiated. Body: EXID + EXTYPE + EXMESSAGE + recovery-method list. |
| S5F10 | H → E | Ack. |
| S5F11 | E → H | Exception Clear. Equipment-initiated when condition resolves. |
| S5F12 | H → E | Ack. |
| S5F13 | H → E | Exception Recover. Body: EXID + which recovery method to attempt. |
| S5F14 | E → H | Recovery progress. |
| S5F15 | E → H | Recovery Complete (or Failed). |
| S5F16 | H → E | Ack. |
| S5F17 | H → E | Exception Recover Abort. Cancel an in-progress recovery. |
| S5F18 | E → H | Recovery Aborted reply. |
The flow:
```
1. Vacuum leak detected. EAP calls exceptions.post(EXID=42, recovery=["vent","seal","pump-down"]).
→ ExceptionState: NoState → Posted.
→ S5F9 fires.
2. Host sees S5F9 → operator decides to attempt recovery → host sends S5F13(EXID=42, method="vent").
→ ExceptionState: Posted → Recovering.
3. Recovery in progress. EAP fires S5F14 periodically with progress.
4. EAP completes the venting step. Fires recover_complete event.
→ ExceptionState: Recovering → Cleared.
→ S5F15 fires.
5. Host acknowledges (S5F16). EAP fires S5F11 to confirm the
underlying condition is gone.
```
Or, the abort path:
```
3'. Operator decides recovery isn't working → host sends S5F17.
4'. ExceptionState: Recovering → Posted.
5'. EAP can be re-instructed via another S5F13 with a different
method, or the condition can clear autonomously (Clear event
→ Cleared state).
```
### Code
State machine:
[`include/secsgem/gem/exception_state.hpp`](../include/secsgem/gem/exception_state.hpp).
Store:
[`include/secsgem/gem/store/exceptions.hpp`](../include/secsgem/gem/store/exceptions.hpp)
— persistent, so an exception in flight survives a power cycle.
Tests:
[`tests/test_exceptions.cpp`](../tests/test_exceptions.cpp) (11
cases) + persistence in
[`tests/test_exception_persistence.cpp`](../tests/test_exception_persistence.cpp)
(5 cases).
### Why this is its own family
Two reasons:
1. **State persistence.** Alarms come and go in seconds; exception
recovery can span hours and a few power cycles. The store
journal lets the equipment remember "we were halfway through
recovery method 2 of EXID=42" across restarts.
2. **Multi-step coordination.** Each step (`S5F13``S5F14`
`S5F15`) is a host-supervised transaction. Base alarms can't
express "host, here are three recovery options, pick one."
Exception recovery is an Additional GEM capability — not every MES
asks for it — but the codebase implements it because it's
upstream-absent in `secsgem-py` (see [docs/COMPLIANCE.md](COMPLIANCE.md)
§4k) and because the persistent state machine is a nice example of
the spec-as-data pattern applied to a less-trivial FSM.
---
## The auto-S9 family (revisited)
We covered the S9 wire-error replies in chapter 11. Worth
re-listing here because S9 is part of the **error/exception layer**
even though it's transport-level rather than application-level:
| Function | Trigger |
|----------|----------------------------------------------------------|
| S9F1 | Unrecognized Device ID |
| S9F3 | Unrecognized Stream |
| S9F5 | Unrecognized Function |
| S9F7 | Illegal Data (body failed to decode) |
| S9F9 | Transaction Timer Timeout (T3 expired) |
| S9F11 | Data Too Long (body exceeded configured cap) |
| S9F13 | Conversation Timer Timeout (equipment-internal) |
Implementation:
[`hsms::Connection::emit_s9`](../include/secsgem/hsms/connection.hpp)
called from the connection's framing and routing paths. Tested
across [`tests/test_hsms_s9.cpp`](../tests/test_hsms_s9.cpp) and
[`tests/test_s9_fallback.cpp`](../tests/test_s9_fallback.cpp).
Difference from S5F9: S9 is **transport-level** (the bytes
themselves were wrong); S5F9 is **application-level** (the
equipment can't continue normal operation).
---
## End of Part 2
You now know every SECS/GEM and GEM 300 standard that this
codebase implements. Twelve standards across nine chapters, each
one mapped to its state machine, its messages, its store, and the
tests that hold it down.
Part 3 starts. We turn from "what the spec says" to "how this
codebase implements it" — repository tour, codegen, the data model
structure, transport internals, state-machine composition,
persistence mechanics.
Next: [→ 30 Repository tour](30_repository_tour.md)