Rename set-type to set-connector-type; help-panel & types-glossary polish.

- Command renamed from `set-type` to `set-connector-type` for
  clarity (the previous name was ambiguous — "type" of what?). No
  legacy alias kept; old scripts that still used `set-type` must be
  migrated. `test/system.essim` and all user/design docs updated.
- Help panel (RenderHelpPanel) now wraps in borderRounded with a
  centred bold title, so it is visually distinct from the main
  content on every screen. Width bumped from 30 to 32 to include
  the border.
- Analyze screen's Types tab gains a sibling "type glossary" panel
  (also borderRounded, only visible when the Types tab is focused)
  that explains Power / Suspect Power / Hard floor / Gnd in plain
  language using `paragraph()` for clean word-wrap.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-14 20:41:35 +02:00
parent 90502c0762
commit 516149cdae
17 changed files with 90 additions and 87 deletions

View File

@@ -87,7 +87,7 @@ doc/classes.puml -- PlantUML class diagram
- Multi-step prompts work via a `std::deque<Prompt>` queue. `Submit()` pops them one by one before falling back to dispatch. Adding a new command = one entry in `RegisterCommands()`; the prompt-flow and inline-flow are both handled automatically. - Multi-step prompts work via a `std::deque<Prompt>` queue. `Submit()` pops them one by one before falling back to dispatch. Adding a new command = one entry in `RegisterCommands()`; the prompt-flow and inline-flow are both handled automatically.
- Tab completion: at the top-level prompt (no `pending`), completes built-in command names. Inside a prompt with `path_completion = true` (e.g. the `filename` step of `load`), completes file paths via `std::filesystem::directory_iterator` (handles `~/`, dirs get a trailing `/`). Logic: 1 match → replace; multiple with progress on the longest common prefix → extend; multiple stuck at LCP → list candidates in the visualisation area. - Tab completion: at the top-level prompt (no `pending`), completes built-in command names. Inside a prompt with `path_completion = true` (e.g. the `filename` step of `load`), completes file paths via `std::filesystem::directory_iterator` (handles `~/`, dirs get a trailing `/`). Logic: 1 match → replace; multiple with progress on the longest common prefix → extend; multiple stuck at LCP → list candidates in the visualisation area.
Built-in commands: `new`, `set`, `load`, `duplicate`, `save`, `restore`, `source`, `script-save`, `connect`, `set-type`, `set-signal-type`, `search`, `explore`, `verify`, `net`, `clear`, `help`, `quit`/`exit`. `Esc` cancels an in-progress multi-step prompt. Built-in commands: `new`, `set`, `load`, `duplicate`, `save`, `restore`, `source`, `script-save`, `connect`, `set-connector-type`, `set-signal-type`, `search`, `explore`, `verify`, `net`, `clear`, `help`, `quit`/`exit`. `Esc` cancels an in-progress multi-step prompt.
`set <name> <value>` declares a session-scoped variable. Subsequent commands expand `$name` and `${name}` in their args (substitution happens in `Finalize` between canonical-form recording and `spec.action(args)` — so `history` and `script-save` keep the **unexpanded** form, while the action sees resolved values). Unknown variables are left literal. `vars` is reset by `new`. Validation: `[A-Za-z_][A-Za-z0-9_]*`. `set <name> <value>` declares a session-scoped variable. Subsequent commands expand `$name` and `${name}` in their args (substitution happens in `Finalize` between canonical-form recording and `spec.action(args)` — so `history` and `script-save` keep the **unexpanded** form, while the action sees resolved values). Unknown variables are left literal. `vars` is reset by `new`. Validation: `[A-Za-z_][A-Za-z0-9_]*`.
@@ -97,7 +97,7 @@ Built-in commands: `new`, `set`, `load`, `duplicate`, `save`, `restore`, `source
`source <file>` reads a script line by line and feeds each line through `Submit()`. While the script is running, `in_source = true` is set on the `Tui` and: `source <file>` reads a script line by line and feeds each line through `Submit()`. While the script is running, `in_source = true` is set on the `Tui` and:
- `Dispatch` / `Finalize` skip writing to memory + on-disk history. - `Dispatch` / `Finalize` skip writing to memory + on-disk history.
- After each `Submit`, if `screen_idx != 0` (a screen was opened by an "interactive" command like bare `connect`/`search`/`set-type`), the script is aborted with an error message and `screen_idx` is reset to 0 — interactive screen-opening commands are explicitly disallowed in scripts. - After each `Submit`, if `screen_idx != 0` (a screen was opened by an "interactive" command like bare `connect`/`search`/`set-connector-type`), the script is aborted with an error message and `screen_idx` is reset to 0 — interactive screen-opening commands are explicitly disallowed in scripts.
Pending prompts (from incomplete inline commands) are NOT considered interactive and are filled by subsequent script lines, the way you'd expect. Lines starting with `#` and blank lines are skipped; leading/trailing whitespace is trimmed; `~/` is expanded. Pending prompts (from incomplete inline commands) are NOT considered interactive and are filled by subsequent script lines, the way you'd expect. Lines starting with `#` and blank lines are skipped; leading/trailing whitespace is trimmed; `~/` is expanded.
@@ -114,9 +114,9 @@ Pending prompts (from incomplete inline commands) are NOT considered interactive
The explore screen shows the type in the signal detail header. The explore screen shows the type in the signal detail header.
**Pin role expectations**: every Pin carries an `expected_signal_type` populated by `set-type` from a per-(connector_type, pin_name) lookup (`src/system/pin_role.{hpp,cpp}`). The framework is wired end-to-end; the actual VPX 3U lookup table is currently a stub returning Other for all positions — fill in `vpx_3u_role(col, row, idx)` with the real VITA 46 layout when needed. **Pin role expectations**: every Pin carries an `expected_signal_type` populated by `set-connector-type` from a per-(connector_type, pin_name) lookup (`src/system/pin_role.{hpp,cpp}`). The framework is wired end-to-end; the actual VPX 3U lookup table is currently a stub returning Other for all positions — fill in `vpx_3u_role(col, row, idx)` with the real VITA 46 layout when needed.
**Connector pin layout (preparation)**: `pin_layout(connector_type)` returns the canonical full pin-name list for a known connector kind, and `FillPartFromLayout(part, kind)` materialises NC pins for any layout position absent from the imported netlist. `set-type` calls it after setting `connector_type` (no-op today since `pin_layout` is a stub returning `{}` for everything — populate alongside `vpx_3u_role`). End-to-end chain in place: `set-type → FillPartFromLayout → pin_role`. **Connector pin layout (preparation)**: `pin_layout(connector_type)` returns the canonical full pin-name list for a known connector kind, and `FillPartFromLayout(part, kind)` materialises NC pins for any layout position absent from the imported netlist. `set-connector-type` calls it after setting `connector_type` (no-op today since `pin_layout` is a stub returning `{}` for everything — populate alongside `vpx_3u_role`). End-to-end chain in place: `set-connector-type → FillPartFromLayout → pin_role`.
**`verify` (three passes)**: (1) walks all typed pins and reports local mismatches between `expected_signal_type` and the actual signal type; (2) walks all bridged nets reporting Power↔GndShield inconsistencies; (3) prints a single-line orphan summary `N orphan pin(s) at import (X imported NC, Y dropped singleton)`. The orphan pass filters out pins that appear in any `Connection::pin_map` — those are bridged to a real signal on the peer module (typically `FillIdentityNCs`-materialised) and not real NCs at system level. `net <module> <signal>` prints the BFS-reached `(module, signal)` set with types and an `[INCONSISTENT]` flag. **`verify` (three passes)**: (1) walks all typed pins and reports local mismatches between `expected_signal_type` and the actual signal type; (2) walks all bridged nets reporting Power↔GndShield inconsistencies; (3) prints a single-line orphan summary `N orphan pin(s) at import (X imported NC, Y dropped singleton)`. The orphan pass filters out pins that appear in any `Connection::pin_map` — those are bridged to a real signal on the peer module (typically `FillIdentityNCs`-materialised) and not real NCs at system level. `net <module> <signal>` prints the BFS-reached `(module, signal)` set with types and an `[INCONSISTENT]` flag.
@@ -130,7 +130,7 @@ The explore screen shows the type in the signal detail header.
Exposed as the `analyze` shell command which prints groups (sorted by module + label) followed by anomalies. Designed to be consumed by the upcoming dashboard so the summary is visible at a glance. Tests: `tests/test_analysis.cpp`. Exposed as the `analyze` shell command which prints groups (sorted by module + label) followed by anomalies. Designed to be consumed by the upcoming dashboard so the summary is visible at a glance. Tests: `tests/test_analysis.cpp`.
**Component classification**: every `Part` carries a `ComponentKind kind` (`Passive | Semiconductor | IntegratedCircuit | Connector | TestPoint | Switch | Crystal | Mechanical | Other`) inferred at construction by `infer_component_kind(name)` from the leading reference-designator letter(s) (longest-match: `LED/TP/SW/FB/MK/MP/MH/HS/RA/RN/RP/RV` first, then single-letter R/C/L/F/D/Q/U/J/P/Y/X/S). Recomputed on `restore` (no persistence tag). Not yet exposed in TUI commands — branchpoints will be `search` filter, `set-type` guard, and `explore` header. **Component classification**: every `Part` carries a `ComponentKind kind` (`Passive | Semiconductor | IntegratedCircuit | Connector | TestPoint | Switch | Crystal | Mechanical | Other`) inferred at construction by `infer_component_kind(name)` from the leading reference-designator letter(s) (longest-match: `LED/TP/SW/FB/MK/MP/MH/HS/RA/RN/RP/RV` first, then single-letter R/C/L/F/D/Q/U/J/P/Y/X/S). Recomputed on `restore` (no persistence tag). Not yet exposed in TUI commands — branchpoints will be `search` filter, `set-connector-type` guard, and `explore` header.
`SignalType` lives in its own header `src/system/signal_type.hpp` (extracted from signals to avoid a pins↔signals include cycle). `SignalType` lives in its own header `src/system/signal_type.hpp` (extracted from signals to avoid a pins↔signals include cycle).
@@ -138,13 +138,13 @@ Exposed as the `analyze` shell command which prints groups (sorted by module + l
**NC origin tag**: each `Pin` carries `NcOrigin nc_origin` (`None | ImportedUnconnected | DroppedSingleton`, default `None`). Set in three places: (a) Mentor importer when the signal field starts with `unconnected``ImportedUnconnected`; (b) `drop_singleton_signals(Signals*)` called at the end of `load``DroppedSingleton` on each detached pin (signals with exactly one pin are NC by definition — see commits motivating this); (c) `duplicate` propagates the tag. Pins materialised by `FillIdentityNCs` keep `None` — they have no local signal but are bridged via `pin_map` and shouldn't be counted as orphans. The tag is persisted (see `N` record), reported as a total in `verify`, and tested in `tests/test_nc_origin.cpp`. **NC origin tag**: each `Pin` carries `NcOrigin nc_origin` (`None | ImportedUnconnected | DroppedSingleton`, default `None`). Set in three places: (a) Mentor importer when the signal field starts with `unconnected``ImportedUnconnected`; (b) `drop_singleton_signals(Signals*)` called at the end of `load``DroppedSingleton` on each detached pin (signals with exactly one pin are NC by definition — see commits motivating this); (c) `duplicate` propagates the tag. Pins materialised by `FillIdentityNCs` keep `None` — they have no local signal but are bridged via `pin_map` and shouldn't be counted as orphans. The tag is persisted (see `N` record), reported as a total in `verify`, and tested in `tests/test_nc_origin.cpp`.
**Connector types & transforms**: every `Part` carries a `connector_type` string (default `""`, set via the `set-type` command — inline `set-type m p kind` or bare which opens a TUI screen with module menu, part filter+menu, type input, list of types already in use, and an Apply button). When `connect` validates a pair, it consults `TransformRegistry::lookup(p1->connector_type, p2->connector_type)` (defined in `src/system/transform.{hpp,cpp}`) — both directions of the pair are tried. If neither is registered, an `IdentityTransform` fallback wires each pin of A to the canonical-equivalent pin of B (when present). The resulting `(Pin*, Pin*)` list and the transform's name are stored on the `Connection` (`pin_map`, `transform_name`). To register a real transform: define a `Transform` subclass in `transform.cpp` and call `TransformRegistry::get().add("kindA", "kindB", new MyTransform())` at init — there's no startup hook for this yet, so a small `RegisterBuiltinTransforms()` helper is the natural place to add when more types appear. **Connector types & transforms**: every `Part` carries a `connector_type` string (default `""`, set via the `set-connector-type` command — inline `set-connector-type m p kind` or bare which opens a TUI screen with module menu, part filter+menu, type input, list of types already in use, and an Apply button). When `connect` validates a pair, it consults `TransformRegistry::lookup(p1->connector_type, p2->connector_type)` (defined in `src/system/transform.{hpp,cpp}`) — both directions of the pair are tried. If neither is registered, an `IdentityTransform` fallback wires each pin of A to the canonical-equivalent pin of B (when present). The resulting `(Pin*, Pin*)` list and the transform's name are stored on the `Connection` (`pin_map`, `transform_name`). To register a real transform: define a `Transform` subclass in `transform.cpp` and call `TransformRegistry::get().add("kindA", "kindB", new MyTransform())` at init — there's no startup hook for this yet, so a small `RegisterBuiltinTransforms()` helper is the natural place to add when more types appear.
**Identity wiring uses canonical names**: `IdentityTransform::apply` builds `unordered_map<canonical, Pin*>` for side B and looks up each side-A pin by its canonical form. So `A1` (one card) auto-pairs with `A001` (the other) thanks to `canonical_pin_name` (`pre + zero-padded(3) digit suffix`; mixed/non-numeric returns the original). Same canonicalisation in `CheckIdentityCompatible`. **`pin_role` doesn't need canonicalisation** because `parse_pin` extracts `(col, row)` via `stoi` which already strips leading zeros. **Identity wiring uses canonical names**: `IdentityTransform::apply` builds `unordered_map<canonical, Pin*>` for side B and looks up each side-A pin by its canonical form. So `A1` (one card) auto-pairs with `A001` (the other) thanks to `canonical_pin_name` (`pre + zero-padded(3) digit suffix`; mixed/non-numeric returns the original). Same canonicalisation in `CheckIdentityCompatible`. **`pin_role` doesn't need canonicalisation** because `parse_pin` extracts `(col, row)` via `stoi` which already strips leading zeros.
**Subset wiring + NC backfill**: `CheckIdentityCompatible(a, b, info=&s)` accepts the case where one side's canonical pin set is a subset of the other's — typical when one importer drops NC pins (Altium) and the other doesn't (Mentor). It populates `info` with a non-fatal "N pin(s) only on '<part>'" message. Bidirectional mismatch (both sides have orphans) is still refused. After acceptance, `connect` calls `FillIdentityNCs(p1, p2)` which materialises the orphan canonical positions on the missing side as NC pins (`new Pin(other_side_name)`) — so `Connection::pin_map.size()` matches the larger side's count. Idempotent. **Subset wiring + NC backfill**: `CheckIdentityCompatible(a, b, info=&s)` accepts the case where one side's canonical pin set is a subset of the other's — typical when one importer drops NC pins (Altium) and the other doesn't (Mentor). It populates `info` with a non-fatal "N pin(s) only on '<part>'" message. Bidirectional mismatch (both sides have orphans) is still refused. After acceptance, `connect` calls `FillIdentityNCs(p1, p2)` which materialises the orphan canonical positions on the missing side as NC pins (`new Pin(other_side_name)`) — so `Connection::pin_map.size()` matches the larger side's count. Idempotent.
`screen_idx` mapping: **6 = dashboard (home, set in the constructor)**, 0 = console (textual shell + log view), 1 = search, 2 = connect, 3 = set-type, 4 = explore, 5 = net, 7 = analyze. The dashboard is the boot screen; the console is the secondary screen reachable via the `[c]` shortcut and used to display textual output from `verify`/`analyze`/etc. plus collect arguments for multi-step commands. The label was renamed from "log" to "console" because the screen is also where commands are typed — "log" only described half of what it does. `screen_idx` mapping: **6 = dashboard (home, set in the constructor)**, 0 = console (textual shell + log view), 1 = search, 2 = connect, 3 = set-connector-type, 4 = explore, 5 = net, 7 = analyze. The dashboard is the boot screen; the console is the secondary screen reachable via the `[c]` shortcut and used to display textual output from `verify`/`analyze`/etc. plus collect arguments for multi-step commands. The label was renamed from "log" to "console" because the screen is also where commands are typed — "log" only described half of what it does.
**Dashboard letter conflicts**: with the screen renames, `[c]` now opens the **console** rather than `connect`. The connect command is surfaced as **`[p]lug`** on the dashboard (a UI rename only — the canonical command stays `connect` for script + save/restore stability, with `plug` registered as an alias so the palette finds it under either name). **Dashboard letter conflicts**: with the screen renames, `[c]` now opens the **console** rather than `connect`. The connect command is surfaced as **`[p]lug`** on the dashboard (a UI rename only — the canonical command stays `connect` for script + save/restore stability, with `plug` registered as an alias so the palette finds it under either name).
@@ -260,7 +260,7 @@ Type is set by `infer_signal_types(System*)` (`src/system/analysis.cpp`), called
The analyze screen additionally surfaces two "verify-class" issues, computed the same way as the textual `verify` command: The analyze screen additionally surfaces two "verify-class" issues, computed the same way as the textual `verify` command:
- **pin-role mismatch** — a pin whose `expected_signal_type` (set by `set-type` via `pin_role(connector_type, pin_name)`) disagrees with the actual signal type. - **pin-role mismatch** — a pin whose `expected_signal_type` (set by `set-connector-type` via `pin_role(connector_type, pin_name)`) disagrees with the actual signal type.
- **net-mix** — a bridged net (BFS over `Connection::pin_map`, ≥ 2 members) where `net_type_consistent(net, &dominant)` returns false. Specifically, the net contains both `Power` and `GndShield` signals. - **net-mix** — a bridged net (BFS over `Connection::pin_map`, ≥ 2 members) where `net_type_consistent(net, &dominant)` returns false. Specifically, the net contains both `Power` and `GndShield` signals.
### Component kind ### Component kind
@@ -271,7 +271,7 @@ The analyze screen additionally surfaces two "verify-class" issues, computed the
- Single-letter fallback: `R / C / L / F → Passive`, `D / Q → Semiconductor`, `U → IntegratedCircuit`, `J / P → Connector`, `Y / X → Crystal`, `S → Switch`. - Single-letter fallback: `R / C / L / F → Passive`, `D / Q → Semiconductor`, `U → IntegratedCircuit`, `J / P → Connector`, `Y / X → Crystal`, `S → Switch`.
- No match → `Other`. - No match → `Other`.
Recomputed on `restore` (no persistence tag). Currently not used by any decision flow — branch points are search filter / `set-type` guard / explore header. Recomputed on `restore` (no persistence tag). Currently not used by any decision flow — branch points are search filter / `set-connector-type` guard / explore header.
### Connector wiring (transforms) ### Connector wiring (transforms)
@@ -286,7 +286,7 @@ Pins materialised this way are bridged via `Connection::pin_map` to a real signa
## Gotchas ## Gotchas
- All three importers (`IMPORT_MENTOR`, `IMPORT_ALTIUM`, `IMPORT_ODS`) are wired in `System::Load`. Wrap calls in `try/catch` (the TUI does). - All three importers (`IMPORT_MENTOR`, `IMPORT_ALTIUM`, `IMPORT_ODS`) are wired in `System::Load`. Wrap calls in `try/catch` (the TUI does).
- **Altium importer drops NC pins entirely**: the source format only enumerates pins inside `(signal …)` blocks, so positions not connected to any signal on this card never become `Pin`s. Mentor (via `Explicit Pin:`) and ODS (one row per pin) materialise NC. This is the asymmetry that motivates `FillIdentityNCs` at `connect` time and (eventually) `FillPartFromLayout` at `set-type` time. - **Altium importer drops NC pins entirely**: the source format only enumerates pins inside `(signal …)` blocks, so positions not connected to any signal on this card never become `Pin`s. Mentor (via `Explicit Pin:`) and ODS (one row per pin) materialise NC. This is the asymmetry that motivates `FillIdentityNCs` at `connect` time and (eventually) `FillPartFromLayout` at `set-connector-type` time.
- **Mentor importer + NC**: the Mentor `.qcv` format names every pin's signal explicitly. Sentinel values like `'unconnected'` or `'unconnected (by TERM)'` mean NC — the parser detects them via `is_nc_signal_name` (lowercase prefix match) and keeps the pin on the part with no signal, tagged `ImportedUnconnected`. Additionally, after each `load` the system runs `drop_singleton_signals(mod->signals)`: any signal whose pin set has size 1 is unconnected by definition (electrically nowhere to go), so it is detached and the lone pin is tagged `DroppedSingleton`. The count is shown inline in the `load` output. The semantics covers both Mentor patterns and the few `NC_*`-prefixed signals that turn out to be singletons in real-world boards — the name `NC_*` alone is *not* enough (most of them connect two or more parts and are real bridges, even if cosmetically called NC). - **Mentor importer + NC**: the Mentor `.qcv` format names every pin's signal explicitly. Sentinel values like `'unconnected'` or `'unconnected (by TERM)'` mean NC — the parser detects them via `is_nc_signal_name` (lowercase prefix match) and keeps the pin on the part with no signal, tagged `ImportedUnconnected`. Additionally, after each `load` the system runs `drop_singleton_signals(mod->signals)`: any signal whose pin set has size 1 is unconnected by definition (electrically nowhere to go), so it is detached and the lone pin is tagged `DroppedSingleton`. The count is shown inline in the `load` output. The semantics covers both Mentor patterns and the few `NC_*`-prefixed signals that turn out to be singletons in real-world boards — the name `NC_*` alone is *not* enough (most of them connect two or more parts and are real bridges, even if cosmetically called NC).
- ODS importer: each spreadsheet sheet becomes a `Part` (sheet name = part name). Rows are pin/signal pairs; the **first non-empty row of each sheet is dropped as a header** (no validation of header content). Empty cells skip the row; `"NC"` keeps the pin in the part but doesn't connect it to a signal. Pins or parts whose name collides (rare in well-formed sheets) are silently dropped. - ODS importer: each spreadsheet sheet becomes a `Part` (sheet name = part name). Rows are pin/signal pairs; the **first non-empty row of each sheet is dropped as a header** (no validation of header content). Empty cells skip the row; `"NC"` keeps the pin in the part but doesn't connect it to a signal. Pins or parts whose name collides (rare in well-formed sheets) are silently dropped.
- `System::Load` throws `std::runtime_error("Unknown import type")` for any value outside the three enum cases. - `System::Load` throws `std::runtime_error("Unknown import type")` for any value outside the three enum cases.

View File

@@ -111,7 +111,7 @@ deserves attention:
| Tag | What it means | | Tag | What it means |
|---|---| |---|---|
| `[pin-role]` | A connector pin is typed (via `set-type`) as Power or Gnd but the actual signal landing on it disagrees. | | `[pin-role]` | A connector pin is typed (via `set-connector-type`) as Power or Gnd but the actual signal landing on it disagrees. |
| `[net-mix]` | A net bridged across modules carries both Power and Gnd signals — almost always a topology mistake. | | `[net-mix]` | A net bridged across modules carries both Power and Gnd signals — almost always a topology mistake. |
| `[diff-pair-orphan]` | `STEM_P` with no `STEM_N` in the same module. | | `[diff-pair-orphan]` | `STEM_P` with no `STEM_N` in the same module. |
| `[bus-gap]` | A bus is missing one or more index values inside its range. | | `[bus-gap]` | A bus is missing one or more index values inside its range. |
@@ -128,7 +128,7 @@ Every classification is advisory. To force a different type:
on a signal entry → a popup lets you pick `power` / `gnd` / on a signal entry → a popup lets you pick `power` / `gnd` /
`other`. Or type `set-signal-type <module> <signal> <type>` in the `other`. Or type `set-signal-type <module> <signal> <type>` in the
console (or from the palette). console (or from the palette).
- **Connector type**: `set-type <module> <part> <connector-kind>` - **Connector type**: `set-connector-type <module> <part> <connector-kind>`
(also via the dashboard `[t]` shortcut). This drives the pin role (also via the dashboard `[t]` shortcut). This drives the pin role
expectations, which feed the `pin-role` check. expectations, which feed the `pin-role` check.

View File

@@ -31,8 +31,8 @@ commands. The most common bring-up looks like this:
> new > new
> load backplane /path/to/netlists/backplane.NET altium > load backplane /path/to/netlists/backplane.NET altium
> load payload1 /path/to/netlists/payload.qcv mentor > load payload1 /path/to/netlists/payload.qcv mentor
> set-type backplane J20 vpx-3u-bkp-p0 > set-connector-type backplane J20 vpx-3u-bkp-p0
> set-type payload1 P0 vpx-3u-payload-p0 > set-connector-type payload1 P0 vpx-3u-payload-p0
> connect backplane J20 payload1 P0 > connect backplane J20 payload1 P0
> verify > verify
> save my-system.essim > save my-system.essim
@@ -46,7 +46,7 @@ Things to try at any time:
| Help on one command | `help <name>` | | Help on one command | `help <name>` |
| Scroll back through output | `PageUp` / `PageDown`, `Home`, `End` | | Scroll back through output | `PageUp` / `PageDown`, `Home`, `End` |
| Re-run a previous command | ↑ / ↓ (also `history` is on disk) | | Re-run a previous command | ↑ / ↓ (also `history` is on disk) |
| Tab-complete a command name | `setTab``set-type` etc. | | Tab-complete a command name | `setTab``set-connector-type` etc. |
| Cancel a multi-step prompt | `Esc` | | Cancel a multi-step prompt | `Esc` |
| Leave essim | `quit` (or `exit`) | | Leave essim | `quit` (or `exit`) |
@@ -65,7 +65,7 @@ share the same conventions:
a sourced script. A sourced script must use the inline form of these a sourced script. A sourced script must use the inline form of these
commands instead. commands instead.
Today's interactive screens: `connect`, `search`, `set-type`, Today's interactive screens: `connect`, `search`, `set-connector-type`,
`explore`, `net`. See [`commands.md`](commands.md) for each. `explore`, `net`. See [`commands.md`](commands.md) for each.
## Saving, restoring, replaying ## Saving, restoring, replaying

View File

@@ -15,7 +15,7 @@
// NOTE: real VITA 46 pin roles are connector-/profile-specific (data lanes, // NOTE: real VITA 46 pin roles are connector-/profile-specific (data lanes,
// power planes, GND chassis, etc.). The placeholders below are intentionally // power planes, GND chassis, etc.). The placeholders below are intentionally
// minimal — fill in the actual per-(col,row) roles for your design when the // minimal — fill in the actual per-(col,row) roles for your design when the
// reference is available; the rest of the chain (set-type → verify) is // reference is available; the rest of the chain (set-connector-type → verify) is
// already wired through this single function. // already wired through this single function.
namespace { namespace {

View File

@@ -9,7 +9,7 @@
class Part; class Part;
// For a given connector type and pin position, return the expected SignalType // For a given connector type and pin position, return the expected SignalType
// (Power / GndShield / Other). Used at `set-type` to populate each pin's // (Power / GndShield / Other). Used at `set-connector-type` to populate each pin's
// `expected_signal_type`, then later by `verify` to flag mismatches between // `expected_signal_type`, then later by `verify` to flag mismatches between
// the connector's expectation and the actual signal's inferred/declared type. // the connector's expectation and the actual signal's inferred/declared type.
// //
@@ -23,7 +23,7 @@ SignalType pin_role(const std::string &connector_type,
// vector for connector types that don't have a registered layout — callers // vector for connector types that don't have a registered layout — callers
// must treat that as "unknown, do not auto-fill". // must treat that as "unknown, do not auto-fill".
// //
// Used at `set-type` to materialise NC pins for positions absent from the // Used at `set-connector-type` to materialise NC pins for positions absent from the
// imported netlist (Altium drops NC, Mentor doesn't). Stub today: every // imported netlist (Altium drops NC, Mentor doesn't). Stub today: every
// known kind returns {} — populate alongside `vpx_3u_role`. // known kind returns {} — populate alongside `vpx_3u_role`.
std::vector<std::string> pin_layout(const std::string &connector_type); std::vector<std::string> pin_layout(const std::string &connector_type);

View File

@@ -29,7 +29,7 @@ class Pin : public SystemElement
public: public:
Pin(std::string name); Pin(std::string name);
Part *prnt; ///< Pointer to the parent part. Part *prnt; ///< Pointer to the parent part.
SignalType expected_signal_type; ///< Set from connector_type at set-type. SignalType expected_signal_type; ///< Set from connector_type at set-connector-type.
NcOrigin nc_origin = NcOrigin::None; NcOrigin nc_origin = NcOrigin::None;
bool connected(); bool connected();
Signal *signal() const { return sig; } Signal *signal() const { return sig; }

View File

@@ -19,7 +19,7 @@
// instances are registered at startup, under the type pairs // instances are registered at startup, under the type pairs
// (vpx-3u-bkp-N, vpx-3u-payload-N) // (vpx-3u-bkp-N, vpx-3u-payload-N)
// for N in {0, 1, 2}. Tag each Part with the correct connector_type via // for N in {0, 1, 2}. Tag each Part with the correct connector_type via
// `set-type`, then `connect` will pick the right transform automatically. // `set-connector-type`, then `connect` will pick the right transform automatically.
class VpxTransform : public Transform class VpxTransform : public Transform
{ {
public: public:

View File

@@ -431,7 +431,7 @@ void Tui::RegisterCommands() {
"override the auto-detected signal type (power | gnd | other)", "override the auto-detected signal type (power | gnd | other)",
}; };
commands["set-type"] = { commands["set-connector-type"] = {
{{"module", Completion::None}, {{"module", Completion::None},
{"part (name or pattern)", Completion::None}, {"part (name or pattern)", Completion::None},
{"connector type (free string, e.g. vpx-bp, vpx-payload)", Completion::None}}, {"connector type (free string, e.g. vpx-bp, vpx-payload)", Completion::None}},
@@ -454,7 +454,7 @@ void Tui::RegisterCommands() {
} }
if (args.size() != 3) { if (args.size() != 3) {
Print("usage: set-type <module> <part> <kind> (or no args for interactive)"); Print("usage: set-connector-type <module> <part> <kind> (or no args for interactive)");
return; return;
} }
@@ -480,7 +480,7 @@ void Tui::RegisterCommands() {
} }
std::string err = ValidatePartForKind(prt, args[2]); std::string err = ValidatePartForKind(prt, args[2]);
if (!err.empty()) { if (!err.empty()) {
Print("set-type refused: " + err); Print("set-connector-type refused: " + err);
return; return;
} }
prt->connector_type = args[2]; prt->connector_type = args[2];
@@ -490,7 +490,7 @@ void Tui::RegisterCommands() {
Print(mod->name + "/" + prt->name + ": connector_type = " Print(mod->name + "/" + prt->name + ": connector_type = "
+ (args[2].empty() ? "(none)" : args[2])); + (args[2].empty() ? "(none)" : args[2]));
if (filled > 0) if (filled > 0)
Print("set-type: materialised " + std::to_string(filled) Print("set-connector-type: materialised " + std::to_string(filled)
+ " NC pin(s) from connector layout"); + " NC pin(s) from connector layout");
}, },
/*prompt_for_missing=*/ false, /*prompt_for_missing=*/ false,
@@ -498,7 +498,6 @@ void Tui::RegisterCommands() {
/*scriptable=*/ true, /*scriptable=*/ true,
/*interactive=*/ true, /*interactive=*/ true,
}; };
commands["connect"] = { commands["connect"] = {
{{"module1", Completion::None}, {{"module1", Completion::None},
{"part1 (name or pattern)", Completion::None}, {"part1 (name or pattern)", Completion::None},
@@ -596,7 +595,7 @@ void Tui::RegisterCommands() {
+ (p1->connector_type.empty() ? "(none)" : p1->connector_type) + (p1->connector_type.empty() ? "(none)" : p1->connector_type)
+ "' ↔ '" + "' ↔ '"
+ (p2->connector_type.empty() ? "(none)" : p2->connector_type) + (p2->connector_type.empty() ? "(none)" : p2->connector_type)
+ "'. Set matching types via 'set-type' first."); + "'. Set matching types via 'set-connector-type' first.");
return; return;
} }
std::string info; std::string info;

View File

@@ -244,34 +244,35 @@ Component Tui::BuildAnalyzeScreen() {
{"Esc", "dashboard"}, {"Esc", "dashboard"},
}); });
// Glossary: only relevant on the Types tab. Same width as the help // Glossary: shown only when the Types tab is focused. Same border
// panel for visual coherence. // styling as the help panel (borderRounded) for visual coherence;
// text uses `paragraph` so lines wrap cleanly to the panel width.
auto term = [](const std::string &name, const std::string &desc) {
return vbox({
text(name) | bold,
paragraph(desc) | dim,
text(""),
});
};
Element types_glossary = vbox({ Element types_glossary = vbox({
text(" types ") | bold, text(" type glossary ") | bold | center,
separator(), separator(),
hbox({text(" Power ") | bold | size(WIDTH, EQUAL, 12), term("Power",
text("name + structure") | flex}), "Name suggests Power AND structure agrees: fan-out ≥ 4 pins, "
hbox({text(" ") | dim | size(WIDTH, EQUAL, 12), "or a voltage pattern in the name (e.g. 3V3, 5V, 12V)."),
text("(fan-out ≥ 4 or") | dim | flex}), term("Suspect Power",
hbox({text(" ") | dim | size(WIDTH, EQUAL, 12), "Name suggests Power but the structural check failed — "
text(" voltage in name)") | dim | flex}), "fan-out too low and no voltage in the name."),
text(""), term("Hard floor",
hbox({text(" Suspect ") | bold | size(WIDTH, EQUAL, 12), "Fan-out below 3 pins forces Other regardless of the name. "
text("name only,") | flex}), "A real rail physically cannot live on 1-2 pads."),
hbox({text(" ") | dim | size(WIDTH, EQUAL, 12), term("Gnd",
text("weak evidence") | dim | flex}), "Name matches GND, SHIELD, CHASSIS or EARTH. Name alone is "
text(""), "enough — false positives are essentially nil."),
hbox({text(" hard ") | bold | size(WIDTH, EQUAL, 12), }) | borderRounded | size(WIDTH, EQUAL, 32);
text("fan-out < 3 →") | flex}),
hbox({text(" floor ") | bold | size(WIDTH, EQUAL, 12),
text("never Power") | flex}),
text(""),
hbox({text(" Gnd ") | bold | size(WIDTH, EQUAL, 12),
text("name only") | flex}),
}) | size(WIDTH, EQUAL, 30);
Element side = (analyze_focus_idx == 2) Element side = (analyze_focus_idx == 2)
? vbox({help, text(""), types_glossary}) | size(WIDTH, EQUAL, 30) ? vbox({help, text(""), types_glossary}) | size(WIDTH, EQUAL, 32)
: help; : help;
return vbox({ return vbox({

View File

@@ -76,7 +76,7 @@ Component Tui::BuildConnectScreen() {
+ (p1->connector_type.empty() ? "(none)" : p1->connector_type) + (p1->connector_type.empty() ? "(none)" : p1->connector_type)
+ "' ↔ '" + "' ↔ '"
+ (p2->connector_type.empty() ? "(none)" : p2->connector_type) + (p2->connector_type.empty() ? "(none)" : p2->connector_type)
+ "'. Set matching types via 'set-type' first."); + "'. Set matching types via 'set-connector-type' first.");
screen_idx = 0; screen_idx = 0;
return; return;
} }

View File

@@ -296,7 +296,7 @@ Component Tui::BuildDashboardScreen() {
{"c", "console"}, {"c", "console"},
{"s", "search"}, {"s", "search"},
{"p", "plug"}, {"p", "plug"},
{"t", "set-type"}, {"t", "set-connector-type"},
{"e", "explore"}, {"e", "explore"},
{"n", "net"}, {"n", "net"},
{"a", "analyze (verify + groups)"}, {"a", "analyze (verify + groups)"},

View File

@@ -58,7 +58,7 @@ Component Tui::BuildSettypeScreen() {
std::string msg = mod->name + "/" + prt->name + " = " std::string msg = mod->name + "/" + prt->name + " = "
+ (settype_type.empty() ? "(none)" : settype_type); + (settype_type.empty() ? "(none)" : settype_type);
settype_status = "applied: " + msg; settype_status = "applied: " + msg;
Print("set-type " + msg); Print("set-connector-type " + msg);
} catch (const std::exception &e) { } catch (const std::exception &e) {
settype_status = std::string("failed: ") + e.what(); settype_status = std::string("failed: ") + e.what();
} }
@@ -129,11 +129,11 @@ Component Tui::BuildSettypeScreen() {
auto title = hbox({ auto title = hbox({
text(" essim ") | bold, text(" essim ") | bold,
text("") | dim, text("") | dim,
text("set-type") | bold, text("set-connector-type") | bold,
text(" — tag a part with its connector kind (drives transforms + pin roles)") | dim, text(" — tag a part with its connector kind (drives transforms + pin roles)") | dim,
}); });
Element help = RenderHelpPanel("set-type", { Element help = RenderHelpPanel("set-connector-type", {
{"Tab", "cycle focus"}, {"Tab", "cycle focus"},
{"↑/↓", "navigate menu"}, {"↑/↓", "navigate menu"},
{"Enter", "on [Apply] → tag"}, {"Enter", "on [Apply] → tag"},

View File

@@ -100,7 +100,7 @@ void Tui::Run() {
if (e == Event::Character("c")) { screen_idx = 0; return true; } if (e == Event::Character("c")) { screen_idx = 0; return true; }
if (e == Event::Character("p")) { Dispatch("connect"); return true; } if (e == Event::Character("p")) { Dispatch("connect"); return true; }
if (e == Event::Character("s")) { Dispatch("search"); return true; } if (e == Event::Character("s")) { Dispatch("search"); return true; }
if (e == Event::Character("t")) { Dispatch("set-type"); return true; } if (e == Event::Character("t")) { Dispatch("set-connector-type"); return true; }
if (e == Event::Character("e")) { Dispatch("explore"); return true; } if (e == Event::Character("e")) { Dispatch("explore"); return true; }
if (e == Event::Character("n")) { Dispatch("net"); return true; } if (e == Event::Character("n")) { Dispatch("net"); return true; }
// [a]nalyze is the unified verify + analyze screen (issues + // [a]nalyze is the unified verify + analyze screen (issues +
@@ -121,7 +121,7 @@ void Tui::Run() {
if (e == Event::TabReverse) { explore_focus_idx = (explore_focus_idx + 5) % 6; return true; } if (e == Event::TabReverse) { explore_focus_idx = (explore_focus_idx + 5) % 6; return true; }
return false; return false;
case 3: // set-type case 3: // set-connector-type
if (e == Event::Escape) { screen_idx = 6; return true; } if (e == Event::Escape) { screen_idx = 6; return true; }
if (e == Event::Tab) { settype_focus_idx = (settype_focus_idx + 1) % 5; return true; } if (e == Event::Tab) { settype_focus_idx = (settype_focus_idx + 1) % 5; return true; }
if (e == Event::TabReverse) { settype_focus_idx = (settype_focus_idx + 4) % 5; return true; } if (e == Event::TabReverse) { settype_focus_idx = (settype_focus_idx + 4) % 5; return true; }

View File

@@ -188,7 +188,7 @@ private:
// and close the popup. // and close the popup.
void ApplySignalTypeChoice(); void ApplySignalTypeChoice();
// Filtered part list rebuild (used by connect & set-type screens) // Filtered part list rebuild (used by connect & set-connector-type screens)
void RefreshFilteredPartList(const std::vector<std::string> &modules, void RefreshFilteredPartList(const std::vector<std::string> &modules,
int m_idx, int m_idx,
const std::string &filter, const std::string &filter,

View File

@@ -14,15 +14,18 @@ Element RenderHelpPanel(const std::string &title,
Elements rows; Elements rows;
for (const auto &e : entries) { for (const auto &e : entries) {
rows.push_back(hbox({ rows.push_back(hbox({
text(" " + e.key) | bold | size(WIDTH, EQUAL, KEY_W), text(e.key) | bold | size(WIDTH, EQUAL, KEY_W),
text(e.desc) | flex, text(e.desc) | flex,
})); }));
} }
// borderRounded gives the panel a distinct visual boundary, so the
// user can find it without ambiguity even when the main content is
// dense (e.g. the analyze screen).
return vbox({ return vbox({
text(" " + title + " ") | bold, text(" " + title + " ") | bold | center,
separator(), separator(),
vbox(std::move(rows)), vbox(std::move(rows)),
}) | size(WIDTH, EQUAL, 30); }) | borderRounded | size(WIDTH, EQUAL, 32);
} }

View File

@@ -27,32 +27,32 @@ load peripheral3 $peripheral3_nets mentor
# ---------------------------------------------------------------- VPX tags # ---------------------------------------------------------------- VPX tags
# Backplane payload-side connectors, one slot per (Jx0,Jx1,Jx2): # Backplane payload-side connectors, one slot per (Jx0,Jx1,Jx2):
# J2x → payload1, J3x → payload2, J4x → payload3, J5x → payload4. # J2x → payload1, J3x → payload2, J4x → payload3, J5x → payload4.
set-type backplane J20 vpx-3u-bkp-p0 set-connector-type backplane J20 vpx-3u-bkp-p0
set-type backplane J21 vpx-3u-bkp-p1 set-connector-type backplane J21 vpx-3u-bkp-p1
set-type backplane J22 vpx-3u-bkp-p2 set-connector-type backplane J22 vpx-3u-bkp-p2
set-type backplane J30 vpx-3u-bkp-p0 set-connector-type backplane J30 vpx-3u-bkp-p0
set-type backplane J31 vpx-3u-bkp-p1 set-connector-type backplane J31 vpx-3u-bkp-p1
set-type backplane J32 vpx-3u-bkp-p2 set-connector-type backplane J32 vpx-3u-bkp-p2
set-type backplane J40 vpx-3u-bkp-p0 set-connector-type backplane J40 vpx-3u-bkp-p0
set-type backplane J41 vpx-3u-bkp-p1 set-connector-type backplane J41 vpx-3u-bkp-p1
set-type backplane J42 vpx-3u-bkp-p2 set-connector-type backplane J42 vpx-3u-bkp-p2
set-type backplane J50 vpx-3u-bkp-p0 set-connector-type backplane J50 vpx-3u-bkp-p0
set-type backplane J51 vpx-3u-bkp-p1 set-connector-type backplane J51 vpx-3u-bkp-p1
set-type backplane J52 vpx-3u-bkp-p2 set-connector-type backplane J52 vpx-3u-bkp-p2
# Payload connectors on each plug-in card. # Payload connectors on each plug-in card.
set-type payload1 P0 vpx-3u-payload-p0 set-connector-type payload1 P0 vpx-3u-payload-p0
set-type payload1 P1 vpx-3u-payload-p1 set-connector-type payload1 P1 vpx-3u-payload-p1
set-type payload1 P2 vpx-3u-payload-p2 set-connector-type payload1 P2 vpx-3u-payload-p2
set-type payload2 P0 vpx-3u-payload-p0 set-connector-type payload2 P0 vpx-3u-payload-p0
set-type payload2 P1 vpx-3u-payload-p1 set-connector-type payload2 P1 vpx-3u-payload-p1
set-type payload2 P2 vpx-3u-payload-p2 set-connector-type payload2 P2 vpx-3u-payload-p2
set-type payload3 P0 vpx-3u-payload-p0 set-connector-type payload3 P0 vpx-3u-payload-p0
set-type payload3 P1 vpx-3u-payload-p1 set-connector-type payload3 P1 vpx-3u-payload-p1
set-type payload3 P2 vpx-3u-payload-p2 set-connector-type payload3 P2 vpx-3u-payload-p2
set-type payload4 P0 vpx-3u-payload-p0 set-connector-type payload4 P0 vpx-3u-payload-p0
set-type payload4 P1 vpx-3u-payload-p1 set-connector-type payload4 P1 vpx-3u-payload-p1
set-type payload4 P2 vpx-3u-payload-p2 set-connector-type payload4 P2 vpx-3u-payload-p2
# ---------------------------------------------------------------- VPX wiring # ---------------------------------------------------------------- VPX wiring
# Each connect dispatches via the registered vpx-3u transform. # Each connect dispatches via the registered vpx-3u transform.

View File

@@ -16,7 +16,7 @@
namespace { namespace {
// Build a Part with `pin_names` pins, attached to a fresh module so prnt // Build a Part with `pin_names` pins, attached to a fresh module so prnt
// chains exist (set-type's validation depends on pins; transforms don't). // chains exist (set-connector-type's validation depends on pins; transforms don't).
Part *make_part(Module *mod, const std::string &part_name, Part *make_part(Module *mod, const std::string &part_name,
const std::vector<std::string> &pin_names) { const std::vector<std::string> &pin_names) {
Part *p = new Part(part_name); Part *p = new Part(part_name);