User-facing docs: --commands-md flag, doc/user/ tree, anonymised script.

- `essim --commands-md [file]` instantiates the Tui, calls
  `Tui::DumpCommandsMd(ostream&)` which iterates the live registry and
  emits Markdown grouped by interactive/other, then exits. Single
  source of truth: a new `CommandSpec` field surfaces automatically.
- CMake `doc` target now `DEPENDS essim` and chains:
    doxygen → gen_api_md.py → doc/api/
    essim --commands-md      → doc/user/commands.md
- `doc/user/` adds:
    - index.md (hand-written) — first session, interactive-screen
      conventions, save/restore/replay overview.
    - scripting.md (hand-written) — `set`/`$var` expansion semantics,
      `source` event-paced execution, script-save denylist, worked
      example pointing at test/system.essim.
    - commands.md (auto-generated, regenerated by the `doc` target).
- Top-level README refocused on quick start; pointers to the new
  doc tree (user/, api/, DESIGN.md) instead of an inline command table.
- doc/README.md and DESIGN.md document the two-pipeline doc workflow.
- `test/system.essim` and user docs anonymised: bkp → backplane,
  vdn1/2/3 → payload1/2/3, cb3p → payload4, bpb/cob/ssu →
  peripheral1/2/3; netlist file names + variable names + paths all
  replaced with generic equivalents.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-12 08:29:45 +02:00
parent 66460262af
commit 043fef0a31
16 changed files with 825 additions and 242 deletions

View File

@@ -78,17 +78,21 @@ if(DOXYGEN_FOUND AND Python3_Interpreter_FOUND)
@ONLY)
set(DOC_API_DIR "${CMAKE_SOURCE_DIR}/doc/api")
set(DOC_USER_DIR "${CMAKE_SOURCE_DIR}/doc/user")
add_custom_target(doc
DEPENDS essim
COMMAND ${CMAKE_COMMAND} -E rm -rf "${DOC_API_DIR}"
COMMAND ${CMAKE_COMMAND} -E make_directory "${DOC_API_DIR}/classes"
COMMAND ${CMAKE_COMMAND} -E make_directory "${DOC_API_DIR}/files"
COMMAND ${CMAKE_COMMAND} -E make_directory "${DOC_USER_DIR}"
COMMAND ${DOXYGEN_EXECUTABLE} "${DOXYGEN_OUTPUT_DIR}/Doxyfile"
COMMAND ${Python3_EXECUTABLE}
"${CMAKE_SOURCE_DIR}/doc/gen_api_md.py"
"${DOXYGEN_OUTPUT_DIR}/xml"
"${DOC_API_DIR}"
COMMAND $<TARGET_FILE:essim> --commands-md "${DOC_USER_DIR}/commands.md"
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
COMMENT "Generating API documentation (doxygen → gen_api_md.py → doc/api/)"
COMMENT "Generating documentation (doxygen → gen_api_md.py → doc/api/, essim --commands-md → doc/user/commands.md)"
VERBATIM)
elseif(NOT DOXYGEN_FOUND AND NOT Python3_Interpreter_FOUND)
message(STATUS "doc: Doxygen and Python 3 not found — `doc` target disabled.")

View File

