From 043fef0a31e99174843c6965802984a73c6f38be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois?= Date: Tue, 12 May 2026 08:29:45 +0200 Subject: [PATCH] User-facing docs: --commands-md flag, doc/user/ tree, anonymised script. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `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 --- CMakeLists.txt | 6 +- DESIGN.md | 9 +- README.md | 93 +++---- doc/README.md | 14 +- doc/api/classes/Tui.md | 190 +++++++------- doc/api/classes/Tui_1_1CommandSpec.md | 14 +- .../classes/Tui_1_1CommandSpec_1_1Param.md | 6 +- doc/api/classes/Tui_1_1Prompt.md | 8 +- doc/api/files/main_8cpp.md | 4 +- doc/user/commands.md | 242 ++++++++++++++++++ doc/user/index.md | 100 ++++++++ doc/user/scripting.md | 132 ++++++++++ src/main.cpp | 45 +++- src/tui/shell.cpp | 71 +++++ src/tui/tui.hpp | 2 + test/system.essim | 131 +++++----- 16 files changed, 825 insertions(+), 242 deletions(-) create mode 100644 doc/user/commands.md create mode 100644 doc/user/index.md create mode 100644 doc/user/scripting.md diff --git a/CMakeLists.txt b/CMakeLists.txt index fac5b26..e8ae200 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 $ --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.") diff --git a/DESIGN.md b/DESIGN.md index 84ad370..a044106 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -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 ` 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 diff --git a/README.md b/README.md index db8f55d..1fb2725 100644 --- a/README.md +++ b/README.md @@ -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 ` 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 ` | load a module from a netlist / pinout file | -| `save ` / `restore ` | snapshot the current system / replace it from a snapshot | -| `source ` | replay a file of commands (interactive ones rejected) | -| `script-save ` | 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 ` | 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 - 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 diff --git a/doc/README.md b/doc/README.md index 06a8fd4..b8b4c50 100644 --- a/doc/README.md +++ b/doc/README.md @@ -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. diff --git a/doc/api/classes/Tui.md b/doc/api/classes/Tui.md index 46b7a41..68d40b5 100644 --- a/doc/api/classes/Tui.md +++ b/doc/api/classes/Tui.md @@ -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) --- diff --git a/doc/api/classes/Tui_1_1CommandSpec.md b/doc/api/classes/Tui_1_1CommandSpec.md index f6e552c..3f0ca00 100644 --- a/doc/api/classes/Tui_1_1CommandSpec.md +++ b/doc/api/classes/Tui_1_1CommandSpec.md @@ -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 diff --git a/doc/api/classes/Tui_1_1CommandSpec_1_1Param.md b/doc/api/classes/Tui_1_1CommandSpec_1_1Param.md index 8b5300e..0e1ed0d 100644 --- a/doc/api/classes/Tui_1_1CommandSpec_1_1Param.md +++ b/doc/api/classes/Tui_1_1CommandSpec_1_1Param.md @@ -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) --- diff --git a/doc/api/classes/Tui_1_1Prompt.md b/doc/api/classes/Tui_1_1Prompt.md index 2e19a47..2e9e679 100644 --- a/doc/api/classes/Tui_1_1Prompt.md +++ b/doc/api/classes/Tui_1_1Prompt.md @@ -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) --- diff --git a/doc/api/files/main_8cpp.md b/doc/api/files/main_8cpp.md index f29ac28..e2ce12f 100644 --- a/doc/api/files/main_8cpp.md +++ b/doc/api/files/main_8cpp.md @@ -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) --- diff --git a/doc/user/commands.md b/doc/user/commands.md new file mode 100644 index 0000000..346fcf5 --- /dev/null +++ b/doc/user/commands.md @@ -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 / 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.** +--- diff --git a/doc/user/index.md b/doc/user/index.md new file mode 100644 index 0000000..e6e54e9 --- /dev/null +++ b/doc/user/index.md @@ -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 ` | +| Scroll back through output | `PageUp` / `PageDown`, `Home`, `End` | +| Re-run a previous command | ↑ / ↓ (also `history` is on disk) | +| Tab-complete a command name | `set‹Tab›` → `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 → ` 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 ` writes a **binary-tab-delimited snapshot** of the whole + system (modules, parts, signals, connector types, signal-type + overrides, connections, pin maps). `restore ` replaces the + current system with the snapshot. +- `script-save ` 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 ` 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. diff --git a/doc/user/scripting.md b/doc/user/scripting.md new file mode 100644 index 0000000..7d34237 --- /dev/null +++ b/doc/user/scripting.md @@ -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 ` 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 ` + +`source ` 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 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 ` + +`script-save ` dumps every command issued since the last `new` +into ``, 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. diff --git a/src/main.cpp b/src/main.cpp index 3db67c4..74eebf2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,6 +1,49 @@ #include "tui/tui.hpp" -int main() { +#include +#include +#include + +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 : 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; diff --git a/src/tui/shell.cpp b/src/tui/shell.cpp index 8dcc9b9..c728c77 100644 --- a/src/tui/shell.cpp +++ b/src/tui/shell.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -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 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; diff --git a/src/tui/tui.hpp b/src/tui/tui.hpp index cb79ee8..cd29fee 100644 --- a/src/tui/tui.hpp +++ b/src/tui/tui.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -122,6 +123,7 @@ public: Tui(); ~Tui(); void Run(); + void DumpCommandsMd(std::ostream &out) const; private: // Lifecycle (commands.cpp) diff --git a/test/system.essim b/test/system.essim index 810e17c..19676ef 100644 --- a/test/system.essim +++ b/test/system.essim @@ -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