fwt_software/src/core/HardwarePresence.cpp

172 lines
7.4 KiB
C++

#include "fgc/HardwarePresence.h"
#include <algorithm>
#include <string>
namespace fgc {
namespace {
bool listed(const std::vector<std::string>& v, const std::string& name) {
return std::find(v.begin(), v.end(), name) != v.end();
}
// A sensor slot is declared when it names a model. Empty means the file said
// nothing; "none" means it said "deliberately not fitted" - and the difference
// matters, because only the second one makes a found sensor a stale-file warning.
bool names(const std::string& slot) { return !slot.empty() && slot != "none"; }
bool stated(const std::string& slot) { return !slot.empty(); }
void missing(PresenceReport& r, std::string subsystem, std::string detail) {
r.findings.push_back({PresenceIssue::MissingDeclared, std::move(subsystem), std::move(detail)});
}
void undeclared(PresenceReport& r, std::string subsystem, std::string detail) {
r.findings.push_back({PresenceIssue::UndeclaredFound, std::move(subsystem), std::move(detail)});
}
} // namespace
bool PresenceReport::anyMissing() const {
return std::any_of(findings.begin(), findings.end(),
[](const PresenceFinding& f) { return f.issue == PresenceIssue::MissingDeclared; });
}
bool PresenceReport::anyUndeclared() const {
return std::any_of(findings.begin(), findings.end(),
[](const PresenceFinding& f) { return f.issue == PresenceIssue::UndeclaredFound; });
}
std::vector<std::string> PresenceReport::missing() const {
std::vector<std::string> out;
for (const auto& f : findings)
if (f.issue == PresenceIssue::MissingDeclared) out.push_back(f.subsystem);
return out;
}
PresenceReport reconcilePresence(const HardwareConfig& declared, const FoundHardware& found) {
PresenceReport r;
// Nothing was read, so there is nothing to contradict. Every un-migrated
// tower lands here and must behave exactly as it did before.
if (!declared.declared) return r;
r.reconciled = true;
// ---- axes ----
// An empty axes list states nothing, matching hasAxis()'s "answers true for
// everything". Reconciling it would turn silence into a fault.
if (!declared.axes.empty()) {
if (!found.axes_known) {
missing(r, "motor telemetry",
"no telemetry arrived, so the declared axes (" + declared.axesLabel() +
") could not be confirmed -- check [Serial] device and that the MCU is "
"flashed and enumerating");
} else {
for (const auto& a : declared.axes)
if (!listed(found.axes, a))
missing(r, "axis " + a,
"declared in " + declared.source_path +
" but absent from telemetry -- the driver is not answering, or "
"the firmware was built for fewer axes than this rig has");
for (const auto& a : found.axes)
if (!listed(declared.axes, a))
undeclared(r, "axis " + a,
"present in telemetry but not in [Hardware] axes -- add it, or the "
"declaration is describing a different rig");
}
}
// ---- IMU ----
if (names(declared.imu) && !found.imu)
missing(r, "imu",
"declared as '" + declared.imu +
"' but no IMU came up -- check [IMU] device; declaring it here is what "
"enables it");
if (stated(declared.imu) && !names(declared.imu) && found.imu)
undeclared(r, "imu", "running, but [Hardware] imu says none -- the declaration is stale");
// ---- env sensor ----
if (names(declared.env) && !found.env)
missing(r, "env",
"declared as '" + declared.env +
"' but no ambient sensor came up -- check [Env] i2c_device, and that it is a "
"real I2C adapter rather than SMBus-only");
if (stated(declared.env) && !names(declared.env) && found.env)
undeclared(r, "env", "running, but [Hardware] env says none -- the declaration is stale");
// ---- cameras ----
if (!declared.cameras.empty()) {
for (const auto& c : declared.cameras)
if (!listed(found.cameras, c))
missing(r, "camera " + c,
"declared in " + declared.source_path +
" but not found -- check the [Camera." + c +
"] block is enabled and the device is on the bus");
for (const auto& c : found.cameras)
if (!listed(declared.cameras, c))
undeclared(r, "camera " + c,
"active but not in [Hardware] cameras -- add it, or the declaration is "
"describing a different rig");
// Configured and declared is not the same as present on the bus. The
// source counts what it opened without saying which, so the shortfall is
// reported as a number and `test hardware camera` names the device.
const int want = static_cast<int>(found.cameras.size());
if (found.cameras_opened >= 0 && found.cameras_opened < want)
missing(r, "cameras",
std::to_string(want) + " configured, only " +
std::to_string(found.cameras_opened) +
" opened -- one is not on the bus; 'test hardware camera' names which");
}
return r;
}
PresenceVerdict presenceVerdict(const PresenceReport& report, bool strict, bool degraded) {
PresenceVerdict v;
if (report.findings.empty()) return v; // Proceed, nothing to say
if (!report.anyMissing()) {
// Only stale-file warnings. Never fatal: the rig has MORE than was
// declared, which is a documentation fault, not a broken tower.
v.action = PresenceAction::WarnOnly;
v.reason = "hardware.ini is out of date with this rig";
return v;
}
std::string gone;
for (const auto& m : report.missing()) gone += (gone.empty() ? "" : ", ") + m;
if (!strict) {
v.action = PresenceAction::WarnOnly;
v.reason = "declared hardware is missing (" + gone +
"); continuing because [Features] strict_presence is off -- set it once this "
"tower's hardware.ini is known good";
return v;
}
if (degraded) {
// Loudly, per the plan: this is field triage, and it must not become the
// way a tower is normally run.
v.action = PresenceAction::WarnOnly;
v.reason = "RUNNING DEGRADED BY REQUEST: declared hardware is missing (" + gone +
") and --degraded overrode the refusal -- this tower is not doing the job it "
"is declared to do";
return v;
}
v.action = PresenceAction::Refuse;
v.reason = "declared hardware is missing: " + gone +
" -- fix the rig, correct " +
"hardware.ini, or pass --degraded to start anyway";
return v;
}
std::vector<std::string> formatPresence(const PresenceReport& report) {
std::vector<std::string> out;
for (const auto& f : report.findings)
if (f.issue == PresenceIssue::MissingDeclared) out.push_back(f.subsystem + ": " + f.detail);
for (const auto& f : report.findings)
if (f.issue == PresenceIssue::UndeclaredFound) out.push_back(f.subsystem + ": " + f.detail);
return out;
}
} // namespace fgc