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