Files
secs-gem/docs/30_repository_tour.md
T
raphael af1a159c59 docs: bring the documentation up to the daemon/client era
A large gap had opened between the docs and the code: the README and
INTEGRATION guide did not mention the gRPC daemon or the Python client at
all (the entire vendor surface), ARCHITECTURE still described secs_server
as the ~1200-line canonical wiring example (it is a ~110-line thin main
over EquipmentRuntime), and test counts across six files were stale
(445/2753 -> 473/3087 core + the separate 125-assertion daemon suite).

- README: new "Integrating your tool (pick a tier)" section — Python
  client / any-language gRPC / embedded C++ — plus daemon tests and
  tools/run_interop.sh in the Testing section.
- ARCHITECTURE: layer diagram gains the vendor-surface and
  EquipmentRuntime/default_handlers tiers; stale wiring row fixed.
- INTEGRATION: three-tier chooser up front (this guide = the C++ tier).
- ch30 tour: secs_gemd + secs_gemd_tests in the binaries table.
- ch31: example alarm used a nonexistent `alcd:` field with bit 7 set
  (which the validator forbids) -> real `category:`/`name:` fields, and
  the roles: block documented.
- ch35: handler-location note now points at default_handlers.cpp's 15
  per-capability register_* functions.
- ch40: built-artifacts list + sample output counts.
- ch50: secsgem::gem runtime/default_handlers/handler_slot/name_index
  includes + new secsgem::daemon namespace section.
- PROOFS: test-count table gains the runtime/handlers/daemon row so the
  tally adds up; daemon suite noted. VERIFICATION/COMPLIANCE counts.
- interop/README: the one-command runner + the two daemon-track harnesses
  (daemon_interop, pyclient_interop).

Audited via a docs-vs-code sweep (the audit itself under-reported: it
validated counts textually; reality was 473/3087).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 23:18:31 +02:00

