gondola_core/monitoring.rs
1// This file is part of gaps-online-software and published
2// under the GPLv3 license
3
4pub mod pa_moni_data;
5pub use pa_moni_data::{
6 PAMoniData,
7 PAMoniDataSeries
8};
9pub mod pb_moni_data;
10pub use pb_moni_data::{
11 PBMoniData,
12 PBMoniDataSeries
13};
14pub mod mtb_moni_data;
15pub use mtb_moni_data::{
16 MtbMoniData,
17 MtbMoniDataSeries
18};
19pub mod ltb_moni_data;
20pub use ltb_moni_data::{
21 LTBMoniData,
22 LTBMoniDataSeries
23};
24pub mod rb_moni_data;
25pub use rb_moni_data::{
26 RBMoniData,
27 RBMoniDataSeries
28};
29pub mod cpu_moni_data;
30pub use cpu_moni_data::{
31 CPUMoniData,
32 CPUMoniDataSeries
33};
34
35pub mod heartbeats;
36pub use heartbeats::{
37 DataSinkHB,
38 DataSinkHBSeries,
39 MasterTriggerHB,
40 MasterTriggerHBSeries,
41 EventBuilderHB,
42 EventBuilderHBSeries,
43};
44
45pub mod run_statistics;
46pub use run_statistics::RunStatistics;
47
48pub mod sip_position;
49pub use sip_position::{
50 SipPosMoniData,
51 SipPosMoniDataSeries
52};
53
54pub mod sip_pressure;
55pub use sip_pressure::{
56 SipPresMoniData,
57 SipPresMoniDataSeries,
58};
59
60pub mod sip_time;
61pub use sip_time::{
62 SipTimeMoniData,
63 SipTimeMoniDataSeries
64};
65
66pub mod gcu_ev_stats;
67pub use gcu_ev_stats::{
68 GcuEvBldStatsMoniData,
69 GcuEvBldStatsMoniDataSeries
70};
71
72pub mod tracker_gps;
73pub use tracker_gps::{
74 TrackerGpsMoniData,
75 TrackerGpsMoniDataSeries
76};
77
78pub mod cooling;
79pub use cooling::{
80 CoolingMoniData,
81 CoolingMoniDataSeries
82};
83
84pub mod wastie;
85pub use wastie::*;
86
87use std::collections::VecDeque;
88use std::collections::HashMap;
89
90#[cfg(feature="pybindings")]
91use crate::prelude::*;
92
93#[cfg(feature="pybindings")]
94use polars::frame::column::Column;
95#[cfg(feature="pybindings")]
96use polars::prelude::NamedFrom;
97
98/// Monitoring data shall share the same kind
99/// of interface.
100pub trait MoniData {
101 /// Monitoring data is always tied to a specific
102 /// board. This might not be its own board, but
103 /// maybe the RB the data was gathered from
104 /// This is an unique identifier for the
105 /// monitoring data
106 fn get_board_id(&self) -> u8;
107
108 /// Access the (data) members by name
109 fn get(&self, varname : &str) -> Option<f32>;
110
111 /// A list of the variables in this MoniData
112 fn keys() -> Vec<&'static str>;
113
114 fn get_timestamp(&self) -> u64 {
115 0
116 }
117
118 fn set_timestamp(&mut self, _ts : u64) {
119 }
120 ///// access the internal timestamps as obtained from
121 ///// MoniDat
122 //fn get_timestamps_mut(&mut self) -> &Vec<u64> {
123 //}
124}
125
126/// A MoniSeries is a collection of (primarily) monitoring
127/// data, which comes from multiple senders.
128/// E.g. a MoniSeries could hold RBMoniData from all
129/// 40 ReadoutBoards.
130pub trait MoniSeries<T>
131 where T : Copy + MoniData {
132
133 fn get_first_ts(&self) -> u64;
134
135 fn get_data(&self) -> &HashMap<u8,VecDeque<T>>;
136
137 fn get_data_mut(&mut self) -> &mut HashMap<u8,VecDeque<T>>;
138
139 fn get_max_size(&self) -> usize;
140
141 fn set_max_size(&mut self, size : usize);
142
143 fn get_timestamps(&self) -> &Vec<u64>;
144
145 fn add_timestamp(&mut self, ts : u64);
146
147 /// A HashMap of -> rbid, Vec\<var\>
148 fn get_var(&self, varname : &str) -> HashMap<u8, Vec<f32>> {
149 let mut values = HashMap::<u8, Vec<f32>>::new();
150 for k in self.get_data().keys() {
151 match self.get_var_for_board(varname, k) {
152 None => (),
153 Some(vals) => {
154 values.insert(*k, vals);
155 }
156 }
157 //values.insert(*k, Vec::<f32>::new());
158 //match self.get_data().get(k) {
159 // None => (),
160 // Some(vec_moni) => {
161 // for moni in vec_moni {
162 // match moni.get(varname) {
163 // None => (),
164 // Some(val) => {
165 // values.get_mut(k).unwrap().push(val);
166 // }
167 // }
168 // }
169 // }
170 //}
171 }
172 values
173 }
174
175 /// Get a certain variable, but only for a single board
176 fn get_var_for_board(&self, varname : &str, rb_id : &u8) -> Option<Vec<f32>> {
177 let mut values = Vec::<f32>::new();
178 match self.get_data().get(&rb_id) {
179 None => (),
180 Some(vec_moni) => {
181 for moni in vec_moni {
182 match moni.get(varname) {
183 None => {
184 return None;
185 },
186 Some(val) => {
187 values.push(val);
188 }
189 }
190 }
191 }
192 }
193 // FIXME This needs to be returning a reference,
194 // not cloning
195 Some(values)
196 }
197
198 #[cfg(feature = "pybindings")]
199 fn get_dataframe(&self) -> PolarsResult<DataFrame> {
200 let mut series = Vec::<Column>::new();
201 for k in Self::keys() {
202 match self.get_series(k) {
203 None => {
204 error!("Unable to get series for {}", k);
205 }
206 Some(ser) => {
207 //println!("{}", ser);
208 series.push(ser.into());
209 }
210 }
211 }
212 //if self.get_timestamps().len() > 0 {
213 // let timestamps = Series::new("timestamps".into(), self.get_timestamps());
214 // series.push(timestamps.into());
215 //}
216 // each column is now the specific variable but in terms for
217 // all rbs
218 let df = DataFrame::new(series)?;
219 Ok(df)
220 }
221
222 #[cfg(feature = "pybindings")]
223 /// Get the variable for all boards. This keeps the order of the
224 /// underlying VecDeque. Values of all boards intermixed.
225 /// To get a more useful version, use the Dataframe instead.
226 ///
227 /// # Arguments
228 ///
229 /// * varname : The name of the attribute of the underlying
230 /// moni structure
231 fn get_series(&self, varname : &str) -> Option<Series> {
232 let mut data = Vec::<f32>::with_capacity(self.get_data().len());
233 let sorted_keys: Vec<u8> = self.get_data().keys().cloned().collect();
234 for board_id in sorted_keys.iter() {
235 let dqe = self.get_data().get(board_id).unwrap(); //uwrap is fine, bc we checked earlier
236 for moni in dqe {
237 match moni.get(varname) {
238 None => {
239 error!("This type of MoniData does not have a key called {}", varname);
240 return None;
241 }
242 Some(var) => {
243 data.push(var);
244 }
245 }
246 }
247 }
248 let series = Series::new(varname.into(), data);
249 Some(series)
250 }
251
252 /// A list of the variables in this MoniSeries
253 fn keys() -> Vec<&'static str> {
254 T::keys()
255 }
256
257 /// A list of boards in this series
258 fn get_board_ids(&self) -> Vec<u8> {
259 self.get_data().keys().cloned().collect()
260 }
261
262 /// Add another instance of the data container to the series
263 fn add(&mut self, data : T) {
264 let board_id = data.get_board_id();
265 if !self.get_data().contains_key(&board_id) {
266 self.get_data_mut().insert(board_id, VecDeque::<T>::new());
267 }
268 self.get_data_mut().get_mut(&board_id).unwrap().push_back(data);
269 if self.get_data_mut().get_mut(&board_id).unwrap().len() > self.get_max_size() {
270 error!("The queue is too large, returning the first element! If you need a larger series size, set the max_size field");
271 self.get_data_mut().get_mut(&board_id).unwrap().pop_front();
272 }
273 }
274
275 fn get_last_moni(&self, board_id : u8) -> Option<T> {
276 let size = self.get_data().get(&board_id)?.len();
277 Some(self.get_data().get(&board_id).unwrap()[size - 1])
278 }
279}
280
281//--------------------------------------------------
282
283#[macro_export]
284macro_rules! moniseries_general {
285 ($name : ident, $class:ty) => {
286 use std::collections::VecDeque;
287 use std::collections::HashMap;
288
289 use crate::monitoring::MoniSeries;
290
291 #[cfg_attr(feature="pybindings",pyclass)]
292 #[derive(Debug, Clone, PartialEq)]
293 pub struct $name {
294 data : HashMap<u8, VecDeque<$class>>,
295 max_size : usize,
296 timestamps : Vec<u64>,
297 }
298
299 impl $name {
300 pub fn new() -> Self {
301 Self {
302 data : HashMap::<u8, VecDeque<$class>>::new(),
303 max_size : 10000,
304 timestamps : Vec::<u64>::new()
305 }
306 }
307 }
308
309 impl Default for $name {
310 fn default() -> Self {
311 Self::new()
312 }
313 }
314
315 impl fmt::Display for $name {
316 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
317 write!(f, "<{} : {} boards>", stringify!($name), self.data.len())
318 }
319 }
320
321 impl MoniSeries<$class> for $name {
322
323 fn get_first_ts(&self) -> u64 {
324 if self.timestamps.len() == 0 {
325 return 0;
326 } else {
327 self.timestamps[0]
328 }
329 }
330
331 fn get_data(&self) -> &HashMap<u8,VecDeque<$class>> {
332 return &self.data;
333 }
334
335 fn get_data_mut(&mut self) -> &mut HashMap<u8,VecDeque<$class>> {
336 return &mut self.data;
337 }
338
339 fn get_max_size(&self) -> usize {
340 return self.max_size;
341 }
342
343 fn set_max_size(&mut self, size : usize) {
344 self.max_size = size;
345 }
346
347 fn add_timestamp(&mut self, ts : u64) {
348 self.timestamps.push(ts);
349 }
350
351 fn get_timestamps(&self) -> &Vec<u64> {
352 //if self.timestamps.len() == 0 {
353 // let mut timestamps = Vec::<u64>::new();
354 // for k in self.
355 //}
356 return &self.timestamps;
357 }
358 }
359
360 #[cfg(feature="pybindings")]
361 #[pymethods]
362 impl $name {
363 #[new]
364 fn new_py() -> Self {
365 Self::new()
366 }
367
368 /// The maximum size of the series. If more data
369 /// are added, data from the front will be removed
370 #[getter]
371 #[pyo3(name="max_size")]
372 fn get_max_size_py(&self) -> usize {
373 self.get_max_size()
374 }
375
376 #[setter]
377 #[pyo3(name="max_size")]
378 fn set_max_size_py(&mut self, size : usize) {
379 self.set_max_size(size);
380 }
381
382
383 #[getter]
384 #[pyo3(name="first_ts")]
385 pub fn get_first_ts_py(&self) -> u64 {
386 self.get_first_ts()
387 }
388
389 /// If monitoring is retrieved from telemetry, we
390 /// save the gcu timestamp of the packet, wich
391 /// herein can be accessed.
392 #[getter]
393 #[pyo3(name="timestamps")]
394 fn get_timestamps_py(&self) -> Vec<u64> {
395 warn!("This returns a full copy and is a performance bottleneck!");
396 return self.timestamps.clone();
397 }
398
399 #[pyo3(name="get_var_for_board")]
400 fn get_var_for_board_py(&self, varname : &str, rb_id : u8) -> Option<Vec<f32>> {
401 self.get_var_for_board(varname, &rb_id)
402 }
403
404 /// Reduces the MoniSeries to a single polars data frame
405 /// The structure itself will not be changed
406 #[pyo3(name="get_dataframe")]
407 fn get_dataframe_py(&self) -> PyResult<PyDataFrame> {
408 match self.get_dataframe() {
409 Ok(df) => {
410 let pydf = PyDataFrame(df);
411 return Ok(pydf);
412 },
413 Err(err) => {
414 return Err(PyValueError::new_err(err.to_string()));
415 }
416 }
417 }
418 }
419 #[cfg(feature="pybindings")]
420 pythonize_display!($name);
421 }
422}
423
424/// Implements the moniseries trait for a MoniData
425/// type of class
426#[macro_export]
427macro_rules! moniseries {
428 ($name : ident, $class:ty) => {
429
430 moniseries_general!($name, $class);
431 //use std::collections::VecDeque;
432 //use std::collections::HashMap;
433
434 //use crate::monitoring::MoniSeries;
435
436 //#[cfg_attr(feature="pybindings",pyclass)]
437 //#[derive(Debug, Clone, PartialEq)]
438 //pub struct $name {
439 // data : HashMap<u8, VecDeque<$class>>,
440 // max_size : usize,
441 // timestamps : Vec<u64>,
442 //}
443 //
444 //impl $name {
445 // pub fn new() -> Self {
446 // Self {
447 // data : HashMap::<u8, VecDeque<$class>>::new(),
448 // max_size : 10000,
449 // timestamps : Vec::<u64>::new()
450 // }
451 // }
452 //}
453 //
454 //impl Default for $name {
455 // fn default() -> Self {
456 // Self::new()
457 // }
458 //}
459 //
460 //impl fmt::Display for $name {
461 // fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
462 // write!(f, "<{} : {} boards>", stringify!($name), self.data.len())
463 // }
464 //}
465 //
466 //impl MoniSeries<$class> for $name {
467
468 // fn get_first_ts(&self) -> u64 {
469 // if self.timestamps.len() == 0 {
470 // return 0;
471 // } else {
472 // self.timestamps[0]
473 // }
474 // }
475
476 // fn get_data(&self) -> &HashMap<u8,VecDeque<$class>> {
477 // return &self.data;
478 // }
479 //
480 // fn get_data_mut(&mut self) -> &mut HashMap<u8,VecDeque<$class>> {
481 // return &mut self.data;
482 // }
483 //
484 // fn get_max_size(&self) -> usize {
485 // return self.max_size;
486 // }
487 //
488 // fn set_max_size(&mut self, size : usize) {
489 // self.max_size = size;
490 // }
491
492 // fn add_timestamp(&mut self, ts : u64) {
493 // self.timestamps.push(ts);
494 // }
495
496 // fn get_timestamps(&self) -> &Vec<u64> {
497 // //if self.timestamps.len() == 0 {
498 // // let mut timestamps = Vec::<u64>::new();
499 // // for k in self.
500 // //}
501 // return &self.timestamps;
502 // }
503 //}
504
505 //#[cfg(feature="pybindings")]
506 //#[pymethods]
507 //impl $name {
508 // #[new]
509 // fn new_py() -> Self {
510 // Self::new()
511 // }
512
513 // /// The maximum size of the series. If more data
514 // /// are added, data from the front will be removed
515 // #[getter]
516 // #[pyo3(name="max_size")]
517 // fn get_max_size_py(&self) -> usize {
518 // self.get_max_size()
519 // }
520
521 // #[setter]
522 // #[pyo3(name="max_size")]
523 // fn set_max_size_py(&mut self, size : usize) {
524 // self.set_max_size(size);
525 // }
526 //
527
528 // #[getter]
529 // #[pyo3(name="get_first_ts")]
530 // fn get_first_ts_py(&self) -> u64 {
531 // self.get_first_ts()
532 // }
533
534 // /// If monitoring is retrieved from telemetry, we
535 // /// save the gcu timestamp of the packet, wich
536 // /// herein can be accessed.
537 // #[getter]
538 // #[pyo3(name="timestamps")]
539 // fn get_timestamps_py(&self) -> Vec<u64> {
540 // warn!("This returns a full copy and is a performance bottleneck!");
541 // return self.timestamps.clone();
542 // }
543
544 #[cfg(feature="pybindings")]
545 #[pymethods]
546 impl $name {
547
548 /// Add an additional (Caraspace) file to the series
549 ///
550 /// # Arguments:
551 /// * filename : The name of the (caraspace) file to add
552 /// * from_object : Since this adds caraspace files, it is possible
553 /// to choose from where to get the information.
554 /// Either the telemetry packet, or the tofpacket, if
555 /// either is present in the frame. When
556 /// CRFrameObjectType = Unknown, it will figure it out
557 /// automatically, preferring the telemetry since it has
558 /// the gcu timestamp
559 #[pyo3(signature = (filename, from_object = CRFrameObjectType::TelemetryPacket))]
560 fn add_crfile(&mut self, filename : String, from_object : CRFrameObjectType) {
561 let reader = CRReader::new(filename).expect("Unable to open file!");
562 // now we have a problem - from which frame should we get it?
563 // if we get it from the dedicated TOF stream it will be much
564 // faster (if that is available) since it will be it's own
565 // presence in the frame
566 //let address = &source.clone();
567 //let mut try_from_telly = false;
568 let tp_source = String::from("TofPacketType.") + stringify!($class);
569 let tp_source_alt = String::from("PacketType.") + stringify!($class);
570 let tel_source = "TelemetryPacketType.AnyTofHK";
571 for frame in reader {
572 match from_object {
573 CRFrameObjectType::TofPacket => {
574 if frame.has(&tp_source) || frame.has(&tp_source_alt) {
575 if frame.has(&tp_source) {
576 let moni_res = frame.get::<TofPacket>(&tp_source).unwrap().unpack::<$class>();
577 match moni_res {
578 Err(err) => {
579 println!("Error unpacking! {err}");
580 }
581 Ok(moni) => {
582 self.add(moni);
583 }
584 }
585 }
586 if frame.has(&tp_source_alt) {
587 let moni_res = frame.get::<TofPacket>(&tp_source_alt).unwrap().unpack::<$class>();
588 match moni_res {
589 Err(err) => {
590 println!("Error unpacking! {err}");
591 }
592 Ok(moni) => {
593 self.add(moni);
594 }
595 }
596 }
597 }
598 }
599 CRFrameObjectType::TelemetryPacket | CRFrameObjectType::Unknown => {
600 if frame.has(tel_source) {
601 let hk_res = frame.get::<TelemetryPacket>(tel_source);
602 match hk_res {
603 Err(err) => {
604 println!("Error unpacking! {err}");
605 }
606 Ok(hk) => {
607 let mut pos = 0;
608 // subtract the 2020/1/1 midnight from the gcutime to make
609 // it f32
610 let gcutime = hk.header.get_gcutime() as u64;
611 match TofPacket::from_bytestream(&hk.payload, &mut pos) {
612 Err(err) => {
613 println!("Error unpackin! {err}");
614 }
615 Ok(tp) => {
616 if tp.packet_type == <$class>::TOF_PACKET_TYPE {
617 match tp.unpack::<$class>() {
618 Err(err) => {
619 println!("Error unpacking! {err}");
620 }
621 Ok(mut moni) => {
622 self.add_timestamp(gcutime);
623 moni.set_timestamp(gcutime - self.get_first_ts());
624 self.add(moni);
625 //self.timestamps.push(gcutime);
626 }
627 }
628 }
629 }
630 }
631 }
632 }
633 }
634 }
635 _ => () // do nothing for other types
636 }
637 }
638 }
639
640 /// Add an additional (Telemetry) file to the series
641 ///
642 /// # Arguments:
643 /// * filename : The name of the (telemetry) file to add
644 fn add_telemetryfile(&mut self, filename : String) {
645 let reader = TelemetryPacketReader::new(filename, true, None, None, 0, 0);
646 for pack in reader {
647 if pack.header.packet_type == TelemetryPacketType::AnyTofHK {
648 let mut pos = 0;
649 // subtract the 2020/1/1 midnight from the gcutime to make
650 // it f32
651 let gcutime = pack.header.get_gcutime() as u64;
652 match TofPacket::from_bytestream(&pack.payload, &mut pos) {
653 Err(err) => {
654 println!("Error unpackin! {err}");
655 }
656 Ok(tp) => {
657 if tp.packet_type == <$class>::TOF_PACKET_TYPE {
658 match tp.unpack::<$class>() {
659 Err(err) => {
660 println!("Error unpacking! {err}");
661 }
662 Ok(mut moni) => {
663 self.add_timestamp(gcutime);
664 moni.set_timestamp(gcutime - self.get_first_ts());
665 self.add(moni);
666 //self.timestamps.push(gcutime);
667 }
668 }
669 }
670 }
671 }
672 }
673 }
674 }
675
676 /// Add an additional (Tof) file to the series
677 ///
678 /// # Arguments:
679 /// * filename : The name of the (caraspace) file to add
680 /// * from_object : Since this adds caraspace files, it is possible
681 /// to choose from where to get the information.
682 /// Either the telemetry packet, or the tofpacket, if
683 /// either is present in the frame. When
684 /// CRFrameObjectType = Unknown, it will figure it out
685 /// automatically, preferring the telemetry since it has
686 /// the gcu timestamp
687 #[pyo3(signature = (filename))]
688 fn add_toffile(&mut self, filename : String) {
689 let reader = TofPacketReader::new(&filename);
690 for tp in reader {
691 if tp.packet_type == <$class>::TOF_PACKET_TYPE {
692 match tp.unpack::<$class>() {
693 Err(err) => {
694 println!("Error unpacking! {err}");
695 }
696 Ok(mut moni) => {
697 self.add_timestamp(moni.get_timestamp());
698 moni.set_timestamp(moni.get_timestamp());
699 self.add(moni);
700 //self.timestamps.push(gcutime);
701 }
702 }
703 }
704 }
705 }
706
707 /// Generate a polars dataframe with monitoring data from the
708 /// given TOF file.
709 /// This will load ONLY data of the specific type of the
710 /// MoniSeries itself
711 ///
712 /// # Arguments:
713 /// * filename : A single .tof.gaps file with monitoring
714 /// information
715 #[staticmethod]
716 fn from_tof_file(filename : String) -> PyResult<PyDataFrame> {
717 let mut reader = TofPacketReader::new(&filename);
718 let mut series = Self::new();
719 // it would be nice to set the filter here, but I
720 // don't know how that can be done in the macro
721 reader.filter = <$class>::TOF_PACKET_TYPE;
722 for tp in reader {
723 if let Ok(moni) = tp.unpack::<$class>() {
724 series.add(moni);
725 }
726 // other packets will get thrown away
727 }
728 match series.get_dataframe() {
729 Ok(df) => {
730 let pydf = PyDataFrame(df);
731 return Ok(pydf);
732 },
733 Err(err) => {
734 return Err(PyValueError::new_err(err.to_string()));
735 }
736 }
737 }
738
739 //#[pyo3(name="get_var_for_board")]
740 //fn get_var_for_board_py(&self, varname : &str, rb_id : u8) -> Option<Vec<f32>> {
741 // self.get_var_for_board(varname, &rb_id)
742 //}
743
744 ///// Reduces the MoniSeries to a single polars data frame
745 ///// The structure itself will not be changed
746 //#[pyo3(name="get_dataframe")]
747 //fn get_dataframe_py(&self) -> PyResult<PyDataFrame> {
748 // match self.get_dataframe() {
749 // Ok(df) => {
750 // let pydf = PyDataFrame(df);
751 // return Ok(pydf);
752 // },
753 // Err(err) => {
754 // return Err(PyValueError::new_err(err.to_string()));
755 // }
756 // }
757 //}
758
759 ////fn get_pl_series_py(&self) -> PyResult<PyS
760 ////fn get_data(&self) -> &HashMap<u8,VecDeque<$class>> {
761 //// return &self.data;
762 ////}
763
764 ////fn get_data_mut(&mut self) -> &mut HashMap<u8,VecDeque<$class>> {
765 //// return &mut self.data;
766 ////}
767
768 ////fn get_max_size(&self) -> usize {
769 //// return self.max_size;
770 ////}
771 }
772
773 //#[cfg(feature="pybindings")]
774 //pythonize_display!($name);
775 }
776}
777
778// ------------------------------------------------
779
780// baiscally the same macro, but for data coming
781// from telemetry
782
783/// Implements the moniseries trait for a MoniData
784/// type of class
785#[macro_export]
786macro_rules! moniseries_telemetry {
787 ($name : ident, $class:ty) => {
788
789 moniseries_general!($name, $class);
790
791 #[cfg(feature="pybindings")]
792 #[pymethods]
793 impl $name {
794
795 /// Add an additional (Caraspace) file to the series
796 ///
797 /// # Arguments:
798 /// * filename : The name of the (caraspace) file to add
799 /// * from_object : Since this adds caraspace files, it is possible
800 /// to choose from where to get the information.
801 /// Either the telemetry packet, or the tofpacket, if
802 /// either is present in the frame. When
803 /// CRFrameObjectType = Unknown, it will figure it out
804 /// automatically, preferring the telemetry since it has
805 /// the gcu timestamp
806 #[pyo3(signature = (filename, from_object = CRFrameObjectType::TelemetryPacket))]
807 fn add_crfile(&mut self, filename : String, from_object : CRFrameObjectType) {
808 let reader = CRReader::new(filename).expect("Unable to open file!");
809 // now we have a problem - from which frame should we get it?
810 // if we get it from the dedicated TOF stream it will be much
811 // faster (if that is available) since it will be it's own
812 // presence in the frame
813 //let address = &source.clone();
814 //let mut try_from_telly = false;
815 let tel_source = "TelemetryPacketType.AnyTofHK";
816 for frame in reader {
817 match from_object {
818 CRFrameObjectType::TelemetryPacket | CRFrameObjectType::Unknown => {
819 if frame.has(tel_source) {
820 let hk_res = frame.get::<TelemetryPacket>(tel_source);
821 match hk_res {
822 Err(err) => {
823 println!("Error unpacking! {err}");
824 }
825 Ok(hk) => {
826 if hk.header.packet_type != <$class>::TEL_PACKET_TYPE {
827 // this is not the packet you are looking for
828 continue;
829 }
830 let mut pos = 0;
831 // subtract the 2020/1/1 midnight from the gcutime to make
832 // it f32
833 let gcutime = hk.header.get_gcutime() as u64;
834 match <$class>::from_bytestream(&hk.payload, &mut pos) {
835 Err(err) => {
836 println!("Error unpackin! {err}");
837 }
838 Ok(mut moni) => {
839 self.add_timestamp(gcutime);
840 moni.set_timestamp(gcutime - self.get_first_ts());
841 self.add(moni);
842 //self.timestamps.push(gcutime);
843 }
844 }
845 }
846 }
847 }
848 }
849 _ => ()
850 }
851 }
852 }
853
854 /// Add an additional (Telemetry) file to the series
855 ///
856 /// # Arguments:
857 /// * filename : The name of the (telemetry) file to add
858 fn add_telemetryfile(&mut self, filename : String) {
859 let reader = TelemetryPacketReader::new(filename, true, None, None, 0, 0);
860 for pack in reader {
861 if pack.header.packet_type == <$class>::TEL_PACKET_TYPE {
862 let mut pos = 0;
863 // subtract the 2020/1/1 midnight from the gcutime to make
864 // it f32
865 let gcutime = pack.header.get_gcutime() as u64;
866 match <$class>::from_bytestream(&pack.payload, &mut pos) {
867 Err(err) => {
868 println!("Error unpackin! {err}");
869 }
870 Ok(mut moni) => {
871 self.add_timestamp(gcutime);
872 moni.set_timestamp(gcutime - self.get_first_ts());
873 self.add(moni);
874 }
875 }
876 }
877 }
878 }
879 }
880 }
881}
882
883