1#![allow(non_snake_case)]
2
3use bincode::deserialize;
4use std::error::Error;
5use tokio::{net::UdpSocket, time::timeout};
6
7pub mod checksum;
8pub mod constants;
9pub mod control;
10
11#[derive(Debug)]
12pub struct A8Mini {
13 pub command_socket: UdpSocket,
14 pub http_socket: UdpSocket,
15}
16
17impl A8Mini {
18 pub async fn connect() -> Result<Self, Box<dyn Error>> {
19 Ok(Self::connect_to(
20 constants::CAMERA_IP,
21 constants::CAMERA_COMMAND_PORT,
22 constants::CAMERA_HTTP_PORT,
23 "8080",
24 "8088",
25 )
26 .await?)
27 }
28
29 pub async fn connect_to(
30 camera_ip: &str,
31 camera_command_port: &str,
32 camera_http_port: &str,
33 local_command_port: &str,
34 local_http_port: &str,
35 ) -> Result<A8Mini, Box<dyn Error>> {
36 let camera: A8Mini = A8Mini {
37 command_socket: UdpSocket::bind(format!("0.0.0.0:{}", local_command_port)).await?,
38 http_socket: UdpSocket::bind(format!("0.0.0.0:{}", local_http_port)).await?,
39 };
40
41 camera
42 .command_socket
43 .connect(format!("{}:{}", camera_ip, camera_command_port))
44 .await?;
45 camera
46 .http_socket
47 .connect(format!("{}:{}", camera_ip, camera_http_port))
48 .await?;
49 Ok(camera)
50 }
51
52 pub async fn send_command_blind<T: control::Command>(
53 &self,
54 command: T,
55 ) -> Result<(), Box<dyn Error>> {
56 println!(
57 "[COMMAND] Sending command with bytes: {:?}",
58 command.to_bytes()
59 );
60 println!(
61 "[COMMAND] Sending command with DATA_LEN: {:?} | CMD_ID: {:?}",
62 command.to_bytes()[3],
63 command.to_bytes()[7]
64 );
65
66 if self
67 .command_socket
68 .send(command.to_bytes().as_slice())
69 .await?
70 == 0
71 {
72 println!("[COMMAND] No bytes sent.");
73 return Err("No bytes sent.".into());
74 }
75
76 println!("[COMMAND] Command sent successfully.");
77
78 Ok(())
79 }
80
81 pub async fn send_command<T: control::Command>(
82 &self,
83 command: T,
84 ) -> Result<[u8; constants::RECV_BUFF_SIZE], Box<dyn Error>> {
85 self.send_command_blind(command).await?;
86 let mut recv_buffer = [0; constants::RECV_BUFF_SIZE];
87
88 println!("[COMMAND] Waiting for response.");
89
90 let recv_len = timeout(
91 constants::RECV_TIMEOUT,
92 self.command_socket.recv(&mut recv_buffer),
93 )
94 .await??;
95 if recv_len == 0 {
96 println!("[COMMAND] No bytes received.");
97 return Err("No bytes received.".into());
98 }
99
100 println!(
101 "[COMMAND] Response of size {} received successfully: {:?}",
102 recv_len, recv_buffer
103 );
104 Ok(recv_buffer)
105 }
106
107 pub async fn get_attitude_information(
108 &self,
109 ) -> Result<control::A8MiniAtittude, Box<dyn Error>> {
110 let attitude_bytes = self
111 .send_command(control::A8MiniSimpleCommand::AttitudeInformation)
112 .await?;
113 let attitude_info: control::A8MiniAtittude = deserialize(&attitude_bytes)?;
114 Ok(attitude_info)
115 }
116
117 pub async fn send_http_query<T: control::HTTPQuery>(
118 &self,
119 query: T,
120 ) -> Result<control::HTTPResponse, Box<dyn Error>> {
121 let response = reqwest::get(query.to_string()).await?;
122 println!("[HTTP] Waiting for response.");
123
124 let json = response.json::<control::HTTPResponse>().await?;
125 println!("[HTTP] Received response.");
126 Ok(json)
127 }
128
129 pub async fn send_http_image_query<T: control::HTTPQuery>(
130 &self,
131 query: T,
132 ) -> Result<Vec<u8>, Box<dyn Error>> {
133 let response = reqwest::get(query.to_string()).await?;
134 println!("[HTTP] Waiting for response.");
135
136 let image_bytes = response.bytes().await?;
137 println!("[HTTP] Received response.");
138 Ok(image_bytes.to_vec())
139 }
140}
141
142#[cfg(test)]
143mod tests {
144 use tokio::fs::File;
145 use tokio::io::AsyncWriteExt;
146
147 use super::*;
148 use std::thread::sleep;
149 use std::time::Duration;
150
151 #[ignore]
152 #[tokio::test]
153 async fn test_control_lock() -> Result<(), Box<dyn Error>> {
154 let cam: A8Mini = A8Mini::connect().await?;
155
156 cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchAngle(900, 0))
157 .await?;
158 sleep(Duration::from_millis(1000));
159
160 cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchAngle(900, -900))
161 .await?;
162 sleep(Duration::from_millis(1000));
163
164 cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchAngle(900, 250))
165 .await?;
166 sleep(Duration::from_millis(1000));
167
168 cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchAngle(-900, 0))
169 .await?;
170 sleep(Duration::from_millis(2500));
171
172 cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchAngle(-900, -900))
173 .await?;
174 sleep(Duration::from_millis(1000));
175
176 cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchAngle(-900, 250))
177 .await?;
178 sleep(Duration::from_millis(1000));
179
180 cam.send_command_blind(control::A8MiniSimpleCommand::AutoCenter)
181 .await?;
182 Ok(())
183 }
184
185 #[ignore]
186 #[tokio::test]
187 async fn test_take_and_download_photo() -> Result<(), Box<dyn Error>> {
188 let cam: A8Mini = A8Mini::connect().await?;
189
190 cam.send_command(control::A8MiniSimpleCommand::TakePicture)
191 .await?;
192 sleep(Duration::from_millis(500));
193 let num_pictures = cam
194 .send_http_query(control::A8MiniSimpleHTTPQuery::GetMediaCountPhotos)
195 .await?
196 .data
197 .count
198 .unwrap();
199 let picture_bytes = cam
200 .send_http_image_query(control::A8MiniComplexHTTPQuery::GetPhoto(num_pictures as u8))
201 .await?;
202 File::create("tmp.jpeg")
203 .await?
204 .write_all(&picture_bytes)
205 .await?;
206
207 Ok(())
208 }
209
210 #[ignore]
211 #[tokio::test]
212 async fn test_send_simple_commands_blind() -> Result<(), Box<dyn Error>> {
213 let cam: A8Mini = A8Mini::connect().await?;
214
215 cam.send_command_blind(control::A8MiniSimpleCommand::RotateLeft)
216 .await?;
217 sleep(Duration::from_millis(500));
218
219 cam.send_command_blind(control::A8MiniSimpleCommand::RotateRight)
220 .await?;
221 sleep(Duration::from_millis(1000));
222
223 cam.send_command_blind(control::A8MiniSimpleCommand::RotateLeft)
224 .await?;
225 sleep(Duration::from_millis(500));
226
227 cam.send_command_blind(control::A8MiniSimpleCommand::StopRotation)
228 .await?;
229
230 cam.send_command_blind(control::A8MiniSimpleCommand::RotateUp)
231 .await?;
232 sleep(Duration::from_millis(500));
233
234 cam.send_command_blind(control::A8MiniSimpleCommand::RotateDown)
235 .await?;
236 sleep(Duration::from_millis(500));
237
238 cam.send_command_blind(control::A8MiniSimpleCommand::StopRotation)
239 .await?;
240 sleep(Duration::from_millis(1000));
241
242 cam.send_command_blind(control::A8MiniSimpleCommand::AutoCenter)
243 .await?;
244 Ok(())
245 }
246
247 #[ignore]
248 #[tokio::test]
249 async fn test_send_complex_commands_blind() -> Result<(), Box<dyn Error>> {
250 let cam: A8Mini = A8Mini::connect().await?;
251
252 cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchSpeed(50, 50))
253 .await?;
254 sleep(Duration::from_millis(1000));
255
256 cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchSpeed(50, 10))
257 .await?;
258 sleep(Duration::from_millis(1000));
259
260 cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchSpeed(-25, -15))
261 .await?;
262 sleep(Duration::from_millis(6000));
263
264 cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchSpeed(0, 0))
265 .await?;
266 sleep(Duration::from_millis(1000));
267
268 cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchAngle(90, 0))
269 .await?;
270 sleep(Duration::from_millis(1000));
271
272 cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchAngle(90, -90))
273 .await?;
274 sleep(Duration::from_millis(1000));
275
276 cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchAngle(-90, -90))
277 .await?;
278 sleep(Duration::from_millis(1000));
279
280 cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchAngle(-90, 0))
281 .await?;
282 sleep(Duration::from_millis(1000));
283
284 cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchAngle(0, 0))
285 .await?;
286 sleep(Duration::from_millis(1000));
287
288 cam.send_command_blind(control::A8MiniSimpleCommand::AutoCenter)
289 .await?;
290 Ok(())
291 }
292
293 #[ignore]
294 #[tokio::test]
295 async fn test_send_command_with_ack() -> Result<(), Box<dyn Error>> {
296 let cam: A8Mini = A8Mini::connect().await?;
297 cam.get_attitude_information().await?;
298 Ok(())
299 }
300}