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