fwt_software/include/fgc/RemoteCommands.h

72 lines
2.8 KiB
C++

#pragma once
// Running a console command that arrived over MQTT, and building its answer.
//
// The command goes through exactly the function a typed one does - the caller
// passes it in - on the control thread, so its lock-outs, refusals and output
// are the console's. What this adds is what a remote caller needs and a person
// at the console does not:
// - the answer: every line the command logged while it ran, and the most
// severe level among them, sent back on CommandReply;
// - a retained message is never run - it would re-run on every reconnect;
// - a repeated id (QoS 1 may deliver twice) is answered again, not re-run;
// - an audit line in the log for every remote command, before it runs;
// - for a command whose answer comes back later from the firmware (`gimbal
// dump`), the reply waits for that answer - see Await.
#include "fgc/IControlChannel.h"
#include <deque>
#include <functional>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
namespace fgc {
// What a command's answer still waits for once the command has returned: the
// firmware answers `gimbal dump` on the serial thread, a moment later. While
// waiting, lines from any thread that contain one of `collect` join the reply,
// and the first that contains one of `ends` completes it.
struct Await {
std::vector<std::string> collect;
std::vector<std::string> ends;
long long timeout_ms = 5000;
};
class RemoteCommandRunner {
public:
using Handler = std::function<void(const std::string& line)>;
// Run `rc` through `handle` (the console's command handler). Returns the
// reply to publish - or nothing yet, when `await` is given and the command
// did not warn: then the reply comes from poll(). Must be called on the
// thread that runs the commands: of what is logged while `handle` runs, only
// the calling thread's lines count as this command's output.
std::optional<CommandReply> run(const RemoteCommand& rc, const Handler& handle,
const Await* await = nullptr, long long now_ms = 0);
// A reply is still waiting for its answer; run nothing else until poll()
// has returned it, so replies keep the order of their commands.
bool waiting() const { return pending_ != nullptr; }
// The waiting reply, once its answer has arrived or the wait timed out.
std::optional<CommandReply> poll(long long now_ms);
// How many ids are remembered for de-duplication.
static constexpr size_t kRememberIds = 64;
struct Capture; // the tap's buffer; defined in RemoteCommands.cpp
private:
CommandReply finish(const RemoteCommand& rc, Capture& cap);
struct Pending;
std::shared_ptr<Pending> pending_;
std::deque<std::pair<std::string, CommandReply>> recent_; // id -> reply, newest last
};
} // namespace fgc