1use serde::Deserialize;
10
11use super::net_handle;
12pub use crate::wire::ArmPosition;
13
14#[derive(Debug, Clone, Copy, PartialEq, Deserialize)]
17#[non_exhaustive]
18pub struct ArmAcceleration {
19 pub acceleration: f64,
21 pub travel_acceleration: f64,
23 pub retract_acceleration: f64,
25}
26
27pub(crate) mod ops {
28 use std::time::Duration;
29
30 use serde_json::json;
31
32 use super::ArmAcceleration;
33 use crate::error::{Error, Result};
34 use crate::wire::{
35 net_command, unit, value_arm_position, value_as, ArmPosition, CommandResponse, Op, Timeout,
36 };
37
38 const ROLE: &str = "arm";
39
40 const MOVE_TIMEOUT_S: f64 = 15.0;
42
43 fn move_budget(timeout_s: f64) -> Timeout {
46 Timeout::After(Duration::from_secs_f64(timeout_s + 30.0))
47 }
48
49 fn quick_budget() -> Timeout {
52 Timeout::After(Duration::from_secs(45))
53 }
54
55 pub(crate) fn position(name: &str) -> Op<ArmPosition> {
56 Op {
57 req: net_command(name, ROLE, "position", json!({}), quick_budget()),
58 parse: value_arm_position,
59 }
60 }
61
62 pub(crate) fn move_to_with_timeout(
63 name: &str,
64 x: f64,
65 y: f64,
66 z: f64,
67 timeout_s: f64,
68 ) -> Op<ArmPosition> {
69 Op {
70 req: net_command(
71 name,
72 ROLE,
73 "move",
74 json!({ "x": x, "y": y, "z": z, "timeout": timeout_s }),
75 move_budget(timeout_s),
76 ),
77 parse: value_arm_position,
78 }
79 }
80
81 pub(crate) fn move_to(name: &str, x: f64, y: f64, z: f64) -> Op<ArmPosition> {
82 move_to_with_timeout(name, x, y, z, MOVE_TIMEOUT_S)
83 }
84
85 pub(crate) fn move_by_with_timeout(
86 name: &str,
87 dx: f64,
88 dy: f64,
89 dz: f64,
90 timeout_s: f64,
91 ) -> Op<ArmPosition> {
92 Op {
93 req: net_command(
94 name,
95 ROLE,
96 "move_by",
97 json!({ "dx": dx, "dy": dy, "dz": dz, "timeout": timeout_s }),
98 move_budget(timeout_s),
99 ),
100 parse: value_arm_position,
101 }
102 }
103
104 pub(crate) fn move_by(name: &str, dx: f64, dy: f64, dz: f64) -> Op<ArmPosition> {
105 move_by_with_timeout(name, dx, dy, dz, MOVE_TIMEOUT_S)
106 }
107
108 pub(crate) fn go_home(name: &str) -> Op<()> {
109 Op {
110 req: net_command(name, ROLE, "go_home", json!({}), quick_budget()),
111 parse: unit,
112 }
113 }
114
115 pub(crate) fn enable_motor(name: &str) -> Op<()> {
116 Op {
117 req: net_command(name, ROLE, "enable_motor", json!({}), quick_budget()),
118 parse: unit,
119 }
120 }
121
122 pub(crate) fn disable_motor(name: &str) -> Op<()> {
123 Op {
124 req: net_command(name, ROLE, "disable_motor", json!({}), quick_budget()),
125 parse: unit,
126 }
127 }
128
129 pub(crate) fn read_and_save_position(name: &str) -> Op<ArmPosition> {
130 Op {
131 req: net_command(
132 name,
133 ROLE,
134 "read_and_save_position",
135 json!({}),
136 quick_budget(),
137 ),
138 parse: value_arm_position,
139 }
140 }
141
142 pub(crate) fn get_acceleration(name: &str) -> Op<ArmAcceleration> {
143 Op {
144 req: net_command(name, ROLE, "get_acceleration", json!({}), quick_budget()),
145 parse: value_as::<ArmAcceleration>,
146 }
147 }
148
149 fn parse_readback(resp: CommandResponse) -> Result<Option<ArmAcceleration>> {
152 match resp.value {
153 None | Some(serde_json::Value::Null) => Ok(None),
154 Some(v) => serde_json::from_value(v)
155 .map(Some)
156 .map_err(|e| Error::Decode(format!("invalid acceleration readback: {e}"))),
157 }
158 }
159
160 pub(crate) fn set_acceleration(
161 name: &str,
162 acceleration: u32,
163 travel_acceleration: u32,
164 retract_acceleration: Option<u32>,
165 ) -> Op<Option<ArmAcceleration>> {
166 let mut params = json!({
167 "acceleration": acceleration,
168 "travel_acceleration": travel_acceleration,
169 });
170 if let Some(r) = retract_acceleration {
171 params["retract_acceleration"] = json!(r);
172 }
173 Op {
174 req: net_command(name, ROLE, "set_acceleration", params, quick_budget()),
175 parse: parse_readback,
176 }
177 }
178
179 pub(crate) fn acceleration_compat(err: Error) -> Error {
182 let err = crate::wire::unknown_action_compat(err, "get_acceleration", ">= 0.60.0");
183 match err {
184 Error::Box {
185 status: 400,
186 ref message,
187 } if message.contains("Missing required value")
188 && message.contains("retract_acceleration") =>
189 {
190 Error::UnsupportedByBox {
191 message: "set_acceleration without a retract value requires box \
192 software >= 0.60.0; pass all three values"
193 .to_string(),
194 }
195 }
196 other => other,
197 }
198 }
199}
200
201net_handle! {
202 sync: Arm,
207 async: AsyncArm,
208 methods: {
209 fn position() -> ArmPosition = ops::position;
211 fn move_to(x: f64, y: f64, z: f64) -> ArmPosition = ops::move_to;
213 fn move_to_with_timeout(x: f64, y: f64, z: f64, timeout_s: f64) -> ArmPosition = ops::move_to_with_timeout;
217 fn move_by(dx: f64, dy: f64, dz: f64) -> ArmPosition = ops::move_by;
219 fn move_by_with_timeout(dx: f64, dy: f64, dz: f64, timeout_s: f64) -> ArmPosition = ops::move_by_with_timeout;
222 fn go_home() -> () = ops::go_home;
224 fn enable_motor() -> () = ops::enable_motor;
226 fn disable_motor() -> () = ops::disable_motor;
228 fn read_and_save_position() -> ArmPosition = ops::read_and_save_position;
230 }
231}
232
233#[cfg(feature = "blocking")]
237impl Arm<'_> {
238 pub fn get_acceleration(&self) -> crate::error::Result<ArmAcceleration> {
244 self.client
245 .run(ops::get_acceleration(&self.name))
246 .map_err(ops::acceleration_compat)
247 }
248
249 pub fn set_acceleration(
255 &self,
256 acceleration: u32,
257 travel_acceleration: u32,
258 retract_acceleration: u32,
259 ) -> crate::error::Result<Option<ArmAcceleration>> {
260 self.client.run(ops::set_acceleration(
261 &self.name,
262 acceleration,
263 travel_acceleration,
264 Some(retract_acceleration),
265 ))
266 }
267
268 pub fn set_acceleration_keep_retract(
273 &self,
274 acceleration: u32,
275 travel_acceleration: u32,
276 ) -> crate::error::Result<Option<ArmAcceleration>> {
277 self.client
278 .run(ops::set_acceleration(
279 &self.name,
280 acceleration,
281 travel_acceleration,
282 None,
283 ))
284 .map_err(ops::acceleration_compat)
285 }
286}
287
288#[cfg(feature = "async")]
289impl AsyncArm<'_> {
290 pub async fn get_acceleration(&self) -> crate::error::Result<ArmAcceleration> {
292 self.client
293 .run(ops::get_acceleration(&self.name))
294 .await
295 .map_err(ops::acceleration_compat)
296 }
297
298 pub async fn set_acceleration(
300 &self,
301 acceleration: u32,
302 travel_acceleration: u32,
303 retract_acceleration: u32,
304 ) -> crate::error::Result<Option<ArmAcceleration>> {
305 self.client
306 .run(ops::set_acceleration(
307 &self.name,
308 acceleration,
309 travel_acceleration,
310 Some(retract_acceleration),
311 ))
312 .await
313 }
314
315 pub async fn set_acceleration_keep_retract(
317 &self,
318 acceleration: u32,
319 travel_acceleration: u32,
320 ) -> crate::error::Result<Option<ArmAcceleration>> {
321 self.client
322 .run(ops::set_acceleration(
323 &self.name,
324 acceleration,
325 travel_acceleration,
326 None,
327 ))
328 .await
329 .map_err(ops::acceleration_compat)
330 }
331}