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>
This commit is contained in:
@@ -1,10 +1,12 @@
|
||||
#include "transform.hpp"
|
||||
|
||||
#include "parts.hpp"
|
||||
#include "pin_name.hpp"
|
||||
#include "pins.hpp"
|
||||
#include "transform_vpx.hpp"
|
||||
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <exception>
|
||||
@@ -12,40 +14,83 @@
|
||||
|
||||
Transform::Transform(std::string name) : name(std::move(name)) {}
|
||||
|
||||
std::string CheckIdentityCompatible(const Part *a, const Part *b)
|
||||
std::string CheckIdentityCompatible(const Part *a, const Part *b, std::string *info)
|
||||
{
|
||||
if (!a || !b) return "missing part";
|
||||
// Compare on canonical names so that A1 ↔ A001 etc. count as the same pin.
|
||||
std::set<std::string> a_pins, b_pins;
|
||||
for (auto &kv : *a) a_pins.insert(kv.first);
|
||||
for (auto &kv : *b) b_pins.insert(kv.first);
|
||||
std::unordered_map<std::string, std::string> a_orig, b_orig;
|
||||
for (auto &kv : *a) {
|
||||
std::string c = canonical_pin_name(kv.first);
|
||||
a_pins.insert(c);
|
||||
a_orig.emplace(c, kv.first);
|
||||
}
|
||||
for (auto &kv : *b) {
|
||||
std::string c = canonical_pin_name(kv.first);
|
||||
b_pins.insert(c);
|
||||
b_orig.emplace(c, kv.first);
|
||||
}
|
||||
if (a_pins == b_pins) return "";
|
||||
|
||||
std::vector<std::string> only_a, only_b;
|
||||
for (const auto &n : a_pins) if (!b_pins.count(n)) only_a.push_back(n);
|
||||
for (const auto &n : b_pins) if (!a_pins.count(n)) only_b.push_back(n);
|
||||
for (const auto &n : a_pins) if (!b_pins.count(n)) only_a.push_back(a_orig[n]);
|
||||
for (const auto &n : b_pins) if (!a_pins.count(n)) only_b.push_back(b_orig[n]);
|
||||
|
||||
std::string msg = "identity wiring requires same pin names on both sides";
|
||||
if (!only_a.empty())
|
||||
// True bidirectional mismatch — refuse.
|
||||
if (!only_a.empty() && !only_b.empty()) {
|
||||
std::string msg = "identity wiring requires the pin sets to be related";
|
||||
msg += "; only on '" + a->name + "': "
|
||||
+ std::to_string(only_a.size()) + " (e.g. " + only_a.front() + ")";
|
||||
if (!only_b.empty())
|
||||
msg += "; only on '" + b->name + "': "
|
||||
+ std::to_string(only_b.size()) + " (e.g. " + only_b.front() + ")";
|
||||
return msg;
|
||||
return msg;
|
||||
}
|
||||
|
||||
// One side is a (strict) subset of the other — accept, surface as info.
|
||||
if (info) {
|
||||
const auto &orphans = only_a.empty() ? only_b : only_a;
|
||||
const std::string &side = only_a.empty() ? b->name : a->name;
|
||||
*info = std::to_string(orphans.size()) + " pin(s) only on '" + side
|
||||
+ "' (e.g. " + orphans.front() + ") — wiring intersection";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
int FillIdentityNCs(Part *a, Part *b) {
|
||||
if (!a || !b) return 0;
|
||||
std::unordered_map<std::string, std::string> a_canon, b_canon;
|
||||
for (auto &kv : *a) a_canon.emplace(canonical_pin_name(kv.first), kv.first);
|
||||
for (auto &kv : *b) b_canon.emplace(canonical_pin_name(kv.first), kv.first);
|
||||
|
||||
auto fill = [](Part *dst,
|
||||
const std::unordered_map<std::string, std::string> &src,
|
||||
const std::unordered_map<std::string, std::string> &dst_canon) {
|
||||
int n = 0;
|
||||
for (const auto &kv : src) {
|
||||
if (dst_canon.count(kv.first)) continue; // already there canonically
|
||||
if (dst->exists(kv.second)) continue; // safety net for exotic clashes
|
||||
dst->add(new Pin(kv.second));
|
||||
++n;
|
||||
}
|
||||
return n;
|
||||
};
|
||||
int added = 0;
|
||||
added += fill(a, b_canon, a_canon);
|
||||
added += fill(b, a_canon, b_canon);
|
||||
return added;
|
||||
}
|
||||
|
||||
IdentityTransform::IdentityTransform() : Transform("identity") {}
|
||||
|
||||
std::vector<std::pair<Pin *, Pin *>> IdentityTransform::apply(Part *a, Part *b) const
|
||||
{
|
||||
// Match pins on canonical name so A1 (one card) wires to A001 (the other).
|
||||
std::vector<std::pair<Pin *, Pin *>> out;
|
||||
std::unordered_map<std::string, Pin *> b_canon;
|
||||
for (auto &kv : *b) b_canon.emplace(canonical_pin_name(kv.first), kv.second);
|
||||
for (auto &kv : *a) {
|
||||
try {
|
||||
Pin *pb = b->get(kv.first);
|
||||
out.emplace_back(kv.second, pb);
|
||||
} catch (const std::exception &) {
|
||||
// No same-name pin on the other side — skip.
|
||||
}
|
||||
auto it = b_canon.find(canonical_pin_name(kv.first));
|
||||
if (it != b_canon.end()) out.emplace_back(kv.second, it->second);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user