Prove a Full GEM Startup Handshake Without Any Equipment
Run all ten SEMI E30 startup steps, S1F13 to S2F31, with one npx command against a simulator: the wire capture, what each PASS proves, a refused Select.
The host code is written. It sends S1F13, receives S6F11, parses it, writes it to the database. The problem is that there is no equipment to point it at, and the vendor FAT is in three weeks.
Testing for the first time at FAT means debugging your host driver with four vendor engineers standing behind you. The failures that show up there are not exotic. COMMACK comes back non-zero, S2F33 is rejected outright, the tool never goes online. All of them are checkable from your desk today.
One command does it.
npx secs-gem-host connect <host>:<port> --report
The target is the HSMS passive listener behind the SECS/GEM simulator; a local instance works the same way, just point at its IP and port. With --report the tool splits SEMI E30 startup into ten steps, prints a PASS/FAIL row for each, and exits 1 if any step fails — which also means you can drop it straight into CI.
The table it actually prints
Output of a run on 2026-09-09, secs-gem-host 0.2.0 against the simulator's listener at 127.0.0.1:5501. Nothing synthesised — every line below has a matching equipment-side RX packet, and the bytes are in the next section.
Step Sent Expect Got Ack Time(ms) Result Note
------------------------------------------------------------------------
1 S1F13 S1F14 S1F14 0 1/45000 PASS VX-9000 Plasma Etcher/SECSGEM-1.4.1
2 S1F17 S1F18 S1F18 0 1/45000 PASS
3 S1F3 S1F4 S1F4 - 1/45000 PASS 4 values
4 S1F11 S1F12 S1F12 - 2/45000 PASS 4 SVIDs
5 S2F29 S2F30 S2F30 - 0/45000 PASS 2 ECs
6 S2F33 S2F34 S2F34 0 3/45000 PASS
7 S2F35 S2F36 S2F36 0 1/45000 PASS
8 S2F37 S2F38 S2F38 0 1/45000 PASS
9 S5F3 S5F4 S5F4 0 5/45000 PASS
10 S2F31 S2F32 S2F32 0 1/45000 PASS
------------------------------------------------------------------------
E30 startup: 10/10 passed
flag: model VIDs not in equipment SVID list: 10001, 10002, 10003, 10004, 10005, 10006, 10007, 10008, 10009, 10010, 10011
The note on step 1, VX-9000 Plasma Etcher/SECSGEM-1.4.1, is the MDLN and SOFTREV the equipment put in its S1F14. The tool does not only check COMMACK; it checks that those two fields are actually populated.
A - in the Ack column is not a failure. S1F4, S1F12 and S2F30 carry no one-byte acknowledgement code at all. Those steps are judged on "is the body a list" and "is the namelist non-empty".
The 45000 in the Time(ms) denominator is the T3 reply timeout. E37's timer table gives T3 a typical value of 45 s — typical, not a mandated default — and this tool takes it as its own default; --t3 changes it. The 1 ms is a loopback number. On real equipment, if that column climbs into seconds, that is itself worth reporting. For what each timer protects, see T3, T5, T6, T7 and T8.
The bytes behind the table
The run above went over a socket, so the equipment kept its own copy of it. What follows is the
simulator's /api/export/pcap output for that run, unedited: 27 packets, both sides. Every host
frame is an equipment-side RX record, which is how I know these are wire bytes and not a
rendering of a message object. The raw export is committed at
content/demos/prove-e30-startup-handshake-in-one-command.json.
One flag added for the capture: --device-id 11, so the SessionID on the wire matches the
simulator's. The bare command works too — this listener does not check the field.
Select, before any of the ten steps
H→E 00 00 00 0A 00 0B 00 00 00 01 00 00 00 01
E→H 00 00 00 0A 00 0B 00 00 00 02 00 00 00 01
Length 00 00 00 0A, SessionID 00 0B (11), Header Bytes 2-3 00 00, PType 00, SType 01
out and 02 back, SystemBytes 00 00 00 01 echoed. On a Select.rsp, Byte 3 is the Select
Status, and 00 is accepted (SEMI E37). Step 1 does not exist until this line.
Step 1 — S1F13 → S1F14, COMMACK 0
H→E 00 00 00 1C 00 0B 81 0D 00 00 00 00 00 02
01 02 41 07 47 45 4D 48 4F 53 54 41 05 30 2E 31 2E 30
E→H 00 00 00 37 00 0B 01 0E 00 00 00 00 00 02
01 02 21 01 00 01 02 41 15 56 58 2D 39 30 30 30 20 50 6C 61 73
6D 61 20 45 74 63 68 65 72 41 0D 53 45 43 53 47 45 4D 2D 31 2E
34 2E 31
Byte 2 81 is the W-bit on stream 1, Byte 3 0D is function 13. The body is
L[2]{A "GEMHOST", A "0.1.0"} — the host's own MDLN and SOFTREV. Back comes
L[2]{B 0, L[2]{A "VX-9000 Plasma Etcher", A "SECSGEM-1.4.1"}}: 21 01 00 is COMMACK 0, and
those two strings are byte-for-byte what the table printed in its Note column.
Step 2 — S1F17 → S1F18, ONLACK 0
H→E 00 00 00 0A 00 0B 81 11 00 00 00 00 00 03
E→H 00 00 00 0D 00 0B 01 12 00 00 00 00 00 03 21 01 00
The request is a 14-byte header with no body at all; Byte 3 11 is 17. The reply carries three
body bytes, 21 01 00 — a bare binary item, not a list the way S1F14's body is. A parser
that unwraps every reply as an L[2] breaks here, on a step that passed.
Step 3 — S1F3 with a zero-length list
H→E 00 00 00 0C 00 0B 81 03 00 00 00 00 00 04 01 00
The whole body is 01 00: an L item of length zero. SEMI E5 reads that as "all SVs", and the
S1F4 that came back holds four (A "Idle", two 8-byte floats, A "ETCH_BASE") — the 4 values
in the table.
Step 6 — the delete-all nobody sees, then DRACK 0
H→E 00 00 00 14 00 0B 82 21 00 00 00 00 00 07 01 02 B1 04 00 00 00 01 01 00
E→H 00 00 00 0D 00 0B 02 22 00 00 00 00 00 07 21 01 00
L[2]{U4 1, L[0]} — DATAID 1 and an empty report list, the message that wipes every report
definition on the equipment. B1 is format code 44 (U4) with one length byte; 01 00 is the
empty list. DRACK 0, and the table never shows this transaction at all.
The definitions follow on SystemBytes 00 00 00 08 as a 136-byte S2F33 carrying VIDs
00 00 27 11 through 00 00 27 1B — 10001 to 10011. The answer:
E→H 00 00 00 0D 00 0B 02 22 00 00 00 00 00 08 21 01 00
DRACK 0 again, for eleven VIDs this equipment never published in step 4. That one byte is the subject of the next section.
Step 9 — ALED and the zero-length ALID list
H→E 00 00 00 11 00 0B 85 03 00 00 00 00 00 0B 01 02 21 01 80 B1 00
L[2]{B 0x80, U4[]}. 21 01 80 is ALED with its high bit set — bit 8 in E5's numbering, the
enable bit. B1 00 is a U4 item of length zero: no ALID at all, which E5 reads as every alarm.
B1 04 00 00 00 00 would have been ALID 0, a different request entirely.
Step 10 — the 16-character timestamp
H→E 00 00 00 1C 00 0B 82 1F 00 00 00 00 00 0C
41 10 32 30 32 36 30 39 30 39 30 36 30 33 31 39 39 31
41 10 is an ASCII item of length 16 and the characters are 2026090906031991 —
YYYYMMDDhhmmsscc. E5's TIME also has a 12-character form; equipment that accepts only one of
the two is a real failure mode, and this run exercises only the 16.
Teardown
H→E 00 00 00 0A 00 0B 00 00 00 09 00 00 00 0D
SType 9, Separate.req, no reply. Communication state drops to NOT COMMUNICATING and the socket closes. The control state does not move — the simulator is still ON-LINE REMOTE from step 2, which is why running this command twice in a row is not the same test twice.
If Select is refused, there is no table
I set the simulator's selectRefuse fault to 3 and ran the identical command.
H→E 00 00 00 0A 00 0B 00 00 00 01 00 00 00 01
E→H 00 00 00 0A 00 0B 00 03 00 02 00 00 00 01
connect failed: Select.rsp status 3
$ echo $?
1
Byte 3 is 03 where the good run had 00; SEMI E37 names 3 Connect Exhaust. Everything else in
the frame is well formed — same length, same SessionID, SystemBytes echoed. The exit code is
still 1, so CI still fails, but no table is printed: the ten rows only exist once the
session has been SELECTED. If something downstream parses that table, give it the empty case.
Which Select Status means what, code by code, is in
an accepted Select and a refused one.
What each of the ten steps proves
| Step | What a PASS proves | What a FAIL usually means in a fab |
|---|---|---|
| 1. S1F13 → S1F14 | COMMACK 0 came back and both MDLN and SOFTREV are populated. SECS-II conversation exists from here on | A non-zero COMMACK means the equipment is not ready to accept this host. No reply at all is an HSMS problem, not a SECS one |
| 2. S1F17 → S1F18 | The equipment accepted remote control | The tool's panel is in LOCAL, or nobody pressed online. The most deflating failure at a FAT |
| 3. S1F3 → S1F4 | Values actually come back. An empty SVID list reads as "all of them" in SEMI E5 | A body that is not a list means this tool never implemented S1F3, or answers with S9F5 |
| 4. S1F11 → S1F12 | The SVID namelist is not empty. Steps 6 to 8 are judged against what arrives here | Equipment that will not publish its own list. Every later report definition then rests on the interface manual alone |
| 5. S2F29 → S2F30 | Equipment constants can be read | If the plan was to manage ECs from the host, that plan is already broken |
| 6. S2F33 → S2F34 | DRACK 0. The report definitions really landed on the equipment | DRACK 4 — one of the VIDs requested does not exist. One wrong ID rejects the whole message |
| 7. S2F35 → S2F36 | LRACK 0. The RPTIDs are linked to CEIDs | In SEMI E5, LRACK 4 means a CEID does not exist, 5 an RPTID does not, 3 that a link for that CEID is already defined — you appended without deleting first |
| 8. S2F37 → S2F38 | ERACK 0. Events are enabled. CEED true with an empty CEID list enables all of them | ERACK 1, a CEID you named does not exist. Skip this step and the definitions are perfect and no S6F11 ever arrives |
| 9. S5F3 → S5F4 | ACKC5 0. Alarm reporting is on. ALED with the high bit set (0x80) plus a zero-length ALID item means all alarms, not ALID 0 | A host that gets everything except alarms is usually a host that never sent this message |
| 10. S2F31 → S2F32 | TIACK 0. The equipment took the host's time. The tool sends the 16-character YYYYMMDDhhmmsscc form | The tool refuses to have its clock set. Decide now whether event timestamps get replaced by host receive time |
Step 2 has one exception. The tool passes ONLACK 2 as well as 0 — re-onlining equipment that is already online is not a failure worth flagging. SEMI E5 defines ONLACK 2 as equipment already on-line, but this run never produced one: the simulator answers every S1F17 with a fixed 21 01 00, so the 2 branch is code that has not executed here.
Step 6 also does something the table does not show. Before sending the definitions, it sends one S2F33 carrying a DATAID and an empty report list — the message that deletes every report definition and event link on the equipment. That is what stops you from stacking your configuration on top of whatever the previous integrator left behind. The ack from that delete-all is noted but never counted toward the result. The full DRACK, LRACK and ERACK code tables are in defining, linking and enabling collection events.
Why a 10/10 run still raises a flag
The line under the table is the most valuable output of the run.
flag: model VIDs not in equipment SVID list: 10001, 10002, ... 10011
Just before step 6, the tool compares the S1F12 namelist it collected in step 4 against the VIDs in its own model. The simulator published four SVIDs; the tool's default model builds reports out of VIDs 10001 through 10011. Not one of them overlaps.
DRACK still came back 0. The simulator accepted VIDs it has never heard of. The score is 10/10 and only the flag records it.
Real equipment answers DRACK 4 here, and that is the failure that eats the most human time during startup. One bad VID rejects the entire S2F33, and a host driver that never inspects DRACK logs "report setup complete" and moves on. The configuration then exists only on the host, never on the tool, and stays that way for weeks.
So the flag is something to resolve, not to ignore. Put the SVIDs, CEIDs and RPTIDs from the equipment's interface manual into one JSON file and pass it with --model.
npx secs-gem-host connect <host>:<port> --report --model ./vx9000.json
Now the SVID list the equipment actually published in step 4 is checked against the IDs you intend to use, in the same run. Clearing that flag before the FAT is cheaper than meeting DRACK 4 for the first time on the day.
What this run does not prove
Worth drawing the line explicitly.
- It is not evidence about the equipment. The simulator returning DRACK 0 for VIDs it does not know is the proof of that. What the table proves is that your host driver builds ten transactions to spec, parses the replies, and actually inspects the ack codes.
- Timing does not reproduce. 1 ms is a loopback figure. Whether MES finishes its database write inside a 45-second T3 on real equipment is a separate question.
- RPTID remapping during recovery, spooling, the real content of S6F11 — only equipment does those.
- A failure before SELECTED is not a startup result at all. The refused-Select run above printed one line and no rows. If Select succeeds and the equipment then goes quiet, that is a different problem — start with selected but not communicating.
The order to work in over three weeks
- Run it against the simulator as-is. Anything short of 10/10 is a host driver bug you have today.
- Build the model JSON from the SVIDs, CEIDs and RPTIDs in the equipment's interface manual.
- Run again with
--modeland confirm the flag is gone. - Wire that command into CI. The exit code is already 1/0, so there is nothing else to write.
- On FAT day, change
<host>:<port>to the real tool and run the same command. That one table becomes the minutes of the meeting.
The tool is on npm as secs-gem-host, source at github.com/jungyoseok/secs-gem-host, MIT. Running secs-gem-host mcp exposes the same engine as an MCP server, so an AI agent can be told "connect to this tool, run startup, and explain the step that failed" and do it directly.
If you need something to connect to, the SECS/GEM simulator is up right now.
npx secs-gem-host connect <host>:<port> --report