fwt_software/include/fgc/IMotorController.h

87 lines
3.7 KiB
C++

#pragma once
#include <string>
#include <vector>
namespace fgc {
// Per-axis lifecycle state, from the firmware ST line's state char (B/R/H/A/E).
enum class AxisState { Boot, Reset, Homing, Ready, Error, Unknown };
// One axis segment of a firmware `ST` status line:
// ST Y:<state>,<xactual>,<xenc>,<drv_hex>,<sg>,<cs>,<pwm>,<flags> [P:...]
// Positions are absolute encoder counts; degrees are derived via Geometry.
struct AxisTelemetry {
AxisState state = AxisState::Unknown;
long xactual = 0; // TMC step counter (commanded position)
long xenc = 0; // encoder position (actual shaft position)
unsigned drv_status = 0; // DRV_STATUS register (decoded from the 8-digit hex)
int sg = 0; // SG_RESULT
int cs = 0; // CS_ACTUAL
int pwm = 0; // PWM_SCALE_SUM
bool standstill = false; // flag 'S' (DRV_STST)
bool stall = false; // flag 's' (StallGuard) - inert in StealthChop, see below
// Flag 'd': the TMC's hardware encoder-deviation latch, set when
// |xactual - xenc| exceeded ENC_DEVIATION. This is the usable stall signal on
// this rig - StallGuard needs SpreadCycle, which production does not run.
// Latched until the axis is homed or reset, so it means "position
// untrustworthy", not "stalling right now".
bool deviation = false;
bool overtemp = false; // flag 'o'/'O'
bool endstop_l = false; // flag 'L'
bool endstop_r = false; // flag 'R'
bool moving() const { return state == AxisState::Homing || !standstill; }
bool ready() const { return state == AxisState::Ready; }
};
// Telemetry snapshot from the motor controller: one segment per axis.
struct MotorTelemetry {
AxisTelemetry yaw;
AxisTelemetry pitch;
bool pitch_present = false; // was a P: segment seen?
};
// Abstraction over the gimbal's motor controller. Implemented by
// SerialMotorController (Boost.Asio serial link) and MockMotorController
// (simulated, for development without hardware).
class IMotorController {
public:
virtual ~IMotorController() = default;
// Begin/*end* background telemetry reading.
virtual void start() = 0;
virtual void stop() = 0;
// Send a command line to the controller (e.g. "HOME", "MOVE Y 20000").
// Implementations append the newline terminator the firmware requires.
virtual void sendCommand(const std::string& cmd) = 0;
// Latest telemetry snapshot (thread-safe in implementations).
virtual MotorTelemetry telemetry() = 0;
// The most recently completed firmware DUMP block (the text between
// "DUMP BEGIN" and "DUMP END", inclusive), or "" if none has been captured.
// Thread-safe in implementations.
virtual std::string lastDump() = 0;
// The most recently completed firmware DIAG block (the "DG ..." lines from
// "DG BEGIN" through "DG DONE"), or "" if none. diagSeq() increments on each
// completed capture so callers can detect a fresh result. Thread-safe.
virtual std::string lastDiag() = 0;
virtual unsigned diagSeq() const = 0;
// NEED lines the firmware has emitted since they were last taken, one per
// axis, in arrival order (hardware-config-plan.md section 6). Taking them
// clears the queue: they are a request to answer once, not a state to poll.
//
// Default: none. Only the real controller sees the wire; the mock has no
// firmware to ask, and a null implementation would have nothing to say.
virtual std::vector<std::string> takeNeedLines() { return {}; }
// Whether the underlying link is usable.
virtual bool connected() const = 0;
};
} // namespace fgc