1use crate::error::{Result, TdmsError};
2use crate::io::ext::TdmsWriteExt;
3use crate::model::datatypes::{DataType, PropertyValue};
4use indexmap::IndexMap;
5use std::collections::BTreeMap;
6use std::fs::File;
7use std::io::{BufWriter, Seek, Write};
8use std::marker::PhantomData;
9use std::path::{Path, PathBuf};
10
11pub struct TdmsWriter {
15 path: PathBuf,
16 groups: IndexMap<String, WriterGroupData>,
17 properties: IndexMap<String, PropertyValue>,
18 closed: bool,
19}
20
21struct WriterGroupData {
22 name: String,
23 channels: BTreeMap<String, WriterChannelData>,
24 properties: IndexMap<String, PropertyValue>,
25}
26
27struct WriterChannelData {
28 name: String,
29 data_type: DataType,
30 data: Vec<u8>,
31 properties: IndexMap<String, PropertyValue>,
32}
33
34pub struct WriterGroup<'w> {
35 pub(crate) writer: &'w mut TdmsWriter,
36 pub(crate) group_name: String,
37}
38
39pub struct WriterChannel<'w, T> {
41 pub(crate) writer: &'w mut TdmsWriter,
42 pub(crate) group_name: String,
43 pub(crate) channel_name: String,
44 pub(crate) _phantom: PhantomData<T>,
45}
46
47impl TdmsWriter {
48 pub fn create(path: impl AsRef<Path>) -> Result<Self> {
50 Ok(Self {
51 path: path.as_ref().to_path_buf(),
52 groups: IndexMap::new(),
53 properties: IndexMap::new(),
54 closed: false,
55 })
56 }
57
58 pub fn add_group(&mut self, name: impl Into<String>) -> Result<WriterGroup<'_>> {
60 let name = name.into();
61 if name.is_empty() {
62 return Err(TdmsError::InvalidName("Group name cannot be empty".into()));
63 }
64
65 if !self.groups.contains_key(&name) {
66 self.groups.insert(
67 name.clone(),
68 WriterGroupData {
69 name: name.clone(),
70 channels: BTreeMap::new(),
71 properties: IndexMap::new(),
72 },
73 );
74 }
75
76 Ok(WriterGroup {
77 writer: self,
78 group_name: name,
79 })
80 }
81
82 pub fn add_property(
84 &mut self,
85 name: impl Into<String>,
86 value: PropertyValue,
87 ) -> Result<&mut Self> {
88 let name = name.into();
89 if name.is_empty() {
90 return Err(TdmsError::InvalidName(
91 "Property name cannot be empty".into(),
92 ));
93 }
94 self.properties.insert(name, value);
95 Ok(self)
96 }
97
98 pub fn close(mut self) -> Result<()> {
100 if self.closed {
101 return Err(TdmsError::WriterClosed);
102 }
103 self.write_file()?;
104 self.closed = true;
105 Ok(())
106 }
107
108 pub fn abort(self) -> Result<()> {
110 if self.path.exists() {
111 std::fs::remove_file(&self.path)?;
112 }
113 Ok(())
114 }
115
116 fn write_file(&mut self) -> Result<()> {
117 let file = File::create(&self.path)?;
118 let mut writer = BufWriter::new(file);
119
120 writer.write_all(b"TDSm")?;
121 let toc = 0x0E;
122 writer.write_u32(toc)?;
123 writer.write_u32(4712)?;
124
125 let segment_offset_pos = writer.stream_position()?;
126 writer.write_u64(0xFFFFFFFFFFFFFFFF)?;
127 writer.write_u64(0)?;
128
129 let mut object_count = 0;
130 if !self.properties.is_empty() {
131 object_count += 1;
132 }
133 for group in self.groups.values() {
134 object_count += 1;
135 object_count += group.channels.len() as u32;
136 }
137
138 writer.write_u32(object_count)?;
139
140 if !self.properties.is_empty() {
141 self.write_object_internal(&mut writer, "/", &self.properties, None)?;
142 }
143
144 for group in self.groups.values() {
145 let group_path = format!("/'{}'", group.name);
146 self.write_object_internal(&mut writer, &group_path, &group.properties, None)?;
147
148 for channel in group.channels.values() {
149 let channel_path = format!("/'{}'/'{}'", group.name, channel.name);
150 self.write_object_internal(
151 &mut writer,
152 &channel_path,
153 &channel.properties,
154 Some((&channel.data_type, channel.data.len())),
155 )?;
156 }
157 }
158
159 let raw_data_offset = writer.stream_position()?;
160 for group in self.groups.values() {
161 for channel in group.channels.values() {
162 writer.write_all(&channel.data)?;
163 }
164 }
165
166 let end_pos = writer.stream_position()?;
167 writer.seek(std::io::SeekFrom::Start(segment_offset_pos + 8))?;
168 writer.write_u64(raw_data_offset - 28)?;
169 writer.seek(std::io::SeekFrom::Start(end_pos))?;
170
171 writer.flush()?;
172 Ok(())
173 }
174
175 fn write_object_internal(
176 &self,
177 writer: &mut BufWriter<File>,
178 path: &str,
179 properties: &IndexMap<String, PropertyValue>,
180 raw_data: Option<(&DataType, usize)>,
181 ) -> Result<()> {
182 writer.write_u32(path.len() as u32)?;
183 writer.write_all(path.as_bytes())?;
184
185 let raw_data_index = if raw_data.is_some() {
186 20_u32
187 } else {
188 0xFFFFFFFF_u32
189 };
190 writer.write_u32(raw_data_index)?;
191
192 if let Some((dtype, byte_len)) = raw_data {
193 writer.write_u32(dtype.to_u32())?;
194 writer.write_u32(1)?;
195 let count = byte_len / dtype.itemsize();
196 writer.write_u64(count as u64)?;
197 }
198
199 writer.write_u32(properties.len() as u32)?;
200 for (key, value) in properties {
201 writer.write_u32(key.len() as u32)?;
202 writer.write_all(key.as_bytes())?;
203 self.write_property_value_internal(writer, value)?;
204 }
205 Ok(())
206 }
207
208 fn write_property_value_internal(
209 &self,
210 writer: &mut BufWriter<File>,
211 value: &PropertyValue,
212 ) -> Result<()> {
213 match value {
214 PropertyValue::I8(v) => {
215 writer.write_u32(DataType::I8.to_u32())?;
216 writer.write_i8(*v)?;
217 }
218 PropertyValue::I16(v) => {
219 writer.write_u32(DataType::I16.to_u32())?;
220 writer.write_i16(*v)?;
221 }
222 PropertyValue::I32(v) => {
223 writer.write_u32(DataType::I32.to_u32())?;
224 writer.write_i32(*v)?;
225 }
226 PropertyValue::I64(v) => {
227 writer.write_u32(DataType::I64.to_u32())?;
228 writer.write_i64(*v)?;
229 }
230 PropertyValue::U8(v) => {
231 writer.write_u32(DataType::U8.to_u32())?;
232 writer.write_u8(*v)?;
233 }
234 PropertyValue::U16(v) => {
235 writer.write_u32(DataType::U16.to_u32())?;
236 writer.write_u16(*v)?;
237 }
238 PropertyValue::U32(v) => {
239 writer.write_u32(DataType::U32.to_u32())?;
240 writer.write_u32(*v)?;
241 }
242 PropertyValue::U64(v) => {
243 writer.write_u32(DataType::U64.to_u32())?;
244 writer.write_u64(*v)?;
245 }
246 PropertyValue::Float(v) => {
247 writer.write_u32(DataType::Float.to_u32())?;
248 writer.write_f32(*v)?;
249 }
250 PropertyValue::Double(v) => {
251 writer.write_u32(DataType::Double.to_u32())?;
252 writer.write_f64(*v)?;
253 }
254 PropertyValue::String(s) => {
255 writer.write_u32(DataType::String.to_u32())?;
256 writer.write_u32(s.len() as u32)?;
257 writer.write_all(s.as_bytes())?;
258 }
259 PropertyValue::Boolean(b) => {
260 writer.write_u32(DataType::Boolean.to_u32())?;
261 writer.write_u8(if *b { 1 } else { 0 })?;
262 }
263 PropertyValue::TimeStamp((secs, frac)) => {
264 writer.write_u32(DataType::TimeStamp.to_u32())?;
265 writer.write_u64(*frac)?;
266 writer.write_i64(*secs)?;
267 }
268 }
269 Ok(())
270 }
271}
272
273impl<'w> WriterGroup<'w> {
274 pub fn add_channel<T: WritableType>(
276 &mut self,
277 name: impl Into<String>,
278 ) -> Result<WriterChannel<'_, T>> {
279 let name = name.into();
280 if name.is_empty() {
281 return Err(TdmsError::InvalidName(
282 "Channel name cannot be empty".into(),
283 ));
284 }
285
286 let group = self
287 .writer
288 .groups
289 .get_mut(&self.group_name)
290 .ok_or_else(|| TdmsError::GroupNotFound(self.group_name.clone()))?;
291 if !group.channels.contains_key(&name) {
292 group.channels.insert(
293 name.clone(),
294 WriterChannelData {
295 name: name.clone(),
296 data_type: T::data_type(),
297 data: Vec::new(),
298 properties: IndexMap::new(),
299 },
300 );
301 }
302
303 Ok(WriterChannel {
304 writer: self.writer,
305 group_name: self.group_name.clone(),
306 channel_name: name,
307 _phantom: PhantomData,
308 })
309 }
310
311 pub fn add_property(
313 &mut self,
314 name: impl Into<String>,
315 value: PropertyValue,
316 ) -> Result<&mut Self> {
317 let name = name.into();
318 if name.is_empty() {
319 return Err(TdmsError::InvalidName(
320 "Property name cannot be empty".into(),
321 ));
322 }
323 let group = self
324 .writer
325 .groups
326 .get_mut(&self.group_name)
327 .ok_or_else(|| TdmsError::GroupNotFound(self.group_name.clone()))?;
328 group.properties.insert(name, value);
329 Ok(self)
330 }
331}
332
333impl<'w, T: WritableType> WriterChannel<'w, T> {
334 pub fn write(&mut self, data: &[T]) -> Result<()> {
336 let group = self
337 .writer
338 .groups
339 .get_mut(&self.group_name)
340 .ok_or_else(|| TdmsError::GroupNotFound(self.group_name.clone()))?;
341 let channel = group.channels.get_mut(&self.channel_name).ok_or_else(|| {
342 TdmsError::ChannelNotFound(self.channel_name.clone(), self.group_name.clone())
343 })?;
344 T::write_to_buffer(data, &mut channel.data)?;
345 Ok(())
346 }
347
348 pub fn add_property(
350 &mut self,
351 name: impl Into<String>,
352 value: PropertyValue,
353 ) -> Result<&mut Self> {
354 let name = name.into();
355 if name.is_empty() {
356 return Err(TdmsError::InvalidName(
357 "Property name cannot be empty".into(),
358 ));
359 }
360 let group = self
361 .writer
362 .groups
363 .get_mut(&self.group_name)
364 .ok_or_else(|| TdmsError::GroupNotFound(self.group_name.clone()))?;
365 let channel = group.channels.get_mut(&self.channel_name).ok_or_else(|| {
366 TdmsError::ChannelNotFound(self.channel_name.clone(), self.group_name.clone())
367 })?;
368 channel.properties.insert(name, value);
369 Ok(self)
370 }
371}
372
373pub trait WritableType: Sized {
375 fn data_type() -> DataType;
376 fn write_to_buffer(data: &[Self], buffer: &mut Vec<u8>) -> Result<()>;
377}
378
379impl WritableType for f64 {
380 fn data_type() -> DataType {
381 DataType::Double
382 }
383 fn write_to_buffer(data: &[Self], buffer: &mut Vec<u8>) -> Result<()> {
384 for &v in data {
385 buffer.write_f64(v)?;
386 }
387 Ok(())
388 }
389}
390
391impl WritableType for f32 {
392 fn data_type() -> DataType {
393 DataType::Float
394 }
395 fn write_to_buffer(data: &[Self], buffer: &mut Vec<u8>) -> Result<()> {
396 for &v in data {
397 buffer.write_f32(v)?;
398 }
399 Ok(())
400 }
401}
402
403impl WritableType for i8 {
404 fn data_type() -> DataType {
405 DataType::I8
406 }
407 fn write_to_buffer(data: &[Self], buffer: &mut Vec<u8>) -> Result<()> {
408 for &v in data {
409 buffer.write_i8(v)?;
410 }
411 Ok(())
412 }
413}
414
415impl WritableType for i16 {
416 fn data_type() -> DataType {
417 DataType::I16
418 }
419 fn write_to_buffer(data: &[Self], buffer: &mut Vec<u8>) -> Result<()> {
420 for &v in data {
421 buffer.write_i16(v)?;
422 }
423 Ok(())
424 }
425}
426
427impl WritableType for i32 {
428 fn data_type() -> DataType {
429 DataType::I32
430 }
431 fn write_to_buffer(data: &[Self], buffer: &mut Vec<u8>) -> Result<()> {
432 for &v in data {
433 buffer.write_i32(v)?;
434 }
435 Ok(())
436 }
437}
438
439impl WritableType for i64 {
440 fn data_type() -> DataType {
441 DataType::I64
442 }
443 fn write_to_buffer(data: &[Self], buffer: &mut Vec<u8>) -> Result<()> {
444 for &v in data {
445 buffer.write_i64(v)?;
446 }
447 Ok(())
448 }
449}
450
451impl WritableType for u8 {
452 fn data_type() -> DataType {
453 DataType::U8
454 }
455 fn write_to_buffer(data: &[Self], buffer: &mut Vec<u8>) -> Result<()> {
456 buffer.extend_from_slice(data);
457 Ok(())
458 }
459}
460
461impl WritableType for u16 {
462 fn data_type() -> DataType {
463 DataType::U16
464 }
465 fn write_to_buffer(data: &[Self], buffer: &mut Vec<u8>) -> Result<()> {
466 for &v in data {
467 buffer.write_u16(v)?;
468 }
469 Ok(())
470 }
471}
472
473impl WritableType for u32 {
474 fn data_type() -> DataType {
475 DataType::U32
476 }
477 fn write_to_buffer(data: &[Self], buffer: &mut Vec<u8>) -> Result<()> {
478 for &v in data {
479 buffer.write_u32(v)?;
480 }
481 Ok(())
482 }
483}
484
485impl WritableType for u64 {
486 fn data_type() -> DataType {
487 DataType::U64
488 }
489 fn write_to_buffer(data: &[Self], buffer: &mut Vec<u8>) -> Result<()> {
490 for &v in data {
491 buffer.write_u64(v)?;
492 }
493 Ok(())
494 }
495}
496
497impl WritableType for bool {
498 fn data_type() -> DataType {
499 DataType::Boolean
500 }
501 fn write_to_buffer(data: &[Self], buffer: &mut Vec<u8>) -> Result<()> {
502 for &v in data {
503 buffer.write_u8(if v { 1 } else { 0 })?;
504 }
505 Ok(())
506 }
507}