Files
secs-gem/docs/40_building_running_demo.md
raphael 0355c73211
tests / build-and-test (push) Successful in 2m9s
tests / thread-sanitizer (push) Successful in 2m35s
tests / tshark-dissector (push) Successful in 2m17s
tests / secs4j-interop (push) Successful in 1m6s
tests / libfuzzer (push) Successful in 3m7s
docs: refresh stale file paths after store/ reorg + gen_messages rename
generate_messages.py → gen_messages.py and several gem/ headers moved
under gem/store/ (carrier_store.hpp → store/carriers.hpp, etc.);
e84.hpp split into e84_state.hpp.  The guided-tour chapters still
pointed at the old paths — relink them so the deep-link footnotes
resolve.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-09 23:23:42 +02:00

379 lines
9.4 KiB
Markdown
Raw Permalink 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.
# 40 — Building, running, the demo
← [36 Persistence, validation, metrics](36_persistence_validation_metrics.md) | [Back to index](00_index.md) | Next: [41 Integration: hardware, MES, production](41_integration_hardware_mes_production.md) →
You've read about every layer of the codebase. Now we run it.
This chapter is operational: build the project, start the demo,
walk what each transaction in the two-container flow actually does
and where it lives. By the end you'll have the demo running on
your laptop and you'll know what every log line means.
---
## Prerequisites
Just **Docker**. No host C++ toolchain, no Python deps, nothing
to apt-install. The toolchain image (`Dockerfile`) bundles Ubuntu
24.04 + g++-13 + CMake + Ninja + asio + yaml-cpp + Python 3 +
tshark + tcpdump + clang.
```bash
docker --version
docker compose version
```
If both work, you're set.
---
## Building
```bash
docker compose run --rm builder
```
That:
1. Pulls / builds the toolchain image (first time only, ~3
minutes).
2. Runs `cmake -S /app -B /app/build -G Ninja -DCMAKE_BUILD_TYPE=Release`.
3. Runs `cmake --build /app/build`.
4. Produces every binary under `/app/build/` inside a named Docker
volume.
Subsequent builds are incremental and take ~1030 s.
### What got built
```
build/
├── secs_server passive equipment (the demo target)
├── secs_client active host (drives the demo)
├── secs_conformance 47-check conformance harness
├── secs_interop_probe active host probing secsgem-py equipment
├── secs_bench throughput/latency bench
├── secsgem_tests the 445-case doctest binary
└── pvd_tool worked PVD-tool example
```
Plus the generated `build/generated/secsgem/gem/messages.hpp`
(~3 500 lines, auto-derived from `data/messages.yaml`).
---
## Running the tests
```bash
docker compose run --rm tests
```
Runs `secsgem_tests` end-to-end. Expected output:
```
[doctest] doctest version is "2.4.11"
[doctest] run with "--help" for options
===============================================================================
[doctest] test cases: 445 | 445 passed | 0 failed | 0 skipped
[doctest] assertions: 2753 | 2753 passed | 0 failed |
[doctest] Status: SUCCESS!
```
On a 2024 M-series Mac under Docker Desktop, this takes ~3.5 s.
---
## The two-container demo
```bash
docker compose up --no-deps server client
```
That starts:
- A **`server`** container running `secs_server` on port 5000.
- A **`client`** container running `secs_client` against `server:5000`.
The client drives ~24 SECS transactions through the data model.
Each transaction logs on both sides.
### What each transaction does
Annotated walk through the log output:
#### Communication establishment
```
[host] connecting to server:5000
[equip] accepted connection
[host] sending Select.req
[equip] Select.req received → SELECTED
[host] Select.rsp(Ok) received → SELECTED
```
HSMS SELECT handshake. Both sides now in SELECTED state.
```
[host] sending S1F13 Establish Communications
[equip] S1F13 received
[equip] sending S1F14(COMMACK=Accept, [MDLN, SOFTREV])
[host] S1F14 received → COMMUNICATING
```
E30 §6.5 communication-state transition. Now GEM-level
communication is up.
#### Identification
```
[host] S1F1 Are You There
[equip] S1F2 ["SECS-GEM Demo Equipment", "1.0.0"]
[host] S1F19 GEM Compliance Request
[equip] S1F20 [list of capabilities]
[host] S1F11 SVID Namelist (all)
[equip] S1F12 [SVID 1 "ControlState", SVID 2 "Clock", ...]
[host] S1F21 DVID Namelist (all)
[equip] S1F22 [DVID list]
[host] S1F23 CEID Namelist (all)
[equip] S1F24 [CEID → VID mapping]
```
Host walks the data dictionary.
#### Dynamic event report setup
```
[host] S2F33 DefineReport(RPTID=1, VIDs=[SVID 2])
[equip] S2F34(DRACK=0)
[host] S2F35 LinkEvent(CEID=300 → [RPTID=1])
[equip] S2F36(LRACK=0)
[host] S2F37 EnableEvent(CEED=true, CEIDs=[300])
[equip] S2F38(ERACK=0)
```
The three-message report wiring. CEID 300 now triggers an S6F11
when it fires.
#### Control state + remote command
```
[host] S2F41 RCMD=START
[equip] S2F42(HCACK=Accept)
[equip] HostCommandRegistry dispatched START
[equip] → emit CEID 300
[equip] → compose_reports_for(300) → RPTID 1 = [Clock SV2]
[equip] → fire S6F11
[equip] S6F11(CEID=300, [RPTID=1, [Clock]])
[host] S6F12(ACKC6=0)
```
Host command dispatch + event report emission + acknowledgement.
This is the canonical GEM transaction.
#### Alarms
```
[host] S5F5 List all alarms
[equip] S5F6 [ALID list with ALCD + ALTX]
[host] S5F3 EnableAlarm(ALID=1)
[equip] S5F4(ACKC5=0)
[host] S2F41 RCMD=FAULT
[equip] S2F42(HCACK=Accept)
[equip] → set ALID 1
[equip] → fire S5F1(ALCD=0x84, ALID=1)
[equip] S5F1(...)
[host] S5F2(ACKC5=0)
```
#### Recipes
```
[host] S7F1 PP Load Inquire(PPID="NEW-RECIPE", LENGTH=64)
[equip] S7F2(PPGNT=0=Permit)
[host] S7F3 PP Send(PPID="NEW-RECIPE", PPBODY=<bytes>)
[equip] S7F4(ACKC7=0)
[host] S7F5 PP Request(PPID="NEW-RECIPE")
[equip] S7F6 [PPID, PPBODY]
[host] S7F17 PP Delete(PPIDs=["NEW-RECIPE"])
[equip] S7F18(ACKC7=0)
```
#### Terminal display
```
[host] S10F3 Terminal Display Multi (TID=0, TEXT="hello\nfrom host")
[equip] S10F4(ACKC10=0)
```
#### Clean shutdown
```
[host] S1F15 Request Offline
[equip] S1F16(OFLACK=Accept)
[host] sending Separate.req
[equip] Separate.req received → close
```
Total: 24 transactions exercising S1, S2, S5, S6, S7, S10.
---
## Running the conformance harness
```bash
docker compose up -d server
docker compose run --rm builder /app/build/secs_conformance --host server --port 5000
docker compose down
```
Runs the 47-check conformance harness against the demo server.
Each check covers one E30 / GEM 300 wire-level behaviour:
```
[PASS] E37 §7.2 SELECT handshake
[PASS] E30 §6.5 S1F13/F14 Establish Comms
[PASS] E30 §6.7 S1F1/F2 Are You There
... (43 more)
[PASS] E30 §6.10 S1F19/F20 GEM Compliance
47 / 47 checks passed
```
This is proof #2 in [`docs/PROOFS.md`](PROOFS.md).
---
## Running the interop sweeps
### secsgem-py
```bash
docker compose up -d server
docker compose run --rm interop python3 /app/interop/host_vs_cpp_server.py --host server
docker compose down
```
The Python `secsgem-py` 0.3.0 host drives our equipment. 31 checks
across S1/S2/S5/S6/S7/S10 + unsolicited S6F11 / S5F1.
### secs4java8
```bash
bash interop/secs4j_validate.sh
```
The Java secs4java8 host drives our equipment via a separate
container. 55 checks covering S1/S2/S3/S5/S6/S7/S10/S14/S16
including the GEM 300 streams that secsgem-py couldn't easily
drive.
### tshark dissector
```bash
docker compose run --rm builder bash /app/interop/tshark_validate.sh
```
Captures a pcap of the demo flow, dissects with Wireshark's HSMS
dissector, asserts no malformed packets. 69 frames, 0 errors.
### libFuzzer (60 s, requires clang)
```bash
docker compose run --rm builder bash -c "
cmake -S /app -B /app/build-fuzz -G Ninja -DSECSGEM_FUZZ=ON \
-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++
cmake --build /app/build-fuzz
/app/build-fuzz/fuzz_secs2_decode -max_total_time=60
/app/build-fuzz/fuzz_sml_parse -max_total_time=60
"
```
200 k+ inputs through `secs2::decode`, 1.4 M+ through
`try_parse_sml`, ASan + UBSan clean, 0 crashes.
All five sweeps are wired into CI; see
[`.gitea/workflows/ci.yml`](../.gitea/workflows/ci.yml).
---
## Inspecting the demo from outside
While the demo is running, you can:
### Watch the wire
```bash
# In another shell:
docker compose exec server tcpdump -i any -A -s 0 'tcp port 5000'
```
### Inspect with tshark + HSMS dissector
```bash
docker compose run --rm builder tshark -i any -d "tcp.port==5000,hsms" -V \
| grep -A 2 "Header"
```
### Watch the metrics
`pvd_tool` example exposes a Prometheus endpoint:
```bash
docker compose run --rm --service-ports builder /app/build/pvd_tool \
/app/examples/pvd_tool/equipment.yaml \
/app/data/control_state.yaml \
5000 9090
```
Then `curl localhost:9090/metrics`.
---
## Running the bench
```bash
docker compose run --rm builder /app/build/secs_bench \
--requests 50000 --concurrency 32 --svid-count 32
```
Outputs a markdown table of throughput + p50/p95/p99 latencies for:
- S1F1/F2 (header-only round-trip).
- S1F3/F4 with 32 SVIDs.
- S6F11 push (W=0, fire-and-forget).
- PJ + CJ memory footprint.
See [`docs/BENCHMARKS.md`](BENCHMARKS.md) for the baseline numbers
and capacity-planning notes.
---
## Reading the source while it runs
A common workflow when you're learning:
1. `docker compose up --no-deps server client` in one shell.
2. Source viewer open in another (your IDE on the host —
the source isn't bind-mounted in the container, but it is
on your host).
3. Find a log line that confuses you (e.g. `[equip] S6F11 fired`).
4. Grep the source for it. Most log strings are unique enough to
land in the right file in one search.
5. Read the function around it.
6. Cross-reference back to the chapter that covers the standard.
This is the most efficient way to internalise the codebase. The
demo runs forever (until you `Ctrl-C` — the client loops); you
can read the source at your own pace.
---
## Where to go next
You now have the demo running and you can drive any of the five
external validators. The next chapter is the **integration**
chapter — wiring the runtime to real hardware, talking to a real
MES, production deployment, security, performance tuning.
Next: [→ 41 Integration: hardware, MES, production](41_integration_hardware_mes_production.md)