The Tool Acked the S5F1 Your Host Should Never Have Sent
Alarm integration passes every test and no alarm ever arrives. A real capture: S5F1 sent by the host draws ACKC5 0, and S10F3 draws an abort.
Alarm integration passed every test. Hook up a real tool and not one alarm reaches MES. Open the host log and nothing looks wrong — page after page of S5F1 sent, S5F2 received.
That is the bug. S5F1 is not a message the host sends. The equipment sends it and the host receives it. What passed the tests was the alarm send path, and the receive code has never once run.
The frames below came off a socket opened directly against the simulator's passive listener. The messages the host had no business sending were acknowledged. The messages that are the host's own came back as aborts.
Session first
TX Select.req 00 00 00 0A 00 0B 00 00 00 01 00 00 06 01
RX Select.rsp 00 00 00 0A 00 0B 00 00 00 02 00 00 06 01
SessionID 00 0B is 11. Header byte 3 is 00, so Select Status 0 — accepted (E37). S1F13 has to go first: this listener answers every other stream with SxF0 until it is COMMUNICATING, which is the behaviour worked through in the E30 communication gate.
TX S1F13 W 00 00 00 1A 00 0B 81 0D 00 00 00 00 06 02 01 02 41 07 48 4F 53 54 4D 45 53 41 03 31 2E 30
RX S1F14 00 00 00 37 00 0B 01 0E 00 00 00 00 06 02 01 02 21 01 00 01 02 ...
21 01 00 is COMMFLAG 0. Data messages are handled normally from here.
Stream 5: S5F1 is not the host's
Here is the frame that starts the whole problem. The host sent the alarm report the equipment is supposed to raise.
TX S5F1 W 00 00 00 2C 00 0B 85 01 00 00 00 00 06 03
01 03 21 01 80 B1 04 00 00 13 89 41 15 43 68 61 6D 62 65 72 ...
RX S5F2 00 00 00 0D 00 0B 05 02 00 00 00 00 06 03 21 01 00
The body is L[3] { B[1] 80, U4 5001, A "Chamber pressure high" } — ALCD, ALID, ALTX. ALCD 80 has bit 8 set, so this is an alarm being raised; what that single bit decides is its own article.
Back came S5F2, body 21 01 00, ACKC5 0. That means received and accepted. Not one byte of it objects to the direction.
The message the host actually owns in stream 5 is S5F3, a request to switch one alarm on or off.
TX S5F3 W 00 00 00 15 00 0B 85 03 00 00 00 00 06 04 01 02 21 01 00 B1 04 00 00 13 89
RX S5F4 00 00 00 0D 00 0B 05 04 00 00 00 00 06 04 21 01 00
ACKC5 0 again. The two reply bodies are byte-for-byte identical, which means there is no way to tell a legitimate request from a reversed one by looking at the answer.
Meanwhile another message that is genuinely the host's is not handled at all.
TX S5F5 W 00 00 00 0C 00 0B 85 05 00 00 00 00 06 05 01 00
RX S5F0 00 00 00 0A 00 0B 05 00 00 00 00 00 06 05
Header byte 2 is 05 (W-bit 0, Stream 5) and byte 3 is 00. Function 0 — an abort. A bodyless 14-byte frame with the SystemBytes carried straight back. Reading Function 0 as a failure signal is covered in the stream 9 article.
Stream 10 makes it plainer
Terminal services has the same trap, and here the split is sharper.
TX S10F1 W 00 00 00 21 00 0B 8A 01 00 00 00 00 06 06 01 02 B1 04 00 00 00 01 41 0D 4F 50 45 ...
RX S10F2 00 00 00 0D 00 0B 0A 02 00 00 00 00 06 06 21 01 00
S10F1 is how an operator standing at the tool pushes text up to the host. The host sent it and got S10F2, ACKC10 0.
The one a host actually wants is the other direction — put characters on the tool's screen.
TX S10F3 W 00 00 00 22 00 0B 8A 03 00 00 00 00 06 07 01 02 B1 04 00 00 00 01 41 0E 4C 4F 54 ...
RX S10F0 00 00 00 0A 00 0B 0A 00 00 00 00 00 06 07
TX S10F5 W 00 00 00 1C 00 0B 8A 05 00 00 00 00 06 08 01 02 B1 04 00 00 00 01 01 01 41 06 4C 49 ...
RX S10F0 00 00 00 0A 00 0B 0A 00 00 00 00 00 06 08
Both abort. The only stream 10 message this listener implements is the one a host has no reason to send.
Direction is part of the message definition
E5 fixes which side sends each Function along with the Function itself. Matching the Stream and Function numbers is not enough. The table below is copied from the E5 definitions and is not what this capture proves — the capture proves only what this listener answered each frame with.
| Message | Sent by | This listener's answer |
|---|---|---|
| S5F1 alarm report | equipment | S5F2, ACKC5 0 |
| S5F3 enable/disable alarm | host | S5F4, ACKC5 0 |
| S5F5 list alarms request | host | S5F0 |
| S10F1 terminal request | equipment | S10F2, ACKC10 0 |
| S10F3 terminal display, single block | host | S10F0 |
| S10F5 terminal display, multi-block | host | S10F0 |
E30's alarm management has the equipment raise S5F1. A line in host code that calls send() on an S5F1 is not a feature, it is a defect.
Why this passes integration testing
A permissive equipment simulator does not check direction. It looks at the Stream/Function pair, finds the secondary it is configured to return, and sends it. A primary pushed the wrong way gets a green light.
This is where the days go. Tests all pass, alarms never arrive on the floor, so the tool vendor gets called — and the vendor's log has no record of ever sending an S5F1, so both sides are telling the truth. That the host was sending S5F1 stays invisible until somebody puts the two logs side by side.
Blocking it on the driver side is cheap. Put a direction whitelist in the send function. Hard-code the (Stream, Function) pairs a host may originate and throw before the write hits the socket if the pair is not on the list. For GEM scope that table is short.
What I did not verify
The direction column follows the E5 message definitions; I did not read it against the clause numbering of a specific revision. If you have to cite clauses in a document, check the copy on your desk. No ACKC5 or ACKC10 value other than 0 was observed in this capture either — every acknowledgement that came back was zero.
And this is one listener's behaviour. Equipment exists that checks direction and rejects with S9F5 or SxF0. Either way the host is still sending the message the wrong way, and if the permissive side is the only place you tested, the floor is where you find out.
Every frame above was exchanged over a socket opened against the passive listener in the public SECS/GEM simulator. Fire one S10F3 at it from your own driver and you will see straight away how your host stack records an S10F0. That is the part worth finding out.