docs: streamline tone across reference docs
Tone pass across the non-tutorial markdown — README, PROOFS,
ARCHITECTURE, BENCHMARKS, COMPLIANCE, FAQ, MES_INTEROP, SECURITY,
and interop/README. Three patterns came out:
- Bug-history war stories ("Past interop sweeps surfaced…",
"What these harnesses caught: 1. Strict U-width parsing…").
- Chat-with-reader framing ("Don't skip TLS unless…", "Treat as a
punch list", "If you're running in a pod…", "Misconfiguration
incidents drop dramatically").
- Self-referential narration ("we ship", "our codec", "the
codebase's most-tested layer", "three orders of magnitude above
fab load", "the gift that keeps giving").
README also drops the standalone ThreadSanitizer subsection under
Build details (now a single line under the new Testing section).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+19
-28
@@ -13,17 +13,14 @@ what a fab actually runs.** Real MES stacks include:
|
||||
- **Eyelit MES**
|
||||
|
||||
Each one ships its own SECS/GEM stack with its own quirks. This doc
|
||||
is the structured day-1 protocol your integration team runs against
|
||||
**your** MES before connecting a real tool. Treat it as a punch
|
||||
list you tick off; capture wire traces from every step.
|
||||
is the day-1 punch list your integration team runs against **your**
|
||||
MES before connecting a real tool. Capture wire traces from every
|
||||
step.
|
||||
|
||||
> **You can't skip this.** The in-repo `secs_conformance` harness +
|
||||
> the `interop/` secsgem-py cross-validation prove the codebase is
|
||||
> spec-conformant. They cannot prove the *combination* of (this
|
||||
> codebase, your YAML config, your MES's choice of optional
|
||||
> behaviours) works. Every gap surfaced in prior interop sweeps
|
||||
> (S1F23/F24 missing, S10F3 direction confusion, lenient U-width
|
||||
> parsing) was a real bug masked by passing internal tests.
|
||||
The in-repo `secs_conformance` and secsgem-py cross-validation prove
|
||||
the codebase is spec-conformant; they cannot prove the combination
|
||||
of (this codebase, your YAML, your MES's choice of optional
|
||||
behaviours) works.
|
||||
|
||||
## 0. Prerequisites
|
||||
|
||||
@@ -50,13 +47,13 @@ wire-trace timestamp. This is your audit trail for the integration.
|
||||
|
||||
| ID | Test | Expected wire behaviour | Common quirks |
|
||||
|-------|-------------------------------------------------------------------------------------|--------------------------------------------------------------------|-------------------------------------------------------|
|
||||
| T-01 | MES connects (active) → Select.req | Equipment replies Select.rsp(0=Ok); SELECTED on both sides | Some MES default session ID ≠ 0; override in our YAML |
|
||||
| T-01 | MES connects (active) → Select.req | Equipment replies Select.rsp(0=Ok); SELECTED on both sides | Some MES default session ID ≠ 0; override in equipment.yaml |
|
||||
| T-02 | Idle Linktest cycle (5 min observation) | Linktest.req every N seconds from MES; equipment Linktest.rsp | Camstar uses 30s default; FactoryWorks 60s |
|
||||
| T-03 | MES sends Separate.req → graceful close | Equipment closes socket within 1s; no FIN_WAIT2 leak | Some MES expect equipment to close first |
|
||||
| T-04 | Equipment-initiated Linktest (set `Timers::linktest` to 10s) | MES replies Linktest.rsp | Mozaic ignores equipment-initiated; not a bug |
|
||||
| T-05 | MES disconnects TCP without Separate | Equipment detects closed socket, fires `closed_handler("eof")` | Watch for spool starting to fill |
|
||||
| T-06 | MES reconnects after T-05 → S6F25 if spool has content | Equipment auto-emits S6F25 with queued count | MES must support S6F25/F26 — older Wonderware doesn't |
|
||||
| T-07 | T3 violation: MES sends primary but never replies | Equipment fires T3 → auto-emits S9F9 | Confirm S9F9 appears in trace, not just our logs |
|
||||
| T-07 | T3 violation: MES sends primary but never replies | Equipment fires T3 → auto-emits S9F9 | Confirm S9F9 appears in the wire trace, not just logs |
|
||||
| T-08 | T7 violation: MES connects but never sends Select.req | Equipment closes after T7 with reason "T7" | |
|
||||
| T-09 | Oversized frame: MES sends 17 MiB body | Equipment auto-emits S9F11 + closes | If MES doesn't generate this, skip |
|
||||
|
||||
@@ -81,10 +78,10 @@ positions, or uses non-standard RPTID/DATAID widths.
|
||||
|
||||
| ID | Test | Expected | Common quirks |
|
||||
|-------|-------------------------------------------------------------------------------------|---------------------------------------------------------------------|-------------------------------------------------------|
|
||||
| R-01 | MES → S2F33 (define report 1000 over 3 SVIDs) | S2F34(DRACK=0=Accept) | MES may use U1 for RPTID; our parser accepts widths |
|
||||
| R-01 | MES → S2F33 (define report 1000 over 3 SVIDs) | S2F34(DRACK=0=Accept) | MES may use U1 for RPTID; the parser accepts any width|
|
||||
| R-02 | MES → S2F35 (link CEID 300 ↔ RPTID 1000) | S2F36(LRACK=0=Accept) | |
|
||||
| R-03 | MES → S2F37 (enable CEID 300) | S2F38(ERACK=0=Accept) | Some MES send empty CEID list = enable-all |
|
||||
| R-04 | Equipment fires CEID 300 (driven by `model->compose_reports_for(300)`) | MES → S6F12(ACKC6=0) | MES may take >T6 to reply — extend our T6 if needed |
|
||||
| R-04 | Equipment fires CEID 300 (driven by `model->compose_reports_for(300)`) | MES → S6F12(ACKC6=0) | MES may take >T6 to reply — extend T6 if needed |
|
||||
| R-05 | MES → S6F15 (event report request) | Equipment → S6F16 with current values | |
|
||||
| R-06 | MES → S6F19/F21 (individual / annotated report request) | Equipment → S6F20 / S6F22 with current values | |
|
||||
| R-07 | MES → S2F33 with DATAID=0 (clear all reports) | S2F34(DRACK=0); all link bindings flushed | Older MES use S2F33 + empty body; check both forms |
|
||||
@@ -98,7 +95,7 @@ positions, or uses non-standard RPTID/DATAID widths.
|
||||
| A-03 | MES → S5F7 (list enabled) | S5F8 contains ALID 1 | |
|
||||
| A-04 | Equipment sets alarm 1 active | S5F1(ALCD=0x80 \| category) sent to MES → MES S5F2 ack | ALCD bit-7 must be SET, not cleared |
|
||||
| A-05 | Equipment clears alarm 1 | S5F1(ALCD=0x00 \| category) → S5F2 ack | |
|
||||
| A-06 | Equipment fires an alarm while alarm is *disabled* (S5F5 says no) | NO S5F1 wire frame — alarm registry tracks active, dispatcher gates | Easy to get wrong; covered by our tests |
|
||||
| A-06 | Equipment fires an alarm while alarm is *disabled* (S5F5 says no) | NO S5F1 wire frame — alarm registry tracks active, dispatcher gates | |
|
||||
|
||||
## 5. Remote control (E30 §6.15)
|
||||
|
||||
@@ -114,7 +111,7 @@ positions, or uses non-standard RPTID/DATAID widths.
|
||||
| ID | Test | Expected | Common quirks |
|
||||
|-------|-------------------------------------------------------------------------------------|---------------------------------------------------------------------|-------------------------------------------------------|
|
||||
| P-01 | MES → S7F19 (list) | S7F20 with all PPIDs from `recipes:` YAML | |
|
||||
| P-02 | MES → S7F5(PPID=RECIPE-A) | S7F6 with body (ASCII default; binary if recipe is binary) | PPBODY direction was our biggest interop bug |
|
||||
| P-02 | MES → S7F5(PPID=RECIPE-A) | S7F6 with body (ASCII default; binary if recipe is binary) | Check whether your MES expects ASCII or Binary PPBODY |
|
||||
| P-03 | MES → S7F1 → S7F2(Accept) → S7F3 (PP send, new PPID) | S7F4(ACKC7=0); recipe in store | |
|
||||
| P-04 | MES → S7F17 (delete) | S7F18(ACKC7=0); recipe removed | |
|
||||
| P-05 | MES → S7F23 (E42 formatted PP send) | S7F24(ACKC7=0) | Many MES don't speak E42; OK to skip if unused |
|
||||
@@ -123,7 +120,7 @@ positions, or uses non-standard RPTID/DATAID widths.
|
||||
|
||||
| ID | Test | Expected | Common quirks |
|
||||
|-------|-------------------------------------------------------------------------------------|---------------------------------------------------------------------|-------------------------------------------------------|
|
||||
| TS-01 | MES → S10F3 (terminal display single, host→equipment) | S10F4(ACKC10=0) | OUR codebase's bug fix — confirm direction is right |
|
||||
| TS-01 | MES → S10F3 (terminal display single, host→equipment) | S10F4(ACKC10=0) | Confirm direction matches S10F3 host→equipment |
|
||||
| TS-02 | MES → S10F5 (terminal display multi) | S10F6(ACKC10=0) | |
|
||||
| TS-03 | Equipment → S10F1 (operator request, equipment→host) | MES → S10F2(ACKC10=0) | Some MES don't accept S10F1 at all; document policy |
|
||||
|
||||
@@ -152,12 +149,12 @@ positions, or uses non-standard RPTID/DATAID widths.
|
||||
|
||||
| ID | Test | Expected | Common quirks |
|
||||
|-------|-------------------------------------------------------------------------------------|---------------------------------------------------------------------|-------------------------------------------------------|
|
||||
| K-01 | MES → S2F17 (clock read) | S2F18(YYYYMMDDhhmmsscc) — 16 chars | Some MES expect 14-char; we accept both |
|
||||
| K-01 | MES → S2F17 (clock read) | S2F18(YYYYMMDDhhmmsscc) — 16 chars | Some MES expect 14-char; the library accepts both |
|
||||
| K-02 | MES → S2F31 (clock set) | S2F32(TIACK=0=Accept) | Do this in a maintenance window only |
|
||||
| K-03 | MES → S2F29 (EC namelist) | S2F30 with all ECIDs | |
|
||||
| K-04 | MES → S2F13 (EC values) | S2F14 with current values | |
|
||||
| K-05 | MES → S2F15 (EC set within range) | S2F16(EAC=0=Accept); value reflected on next S2F13 | |
|
||||
| K-06 | MES → S2F15 (EC set OUT of range) | S2F16(EAC=1=Denied_OutOfRange) | Triggers our `min_str`/`max_str` range check |
|
||||
| K-06 | MES → S2F15 (EC set OUT of range) | S2F16(EAC=1=Denied_OutOfRange) | Range enforced via `min_str`/`max_str` in equipment.yaml |
|
||||
|
||||
## 11. Soak (4-24 hours)
|
||||
|
||||
@@ -187,8 +184,7 @@ floor:
|
||||
|
||||
## 13. Known MES quirks worth pre-empting
|
||||
|
||||
Compiled from prior fab integrations. Not exhaustive; treat as
|
||||
search-priors when something doesn't behave the way you expect.
|
||||
Quirks observed in prior fab integrations. Not exhaustive.
|
||||
|
||||
- **Camstar Opcenter**: ALCD bit-7 sometimes inverted in their
|
||||
internal model; double-check the alarm wire trace. Linktest at
|
||||
@@ -199,8 +195,8 @@ search-priors when something doesn't behave the way you expect.
|
||||
flow may need explicit S6F23 from MES. Older versions don't
|
||||
speak E42 (S7F23 family) at all.
|
||||
- **Mozaic**: Sometimes sends S2F33 with DATAID encoded as U1
|
||||
where the spec allows U1-U8 — make sure our lenient parser is on
|
||||
(it is by default since the secsgem-py interop work).
|
||||
where the spec allows U1-U8. The library's identifier parser
|
||||
accepts any unsigned width.
|
||||
- **Inficon FabGuard**: Strict on S1F1 — expects MDLN and SOFTREV
|
||||
in ASCII even if the YAML uses U-types for MDLN elsewhere.
|
||||
- **CMNavigo**: Expects equipment to initiate S1F13 within 5 s of
|
||||
@@ -217,8 +213,3 @@ file it via `raphael@maenle.net` with:
|
||||
3. Wire trace excerpt (pcap clip is fine)
|
||||
4. Expected vs actual behaviour
|
||||
5. Your equipment YAML + secs-gem commit SHA
|
||||
|
||||
Bugs surfaced through this process are how we got
|
||||
[S1F23/F24](interop/README.md), [S10F3 direction
|
||||
fix](interop/README.md), and the lenient identifier-width parser.
|
||||
The interop sweep is the gift that keeps giving.
|
||||
|
||||
Reference in New Issue
Block a user