#pragma once #include 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:,,,,,,, [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' 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; // Whether the underlying link is usable. virtual bool connected() const = 0; }; } // namespace fgc