seersdk_rs/api/
request.rs

1use crate::{ApiRequest, PointId, TaskId};
2
3pub trait ToRequestBody {
4    /// Convert the request to a JSON string body
5    fn to_request_body(&self) -> Result<String, serde_json::Error>;
6    fn to_api_request(&self) -> ApiRequest;
7}
8
9pub const SELF_POSITION: &str = "SELF_POSITION";
10
11#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)]
12pub struct MoveToPoint {
13    pub id: PointId,
14    pub x: Option<f64>,
15    pub y: Option<f64>,
16
17    pub angle: Option<f64>,
18    pub max_speed: Option<f64>,
19    pub max_wspeed: Option<f64>,
20    pub max_acc: Option<f64>,
21    pub max_wacc: Option<f64>,
22}
23
24impl MoveToPoint {
25    /// Move to the origin (0,0)
26    pub fn zeros() -> Self {
27        Self::default()
28    }
29
30    pub fn with_id<T: Into<String>>(id: T) -> Self {
31        Self {
32            id: id.into(),
33            ..Default::default()
34        }
35    }
36
37    pub fn new(x: f64, y: f64) -> Self {
38        Self {
39            x: Some(x),
40            y: Some(y),
41            ..Default::default()
42        }
43    }
44}
45
46#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq)]
47#[serde(rename_all = "lowercase")]
48pub enum MoveMethod {
49    Forward,
50    Backward,
51}
52
53#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
54pub struct GetNavStatus {
55    /// Whether to return only simple data, true = yes, false = no, default is no
56    pub simple: Option<bool>,
57}
58
59impl GetNavStatus {
60    pub fn new() -> Self {
61        Self { simple: None }
62    }
63
64    pub fn with_simple(mut self, simple: bool) -> Self {
65        self.simple = Some(simple);
66        self
67    }
68}
69
70impl Default for GetNavStatus {
71    fn default() -> Self {
72        Self::new()
73    }
74}
75
76#[derive(
77    Debug, Clone, serde::Serialize, serde::Deserialize, Default, PartialEq,
78)]
79pub struct MoveToTarget {
80    /// Target Station Name
81    /// When the robot executes an operation in place, its fixed value is "SELF_POSITION"
82    #[serde(rename = "id")]
83    pub target: PointId,
84    /// Starting Station Name
85    /// When the starting position is not at a station but the current position of the robot, its
86    /// value is fixed as "SELF_POSITION"
87    #[serde(rename = "source_id")]
88    pub start: PointId,
89
90    /// Unique Task ID
91    /// Can be ommitted when only a single MoveToTarget is used
92    /// But required when used in a list of MoveToTargets
93    pub task_id: Option<TaskId>,
94
95    /// When ommitted, the mode configured on the path will be used
96    pub method: Option<MoveMethod>,
97
98    /// Follow-up or not
99    pub spin: Option<bool>,
100
101    /// Delays the time to end the navigation state, with the unit in ms, defaulting to 0
102    #[serde(default)]
103    pub delay: u64,
104
105    /// Starting in-place rotation direction
106    /// -1: Clockwise rotation
107    /// 0: Rotate towards the nearest direction
108    /// 1: Counterclockwise rotation
109    pub start_rot_dir: Option<i8>,
110
111    /// Rotation direction at the point
112    /// -1: Clockwise rotation
113    /// 0: Rotate towards the nearest direction
114    /// 1: Counterclockwise rotation
115    pub end_rot_dir: Option<i8>,
116
117    /// Position Accuracy (m)
118    pub reach_dist: Option<f64>,
119
120    /// Angle Accuracy (rad)
121    pub reach_angle: Option<f64>,
122
123    pub angle: Option<f64>,
124    pub max_speed: Option<f64>,
125    pub max_wspeed: Option<f64>,
126    pub max_acc: Option<f64>,
127    pub max_wacc: Option<f64>,
128
129    #[serde(flatten)]
130    pub jack_operation: Option<JackOperation>,
131}
132
133/// Macro to implement builder methods for MoveToTarget
134/// 3 argumens: method_name, field_name, field_type
135macro_rules! impl_move_to_target_builder {
136    ( $($method_name:ident : $field_name:ident = $field_type:ty),* $(,)? ) => {
137        $(
138            pub fn $method_name(mut self, value: $field_type) -> Self {
139                self.$field_name = value.into();
140                self
141            }
142        )*
143    };
144}
145
146impl MoveToTarget {
147    pub fn new<T: Into<String>>(target: T) -> Self {
148        Self {
149            target: target.into(),
150            start: SELF_POSITION.to_string(),
151            ..Default::default()
152        }
153    }
154
155    impl_move_to_target_builder! {
156        with_task_id: task_id = TaskId,
157        with_start: start = PointId,
158        with_method: method = MoveMethod,
159        with_operation: jack_operation = JackOperation,
160    }
161}
162
163#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq)]
164#[serde(tag = "operation")]
165pub enum JackOperation {
166    JackLoad,
167    JackUnload,
168    JackHeight { jack_height: f64 },
169    Wait,
170}
171
172#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
173pub struct SetJackHeight {
174    pub height: f64,
175}
176
177impl SetJackHeight {
178    pub fn new(height: f64) -> Self {
179        Self { height }
180    }
181}
182
183#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
184pub struct MoveDesignedPath {
185    #[serde(rename = "move_task_list")]
186    pub path: Vec<MoveToTarget>,
187}
188
189impl MoveDesignedPath {
190    pub fn new(path: impl IntoIterator<Item = MoveToTarget>) -> Self {
191        Self {
192            path: path.into_iter().collect(),
193        }
194    }
195}
196
197#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
198pub struct GetTaskStatus {
199    ///Specify the task_id of the task to be queried in the array.
200    ///If the array is empty, the response will also be empty;
201    ///If this field is omitted, the status of the most recently completed task and the status of all incomplete tasks of the robot will be returned.
202    pub task_ids: Vec<String>,
203}
204
205impl FromIterator<String> for GetTaskStatus {
206    fn from_iter<I: IntoIterator<Item = String>>(iter: I) -> Self {
207        Self {
208            task_ids: iter.into_iter().collect(),
209        }
210    }
211}
212
213#[cfg(test)]
214mod tests {
215    use super::*;
216    #[test]
217    fn test_move_to_point_serialization() {
218        let raw_json = r#"
219            {
220                  "id": "AP1",
221                  "source_id": "LM2",
222                  "task_id": "12344321",
223                  "operation": "JackHeight",
224                  "jack_height": 0.2
225            }
226        "#;
227
228        let m1: MoveToTarget = serde_json::from_str(raw_json).unwrap();
229
230        let m2 = MoveToTarget {
231            target: "AP1".to_string(),
232            start: "LM2".to_string(),
233            task_id: Some("12344321".to_string()),
234            jack_operation: Some(JackOperation::JackHeight {
235                jack_height: 0.2,
236            }),
237            ..Default::default()
238        };
239
240        let serialized = serde_json::to_string_pretty(&m2).unwrap();
241        let m2 = serde_json::from_str::<MoveToTarget>(&serialized).unwrap();
242
243        eprintln!("Serialized: {}", serialized);
244        assert_eq!(m1, m2);
245
246        let raw_json = r#"
247            {
248                  "id": "AP1",
249                  "source_id": "LM2",
250                  "task_id": "12344321",
251                  "operation": "JackLoad"
252            }
253        "#;
254        let m1: MoveToTarget = serde_json::from_str(raw_json).unwrap();
255        let m2 = MoveToTarget {
256            target: "AP1".to_string(),
257            start: "LM2".to_string(),
258            task_id: Some("12344321".to_string()),
259            jack_operation: Some(JackOperation::JackLoad),
260            ..Default::default()
261        };
262        let serialized = serde_json::to_string_pretty(&m2).unwrap();
263        let m2 = serde_json::from_str::<MoveToTarget>(&serialized).unwrap();
264        assert_eq!(m1, m2);
265    }
266}