@@ -167,14 +167,17 @@ Each successful submission appends a single line to the file (so a crash doesn't
## Documentation
`doc/` hosts:
- `api/` — auto-generated Markdown API reference, browseable directly in gitea.
- `api/` — auto-generated Markdown C++ API reference (developer-facing).
- `user/` — hand-written intro + auto-generated command reference (user-facing).
- `Doxyfile.in` — CMake-templated Doxygen config (XML-only output to `build/doc/xml/`).
- `gen_api_md.py` — custom XML → Markdown emitter (~330 lines, stdlib-only Python).
- `README.md`, `classes.puml` — meta + diagram.
Pipeline: `doxygen` extracts the XML from `src/**`, then `gen_api_md.py` walks `index.xml` plus each `class*.xml` / `*_8hpp.xml` and emits one Markdown file per class/struct/file plus an index. Source-code references use relative paths (`../../../../src/...#L42`) so gitea's renderer turns them into clickable line-anchored links. We picked a custom emitter over `doxybook2` (not in Arch/AUR) and `moxygen` (Node dep) to keep the toolchain pure-C++/Python and trivially modifiable.
**API reference pipeline**: `doxygen` extracts the XML from `src/**`, then `gen_api_md.py` walks `index.xml` plus each `class*.xml` / `*_8hpp.xml` and emits one Markdown file per class/struct/file plus an index. Source-code references use relative paths (`../../../../src/...#L42`) so gitea's renderer turns them into clickable line-anchored links. Chosen over `doxybook2` (not in Arch/AUR) and `moxygen` (Node dep) to keep the toolchain pure-C++/Python and trivially modifiable.
Regenerate with `cmake --build build --target doc` after substantive code changes — the target is auto-created when both Doxygen and Python 3 are present at configure time; otherwise the regular build target is unaffected and a status line records which tool is missing. The Markdown output is committed (`doc/api/`), so gitea readers see fresh docs without building.
**Command reference pipeline**: `essim --commands-md <out>` instantiates a `Tui` (which calls `RegisterCommands()`), then `Tui::DumpCommandsMd(ostream&)` iterates the `commands` map and writes Markdown — two groups (interactive / other), each command with description, numbered arguments, completion mode per arg, scriptability/interactivity notes. The binary is the single source of truth, so a new `CommandSpec` field is reflected in the doc as soon as `DumpCommandsMd` is taught to render it (no separate parser to drift). The `doc` target `DEPENDS essim` so a stale binary is rebuilt automatically.
`doc/user/index.md` and `doc/user/scripting.md` are hand-written (tutorial + scripting semantics); only `doc/user/commands.md` is regenerated. Regenerate everything with `cmake --build build --target doc` — the target is auto-created when both Doxygen and Python 3 are present at configure time; otherwise the regular build target is unaffected and a status line records which tool is missing. The Markdown output is committed (`doc/api/`, `doc/user/commands.md`) so gitea readers see fresh docs without building.
## Memory layout for sessions

View File

@@ -1,10 +1,13 @@
# essim — system digital twin
Interactive simulator for the interconnections between cards in a system. Built around a domain model of **modules → parts → pins / signals → connections**, with importers for Mentor Graphics and ODS pinout sheets, and a TUI shell for browsing, search, connection authoring, scripting, and snapshot save/restore.
Interactive simulator for the inter-card connections inside a system. Built
around a domain model of **modules → parts → pins / signals → connections**,
with importers for Mentor Graphics, Altium and ODS pinout sheets, a TUI shell
with scripting, snapshots and BFS net analysis.
Status: early work-in-progress.
## Build
## Quick start
```sh
cmake -S . -B build
@@ -17,9 +20,15 @@ Requirements:
- A C++17 compiler.
- CMake 3.14+.
- System packages: `libzip` and `pugixml` (Arch: `pacman -S libzip pugixml`).
- FTXUI (v6.1.9) and doctest (v2.4.11) are fetched at configure time via `FetchContent`.
- FTXUI (v6.1.9) and doctest (v2.4.11) are fetched at configure time via
`FetchContent`.
To run the test suite:
Inside the shell, type `help` for the live command list — or read the
auto-generated reference at [`doc/user/commands.md`](doc/user/commands.md).
A worked bring-up script is at [`test/system.essim`](test/system.essim);
load it with `source test/system.essim`.
## Tests
```sh
./build/essim_tests
@@ -27,77 +36,39 @@ To run the test suite:
ctest --test-dir build
```
To skip building tests:
Skip building tests entirely:
```sh
cmake -S . -B build -DBUILD_TESTING=OFF
```
## TUI commands
## Documentation
Type `help` inside the shell for the live list, or `help <name>` for the per-command synopsis. At a glance:
- [`doc/user/`](doc/user/) — user guide, command reference, scripting.
- [`doc/api/`](doc/api/) — auto-generated C++ API reference.
- [`DESIGN.md`](DESIGN.md) — implementation notes (domain conventions, TUI
internals, gotchas).
- [`doc/README.md`](doc/README.md) — how the doc pipeline is wired.
| Command | Purpose |
|---|---|
| `new` | create a new (empty) system; resets the script-save buffer |
| `load <module> <file> <mentor\|altium\|ods>` | load a module from a netlist / pinout file |
| `save <file>` / `restore <file>` | snapshot the current system / replace it from a snapshot |
| `source <file>` | replay a file of commands (interactive ones rejected) |
| `script-save <file>` | dump commands run since last `new` as a replay-ready script |
| `set-type` | tag a part's connector type (interactive screen if no args) |
| `set-signal-type <module> <signal> <power\|gnd\|other>` | override the auto-detected signal type |
| `connect` | connect a part across two modules (interactive screen if no args) |
| `search` | live-filter parts/signals (interactive screen if no args) |
| `explore` | browse modules → parts/signals/connections → details |
| `verify` | report pins whose connected signal type doesn't match the connector role |
Regenerate auto-generated parts after substantive code changes:
Persistent command history lives at `$XDG_DATA_HOME/essim/history` (or `%LOCALAPPDATA%\essim\history` on Windows once ported).
## Tests
Layered with [doctest](https://github.com/doctest/doctest):
- **Unit tests** on pure helpers: tokenizer, natural-order comparator, longest common prefix, signal-type heuristics, VPX transform mapping.
- **Round-trip tests** on the persist layer: a synthetic system is built in code, saved, restored, and compared field by field (modules, parts, connector types, NC pins, signal-type overrides, connections + `pin_map`).
```sh
cmake --build build --target doc # needs doxygen + python3
```
## Project layout
```
src/
main.cpp — launches Tui
system/ — domain model
syselmts.hpp SystemElement + SystemElementContainer<T>
modules.{hpp,cpp} Module, Modules
parts.{hpp,cpp} Part, Parts
pins.{hpp,cpp} Pin, Pins
signals.{hpp,cpp} Signal, Signals (auto-typed at construction)
signal_type.hpp SignalType + helpers
connect.{hpp,cpp} Connection (endpoints + pin_map + transform_name)
transform.{hpp,cpp} Transform / IdentityTransform / TransformRegistry
transform_vpx.{hpp,cpp} 3U-VPX-specific transforms (3 connector pairs)
pin_role.{hpp,cpp} per-(connector_type, pin) expected SignalType
persist.{hpp,cpp} save / restore (tab-delimited)
system.{hpp,cpp} System: owns Modules + Connections
imports/ — adapters that populate the domain
import_base.hpp
import_mentor.{hpp,cpp}
import_ods.{hpp,cpp}
tui/ — FTXUI shell, split by responsibility
tui.{hpp,cpp} class Tui + Run() + screen-mode dispatcher
tui_helpers.{hpp,cpp} free helpers (ToLower, NaturalLess, …)
shell.cpp Print / Submit / Dispatch / Finalize / persistence
completion.cpp command + path Tab completion
commands.cpp registry of all built-in commands
screen_main.cpp visualisation area + bottom input
screen_search.cpp live filter on parts / signals
screen_connect.cpp part-to-part connection authoring
screen_settype.cpp connector type tagging
screen_explore.cpp modules → parts/signals/connections → detail
tests/ — doctest suite (essim_tests target)
CLAUDE.md — implementation notes
LICENSE — EUPL-1.2
src/system/ domain model (Module/Part/Pin/Signal, Connection, Transform, …)
src/imports/ Mentor / Altium / ODS netlist importers
src/tui/ FTXUI shell (commands, screens, completion, history)
tests/ doctest suite
doc/ api/ + user/ Markdown trees, Doxyfile.in, gen_api_md.py
test/ sample netlists + system.essim bring-up script
```
Full layout & rationale in [`DESIGN.md`](DESIGN.md).
## Licence
Copyright (c) 2026 François Dausseur

View File

@@ -5,9 +5,11 @@ Auto-generated API reference and high-level design notes for the
## Layout
- [`api/`](api/) — auto-generated API reference (Doxygen XML → custom
Markdown emitter). Browse classes and files directly in gitea's
Markdown renderer. Top page: [`api/index.md`](api/index.md).
- [`user/`](user/) — **user-facing** docs (hand-written intro/tutorial
+ auto-generated command reference). Start at [`user/index.md`](user/index.md).
- [`api/`](api/) — **developer-facing** API reference (Doxygen XML →
custom Markdown emitter). Browse classes and files directly in
gitea's Markdown renderer. Top page: [`api/index.md`](api/index.md).
- [`../DESIGN.md`](../DESIGN.md) — implementation notes: domain
conventions, TUI flow, gotchas. Hand-maintained.
- [`classes.puml`](classes.puml) — PlantUML class diagram for the
@@ -41,8 +43,14 @@ Pipeline:
src/**/*.{hpp,cpp} ──┐
README.md ─┼─► doxygen ─► build/doc/xml/ ─► gen_api_md.py ─► doc/api/
DESIGN.md ─┘ (Doxyfile.in)
(built essim) ────────► essim --commands-md ──────────────────────────► doc/user/commands.md
```
`doc/user/index.md` and `doc/user/scripting.md` are hand-written; only
`doc/user/commands.md` is regenerated. The `doc` target depends on the
`essim` binary so a stale build is rebuilt before the dump is taken.
If either Doxygen or Python 3 is missing at CMake-configure time the
`doc` target is silently disabled (the regular build still works) and a
status line is emitted in the CMake log telling you which one to install.

View File

@@ -2,395 +2,399 @@
`class Tui`
Defined in [tui.hpp:17](../../../../src/tui/tui.hpp#L17)
Defined in [tui.hpp:18](../../../../src/tui/tui.hpp#L18)
## Private type
### `enum Completion`
📍 [tui.hpp:18](../../../../src/tui/tui.hpp#L18)
📍 [tui.hpp:19](../../../../src/tui/tui.hpp#L19)
## Private Attributes
### `std::vector< std::string > history`
📍 [tui.hpp:40](../../../../src/tui/tui.hpp#L40)
📍 [tui.hpp:41](../../../../src/tui/tui.hpp#L41)
### `std::vector< std::string > recorded`
📍 [tui.hpp:41](../../../../src/tui/tui.hpp#L41)
📍 [tui.hpp:42](../../../../src/tui/tui.hpp#L42)
### `std::vector< std::string > output`
📍 [tui.hpp:42](../../../../src/tui/tui.hpp#L42)
📍 [tui.hpp:43](../../../../src/tui/tui.hpp#L43)
### `std::string input`
📍 [tui.hpp:43](../../../../src/tui/tui.hpp#L43)
📍 [tui.hpp:44](../../../../src/tui/tui.hpp#L44)
### `int cursor_pos`
📍 [tui.hpp:44](../../../../src/tui/tui.hpp#L44)
📍 [tui.hpp:45](../../../../src/tui/tui.hpp#L45)
### `int history_idx`
📍 [tui.hpp:45](../../../../src/tui/tui.hpp#L45)
📍 [tui.hpp:46](../../../../src/tui/tui.hpp#L46)
### `int scroll_offset`
📍 [tui.hpp:46](../../../../src/tui/tui.hpp#L46)
📍 [tui.hpp:47](../../../../src/tui/tui.hpp#L47)
Lines scrolled up from the tail; 0 = follow newest output.
### `bool quit`
📍 [tui.hpp:47](../../../../src/tui/tui.hpp#L47)
📍 [tui.hpp:48](../../../../src/tui/tui.hpp#L48)
### `bool in_source`
📍 [tui.hpp:48](../../../../src/tui/tui.hpp#L48)
📍 [tui.hpp:49](../../../../src/tui/tui.hpp#L49)
### `std::unique_ptr< System > sys`
📍 [tui.hpp:50](../../../../src/tui/tui.hpp#L50)
📍 [tui.hpp:51](../../../../src/tui/tui.hpp#L51)
### `std::deque< Prompt > pending`
📍 [tui.hpp:51](../../../../src/tui/tui.hpp#L51)
📍 [tui.hpp:52](../../../../src/tui/tui.hpp#L52)
### `std::map< std::string, CommandSpec > commands`
📍 [tui.hpp:52](../../../../src/tui/tui.hpp#L52)
📍 [tui.hpp:53](../../../../src/tui/tui.hpp#L53)
### `std::map< std::string, std::string > vars`
📍 [tui.hpp:53](../../../../src/tui/tui.hpp#L53)
📍 [tui.hpp:54](../../../../src/tui/tui.hpp#L54)
$var-style substitution table.
### `int screen_idx`
📍 [tui.hpp:56](../../../../src/tui/tui.hpp#L56)
📍 [tui.hpp:57](../../../../src/tui/tui.hpp#L57)
### `std::vector< std::string > search_modules`
📍 [tui.hpp:59](../../../../src/tui/tui.hpp#L59)
📍 [tui.hpp:60](../../../../src/tui/tui.hpp#L60)
### `std::vector< std::string > search_types`
📍 [tui.hpp:60](../../../../src/tui/tui.hpp#L60)
📍 [tui.hpp:61](../../../../src/tui/tui.hpp#L61)
### `int search_module_idx`
📍 [tui.hpp:61](../../../../src/tui/tui.hpp#L61)
📍 [tui.hpp:62](../../../../src/tui/tui.hpp#L62)
### `int search_type_idx`
📍 [tui.hpp:62](../../../../src/tui/tui.hpp#L62)
📍 [tui.hpp:63](../../../../src/tui/tui.hpp#L63)
### `int search_focus_idx`
📍 [tui.hpp:63](../../../../src/tui/tui.hpp#L63)
📍 [tui.hpp:64](../../../../src/tui/tui.hpp#L64)
### `std::string search_query`
📍 [tui.hpp:64](../../../../src/tui/tui.hpp#L64)
📍 [tui.hpp:65](../../../../src/tui/tui.hpp#L65)
### `std::vector< std::string > connect_modules`
📍 [tui.hpp:67](../../../../src/tui/tui.hpp#L67)
📍 [tui.hpp:68](../../../../src/tui/tui.hpp#L68)
### `int connect_m1_idx`
📍 [tui.hpp:68](../../../../src/tui/tui.hpp#L68)
📍 [tui.hpp:69](../../../../src/tui/tui.hpp#L69)
### `int connect_m2_idx`
📍 [tui.hpp:69](../../../../src/tui/tui.hpp#L69)
📍 [tui.hpp:70](../../../../src/tui/tui.hpp#L70)
### `std::string connect_p1_filter`
📍 [tui.hpp:70](../../../../src/tui/tui.hpp#L70)
📍 [tui.hpp:71](../../../../src/tui/tui.hpp#L71)
### `std::string connect_p2_filter`
📍 [tui.hpp:71](../../../../src/tui/tui.hpp#L71)
📍 [tui.hpp:72](../../../../src/tui/tui.hpp#L72)
### `std::vector< std::string > connect_p1_list`
📍 [tui.hpp:72](../../../../src/tui/tui.hpp#L72)
📍 [tui.hpp:73](../../../../src/tui/tui.hpp#L73)
### `std::vector< std::string > connect_p2_list`
📍 [tui.hpp:73](../../../../src/tui/tui.hpp#L73)
📍 [tui.hpp:74](../../../../src/tui/tui.hpp#L74)
### `int connect_p1_idx`
📍 [tui.hpp:74](../../../../src/tui/tui.hpp#L74)
📍 [tui.hpp:75](../../../../src/tui/tui.hpp#L75)
### `int connect_p2_idx`
📍 [tui.hpp:75](../../../../src/tui/tui.hpp#L75)
📍 [tui.hpp:76](../../../../src/tui/tui.hpp#L76)
### `int connect_focus_idx`
📍 [tui.hpp:76](../../../../src/tui/tui.hpp#L76)
📍 [tui.hpp:77](../../../../src/tui/tui.hpp#L77)
### `std::vector< std::string > explore_modules`
📍 [tui.hpp:79](../../../../src/tui/tui.hpp#L79)
📍 [tui.hpp:80](../../../../src/tui/tui.hpp#L80)
### `int explore_module_idx`
📍 [tui.hpp:80](../../../../src/tui/tui.hpp#L80)
📍 [tui.hpp:81](../../../../src/tui/tui.hpp#L81)
### `std::vector< std::string > explore_types`
📍 [tui.hpp:81](../../../../src/tui/tui.hpp#L81)
📍 [tui.hpp:82](../../../../src/tui/tui.hpp#L82)
### `int explore_type_idx`
📍 [tui.hpp:82](../../../../src/tui/tui.hpp#L82)
📍 [tui.hpp:83](../../../../src/tui/tui.hpp#L83)
### `std::vector< std::string > explore_children`
📍 [tui.hpp:83](../../../../src/tui/tui.hpp#L83)
📍 [tui.hpp:84](../../../../src/tui/tui.hpp#L84)
### `int explore_child_idx`
📍 [tui.hpp:84](../../../../src/tui/tui.hpp#L84)
📍 [tui.hpp:85](../../../../src/tui/tui.hpp#L85)
### `std::string explore_child_filter`
📍 [tui.hpp:85](../../../../src/tui/tui.hpp#L85)
📍 [tui.hpp:86](../../../../src/tui/tui.hpp#L86)
### `std::string explore_detail_filter`
📍 [tui.hpp:86](../../../../src/tui/tui.hpp#L86)
📍 [tui.hpp:87](../../../../src/tui/tui.hpp#L87)
### `std::vector< std::string > explore_detail`
📍 [tui.hpp:87](../../../../src/tui/tui.hpp#L87)
📍 [tui.hpp:88](../../../../src/tui/tui.hpp#L88)
### `int explore_detail_idx`
📍 [tui.hpp:88](../../../../src/tui/tui.hpp#L88)
📍 [tui.hpp:89](../../../../src/tui/tui.hpp#L89)
### `std::string explore_header`
📍 [tui.hpp:89](../../../../src/tui/tui.hpp#L89)
📍 [tui.hpp:90](../../../../src/tui/tui.hpp#L90)
### `int explore_focus_idx`
📍 [tui.hpp:90](../../../../src/tui/tui.hpp#L90)
📍 [tui.hpp:91](../../../../src/tui/tui.hpp#L91)
### `std::atomic< bool > loading`
📍 [tui.hpp:93](../../../../src/tui/tui.hpp#L93)
📍 [tui.hpp:94](../../../../src/tui/tui.hpp#L94)
true while a script is being processed; read by tick thread.
### `std::atomic< bool > tick_in_flight`
📍 [tui.hpp:94](../../../../src/tui/tui.hpp#L94)
📍 [tui.hpp:95](../../../../src/tui/tui.hpp#L95)
main thread acks each tick by clearing this; ticker waits.
### `std::string loading_filename`
📍 [tui.hpp:95](../../../../src/tui/tui.hpp#L95)
📍 [tui.hpp:96](../../../../src/tui/tui.hpp#L96)
### `std::vector< std::string > loading_lines`
📍 [tui.hpp:96](../../../../src/tui/tui.hpp#L96)
📍 [tui.hpp:97](../../../../src/tui/tui.hpp#L97)
### `size_t loading_idx`
📍 [tui.hpp:97](../../../../src/tui/tui.hpp#L97)
📍 [tui.hpp:98](../../../../src/tui/tui.hpp#L98)
### `int loading_executed`
📍 [tui.hpp:98](../../../../src/tui/tui.hpp#L98)
📍 [tui.hpp:99](../../../../src/tui/tui.hpp#L99)
### `int loading_lineno`
📍 [tui.hpp:99](../../../../src/tui/tui.hpp#L99)
📍 [tui.hpp:100](../../../../src/tui/tui.hpp#L100)
### `bool loading_prev_in_source`
📍 [tui.hpp:100](../../../../src/tui/tui.hpp#L100)
📍 [tui.hpp:101](../../../../src/tui/tui.hpp#L101)
### `ftxui::ScreenInteractive * screen_ptr`
📍 [tui.hpp:101](../../../../src/tui/tui.hpp#L101)
📍 [tui.hpp:102](../../../../src/tui/tui.hpp#L102)
set in `Run()` so Source() can post events. so Source() can post events.
### `std::vector< std::string > net_modules`
📍 [tui.hpp:104](../../../../src/tui/tui.hpp#L104)
📍 [tui.hpp:105](../../../../src/tui/tui.hpp#L105)
### `int net_module_idx`
📍 [tui.hpp:105](../../../../src/tui/tui.hpp#L105)
📍 [tui.hpp:106](../../../../src/tui/tui.hpp#L106)
### `std::string net_sig_filter`
📍 [tui.hpp:106](../../../../src/tui/tui.hpp#L106)
📍 [tui.hpp:107](../../../../src/tui/tui.hpp#L107)
### `std::vector< std::string > net_sigs`
📍 [tui.hpp:107](../../../../src/tui/tui.hpp#L107)
📍 [tui.hpp:108](../../../../src/tui/tui.hpp#L108)
rebuilt every frame from filter
### `int net_sig_idx`
📍 [tui.hpp:108](../../../../src/tui/tui.hpp#L108)
📍 [tui.hpp:109](../../../../src/tui/tui.hpp#L109)
### `int net_focus_idx`
📍 [tui.hpp:109](../../../../src/tui/tui.hpp#L109)
📍 [tui.hpp:110](../../../../src/tui/tui.hpp#L110)
### `std::vector< std::string > settype_modules`
📍 [tui.hpp:112](../../../../src/tui/tui.hpp#L112)
📍 [tui.hpp:113](../../../../src/tui/tui.hpp#L113)
### `int settype_m_idx`
📍 [tui.hpp:113](../../../../src/tui/tui.hpp#L113)
📍 [tui.hpp:114](../../../../src/tui/tui.hpp#L114)
### `std::string settype_p_filter`
📍 [tui.hpp:114](../../../../src/tui/tui.hpp#L114)
📍 [tui.hpp:115](../../../../src/tui/tui.hpp#L115)
### `std::vector< std::string > settype_p_list`
📍 [tui.hpp:115](../../../../src/tui/tui.hpp#L115)
📍 [tui.hpp:116](../../../../src/tui/tui.hpp#L116)
### `int settype_p_idx`
📍 [tui.hpp:116](../../../../src/tui/tui.hpp#L116)
📍 [tui.hpp:117](../../../../src/tui/tui.hpp#L117)
### `std::string settype_type`
📍 [tui.hpp:117](../../../../src/tui/tui.hpp#L117)
📍 [tui.hpp:118](../../../../src/tui/tui.hpp#L118)
### `std::string settype_status`
📍 [tui.hpp:118](../../../../src/tui/tui.hpp#L118)
📍 [tui.hpp:119](../../../../src/tui/tui.hpp#L119)
### `int settype_focus_idx`
📍 [tui.hpp:119](../../../../src/tui/tui.hpp#L119)
📍 [tui.hpp:120](../../../../src/tui/tui.hpp#L120)
## Public Functions
### `Tui()`
📍 [tui.hpp:122](../../../../src/tui/tui.hpp#L122)
📍 [tui.hpp:123](../../../../src/tui/tui.hpp#L123)
### `~Tui()`
📍 [tui.hpp:123](../../../../src/tui/tui.hpp#L123)
📍 [tui.hpp:124](../../../../src/tui/tui.hpp#L124)
### `void Run()`
📍 [tui.hpp:124](../../../../src/tui/tui.hpp#L124)
📍 [tui.hpp:125](../../../../src/tui/tui.hpp#L125)
### `void DumpCommandsMd(std::ostream &out) const`
📍 [tui.hpp:126](../../../../src/tui/tui.hpp#L126)
## Private Functions
### `void RegisterCommands()`
📍 [tui.hpp:128](../../../../src/tui/tui.hpp#L128)
📍 [tui.hpp:130](../../../../src/tui/tui.hpp#L130)
### `void Print(const std::string &line)`
📍 [tui.hpp:131](../../../../src/tui/tui.hpp#L131)
📍 [tui.hpp:133](../../../../src/tui/tui.hpp#L133)
### `void Submit()`
📍 [tui.hpp:132](../../../../src/tui/tui.hpp#L132)
📍 [tui.hpp:134](../../../../src/tui/tui.hpp#L134)
### `void Dispatch(const std::string &raw)`
📍 [tui.hpp:133](../../../../src/tui/tui.hpp#L133)
📍 [tui.hpp:135](../../../../src/tui/tui.hpp#L135)
### `void Finalize(const std::string &name, const CommandSpec &spec, const std::vector< std::string > &args)`
📍 [tui.hpp:134](../../../../src/tui/tui.hpp#L134)
📍 [tui.hpp:136](../../../../src/tui/tui.hpp#L136)
### `void HistoryUp()`
📍 [tui.hpp:137](../../../../src/tui/tui.hpp#L137)
📍 [tui.hpp:139](../../../../src/tui/tui.hpp#L139)
### `void HistoryDown()`
📍 [tui.hpp:138](../../../../src/tui/tui.hpp#L138)
📍 [tui.hpp:140](../../../../src/tui/tui.hpp#L140)
### `void CancelPending()`
📍 [tui.hpp:139](../../../../src/tui/tui.hpp#L139)
📍 [tui.hpp:141](../../../../src/tui/tui.hpp#L141)
### `void LoadHistory()`
📍 [tui.hpp:140](../../../../src/tui/tui.hpp#L140)
📍 [tui.hpp:142](../../../../src/tui/tui.hpp#L142)
### `void AppendHistory(const std::string &cmd)`
📍 [tui.hpp:141](../../../../src/tui/tui.hpp#L141)
📍 [tui.hpp:143](../../../../src/tui/tui.hpp#L143)
### `void Source(const std::string &filename)`
📍 [tui.hpp:142](../../../../src/tui/tui.hpp#L142)
📍 [tui.hpp:144](../../../../src/tui/tui.hpp#L144)
### `void ProcessNextSourceLine()`
📍 [tui.hpp:143](../../../../src/tui/tui.hpp#L143)
📍 [tui.hpp:145](../../../../src/tui/tui.hpp#L145)
### `std::string ExpandVars(const std::string &s) const`
📍 [tui.hpp:144](../../../../src/tui/tui.hpp#L144)
📍 [tui.hpp:146](../../../../src/tui/tui.hpp#L146)
### `void CompleteCommand(size_t start=0)`
📍 [tui.hpp:147](../../../../src/tui/tui.hpp#L147)
📍 [tui.hpp:149](../../../../src/tui/tui.hpp#L149)
### `void CompletePath(size_t start=0)`
📍 [tui.hpp:148](../../../../src/tui/tui.hpp#L148)
📍 [tui.hpp:150](../../../../src/tui/tui.hpp#L150)
### `void CompleteInline()`
📍 [tui.hpp:149](../../../../src/tui/tui.hpp#L149)
📍 [tui.hpp:151](../../../../src/tui/tui.hpp#L151)
### `void RefreshFilteredPartList(const std::vector< std::string > &modules, int m_idx, const std::string &filter, std::vector< std::string > &out, int &sel_idx)`
📍 [tui.hpp:152](../../../../src/tui/tui.hpp#L152)
📍 [tui.hpp:154](../../../../src/tui/tui.hpp#L154)
### `ftxui::Component BuildMainScreen(ftxui::ScreenInteractive &screen)`
📍 [tui.hpp:159](../../../../src/tui/tui.hpp#L159)
📍 [tui.hpp:161](../../../../src/tui/tui.hpp#L161)
### `ftxui::Component BuildSearchScreen()`
📍 [tui.hpp:160](../../../../src/tui/tui.hpp#L160)
📍 [tui.hpp:162](../../../../src/tui/tui.hpp#L162)
### `ftxui::Component BuildConnectScreen()`
📍 [tui.hpp:161](../../../../src/tui/tui.hpp#L161)
📍 [tui.hpp:163](../../../../src/tui/tui.hpp#L163)
### `ftxui::Component BuildSettypeScreen()`
📍 [tui.hpp:162](../../../../src/tui/tui.hpp#L162)
📍 [tui.hpp:164](../../../../src/tui/tui.hpp#L164)
### `ftxui::Component BuildExploreScreen()`
📍 [tui.hpp:163](../../../../src/tui/tui.hpp#L163)
📍 [tui.hpp:165](../../../../src/tui/tui.hpp#L165)
### `ftxui::Component BuildNetScreen()`
📍 [tui.hpp:164](../../../../src/tui/tui.hpp#L164)
📍 [tui.hpp:166](../../../../src/tui/tui.hpp#L166)
---

View File

@@ -2,33 +2,33 @@
`struct Tui::CommandSpec`
Defined in [tui.hpp:26](../../../../src/tui/tui.hpp#L26)
Defined in [tui.hpp:27](../../../../src/tui/tui.hpp#L27)
## Public Attributes
### `std::vector< Param > params`
📍 [tui.hpp:31](../../../../src/tui/tui.hpp#L31)
📍 [tui.hpp:32](../../../../src/tui/tui.hpp#L32)
### `std::function< void(const std::vector< std::string > &)> action`
📍 [tui.hpp:32](../../../../src/tui/tui.hpp#L32)
📍 [tui.hpp:33](../../../../src/tui/tui.hpp#L33)
### `bool prompt_for_missing`
📍 [tui.hpp:33](../../../../src/tui/tui.hpp#L33)
📍 [tui.hpp:34](../../../../src/tui/tui.hpp#L34)
### `std::string description`
📍 [tui.hpp:34](../../../../src/tui/tui.hpp#L34)
📍 [tui.hpp:35](../../../../src/tui/tui.hpp#L35)
### `bool scriptable`
📍 [tui.hpp:35](../../../../src/tui/tui.hpp#L35)
📍 [tui.hpp:36](../../../../src/tui/tui.hpp#L36)
### `bool interactive`
📍 [tui.hpp:36](../../../../src/tui/tui.hpp#L36)
📍 [tui.hpp:37](../../../../src/tui/tui.hpp#L37)
opens a full-screen mode when called bare

View File

@@ -2,17 +2,17 @@
`struct Tui::CommandSpec::Param`
Defined in [tui.hpp:27](../../../../src/tui/tui.hpp#L27)
Defined in [tui.hpp:28](../../../../src/tui/tui.hpp#L28)
## Public Attributes
### `std::string name`
📍 [tui.hpp:28](../../../../src/tui/tui.hpp#L28)
📍 [tui.hpp:29](../../../../src/tui/tui.hpp#L29)
### `Completion completion`
📍 [tui.hpp:29](../../../../src/tui/tui.hpp#L29)
📍 [tui.hpp:30](../../../../src/tui/tui.hpp#L30)
---

View File

@@ -2,21 +2,21 @@
`struct Tui::Prompt`
Defined in [tui.hpp:20](../../../../src/tui/tui.hpp#L20)
Defined in [tui.hpp:21](../../../../src/tui/tui.hpp#L21)
## Public Attributes
### `std::string question`
📍 [tui.hpp:21](../../../../src/tui/tui.hpp#L21)
📍 [tui.hpp:22](../../../../src/tui/tui.hpp#L22)
### `std::function< void(const std::string &)> on_answer`
📍 [tui.hpp:22](../../../../src/tui/tui.hpp#L22)
📍 [tui.hpp:23](../../../../src/tui/tui.hpp#L23)
### `Completion completion`
📍 [tui.hpp:23](../../../../src/tui/tui.hpp#L23)
📍 [tui.hpp:24](../../../../src/tui/tui.hpp#L24)
---

View File

@@ -4,9 +4,9 @@ Source: [main.cpp](../../../../src/main.cpp)
## Free Functions
### `int main()`
### `int main(int argc, char **argv)`
📍 [main.cpp:3](../../../../src/main.cpp#L3)
📍 [main.cpp:21](../../../../src/main.cpp#L21)
---

242
doc/user/commands.md Normal file
View File

@@ -0,0 +1,242 @@
# essim — command reference
Auto-generated from the live command registry. Regenerate with
`cmake --build build --target doc` after adding or changing
commands; the binary itself is the single source of truth.
Keys global to the shell: `Esc` cancels a multi-step prompt or
leaves an interactive screen; `Tab` completes commands/paths
(top-level prompt) or cycles focus inside an interactive
screen; `PageUp` / `PageDown` scroll output by 10 lines,
`Home` / `End` jump to top / bottom; ↑ / ↓ walk command
history.
## Interactive commands
### `connect` *(interactive)*
connect a part across two modules (interactive screen if no args)
**Arguments**
1. `module1`
2. `part1 (name or pattern)`
3. `module2`
4. `part2 (name or pattern)`
**Notes**
- bare form opens an interactive screen; inline form (all args) is scriptable
---
### `explore` *(interactive)*
browse modules → parts/signals/connections → details (interactive)
**No arguments.**
**Notes**
- bare form opens an interactive screen; inline form (all args) is scriptable
- not recorded by `script-save` and rejected by `source`
---
### `net` *(interactive)*
show all signals reachable from <module>/<signal> through connections (interactive screen if no args)
**Arguments**
1. `module`
2. `signal name`
**Notes**
- bare form opens an interactive screen; inline form (all args) is scriptable
---
### `search` *(interactive)*
list parts/signals matching a pattern (interactive screen if no args)
**Arguments**
1. `module`
2. `kind [parts|signals]`
3. `pattern`
**Notes**
- bare form opens an interactive screen; inline form (all args) is scriptable
---
### `set-type` *(interactive)*
tag a part's connector type for transform lookup
**Arguments**
1. `module`
2. `part (name or pattern)`
3. `connector type (free string, e.g. vpx-bp, vpx-payload)`
**Notes**
- bare form opens an interactive screen; inline form (all args) is scriptable
---
## Other commands
### `clear`
clear the visualization area
**No arguments.**
---
### `duplicate`
clone a module under a new name (parts, pins, signals; no connections)
**Arguments**
1. `source module`
2. `new module name`
**Notes**
- missing args trigger one prompt each
---
### `exit`
leave essim (alias of quit)
**No arguments.**
---
### `help`
show command help (optionally for a specific command)
**Arguments**
1. `command name (optional)` *(Tab → command completion)*
**Notes**
- no per-arg prompt: pass all args inline (or run bare for an empty-args path)
---
### `load`
load a module from a netlist / pinout file (mentor, altium, ods)
**Arguments**
1. `module name`
2. `filename` *(Tab → path completion)*
3. `import type [mentor|altium|ods]`
**Notes**
- missing args trigger one prompt each
---
### `new`
create a new (empty) system; resets the script-save buffer and $vars
**No arguments.**
---
### `quit`
leave essim
**No arguments.**
---
### `restore`
replace the current system with a saved snapshot
**Arguments**
1. `filename` *(Tab → path completion)*
**Notes**
- missing args trigger one prompt each
---
### `save`
write the current system snapshot to a file
**Arguments**
1. `filename` *(Tab → path completion)*
**Notes**
- missing args trigger one prompt each
---
### `script-save`
write commands run since last 'new' as a replay-ready script
**Arguments**
1. `filename` *(Tab → path completion)*
**Notes**
- missing args trigger one prompt each
---
### `set`
define a $variable for substitution in subsequent commands (no args = list defined vars)
**Arguments**
1. `name`
2. `value`
**Notes**
- no per-arg prompt: pass all args inline (or run bare for an empty-args path)
---
### `set-signal-type`
override the auto-detected signal type (power | gnd | other)
**Arguments**
1. `module`
2. `signal name`
3. `type [power|gnd|other]`
**Notes**
- missing args trigger one prompt each
---
### `source`
execute a file of commands line by line (interactive cmds rejected)
**Arguments**
1. `filename` *(Tab → path completion)*
**Notes**
- missing args trigger one prompt each
---
### `verify`
check pin roles locally and signal-type consistency across bridged nets
**No arguments.**
---

100
doc/user/index.md Normal file
View File

@@ -0,0 +1,100 @@
# essim — user guide
A short, task-oriented introduction to using essim. For an exhaustive
reference of every command, see [`commands.md`](commands.md) (auto-
generated from the binary). For scripting and `$variable` expansion,
see [`scripting.md`](scripting.md).
## What essim is
A digital twin for the inter-card connections inside a system. You
**load** modules (cards/boards) from netlist or pinout files, **tag**
their connectors with a `connector_type`, **connect** them across
modules, and **verify** that the signal types match the connector role
expectations. You can save a snapshot, restore it later, or replay a
session as a script.
```
┌──────────┐ ┌──────────┐
│ Module A │ │ Module B │
│ Part J1 │◀──▶│ Part P1 │ <— a Connection (built by `connect`)
│ pins │ │ pins │
└──────────┘ └──────────┘
```
## First session
After launching `./build/essim`, the prompt accepts a sequence of
commands. The most common bring-up looks like this:
```text
> new
> load backplane /path/to/netlists/backplane.NET altium
> load payload1 /path/to/netlists/payload.qcv mentor
> set-type backplane J20 vpx-3u-bkp-p0
> set-type payload1 P0 vpx-3u-payload-p0
> connect backplane J20 payload1 P0
> verify
> save my-system.essim
```
Things to try at any time:
| Action | How |
|---|---|
| List every command | `help` |
| Help on one command | `help <name>` |
| Scroll back through output | `PageUp` / `PageDown`, `Home`, `End` |
| Re-run a previous command | ↑ / ↓ (also `history` is on disk) |
| Tab-complete a command name | `setTab``set-type` etc. |
| Cancel a multi-step prompt | `Esc` |
| Leave essim | `quit` (or `exit`) |
## Interactive screens
Some commands open a dedicated full-screen layout when invoked with no
arguments (the help listing tags these `[interactive]`). They all
share the same conventions:
- A title bar `essim → <name> — <short description>` is shown at the
top.
- `Tab` cycles focus between fields; the active field's label flips to
reverse video so it's obvious where the next keystroke goes.
- `Esc` leaves the screen and returns to the main prompt.
- The screens are user-facing only — they are **never** allowed inside
a sourced script. A sourced script must use the inline form of these
commands instead.
Today's interactive screens: `connect`, `search`, `set-type`,
`explore`, `net`. See [`commands.md`](commands.md) for each.
## Saving, restoring, replaying
Three orthogonal mechanisms persist your work:
- `save <file>` writes a **binary-tab-delimited snapshot** of the whole
system (modules, parts, signals, connector types, signal-type
overrides, connections, pin maps). `restore <file>` replaces the
current system with the snapshot.
- `script-save <file>` writes a **replay-ready text script** of every
command issued since the last `new`. The interactive bits and the
noisy commands (`clear`, `help`, …) are filtered out automatically.
- `source <file>` reads a script line by line. Comments start with
`#`, blank lines are skipped, leading `~/` in paths is expanded, and
while a script is running a centred "Computing…" modal shows the
progress (one line per ~30 ms tick).
A typical workflow: experiment in the shell, `script-save` the part
that works, hand-edit the script to introduce `$variables` (see
[`scripting.md`](scripting.md)), then `source` it whenever you start
fresh.
## Where to look next
- [`commands.md`](commands.md) — exhaustive command reference,
regenerated from the binary on every `cmake --build build --target doc`.
- [`scripting.md`](scripting.md) — `set` / `$var` / `${var}`, `source`
semantics, the script-save denylist.
- [`DESIGN.md`](../../DESIGN.md) — implementation notes, useful if
you want to add a command or a screen.
- [`../api/`](../api/) — auto-generated C++ API reference.

132
doc/user/scripting.md Normal file
View File

@@ -0,0 +1,132 @@
# essim — scripting
This page covers the two facilities that make essim sessions
reproducible: `source` (replay a file) and `set` (declare named
variables that subsequent commands expand). For the per-command
reference, see [`commands.md`](commands.md).
## File format
A script is a plain text file, one command per line. The format is
intentionally minimal:
- Lines starting with `#` are comments — skipped.
- Blank lines are skipped.
- Leading / trailing whitespace is trimmed.
- A leading `~/` in arguments is expanded to `$HOME` (paths only).
- Each line goes through the same `Submit` path as if typed at the
prompt, so anything that works interactively works in a script
*except* commands that open an interactive screen (see below).
By convention essim scripts use the `.essim` extension.
## Variables — `set` and `$expansion`
`set <name> <value>` declares a **session-scoped** variable. Subsequent
commands expand `$name` and `${name}` in their arguments:
```text
> set netlist_dir /path/to/netlists
> set backplane_nets $netlist_dir/backplane.NET
> load backplane $backplane_nets altium
```
Names must match `[A-Za-z_][A-Za-z0-9_]*`. Unknown variables are left
literal (`$undef` stays `$undef`) so a typo surfaces as a "file not
found" or "unknown module" error rather than a silent empty string.
The expansion happens at **dispatch time**, between recording the
canonical form and calling the action. So:
- `history` and `script-save` keep the **unexpanded** form (`$backplane_nets`
is preserved as `$backplane_nets`), which makes the recorded script
portable across sessions if you set the variables before sourcing it.
- The action itself sees the resolved value (the actual filesystem
path).
`new` resets the variable table to empty.
## Replaying — `source <file>`
`source <file>` runs the script line by line. Three behavioural
details are worth knowing:
1. **Event-paced execution.** The runtime processes one effective line
(skipping comments / blanks) every ~30 ms tick, dispatched by a
background thread that posts FTXUI events. This lets the screen
redraw between lines and surface a centred `Computing…` modal with
a `N / M lines` counter. Without this pacing, FTXUI would batch
queued events and freeze the modal until the entire script is done.
2. **Interactive screens are rejected.** If a sourced line opens a
full-screen mode (`screen_idx != 0` after `Submit`), the script is
aborted with `source: line <N> is interactive (would open a
screen) — aborting.`. The fix is to use the inline form of that
command (e.g. `connect backplane J20 payload1 P0` instead of a bare
`connect`).
3. **Pending prompts are filled by subsequent lines.** A multi-step
command split across lines is treated as one logical unit. If the
first line says `load`, the next non-blank line answers the
`module name?` prompt, then `filename?`, then `import type?`.
While `in_source = true`:
- `Dispatch` / `Finalize` skip writing to memory + on-disk history.
- The `recorded` buffer (used by `script-save`) is still populated
with each effective line; so sourcing a script and immediately
running `script-save` produces a self-contained replay even if the
original source path is lost.
## Recording — `script-save <file>`
`script-save <file>` dumps every command issued since the last `new`
into `<file>`, one line per command, in canonical inline form. The
following commands are deliberately **not** recorded:
```
clear, help, quit, exit, source, script-save
```
`source` is excluded for a subtle reason: when you source a script,
the individual lines inside it go through `Finalize` and *are*
recorded, so the saved replay reproduces the same end-state without
the indirection.
`set` lines and `$var` references are recorded as typed, so the saved
script keeps its abstraction.
## Worked example
The `test/system.essim` script (committed in the repo) is the
canonical anonymised bring-up — one backplane, four payload cards,
three peripherals:
```text
new
# variables
set netlist_dir /path/to/netlists
set peripheral1_nets $netlist_dir/peripheral1.qcv
set backplane_nets $netlist_dir/backplane.NET
# ... more set lines ...
# modules
load payload1 $payload_nets mentor
duplicate payload1 payload2
duplicate payload1 payload3
load peripheral1 $peripheral1_nets mentor
load backplane $backplane_nets altium
# ... more loads ...
# connector type tagging
set-type backplane J20 vpx-3u-bkp-p0
set-type payload1 P0 vpx-3u-payload-p0
# ... more tags ...
# wiring
connect backplane J20 payload1 P0
# ... more connects ...
```
Run it with `source test/system.essim` after adjusting `$netlist_dir`
to point at your real netlist files.

View File

@@ -1,6 +1,49 @@
#include "tui/tui.hpp"
int main() {
#include <fstream>
#include <iostream>
#include <string>
namespace {
void print_usage(const char *prog) {
std::cerr <<
"usage: " << prog << " [--commands-md [file] | --help]\n"
" (no args) launch the interactive TUI\n"
" --commands-md [file] dump the command registry as Markdown.\n"
" With <file>: write there. Without: stdout.\n"
" (Used by `cmake --build build --target doc`.)\n"
" --help, -h show this help\n";
}
} // namespace
int main(int argc, char **argv) {
for (int i = 1; i < argc; ++i) {
std::string a = argv[i];
if (a == "--commands-md") {
Tui tui;
if (i + 1 < argc) {
std::ofstream f(argv[++i]);
if (!f) {
std::cerr << "essim: cannot open " << argv[i] << " for writing\n";
return 1;
}
tui.DumpCommandsMd(f);
} else {
tui.DumpCommandsMd(std::cout);
}
return 0;
}
if (a == "--help" || a == "-h") {
print_usage(argv[0]);
return 0;
}
std::cerr << "essim: unknown option: " << a << "\n";
print_usage(argv[0]);
return 2;
}
Tui tui;
tui.Run();
return 0;

View File

@@ -6,6 +6,7 @@
#include <cstdlib>
#include <filesystem>
#include <fstream>
#include <ostream>
#include <set>
#include <system_error>
#include <thread>
@@ -289,6 +290,76 @@ void Tui::ProcessNextSourceLine() {
in_source = loading_prev_in_source;
}
void Tui::DumpCommandsMd(std::ostream &out) const {
out << "# essim — command reference\n\n"
"Auto-generated from the live command registry. Regenerate with\n"
"`cmake --build build --target doc` after adding or changing\n"
"commands; the binary itself is the single source of truth.\n\n"
"Keys global to the shell: `Esc` cancels a multi-step prompt or\n"
"leaves an interactive screen; `Tab` completes commands/paths\n"
"(top-level prompt) or cycles focus inside an interactive\n"
"screen; `PageUp` / `PageDown` scroll output by 10 lines,\n"
"`Home` / `End` jump to top / bottom; ↑ / ↓ walk command\n"
"history.\n";
auto emit_group = [&](const std::string &title, bool want_interactive) {
bool printed_title = false;
for (const auto &kv : commands) {
const CommandSpec &spec = kv.second;
if (spec.interactive != want_interactive) continue;
if (!printed_title) {
out << "\n## " << title << "\n\n";
printed_title = true;
}
out << "### `" << kv.first << "`";
if (spec.interactive) out << " *(interactive)*";
out << "\n\n" << spec.description << "\n\n";
if (spec.params.empty()) {
out << "**No arguments.**";
} else {
out << "**Arguments**\n\n";
int i = 1;
for (const auto &p : spec.params) {
out << i << ". `" << p.name << "`";
switch (p.completion) {
case Completion::Path: out << " *(Tab → path completion)*"; break;
case Completion::Command: out << " *(Tab → command completion)*"; break;
case Completion::None: break;
}
out << "\n";
++i;
}
}
out << "\n";
std::vector<std::string> notes;
if (spec.interactive) {
notes.emplace_back("bare form opens an interactive screen; "
"inline form (all args) is scriptable");
} else if (!spec.prompt_for_missing) {
notes.emplace_back("no per-arg prompt: pass all args inline "
"(or run bare for an empty-args path)");
} else if (!spec.params.empty()) {
notes.emplace_back("missing args trigger one prompt each");
}
if (!spec.scriptable) {
notes.emplace_back("not recorded by `script-save` and "
"rejected by `source`");
}
if (!notes.empty()) {
out << "**Notes**\n\n";
for (const auto &n : notes) out << "- " << n << "\n";
out << "\n";
}
out << "---\n";
}
};
emit_group("Interactive commands", true);
emit_group("Other commands", false);
}
void Tui::AppendHistory(const std::string &cmd) {
auto p = HistoryPath();
if (p.empty()) return;

View File

@@ -4,6 +4,7 @@
#include <atomic>
#include <deque>
#include <functional>
#include <iosfwd>
#include <map>
#include <memory>
#include <string>
@@ -122,6 +123,7 @@ public:
Tui();
~Tui();
void Run();
void DumpCommandsMd(std::ostream &out) const;
private:
// Lifecycle (commands.cpp)

View File

@@ -1,86 +1,89 @@
# essim system bring-up script.
# essim system bring-up script (anonymised sample).
#
# Layout: one VPX 3U backplane carrying four payload cards (three of the
# same kind, one specialised) and three peripheral cards wired non-VPX.
new
# ---------------------------------------------------------------- variables
set test_dir /home/francois/Projets/essim_test
set bpb_nets $test_dir/BPB-2177-10222_NETLIST_3_1.qcv
set bkp_nets $test_dir/MERCVPX3UBPA_20221122.NET
set cb3p_nets $test_dir/CB3P-6359-10232_NETLIST_3_0.qcv
set vdn_nets $test_dir/VDN-2910.qcv
set cob_nets $test_dir/COB-2277_NETLIST_10211_2_0.qcv
set ssu_nets $test_dir/SSU-2134_PCB873.qcv
set netlist_dir /path/to/netlists
set peripheral1_nets $netlist_dir/peripheral1.qcv
set backplane_nets $netlist_dir/backplane.NET
set payload4_nets $netlist_dir/payload4.qcv
set payload_nets $netlist_dir/payload.qcv
set peripheral2_nets $netlist_dir/peripheral2.qcv
set peripheral3_nets $netlist_dir/peripheral3.qcv
# ---------------------------------------------------------------- modules
load vdn1 $vdn_nets mentor
duplicate vdn1 vdn2
duplicate vdn1 vdn3
load bpb $bpb_nets mentor
load bkp $bkp_nets altium
load cb3p $cb3p_nets mentor
load cob $cob_nets mentor
load ssu $ssu_nets mentor
load payload1 $payload_nets mentor
duplicate payload1 payload2
duplicate payload1 payload3
load peripheral1 $peripheral1_nets mentor
load backplane $backplane_nets altium
load payload4 $payload4_nets mentor
load peripheral2 $peripheral2_nets mentor
load peripheral3 $peripheral3_nets mentor
# ---------------------------------------------------------------- VPX tags
# Backplane payload-side connectors on BKP, one slot per (Jx0,Jx1,Jx2):
# J2x → VDN1, J3x → VDN2, J4x → VDN3, J5x → CB3P.
set-type bkp J20 vpx-3u-bkp-p0
set-type bkp J21 vpx-3u-bkp-p1
set-type bkp J22 vpx-3u-bkp-p2
set-type bkp J30 vpx-3u-bkp-p0
set-type bkp J31 vpx-3u-bkp-p1
set-type bkp J32 vpx-3u-bkp-p2
set-type bkp J40 vpx-3u-bkp-p0
set-type bkp J41 vpx-3u-bkp-p1
set-type bkp J42 vpx-3u-bkp-p2
set-type bkp J50 vpx-3u-bkp-p0
set-type bkp J51 vpx-3u-bkp-p1
set-type bkp J52 vpx-3u-bkp-p2
# Backplane payload-side connectors, one slot per (Jx0,Jx1,Jx2):
# J2x → payload1, J3x → payload2, J4x → payload3, J5x → payload4.
set-type backplane J20 vpx-3u-bkp-p0
set-type backplane J21 vpx-3u-bkp-p1
set-type backplane J22 vpx-3u-bkp-p2
set-type backplane J30 vpx-3u-bkp-p0
set-type backplane J31 vpx-3u-bkp-p1
set-type backplane J32 vpx-3u-bkp-p2
set-type backplane J40 vpx-3u-bkp-p0
set-type backplane J41 vpx-3u-bkp-p1
set-type backplane J42 vpx-3u-bkp-p2
set-type backplane J50 vpx-3u-bkp-p0
set-type backplane J51 vpx-3u-bkp-p1
set-type backplane J52 vpx-3u-bkp-p2
# Payload connectors on each plug-in card.
set-type vdn1 P0 vpx-3u-payload-p0
set-type vdn1 P1 vpx-3u-payload-p1
set-type vdn1 P2 vpx-3u-payload-p2
set-type vdn2 P0 vpx-3u-payload-p0
set-type vdn2 P1 vpx-3u-payload-p1
set-type vdn2 P2 vpx-3u-payload-p2
set-type vdn3 P0 vpx-3u-payload-p0
set-type vdn3 P1 vpx-3u-payload-p1
set-type vdn3 P2 vpx-3u-payload-p2
set-type cb3p P0 vpx-3u-payload-p0
set-type cb3p P1 vpx-3u-payload-p1
set-type cb3p P2 vpx-3u-payload-p2
set-type payload1 P0 vpx-3u-payload-p0
set-type payload1 P1 vpx-3u-payload-p1
set-type payload1 P2 vpx-3u-payload-p2
set-type payload2 P0 vpx-3u-payload-p0
set-type payload2 P1 vpx-3u-payload-p1
set-type payload2 P2 vpx-3u-payload-p2
set-type payload3 P0 vpx-3u-payload-p0
set-type payload3 P1 vpx-3u-payload-p1
set-type payload3 P2 vpx-3u-payload-p2
set-type payload4 P0 vpx-3u-payload-p0
set-type payload4 P1 vpx-3u-payload-p1
set-type payload4 P2 vpx-3u-payload-p2
# ---------------------------------------------------------------- VPX wiring
# Each connect dispatches via the registered vpx-3u transform.
connect bkp J20 vdn1 P0
connect bkp J21 vdn1 P1
connect bkp J22 vdn1 P2
connect backplane J20 payload1 P0
connect backplane J21 payload1 P1
connect backplane J22 payload1 P2
connect bkp J30 vdn2 P0
connect bkp J31 vdn2 P1
connect bkp J32 vdn2 P2
connect backplane J30 payload2 P0
connect backplane J31 payload2 P1
connect backplane J32 payload2 P2
connect bkp J40 vdn3 P0
connect bkp J41 vdn3 P1
connect bkp J42 vdn3 P2
connect backplane J40 payload3 P0
connect backplane J41 payload3 P1
connect backplane J42 payload3 P2
connect bkp J50 cb3p P0
connect bkp J51 cb3p P1
connect bkp J52 cb3p P2
connect backplane J50 payload4 P0
connect backplane J51 payload4 P1
connect backplane J52 payload4 P2
# ---------------------------------------------------------------- non-VPX
# Both ends untagged → IdentityTransform (matches by canonical pin name,
# so e.g. A1 ↔ A001 is paired thanks to canonical_pin_name).
connect cob P3 ssu P6
connect bkp J1 ssu P1
connect peripheral2 P3 peripheral3 P6
connect backplane J1 peripheral3 P1
# BPB ↔ BKP
connect bkp P100 bpb J100
connect bkp P101 bpb J101
connect bkp P102 bpb J102
# peripheral1 ↔ backplane
connect backplane P100 peripheral1 J100
connect backplane P101 peripheral1 J101
connect backplane P102 peripheral1 J102
# BPB ↔ COB
connect bpb J0 cob P0
connect bpb J1 cob P1
connect bpb J2 cob P2
# peripheral1 ↔ peripheral2
connect peripheral1 J0 peripheral2 P0
connect peripheral1 J1 peripheral2 P1
connect peripheral1 J2 peripheral2 P2