234 lines
11 KiB
Markdown
Raw 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.
# 30 — Repository tour
← [19 E42 + E148 + S9 — Misc](19_e42_e148_s9_misc.md) | [Back to index](00_index.md) | Next: [31 Spec-as-data + codegen](31_spec_as_data_and_codegen.md) →
You've seen what every SEMI standard *does*. Now we shift to how
this **codebase** is laid out. This chapter answers: when you
`git clone` this repo, what are you looking at?
The repo is small — about 15 k lines of C++ + tests + tooling. It
fits in your head with a little patience. By the end of this
chapter you'll know:
- What each top-level directory contains.
- Which binaries get built.
- The dependency graph between modules.
- How the build system finds and links them.
---
## Top-level layout
```
secs-gem/
├── README.md One-page project summary.
├── LICENSE Proprietary terms.
├── CMakeLists.txt Build config (CMake 3.16+, single file).
├── Dockerfile Ubuntu 24.04 + g++-13 + libasio + yaml-cpp.
├── docker-compose.yml Multi-container demo wiring.
├── .gitea/workflows/ci.yml CI pipeline.
├── include/secsgem/ Public headers. All API here.
│ ├── secs2/ E5 codec + SML.
│ ├── hsms/ E37 transport (TCP + framing).
│ ├── secsi/ E4 transport (FSM + TCP tunnel).
│ ├── config/ YAML loader + multi-error validator.
│ ├── metrics/ Prometheus exporter.
│ ├── endpoint.hpp asio::ip::tcp::endpoint factory.
│ └── gem/ E30 + every GEM 300 standard.
│ ├── store/ Per-domain bundles (SVIDs, alarms, …).
│ └── *.hpp State machines + composers.
├── src/ Implementations. Mirrors include/.
│ ├── secs2/{codec,sml}.cpp
│ ├── hsms/{header,connection}.cpp
│ ├── secsi/{header,block,protocol,tcp_transport}.cpp
│ ├── config/...
│ ├── gem/...
│ └── endpoint.cpp
├── apps/ Standalone binaries.
│ ├── secs_server.cpp Passive equipment (demo + integration target).
│ ├── secs_client.cpp Active host driving the demo flow.
│ ├── secs_conformance.cpp 47-check wire-level conformance harness.
│ ├── secs_interop_probe.cpp Probe against secsgem-py passive equip.
│ ├── secs_bench.cpp Throughput / latency / memory bench.
│ ├── fuzz_secs2_decode.cpp libFuzzer harness for secs2::decode.
│ └── fuzz_sml_parse.cpp libFuzzer harness for try_parse_sml.
├── tests/ doctest unit + integration tests.
│ └── test_*.cpp 55 files, 473 cases, 3087 assertions.
├── data/ YAML configs (the spec-as-data).
│ ├── messages.yaml SECS-II message catalog (164 msgs).
│ ├── control_state.yaml E30 §6.2 transition table.
│ ├── process_job_state.yaml E40 transition table.
│ ├── control_job_state.yaml E94 transition table.
│ └── equipment.yaml Demo SVIDs/ECIDs/CEIDs/alarms/recipes.
├── tools/ Build-time scripts.
│ └── gen_messages.py Codegen: messages.yaml → messages.hpp.
├── interop/ External-validator harnesses.
│ ├── README.md Harness-by-harness detail.
│ ├── host_vs_cpp_server.py secsgem-py active host driving us.
│ ├── passive_equipment.py secsgem-py passive equipment for us to drive.
│ ├── raw_gem300_harness.py Raw S3/S14/S16/S12 round-trip.
│ ├── tshark_validate.sh pcap + tshark HSMS dissector check.
│ ├── secs4j_validate.sh secs4java8 (Java) cross-validation.
│ └── secs4j/ Dockerfile + harness for secs4java8.
├── examples/
│ └── pvd_tool/ Worked vendor example: fictional PVD tool.
│ ├── README.md What the example shows.
│ ├── equipment.yaml Realistic SVIDs/ECIDs/CEIDs/alarms/recipes.
│ └── main.cpp Sensor sim, recipe runner, alarm monitor.
└── docs/ This guide + reference docs.
├── 00_index.md The series TOC.
├── 0151_*.md Tutorial chapters.
├── ARCHITECTURE.md One-page architecture overview.
├── COMPLIANCE.md Per-capability audit.
├── INTEGRATION.md Vendor-side production deploy.
├── PROOFS.md 8 commands proving feature-completeness.
├── VERIFICATION.md External-validator test plan.
├── BENCHMARKS.md Performance envelope.
├── MES_INTEROP.md Commercial-MES day-1 punch list.
├── SECURITY.md nftables / stunnel / minisign configs.
├── GLOSSARY.md SEMI vocabulary cheat sheet.
└── FAQ.md Canonical answers.
```
---
## The dependency graph
```
data/*.yaml
┌─────────────┼──────────────────┐
│ (codegen) │ (runtime load) │
▼ ▼ ▼
generated/messages.hpp config::loader
│ │
└──────────► gem::EquipmentDataModel
│ used by
gem::Router
│ wraps
secs2::Message ◄─── codec / SML
│ over
hsms::Connection / secsi::TcpTransport
TCP socket
```
Read it bottom-up: a TCP socket carries bytes; `hsms::Connection`
frames them into `secs2::Message`s; `gem::Router` dispatches by
`(stream, function)` to handlers; handlers read/write
`EquipmentDataModel`; the model composes per-domain stores; the
stores were built from the YAML at startup.
No layer ever calls *up* the graph. `secs2::Item` has no idea
HSMS exists. `hsms::Connection` doesn't know about CEIDs.
`gem::Router` doesn't know whether the bytes came over HSMS or
SECS-I. Strict layering is what keeps the codebase small.
---
## The binaries
Built by [`CMakeLists.txt`](../CMakeLists.txt) (one file, ~250
lines). Each binary lives in `build/` after `cmake --build`.
| Binary | Source | What it does |
|----------------------|-----------------------------------------------------------------|-------------------------------------------------------|
| `secs_server` | [`apps/secs_server.cpp`](../apps/secs_server.cpp) | Passive equipment. Listens on TCP, dispatches via Router. |
| `secs_client` | [`apps/secs_client.cpp`](../apps/secs_client.cpp) | Active host. Drives ~24 transactions in the demo. |
| `secs_conformance` | [`apps/secs_conformance.cpp`](../apps/secs_conformance.cpp) | 47 wire-level conformance checks against a live server. |
| `secs_interop_probe` | [`apps/secs_interop_probe.cpp`](../apps/secs_interop_probe.cpp) | Active host probing a secsgem-py passive equipment. |
| `secs_bench` | [`apps/secs_bench.cpp`](../apps/secs_bench.cpp) | Throughput / latency / memory harness. |
| `secsgem_tests` | All `tests/*.cpp` | The 473-case doctest binary. |
| `secs_gemd` | `apps/secs_gemd.cpp` + `proto/secsgem/v1` | The gRPC daemon: HSMS equipment + name-based tool API. |
| `secs_gemd_tests` | `tests/test_daemon_service.cpp` | In-process gRPC service tests (built when grpc++ is). |
| `fuzz_secs2_decode` | [`apps/fuzz_secs2_decode.cpp`](../apps/fuzz_secs2_decode.cpp) | libFuzzer (clang only, opt-in `-DSECSGEM_FUZZ=ON`). |
| `fuzz_sml_parse` | [`apps/fuzz_sml_parse.cpp`](../apps/fuzz_sml_parse.cpp) | libFuzzer for the SML parser. |
A worked example binary `pvd_tool` (from `examples/pvd_tool/`) is
also built by the same `CMakeLists.txt` when the example is
included.
---
## How the build system finds everything
`CMakeLists.txt` does five things in order:
1. **Pull in dependencies**`find_package(Threads)`,
`find_package(yaml-cpp)`, `FetchContent` for doctest. Standalone
Asio is header-only (no link step).
2. **Run codegen** — invokes `tools/gen_messages.py` to turn
`data/messages.yaml` into `build/generated/secsgem/gem/messages.hpp`.
Listed as a custom command so it re-runs when `messages.yaml`
changes.
3. **Build the library**`add_library(secsgem ...)` with every
source under `src/` plus the generated header.
4. **Build the apps** — one `add_executable` per `apps/*.cpp`,
each linking against `secsgem`.
5. **Build the tests**`add_executable(secsgem_tests ...)` with
every `tests/*.cpp`, linked against doctest + `secsgem`.
Build flags:
- **`-DSECSGEM_TSAN=ON`** — adds `-fsanitize=thread` to a
separate build dir. CI runs this lane.
- **`-DSECSGEM_FUZZ=ON`** — requires clang; adds libFuzzer + ASan +
UBSan; builds the two fuzz harnesses.
Everything else (Release / Debug, parallelism, output dirs) is
standard CMake.
---
## Test layout
50 test files; 473 test cases; 3 087 assertions. One file per
concern. Naming is `test_<thing>.cpp` consistently:
- `test_secs2.cpp`, `test_e5_kat.cpp`, `test_sml.cpp`,
`test_messages.cpp` — codec.
- `test_hsms*.cpp` (5 files), `test_secsi*.cpp` (3 files) — transport.
- `test_control_state.cpp`, `test_communication_state.cpp`,
`test_data_model.cpp`, `test_host_handler.cpp`, `test_loader.cpp`,
`test_config_validate.cpp` — E30.
- `test_process_jobs.cpp`, `test_control_jobs.cpp`,
`test_carriers.cpp`, `test_substrates.cpp`, `test_ept.cpp`,
`test_modules.cpp`, `test_cem_objects.cpp`, `test_e84*.cpp`,
`test_e42_formatted_pp.cpp` — GEM 300.
- `test_*_persistence.cpp` (4) — file-backed journal.
- `test_robustness_fuzz.cpp` — randomized property test.
- `test_thread_safety.cpp` — TSan-validated single-threaded contract.
- `test_metrics_prometheus.cpp` — Prometheus exporter.
- `test_wire_ceid_emission.cpp` — CEID firings observed on a real socket.
- `test_live_gem300.cpp`, `test_gem300_scenario.cpp` — multi-FSM cascades.
Full per-standard breakdown:
[`docs/PROOFS.md`](PROOFS.md) "Per-standard test coverage" table.
---
## Where to go next
Now that you know what's where, the next chapter explains the
*philosophy* that makes the codebase this small: the **spec-as-data**
principle, and how the YAML files + codegen + runtime loader work
together so adding a new SVID / state / message rarely requires C++.
Next: [→ 31 Spec-as-data + codegen](31_spec_as_data_and_codegen.md)