119 lines
5.2 KiB
C++
119 lines
5.2 KiB
C++
#include "fgc/HelpText.h"
|
|
|
|
#include <algorithm>
|
|
#include <cctype>
|
|
|
|
namespace fgc {
|
|
|
|
namespace {
|
|
|
|
std::string lower(std::string s) {
|
|
std::transform(s.begin(), s.end(), s.begin(),
|
|
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
|
|
return s;
|
|
}
|
|
|
|
// First whitespace-delimited token of a syntax string (the command verb), lowercased.
|
|
std::string verbOf(const std::string& syntax) {
|
|
auto end = syntax.find_first_of(" \t");
|
|
return lower(syntax.substr(0, end == std::string::npos ? syntax.size() : end));
|
|
}
|
|
|
|
} // namespace
|
|
|
|
const std::vector<HelpSection>& helpCatalog() {
|
|
// clang-format off
|
|
static const std::vector<HelpSection> catalog = {
|
|
{"Positioning", "Aim the gimbal. 'goto' is degrees; raw MOVE is encoder counts.", {
|
|
{"goto <yaw_deg> <pitch_deg>",
|
|
"Point the gimbal at an absolute heading/elevation in degrees.", {
|
|
"Converts degrees to encoder counts (operator-calibrated) and sends a",
|
|
"two-axis MOVE so both axes start together. Degrees are soft-clamped to",
|
|
"the configured travel limits.",
|
|
"Example: goto 30 -10 (yaw 30 deg, pitch -10 deg)"}},
|
|
{"set motorctl MOVE <yaw>,<pitch>",
|
|
"Move both axes to absolute encoder counts (no degree conversion).", {
|
|
"Example: set motorctl MOVE 100000,250000",
|
|
"Single axis: set motorctl MOVE Y 100000 / set motorctl MOVE P 250000"}},
|
|
{"set motorctl HOME [Y|P]",
|
|
"Run the endstop-finding home sequence (both axes, or one).", {
|
|
"Example: set motorctl HOME / set motorctl HOME Y"}},
|
|
{"set motorctl STOP <Y|P|ALL>",
|
|
"Stop motion immediately on an axis or both.", {}},
|
|
{"set motorctl SPEED <Y|P> <vel>",
|
|
"Set the max slew speed (counts/s) for an axis.", {}},
|
|
}},
|
|
{"Diagnostics", "Inspect the firmware/driver state for debugging.", {
|
|
{"dump",
|
|
"Request a full firmware state dump and show it here.", {
|
|
"Sends DUMP to the firmware; the captured DUMP BEGIN..END block (build,",
|
|
"uptime, reset cause, and per-axis TMC5160 registers) is logged and, in",
|
|
"the TUI, shown in the Diagnostics help section.",
|
|
"Equivalent offline tool: ./firmware/dump.sh"}},
|
|
{"set motorctl DIAG [Y|P|ALL]",
|
|
"Run the motor self-test (emits DG lines, ends with DG DONE).", {
|
|
"Each axis is swept at several speeds/directions; results stream as DG",
|
|
"lines in the log. Axis must be homed first."}},
|
|
{"set motorctl STATUS",
|
|
"Ask the firmware to emit one telemetry (ST) line now.", {}},
|
|
}},
|
|
{"Capture", "Control image capture and encoding.", {
|
|
{"start", "Begin the capture scan.", {}},
|
|
{"stop", "Halt the capture scan.", {}},
|
|
{"set fps <rate>", "Set capture rate in images/second.", {}},
|
|
{"set camera fps <rate>", "Set the camera sensor frame rate.", {}},
|
|
{"set camera jxlq <dist>", "Set JPEG-XL distance (lower = higher quality).", {}},
|
|
{"set camera jxle <effort>", "Set JPEG-XL encode effort.", {}},
|
|
{"set camera display <0|1>", "Toggle the local display window.", {}},
|
|
}},
|
|
{"Logging", "Control console/log verbosity.", {
|
|
{"debug", "Toggle debug-level logging on/off.", {}},
|
|
{"trace <serial|mqtt|camera|control|all|off> [on|off]",
|
|
"Toggle verbatim wire-trace categories.", {
|
|
"Example: trace serial on / trace off"}},
|
|
}},
|
|
{"Session", "Help and exit.", {
|
|
{"help [topic]",
|
|
"Show this reference; 'help <topic>' expands one section.", {
|
|
"Example: help positioning / help dump"}},
|
|
{"exit", "Shut down and quit.", {}},
|
|
}},
|
|
};
|
|
// clang-format on
|
|
return catalog;
|
|
}
|
|
|
|
std::vector<std::string> renderHelp(const std::string& topic) {
|
|
std::vector<std::string> out;
|
|
const std::string q = lower(topic);
|
|
|
|
if (q.empty()) {
|
|
out.emplace_back("Available commands (type 'help <topic>' for detail, e.g. 'help goto'):");
|
|
for (const auto& sec : helpCatalog()) {
|
|
out.emplace_back("");
|
|
out.emplace_back("== " + sec.title + " == " + sec.blurb);
|
|
for (const auto& e : sec.entries)
|
|
out.emplace_back(" " + e.syntax + " - " + e.summary);
|
|
}
|
|
return out;
|
|
}
|
|
|
|
// Topic mode: match a section by title, or an entry by command verb.
|
|
bool matched = false;
|
|
for (const auto& sec : helpCatalog()) {
|
|
const bool sec_match = lower(sec.title).find(q) != std::string::npos;
|
|
for (const auto& e : sec.entries) {
|
|
if (!sec_match && verbOf(e.syntax) != q) continue;
|
|
matched = true;
|
|
out.emplace_back(e.syntax);
|
|
out.emplace_back(" " + e.summary);
|
|
for (const auto& d : e.detail) out.emplace_back(" " + d);
|
|
out.emplace_back("");
|
|
}
|
|
}
|
|
if (!matched) out.emplace_back("No help topic matching '" + topic + "'. Try 'help'.");
|
|
return out;
|
|
}
|
|
|
|
} // namespace fgc
|