Skip to main content

can_motor_control/
error.rs

1//! Layered error type for the `can-motor-control` crate.
2
3use std::io;
4
5use motor_codec::{CodecError, MotorTypeId};
6use thiserror::Error;
7
8use crate::bus::RouteKey;
9use crate::transport::TransportError;
10
11/// All errors `can-motor-control` can return.
12///
13/// Wraps the lower-layer error types ([`TransportError`], [`CodecError`])
14/// while adding composition-level variants (config schema, builder validation,
15/// lifecycle).
16#[derive(Debug, Error)]
17#[non_exhaustive]
18pub enum Error {
19    /// A transport operation failed.
20    #[error("transport: {0}")]
21    Transport(#[from] TransportError),
22
23    /// A codec operation failed.
24    #[error("codec: {0}")]
25    Codec(#[from] CodecError),
26
27    /// TOML schema validation failed.
28    #[error("config schema: {0}")]
29    ConfigSchema(String),
30
31    /// Reading the config file failed.
32    #[error("config IO: {0}")]
33    ConfigIo(#[from] io::Error),
34
35    /// Reference to a bus name that wasn't registered.
36    #[error("unknown bus name: {0}")]
37    UnknownBusName(String),
38
39    /// Vendor name not in the codec registry.
40    #[error("unknown vendor: {0}")]
41    UnknownVendor(String),
42
43    /// Two `add_bus` calls used the same name.
44    #[error("duplicate bus name: {0}")]
45    DuplicateBusName(String),
46
47    /// Two `add_*` group calls used the same name.
48    #[error("duplicate group name: {0}")]
49    DuplicateGroupName(String),
50
51    /// The bus's codec does not support a motor type the user tried to add.
52    #[error("bus '{bus_name}' codec '{vendor}' does not support motor type {motor_type:?}")]
53    MotorNotSupportedByCodec {
54        /// Vendor short-name of the bus's codec.
55        vendor: String,
56        /// Motor type the user attempted to add.
57        motor_type: MotorTypeId,
58        /// Bus name the motor was being attached to.
59        bus_name: String,
60    },
61
62    /// Gripper construction tried to attach a number of motors other than 1.
63    #[error("gripper requires exactly one motor, got {got}")]
64    GripperRequiresOneMotor {
65        /// Motor count supplied.
66        got: usize,
67    },
68
69    /// A batch command's slice length didn't match the group's motor count.
70    #[error("command length mismatch: expected {expected}, got {got}")]
71    CommandLengthMismatch {
72        /// `arm.len()`.
73        expected: usize,
74        /// Caller-supplied slice length.
75        got: usize,
76    },
77
78    /// Operation requires `connect()` first.
79    #[error("not connected; call Robot::connect() first")]
80    NotConnected,
81
82    /// Normalized gripper opening was outside the supported range.
83    #[error("gripper opening must be between 0.0 and 1.0, got {got}")]
84    OpeningOutOfRange {
85        /// Caller-supplied opening.
86        got: f64,
87    },
88
89    /// Per-unit opening current was outside the supported range.
90    #[error("gripper opening current must be > 0.0 and <= 1.0, got {got}")]
91    OpeningCurrentOutOfRange {
92        /// Caller-supplied per-unit current.
93        got: f64,
94    },
95
96    /// Normalized opening was requested before session calibration completed.
97    #[error("gripper opening calibration has not completed")]
98    OpeningCalibrationRequired,
99
100    /// Per-session gripper opening calibration failed.
101    #[error("gripper opening calibration failed for '{name}': {reason}")]
102    OpeningCalibrationFailed {
103        /// Gripper group name.
104        name: String,
105        /// Human-readable reason.
106        reason: String,
107    },
108
109    /// Damiao opening-control mode could not be verified before enable.
110    #[error("opening-control mode verification failed for '{name}': {reason}")]
111    OpeningControlModeVerificationFailed { name: String, reason: String },
112
113    /// Builder-style mutation after `connect()`.
114    #[error("topology locked after connect()")]
115    TopologyLocked,
116
117    /// Two motors on one bus share the same recv ID.
118    #[error("CAN ID collision on bus '{bus_name}': recv_id {recv_id:#x} already routed to {existing:?}, attempted {attempted:?}")]
119    CanIdCollision {
120        /// Bus where the collision occurred.
121        bus_name: String,
122        /// The colliding recv_id.
123        recv_id: u32,
124        /// The existing route.
125        existing: RouteKey,
126        /// The route the second motor was trying to claim.
127        attempted: RouteKey,
128    },
129
130    /// The bus mutex was poisoned (a thread panicked while holding it).
131    #[error("bus mutex poisoned")]
132    BusPoisoned,
133
134    /// Invariant violated.
135    #[error("internal error: {0}")]
136    Internal(&'static str),
137}