9876dd9b5a
tests / build-and-test (push) Successful in 2m59s
tests / thread-sanitizer (push) Successful in 3m36s
tests / tshark-dissector (push) Successful in 2m25s
tests / secs4j-interop (push) Successful in 59s
tests / python-interop (push) Successful in 3m20s
tests / libfuzzer (push) Successful in 3m40s
Completes the daemon's GEM300 surface and adds two new test tiers. D10 — E87 carriers: CarrierStore gains the HandlerSlot observer pattern (add_id/slot_map/access_handler). The daemon's id-observer forwards host S3F17 decisions onto the Subscribe stream as CarrierAction (PROCEED on a Confirmed transition, CANCEL on CancelCarrier); ReportCarrier drives the flow tool-side: WAITING creates the carrier + records the slot map, IN_ACCESS/COMPLETE advance the access FSM (INVALID_OBJECT on unknown, CANNOT_DO_NOW on an illegal transition). E16 — operations RPCs: Describe (full name inventory: variables/events/ alarms/commands/constants + device header), FlushSpool (purge or drain), SendTerminalMessage (S10F1 tool->host, honest CANNOT_DO_NOW when no host and stream 10 isn't spoolable). Stream responsiveness: Subscribe/WatchHealth poll at 100ms (was 500ms) so a cancelled stream frees its sync-server worker thread promptly — this was found by the new stress test, which hung under Subscribe churn at 500ms. Tests: - A randomized concurrent RPC stress case: 4 threads x 250 seeded ops (set/get/fire/alarm/control-state/describe + Subscribe churn), asserts no failed RPC and a still-responsive engine afterward; prints its seed; a strong TSan target. - A virtual fab (interop/virtual_fab.py + the `fab` compose service / tools/spawn_fab.sh): N daemons, each with a secsgem-py host AND a secsgem_client tool, driven by seeded random traffic with end-to-end invariant checks (set/get round-trips, event->S6F11 and alarm->S5F1 delivery, command->tool->completion). Verified green at N=3 (~150 ops/eq, all commands round-tripped, 0 violations). Wired into run_interop.sh (now 13 steps). Also fixes the CI break from the previous commit: the Python-client lane's test_values.py step lacked PYTHONPATH=clients/python (now step-level env). Two bugs found and fixed while building this, both mine from this batch: 1. carrier test hung on a CancelCarrier of a still-NotConfirmed carrier — a self-transition the FSM doesn't signal, so the observer never fired and the stream Read blocked forever. Fixed to cancel a Confirmed carrier; the NotConfirmed edge is documented as a known E87 limitation. 2. the 500ms stream poll above. Daemon suite 7 cases / 214 assertions; core 475 / 3097; virtual fab green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
136 lines
3.8 KiB
YAML
136 lines
3.8 KiB
YAML
# All build artifacts live in the named `build` volume (inside Docker), the
|
|
# host only provides the source tree mounted read-write at /app.
|
|
#
|
|
# docker compose build # build the toolchain image
|
|
# docker compose run --rm builder # configure + compile
|
|
# docker compose run --rm tests # run unit tests
|
|
# docker compose up server client # live HSMS demo
|
|
|
|
x-base: &base
|
|
build: .
|
|
volumes:
|
|
- .:/app
|
|
- build:/app/build
|
|
|
|
services:
|
|
builder:
|
|
<<: *base
|
|
# Put builder on the same network as server/client/equipment_py so
|
|
# one-off `docker compose run --rm builder /app/build/secs_interop_probe ...`
|
|
# commands can DNS-resolve peer services by their compose name.
|
|
networks: [secs]
|
|
command:
|
|
- bash
|
|
- -lc
|
|
- >
|
|
cmake -S /app -B /app/build -G Ninja -DCMAKE_BUILD_TYPE=Release &&
|
|
cmake --build /app/build
|
|
|
|
tests:
|
|
<<: *base
|
|
depends_on:
|
|
builder:
|
|
condition: service_completed_successfully
|
|
command: ["ctest", "--test-dir", "/app/build", "--output-on-failure"]
|
|
|
|
server:
|
|
<<: *base
|
|
depends_on:
|
|
builder:
|
|
condition: service_completed_successfully
|
|
command: ["/app/build/secs_server", "--port", "5000", "--device", "0"]
|
|
networks: [secs]
|
|
# No host port publish: the client reaches the server over the `secs`
|
|
# Docker network by service name. Uncomment to expose to the host.
|
|
# ports:
|
|
# - "5000:5000"
|
|
|
|
client:
|
|
<<: *base
|
|
depends_on:
|
|
builder:
|
|
condition: service_completed_successfully
|
|
server:
|
|
condition: service_started
|
|
command: ["/app/build/secs_client", "--host", "server", "--port", "5000", "--device", "0"]
|
|
networks: [secs]
|
|
|
|
# The gRPC daemon: passive HSMS equipment on :5000 plus the gRPC tool API
|
|
# on :50051. Used by interop/daemon_interop.py (a gRPC tool + a secsgem-py
|
|
# host both drive it) to prove the gRPC<->HSMS bridge against a real host.
|
|
gemd:
|
|
<<: *base
|
|
depends_on:
|
|
builder:
|
|
condition: service_completed_successfully
|
|
command:
|
|
- /app/build/secs_gemd
|
|
- --port
|
|
- "5000"
|
|
- --grpc
|
|
- "0.0.0.0:50051"
|
|
- --config-dir
|
|
- /app/data
|
|
networks: [secs]
|
|
|
|
# A virtual fab: FAB_N secs_gemd equipment in one container (HSMS 5100+i,
|
|
# gRPC 51000+i). interop/virtual_fab.py attaches a secsgem-py host AND a
|
|
# secsgem_client tool to every one and drives seeded random traffic.
|
|
fab:
|
|
<<: *base
|
|
depends_on:
|
|
builder:
|
|
condition: service_completed_successfully
|
|
environment:
|
|
FAB_N: "${FAB_N:-3}"
|
|
command: ["bash", "tools/spawn_fab.sh"]
|
|
networks: [secs]
|
|
|
|
# Python container preloaded with secsgem-py 0.3.0 for cross-validation
|
|
# of our C++ HSMS/SECS-II/GEM implementation against the reference library.
|
|
interop:
|
|
build: ./interop
|
|
volumes:
|
|
- .:/app
|
|
working_dir: /app/interop
|
|
networks: [secs]
|
|
|
|
# secsgem-py running as passive equipment so the C++ active host can
|
|
# connect to it (drive with `secs_interop_probe --host equipment_py`).
|
|
equipment_py:
|
|
build: ./interop
|
|
volumes:
|
|
- .:/app
|
|
working_dir: /app/interop
|
|
command: ["python3", "/app/interop/passive_equipment.py", "--port", "5000"]
|
|
networks: [secs]
|
|
|
|
# Server variant for the persistent-spool restart test. Identical to
|
|
# `server` but with --spool-dir pointed at a named volume so spooled
|
|
# messages survive `docker compose restart server-spool`.
|
|
server-spool:
|
|
<<: *base
|
|
depends_on:
|
|
builder:
|
|
condition: service_completed_successfully
|
|
command:
|
|
- /app/build/secs_server
|
|
- --port
|
|
- "5000"
|
|
- --device
|
|
- "0"
|
|
- --spool-dir
|
|
- /spool
|
|
volumes:
|
|
- .:/app
|
|
- build:/app/build
|
|
- spool:/spool
|
|
networks: [secs]
|
|
|
|
networks:
|
|
secs: {}
|
|
|
|
volumes:
|
|
build: {}
|
|
spool: {}
|