Files
essim/src/system/nets.cpp
François c3bb00cb4d Altium import, nets, canonical pins, component kinds, set/$var, scrollback, source modal.
Major additions, all wired end-to-end with doctest coverage:

- Altium netlist importer (`imports/import_altium.{hpp,cpp}`): two-pass
  parser for `[ ]` parts and `( )` signals; `System::Load` no longer has
  the IMPORT_ALTIUM hole.
- `duplicate <src> <dst>` deep-copies a module (signals, parts, pins,
  rewired signals); connections excluded by design.
- Nets (`system/nets.{hpp,cpp}`): BFS over `Connection::pin_map` to
  return the transitive (Module, Signal) closure. `verify` extended with
  a second pass flagging Power↔GndShield inconsistencies in bridged
  nets; new `net <module> <signal>` command for inspection.
- Canonical pin names (`system/pin_name.{hpp,cpp}`): zero-padded digit
  suffix lets A1 ↔ A001 pair via `IdentityTransform` and
  `CheckIdentityCompatible` without losing the imported notation.
- Component classification (`system/component_kind.{hpp,cpp}`):
  `Part::kind` inferred at construction from the reference-designator
  prefix (longest-match: LED/TP/SW/FB/MK/MP/MH/HS/RA/RN/RP/RV first,
  then R/C/L/F/D/Q/U/J/P/Y/X/S).
- Identity wiring tolerance: `CheckIdentityCompatible` accepts the
  subset case (typical when one importer drops NC pins, e.g. Altium)
  and surfaces orphans as an info string. `FillIdentityNCs`
  materialises orphan canonical positions as NC pins on the missing
  side at connect time.
- Connector layout preparation: `pin_layout(kind)` and
  `FillPartFromLayout(part, kind)` stubs in `pin_role`, called from
  `set-type`. Empty today; populate alongside `vpx_3u_role`.
- TUI scrollback: PageUp/PageDown step 10 lines, Home/End jump to
  ends; `Print()` snaps back to the tail.
- `set <name> <value>` declares session variables; `$name` / `${name}`
  expanded inside `Finalize` between canonical-form recording and the
  action call — history and script-save preserve `$var` references.
- Long `source` scripts now show a centred "Computing…" modal with a
  N/M progress counter. Driven by a ticker thread that posts one
  paced `Event::Special` per processed line, ack'd by the main thread,
  so heavy lines don't backlog ticks and freeze the counter.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 20:28:21 +02:00

118 lines
3.5 KiB
C++

#include "system/nets.hpp"
#include "system/connect.hpp"
#include "system/modules.hpp"
#include "system/parts.hpp"
#include "system/pins.hpp"
#include "system/signals.hpp"
#include "system/system.hpp"
#include <queue>
#include <unordered_map>
#include <unordered_set>
namespace {
using SigKey = std::pair<Module *, Signal *>;
struct SigKeyHash {
size_t operator()(const SigKey &k) const noexcept {
return std::hash<void *>()(k.first) ^ (std::hash<void *>()(k.second) << 1);
}
};
std::unordered_map<Pin *, std::vector<Pin *>>
build_bridges(System *sys) {
std::unordered_map<Pin *, std::vector<Pin *>> br;
if (!sys || !sys->connections()) return br;
for (auto &kv : *sys->connections()) {
for (auto &wp : kv.second->pin_map) {
br[wp.first].push_back(wp.second);
br[wp.second].push_back(wp.first);
}
}
return br;
}
void bfs_net(const std::unordered_map<Pin *, std::vector<Pin *>> &bridges,
Module *start_m, Signal *start_s,
std::unordered_set<SigKey, SigKeyHash> &visited,
Net &out) {
if (!start_m || !start_s) return;
SigKey start{start_m, start_s};
if (!visited.insert(start).second) return;
std::queue<SigKey> q;
q.push(start);
while (!q.empty()) {
auto [m, s] = q.front();
q.pop();
out.members.emplace_back(m, s);
for (auto &pkv : *s) {
auto it = bridges.find(pkv.second);
if (it == bridges.end()) continue;
for (Pin *other : it->second) {
Signal *os = other->signal();
if (!os) continue;
Part *opart = other->prnt;
if (!opart) continue;
Module *om = opart->prnt;
if (!om) continue;
SigKey k{om, os};
if (visited.insert(k).second) q.push(k);
}
}
}
}
} // namespace
Net find_net(System *sys, Module *m, Signal *s) {
Net n;
if (!sys || !m || !s) return n;
auto bridges = build_bridges(sys);
std::unordered_set<SigKey, SigKeyHash> visited;
bfs_net(bridges, m, s, visited, n);
return n;
}
Net find_net(System *sys, Pin *pin) {
if (!sys || !pin || !pin->signal() || !pin->prnt) return {};
return find_net(sys, pin->prnt->prnt, pin->signal());
}
std::vector<Net> compute_all_nets(System *sys) {
std::vector<Net> nets;
if (!sys || !sys->modules()) return nets;
auto bridges = build_bridges(sys);
std::unordered_set<SigKey, SigKeyHash> visited;
for (auto &mkv : *sys->modules()) {
Module *m = mkv.second;
if (!m->signals) continue;
for (auto &skv : *m->signals) {
SigKey k{m, skv.second};
if (visited.count(k)) continue;
Net n;
bfs_net(bridges, m, skv.second, visited, n);
if (!n.members.empty()) nets.push_back(std::move(n));
}
}
return nets;
}
bool net_type_consistent(const Net &net, SignalType &dominant) {
bool seen_power = false, seen_gnd = false;
for (auto &mp : net.members) {
if (!mp.second) continue;
switch (mp.second->type) {
case SignalType::Power: seen_power = true; break;
case SignalType::GndShield: seen_gnd = true; break;
default: break;
}
}
if (seen_power && seen_gnd) { dominant = SignalType::Power; return false; }
dominant = seen_power ? SignalType::Power
: seen_gnd ? SignalType::GndShield
: SignalType::Other;
return true;
}