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 Ok(json)
126 }
127
128 pub async fn send_http_image_query<T: control::HTTPQuery>(
129 &self,
130 query: T,
131 ) -> Result<Vec<u8>, Box<dyn Error>> {
132 let response = reqwest::get(query.to_string()).await?;
133 println!("[HTTP] Waiting for response.");
134
135 let image_bytes = response.bytes().await?;
136 Ok(image_bytes.to_vec())
137 }
138}
139
140#[cfg(test)]
141mod tests {
142 use tokio::fs::File;
143 use tokio::io::AsyncWriteExt;
144
145 use super::*;
146 use std::thread::sleep;
147 use std::time::Duration;
148
149 #[tokio::test]
150 async fn test_control_lock() -> Result<(), Box<dyn Error>> {
151 let cam: A8Mini = A8Mini::connect().await?;
152
153 cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchAngle(900, 0))
154 .await?;
155 sleep(Duration::from_millis(1000));
156
157 cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchAngle(900, -900))
158 .await?;
159 sleep(Duration::from_millis(1000));
160
161 cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchAngle(900, 250))
162 .await?;
163 sleep(Duration::from_millis(1000));
164
165 cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchAngle(-900, 0))
166 .await?;
167 sleep(Duration::from_millis(2500));
168
169 cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchAngle(-900, -900))
170 .await?;
171 sleep(Duration::from_millis(1000));
172
173 cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchAngle(-900, 250))
174 .await?;
175 sleep(Duration::from_millis(1000));
176
177 cam.send_command_blind(control::A8MiniSimpleCommand::AutoCenter)
178 .await?;
179 Ok(())
180 }
181
182 #[tokio::test]
183 async fn test_take_and_download_photo() -> Result<(), Box<dyn Error>> {
184 let cam: A8Mini = A8Mini::connect().await?;
185
186 cam.send_command_blind(control::A8MiniSimpleCommand::TakePicture)
187 .await?;
188 sleep(Duration::from_millis(500));
189 let num_pictures = cam
190 .send_http_query(control::A8MiniSimpleHTTPQuery::GetMediaCountPhotos)
191 .await?
192 .data
193 .count
194 .unwrap();
195 dbg!(num_pictures);
196 let picture_bytes = cam
197 .send_http_image_query(control::A8MiniComplexHTTPQuery::GetPhoto(num_pictures as u8))
198 .await?;
199 File::create("tmp.jpeg")
200 .await?
201 .write_all(&picture_bytes)
202 .await?;
203
204 Ok(())
205 }
206
207 #[tokio::test]
208 async fn test_send_simple_commands_blind() -> Result<(), Box<dyn Error>> {
209 let cam: A8Mini = A8Mini::connect().await?;
210
211 cam.send_command_blind(control::A8MiniSimpleCommand::RotateLeft)
212 .await?;
213 sleep(Duration::from_millis(500));
214
215 cam.send_command_blind(control::A8MiniSimpleCommand::RotateRight)
216 .await?;
217 sleep(Duration::from_millis(1000));
218
219 cam.send_command_blind(control::A8MiniSimpleCommand::RotateLeft)
220 .await?;
221 sleep(Duration::from_millis(500));
222
223 cam.send_command_blind(control::A8MiniSimpleCommand::StopRotation)
224 .await?;
225
226 cam.send_command_blind(control::A8MiniSimpleCommand::RotateUp)
227 .await?;
228 sleep(Duration::from_millis(500));
229
230 cam.send_command_blind(control::A8MiniSimpleCommand::RotateDown)
231 .await?;
232 sleep(Duration::from_millis(500));
233
234 cam.send_command_blind(control::A8MiniSimpleCommand::StopRotation)
235 .await?;
236 sleep(Duration::from_millis(1000));
237
238 cam.send_command_blind(control::A8MiniSimpleCommand::AutoCenter)
239 .await?;
240 Ok(())
241 }
242
243 #[tokio::test]
244 async fn test_send_complex_commands_blind() -> Result<(), Box<dyn Error>> {
245 let cam: A8Mini = A8Mini::connect().await?;
246
247 cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchSpeed(50, 50))
248 .await?;
249 sleep(Duration::from_millis(1000));
250
251 cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchSpeed(50, 10))
252 .await?;
253 sleep(Duration::from_millis(1000));
254
255 cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchSpeed(-25, -15))
256 .await?;
257 sleep(Duration::from_millis(6000));
258
259 cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchSpeed(0, 0))
260 .await?;
261 sleep(Duration::from_millis(1000));
262
263 cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchAngle(90, 0))
264 .await?;
265 sleep(Duration::from_millis(1000));
266
267 cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchAngle(90, -90))
268 .await?;
269 sleep(Duration::from_millis(1000));
270
271 cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchAngle(-90, -90))
272 .await?;
273 sleep(Duration::from_millis(1000));
274
275 cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchAngle(-90, 0))
276 .await?;
277 sleep(Duration::from_millis(1000));
278
279 cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchAngle(0, 0))
280 .await?;
281 sleep(Duration::from_millis(1000));
282
283 cam.send_command_blind(control::A8MiniSimpleCommand::AutoCenter)
284 .await?;
285 Ok(())
286 }
287
288 #[tokio::test]
289 async fn test_send_command_with_ack() -> Result<(), Box<dyn Error>> {
290 let cam: A8Mini = A8Mini::connect().await?;
291 cam.get_attitude_information().await?;
292 Ok(())
293 }
294}