Source code for cubemarspycan.errors

"""Exception hierarchy.

Every exception in this module is raised on the **caller's** thread. Nothing in the
receive path ever raises: a driver fault becomes a latched
:class:`~cubemarspycan.state.FaultEvent`, and only ``update()`` turns it into control
flow. That is the fix for the single most safety-relevant defect in TMotorCANControl,
where a fault raised inside the python-can notifier thread is swallowed, never reaches the
control loop, and the motor keeps being commanded while faulted.
"""

from __future__ import annotations

# ruff: noqa: N818
# Several names here deliberately omit the "Error" suffix. They are domain vocabulary and
# read better at the call site -- `except MotorFault:` and `except MalformedFrame:` say
# what happened; `MotorFaultError` does not say more. These names are public API.


[docs] class CubemarsError(Exception): """Base class for every error this library raises."""
# --- spec / configuration ----------------------------------------------------------
[docs] class SpecError(CubemarsError): """The motor specification cannot support what was asked of it."""
[docs] class SpecIncompleteError(SpecError): """A conversion needs a constant this spec does not know. Raised instead of guessing. Guessing is what produced the 8.6x position error and the 78x torque error in the reference library. """
[docs] class CapabilityError(SpecError): """This motor variant does not support the requested operation. For example, permanent-zero (origin mode 1) on a single-encoder model such as the AK40-10. No frame is emitted when this is raised. """
[docs] class UnresolvedFrameError(SpecError): """The value exists on the wire but which side of the gearbox it refers to is unknown."""
# --- protocol ----------------------------------------------------------------------
[docs] class ProtocolError(CubemarsError): """Something on the wire did not match the protocol."""
[docs] class MalformedFrame(ProtocolError): """A frame could not be decoded. The only exception a codec may raise."""
# --- transport ---------------------------------------------------------------------
[docs] class TransportError(CubemarsError): """The CAN link failed."""
[docs] class UnsupportedPlatform(TransportError): """The requested backend does not exist on this platform (e.g. socketcan on macOS)."""
[docs] class SendFailed(TransportError): """A frame could not be put on the bus."""
# --- motor lifecycle ---------------------------------------------------------------
[docs] class MotorError(CubemarsError): """Base for runtime problems with a specific motor."""
[docs] class MotorFault(MotorError): """The driver reported a non-zero fault code. Raised on the control thread by ``update()``, after a safe-stop frame has been sent. """
[docs] class StaleFeedbackError(MotorError): """No fresh feedback within the configured window; the motor may be gone."""
[docs] class NotInControlMode(MotorError): """Commanded a motor that is not inside its ``control()`` block."""
[docs] class ServoModeNotConfirmed(MotorError): """The driver never confirmed it is in servo mode. Carries triage, because the usual cause is not wiring: CubeMarsTool defaults the CAN status-message rate to 0 on some drivers, in which case no 0x29 frames are ever sent and a naive library reports zeros forever. """