mdf4_rs/blocks/
channel_block.rs

1use super::CN_BLOCK_SIZE;
2use crate::{
3    Result,
4    blocks::{
5        common::{
6            BlockHeader, BlockParse, DataType, debug_assert_aligned, read_f64, read_u8, read_u16,
7            read_u32, read_u64, validate_block_id, validate_block_length, validate_buffer_size,
8        },
9        conversion::ConversionBlock,
10        text_block::TextBlock,
11    },
12    types::DecodedValue,
13};
14use alloc::string::String;
15use alloc::vec::Vec;
16
17#[derive(Debug, Clone)]
18pub struct ChannelBlock {
19    pub header: BlockHeader,
20    pub next_ch_addr: u64,
21    pub component_addr: u64,
22    pub name_addr: u64,
23    pub source_addr: u64,
24    pub conversion_addr: u64,
25    pub data_addr: u64,
26    pub unit_addr: u64,
27    pub comment_addr: u64,
28    pub channel_type: u8,
29    pub sync_type: u8,
30    pub data_type: DataType,
31    pub bit_offset: u8,
32    pub byte_offset: u32,
33    pub bit_count: u32,
34    pub flags: u32,
35    pub pos_invalidation_bit: u32,
36    pub precision: u8,
37    pub reserved1: u8,
38    pub attachment_count: u16,
39    pub min_raw_value: f64,
40    pub max_raw_value: f64,
41    pub lower_limit: f64,
42    pub upper_limit: f64,
43    pub lower_ext_limit: f64,
44    pub upper_ext_limit: f64,
45    pub name: Option<String>,
46    pub conversion: Option<ConversionBlock>,
47}
48
49impl BlockParse<'_> for ChannelBlock {
50    const ID: &'static str = "##CN";
51
52    fn from_bytes(bytes: &[u8]) -> Result<Self> {
53        let header = Self::parse_header(bytes)?;
54        validate_buffer_size(bytes, CN_BLOCK_SIZE)?;
55
56        Ok(Self {
57            header,
58            // Links section (8 x u64 = 64 bytes at offset 24)
59            next_ch_addr: read_u64(bytes, 24),
60            component_addr: read_u64(bytes, 32),
61            name_addr: read_u64(bytes, 40),
62            source_addr: read_u64(bytes, 48),
63            conversion_addr: read_u64(bytes, 56),
64            data_addr: read_u64(bytes, 64),
65            unit_addr: read_u64(bytes, 72),
66            comment_addr: read_u64(bytes, 80),
67            // Format section at offset 88
68            channel_type: read_u8(bytes, 88),
69            sync_type: read_u8(bytes, 89),
70            data_type: DataType::from_u8(read_u8(bytes, 90)),
71            bit_offset: read_u8(bytes, 91),
72            byte_offset: read_u32(bytes, 92),
73            bit_count: read_u32(bytes, 96),
74            flags: read_u32(bytes, 100),
75            pos_invalidation_bit: read_u32(bytes, 104),
76            precision: read_u8(bytes, 108),
77            reserved1: read_u8(bytes, 109),
78            attachment_count: read_u16(bytes, 110),
79            // Range section (6 x f64 = 48 bytes at offset 112)
80            min_raw_value: read_f64(bytes, 112),
81            max_raw_value: read_f64(bytes, 120),
82            lower_limit: read_f64(bytes, 128),
83            upper_limit: read_f64(bytes, 136),
84            lower_ext_limit: read_f64(bytes, 144),
85            upper_ext_limit: read_f64(bytes, 152),
86            // Resolved fields
87            name: None,
88            conversion: None,
89        })
90    }
91}
92
93impl ChannelBlock {
94    /// Serializes the ChannelBlock to bytes according to MDF 4.1 specification.
95    pub fn to_bytes(&self) -> Result<Vec<u8>> {
96        validate_block_id(&self.header, "##CN")?;
97        validate_block_length(&self.header, CN_BLOCK_SIZE as u64)?;
98
99        let mut buffer = Vec::with_capacity(CN_BLOCK_SIZE);
100
101        // Header (24 bytes)
102        buffer.extend_from_slice(&self.header.to_bytes()?);
103
104        // Links (64 bytes)
105        buffer.extend_from_slice(&self.next_ch_addr.to_le_bytes());
106        buffer.extend_from_slice(&self.component_addr.to_le_bytes());
107        buffer.extend_from_slice(&self.name_addr.to_le_bytes());
108        buffer.extend_from_slice(&self.source_addr.to_le_bytes());
109        buffer.extend_from_slice(&self.conversion_addr.to_le_bytes());
110        buffer.extend_from_slice(&self.data_addr.to_le_bytes());
111        buffer.extend_from_slice(&self.unit_addr.to_le_bytes());
112        buffer.extend_from_slice(&self.comment_addr.to_le_bytes());
113
114        // Format section (24 bytes)
115        buffer.push(self.channel_type);
116        buffer.push(self.sync_type);
117        buffer.push(self.data_type.to_u8());
118        buffer.push(self.bit_offset);
119        buffer.extend_from_slice(&self.byte_offset.to_le_bytes());
120        buffer.extend_from_slice(&self.bit_count.to_le_bytes());
121        buffer.extend_from_slice(&self.flags.to_le_bytes());
122        buffer.extend_from_slice(&self.pos_invalidation_bit.to_le_bytes());
123        buffer.push(self.precision);
124        buffer.push(self.reserved1);
125        buffer.extend_from_slice(&self.attachment_count.to_le_bytes());
126
127        // Range section (48 bytes)
128        buffer.extend_from_slice(&self.min_raw_value.to_le_bytes());
129        buffer.extend_from_slice(&self.max_raw_value.to_le_bytes());
130        buffer.extend_from_slice(&self.lower_limit.to_le_bytes());
131        buffer.extend_from_slice(&self.upper_limit.to_le_bytes());
132        buffer.extend_from_slice(&self.lower_ext_limit.to_le_bytes());
133        buffer.extend_from_slice(&self.upper_ext_limit.to_le_bytes());
134
135        debug_assert_aligned(buffer.len());
136        Ok(buffer)
137    }
138
139    /// Load the channel name from the file using the stored `name_addr`.
140    pub fn resolve_name(&mut self, file_data: &[u8]) -> Result<()> {
141        if self.name.is_none() && self.name_addr != 0 {
142            let offset = self.name_addr as usize;
143            if offset + 24 <= file_data.len() {
144                let text_block = TextBlock::from_bytes(&file_data[offset..])?;
145                self.name = Some(text_block.text);
146            }
147        }
148        Ok(())
149    }
150
151    /// Resolve and store the conversion block pointed to by `conversion_addr`.
152    pub fn resolve_conversion(&mut self, bytes: &[u8]) -> Result<()> {
153        if self.conversion.is_none() && self.conversion_addr != 0 {
154            let offset = self.conversion_addr as usize;
155            validate_buffer_size(bytes, offset + 24)?;
156
157            let mut conv_block = ConversionBlock::from_bytes(&bytes[offset..])?;
158            let _ = conv_block.resolve_formula(bytes);
159            self.conversion = Some(conv_block);
160        }
161        Ok(())
162    }
163
164    /// Apply the stored conversion to a decoded value.
165    pub fn apply_conversion_value(
166        &self,
167        raw: DecodedValue,
168        file_data: &[u8],
169    ) -> Result<DecodedValue> {
170        if let Some(conv) = &self.conversion {
171            conv.apply_decoded(raw, file_data)
172        } else {
173            Ok(raw)
174        }
175    }
176}
177
178impl Default for ChannelBlock {
179    fn default() -> Self {
180        Self {
181            header: BlockHeader {
182                id: String::from("##CN"),
183                reserved: 0,
184                length: CN_BLOCK_SIZE as u64,
185                link_count: 8,
186            },
187            next_ch_addr: 0,
188            component_addr: 0,
189            name_addr: 0,
190            source_addr: 0,
191            conversion_addr: 0,
192            data_addr: 0,
193            unit_addr: 0,
194            comment_addr: 0,
195            channel_type: 0,
196            sync_type: 0,
197            data_type: DataType::UnsignedIntegerLE,
198            bit_offset: 0,
199            byte_offset: 0,
200            bit_count: 0,
201            flags: 0,
202            pos_invalidation_bit: 0,
203            precision: 0,
204            reserved1: 0,
205            attachment_count: 0,
206            min_raw_value: 0.0,
207            max_raw_value: 0.0,
208            lower_limit: 0.0,
209            upper_limit: 0.0,
210            lower_ext_limit: 0.0,
211            upper_ext_limit: 0.0,
212            name: None,
213            conversion: None,
214        }
215    }
216}