mdf4_rs/channel.rs
1use crate::{
2 Result,
3 blocks::{ChannelBlock, read_string_block},
4 parsing::{
5 RawChannel, RawChannelGroup, RawDataGroup, SourceInfo,
6 decoder::{DecodedValue, decode_channel_value_with_validity},
7 },
8};
9
10/// High level handle for a single channel within a group.
11///
12/// It holds references to the raw blocks and allows convenient access to
13/// metadata and decoded values.
14pub struct Channel<'a> {
15 block: &'a ChannelBlock,
16 raw_data_group: &'a RawDataGroup,
17 raw_channel_group: &'a RawChannelGroup,
18 raw_channel: &'a RawChannel,
19 mmap: &'a [u8],
20}
21
22impl<'a> Channel<'a> {
23 /// Construct a new [`Channel`] from raw block references.
24 ///
25 /// # Arguments
26 /// * `block` - Channel block containing metadata
27 /// * `raw_data_group` - Parent data group
28 /// * `raw_channel_group` - Parent channel group
29 /// * `raw_channel` - Raw channel helper used to iterate samples
30 /// * `mmap` - Memory mapped file backing all data
31 ///
32 /// # Returns
33 /// A [`Channel`] handle with no samples decoded yet.
34 pub fn new(
35 block: &'a ChannelBlock,
36 raw_data_group: &'a RawDataGroup,
37 raw_channel_group: &'a RawChannelGroup,
38 raw_channel: &'a RawChannel,
39 mmap: &'a [u8],
40 ) -> Self {
41 Channel {
42 block,
43 raw_data_group,
44 raw_channel_group,
45 raw_channel,
46 mmap,
47 }
48 }
49 /// Retrieve the channel name if present.
50 pub fn name(&self) -> Result<Option<String>> {
51 read_string_block(self.mmap, self.block.name_addr)
52 }
53
54 /// Retrieve the physical unit description.
55 pub fn unit(&self) -> Result<Option<String>> {
56 read_string_block(self.mmap, self.block.unit_addr)
57 }
58
59 /// Retrieve the channel comment if present.
60 pub fn comment(&self) -> Result<Option<String>> {
61 read_string_block(self.mmap, self.block.comment_addr)
62 }
63
64 /// Get the acquisition source for this channel if available.
65 pub fn source(&self) -> Result<Option<SourceInfo>> {
66 let addr = self.block.source_addr;
67 SourceInfo::from_mmap(self.mmap, addr)
68 }
69
70 /// Decode and convert all samples of this channel.
71 ///
72 /// This method decodes all channel values and applies conversions.
73 /// Invalid samples (as indicated by invalidation bits) are returned as `None`.
74 ///
75 /// # Returns
76 /// A vector with one `Option<DecodedValue>` per record:
77 /// - `Some(value)` for valid samples
78 /// - `None` for invalid samples (invalidation bit set or decoding failed)
79 pub fn values(&self) -> Result<Vec<Option<DecodedValue>>> {
80 let record_id_size = self.raw_data_group.block.record_id_size as usize;
81 let cg_data_bytes = self.raw_channel_group.block.record_size;
82 let mut out = Vec::new();
83
84 let records_iter =
85 self.raw_channel
86 .records(self.raw_data_group, self.raw_channel_group, self.mmap)?;
87
88 for rec_res in records_iter {
89 let rec = rec_res?;
90
91 // Decode with validity checking
92 if let Some(decoded) =
93 decode_channel_value_with_validity(rec, record_id_size, cg_data_bytes, self.block)
94 {
95 if decoded.is_valid {
96 // Value is valid, apply conversion
97 let phys = self
98 .block
99 .apply_conversion_value(decoded.value, self.mmap)?;
100 out.push(Some(phys));
101 } else {
102 // Value is invalid according to invalidation bit
103 out.push(None);
104 }
105 } else {
106 // Decoding failed
107 out.push(None);
108 }
109 }
110 Ok(out)
111 }
112
113 /// Get the channel block (for internal use)
114 pub fn block(&self) -> &ChannelBlock {
115 self.block
116 }
117
118 /// Return a streaming iterator over decoded channel values.
119 ///
120 /// Unlike [`values()`](Self::values), this method does not load all values into memory.
121 /// Instead, it decodes each value on-demand, making it suitable for large files.
122 ///
123 /// # Returns
124 /// An iterator that yields `Result<Option<DecodedValue>>` for each record:
125 /// - `Ok(Some(value))` for valid samples
126 /// - `Ok(None)` for invalid samples (invalidation bit set or decoding failed)
127 /// - `Err(...)` if there was an error reading or decoding the record
128 ///
129 /// # Example
130 /// ```ignore
131 /// for value_result in channel.iter_values()? {
132 /// match value_result {
133 /// Ok(Some(value)) => println!("Value: {:?}", value),
134 /// Ok(None) => println!("Invalid sample"),
135 /// Err(e) => eprintln!("Error: {:?}", e),
136 /// }
137 /// }
138 /// ```
139 pub fn iter_values(&self) -> Result<ChannelValuesIter<'a>> {
140 let record_id_size = self.raw_data_group.block.record_id_size as usize;
141 let cg_data_bytes = self.raw_channel_group.block.record_size;
142
143 let records_iter =
144 self.raw_channel
145 .records(self.raw_data_group, self.raw_channel_group, self.mmap)?;
146
147 Ok(ChannelValuesIter {
148 records_iter,
149 block: self.block,
150 mmap: self.mmap,
151 record_id_size,
152 cg_data_bytes,
153 })
154 }
155}
156
157/// Streaming iterator over decoded channel values.
158///
159/// Created by [`Channel::iter_values()`]. This iterator decodes values on-demand
160/// without loading all data into memory, making it suitable for large MDF4 files.
161pub struct ChannelValuesIter<'a> {
162 records_iter: Box<dyn Iterator<Item = Result<&'a [u8]>> + 'a>,
163 block: &'a ChannelBlock,
164 mmap: &'a [u8],
165 record_id_size: usize,
166 cg_data_bytes: u32,
167}
168
169impl<'a> Iterator for ChannelValuesIter<'a> {
170 type Item = Result<Option<DecodedValue>>;
171
172 fn next(&mut self) -> Option<Self::Item> {
173 let rec_result = self.records_iter.next()?;
174
175 Some(match rec_result {
176 Ok(rec) => {
177 // Decode with validity checking
178 if let Some(decoded) = decode_channel_value_with_validity(
179 rec,
180 self.record_id_size,
181 self.cg_data_bytes,
182 self.block,
183 ) {
184 if decoded.is_valid {
185 // Value is valid, apply conversion
186 match self.block.apply_conversion_value(decoded.value, self.mmap) {
187 Ok(phys) => Ok(Some(phys)),
188 Err(e) => Err(e),
189 }
190 } else {
191 // Value is invalid according to invalidation bit
192 Ok(None)
193 }
194 } else {
195 // Decoding failed
196 Ok(None)
197 }
198 }
199 Err(e) => Err(e),
200 })
201 }
202}