Skip to content

Commit e20e467

Browse files
authored
Refactor CLI to use crate library functions (eliminate code duplication) (#9) (#10)
* refactor: import GPS conversion functions from crate library - Add import for convert_gps_coordinate, convert_gps_altitude, convert_gps_speed, convert_gps_course from bbl_parser::conversion - Remove duplicate conversion function definitions from CLI main.rs - Eliminates ~40 lines of duplicated code - Addresses Issue #9: Refactor CLI to use crate library functions * refactor: import flag formatting functions from crate library - Add imports for format_flight_mode_flags, format_state_flags, format_failsafe_phase from bbl_parser::conversion - Remove ~90 lines of duplicate formatting function definitions - Addresses Issue #9: Refactor CLI to use crate library functions * refactor: migrate crate library to use anyhow::Result This enables CLI and crate to share the same error handling pattern, which is a prerequisite for deduplicating stream and parser code. - parser/stream.rs: use anyhow::Result instead of BBLError - parser/decoder.rs: use anyhow::anyhow! for error messages - parser/event.rs: use anyhow::Result - parser/frame.rs: use anyhow::Result - parser/gps.rs: use anyhow::Result - parser/header.rs: use anyhow::Result and anyhow::anyhow! - parser/main.rs: use anyhow::Result - export.rs: use anyhow::Result Addresses Issue #9: Refactor CLI to use crate library functions * fix: align crate parse_h_frame and 14-bit encoding with CLI behavior - parser/gps.rs: parse_h_frame now uses graceful fallback for unknown encodings (read_signed_vb with unwrap_or(0)) matching CLI behavior - parser/stream.rs: read_neg_14bit now uses sign-magnitude encoding matching CLI's bbl_format::sign_extend_14bit + negation - Updated tests for sign-magnitude encoding - Removed unused two's complement sign extension function This ensures crate's H-frame parsing matches the working CLI logic. Addresses Issue #9: CLI logic takes precedence
1 parent 40f9311 commit e20e467

9 files changed

Lines changed: 85 additions & 222 deletions

File tree

src/export.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@
55
66
use crate::conversion::*;
77
use crate::types::*;
8-
use crate::Result;
9-
use anyhow::Context;
8+
use anyhow::{Context, Result};
109
use std::collections::HashMap;
1110
use std::fs::File;
1211
use std::io::{BufWriter, Write};

src/main.rs

Lines changed: 9 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ use std::fs;
99
use std::io::Write;
1010
use std::path::{Path, PathBuf};
1111

12+
// Import conversion functions from crate library to avoid code duplication
13+
use bbl_parser::conversion::{
14+
convert_gps_altitude, convert_gps_coordinate, convert_gps_course, convert_gps_speed,
15+
format_failsafe_phase, format_flight_mode_flags, format_state_flags,
16+
};
17+
1218
/// Maximum recursion depth to prevent stack overflow
1319
const MAX_RECURSION_DEPTH: usize = 100;
1420

@@ -2910,148 +2916,11 @@ fn parse_bbl_file_streaming(
29102916
Ok(processed_logs)
29112917
}
29122918

2913-
fn format_flight_mode_flags(flags: i32) -> String {
2914-
let mut modes = Vec::new();
2915-
2916-
// Based on Betaflight firmware runtime_config.h flightModeFlags_e enum
2917-
// This matches the blackbox-tools implementation exactly:
2918-
// https://github.com/betaflight/blackbox-tools/blob/master/src/blackbox_fielddefs.c
2919-
2920-
// FLIGHT_LOG_FLIGHT_MODE_NAME array from blackbox-tools
2921-
if (flags & (1 << 0)) != 0 {
2922-
modes.push("ANGLE_MODE"); // ANGLE_MODE = (1 << 0)
2923-
}
2924-
if (flags & (1 << 1)) != 0 {
2925-
modes.push("HORIZON_MODE"); // HORIZON_MODE = (1 << 1)
2926-
}
2927-
if (flags & (1 << 2)) != 0 {
2928-
modes.push("MAG"); // MAG_MODE = (1 << 2)
2929-
}
2930-
if (flags & (1 << 3)) != 0 {
2931-
modes.push("BARO"); // ALT_HOLD_MODE = (1 << 3) (old name BARO)
2932-
}
2933-
if (flags & (1 << 4)) != 0 {
2934-
modes.push("GPS_HOME"); // GPS_HOME_MODE (disabled in current firmware)
2935-
}
2936-
if (flags & (1 << 5)) != 0 {
2937-
modes.push("GPS_HOLD"); // POS_HOLD_MODE = (1 << 5) (old name GPS_HOLD)
2938-
}
2939-
if (flags & (1 << 6)) != 0 {
2940-
modes.push("HEADFREE"); // HEADFREE_MODE = (1 << 6)
2941-
}
2942-
if (flags & (1 << 7)) != 0 {
2943-
modes.push("UNUSED"); // CHIRP_MODE = (1 << 7) (old autotune, now unused)
2944-
}
2945-
if (flags & (1 << 8)) != 0 {
2946-
modes.push("PASSTHRU"); // PASSTHRU_MODE = (1 << 8)
2947-
}
2948-
if (flags & (1 << 9)) != 0 {
2949-
modes.push("RANGEFINDER_MODE"); // RANGEFINDER_MODE (disabled in current firmware)
2950-
}
2951-
if (flags & (1 << 10)) != 0 {
2952-
modes.push("FAILSAFE_MODE"); // FAILSAFE_MODE = (1 << 10)
2953-
}
2954-
if (flags & (1 << 11)) != 0 {
2955-
modes.push("GPS_RESCUE_MODE"); // GPS_RESCUE_MODE = (1 << 11) (new in current firmware)
2956-
}
2957-
2958-
if modes.is_empty() {
2959-
"0".to_string()
2960-
} else {
2961-
modes.join("|") // Use pipe separator to avoid breaking CSV format
2962-
}
2963-
}
2964-
2965-
fn format_state_flags(flags: i32) -> String {
2966-
let mut states = Vec::new();
2967-
2968-
// Based on Betaflight firmware runtime_config.h stateFlags_t enum
2969-
// This matches the blackbox-tools implementation exactly:
2970-
// https://github.com/betaflight/blackbox-tools/blob/master/src/blackbox_fielddefs.c
2971-
2972-
// FLIGHT_LOG_FLIGHT_STATE_NAME array from blackbox-tools
2973-
if (flags & (1 << 0)) != 0 {
2974-
states.push("GPS_FIX_HOME"); // GPS_FIX_HOME = (1 << 0)
2975-
}
2976-
if (flags & (1 << 1)) != 0 {
2977-
states.push("GPS_FIX"); // GPS_FIX = (1 << 1)
2978-
}
2979-
if (flags & (1 << 2)) != 0 {
2980-
states.push("CALIBRATE_MAG"); // GPS_FIX_EVER = (1 << 2) but old name CALIBRATE_MAG
2981-
}
2982-
if (flags & (1 << 3)) != 0 {
2983-
states.push("SMALL_ANGLE"); // Used in blackbox-tools for compatibility
2984-
}
2985-
if (flags & (1 << 4)) != 0 {
2986-
states.push("FIXED_WING"); // Used in blackbox-tools for compatibility
2987-
}
2988-
2989-
if states.is_empty() {
2990-
"0".to_string()
2991-
} else {
2992-
states.join("|") // Use pipe separator to avoid breaking CSV format
2993-
}
2994-
}
2995-
2996-
fn format_failsafe_phase(phase: i32) -> String {
2997-
// Based on Betaflight firmware failsafe.h failsafePhase_e enum
2998-
// This matches the blackbox-tools implementation exactly:
2999-
// https://github.com/betaflight/blackbox-tools/blob/master/src/blackbox_fielddefs.c
3000-
3001-
// FLIGHT_LOG_FAILSAFE_PHASE_NAME array from blackbox-tools
3002-
match phase {
3003-
0 => "IDLE".to_string(), // FAILSAFE_IDLE = 0
3004-
1 => "RX_LOSS_DETECTED".to_string(), // FAILSAFE_RX_LOSS_DETECTED
3005-
2 => "LANDING".to_string(), // FAILSAFE_LANDING
3006-
3 => "LANDED".to_string(), // FAILSAFE_LANDED
3007-
4 => "RX_LOSS_MONITORING".to_string(), // FAILSAFE_RX_LOSS_MONITORING (new in current firmware)
3008-
5 => "RX_LOSS_RECOVERED".to_string(), // FAILSAFE_RX_LOSS_RECOVERED (new in current firmware)
3009-
6 => "GPS_RESCUE".to_string(), // FAILSAFE_GPS_RESCUE (new in current firmware)
3010-
_ => phase.to_string(),
3011-
}
3012-
}
2919+
// Note: Flag formatting functions now imported from bbl_parser::conversion module
2920+
// (format_flight_mode_flags, format_state_flags, format_failsafe_phase)
30132921

30142922
// GPS/GPX export functions
3015-
fn extract_major_firmware_version(firmware_revision: &str) -> u8 {
3016-
// Extract major version from firmware string like "Betaflight 4.5.1 (77d01ba3b) AT32F435M"
3017-
if let Some(start) = firmware_revision.find(' ') {
3018-
let version_part = &firmware_revision[start + 1..];
3019-
if let Some(end) = version_part.find('.') {
3020-
if let Ok(major) = version_part[..end].parse::<u8>() {
3021-
return major;
3022-
}
3023-
}
3024-
}
3025-
// Default to 4 if parsing fails (assume modern firmware)
3026-
4
3027-
}
3028-
3029-
fn convert_gps_coordinate(raw_value: i32) -> f64 {
3030-
// GPS coordinates are stored as degrees * 10000000
3031-
raw_value as f64 / 10000000.0
3032-
}
3033-
3034-
fn convert_gps_altitude(raw_value: i32, firmware_revision: &str) -> f64 {
3035-
// Altitude units changed between firmware versions:
3036-
// Before Betaflight 4: centimeters (factor 0.01)
3037-
// Betaflight 4+: decimeters (factor 0.1)
3038-
let major_version = extract_major_firmware_version(firmware_revision);
3039-
if major_version >= 4 {
3040-
raw_value as f64 / 10.0 // decimeters to meters
3041-
} else {
3042-
raw_value as f64 / 100.0 // centimeters to meters
3043-
}
3044-
}
3045-
3046-
fn convert_gps_speed(raw_value: i32) -> f64 {
3047-
// Speed is stored as cm/s * 100, convert to m/s
3048-
raw_value as f64 / 100.0
3049-
}
3050-
3051-
fn convert_gps_course(raw_value: i32) -> f64 {
3052-
// Course is stored as degrees * 10
3053-
raw_value as f64 / 10.0
3054-
}
2923+
// Note: GPS conversion functions now imported from bbl_parser::conversion module
30552924

30562925
fn export_gpx_file(
30572926
file_path: &Path,

src/parser/decoder.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
use crate::error::{BBLError, Result};
21
use crate::parser::stream::BBLDataStream;
2+
use anyhow::Result;
33

44
// BBL Encoding constants - directly from JavaScript reference
55
pub const ENCODING_SIGNED_VB: u8 = 0;
@@ -46,7 +46,7 @@ pub fn decode_field_value(
4646
values[index] = 0;
4747
}
4848
_ => {
49-
return Err(BBLError::InvalidEncoding(encoding));
49+
return Err(anyhow::anyhow!("Invalid encoding type: {}", encoding));
5050
}
5151
}
5252
Ok(())
@@ -127,6 +127,6 @@ pub fn apply_predictor(
127127
let motor_output_min = sysconfig.get("motorOutput[0]").copied().unwrap_or(48);
128128
Ok(value + motor_output_min) // Force signed 32-bit like Betaflight
129129
}
130-
_ => Err(BBLError::InvalidPredictor(predictor)),
130+
_ => Err(anyhow::anyhow!("Invalid predictor type: {}", predictor)),
131131
}
132132
}

