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}