docs: customer-ready sweep + README restructure + tshark CI fix
Audit pass over the public-facing surface so a customer can read it end-to-end without tripping on stale numbers or self-contradictions. README + docs accuracy: - Test counts 426 → 445, assertions 2 557 → 2 753 (verified via doctest run); E5 row was missing test_e5_kat (19 cases) - Interop checks 24 → 31, COMPLIANCE.md message count 149 → 164, COMPLIANCE.md "291 cases / 1515 assertions" → 445 / 2 753 - README "60+ test IDs" for MES_INTEROP → actual 59 - PVD example counts: 32 SVIDs/17 CEIDs → 29/21, "~40 handlers in ~200 lines" → 51 in ~460, "~700 lines" → ~1,100; main.cpp header table-of-contents resynced with the actual 7 sections Out-of-scope honesty (COMPLIANCE.md §8 + FAQ.md): - Removed HSMS-GS (was both ✅ implemented in §1 and "out of scope" in §8; INTEGRATION.md §7 documents using it) - Removed multi-block SECS-I (split_message/assemble_message exist with 4 dedicated tests) - Added serial-port wiring as the genuine open ⬜ item — FSM is tested end-to-end over TCP; only the asio serial_port glue is deferred - COMPLIANCE.md intro now lists E42 and notes "E37 (SS + GS)" README restructure: - Moved the 8-command proof table and per-standard test-coverage table to a new PROOFS.md (72 lines) - README now leads with what / Quick start / Documentation map, then a one-paragraph "How it's proved" linking to PROOFS.md - Updated cross-refs in FAQ.md, GLOSSARY.md, VERIFICATION.md, and interop/README.md to point at PROOFS.md CI fix — tshark-dissector job: - interop/tshark_validate.sh hardcoded /app/build/secs_server etc. which only works inside the docker image. Now derives ROOT from the script's own location and accepts BUILD/SERVER/CLIENT/DATA env overrides, so CI can run it from the workspace dir - Verified still passes in docker (69 frames, 0 malformed) .gitignore: - Added build-fuzz/ and build-tsan/ (were showing as untracked) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,6 @@
|
|||||||
build/
|
build/
|
||||||
|
build-fuzz/
|
||||||
|
build-tsan/
|
||||||
*.o
|
*.o
|
||||||
*.obj
|
*.obj
|
||||||
*.a
|
*.a
|
||||||
|
|||||||
+10
-10
@@ -1,12 +1,13 @@
|
|||||||
# SECS/GEM Compliance
|
# SECS/GEM Compliance
|
||||||
|
|
||||||
A per-capability accounting against the foundational SEMI standards
|
A per-capability accounting against the foundational SEMI standards
|
||||||
**E5 (SECS-II)**, **E30 (GEM)**, **E37 (HSMS)**, **E4 (SECS-I)**, plus
|
**E5 (SECS-II)**, **E30 (GEM)**, **E37 (HSMS, SS + GS)**, **E4 (SECS-I)**,
|
||||||
the full GEM 300 stack: **E40** (process jobs), **E94** (control jobs),
|
plus the full GEM 300 stack: **E40** (process jobs), **E42** (formatted
|
||||||
**E87** (carriers), **E90** (substrates), **E116** (equipment
|
process programs), **E94** (control jobs), **E87** (carriers), **E90**
|
||||||
performance tracking), **E120** (common equipment model), **E157**
|
(substrates), **E116** (equipment performance tracking), **E120** (common
|
||||||
(module process tracking), **E84** (parallel I/O), **E148** (time
|
equipment model), **E157** (module process tracking), **E84** (parallel
|
||||||
synchronization), **E39** (object services), plus **E5 §13** wafer maps.
|
I/O), **E148** (time synchronization), **E39** (object services), plus
|
||||||
|
**E5 §13** wafer maps.
|
||||||
|
|
||||||
> **Status.** Every GEM Fundamental and every GEM Additional capability
|
> **Status.** Every GEM Fundamental and every GEM Additional capability
|
||||||
> that E30 binds to a concrete SECS-II message set is implemented, and
|
> that E30 binds to a concrete SECS-II message set is implemented, and
|
||||||
@@ -227,7 +228,7 @@ Legend:
|
|||||||
|
|
||||||
## 5. Message coverage matrix
|
## 5. Message coverage matrix
|
||||||
|
|
||||||
149 SECS-II messages in the catalog, spanning streams 1, 2, 3, 5, 6, 7, 9, 10, 12, 14, 16.
|
164 SECS-II messages in the catalog, spanning streams 1, 2, 3, 5, 6, 7, 9, 10, 12, 14, 16.
|
||||||
|
|
||||||
| Pair | Direction | Status | Notes |
|
| Pair | Direction | Status | Notes |
|
||||||
|------------------|-----------|--------|-------|
|
|------------------|-----------|--------|-------|
|
||||||
@@ -345,7 +346,7 @@ walks ~20 SECS transactions end-to-end:
|
|||||||
23. `S1F15`/`S1F16` Request Offline.
|
23. `S1F15`/`S1F16` Request Offline.
|
||||||
24. `Separate.req` → clean close on both sides.
|
24. `Separate.req` → clean close on both sides.
|
||||||
|
|
||||||
Unit tests: **291 cases / 1515 assertions pass** (`docker compose run --rm tests`).
|
Unit tests: **445 cases / 2753 assertions pass** (`docker compose run --rm tests`).
|
||||||
The suite includes integration tests that drive a real `hsms::Connection`
|
The suite includes integration tests that drive a real `hsms::Connection`
|
||||||
over a loopback socket pair to verify the E37 §7.2 / §7.4 / §7.7
|
over a loopback socket pair to verify the E37 §7.2 / §7.4 / §7.7
|
||||||
edge cases — not just the happy path.
|
edge cases — not just the happy path.
|
||||||
@@ -398,9 +399,8 @@ compliance claim.
|
|||||||
|
|
||||||
| Item | Why it's out of scope |
|
| Item | Why it's out of scope |
|
||||||
|---------------------------------------|----------------------|
|
|---------------------------------------|----------------------|
|
||||||
| Multi-block SECS-I transfers | Multi-block is a **SECS-I** concept for 244-byte serial frames. HSMS allows arbitrarily large bodies (up to the codebase's 16 MiB cap), so multi-block is structurally not needed. E37-based GEM equipment does not require it. |
|
|
||||||
| HSMS-GS (multi-session) | Out of scope — modern HSMS-SS covers virtually all current GEM equipment. |
|
|
||||||
| Equipment Processing States (concrete states) | E30 §6.3 says the specific states are tool-defined. We provide the engine (`ControlTransitionTable` + the YAML loader); equipment vendors load their concrete states (IDLE / SETUP / READY / EXECUTING / PAUSE / …) the same way `data/control_state.yaml` is loaded today. Spec-compliant either way. |
|
| Equipment Processing States (concrete states) | E30 §6.3 says the specific states are tool-defined. We provide the engine (`ControlTransitionTable` + the YAML loader); equipment vendors load their concrete states (IDLE / SETUP / READY / EXECUTING / PAUSE / …) the same way `data/control_state.yaml` is loaded today. Spec-compliant either way. |
|
||||||
|
| Serial-port wiring for SECS-I | The SECS-I FSM (`secsi::Protocol`) is fully implemented and tested end-to-end over the asio TCP transport (`secsi::TcpTransport`). The serial-port driver (asio `serial_port` wrapper around the same FSM) is a few hundred lines of straightforward wiring that hasn't been written yet — most modern GEM equipment runs HSMS. Plug it in by mirroring `TcpTransport`. |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -210,7 +210,7 @@ Both are necessary; neither replaces the other. See VERIFICATION.md.
|
|||||||
## How do I bring this to a customer site?
|
## How do I bring this to a customer site?
|
||||||
|
|
||||||
Run through the five external proofs in
|
Run through the five external proofs in
|
||||||
[README's proof table](README.md#proof-of-feature-completeness) at
|
[the eight commands in PROOFS.md](PROOFS.md) at
|
||||||
the customer's network. Then walk MES_INTEROP.md against their
|
the customer's network. Then walk MES_INTEROP.md against their
|
||||||
actual MES. Then deploy per [SECURITY.md](SECURITY.md) for the
|
actual MES. Then deploy per [SECURITY.md](SECURITY.md) for the
|
||||||
nftables / stunnel / signing setup. Then page on the metrics from
|
nftables / stunnel / signing setup. Then page on the metrics from
|
||||||
@@ -219,7 +219,9 @@ INTEGRATION.md §6.4.
|
|||||||
## What's not implemented?
|
## What's not implemented?
|
||||||
|
|
||||||
See [COMPLIANCE.md](COMPLIANCE.md) §8 ("Explicitly out of scope")
|
See [COMPLIANCE.md](COMPLIANCE.md) §8 ("Explicitly out of scope")
|
||||||
for the honest list. The short version: multi-block SECS-I
|
for the honest list. The short version: tool-specific Equipment
|
||||||
transfers (irrelevant on HSMS), tool-specific Equipment Processing
|
Processing States (the engine is there, vendors plug in their
|
||||||
States (the engine is there, vendors plug in their states), and
|
states), the serial-port driver for SECS-I (the FSM is wired
|
||||||
GEM RTS certification (paid third-party gate, not a code feature).
|
end-to-end over TCP, the asio `serial_port` glue is a deferred
|
||||||
|
follow-up), and GEM RTS certification (paid third-party gate, not
|
||||||
|
a code feature).
|
||||||
|
|||||||
+1
-1
@@ -149,7 +149,7 @@ TA3 (BUSY duration) live alongside the signals — see
|
|||||||
| **The router** | `gem::Router` — `(stream, function) → handler` dispatch table. |
|
| **The router** | `gem::Router` — `(stream, function) → handler` dispatch table. |
|
||||||
| **The codec** | `secs2::encode` / `secs2::decode` for the wire bytes. |
|
| **The codec** | `secs2::encode` / `secs2::decode` for the wire bytes. |
|
||||||
| **The catalog** | `data/messages.yaml` — every SECS-II message we ship, codegen'd to `messages.hpp`. |
|
| **The catalog** | `data/messages.yaml` — every SECS-II message we ship, codegen'd to `messages.hpp`. |
|
||||||
| **The proof** | The 8 commands in [README.md](README.md#proof-of-feature-completeness). |
|
| **The proof** | The 8 commands in [PROOFS.md](PROOFS.md). |
|
||||||
| **The bench** | `apps/secs_bench.cpp` — single-threaded throughput / latency / memory harness. |
|
| **The bench** | `apps/secs_bench.cpp` — single-threaded throughput / latency / memory harness. |
|
||||||
| **The fuzz** | `tests/test_robustness_fuzz.cpp` — randomized property test of the model. |
|
| **The fuzz** | `tests/test_robustness_fuzz.cpp` — randomized property test of the model. |
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
# Proof of feature-completeness
|
||||||
|
|
||||||
|
"Feature-complete" is a claim that the code must prove, not the
|
||||||
|
README. These eight commands are the proof. If they all exit zero
|
||||||
|
on a fresh clone, the codebase implements what
|
||||||
|
[COMPLIANCE.md](COMPLIANCE.md) claims.
|
||||||
|
|
||||||
|
| # | Command | What it proves |
|
||||||
|
|---|--------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------|
|
||||||
|
| 1 | `docker compose run --rm tests` | **445 test cases / 2 753 assertions** pass: every store, FSM, codec, parser, persistence path |
|
||||||
|
| 2 | `docker compose run --rm builder /app/build/secs_conformance --host server --port 5000` | **47 wire-level conformance checks** PASS against a live passive equipment |
|
||||||
|
| 3 | `docker compose run --rm interop python3 /app/interop/host_vs_cpp_server.py --host server` | **31 interop checks** PASS against secsgem-py 0.3.0 (the Python reference impl) |
|
||||||
|
| 4 | `SECSGEM_ROBUSTNESS_SOAK=1 docker compose run --rm builder /app/build/secsgem_tests -tc='*soak*'` | **100 000 random tool operations** execute with all invariants and persistence round-trips holding |
|
||||||
|
| 5 | `docker compose run --rm builder /app/build/secs_server --validate-config --config /app/data/equipment.yaml --state-table /app/data/control_state.yaml --pj-state-table /app/data/process_job_state.yaml --cj-state-table /app/data/control_job_state.yaml` | Every shipped YAML config passes structural + referential validation |
|
||||||
|
| 6 | `docker compose run --rm builder bash /app/interop/tshark_validate.sh` | **69 HSMS frames** dissected by Wireshark's HSMS dissector (independent third codec) with no malformed packets |
|
||||||
|
| 7 | `bash interop/secs4j_validate.sh` | **55 cross-validation checks** PASS against [secs4java8](https://github.com/kenta-shimizu/secs4java8) (independent Java implementation), covering S1/S2/S3/S5/S6/S7/S10/S14/S16, the full E40 PJ body, dynamic event reports + unsolicited S6F11 / S5F1 observation, alarm management, spool, PP management, terminal services, limits, trace, E39, and the GEM 300 streams secsgem-py couldn't easily drive |
|
||||||
|
| 8 | `cmake -B build-fuzz -DSECSGEM_FUZZ=ON && build-fuzz/fuzz_secs2_decode -max_total_time=60` | **200 000+ random inputs** through `secs2::decode`, **1.4 M+** through `try_parse_sml` per 60 s lane, ASan + UBSan + libFuzzer coverage, **0 crashes** |
|
||||||
|
|
||||||
|
Plus, on every push to `main`, [Gitea Actions](.gitea/workflows/ci.yml)
|
||||||
|
runs both a **Release build + full test suite** and a separate
|
||||||
|
**ThreadSanitizer lane** that builds with `-fsanitize=thread` and
|
||||||
|
fails on any race. All 445 cases / 2 753 assertions pass under TSan
|
||||||
|
clean.
|
||||||
|
|
||||||
|
## Per-standard test coverage
|
||||||
|
|
||||||
|
Every claimed standard has dedicated tests. Counts are
|
||||||
|
`grep -c TEST_CASE`; cross-cutting tests (e.g. `test_robustness_fuzz`,
|
||||||
|
`test_gem300_scenario`) exercise multiple standards in concert.
|
||||||
|
|
||||||
|
| Standard | Test files | Cases |
|
||||||
|
|-----------------------------------|-------------------------------------------------------------------------------------------|------:|
|
||||||
|
| **E5** — SECS-II encoding | `test_secs2`, `test_sml`, `test_messages`, `test_e5_kat`, `test_identifier_wildcards`, `test_fuzz` | 139 |
|
||||||
|
| **E5 §13** — exceptions | `test_exceptions`, `test_exception_persistence` | 16 |
|
||||||
|
| **E4** — SECS-I transport | `test_secsi`, `test_secsi_timers`, `test_secsi_tcp` | 27 |
|
||||||
|
| **E37** — HSMS (SS + GS) | `test_hsms`, `test_hsms_connection`, `test_hsms_timers`, `test_hsms_s9`, `test_hsms_gs`, `test_hsms_gs_integration`, `test_s9_fallback`, `test_concurrency` | 34 |
|
||||||
|
| **E30** — GEM core | `test_control_state`, `test_communication_state`, `test_host_handler`, `test_data_model`, `test_loader`, `test_config_validate` | 71 |
|
||||||
|
| **E40** — process jobs | `test_process_jobs` | 21 |
|
||||||
|
| **E94** — control jobs | `test_control_jobs` | 9 |
|
||||||
|
| **E42** — formatted PP | `test_e42_formatted_pp` | 6 |
|
||||||
|
| **E87** — carriers + load ports | `test_carriers`, `test_carrier_state`, `test_carrier_persistence`, `test_e87_wire_scenarios` | 27 |
|
||||||
|
| **E90** — substrate tracking | `test_substrates`, `test_substrate_persistence` | 21 |
|
||||||
|
| **E116** — EPT | `test_ept` | 7 |
|
||||||
|
| **E120 / E39** — common equip / object service | `test_cem_objects` | 3 |
|
||||||
|
| **E157** — module process tracking | `test_modules` | 5 |
|
||||||
|
| **E84** — parallel I/O + timers | `test_e84`, `test_e84_ports`, `test_e84_timers`, `test_e84_asio_timers` | 27 |
|
||||||
|
| Persistence + cross-cutting | `test_job_persistence`, `test_persistence_upgrade`, `test_wire_ceid_emission`, `test_gem300_scenario`, `test_live_gem300`, `test_thread_safety`, `test_metrics_prometheus`, `test_robustness_fuzz` | 32 |
|
||||||
|
| **Total** | | **445** |
|
||||||
|
|
||||||
|
A single command to see this live: `docker compose run --rm builder
|
||||||
|
/app/build/secsgem_tests --list-test-cases | wc -l` (currently 445).
|
||||||
|
|
||||||
|
## What each proof actually demonstrates
|
||||||
|
|
||||||
|
The eight commands above split into four kinds of evidence:
|
||||||
|
|
||||||
|
- **Internal** (#1, #2, #4, #5) — our code testing our code: unit
|
||||||
|
suite, conformance harness, soak property test, config validator.
|
||||||
|
Necessary but not independent.
|
||||||
|
- **External, second implementation** (#3, #7) — round-trip against
|
||||||
|
secsgem-py 0.3.0 (Python) and secs4java8 (Java). Two independent
|
||||||
|
SECS implementations must agree with us on every frame.
|
||||||
|
- **External, third codec** (#6) — Wireshark's HSMS dissector,
|
||||||
|
written by network-protocol authors who don't share code with
|
||||||
|
either of us. Catches framing bugs the implementations might both
|
||||||
|
share.
|
||||||
|
- **External, structural search** (#8) — libFuzzer + ASan + UBSan
|
||||||
|
exploring the decoder and SML parser surface for crashes, memory
|
||||||
|
errors, and UB.
|
||||||
|
|
||||||
|
See [VERIFICATION.md](VERIFICATION.md) for the full test plan and
|
||||||
|
the rationale for each external validator.
|
||||||
@@ -13,67 +13,13 @@ E157**.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Proof of feature-completeness
|
|
||||||
|
|
||||||
"Feature-complete" is a claim that the code must prove, not the
|
|
||||||
README. These five commands are the proof. If they all exit zero on
|
|
||||||
a fresh clone, the codebase implements what
|
|
||||||
[COMPLIANCE.md](COMPLIANCE.md) claims.
|
|
||||||
|
|
||||||
| # | Command | What it proves |
|
|
||||||
|---|--------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------|
|
|
||||||
| 1 | `docker compose run --rm tests` | **426 test cases / 2 557 assertions** pass: every store, FSM, codec, parser, persistence path |
|
|
||||||
| 2 | `docker compose run --rm builder /app/build/secs_conformance --host server --port 5000` | **47 wire-level conformance checks** PASS against a live passive equipment |
|
|
||||||
| 3 | `docker compose run --rm interop python3 /app/interop/host_vs_cpp_server.py --host server` | **24 interop checks** PASS against secsgem-py 0.3.0 (the Python reference impl) |
|
|
||||||
| 4 | `SECSGEM_ROBUSTNESS_SOAK=1 docker compose run --rm builder /app/build/secsgem_tests -tc='*soak*'` | **100 000 random tool operations** execute with all invariants and persistence round-trips holding |
|
|
||||||
| 5 | `docker compose run --rm builder /app/build/secs_server --validate-config --config /app/data/equipment.yaml --state-table /app/data/control_state.yaml --pj-state-table /app/data/process_job_state.yaml --cj-state-table /app/data/control_job_state.yaml` | Every shipped YAML config passes structural + referential validation |
|
|
||||||
| 6 | `docker compose run --rm builder bash /app/interop/tshark_validate.sh` | **69 HSMS frames** dissected by Wireshark's HSMS dissector (independent third codec) with no malformed packets |
|
|
||||||
| 7 | `bash interop/secs4j_validate.sh` | **55 cross-validation checks** PASS against [secs4java8](https://github.com/kenta-shimizu/secs4java8) (independent Java implementation), covering S1/S2/S3/S5/S6/S7/S10/S14/S16, the full E40 PJ body, dynamic event reports + unsolicited S6F11 / S5F1 observation, alarm management, spool, PP management, terminal services, limits, trace, E39, and the GEM 300 streams secsgem-py couldn't easily drive |
|
|
||||||
| 8 | `cmake -B build-fuzz -DSECSGEM_FUZZ=ON && build-fuzz/fuzz_secs2_decode -max_total_time=60` | **~70 000 random inputs** through `secs2::decode`, **~285 000** through `try_parse_sml`, ASan + UBSan + libFuzzer coverage, **0 crashes** |
|
|
||||||
|
|
||||||
Plus, on every push to `main`, [Gitea Actions](.gitea/workflows/ci.yml)
|
|
||||||
runs both a **Release build + full test suite** and a separate
|
|
||||||
**ThreadSanitizer lane** that builds with `-fsanitize=thread` and
|
|
||||||
fails on any race. All 426 cases / 2 557 assertions pass under TSan
|
|
||||||
clean.
|
|
||||||
|
|
||||||
### Per-standard test coverage
|
|
||||||
|
|
||||||
Every claimed standard has dedicated tests. Counts are
|
|
||||||
`grep -c TEST_CASE`; cross-cutting tests (e.g. `test_robustness_fuzz`,
|
|
||||||
`test_gem300_scenario`) exercise multiple standards in concert.
|
|
||||||
|
|
||||||
| Standard | Test files | Cases |
|
|
||||||
|-----------------------------------|-------------------------------------------------------------------------------------------|------:|
|
|
||||||
| **E5** — SECS-II encoding | `test_secs2`, `test_sml`, `test_messages`, `test_identifier_wildcards`, `test_fuzz` | 120 |
|
|
||||||
| **E5 §13** — exceptions | `test_exceptions`, `test_exception_persistence` | 16 |
|
|
||||||
| **E4** — SECS-I transport | `test_secsi`, `test_secsi_timers`, `test_secsi_tcp` | 27 |
|
|
||||||
| **E37** — HSMS (SS + GS) | `test_hsms`, `test_hsms_connection`, `test_hsms_timers`, `test_hsms_s9`, `test_hsms_gs`, `test_hsms_gs_integration`, `test_s9_fallback`, `test_concurrency` | 34 |
|
|
||||||
| **E30** — GEM core | `test_control_state`, `test_communication_state`, `test_host_handler`, `test_data_model`, `test_loader`, `test_config_validate` | 71 |
|
|
||||||
| **E40** — process jobs | `test_process_jobs` | 21 |
|
|
||||||
| **E94** — control jobs | `test_control_jobs` | 9 |
|
|
||||||
| **E42** — formatted PP | `test_e42_formatted_pp` | 6 |
|
|
||||||
| **E87** — carriers + load ports | `test_carriers`, `test_carrier_state`, `test_carrier_persistence`, `test_e87_wire_scenarios` | 27 |
|
|
||||||
| **E90** — substrate tracking | `test_substrates`, `test_substrate_persistence` | 21 |
|
|
||||||
| **E116** — EPT | `test_ept` | 7 |
|
|
||||||
| **E120 / E39** — common equip / object service | `test_cem_objects` | 3 |
|
|
||||||
| **E157** — module process tracking | `test_modules` | 5 |
|
|
||||||
| **E84** — parallel I/O + timers | `test_e84`, `test_e84_ports`, `test_e84_timers`, `test_e84_asio_timers` | 27 |
|
|
||||||
| Persistence + cross-cutting | `test_job_persistence`, `test_persistence_upgrade`, `test_wire_ceid_emission`, `test_gem300_scenario`, `test_live_gem300`, `test_thread_safety`, `test_metrics_prometheus`, `test_robustness_fuzz` | 32 |
|
|
||||||
| **Total** | | **426** |
|
|
||||||
|
|
||||||
A single command to see this live: `docker compose run --rm builder
|
|
||||||
/app/build/secsgem_tests --list-test-cases | wc -l` (currently 426).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Quick start
|
## Quick start
|
||||||
|
|
||||||
Everything runs in Docker — no compiler or build tools on the host.
|
Everything runs in Docker — no compiler or build tools on the host.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker compose run --rm builder # configure + compile
|
docker compose run --rm builder # configure + compile
|
||||||
docker compose run --rm tests # 426 cases / 2 557 assertions
|
docker compose run --rm tests # 445 cases / 2 753 assertions
|
||||||
docker compose up --no-deps server client # live two-container demo
|
docker compose up --no-deps server client # live two-container demo
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -89,9 +35,10 @@ through the data model. Watch the logs interleave.
|
|||||||
| [COMPLIANCE.md](COMPLIANCE.md) | Per-capability audit against every SEMI standard implemented |
|
| [COMPLIANCE.md](COMPLIANCE.md) | Per-capability audit against every SEMI standard implemented |
|
||||||
| [INTEGRATION.md](INTEGRATION.md) | Vendor-side tutorial: YAML → callbacks → production deploy |
|
| [INTEGRATION.md](INTEGRATION.md) | Vendor-side tutorial: YAML → callbacks → production deploy |
|
||||||
| [ARCHITECTURE.md](ARCHITECTURE.md) | How the pieces fit + how to extend (new store / FSM / message) |
|
| [ARCHITECTURE.md](ARCHITECTURE.md) | How the pieces fit + how to extend (new store / FSM / message) |
|
||||||
|
| [PROOFS.md](PROOFS.md) | The eight commands that prove the feature-completeness claim |
|
||||||
| [VERIFICATION.md](VERIFICATION.md) | Test plan for the external validators behind the proof table |
|
| [VERIFICATION.md](VERIFICATION.md) | Test plan for the external validators behind the proof table |
|
||||||
| [BENCHMARKS.md](BENCHMARKS.md) | Performance envelope (throughput, latency, memory) + how to re-run |
|
| [BENCHMARKS.md](BENCHMARKS.md) | Performance envelope (throughput, latency, memory) + how to re-run |
|
||||||
| [MES_INTEROP.md](MES_INTEROP.md) | Day-1 punch list to run against your commercial MES (60+ test IDs) |
|
| [MES_INTEROP.md](MES_INTEROP.md) | Day-1 punch list to run against your commercial MES (59 test IDs) |
|
||||||
| [SECURITY.md](SECURITY.md) | Concrete configs: nftables, stunnel, minisign, SIEM audit-log schema |
|
| [SECURITY.md](SECURITY.md) | Concrete configs: nftables, stunnel, minisign, SIEM audit-log schema |
|
||||||
| [GLOSSARY.md](GLOSSARY.md) | SEMI vocabulary: SVID, CEID, PPID, ALCD, HCACK, T-timers, … |
|
| [GLOSSARY.md](GLOSSARY.md) | SEMI vocabulary: SVID, CEID, PPID, ALCD, HCACK, T-timers, … |
|
||||||
| [FAQ.md](FAQ.md) | Common questions and their canonical answers |
|
| [FAQ.md](FAQ.md) | Common questions and their canonical answers |
|
||||||
@@ -100,6 +47,24 @@ through the data model. Watch the logs interleave.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## How it's proved
|
||||||
|
|
||||||
|
"Feature-complete" is a claim that the code must prove, not the
|
||||||
|
README. On a fresh clone, **eight commands** demonstrate it:
|
||||||
|
unit + integration suite (445 cases / 2 753 assertions), a live
|
||||||
|
conformance harness (47 wire-level checks), interop against
|
||||||
|
secsgem-py (31 checks) and secs4java8 (55 checks), a 100 000-op
|
||||||
|
soak property test, YAML config validation, Wireshark's HSMS
|
||||||
|
dissector on a recorded pcap (69 frames, 0 malformed), and libFuzzer
|
||||||
|
(ASan + UBSan, 0 crashes). Each command, exit code, and the
|
||||||
|
external-vs-internal breakdown live in [PROOFS.md](PROOFS.md).
|
||||||
|
|
||||||
|
CI runs the full suite plus a separate ThreadSanitizer lane on
|
||||||
|
every push to `main` ([Gitea Actions](.gitea/workflows/ci.yml));
|
||||||
|
all 445 cases pass clean under `-fsanitize=thread`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
The project is **spec-as-data**: the SEMI behavioural rules live in
|
The project is **spec-as-data**: the SEMI behavioural rules live in
|
||||||
@@ -142,6 +107,9 @@ YAML; the C++ is the engine that reads them.
|
|||||||
secsgem::metrics Prometheus exporter (Registry + HTTP server).
|
secsgem::metrics Prometheus exporter (Registry + HTTP server).
|
||||||
```
|
```
|
||||||
|
|
||||||
|
See [ARCHITECTURE.md](ARCHITECTURE.md) for how to extend it (new
|
||||||
|
store / FSM / message).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Adding a capability
|
## Adding a capability
|
||||||
|
|||||||
+4
-5
@@ -1,13 +1,12 @@
|
|||||||
# External verification plan
|
# External verification plan
|
||||||
|
|
||||||
The five proofs in [README.md](README.md#proof-of-feature-completeness)
|
The proofs in [PROOFS.md](PROOFS.md) are mostly **us testing us**:
|
||||||
are mostly **us testing us**:
|
|
||||||
|
|
||||||
| Proof | Independence |
|
| Proof | Independence |
|
||||||
|--------------------------------|--------------------------------------------------------------|
|
|--------------------------------|--------------------------------------------------------------|
|
||||||
| 426 unit/integration tests | Internal — our code testing our code |
|
| 445 unit/integration tests | Internal — our code testing our code |
|
||||||
| 47 conformance harness checks | Internal — our host driving our server |
|
| 47 conformance harness checks | Internal — our host driving our server |
|
||||||
| 24 secsgem-py interop checks | **External**, but covers ~15–20 % of the claimed wire surface |
|
| 31 secsgem-py interop checks | **External**, but covers ~15–20 % of the claimed wire surface |
|
||||||
| 100 k random tool ops | Internal — property test of our model |
|
| 100 k random tool ops | Internal — property test of our model |
|
||||||
| YAML validation | Internal — our validator on our YAML |
|
| YAML validation | Internal — our validator on our YAML |
|
||||||
|
|
||||||
@@ -273,7 +272,7 @@ After all four:
|
|||||||
|
|
||||||
| Proof channel | Independence |
|
| Proof channel | Independence |
|
||||||
|--------------------------------|------------------------------------------------------|
|
|--------------------------------|------------------------------------------------------|
|
||||||
| 426 unit/integration tests | Internal |
|
| 445 unit/integration tests | Internal |
|
||||||
| 47 conformance harness checks | Internal |
|
| 47 conformance harness checks | Internal |
|
||||||
| **SEMI E5 KAT** | **External — standards body's own encoding rules** |
|
| **SEMI E5 KAT** | **External — standards body's own encoding rules** |
|
||||||
| **tshark dissector** | **External — independent network-protocol authors** |
|
| **tshark dissector** | **External — independent network-protocol authors** |
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
A fictional Physical Vapor Deposition tool, end-to-end. This is what
|
A fictional Physical Vapor Deposition tool, end-to-end. This is what
|
||||||
a real tool integrator's deployment looks like:
|
a real tool integrator's deployment looks like:
|
||||||
|
|
||||||
- `equipment.yaml` — the tool's data dictionary (32 SVIDs, 5 DVIDs,
|
- `equipment.yaml` — the tool's data dictionary (29 SVIDs, 5 DVIDs,
|
||||||
7 ECIDs, 17 CEIDs, 12 alarms, 3 recipes, 9 host commands)
|
7 ECIDs, 21 CEIDs, 12 alarms, 3 recipes, 9 host commands)
|
||||||
- `main.cpp` — the vendor application: sensor simulator, recipe
|
- `main.cpp` — the vendor application: sensor simulator, recipe
|
||||||
runner, alarm threshold monitor, EPT cycling, metrics exporter,
|
runner, alarm threshold monitor, EPT cycling, metrics exporter,
|
||||||
Router handlers wiring it all to the wire.
|
Router handlers wiring it all to the wire.
|
||||||
@@ -21,9 +21,8 @@ customize. They're written to be a template, not an abstract demo.
|
|||||||
| §3 Recipe runner | Driving a PJ through SettingUp → Processing → ProcessComplete by walking the recipe body, with per-step CEID emission |
|
| §3 Recipe runner | Driving a PJ through SettingUp → Processing → ProcessComplete by walking the recipe body, with per-step CEID emission |
|
||||||
| §4 Alarm threshold monitor | Continuous threshold-based alarm logic (chamber pressure, cleaning interval) with set/clear emission |
|
| §4 Alarm threshold monitor | Continuous threshold-based alarm logic (chamber pressure, cleaning interval) with set/clear emission |
|
||||||
| §5 EPT cycling | E116 state transitions driven by PJ state + safety alarms |
|
| §5 EPT cycling | E116 state transitions driven by PJ state + safety alarms |
|
||||||
| §6 Metrics | Prometheus exporter on `:9090` with per-CEID counters and gauge updates |
|
| §6 Router handlers | Every SECS/GEM message a host might send to a PVD tool, 51 handlers in ~460 lines |
|
||||||
| §7 Router handlers | Every SECS/GEM message a host might send to a PVD tool, ~40 handlers in ~200 lines |
|
| §7 main() | Loading YAML → validating → composing → running, including the Prometheus exporter on `:9090` (§7.3) |
|
||||||
| §8 main() | Loading YAML → validating → composing → running |
|
|
||||||
|
|
||||||
## Running it
|
## Running it
|
||||||
|
|
||||||
@@ -138,7 +137,7 @@ from `main.cpp` directly — each section is independently usable.
|
|||||||
defined and you'll need them all in production.
|
defined and you'll need them all in production.
|
||||||
|
|
||||||
That's it. No framework, no DI container, no abstract base
|
That's it. No framework, no DI container, no abstract base
|
||||||
classes. ~700 lines of vendor code on top of the library.
|
classes. ~1,100 lines of vendor code on top of the library.
|
||||||
|
|
||||||
## Cross-references
|
## Cross-references
|
||||||
|
|
||||||
|
|||||||
@@ -10,10 +10,10 @@
|
|||||||
// §3. Recipe runner (drives PJ FSM through Processing → Complete)
|
// §3. Recipe runner (drives PJ FSM through Processing → Complete)
|
||||||
// §4. Alarm threshold monitor (pressure-based)
|
// §4. Alarm threshold monitor (pressure-based)
|
||||||
// §5. EPT cycling (Standby ↔ Productive ↔ UnscheduledDowntime)
|
// §5. EPT cycling (Standby ↔ Productive ↔ UnscheduledDowntime)
|
||||||
// §6. Metrics (Prometheus exporter on :9090)
|
// §6. Router handlers (the minimum set; mostly copied from
|
||||||
// §7. Router handlers (the minimum set; mostly copied from
|
|
||||||
// apps/secs_server.cpp which has the full catalogue)
|
// apps/secs_server.cpp which has the full catalogue)
|
||||||
// §8. main() — wires everything together
|
// §7. main() — wires everything together, including the
|
||||||
|
// Prometheus metrics exporter on :9090 (§7.3)
|
||||||
//
|
//
|
||||||
// This file is deliberately self-contained — no pvd_internal/ helper
|
// This file is deliberately self-contained — no pvd_internal/ helper
|
||||||
// headers, no factored-out "framework." Customers should be able to
|
// headers, no factored-out "framework." Customers should be able to
|
||||||
|
|||||||
+2
-2
@@ -125,8 +125,8 @@ is consistent:
|
|||||||
non-Python validators).
|
non-Python validators).
|
||||||
2. Wired into `.gitea/workflows/ci.yml` as a separate job.
|
2. Wired into `.gitea/workflows/ci.yml` as a separate job.
|
||||||
3. Listed in this README's table + in `../VERIFICATION.md`.
|
3. Listed in this README's table + in `../VERIFICATION.md`.
|
||||||
4. Surfaced in the README's proof-of-feature-completeness table if
|
4. Surfaced in [`../PROOFS.md`](../PROOFS.md) if it adds a
|
||||||
it adds a meaningful new dimension.
|
meaningful new dimension.
|
||||||
|
|
||||||
Bug reports from a new validator → file at
|
Bug reports from a new validator → file at
|
||||||
`raphael@maenle.net` with the wire trace, the validator's output,
|
`raphael@maenle.net` with the wire trace, the validator's output,
|
||||||
|
|||||||
@@ -9,7 +9,10 @@
|
|||||||
# both our internal tests and the secsgem-py interop path.
|
# both our internal tests and the secsgem-py interop path.
|
||||||
#
|
#
|
||||||
# Usage: tshark_validate.sh [--port PORT]
|
# Usage: tshark_validate.sh [--port PORT]
|
||||||
# Run inside the builder image (which has tshark + tcpdump).
|
# Run inside the builder image (which has tshark + tcpdump), or on
|
||||||
|
# any host with tshark + tcpdump installed. Resolves $ROOT from the
|
||||||
|
# script's own location, so /app/... and any out-of-source checkout
|
||||||
|
# both work without env overrides.
|
||||||
#
|
#
|
||||||
# Exit codes:
|
# Exit codes:
|
||||||
# 0 — tshark dissected every HSMS frame without errors
|
# 0 — tshark dissected every HSMS frame without errors
|
||||||
@@ -17,9 +20,12 @@
|
|||||||
# 2 — script error (binary missing, capture failed, etc.)
|
# 2 — script error (binary missing, capture failed, etc.)
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
||||||
PORT=${PORT:-5099}
|
PORT=${PORT:-5099}
|
||||||
SERVER=/app/build/secs_server
|
BUILD=${BUILD:-$ROOT/build}
|
||||||
CLIENT=/app/build/secs_client
|
SERVER=${SERVER:-$BUILD/secs_server}
|
||||||
|
CLIENT=${CLIENT:-$BUILD/secs_client}
|
||||||
|
DATA=${DATA:-$ROOT/data}
|
||||||
[ -x "$SERVER" ] || { echo "FAIL: $SERVER missing — rebuild first"; exit 2; }
|
[ -x "$SERVER" ] || { echo "FAIL: $SERVER missing — rebuild first"; exit 2; }
|
||||||
[ -x "$CLIENT" ] || { echo "FAIL: $CLIENT missing — rebuild first"; exit 2; }
|
[ -x "$CLIENT" ] || { echo "FAIL: $CLIENT missing — rebuild first"; exit 2; }
|
||||||
|
|
||||||
@@ -44,10 +50,10 @@ sleep 0.5
|
|||||||
|
|
||||||
echo "starting secs_server on 127.0.0.1:$PORT"
|
echo "starting secs_server on 127.0.0.1:$PORT"
|
||||||
$SERVER --port "$PORT" --device 0 \
|
$SERVER --port "$PORT" --device 0 \
|
||||||
--config /app/data/equipment.yaml \
|
--config "$DATA/equipment.yaml" \
|
||||||
--state-table /app/data/control_state.yaml \
|
--state-table "$DATA/control_state.yaml" \
|
||||||
--pj-state-table /app/data/process_job_state.yaml \
|
--pj-state-table "$DATA/process_job_state.yaml" \
|
||||||
--cj-state-table /app/data/control_job_state.yaml \
|
--cj-state-table "$DATA/control_job_state.yaml" \
|
||||||
> "$SERVER_LOG" 2>&1 &
|
> "$SERVER_LOG" 2>&1 &
|
||||||
SERVER_PID=$!
|
SERVER_PID=$!
|
||||||
sleep 0.5
|
sleep 0.5
|
||||||
|
|||||||
Reference in New Issue
Block a user