build: split core/ from frontends/; prepare for multiple GUI/TUI targets

Reorganise the tree into business vs frontend as separate directories:
  src/core/{domain,imports,app}   (was system/, imports/, app/)
  src/frontends/tui/              (was tui/ + main.cpp)
  tests/tui/                      (the FTXUI-coupled helper test)
All cross-dir #include paths rewritten; same-dir includes untouched.

CMake: essim_core is the frontend-agnostic business library — links libzip,
pugixml and bsdl, NO GUI toolkit. Each frontend is a self-contained
src/frontends/<name>/ (own CMakeLists, toolkit, main.cpp) that links
essim_core, selected with -DESSIM_FRONTEND=<name> (default tui; 'none' = core +
tests only, no toolkit fetched). FTXUI moved into the tui frontend. Tests are
split: essim_tests links essim_core (no FTXUI), essim_tui_tests links essim_tui.

Verified: default tui build green (ctest 2/2); ESSIM_FRONTEND=none builds the
core + tests with FTXUI never fetched and no `essim` binary.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-03 19:33:06 +02:00
parent 3010bb25eb
commit 63ca17d048
83 changed files with 282 additions and 228 deletions

116
src/core/domain/signals.cpp Normal file
View File

@@ -0,0 +1,116 @@
#include "signals.hpp"
#include "parts.hpp"
#include <algorithm>
#include <cctype>
#include <vector>
const char *signal_type_name(SignalType t) {
switch (t) {
case SignalType::Power: return "power";
case SignalType::GndShield: return "gnd";
case SignalType::Other: return "other";
}
return "other";
}
SignalType next_signal_type(SignalType t) {
switch (t) {
case SignalType::Power: return SignalType::GndShield;
case SignalType::GndShield: return SignalType::Other;
case SignalType::Other: return SignalType::Power;
}
return SignalType::Other;
}
bool signal_type_from_name(const std::string &s, SignalType &out) {
std::string l = s;
std::transform(l.begin(), l.end(), l.begin(),
[](unsigned char c) { return std::tolower(c); });
if (l == "power" || l == "p") { out = SignalType::Power; return true; }
if (l == "gnd" || l == "g" || l == "shield" || l == "ground")
{ out = SignalType::GndShield; return true; }
if (l == "other" || l == "o" || l == "signal")
{ out = SignalType::Other; return true; }
return false;
}
// Heuristic. Names like GND, GNDA, SHIELD, CHASSIS → GndShield.
// Names containing PWR/POWER/VCC/VDD/VEE/VSS, or matching V±N or +N.NV
// patterns, or starting with VS_/VS3_ → Power. Else Other.
SignalType infer_signal_type(const std::string &name) {
if (name.empty()) return SignalType::Other;
std::string u = name;
std::transform(u.begin(), u.end(), u.begin(),
[](unsigned char c) { return std::toupper(c); });
auto contains = [&](const char *needle) {
return u.find(needle) != std::string::npos;
};
auto starts_with = [&](const char *needle) {
return u.rfind(needle, 0) == 0;
};
if (u == "GND" || u == "GROUND") return SignalType::GndShield;
if (starts_with("GND_")
|| (starts_with("GND") && u.size() >= 4
&& std::isalpha((unsigned char)u[3]))) {
return SignalType::GndShield;
}
if (contains("SHIELD") || contains("CHASSIS") || contains("EARTH"))
return SignalType::GndShield;
if (contains("PWR") || contains("POWER")
|| contains("VCC") || contains("VDD") || contains("VEE") || contains("VSS")
|| starts_with("VS_") || starts_with("VS1_") || starts_with("VS2_")
|| starts_with("VS3_") || starts_with("VS4_")
|| starts_with("VBAT") || starts_with("VBUS")
|| starts_with("+") || starts_with("-")) {
return SignalType::Power;
}
return SignalType::Other;
}
Signal::Signal(std::string name)
: SystemElementContainer<Pin>(name), prnt(nullptr),
type(SignalType::Other) {};
void Signal::add(Pin *pin)
{
string pname = pin->prnt->name + "." + pin->name;
SystemElementContainer<Pin>::add(pname, pin);
}
Signals::Signals(void): SystemElementContainer<Signal>("signals") {}
Signals::Signals(std::vector<Signal *> signals): SystemElementContainer<Signal>("signals", signals) {}
Signals::~Signals() {
for (const auto& [key, value] : content) {
delete value;
}
}
int drop_singleton_signals(Signals *signals) {
if (!signals) return 0;
std::vector<Signal *> doomed;
for (auto &kv : *signals)
if (kv.second->size() == 1) doomed.push_back(kv.second);
for (Signal *s : doomed) {
// Detach the lone pin so it surfaces as `(NC)` in views.
for (auto &pkv : *s) {
pkv.second->connect(nullptr);
pkv.second->nc_origin = NcOrigin::DroppedSingleton;
}
signals->remove(s->name);
delete s;
}
return (int)doomed.size();
}
void Signals::add(Signal *signal)
{
SystemElementContainer<Signal>::add(signal);
signal->prnt = this;
}