src/parser/event.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
66
use crate::parser::stream::BBLDataStream;
77
use crate::types::EventFrame;
8-
use crate::Result;
8+
use anyhow::Result;
99

1010
/// Helper function to parse inflight adjustment events (types 4 and 13)
1111
/// Returns the event description string

src/parser/frame.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
use crate::error::Result;
21
use crate::parser::{decoder::*, event::parse_e_frame, gps::*, stream::BBLDataStream};
32
use crate::types::{
43
DecodedFrame, EventFrame, FrameDefinition, FrameHistory, FrameStats, GpsCoordinate,
54
GpsHomeCoordinate,
65
};
6+
use anyhow::Result;
77
use std::collections::HashMap;
88

99
/// Parse frames from binary data

src/parser/gps.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use crate::parser::decoder::{
1212
use crate::parser::frame::parse_frame_data;
1313
use crate::parser::stream::BBLDataStream;
1414
use crate::types::{FrameDefinition, GpsCoordinate, GpsHomeCoordinate};
15-
use crate::Result;
15+
use anyhow::Result;
1616
use std::collections::HashMap;
1717

1818
/// Parse H-frame (GPS home position) data from the stream
@@ -42,12 +42,15 @@ pub fn parse_h_frame(
4242
ENCODING_NEG_14BIT => stream.read_neg_14bit()?,
4343
ENCODING_NULL => 0,
4444
_ => {
45-
// Unsupported H-frame encoding - return error instead of silently continuing
46-
// This prevents stream desynchronization from being masked by default values
47-
return Err(anyhow::anyhow!(
48-
"Unsupported H-frame encoding {} for field '{}' - stream desynchronization possible",
49-
field.encoding, field.name
50-
));
45+
// Unsupported H-frame encoding - gracefully fall back to signed VB
46+
// This matches CLI behavior for maximum compatibility with various logs
47+
if debug {
48+
println!(
49+
"Unsupported H-frame encoding {} for field {}",
50+
field.encoding, field.name
51+
);
52+
}
53+
stream.read_signed_vb().unwrap_or(0)
5154
}
5255
};
5356

src/parser/header.rs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
use crate::error::{BBLError, Result};
21
use crate::types::{BBLHeader, FrameDefinition};
2+
use anyhow::Result;
33
use std::collections::HashMap;
44

55
/// Parse BBL headers from text
@@ -157,9 +157,7 @@ fn parse_predictor_info(line: &str, frame_def: &mut FrameDefinition) -> Result<(
157157
frame_def.update_predictors(&predictors);
158158
Ok(())
159159
}
160-
Err(_) => Err(BBLError::InvalidHeader(
161-
"Invalid predictor values".to_string(),
162-
)),
160+
Err(_) => Err(anyhow::anyhow!("Invalid header: Invalid predictor values")),
163161
}
164162
}
165163

@@ -178,9 +176,7 @@ fn parse_encoding_info(line: &str, frame_def: &mut FrameDefinition) -> Result<()
178176
frame_def.update_encoding(&encodings);
179177
Ok(())
180178
}
181-
Err(_) => Err(BBLError::InvalidHeader(
182-
"Invalid encoding values".to_string(),
183-
)),
179+
Err(_) => Err(anyhow::anyhow!("Invalid header: Invalid encoding values")),
184180
}
185181
}
186182

src/parser/main.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use crate::types::*;
2-
use crate::Result;
3-
use anyhow::{anyhow, Context};
2+
use anyhow::{anyhow, Context, Result};
43
use std::path::Path;
54

65
/// Parse BBL file and return all logs (for CLI and multi-log processing)

0 commit comments

Comments
 (0)