1use std::ffi::c_double;
2use std::fmt;
3use std::ptr;
4use std::slice::from_raw_parts;
5
6use crate::{
7 error::check, raw, util, MSControlFlags, MSError, MSRecord, MSResult, MSSampleType,
8 MSSubSeconds, MSTimeFormat,
9};
10use time::OffsetDateTime;
11
12use raw::{MS3TraceID, MS3TraceList, MS3TraceSeg};
13
14#[derive(Debug)]
16pub struct MSTraceId(*mut MS3TraceID);
17
18impl MSTraceId {
19 fn ptr(&self) -> MS3TraceID {
20 unsafe { *self.0 }
21 }
22
23 #[allow(dead_code)]
24 pub(crate) fn get_raw(&self) -> *const MS3TraceID {
25 self.0
26 }
27
28 #[allow(dead_code)]
29 pub(crate) unsafe fn get_raw_mut(&mut self) -> *mut MS3TraceID {
30 self.0
31 }
32
33 pub fn sid(&self) -> MSResult<String> {
35 let nslc = util::NetStaLocCha::from_sid(&self.ptr().sid)?;
36 Ok(nslc.to_string())
37 }
38
39 pub fn pub_version(&self) -> u8 {
41 self.ptr().pubversion
42 }
43
44 pub fn start_time(&self) -> MSResult<OffsetDateTime> {
46 util::nstime_to_time(self.ptr().earliest)
47 }
48
49 pub fn end_time(&self) -> MSResult<OffsetDateTime> {
51 util::nstime_to_time(self.ptr().latest)
52 }
53
54 pub fn len(&self) -> u32 {
56 self.ptr().numsegments
57 }
58
59 pub fn is_empty(&self) -> bool {
61 self.len() == 0
62 }
63
64 pub fn iter(&self) -> MSTraceSegmentIter {
66 MSTraceSegmentIter {
67 trace_id: self,
68
69 next: self.ptr().first,
70 prev: ptr::null_mut(),
71 }
72 }
73}
74
75#[derive(Debug)]
77pub struct MSTraceIdIter {
78 next: *mut MS3TraceID,
79}
80
81impl Iterator for MSTraceIdIter {
82 type Item = MSTraceId;
83 fn next(&mut self) -> Option<Self::Item> {
84 if self.next.is_null() {
85 return None;
86 }
87
88 let rv = Some(MSTraceId(self.next));
89 self.next = unsafe { (*self.next).next[0] };
90 rv
91 }
92}
93
94#[derive(Debug)]
96pub struct MSTraceSegment<'id> {
97 _trace_id: &'id MSTraceId,
98
99 inner: *mut MS3TraceSeg,
100}
101
102impl<'id> MSTraceSegment<'id> {
103 fn ptr(&self) -> MS3TraceSeg {
104 unsafe { *self.inner }
105 }
106
107 pub fn start_time(&self) -> MSResult<OffsetDateTime> {
109 util::nstime_to_time(self.ptr().starttime)
110 }
111
112 pub fn end_time(&self) -> MSResult<OffsetDateTime> {
114 util::nstime_to_time(self.ptr().endtime)
115 }
116
117 pub fn sample_rate_hz(&self) -> c_double {
119 self.ptr().samprate
120 }
121
122 pub fn sample_cnt(&self) -> i64 {
124 self.ptr().samplecnt as _
125 }
126
127 pub fn data_samples<T: DataSampleType>(&mut self) -> MSResult<&[T]> {
132 if !self.is_data_unpacked() {
133 return Err(MSError::from_str("data samples must be unpacked"));
134 }
135
136 let rv = unsafe {
137 <T as DataSampleType>::convert_into(self.inner, false)?;
138 from_raw_parts(
139 self.ptr().datasamples as *mut T,
140 self.ptr().samplecnt as usize,
141 )
142 };
143
144 Ok(rv)
145 }
146
147 pub fn data_size(&self) -> u64 {
149 self.ptr().datasize
150 }
151
152 pub fn num_samples(&self) -> i64 {
154 self.ptr().numsamples as _
155 }
156
157 pub fn sample_type(&self) -> MSSampleType {
159 MSSampleType::from_char(self.ptr().sampletype as _)
160 }
161
162 pub fn is_data_unpacked(&self) -> bool {
164 self.sample_cnt() == self.num_samples()
165 && self.data_size() > 0
166 && !self.ptr().datasamples.is_null()
167 }
168
169 }
190
191pub trait DataSampleType {
192 unsafe fn convert_into(seg: *mut MS3TraceSeg, truncate: bool) -> MSResult<()>;
198}
199
200impl DataSampleType for u8 {
201 unsafe fn convert_into(_seg: *mut MS3TraceSeg, _truncate: bool) -> MSResult<()> {
202 Ok(())
203 }
204}
205
206impl DataSampleType for i32 {
207 unsafe fn convert_into(seg: *mut MS3TraceSeg, truncate: bool) -> MSResult<()> {
208 let rv = unsafe {
209 check(raw::mstl3_convertsamples(
210 seg,
211 MSSampleType::Integer32 as _,
212 truncate as _,
213 ))
214 };
215
216 match rv {
217 Ok(_) => Ok(()),
218 Err(e) => Err(e),
219 }
220 }
221}
222
223impl DataSampleType for f32 {
224 unsafe fn convert_into(seg: *mut MS3TraceSeg, truncate: bool) -> MSResult<()> {
225 let rv = unsafe {
226 check(raw::mstl3_convertsamples(
227 seg,
228 MSSampleType::Float32 as _,
229 truncate as _,
230 ))
231 };
232
233 match rv {
234 Ok(_) => Ok(()),
235 Err(e) => Err(e),
236 }
237 }
238}
239
240impl DataSampleType for f64 {
241 unsafe fn convert_into(seg: *mut MS3TraceSeg, truncate: bool) -> MSResult<()> {
242 let rv = unsafe {
243 check(raw::mstl3_convertsamples(
244 seg,
245 MSSampleType::Float64 as _,
246 truncate as _,
247 ))
248 };
249
250 match rv {
251 Ok(_) => Ok(()),
252 Err(e) => Err(e),
253 }
254 }
255}
256
257#[derive(Debug)]
259pub struct MSTraceSegmentIter<'id> {
260 trace_id: &'id MSTraceId,
261
262 next: *mut MS3TraceSeg,
263 prev: *mut MS3TraceSeg,
264}
265
266impl<'id> Iterator for MSTraceSegmentIter<'id> {
267 type Item = MSTraceSegment<'id>;
268
269 fn next(&mut self) -> Option<Self::Item> {
270 if self.next.is_null() {
271 return None;
272 }
273
274 let rv = Some(MSTraceSegment {
275 _trace_id: self.trace_id,
276 inner: self.next,
277 });
278 self.prev = self.next;
279 self.next = unsafe { (*self.next).next };
280 rv
281 }
282}
283
284impl<'id> DoubleEndedIterator for MSTraceSegmentIter<'id> {
285 fn next_back(&mut self) -> Option<Self::Item> {
286 if self.prev.is_null() {
287 return None;
288 }
289
290 let rv = Some(MSTraceSegment {
291 _trace_id: self.trace_id,
292 inner: self.prev,
293 });
294 self.next = self.prev;
295 self.prev = unsafe { (*self.prev).prev };
296 rv
297 }
298}
299
300#[derive(Debug)]
354pub struct MSTraceList {
355 inner: *mut MS3TraceList,
356}
357
358impl MSTraceList {
359 fn ptr(&self) -> MS3TraceList {
360 unsafe { *self.inner }
361 }
362
363 #[allow(dead_code)]
364 pub(crate) fn get_raw(&self) -> *const MS3TraceList {
365 self.inner
366 }
367
368 #[allow(dead_code)]
369 pub(crate) unsafe fn get_raw_mut(&mut self) -> *mut MS3TraceList {
370 self.inner
371 }
372
373 pub fn new() -> MSResult<Self> {
375 let mstl: *mut MS3TraceList = ptr::null_mut();
376 let mstl = unsafe { raw::mstl3_init(mstl) };
377 if mstl.is_null() {
378 return Err(MSError::from_str("failed to initialize trace list"));
379 }
380
381 Ok(Self { inner: mstl })
382 }
383
384 pub fn from_buffer(buf: &[u8], flags: MSControlFlags) -> MSResult<Self> {
386 let mut rv = Self::new()?;
387
388 unsafe {
389 let buf = &*(buf as *const [_] as *const [_]);
390 check(raw::mstl3_readbuffer(
391 (&mut rv.get_raw_mut()) as *mut *mut _,
392 buf.as_ptr(),
393 buf.len() as _,
394 0,
395 flags.bits(),
396 ptr::null_mut(),
397 0,
398 ))
399 }?;
400
401 Ok(rv)
402 }
403
404 pub fn len(&self) -> u32 {
406 self.ptr().numtraceids
407 }
408
409 pub fn is_empty(&self) -> bool {
411 self.len() == 0
412 }
413
414 pub fn iter(&self) -> MSTraceIdIter {
416 MSTraceIdIter {
417 next: self.ptr().traces.next[0],
418 }
419 }
420
421 pub fn insert(&mut self, rec: MSRecord, autoheal: bool) -> MSResult<()> {
427 let rv = unsafe {
428 raw::mstl3_addmsr_recordptr(
429 self.inner,
430 rec.into_raw(),
431 ptr::null_mut(),
432 0,
433 autoheal as _,
434 MSControlFlags::empty().bits(),
435 ptr::null_mut(),
436 )
437 };
438
439 if rv.is_null() {
440 return Err(MSError::from_str("failed to insert record"));
441 }
442
443 Ok(())
444 }
445
446 pub fn display(
459 &self,
460 time_format: MSTimeFormat,
461 detail: i8,
462 gap: i8,
463 version: i8,
464 ) -> TraceListDisplay<'_> {
465 TraceListDisplay {
466 mstl: self,
467 time_format,
468 detail,
469 gap,
470 version,
471 }
472 }
473}
474
475impl Drop for MSTraceList {
476 fn drop(&mut self) {
477 unsafe { raw::mstl3_free((&mut self.inner) as *mut *mut MS3TraceList, 1) };
478 }
479}
480
481pub struct TraceListDisplay<'a> {
483 mstl: &'a MSTraceList,
484 time_format: MSTimeFormat,
485 detail: i8,
486 gap: i8,
487 version: i8,
488}
489
490impl fmt::Debug for TraceListDisplay<'_> {
491 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
492 fmt::Debug::fmt(&self.mstl, f)
493 }
494}
495
496impl fmt::Display for TraceListDisplay<'_> {
497 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
498 if self.detail > 0 && self.gap > 0 {
500 writeln!(f, " SourceID Start sample End sample Gap Hz Samples")?;
501 } else if self.detail <= 0 && self.gap > 0 {
502 writeln!(f, " SourceID Start sample End sample Gap")?;
503 } else if self.detail > 0 && self.gap <= 0 {
504 writeln!(f, " SourceID Start sample End sample Hz Samples")?;
505 } else {
506 writeln!(
507 f,
508 " SourceID Start sample End sample"
509 )?;
510 }
511
512 let mut tid_cnt = 0;
513 let mut tseg_cnt = 0;
514
515 for tid in self.mstl.iter() {
516 let sid = tid.sid().map_err(|_| fmt::Error)?;
517 let sid = if self.version > 0 {
518 format!("{}#{}", sid, tid.pub_version())
519 } else {
520 sid
521 };
522 for tseg in tid.iter() {
523 let start_time = unsafe { (*tseg.inner).starttime };
524 let start_time_str =
525 util::nstime_to_string(start_time, self.time_format, MSSubSeconds::NanoMicro)
526 .map_err(|_| fmt::Error)?;
527
528 let end_time = unsafe { (*tseg.inner).endtime };
529 let end_time_str =
530 util::nstime_to_string(end_time, self.time_format, MSSubSeconds::NanoMicro)
531 .map_err(|_| fmt::Error)?;
532
533 if self.gap > 0 {
534 let mut gap: f64 = 0.0;
535 let mut no_gap = false;
536
537 let prev_tseg_ptr = unsafe { (*tseg.inner).prev };
538 if !prev_tseg_ptr.is_null() {
539 gap = (start_time - unsafe { (*prev_tseg_ptr).endtime }) as f64
540 / raw::NSTMODULUS as f64;
541 } else {
542 no_gap = true;
543 }
544
545 if gap < 0.0 {
547 let sample_rate = unsafe { (*tseg.inner).samprate };
548 let delta = if sample_rate != 0.0 {
549 1.0 / sample_rate
550 } else {
551 0.0
552 };
553 if gap * -1.0
554 > ((end_time - start_time) as f64 / raw::NSTMODULUS as f64 + delta)
555 {
556 gap = -1.0 * (end_time - start_time) as f64 / raw::NSTMODULUS as f64
557 + delta;
558 }
559 }
560
561 let gap_str = if no_gap {
562 " == ".to_string()
563 } else if gap >= 86400.0 || gap <= -86400.0 {
564 format!("{:<3.1}d", gap / 86400.0)
565 } else if gap >= 3600.0 || gap <= -3600.0 {
566 format!("{:<3.1}h", gap / 3600.0)
567 } else if gap == 0.0 {
568 "-0 ".to_string()
569 } else {
570 format!("{:<4.4}", gap)
571 };
572
573 if self.detail <= 0 {
574 writeln!(
575 f,
576 "{:<27} {:<28} {:<28} {:<4}",
577 sid, start_time_str, end_time_str, gap_str
578 )?;
579 } else {
580 writeln!(
581 f,
582 "{:<27} {:<28} {:<28} {:<} {:<3.3} {:<}",
583 sid,
584 start_time_str,
585 end_time_str,
586 gap_str,
587 tseg.sample_rate_hz(),
588 tseg.sample_cnt()
589 )?;
590 }
591 } else if self.detail > 0 && self.gap <= 0 {
592 writeln!(
593 f,
594 "{:<27} {:<28} {:<28} {:<3.3} {:<}",
595 sid,
596 start_time_str,
597 end_time_str,
598 tseg.sample_rate_hz(),
599 tseg.sample_cnt()
600 )?;
601 } else {
602 writeln!(f, "{:<27} {:<28} {:<28}", sid, start_time_str, end_time_str)?;
603 }
604
605 tseg_cnt += 1;
606 }
607
608 tid_cnt += 1;
609 }
610
611 if self.detail > 0 {
612 writeln!(
613 f,
614 "Total: {} trace(s) with {} segment(s)",
615 tid_cnt, tseg_cnt
616 )?;
617 }
618
619 Ok(())
620 }
621}
622
623#[cfg(test)]
624mod tests {
625
626 use super::*;
627
628 use pretty_assertions::assert_eq;
629 use time::format_description::well_known::Iso8601;
630
631 use crate::{test, MSReader, MSSampleType};
632
633 #[test]
634 fn test_read_unpack_mstl_mseed3() {
635 let mut p = test::test_data_base_dir();
636 assert!(p.is_dir());
637
638 p.push("testdata-oneseries-mixedlengths-mixedorder.mseed3");
639
640 let mut mstl = MSTraceList::new().unwrap();
641
642 let flags = MSControlFlags::MSF_UNPACKDATA;
643 let mut reader = MSReader::new_with_flags(p, flags).unwrap();
644
645 while let Some(res) = reader.next() {
646 let msr = res.unwrap();
647 mstl.insert(msr, true).unwrap();
648 }
649
650 assert_eq!(mstl.len(), 1);
651 let mut mstl_iter = mstl.iter();
652 let trace_id = mstl_iter.next();
653 assert!(trace_id.is_some());
654 let trace_id = trace_id.unwrap();
655 assert_eq!(&trace_id.sid().unwrap(), "FDSN:XX_TEST_00_L_H_Z");
656 assert_eq!(trace_id.pub_version(), 1);
657 assert_eq!(trace_id.len(), 1);
658 assert_eq!(
659 trace_id
660 .start_time()
661 .unwrap()
662 .format(&Iso8601::DEFAULT)
663 .unwrap(),
664 "2010-02-27T06:50:00.069539000Z"
665 );
666 assert_eq!(
667 trace_id
668 .end_time()
669 .unwrap()
670 .format(&Iso8601::DEFAULT)
671 .unwrap(),
672 "2010-02-27T07:55:51.069539000Z"
673 );
674 let mut trace_id_iter = trace_id.iter();
675 let trace_seg = trace_id_iter.next();
676 assert!(trace_seg.is_some());
677 let mut trace_seg = trace_seg.unwrap();
678 assert_eq!(
679 trace_seg
680 .start_time()
681 .unwrap()
682 .format(&Iso8601::DEFAULT)
683 .unwrap(),
684 "2010-02-27T06:50:00.069539000Z"
685 );
686 assert_eq!(
687 trace_seg
688 .end_time()
689 .unwrap()
690 .format(&Iso8601::DEFAULT)
691 .unwrap(),
692 "2010-02-27T07:55:51.069539000Z"
693 );
694 assert_eq!(trace_seg.sample_cnt(), 3952);
695 assert_eq!(trace_seg.sample_type(), MSSampleType::Integer32);
696 assert_eq!(trace_seg.num_samples(), 3952);
697
698 let data_samples: &[i32] = trace_seg.data_samples().unwrap();
699 assert_eq!(data_samples[3948], 28067);
701 assert_eq!(data_samples[3949], -9565);
702 assert_eq!(data_samples[3950], -71961);
703 assert_eq!(data_samples[3951], -146622);
704
705 assert!(trace_id_iter.next().is_none());
706 assert!(mstl_iter.next().is_none());
707 }
708
709 #[test]
710 fn test_read_unpack_mstl_mseed2() {
711 let mut p = test::test_data_base_dir();
712 assert!(p.is_dir());
713
714 p.push("testdata-oneseries-mixedlengths-mixedorder.mseed2");
715
716 let mut mstl = MSTraceList::new().unwrap();
717
718 let flags = MSControlFlags::MSF_UNPACKDATA;
719 let mut reader = MSReader::new_with_flags(p, flags).unwrap();
720
721 while let Some(res) = reader.next() {
722 let msr = res.unwrap();
723 mstl.insert(msr, true).unwrap();
724 }
725
726 assert_eq!(mstl.len(), 1);
727 let mut mstl_iter = mstl.iter();
728 let trace_id = mstl_iter.next();
729 assert!(trace_id.is_some());
730 let trace_id = trace_id.unwrap();
731 assert_eq!(&trace_id.sid().unwrap(), "FDSN:XX_TEST_00_L_H_Z");
732 assert_eq!(trace_id.pub_version(), 1);
733 assert_eq!(trace_id.len(), 1);
734 assert_eq!(
735 trace_id
736 .start_time()
737 .unwrap()
738 .format(&Iso8601::DEFAULT)
739 .unwrap(),
740 "2010-02-27T06:50:00.069539000Z"
741 );
742 assert_eq!(
743 trace_id
744 .end_time()
745 .unwrap()
746 .format(&Iso8601::DEFAULT)
747 .unwrap(),
748 "2010-02-27T07:55:51.069539000Z"
749 );
750 let mut trace_id_iter = trace_id.iter();
751 let trace_seg = trace_id_iter.next();
752 assert!(trace_seg.is_some());
753 let mut trace_seg = trace_seg.unwrap();
754 assert_eq!(
755 trace_seg
756 .start_time()
757 .unwrap()
758 .format(&Iso8601::DEFAULT)
759 .unwrap(),
760 "2010-02-27T06:50:00.069539000Z"
761 );
762 assert_eq!(
763 trace_seg
764 .end_time()
765 .unwrap()
766 .format(&Iso8601::DEFAULT)
767 .unwrap(),
768 "2010-02-27T07:55:51.069539000Z"
769 );
770 assert_eq!(trace_seg.sample_cnt(), 3952);
771 assert_eq!(trace_seg.sample_type(), MSSampleType::Integer32);
772 assert_eq!(trace_seg.num_samples(), 3952);
773
774 let data_samples: &[i32] = trace_seg.data_samples().unwrap();
775 assert_eq!(data_samples[3948], 28067);
777 assert_eq!(data_samples[3949], -9565);
778 assert_eq!(data_samples[3950], -71961);
779 assert_eq!(data_samples[3951], -146622);
780
781 assert!(trace_id_iter.next().is_none());
782 assert!(mstl_iter.next().is_none());
783 }
784}