#pragma once #include #include namespace fgc { // Structured result of a hardware self-test run (the `test` command). Pure data // + I/O-free format/parse/compare, mirroring DiagParser so it unit-tests in // fgc_core. formatTestReport() emits a plain-text report that is also stable // enough for parseTestReport() to read back for baseline/previous comparison. struct TestMetric { std::string name; // e.g. "yaw_lim_neg_spread" double value = 0.0; std::string unit; // e.g. "counts", "ms", "deg/min" ("" = none) bool lower_is_better = true; bool has_threshold = false; double threshold = 0.0; bool pass = true; // verdict for this metric (abs + optional drift) // Filled by applyComparison() against the baseline / previous report. bool has_baseline = false; double baseline = 0.0; bool has_prev = false; double prev = 0.0; double drift_pct = 0.0; // vs baseline (or prev if no baseline) bool drift_flag = false; // drift exceeded the profile's warn threshold }; struct TestSection { std::string id; // "homing", "encoder", "imu_health", ... std::string title; // human label bool ran = false; bool pass = true; double duration_ms = 0.0; std::vector metrics; std::vector notes; // free-form lines (e.g. "LP unavailable; ST-sampled") }; struct TestReport { long long ts_ms = 0; std::string profile; std::string host; std::string fw; bool all_pass = true; std::vector sections; // Find a metric by section id + metric name (used by applyComparison/tests). const TestMetric* find(const std::string& section_id, const std::string& metric) const; }; // Plain-text, human-readable AND round-trippable report. std::string formatTestReport(const TestReport& r); // Re-read a report previously produced by formatTestReport(). Only the fields // needed for comparison are recovered (section id, metric name + value); // derived columns (base/prev/drift) are recomputed by applyComparison(). TestReport parseTestReport(const std::string& text); // Fill baseline/prev/drift on `cur` from prior reports (either may be null). // `drift_warn_pct` flags a metric when |drift| exceeds it; if `drift_gates_pass` // a flagged metric also fails (and its section + the report roll up to FAIL). void applyComparison(TestReport& cur, const TestReport* baseline, const TestReport* prev, double drift_warn_pct, bool drift_gates_pass); } // namespace fgc