phoxal_api/api/v0_1/
drive.rs1#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
2#[serde(rename_all = "snake_case")]
3pub enum StopReason {
4 TargetStale,
5 TargetNotFinite,
6 ActuatorCommandNotFinite,
7 EmergencyStop,
8 Fault,
9}
10
11#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
14#[serde(deny_unknown_fields)]
15pub struct Target {
16 #[serde(deserialize_with = "crate::api::v0_1::drive::deserialize_finite_target_scalar")]
17 pub(crate) linear_x_mps: f32,
18 #[serde(deserialize_with = "crate::api::v0_1::drive::deserialize_finite_target_scalar")]
19 pub(crate) angular_z_radps: f32,
20}
21
22impl Target {
23 pub fn try_new(linear_x_mps: f32, angular_z_radps: f32) -> Result<Self, InvalidTarget> {
24 if !linear_x_mps.is_finite() {
25 return Err(InvalidTarget::LinearXNotFinite);
26 }
27 if !angular_z_radps.is_finite() {
28 return Err(InvalidTarget::AngularZNotFinite);
29 }
30 Ok(Self {
31 linear_x_mps,
32 angular_z_radps,
33 })
34 }
35
36 #[must_use]
37 pub const fn linear_x_mps(&self) -> f32 {
38 self.linear_x_mps
39 }
40
41 #[must_use]
42 pub const fn angular_z_radps(&self) -> f32 {
43 self.angular_z_radps
44 }
45
46 #[must_use]
47 pub const fn stopped() -> Self {
48 Self {
49 linear_x_mps: 0.0,
50 angular_z_radps: 0.0,
51 }
52 }
53}
54
55#[derive(Debug, Clone, Copy, PartialEq, Eq)]
56pub enum InvalidTarget {
57 LinearXNotFinite,
58 AngularZNotFinite,
59}
60
61impl std::fmt::Display for InvalidTarget {
62 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63 let field = match self {
64 Self::LinearXNotFinite => "linear_x_mps",
65 Self::AngularZNotFinite => "angular_z_radps",
66 };
67 write!(formatter, "control target field {field} must be finite")
68 }
69}
70
71impl std::error::Error for InvalidTarget {}
72
73#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
75pub enum State {
76 Active {
77 target: Target,
78 limited_target: Target,
79 },
80 Stopped {
81 target: Target,
82 reason: StopReason,
83 },
84}
85
86pub(crate) fn deserialize_finite_target_scalar<'de, D>(deserializer: D) -> Result<f32, D::Error>
91where
92 D: serde::Deserializer<'de>,
93{
94 let value = <f32 as serde::Deserialize>::deserialize(deserializer)?;
95 value
96 .is_finite()
97 .then_some(value)
98 .ok_or_else(|| serde::de::Error::custom("control target scalar must be finite"))
99}
100
101phoxal_macros::phoxal_api_fragment! {
102 path drive;
103
104 version v0_1;
105
106 command target: Setpoint<Target>;
107 topic state: State<State>;
108}