use super::{BatchDownloadResult, SymbolDownloadResult};
use crate::data::{Acquisition, ChannelData};
use crate::decoder::decode_waveform_chunks;
use crate::errors::TekHsiError;
use tracing::warn;
pub(super) fn decode_batch(batch: &mut BatchDownloadResult) -> Result<Acquisition, TekHsiError> {
let mut has_success = false;
let mut channel_data = Vec::with_capacity(batch.results.len());
for result in batch.results.iter_mut() {
match &result {
SymbolDownloadResult::Success {
symbol,
header,
data_chunks,
} => {
has_success = true;
match decode_waveform_chunks(header, data_chunks) {
Ok(wf) => {
channel_data.push(ChannelData::Waveform {
acq_id: header.dataid,
symbol: symbol.clone(),
header: header.clone(),
waveform: wf,
});
}
Err(de) => {
channel_data.push(ChannelData::DecodeError {
symbol: symbol.clone(),
header: header.clone(),
error: de,
});
}
};
}
SymbolDownloadResult::Failure { symbol, error } => {
warn!("Failed to download symbol {}: {}", symbol, error);
channel_data.push(ChannelData::AcquisitionError {
symbol: symbol.clone(),
error: error.clone(),
});
}
}
}
if !has_success {
return Err(TekHsiError::NoValidData);
}
Ok(Acquisition::new(
channel_data,
batch.wait_time,
batch.download_time,
))
}
#[cfg(test)]
#[cfg_attr(coverage, coverage(off))]
mod tests {
use super::super::{BatchDownloadResult, SymbolDownloadResult};
use super::decode_batch;
use crate::data::{ChannelData, Waveform};
use crate::errors::AcquisitionError;
use crate::errors::TekHsiError;
use crate::tekscope::WaveformHeader;
use crate::validation::is_header_valid;
use smol_str::SmolStr;
fn base_header() -> WaveformHeader {
WaveformHeader {
sourcename: "ch1".to_string(),
verticalunits: "V".to_string(),
horizontal_units: "s".to_string(),
hasdata: true,
..Default::default()
}
}
#[test]
fn decode_batch_returns_no_valid_data() {
let mut result = BatchDownloadResult {
results: vec![SymbolDownloadResult::Failure {
symbol: SmolStr::from("ch1"),
error: AcquisitionError::DownloadFailed {
message: "test".to_string(),
},
}],
..Default::default()
};
let err = decode_batch(&mut result).expect_err("expected error");
assert!(matches!(err, TekHsiError::NoValidData));
}
#[test]
fn decode_batch_handles_partial_failures() {
let header = WaveformHeader {
wfmtype: 1,
sourcewidth: 1,
noofsamples: 2,
dataid: 7,
..base_header()
};
assert!(is_header_valid(&header));
let mut result = BatchDownloadResult {
results: vec![
SymbolDownloadResult::Success {
symbol: SmolStr::from("ch1"),
header,
data_chunks: vec![vec![1u8, 2]],
},
SymbolDownloadResult::Failure {
symbol: SmolStr::from("ch2"),
error: AcquisitionError::HeaderRequestFailed {
status: "Server did not provide header payload".to_string(),
},
},
],
..Default::default()
};
let acquisition = decode_batch(&mut result).expect("expected acquisition");
assert_eq!(acquisition.data.len(), 2);
let ch1 = acquisition
.data
.iter()
.find(|cd| cd.symbol() == "ch1")
.unwrap();
match ch1 {
ChannelData::Waveform {
acq_id,
waveform: Waveform::Analog(_),
..
} => assert_eq!(*acq_id, 7),
_ => panic!("expected analog waveform for ch1"),
}
let ch2 = acquisition
.data
.iter()
.find(|cd| cd.symbol() == "ch2")
.unwrap();
assert!(matches!(ch2, ChannelData::AcquisitionError { .. }));
}
#[test]
fn decode_batch_successful_acquisition() {
let header = WaveformHeader {
wfmtype: 1,
sourcewidth: 1,
noofsamples: 2,
dataid: 42,
..base_header()
};
assert!(is_header_valid(&header));
let mut result = BatchDownloadResult {
results: vec![SymbolDownloadResult::Success {
symbol: SmolStr::from("ch1"),
header,
data_chunks: vec![vec![1u8, 2]],
}],
..Default::default()
};
let acquisition = decode_batch(&mut result).expect("expected acquisition");
assert_eq!(acquisition.data.len(), 1);
let ch1 = acquisition
.data
.iter()
.find(|cd| cd.symbol() == "ch1")
.unwrap();
match ch1 {
ChannelData::Waveform {
acq_id,
waveform: Waveform::Analog(_),
..
} => assert_eq!(*acq_id, 42),
_ => panic!("expected analog waveform for ch1"),
}
}
#[test]
fn decode_batch_propagates_timing() {
let header = WaveformHeader {
wfmtype: 1,
sourcewidth: 1,
noofsamples: 2,
dataid: 9,
..base_header()
};
assert!(is_header_valid(&header));
let mut result = BatchDownloadResult {
results: vec![SymbolDownloadResult::Success {
symbol: SmolStr::from("ch1"),
header,
data_chunks: vec![vec![1u8, 2]],
}],
wait_time: std::time::Duration::from_millis(12),
download_time: std::time::Duration::from_millis(34),
};
let acquisition = decode_batch(&mut result).expect("expected acquisition");
assert_eq!(acquisition.wait_time, std::time::Duration::from_millis(12));
assert_eq!(
acquisition.download_time,
std::time::Duration::from_millis(34)
);
}
#[test]
fn decode_batch_records_decode_error() {
let header = WaveformHeader {
wfmtype: 1,
sourcewidth: 3,
noofsamples: 1,
dataid: 3,
..base_header()
};
assert!(is_header_valid(&header));
let mut result = BatchDownloadResult {
results: vec![SymbolDownloadResult::Success {
symbol: SmolStr::from("ch1"),
header,
data_chunks: vec![vec![1u8]],
}],
..Default::default()
};
let acquisition = decode_batch(&mut result).expect("expected acquisition");
assert_eq!(acquisition.data.len(), 1);
assert!(matches!(
acquisition.data[0],
ChannelData::DecodeError { .. }
));
}
}