@@ -185,23 +185,53 @@ fn main() -> Result<()> {
185185
186186 let mut processed_files = 0 ;
187187
188+ if debug {
189+ println ! ( "Input patterns: {file_patterns:?}" ) ;
190+ }
191+
188192 // Collect all valid file paths
189193 let mut valid_paths = Vec :: new ( ) ;
190194 for pattern in & file_patterns {
195+ if debug {
196+ println ! ( "Processing pattern: {pattern}" ) ;
197+ }
198+
191199 let paths: Vec < _ > = if pattern. contains ( '*' ) || pattern. contains ( '?' ) {
192- glob ( pattern)
193- . with_context ( || format ! ( "Invalid glob pattern: {pattern}" ) ) ?
194- . collect :: < Result < Vec < _ > , _ > > ( )
195- . with_context ( || format ! ( "Error expanding glob pattern: {pattern}" ) ) ?
200+ match glob ( pattern) {
201+ Ok ( glob_iter) => {
202+ let collected = glob_iter. collect :: < Result < Vec < _ > , _ > > ( ) ;
203+ match collected {
204+ Ok ( paths) => {
205+ if debug {
206+ println ! ( "Glob pattern '{pattern}' matched {} files" , paths. len( ) ) ;
207+ }
208+ paths
209+ }
210+ Err ( e) => {
211+ eprintln ! ( "Error expanding glob pattern '{pattern}': {e}" ) ;
212+ continue ;
213+ }
214+ }
215+ }
216+ Err ( e) => {
217+ eprintln ! ( "Invalid glob pattern '{pattern}': {e}" ) ;
218+ continue ;
219+ }
220+ }
196221 } else {
197222 vec ! [ Path :: new( pattern) . to_path_buf( ) ]
198223 } ;
199224
200225 for path in paths {
226+ if debug {
227+ println ! ( "Checking file: {path:?}" ) ;
228+ }
229+
201230 if !path. exists ( ) {
202231 eprintln ! ( "Warning: File does not exist: {path:?}" ) ;
203232 continue ;
204233 }
234+
205235 let valid_extension = path. extension ( )
206236 . and_then ( |ext| ext. to_str ( ) )
207237 . map ( |ext| {
@@ -211,13 +241,31 @@ fn main() -> Result<()> {
211241 . unwrap_or ( false ) ;
212242
213243 if !valid_extension {
214- eprintln ! ( "Warning: Skipping file with unsupported extension: {path:?}" ) ;
244+ let ext = path. extension ( )
245+ . and_then ( |e| e. to_str ( ) )
246+ . unwrap_or ( "none" ) ;
247+ eprintln ! ( "Warning: Skipping file with unsupported extension '{ext}': {path:?}" ) ;
215248 continue ;
216249 }
250+
251+ if debug {
252+ println ! ( "Added valid file: {path:?}" ) ;
253+ }
217254 valid_paths. push ( path) ;
218255 }
219256 }
220257
258+ if debug {
259+ println ! ( "Found {} valid files to process" , valid_paths. len( ) ) ;
260+ }
261+
262+ if valid_paths. is_empty ( ) {
263+ eprintln ! ( "Error: No valid files found to process." ) ;
264+ eprintln ! ( "Supported extensions: .BBL, .BFL, .TXT (case-insensitive)" ) ;
265+ eprintln ! ( "Input patterns were: {file_patterns:?}" ) ;
266+ std:: process:: exit ( 1 ) ;
267+ }
268+
221269 // Process files
222270 for ( index, path) in valid_paths. iter ( ) . enumerate ( ) {
223271 if index > 0 {
@@ -254,7 +302,12 @@ fn main() -> Result<()> {
254302 }
255303
256304 if processed_files == 0 {
257- eprintln ! ( "No files were successfully processed." ) ;
305+ eprintln ! ( "Error: No files were successfully processed out of {} files found." , valid_paths. len( ) ) ;
306+ eprintln ! ( "This could be due to:" ) ;
307+ eprintln ! ( " - Files not being valid BBL/BFL format" ) ;
308+ eprintln ! ( " - Corrupted or empty files" ) ;
309+ eprintln ! ( " - Missing blackbox log headers" ) ;
310+ eprintln ! ( "Use --debug flag for more detailed error information." ) ;
258311 std:: process:: exit ( 1 ) ;
259312 }
260313
@@ -1586,3 +1639,156 @@ fn format_failsafe_phase(phase: i32) -> String {
15861639 _ => phase. to_string ( ) ,
15871640 }
15881641}
1642+
1643+ #[ cfg( test) ]
1644+ mod tests {
1645+ use super :: * ;
1646+ use std:: path:: PathBuf ;
1647+
1648+ #[ test]
1649+ fn test_frame_definition_creation ( ) {
1650+ let mut frame_def = FrameDefinition :: new ( ) ;
1651+ assert_eq ! ( frame_def. count, 0 ) ;
1652+ assert ! ( frame_def. field_names. is_empty( ) ) ;
1653+
1654+ let field_names = vec ! [ "time" . to_string( ) , "loopIteration" . to_string( ) ] ;
1655+ frame_def = FrameDefinition :: from_field_names ( field_names. clone ( ) ) ;
1656+ assert_eq ! ( frame_def. count, 2 ) ;
1657+ assert_eq ! ( frame_def. field_names, field_names) ;
1658+ }
1659+
1660+ #[ test]
1661+ fn test_frame_definition_predictor_update ( ) {
1662+ let mut frame_def = FrameDefinition :: from_field_names ( vec ! [ "field1" . to_string( ) , "field2" . to_string( ) ] ) ;
1663+ let predictors = vec ! [ 1 , 2 ] ;
1664+ frame_def. update_predictors ( & predictors) ;
1665+
1666+ assert_eq ! ( frame_def. fields[ 0 ] . predictor, 1 ) ;
1667+ assert_eq ! ( frame_def. fields[ 1 ] . predictor, 2 ) ;
1668+ }
1669+
1670+ #[ test]
1671+ fn test_unit_conversions ( ) {
1672+ // Test voltage conversion (0.1V units)
1673+ let volts = convert_vbat_to_volts ( 33 ) ; // 33 * 0.1 = 3.3V
1674+ assert ! ( ( volts - 3.3 ) . abs( ) < 0.01 ) ;
1675+
1676+ // Test amperage conversion (0.01A units)
1677+ let amps = convert_amperage_to_amps ( 100 ) ; // 100 * 0.01 = 1.0A
1678+ assert ! ( ( amps - 1.0 ) . abs( ) < 0.01 ) ;
1679+ }
1680+
1681+ #[ test]
1682+ fn test_frame_stats_default ( ) {
1683+ let stats = FrameStats :: default ( ) ;
1684+ assert_eq ! ( stats. total_frames, 0 ) ;
1685+ assert_eq ! ( stats. i_frames, 0 ) ;
1686+ assert_eq ! ( stats. p_frames, 0 ) ;
1687+ assert_eq ! ( stats. failed_frames, 0 ) ;
1688+ }
1689+
1690+ #[ test]
1691+ fn test_csv_export_options ( ) {
1692+ let options = CsvExportOptions {
1693+ output_dir : Some ( "/tmp" . to_string ( ) ) ,
1694+ } ;
1695+ assert_eq ! ( options. output_dir. as_ref( ) . unwrap( ) , "/tmp" ) ;
1696+
1697+ let options = CsvExportOptions {
1698+ output_dir : None ,
1699+ } ;
1700+ assert ! ( options. output_dir. is_none( ) ) ;
1701+ }
1702+
1703+ #[ test]
1704+ fn test_file_extension_validation ( ) {
1705+ let valid_extensions = [ "bbl" , "bfl" , "txt" ] ;
1706+ let invalid_extensions = [ "csv" , "json" , "xml" ] ;
1707+
1708+ for ext in valid_extensions {
1709+ let path = PathBuf :: from ( format ! ( "test.{ext}" ) ) ;
1710+ let is_valid = path. extension ( )
1711+ . and_then ( |e| e. to_str ( ) )
1712+ . map ( |e| {
1713+ let ext_lower = e. to_ascii_lowercase ( ) ;
1714+ ext_lower == "bbl" || ext_lower == "bfl" || ext_lower == "txt"
1715+ } )
1716+ . unwrap_or ( false ) ;
1717+ assert ! ( is_valid, "Extension {ext} should be valid" ) ;
1718+ }
1719+
1720+ for ext in invalid_extensions {
1721+ let path = PathBuf :: from ( format ! ( "test.{ext}" ) ) ;
1722+ let is_valid = path. extension ( )
1723+ . and_then ( |e| e. to_str ( ) )
1724+ . map ( |e| {
1725+ let ext_lower = e. to_ascii_lowercase ( ) ;
1726+ ext_lower == "bbl" || ext_lower == "bfl" || ext_lower == "txt"
1727+ } )
1728+ . unwrap_or ( false ) ;
1729+ assert ! ( !is_valid, "Extension {ext} should be invalid" ) ;
1730+ }
1731+ }
1732+
1733+ #[ test]
1734+ fn test_format_functions_basic ( ) {
1735+ // Test that format functions work correctly with basic inputs
1736+ let flight_mode = format_flight_mode_flags ( 0 ) ;
1737+ assert_eq ! ( flight_mode, "0" ) ;
1738+
1739+ let flight_mode_armed = format_flight_mode_flags ( 1 ) ; // ARM flag
1740+ assert ! ( flight_mode_armed. contains( "ARM" ) ) ;
1741+
1742+ let state = format_state_flags ( 0 ) ;
1743+ assert_eq ! ( state, "0" ) ;
1744+
1745+ let failsafe = format_failsafe_phase ( 0 ) ;
1746+ assert_eq ! ( failsafe, "IDLE" ) ;
1747+
1748+ let failsafe_landing = format_failsafe_phase ( 2 ) ;
1749+ assert_eq ! ( failsafe_landing, "LANDING" ) ;
1750+ }
1751+
1752+ #[ test]
1753+ fn test_bbl_header_creation ( ) {
1754+ let header = BBLHeader {
1755+ firmware_revision : "4.5.0" . to_string ( ) ,
1756+ board_info : "MAMBAF722" . to_string ( ) ,
1757+ craft_name : "TestCraft" . to_string ( ) ,
1758+ data_version : 2 ,
1759+ looptime : 500 ,
1760+ i_frame_def : FrameDefinition :: new ( ) ,
1761+ p_frame_def : FrameDefinition :: new ( ) ,
1762+ s_frame_def : FrameDefinition :: new ( ) ,
1763+ g_frame_def : FrameDefinition :: new ( ) ,
1764+ h_frame_def : FrameDefinition :: new ( ) ,
1765+ sysconfig : HashMap :: new ( ) ,
1766+ all_headers : Vec :: new ( ) ,
1767+ } ;
1768+
1769+ assert_eq ! ( header. firmware_revision, "4.5.0" ) ;
1770+ assert_eq ! ( header. board_info, "MAMBAF722" ) ;
1771+ assert_eq ! ( header. craft_name, "TestCraft" ) ;
1772+ assert_eq ! ( header. data_version, 2 ) ;
1773+ assert_eq ! ( header. looptime, 500 ) ;
1774+ }
1775+
1776+ #[ test]
1777+ fn test_decoded_frame_creation ( ) {
1778+ let mut data = HashMap :: new ( ) ;
1779+ data. insert ( "time" . to_string ( ) , 1000 ) ;
1780+ data. insert ( "loopIteration" . to_string ( ) , 1 ) ;
1781+
1782+ let frame = DecodedFrame {
1783+ frame_type : 'I' ,
1784+ timestamp_us : 1000 ,
1785+ loop_iteration : 1 ,
1786+ data,
1787+ } ;
1788+
1789+ assert_eq ! ( frame. frame_type, 'I' ) ;
1790+ assert_eq ! ( frame. timestamp_us, 1000 ) ;
1791+ assert_eq ! ( frame. loop_iteration, 1 ) ;
1792+ assert_eq ! ( frame. data. get( "time" ) , Some ( & 1000 ) ) ;
1793+ }
1794+ }
0 commit comments