1use crate::{checksum, constants};
2use serde::{Deserialize, Serialize};
3
4
5pub trait Command {
7 fn to_bytes(&self) -> Vec<u8>;
8}
9
10pub trait HTTPQuery {
12 fn to_string(&self) -> String;
13}
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub enum A8MiniSimpleCommand {
18 AutoCenter = 0, RotateUp = 1, RotateDown = 2, RotateRight = 3, RotateLeft = 4, StopRotation = 5, ZoomIn = 6, ZoomOut = 7, ZoomMax = 8,
27 MaxZoomInformation = 9,
28 FocusIn = 10,
29 FocusOut = 11,
30 TakePicture = 12, RecordVideo = 13, Rotate100100 = 14,
33 CameraInformation = 15,
34 AutoFocus = 16, HardwareIDInformation = 17,
36 FirmwareVersionInformation = 18,
37 SetLockMode = 19,
38 SetFollowMode = 20,
39 SetFPVMode = 21,
40 AttitudeInformation = 22,
41 SetVideoOutputHDMI = 23,
42 SetVideoOutputCVBS = 24,
43 SetVideoOutputOff = 25,
44 LaserRangefinderInformation = 26,
45 RebootCamera = 27,
46 RebootGimbal = 28,
47}
48
49impl Command for A8MiniSimpleCommand {
50 fn to_bytes(&self) -> Vec<u8> {
51 constants::HARDCODED_COMMANDS[*self as usize].to_vec()
52 }
53}
54
55#[derive(Debug, Clone, Copy, PartialEq, Eq)]
57pub enum A8MiniComplexCommand {
58 SetYawPitchSpeed(i8, i8),
59 SetYawPitchAngle(i16, i16),
60}
61
62impl Command for A8MiniComplexCommand {
63 fn to_bytes(&self) -> Vec<u8> {
64 match *self {
65 A8MiniComplexCommand::SetYawPitchSpeed(v_yaw, v_pitch) => {
66 let mut byte_arr: Vec<u8> = vec![0x55, 0x66, 0x01, 0x02, 0x00, 0x00, 0x00, 0x07];
67
68 byte_arr.push(v_yaw.clamp(-100, 100) as u8);
69 byte_arr.push(v_pitch.clamp(-100, 100) as u8);
70
71 byte_arr.extend_from_slice(&checksum::crc16_calc(&byte_arr, 0));
72
73 byte_arr
74 }
75 A8MiniComplexCommand::SetYawPitchAngle(theta_yaw, theta_pitch) => {
76 let mut byte_arr: Vec<u8> = vec![0x55, 0x66, 0x01, 0x04, 0x00, 0x00, 0x00, 0x0e];
77
78 byte_arr.extend_from_slice(&theta_yaw.clamp(-1350, 1350).to_be_bytes());
79 byte_arr.extend_from_slice(&theta_pitch.clamp(-900, 250).to_be_bytes());
80
81 byte_arr.extend_from_slice(&checksum::crc16_calc(&byte_arr, 0));
82
83 byte_arr
84 }
85 }
86 }
87}
88
89#[derive(Debug, Clone, Copy, PartialEq, Eq)]
91pub enum A8MiniSimpleHTTPQuery {
92 GetDirectoriesPhotos,
93 GetDirectoriesVideos,
94 GetMediaCountPhotos,
95 GetMediaCountVideos,
96}
97
98impl HTTPQuery for A8MiniSimpleHTTPQuery {
99 fn to_string(&self) -> String {
100 match *self {
101 A8MiniSimpleHTTPQuery::GetDirectoriesPhotos => "http://192.168.144.25:82/cgi-bin/media.cgi/api/v1/getdirectories?media_type=0".to_string(),
102 A8MiniSimpleHTTPQuery::GetDirectoriesVideos => "http://192.168.144.25:82/cgi-bin/media.cgi/api/v1/getdirectories?media_type=1".to_string(),
103 A8MiniSimpleHTTPQuery::GetMediaCountPhotos => "http://192.168.144.25:82/cgi-bin/media.cgi/api/v1/getmediacount?media_type=0&path=101SIYI_IMG".to_string(),
104 A8MiniSimpleHTTPQuery::GetMediaCountVideos => "http://192.168.144.25:82/cgi-bin/media.cgi/api/v1/getmediacount?media_type=1&path=100SIYI_VID".to_string(),
105 }
106 }
107}
108
109#[derive(Debug, Clone, Copy, PartialEq, Eq)]
111pub enum A8MiniComplexHTTPQuery {
112 GetPhoto(u8),
113 GetVideo(u8),
114}
115
116impl HTTPQuery for A8MiniComplexHTTPQuery {
117 fn to_string(&self) -> String {
118 match *self {
119 A8MiniComplexHTTPQuery::GetPhoto(photo_ind) => format!(
120 "http://192.168.144.25:82/photo/101SIYI_IMG/IMG_{:0>4}.jpg",
121 photo_ind
122 ),
123 A8MiniComplexHTTPQuery::GetVideo(video_ind) => format!(
124 "http://192.168.144.25:82/video/100SIYI_VID/REC_{:0>4}.mp4",
125 video_ind
126 ),
127 }
128 }
129}
130
131#[derive(Debug, Serialize, Deserialize)]
133pub struct HTTPResponse {
134 pub code: i32,
135 pub data: HTTPResponseData,
136 pub success: bool,
137 pub message: String,
138}
139
140#[derive(Debug, Serialize, Deserialize)]
142pub struct HTTPResponseData {
143 pub media_type: i32,
144 #[serde(skip_serializing_if = "Option::is_none")]
145 pub directories: Option<String>,
146 #[serde(skip_serializing_if = "Option::is_none")]
147 pub path: Option<String>,
148 #[serde(skip_serializing_if = "Option::is_none")]
149 pub start: Option<i32>,
150 #[serde(skip_serializing_if = "Option::is_none")]
151 pub count: Option<i32>,
152 #[serde(skip_serializing_if = "Option::is_none")]
153 pub list: Option<String>,
154}
155
156#[derive(Debug, PartialEq, Eq, Deserialize)]
158pub struct A8MiniAtittude {
159 pub theta_yaw: i16,
160 pub theta_pitch: i16,
161 pub theta_roll: i16,
162 pub v_yaw: i16,
163 pub v_pitch: i16,
164 pub v_roll: i16,
165}
166
167#[cfg(test)]
168mod tests {
169 use super::*;
170
171 #[test]
172 fn test_complex_command_creation_angle() {
173 let computed_command = A8MiniComplexCommand::SetYawPitchAngle(130, -20).to_bytes();
174 let expected_command: [u8; 14] = [
175 0x55, 0x66, 0x01, 0x04, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x82, 0xff, 0xec, 0x8f, 0xad,
176 ];
177 assert_eq!(computed_command, expected_command);
178 }
179
180 #[test]
181 fn test_complex_command_creation_speed() {
182 let computed_command = A8MiniComplexCommand::SetYawPitchSpeed(104, -20).to_bytes();
183 let expected_command: [u8; 12] = [
184 0x55, 0x66, 0x01, 0x02, 0x00, 0x00, 0x00, 0x07, 0x64, 0xec, 0xbd, 0xdf,
185 ];
186 assert_eq!(computed_command, expected_command);
187 }
188
189 #[test]
190 fn test_byte_deserialization() {
191 let attitude_bytes: &[u8] = &[
192 0x28, 0x00, 0x32, 0x00, 0x3c, 0x00, 0x04, 0x00, 0x05, 0x00, 0x06, 0x00,
193 ];
194
195 let computed_attitude_info: A8MiniAtittude = bincode::deserialize(attitude_bytes).unwrap();
197
198 let expected_attitude_info = A8MiniAtittude {
199 theta_yaw: 40,
200 theta_pitch: 50,
201 theta_roll: 60,
202 v_yaw: 4,
203 v_pitch: 5,
204 v_roll: 6,
205 };
206
207 assert_eq!(computed_attitude_info, expected_attitude_info);
208 }
209}