use std::{u64, vec}; use crate::SignalFormat; /// Record information obtained from the header. /// /// Only the `name`, `signal_count`, and `sampling_freq` values are required by default. #[derive(Debug, Clone)] struct Record { name: String, seg_num: Option, signal_count: u64, sampling_freq: u64, counter_freq: Option, base_counter_val: Option, sample_num: Option, basetime: Option, // I dont thing we really need to care much about these basedate: Option } impl Record { /// Attempts to generate the record information from a string of the argument line pub fn from_str(argument_line: &str) -> Result { let args: Vec<&str> = argument_line.split(' ').collect(); if args.len() < 2 { return Err("error: header file provided lacks sufficient record arguments."); } let name = args[0].to_string(); let seg_num: Option; let sig_count: u64; let sampling_freq: u64; // Everything else is initialized with None, this is for sake of me not getting a stroke let mut counter_freq: Option = None; let mut base_counter_val: Option = None; let mut sample_num: Option = None; let mut basetime: Option = None; let mut basedate: Option = None; // Signals and segments are kept in a single argument organized as signal/segment // segments are optional and we need to check this too { let seg_sig: Vec<&str> = args[1].split('/').collect(); if seg_sig.len() == 2 { match seg_sig[1].parse::() { Ok(value) => { seg_num = Some(value); } Err(_) => { return Err("error: failed to parse segment number from header."); } } } else { seg_num = None; } match seg_sig[0].parse::() { Ok(value) => { sig_count = value; } Err(_) => { return Err("error: failed to parse signal number from header."); } } } // Parse everything else, if present loop { // This is apparently the way to get safe correct goto-like behaviour if args.len() <= 2 { // Sampling frequency and counter frequency sampling_freq = 250; // Default value break; } { let freq_dual: Vec<&str> = args[2].split('/').collect(); if freq_dual.len() == 2 { match freq_dual[1].parse::() { Ok(value) => { counter_freq = Some(value); } Err(_) => { return Err("error: failed to parse counter frequency from header."); } } } match freq_dual[0].parse::() { Ok(value) => { sampling_freq = value; } Err(_) => { return Err("error: failed to parse sampling frequency from header."); } } } if args.len() <= 3 {break;} match args[3].parse::() { Ok(value) => { base_counter_val = Some(value); } Err(_) => {} } if args.len() <= 4 {break;} match args[4].parse::() { Ok(value) => { sample_num = Some(value); } Err(_) => {} } if args.len() <= 5 {break;} basetime = Some(args[5].to_string()); if args.len() <= 6 {break;} basedate = Some(args[6].to_string()); break; } Ok(Record { name: name, seg_num: seg_num, signal_count: sig_count, sampling_freq: sampling_freq, counter_freq: counter_freq, base_counter_val: base_counter_val, sample_num: sample_num, basetime: basetime, basedate: basedate }) } } #[derive(Debug, Clone)] struct SignalSpec { filename: String, format: SignalFormat, samples_frame: Option, skew: Option, offset: Option, adc_gain: Option, baseline: Option, units: Option, adc_resolution: Option, adc_zero: Option, initial_val: Option, checksum: Option, blocksize: Option, desc: Option } impl SignalSpec { /// Attempts to generate a valid signal specification struct from a supplied string. /// Returns a `Result` containing possible error information. /// /// ## Arguments /// - `argument_line` - argument line for the signal specification, taken from header file pub fn from_str(argument_line: &str) -> Result { let args: Vec<&str> = argument_line.split(' ').collect(); if args.len() < 2 { return Err("error: signal provided by header file lacks sufficient arguments."); } let name = args[0].to_string(); let sigformat: SignalFormat; // Optional args let samples_frame: Option = None; let skew: Option = None; let offset: Option = None; let adc_gain: Option = None; let baseline: Option = None; let units: Option = None; let adc_resolution: Option = None; let adc_zero: Option = None; let initial_val: Option = None; let checksum: Option = None; let blocksize: Option = None; let desc: Option = None; // TODO: implement samples per frame, skew, and offset match args[1].parse::() { Ok(value) => { sigformat = SignalSpec::parse_format(value); }, Err(_) => { return Err("error: unable to parse format from signal"); } } loop { if args.len() <= 2 { break; } { let bracket_split: Vec<&str> = args[2].split("(").collect(); } if args.len() <= 3 { } break; } Ok( SignalSpec { filename: name, format: sigformat, samples_frame: samples_frame, skew: skew, offset: offset, adc_gain: adc_gain, baseline: baseline, units: units, adc_resolution: adc_resolution, adc_zero: adc_zero, initial_val: initial_val, checksum: checksum, blocksize: blocksize, desc: desc } ) } fn parse_format(formatnum: u64) -> SignalFormat { match formatnum { 16 => SignalFormat::Format16, 212 => SignalFormat::Format212, 0..=u64::MAX => SignalFormat::Unimpl } } } #[derive(Debug, Clone)] pub struct Header { record: Option, signal_specs: Vec } impl Header { /// Creates a completely empty, not fully initialized header /// /// This is a workaround because I couldn't figure out how to make the compiler /// accept non-initializing the value for the first pass of a for loop in /// `parse_header()` fn new() -> Header { Header { record: None, signal_specs: vec![] } } fn from_record(record: Record) -> Header { Header { record: Some(record), signal_specs: vec![] } } fn add_signal_spec(&mut self, spec: SignalSpec) { self.signal_specs.push(spec); } pub fn is_empty(&self) -> bool { match self.record { Some(_) => false, None => true } } } /// Attempts to parse the header file pub fn parse_header(header_data: &str) -> Result { let header_lines: Vec<&str> = header_data.split("\n").collect(); let mut found_record: bool = false; let mut header: Header = Header::new(); let mut specs_read: u64 = 0; let mut specs_max: u64 = 0; for line in header_lines { // Ignore commented lines if line.starts_with("#") { continue; } if !found_record { let possible_record = Record::from_str(line); match possible_record { Ok(rec) => { specs_max = rec.signal_count; header = Header::from_record(rec); found_record = true; continue; } Err(e) => { return Err(e) } } } let possible_spec = SignalSpec::from_str(line); match possible_spec { Ok(spec) => { header.add_signal_spec(spec); } Err(e) => { return Err(e); } } specs_read += 1; if specs_read >= specs_max { break; } } if header.is_empty() { return Err("Unable to parse valid header information"); } Ok(header) }