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:
116
src/core/domain/signals.cpp
Normal file
116
src/core/domain/signals.cpp
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user