#include "fgc/DiagParser.h" #include #include namespace fgc { namespace { long toLong(const std::string& s, long fallback = 0) { if (s.empty()) return fallback; try { bool hex = s.size() > 1 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X'); return std::stol(s, nullptr, hex ? 16 : 10); } catch (const std::exception&) { return fallback; } } } // namespace bool DiagResult::allPass() const { bool any = false; for (const auto& ax : axes) { if (!ax.has_result) continue; any = true; if (!ax.pass) return false; } return any; } DiagResult parseDiag(const std::string& block) { DiagResult d; std::istringstream iss(block); std::string line; DiagAxis* cur = nullptr; auto axisFor = [&d](char a) -> DiagAxis& { for (auto& ax : d.axes) if (ax.axis == a) return ax; d.axes.push_back(DiagAxis{}); d.axes.back().axis = a; return d.axes.back(); }; while (std::getline(iss, line)) { if (!line.empty() && line.back() == '\r') line.pop_back(); // Only "DG ..." lines are part of the diag (ST/other lines interleave). auto pos = line.find("DG "); if (pos == std::string::npos) continue; std::istringstream ls(line.substr(pos)); std::vector tok; std::string t; while (ls >> t) tok.push_back(t); if (tok.size() < 2) continue; // "DG" + something d.valid = true; if (tok[1] == "DONE") { d.done = true; continue; } if (tok[1] == "BEGIN" && tok.size() >= 3) { cur = &axisFor(tok[2][0]); continue; } // Otherwise tok[1] is the axis letter. char axis = tok[1][0]; if (axis != 'Y' && axis != 'P') continue; DiagAxis& ax = axisFor(axis); cur = &ax; if (tok.size() >= 4 && tok[2] == "RESULT") { ax.has_result = true; ax.pass = (tok[3] == "PASS"); continue; } // Test line: "DG S KEY val KEY val ... " if (tok.size() < 4 || tok[2].empty() || (tok[2][0] != 'S' && tok[2][0] != 's')) continue; DiagTest test; test.speed = static_cast(toLong(tok[2].substr(1))); test.fwd = (tok[3] != "REV"); test.pass = (tok.back() == "PASS"); // Remaining KEY value pairs (between dir and the trailing PASS/FAIL). std::map kv; for (size_t i = 4; i + 1 < tok.size(); i += 2) kv[tok[i]] = tok[i + 1]; auto g = [&](const char* k) { auto it = kv.find(k); return it == kv.end() ? std::string() : it->second; }; test.err_peak = toLong(g("ERR_PEAK"), 0); test.err_rms = toLong(g("ERR_RMS"), 0); test.err_still = toLong(g("ERR_STILL"), 0); test.cs_min = static_cast(toLong(g("CS_MIN"))); test.cs_max = static_cast(toLong(g("CS_MAX"))); test.sg_min = static_cast(toLong(g("SG_MIN"))); test.pwm_avg = static_cast(toLong(g("PWM_AVG"))); test.flags = static_cast(toLong(g("FLAGS"))); ax.tests.push_back(test); } return d; } std::vector formatDiag(const DiagResult& d) { std::vector out; if (!d.valid) { out.emplace_back("(no diagnostic data)"); return out; } for (const auto& ax : d.axes) { std::ostringstream hdr; hdr << "[" << ax.axis << "] " << ax.tests.size() << " tests RESULT=" << (ax.has_result ? (ax.pass ? "PASS" : "FAIL") : "?"); out.push_back(hdr.str()); for (const auto& t : ax.tests) { std::ostringstream os; os << " S" << t.speed << " " << (t.fwd ? "FWD" : "REV") << " err_peak=" << t.err_peak << " err_rms=" << t.err_rms << " err_still=" << t.err_still << " cs=" << t.cs_min << ".." << t.cs_max << " sg_min=" << t.sg_min << " pwm=" << t.pwm_avg << " flags=0x" << std::hex << t.flags << std::dec << " " << (t.pass ? "PASS" : "FAIL"); out.push_back(os.str()); } } out.push_back(std::string("overall: ") + (d.allPass() ? "PASS" : "FAIL") + (d.done ? "" : " (incomplete - no DG DONE)")); return out; } } // namespace fgc