dji_log_parser/frame/
mod.rs1use serde::Serialize;
2#[cfg(target_arch = "wasm32")]
3use tsify_next::Tsify;
4
5use crate::layout::details::Details;
6use crate::record::osd::{AppCommand, GroundOrSky};
7use crate::record::smart_battery_group::SmartBatteryGroup;
8use crate::record::Record;
9use crate::utils::append_message;
10
11mod app;
12mod battery;
13mod camera;
14mod custom;
15mod details;
16mod gimbal;
17mod home;
18mod osd;
19mod rc;
20mod recover;
21
22pub use app::FrameApp;
23pub use battery::FrameBattery;
24pub use camera::FrameCamera;
25pub use custom::FrameCustom;
26pub use details::FrameDetails;
27pub use gimbal::FrameGimbal;
28pub use home::FrameHome;
29pub use osd::FrameOSD;
30pub use rc::FrameRC;
31pub use recover::FrameRecover;
32
33#[derive(Serialize, Debug, Default, Clone)]
40#[serde(rename_all = "camelCase")]
41#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
42pub struct Frame {
43 pub custom: FrameCustom,
44 pub osd: FrameOSD,
45 pub gimbal: FrameGimbal,
46 pub camera: FrameCamera,
47 pub rc: FrameRC,
48 pub battery: FrameBattery,
49 pub home: FrameHome,
50 pub recover: FrameRecover,
51 pub app: FrameApp,
52}
53
54impl Frame {
55 fn reset(&mut self) {
61 self.camera.is_photo = bool::default();
62 self.app.tip = String::default();
63 self.app.warn = String::default();
64
65 if self.battery.is_cell_voltage_estimated {
66 self.battery.cell_voltages.fill(0.0);
67 }
68 }
69
70 fn finalize(&mut self) {
77 if self.osd.height_max < self.osd.height {
78 self.osd.height_max = self.osd.height;
79 }
80 if self.osd.x_speed_max < self.osd.x_speed {
81 self.osd.x_speed_max = self.osd.x_speed;
82 }
83 if self.osd.y_speed_max < self.osd.y_speed {
84 self.osd.y_speed_max = self.osd.y_speed;
85 }
86 if self.osd.z_speed_max < self.osd.z_speed {
87 self.osd.z_speed_max = self.osd.z_speed;
88 }
89
90 if let Some(first_cell) = self.battery.cell_voltages.first() {
91 if *first_cell == 0.0 && self.battery.voltage > 0.0 {
92 self.battery.is_cell_voltage_estimated = true;
93 self.battery
94 .cell_voltages
95 .fill(self.battery.voltage / self.battery.cell_num as f32)
96 }
97 }
98
99 if self.battery.temperature > self.battery.max_temperature {
100 self.battery.max_temperature = self.battery.temperature
101 }
102
103 if self.battery.temperature < self.battery.min_temperature
104 || self.battery.min_temperature == f32::default()
105 {
106 self.battery.min_temperature = self.battery.temperature
107 }
108
109 let max_voltage = self
110 .battery
111 .cell_voltages
112 .iter()
113 .copied()
114 .max_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
115 .unwrap_or(0.0);
116
117 let min_voltage = self
118 .battery
119 .cell_voltages
120 .iter()
121 .copied()
122 .min_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
123 .unwrap_or(0.0);
124
125 self.battery.cell_voltage_deviation =
126 ((max_voltage - min_voltage) * 1000.0).round() / 1000.0;
127
128 if self.battery.cell_voltage_deviation > self.battery.max_cell_voltage_deviation {
129 self.battery.max_cell_voltage_deviation = self.battery.cell_voltage_deviation;
130 }
131 }
132}
133
134pub fn records_to_frames(records: Vec<Record>, details: Details) -> Vec<Frame> {
150 let mut frames = Vec::new();
151 let mut frame = Frame {
152 battery: FrameBattery {
153 cell_num: details.product_type.battery_cell_num(),
154 cell_voltages: vec![0.0; details.product_type.battery_cell_num() as usize],
155 is_cell_voltage_estimated: true,
156 ..FrameBattery::default()
157 },
158 ..Frame::default()
159 };
160
161 let mut frame_index = 0;
162
163 for record in records {
164 match record {
165 Record::OSD(osd) => {
166 if frame_index > 0 {
167 frame.finalize();
168 frames.push(frame.clone());
169 frame.reset();
170 }
171
172 frame.osd.fly_time = osd.fly_time;
174 frame.osd.latitude = osd.latitude;
175 frame.osd.longitude = osd.longitude;
176 frame.osd.altitude = osd.altitude + frame.home.altitude;
178 frame.osd.height = osd.altitude;
179 frame.osd.vps_height = osd.s_wave_height;
180
181 frame.osd.x_speed = osd.speed_x;
182 frame.osd.y_speed = osd.speed_y;
183 frame.osd.z_speed = osd.speed_z;
184 frame.osd.pitch = osd.pitch;
185 frame.osd.yaw = osd.yaw;
186 frame.osd.roll = osd.roll;
187
188 if frame.osd.flyc_state != Some(osd.flight_mode) {
189 frame.app.tip = append_message(
190 frame.app.tip,
191 format!("Flight mode changed to {:?}.", osd.flight_mode),
192 );
193 }
194 frame.osd.flyc_state = Some(osd.flight_mode);
195 if let AppCommand::Unknown(0) = osd.app_command {
196 frame.osd.flyc_command = None;
197 } else {
198 frame.osd.flyc_command = Some(osd.app_command);
199 }
200 frame.osd.flight_action = Some(osd.flight_action);
201 frame.osd.gps_num = osd.gps_num;
202 frame.osd.gps_level = osd.gps_level;
203 frame.osd.is_gpd_used = osd.is_gps_valid;
204 frame.osd.non_gps_cause = Some(osd.non_gps_cause);
205 frame.osd.drone_type = Some(osd.drone_type);
206 frame.osd.is_swave_work = osd.is_swave_work;
207 frame.osd.wave_error = osd.wave_error;
208 frame.osd.go_home_status = Some(osd.go_home_status);
209 frame.osd.battery_type = Some(osd.battery_type);
210 frame.osd.is_on_ground = osd.ground_or_sky == GroundOrSky::Ground;
211 frame.osd.is_motor_on = osd.is_motor_up;
212 frame.osd.is_motor_blocked = osd.is_motor_blocked;
213 frame.osd.motor_start_failed_cause = Some(osd.motor_start_failed_cause);
214 frame.osd.is_imu_preheated = osd.is_imu_preheated;
215 frame.osd.imu_init_fail_reason = Some(osd.imu_init_fail_reason);
216 frame.osd.is_acceletor_over_range = osd.is_acceletor_over_range;
217 frame.osd.is_barometer_dead_in_air = osd.is_barometer_dead_in_air;
218 frame.osd.is_compass_error = osd.is_compass_error;
219 frame.osd.is_go_home_height_modified = osd.is_go_home_height_modified;
220 frame.osd.can_ioc_work = osd.can_ioc_work;
221 frame.osd.is_not_enough_force = osd.is_not_enough_force;
222 frame.osd.is_out_of_limit = osd.is_out_of_limit;
223 frame.osd.is_propeller_catapult = osd.is_propeller_catapult;
224 frame.osd.is_vibrating = osd.is_vibrating;
225 frame.osd.is_vision_used = osd.is_vision_used;
226 frame.osd.voltage_warning = osd.voltage_warning;
227
228 frame_index += 1;
229 }
230 Record::Gimbal(gimbal) => {
231 frame.gimbal.mode = Some(gimbal.mode);
232 frame.gimbal.pitch = gimbal.pitch;
233 frame.gimbal.roll = gimbal.roll;
234 frame.gimbal.yaw = gimbal.yaw;
235 if !frame.gimbal.is_pitch_at_limit && gimbal.is_pitch_at_limit {
236 frame.app.tip =
237 append_message(frame.app.tip, "Gimbal pitch axis endpoint reached.")
238 }
239 frame.gimbal.is_pitch_at_limit = gimbal.is_pitch_at_limit;
240 if !frame.gimbal.is_roll_at_limit && gimbal.is_roll_at_limit {
241 frame.app.tip =
242 append_message(frame.app.tip, "Gimbal roll axis endpoint reached.")
243 }
244 frame.gimbal.is_roll_at_limit = gimbal.is_roll_at_limit;
245 if !frame.gimbal.is_yaw_at_limit && gimbal.is_yaw_at_limit {
246 frame.app.tip =
247 append_message(frame.app.tip, "Gimbal yaw axis endpoint reached.")
248 }
249 frame.gimbal.is_yaw_at_limit = gimbal.is_yaw_at_limit;
250 frame.gimbal.is_stuck = gimbal.is_stuck;
251 }
252 Record::Camera(camera) => {
253 frame.camera.is_photo = camera.is_shooting_single_photo;
254 frame.camera.is_video = camera.is_recording;
255 frame.camera.sd_card_is_inserted = camera.has_sd_card;
256 frame.camera.sd_card_state = Some(camera.sd_card_state);
257 }
258 Record::RC(rc) => {
259 frame.rc.aileron = rc.aileron;
260 frame.rc.elevator = rc.elevator;
261 frame.rc.throttle = rc.throttle;
262 frame.rc.rudder = rc.rudder;
263 }
264 Record::RCDisplayField(rc) => {
265 frame.rc.aileron = rc.aileron;
266 frame.rc.elevator = rc.elevator;
267 frame.rc.throttle = rc.throttle;
268 frame.rc.rudder = rc.rudder;
269 }
270 Record::CenterBattery(battery) => {
271 frame.battery.charge_level = battery.relative_capacity;
272 frame.battery.voltage = battery.voltage;
273 frame.battery.current_capacity = battery.current_capacity as u32;
274 frame.battery.full_capacity = battery.full_capacity as u32;
275 frame.battery.full_capacity = battery.full_capacity as u32;
276 frame.battery.is_cell_voltage_estimated = false;
277
278 let cell_num = frame.battery.cell_voltages.len();
279 if cell_num > 0 {
280 frame.battery.cell_voltages[0] = battery.voltage_cell1;
281 }
282 if cell_num > 1 {
283 frame.battery.cell_voltages[1] = battery.voltage_cell2;
284 }
285 if cell_num > 2 {
286 frame.battery.cell_voltages[2] = battery.voltage_cell3;
287 }
288 if cell_num > 3 {
289 frame.battery.cell_voltages[3] = battery.voltage_cell4;
290 }
291 if cell_num > 4 {
292 frame.battery.cell_voltages[4] = battery.voltage_cell5;
293 }
294 if cell_num > 5 {
295 frame.battery.cell_voltages[5] = battery.voltage_cell6;
296 }
297 }
298 Record::SmartBattery(battery) => {
299 frame.battery.charge_level = battery.percent;
300 frame.battery.voltage = battery.voltage;
301 }
302 Record::SmartBatteryGroup(battery_group) => match battery_group {
303 SmartBatteryGroup::SmartBatteryStatic(_) => {}
304 SmartBatteryGroup::SmartBatteryDynamic(battery) => {
305 if details.product_type.battery_num() < 2 || battery.index == 1 {
307 frame.battery.voltage = battery.current_voltage;
308 frame.battery.current = battery.current_current;
309 frame.battery.current_capacity = battery.remained_capacity;
310 frame.battery.full_capacity = battery.full_capacity;
311 frame.battery.charge_level = battery.capacity_percent;
312 frame.battery.temperature = battery.temperature;
313 }
314 }
315 SmartBatteryGroup::SmartBatterySingleVoltage(battery) => {
316 let cell_num = frame
317 .battery
318 .cell_voltages
319 .len()
320 .min(battery.cell_count as usize);
321
322 frame.battery.is_cell_voltage_estimated = false;
323
324 frame.battery.cell_voltages[..cell_num]
325 .copy_from_slice(&battery.cell_voltages[..cell_num]);
326 }
327 },
328 Record::OFDM(ofdm) => {
329 if ofdm.is_up {
330 frame.rc.uplink_signal = Some(ofdm.signal_percent);
331 } else {
332 frame.rc.downlink_signal = Some(ofdm.signal_percent);
333 }
334 }
335 Record::Custom(custom) => {
336 frame.custom.date_time = custom.update_timestamp;
337 }
338 Record::Home(home) => {
339 frame.home.latitude = home.latitude;
340 frame.home.longitude = home.longitude;
341 if frame.home.altitude == f32::default() {
343 frame.osd.altitude += home.altitude;
344 }
345 frame.home.altitude = home.altitude;
346 frame.home.height_limit = home.max_allowed_height;
347 frame.home.is_home_record = home.is_home_record;
348 frame.home.go_home_mode = Some(home.go_home_mode);
349 frame.home.is_dynamic_home_point_enabled = home.is_dynamic_home_point_enabled;
350 frame.home.is_near_distance_limit = home.is_near_distance_limit;
351 frame.home.is_near_height_limit = home.is_near_height_limit;
352 frame.home.is_compass_calibrating = home.is_compass_adjust;
353 if home.is_compass_adjust {
354 frame.home.compass_calibration_state = Some(home.compass_state);
355 }
356 frame.home.is_multiple_mode_enabled = home.is_multiple_mode_open;
357 frame.home.is_beginner_mode = home.is_beginner_mode;
358 frame.home.is_ioc_enabled = home.is_ioc_open;
359 if home.is_ioc_open {
360 frame.home.ioc_mode = Some(home.ioc_mode);
361 }
362 frame.home.go_home_height = home.go_home_height;
363 if home.is_ioc_open {
364 frame.home.ioc_course_lock_angle = Some(home.ioc_course_lock_angle);
365 }
366 frame.home.max_allowed_height = home.max_allowed_height;
367 frame.home.current_flight_record_index = home.current_flight_record_index;
368 }
369 Record::Recover(recover) => {
370 frame.recover.app_platform = Some(recover.app_platform);
371 frame.recover.app_version = recover.app_version;
372 frame.recover.aircraft_name = recover.aircraft_name;
373 frame.recover.aircraft_sn = recover.aircraft_sn;
374 frame.recover.camera_sn = recover.camera_sn;
375 frame.recover.rc_sn = recover.rc_sn;
376 frame.recover.battery_sn = recover.battery_sn;
377 }
378 Record::AppTip(app_tip) => {
379 frame.app.tip = append_message(frame.app.tip, app_tip.message);
380 }
381 Record::AppWarn(app_warn) => {
382 frame.app.warn = append_message(frame.app.warn, app_warn.message);
383 }
384 Record::AppSeriousWarn(app_serious_warn) => {
385 frame.app.warn = append_message(frame.app.warn, app_serious_warn.message);
386 }
387 _ => {}
388 }
389 }
390
391 frames
392}