go_pybindings/
dataclasses.rs

1use std::collections::HashMap;
2
3use numpy::{
4  PyArray,
5  PyArray1,
6  PyArray2, 
7};
8
9use pyo3_polars::{
10  PyDataFrame,
11  //PySeries
12};
13
14use pyo3::Python;
15
16use tof_dataclasses::ProtocolVersion;
17use tof_dataclasses::io::TofPacketReader;
18#[cfg(feature = "caraspace-serial")]
19use caraspace::reader::CRReader;
20use tof_dataclasses::packets::{
21  TofPacket,
22  PacketType
23};
24use tof_dataclasses::database::DsiJChPidMapping;
25
26use tof_dataclasses::heartbeats::HeartBeatDataSink;
27use tof_dataclasses::heartbeats::MTBHeartbeat;
28use tof_dataclasses::heartbeats::EVTBLDRHeartbeat;
29
30#[cfg(feature="telemetry")]
31use crate::tel_api::TelemetryPacket;
32
33use tof_dataclasses::monitoring::{
34  MoniData,
35  MoniSeries,
36  PAMoniData,
37  PBMoniData,
38  RBMoniData,
39  MtbMoniData, 
40  CPUMoniData,
41  LTBMoniData,
42};
43
44use tof_dataclasses::status::TofDetectorStatus;
45
46use tof_dataclasses::series::{
47  PAMoniDataSeries,
48  PBMoniDataSeries,
49  RBMoniDataSeries,
50  MtbMoniDataSeries,
51  CPUMoniDataSeries,
52  LTBMoniDataSeries,
53};
54
55use tof_dataclasses::events::{
56  TofEvent,
57  TofEventHeader,
58  TofEventSummary,
59  EventStatus,
60  TofHit,
61  MasterTriggerEvent,
62  RBEvent,
63  RBEventHeader,
64  RBWaveform
65};
66
67use tof_dataclasses::serialization::{
68  Serialization,
69  Packable
70};
71
72use tof_dataclasses::commands::{
73  TofCommandV2,
74  TofCommandCode
75};
76
77use tof_dataclasses::calibrations::{
78  RBCalibrations,
79  //clean_spikes,
80  //spike_cleaning
81};
82
83use tof_dataclasses::commands::config::{
84  AnalysisEngineConfig,
85  RunConfig, // deprecated
86  TriggerConfig,
87  TOFEventBuilderConfig,
88  DataPublisherConfig,
89  TofRunConfig,
90  TofRBConfig,
91  BuildStrategy
92};
93
94use pyo3::prelude::*;
95use pyo3::exceptions::{
96  PyKeyError,
97  PyValueError,
98  PyIOError,
99};
100
101use tof_dataclasses::events::TriggerType;
102use tof_dataclasses::events::master_trigger::LTBThreshold;
103use tof_dataclasses::events::rb_event::RBPaddleID;
104
105//trait<T> Wrapper {
106//  where T : Packable
107//
108//  /// Return the name of the underlying struct
109//  fn wrapped_name() -> &str;
110//
111//  /// Unpack from a wrapped TofPacket
112//  fn from_tofpacket(&mut self, packet : &PyTofPacket) -> PyResult<()> {
113//    let tp = packet.get_tp();
114//    match tp.unpack::<T>() {
115//      Ok(moni) => {
116//        self.moni = moni;
117//        return Ok(());
118//      }
119//      Err(err) => {
120//        let err_msg = format!("Unable to unpack TofPacket! {err}");
121//        return Err(PyIOError::new_err(err_msg));
122//      }
123//    }
124//  }
125//
126//  fn __repr__(&self) -> PyResult<String> {
127//    Ok(format!("<PyO3Wrapper: {}>", self.moni)) 
128//  }
129//
130
131#[pyclass]
132#[pyo3(name="RBPaddleID")]
133pub struct PyRBPaddleID {
134  pub pid : RBPaddleID
135}
136
137#[pymethods]
138impl PyRBPaddleID {
139
140  #[new]
141  fn new() -> Self { 
142    Self {
143      pid : RBPaddleID::new()    
144    }
145  }
146 
147  fn to_u64(&self) -> u64 {
148    self.pid.to_u64()
149  }
150  
151  fn get_order_flipped(&self, channel : u8) -> bool {
152    self.pid.get_order_flipped(channel)
153  }
154  
155  fn get_order_str(&self, channel : u8) -> String {
156    self.pid.get_order_str(channel)
157  }
158  
159  fn is_a(&self, channel : u8) -> bool {
160    self.pid.is_a(channel)
161  }
162  
163  fn from_u64(&self, val : u64) -> Self {
164    let pid = RBPaddleID::from_u64(val);
165    Self {
166      pid
167    }
168  }
169
170  //fn from_rb(
171  fn get_paddle_id(&self, channel : u8) -> (u8, bool) {  
172    self.pid.get_paddle_id(channel)
173  }
174  
175  fn __repr__(&self) -> PyResult<String> {
176    Ok(format!("<PyO3Wrapper: {}>", self.pid)) 
177  }
178}
179
180#[pyclass]
181#[pyo3(name="TofDetectorStatus")]
182pub struct PyTofDetectorStatus {
183  pub status : TofDetectorStatus
184}
185
186#[pymethods]
187impl PyTofDetectorStatus {
188  
189  #[new]
190  fn new() -> Self {
191    let status =  TofDetectorStatus::new();
192    Self {
193      status
194    }
195  }
196  //fn to_bytestream(&self) -> Vec<u8> {
197  //  self.config.to_bytestream()
198  //}
199  fn from_tofpacket(&mut self, packet : &PyTofPacket) -> PyResult<()> {
200    let tp = packet.get_tp();
201    match tp.unpack::<TofDetectorStatus>() {
202      Ok(status) => {
203        self.status = status;
204        return Ok(());
205      }
206      Err(err) => {
207        let err_msg = format!("Unable to unpack TofPacket! {err}");
208        return Err(PyIOError::new_err(err_msg));
209      }
210    }
211  }
212
213  #[getter]
214  fn channels000_031(&self) -> u32 {
215    self.status.channels000_031
216  }
217
218  #[getter]
219  fn channels032_063(&self) -> u32 { 
220    self.status.channels032_063
221  }
222
223  #[getter]
224  fn channels064_095(&self) -> u32 { 
225    self.status.channels064_095
226  }
227
228  #[getter]
229  fn channels096_127(&self) -> u32 { 
230    self.status.channels096_127
231  }  
232
233  #[getter]
234  fn channels128_159(&self) -> u32 { 
235    self.status.channels128_159
236  }
237
238  #[getter]
239  fn channels160_191(&self) -> u32 { 
240    self.status.channels160_191
241  }
242
243  #[getter]
244  fn channels192_223(&self) -> u32 { 
245    self.status.channels192_223
246  }
247
248  #[getter]
249  fn channels224_255(&self) -> u32 { 
250    self.status.channels224_255
251  }
252  
253  #[getter]
254  fn channels256_297(&self) -> u32 { 
255    self.status.channels256_297
256  }
257
258  #[getter]
259  fn channels298_319(&self) -> u32 { 
260    self.status.channels298_319
261  }
262
263  fn __repr__(&self) -> PyResult<String> {
264    Ok(format!("<PyO3Wrapper: {}>", self.status)) 
265  }
266}
267
268
269#[pyclass]
270#[pyo3(name="RunConfig")]
271pub struct PyRunConfig {
272  pub config : RunConfig
273}
274
275impl PyRunConfig {
276  pub fn set_config(&mut self, config : RunConfig) {
277    self.config = config
278  }
279}
280
281#[pymethods]
282impl PyRunConfig {
283  #[new]
284  fn new() -> Self {
285    let config =  RunConfig::new();
286    Self {
287      config
288    }
289  }
290
291  #[getter]
292  fn get_runid(&self) -> PyResult<u32> {
293    Ok(self.config.runid)
294  }
295  
296  #[setter]
297  fn set_runid(&mut self, runid: u32) -> PyResult<()> {
298    self.config.runid = runid;
299    Ok(())
300  }
301  
302  fn to_bytestream(&self) -> Vec<u8> {
303    self.config.to_bytestream()
304  }
305  
306  fn __repr__(&self) -> PyResult<String> {
307    Ok(format!("<PyO3Wrapper: {}>", self.config)) 
308  }
309}
310
311#[pyclass]
312#[pyo3(name="TofRunConfig")]
313pub struct PyTofRunConfig {
314  pub config : TofRunConfig
315}
316
317#[pymethods]
318impl PyTofRunConfig {
319
320  #[new]
321  fn new() -> Self {
322    let config =  TofRunConfig::new();
323    Self {
324      config
325    }
326  }
327
328  #[getter]
329  pub fn get_runtime(&self) -> Option<u32> {
330    self.config.runtime
331  }
332
333  #[setter]
334  pub fn set_runtime(&mut self, secs : u32) {
335    self.config.set_runtime(secs);
336  }
337  
338  fn to_bytestream(&self) -> Vec<u8> {
339    self.config.to_bytestream()
340  }
341  
342  fn __getitem__<'a>(&self, py: Python<'a>, key: &str) -> PyResult<Option<Bound<'a,PyAny>>> {  
343    match key {
344      "runtime"          => Ok(Some(self.config.runtime.into_pyobject(py).unwrap())),
345      _     => Err(PyKeyError::new_err(format!("Key '{}' not found", key)))
346    }
347  }
348
349  fn __setitem__(&mut self, key: &str, value: &Bound<'_, PyAny>) -> PyResult<()> {
350    match key {
351      "runtime" => {
352          self.config.active_fields |= 1;
353          self.config.runtime = Some(value.extract::<u32>()?);
354          Ok(())
355      }
356      _ => Err(PyKeyError::new_err(format!("Key '{}' not found", key))),
357    }
358  }
359  
360  fn __repr__(&self) -> PyResult<String> {
361    Ok(format!("<PyO3Wrapper: {}>", self.config)) 
362  }
363}
364
365#[pyclass]
366#[pyo3(name="TofRBConfig")]
367pub struct PyTofRBConfig {
368  pub config : TofRBConfig
369}
370
371#[pymethods]
372impl PyTofRBConfig {
373
374  #[new]
375  fn new() -> Self {
376    let config =  TofRBConfig::new();
377    Self {
378      config
379    }
380  }
381  
382  #[getter]
383  pub fn get_rb_moni_interval(&self) -> Option<u32> {
384    self.config.rb_moni_interval
385  }
386
387  #[setter]
388  pub fn set_rb_moni_interval(&mut self, secs : u32) {
389    self.config.set_rb_moni_interval(secs);
390  }
391  
392  #[getter]
393  pub fn get_pa_moni_every_x(&self) -> Option<u32> {
394    self.config.pa_moni_every_x
395  }
396
397  #[setter]
398  pub fn set_pa_moni_every_x(&mut self, secs : u32) {
399    self.config.set_pa_moni_every_x(secs);
400  }
401  
402  #[getter]
403  pub fn get_pb_moni_every_x(&self) -> Option<u32> {
404    self.config.pb_moni_every_x
405  }
406
407  #[setter]
408  pub fn set_pb_moni_every_x(&mut self, secs : u32) {
409    self.config.set_pb_moni_every_x(secs);
410  }
411  
412  #[getter]
413  pub fn get_ltb_moni_every_x(&self) -> Option<u32> {
414    self.config.ltb_moni_every_x
415  }
416
417  #[setter]
418  pub fn set_ltb_moni_every_x(&mut self, secs : u32) {
419    self.config.set_ltb_moni_every_x(secs);
420  }
421  
422  #[getter]
423  pub fn get_drs_deadtime_instead_fpga_temp(&self) -> Option<bool> {
424    self.config.drs_deadtime_instead_fpga_temp
425  }
426
427  #[setter]
428  pub fn set_drs_deadtime_instead_fpga_temp(&mut self, do_it : bool) {
429    self.config.set_drs_deadtime_instead_fpga_temp(do_it);
430  }
431  
432  fn to_bytestream(&self) -> Vec<u8> {
433    self.config.to_bytestream()
434  }
435  
436  fn __getitem__<'a>(&self, py: Python<'a>, key: &str) -> PyResult<Option<Bound<'a,PyAny>>> {  
437    match key {
438      "rb_moni_interval"  => Ok(Some(self.config.rb_moni_interval.into_pyobject(py).unwrap())),
439      "pa_moni_every_x"   => Ok(Some(self.config.pa_moni_every_x .into_pyobject(py).unwrap())),
440      "pb_moni_every_x"   => Ok(Some(self.config.pb_moni_every_x .into_pyobject(py).unwrap())),
441      "ltb_moni_every_x"  => Ok(Some(self.config.ltb_moni_every_x.into_pyobject(py).unwrap())),
442      "drs_deadtime_instead_fpga_temp" => Ok(Some(self.config.drs_deadtime_instead_fpga_temp.into_pyobject(py).unwrap())),
443      _                   => Err(PyKeyError::new_err(format!("Key '{}' not found", key)))
444    }
445  }
446
447  fn __setitem__(&mut self, key: &str, value: &Bound<'_, PyAny>) -> PyResult<()> {
448    match key {
449      "rb_moni_interval" => {
450        self.config.active_fields |= 1;
451        self.config.rb_moni_interval = Some(value.extract::<u32>()?);
452        Ok(())
453      }
454      "pa_moni_every_x" => {
455        self.config.active_fields |= 2;
456        self.config.pa_moni_every_x = Some(value.extract::<u32>()?);
457        Ok(())
458      }
459      "pb_moni_every_x" => {
460        self.config.active_fields |= 4;
461        self.config.pb_moni_every_x = Some(value.extract::<u32>()?);
462        Ok(())
463      }
464      "ltb_moni_every_x" => {
465        self.config.active_fields |= 8;
466        self.config.ltb_moni_every_x = Some(value.extract::<u32>()?);
467        Ok(())
468      }
469      "drs_deadtime_instead_fpga_temp" => {
470        self.config.active_fields |= 16;
471        self.config.drs_deadtime_instead_fpga_temp = Some(value.extract::<bool>()?);
472        Ok(())
473      }
474      _ => Err(PyKeyError::new_err(format!("Key '{}' not found", key))),
475    }
476  }
477  
478  fn __repr__(&self) -> PyResult<String> {
479    Ok(format!("<PyO3Wrapper: {}>", self.config)) 
480  }
481}
482
483#[pyclass]
484#[pyo3(name="DataPublisherConfig")]
485pub struct PyDataPublisherConfig {
486  pub config : DataPublisherConfig
487}
488
489#[pymethods]
490impl PyDataPublisherConfig {
491  #[new]
492  fn new() -> Self {
493    let config =  DataPublisherConfig::new();
494    Self {
495      config
496    }
497  }
498
499  #[getter]
500  pub fn get_mbytes_per_file(&self) -> Option<u16> {
501    self.config.mbytes_per_file
502  }
503
504  #[setter]
505  pub fn set_mbytes_per_file(&mut self, mbytes : u16) {
506    self.config.mbytes_per_file = Some(mbytes);
507  }
508  
509  #[getter]
510  pub fn get_discard_event_fraction(&self) -> Option<f32> {
511    self.config.discard_event_fraction
512  }
513
514  #[setter]
515  pub fn set_discard_event_fraction(&mut self, frac : f32) {
516    self.config.discard_event_fraction = Some(frac);
517  }
518  
519  #[getter]
520  pub fn get_send_mtb_event_packets(&self) -> Option<bool> {
521    self.config.send_mtb_event_packets
522  }
523
524  #[setter]
525  pub fn set_send_mtb_event_packets(&mut self, send : bool ) {
526    self.config.send_mtb_event_packets = Some(send);
527  }
528  
529  #[getter]
530  pub fn get_send_rbwaveform_packets(&self) -> Option<bool> {
531    self.config.send_rbwaveform_packets
532  }
533
534  #[setter]
535  pub fn set_send_rbwaveform_packets(&mut self, send : bool ) {
536    self.config.send_rbwaveform_packets = Some(send);
537  }
538  
539  #[getter]
540  pub fn get_send_rbwf_every_x_event(&self) -> Option<u32> {
541    self.config.send_rbwf_every_x_event
542  }
543
544  #[setter]
545  pub fn set_send_rbwf_every_x_event(&mut self, nevent : u32) {
546    self.config.send_rbwf_every_x_event = Some(nevent);
547  }
548  
549  #[getter]
550  pub fn get_send_tof_summary_packets(&self) -> Option<bool> {
551    self.config.send_tof_summary_packets
552  }
553
554  #[setter]
555  pub fn set_send_tof_summary_packets(&mut self, send : bool ) {
556    self.config.send_tof_summary_packets = Some(send);
557  }
558  
559  #[getter]
560  pub fn get_send_tof_event_packets(&self) -> Option<bool> {
561    self.config.send_tof_event_packets
562  }
563
564  #[setter]
565  pub fn set_send_tof_event_packets(&mut self, send : bool ) {
566    self.config.send_tof_event_packets = Some(send);
567  }
568  
569  #[getter]
570  pub fn get_hb_send_interval(&self) -> Option<u16> {
571    self.config.hb_send_interval
572  }
573
574  #[setter]
575  pub fn set_hb_send_interfal(&mut self, interv : u16) {
576    self.config.hb_send_interval = Some(interv)
577  }
578  
579  fn to_bytestream(&self) -> Vec<u8> {
580    self.config.to_bytestream()
581  }
582  
583  fn __getitem__<'a>(&self, py: Python<'a>, key: &str) -> PyResult<Option<Bound<'a,PyAny>>> {  
584    match key {
585      "mbytes_per_file"          => Ok(Some(self.config.mbytes_per_file         .into_pyobject(py).unwrap())),
586      "discard_event_fraction"   => Ok(Some(self.config.discard_event_fraction  .into_pyobject(py).unwrap())),
587      "send_mtb_event_packets"   => Ok(Some(self.config.send_mtb_event_packets  .into_pyobject(py).unwrap())),
588      "send_rbwaveform_packets"  => Ok(Some(self.config.send_rbwaveform_packets .into_pyobject(py).unwrap())),
589      "send_rbwf_every_x_event"  => Ok(Some(self.config.send_rbwf_every_x_event .into_pyobject(py).unwrap())),
590      "send_tof_summary_packets" => Ok(Some(self.config.send_tof_summary_packets.into_pyobject(py).unwrap())),
591      "send_tof_event_packets"   => Ok(Some(self.config.send_tof_event_packets  .into_pyobject(py).unwrap())),
592      "hb_send_interval"         => Ok(Some(self.config.hb_send_interval        .into_pyobject(py).unwrap())),
593      _     => Err(PyKeyError::new_err(format!("Key '{}' not found", key)))
594    }
595  }
596
597  fn __setitem__(&mut self, key: &str, value: &Bound<'_, PyAny>) -> PyResult<()> {
598    match key {
599      "mbytes_per_file" => {
600          self.config.active_fields |= 1;
601          self.config.mbytes_per_file = Some(value.extract::<u16>()?);
602          Ok(())
603      }
604      "discard_event_fraction" => {
605          self.config.active_fields |= 2;
606          self.config.discard_event_fraction = Some(value.extract::<f32>()?);
607          Ok(())
608      }
609      "send_mtb_event_packets" => {
610          self.config.active_fields |= 4;
611          self.config.send_mtb_event_packets = Some(value.extract::<bool>()?);
612          Ok(())
613      }
614      "send_rbwaveform_packets" => {
615          self.config.active_fields |= 8;
616          self.config.send_rbwaveform_packets = Some(value.extract::<bool>()?);
617          Ok(())
618      }
619      "send_rbwf_every_x_event" => {
620          self.config.active_fields |= 16;
621          self.config.send_rbwf_every_x_event = Some(value.extract::<u32>()?);
622          Ok(())
623      }
624      "send_tof_summary_packets" => {
625          self.config.active_fields |= 32;
626          self.config.send_tof_summary_packets = Some(value.extract::<bool>()?);
627          Ok(())
628      }
629      "send_tof_event_packets" => {
630          self.config.active_fields |= 64;
631          self.config.send_tof_event_packets = Some(value.extract::<bool>()?);
632          Ok(())
633      }
634      "hb_send_interval" => {
635          self.config.active_fields |= 128;
636          self.config.hb_send_interval = Some(value.extract::<u16>()?);
637          Ok(())
638      }
639      _ => Err(PyKeyError::new_err(format!("Key '{}' not found", key))),
640    }
641  }
642  
643  fn __repr__(&self) -> PyResult<String> {
644    Ok(format!("<PyO3Wrapper: {}>", self.config)) 
645  }
646}
647
648#[pyclass]
649#[pyo3(name="TriggerConfig")]
650pub struct PyTriggerConfig {
651  pub config : TriggerConfig
652}
653
654#[pymethods]
655impl PyTriggerConfig {
656  #[new]
657  fn new() -> Self {
658    let cfg =  TriggerConfig::new();
659    Self {
660      config : cfg
661    }
662  }
663
664  #[getter] 
665  fn get_prescale(&self) -> Option<f32> {
666    self.config.prescale
667  }
668  
669  #[setter]
670  fn set_prescale(&mut self, prescale: f32) -> PyResult<()> {
671    self.config.set_prescale (prescale);
672    Ok(())
673  }
674
675  #[getter] 
676  fn get_gaps_trigger_use_beta(&self) -> Option<bool> {
677    self.config.gaps_trigger_use_beta
678  }
679  
680  #[setter]
681  fn set_gaps_trigger_use_beta(&mut self, gaps_trigger_use_beta: bool) -> PyResult<()> {
682    self.config.set_gaps_trigger_use_beta(gaps_trigger_use_beta);
683    Ok(())
684  }
685
686  #[getter] 
687  fn get_trigger_type(&self) -> Option<TriggerType> {
688    self.config.trigger_type
689  }
690
691  #[setter]
692  fn set_trigger_type(&mut self, trigger_type: TriggerType) -> PyResult<()> {
693    self.config.set_trigger_type(trigger_type);
694    Ok(())
695  }
696  
697  #[getter]
698  fn get_use_combo_trigger(&self) -> Option<bool> {
699    self.config.use_combo_trigger 
700  }
701  #[setter]
702  fn set_use_combo_trigger(&mut self, combo : bool) {
703    self.config.set_use_combo_trigger(combo);
704  }
705  #[getter]
706  fn get_combo_trigger_type(&self) -> Option<TriggerType> {
707    self.config.combo_trigger_type
708  }
709  #[setter]
710  fn set_combo_trigger_type(&mut self, combo_trigger_type : TriggerType) {
711    self.config.set_combo_trigger_type(combo_trigger_type);
712  }
713  #[getter]
714  fn get_combo_trigger_prescale(&self) -> Option<f32> {
715    self.config.combo_trigger_prescale
716  }
717  #[setter]
718  fn set_combo_trigger_prescale(&mut self, prescale : f32) {
719    self.config.set_combo_trigger_prescale(prescale);
720  }
721  #[getter]
722  fn get_trace_suppression(&self) -> Option<bool> {
723    self.config.trace_suppression
724  }
725  #[setter]
726  fn set_trace_suppression(&mut self, tsup : bool) {
727    self.config.set_trace_suppression(tsup);
728  }
729  #[getter]
730  fn get_mtb_moni_interval(&mut self) -> Option<u16> {
731    self.config.mtb_moni_interval
732  }
733  #[setter]
734  fn set_mtb_moni_interval(&mut self, moni_int : u16) {
735    self.config.set_mtb_moni_interval(moni_int);
736  }
737  #[getter]
738  fn get_tiu_ignore_busy(&self) -> Option<bool> {
739    self.config.tiu_ignore_busy
740  }
741
742  #[setter]
743  fn set_tiu_ignore_busy(&mut self, ignore_busy : bool) {
744    self.config.set_tiu_ignore_busy(ignore_busy);
745  }
746  #[getter]
747  fn get_hb_send_interval(&self) -> Option<u16> {
748    self.config.hb_send_interval
749  }
750
751  fn to_bytestream(&self) -> Vec<u8> {
752    self.config.to_bytestream()
753  }
754
755  #[setter]
756  fn set_hb_send_interval(&mut self, hb_int :  Option<u16>) {
757    self.config.hb_send_interval = hb_int;
758  }
759
760  fn __getitem__<'a>(&self, py: Python<'a>, key: &str) -> PyResult<Option<Bound<'a,PyAny>>> {  
761    match key {
762      "gaps_trigger_use_beta"  => Ok(Some(self.config.gaps_trigger_use_beta .into_pyobject(py).unwrap())),
763      "prescale"               => Ok(Some(self.config.prescale              .into_pyobject(py).unwrap())),
764      "trigger_type"           => Ok(Some(self.config.trigger_type          .into_pyobject(py).unwrap())),
765      "use_combo_trigger"      => Ok(Some(self.config.use_combo_trigger     .into_pyobject(py).unwrap())),
766      "combo_trigger_type"     => Ok(Some(self.config.combo_trigger_type    .into_pyobject(py).unwrap())),
767      "combo_trigger_prescale" => Ok(Some(self.config.combo_trigger_prescale.into_pyobject(py).unwrap())),
768      "trace_suppression"      => Ok(Some(self.config.trace_suppression     .into_pyobject(py).unwrap())),
769      "mtb_moni_interval"      => Ok(Some(self.config.mtb_moni_interval     .into_pyobject(py).unwrap())),
770      "tiu_ignore_busy"        => Ok(Some(self.config.tiu_ignore_busy       .into_pyobject(py).unwrap())),
771      "hb_send_interval"       => Ok(Some(self.config.hb_send_interval      .into_pyobject(py).unwrap())),
772      _     => Err(PyKeyError::new_err(format!("Key '{}' not found", key)))
773    }
774  }
775
776  fn __setitem__(&mut self, key: &str, value: &Bound<'_, PyAny>) -> PyResult<()> {
777    match key {
778      "gaps_trigger_use_beta" => {
779          self.config.active_fields |= 1;
780          self.config.gaps_trigger_use_beta = Some(value.extract::<bool>()?);
781          Ok(())
782      }
783      "prescale" => {
784          self.config.active_fields |= 2;
785          self.config.prescale = Some(value.extract::<f32>()?);
786          Ok(())
787      }
788      "trigger_type" => {
789          self.config.active_fields |= 4;
790          self.config.trigger_type = Some(value.extract::<TriggerType>()?);
791          Ok(())
792      }
793      "use_combo_trigger" => {
794          self.config.active_fields |= 8;
795          self.config.use_combo_trigger = Some(value.extract::<bool>()?);
796          Ok(())
797      }
798      "combo_trigger_type" => {
799          self.config.active_fields |= 16;
800          self.config.combo_trigger_type = Some(value.extract::<TriggerType>()?);
801          Ok(())
802      }
803      "combo_trigger_prescale" => {
804          self.config.active_fields |= 32;
805          self.config.combo_trigger_prescale = Some(value.extract::<f32>()?);
806          Ok(())
807      }
808      "trace_suppression" => {
809          self.config.active_fields |= 64;
810          self.config.trace_suppression = Some(value.extract::<bool>()?);
811          Ok(())
812      }
813      "mtb_moni_interval" => {
814          self.config.active_fields |= 128;
815          self.config.mtb_moni_interval = Some(value.extract::<u16>()?);
816          Ok(())
817      }
818      "tiu_ignore_busy" => {
819          self.config.active_fields |= 256;
820          self.config.tiu_ignore_busy = Some(value.extract::<bool>()?);
821          Ok(())
822      }
823      "hb_send_interval" => {
824          self.config.active_fields |= 512;
825          self.config.hb_send_interval = Some(value.extract::<u16>()?);
826          Ok(())
827      }
828      _ => Err(PyKeyError::new_err(format!("Key '{}' not found", key))),
829    }
830  }
831
832  fn __repr__(&self) -> PyResult<String> {
833    Ok(format!("<PyO3Wrapper: {}>", self.config)) 
834  }
835
836}
837
838
839#[pyclass]
840#[pyo3(name="TOFEventBuilderConfig")]
841
842pub struct PyTOFEventBuilderConfig{
843  pub config : TOFEventBuilderConfig
844}
845
846//impl PyTOFEventBuilderConfig {
847//  pub fn set_config(&mut self, cfg : TOFEventBuilderConfig) {
848//    self.config = cfg;
849//  }
850//}
851
852#[pymethods]
853impl PyTOFEventBuilderConfig{
854  #[new]
855  fn new() -> Self {
856    let cfg: TOFEventBuilderConfig = TOFEventBuilderConfig::new();
857    Self {
858      config : cfg
859    }
860  }
861  
862  #[getter]
863  fn get_greediness(&self) -> Option<u8> {
864    self.config.greediness
865  }
866
867  #[setter]
868  fn set_greediness(&mut self, greediness: u8) -> PyResult<()> {
869    self.config.set_greediness(greediness);
870    Ok(())
871  }
872  
873  // wait for num. RB
874  #[getter]
875  fn get_wait_nrb(&self) -> Option<u8> {
876    self.config.wait_nrb
877  }
878  #[setter]
879  fn set_wait_nrb(&mut self, wait_nrb: u8) -> PyResult<()> {
880    self.config.set_wait_nrb(wait_nrb);
881    Ok(())
882  }
883  // Cache size
884  #[getter]
885  fn get_cachesize(&self) -> Option<u32> {
886    self.config.cachesize
887  }
888  #[setter]
889  fn set_cachesize(&mut self, cachesize: u32) -> PyResult<()> {
890    self.config.set_cachesize(cachesize);
891    Ok(())
892  }
893  // Num. MTB events per loop
894  #[getter]
895  fn get_n_mte_per_loop(&self) -> Option<u32> {
896    self.config.n_mte_per_loop
897  }
898  #[setter]
899  fn set_n_mte_per_loop(&mut self, n_mte_per_loop: u32) -> PyResult<()> {
900    self.config.set_n_mte_per_loop(n_mte_per_loop);
901    Ok(())
902  }
903  // Num. RB events per loop
904  #[getter]
905  fn get_n_rbe_per_loop(&self) -> Option<u32> {
906    self.config.n_rbe_per_loop
907  }
908  #[setter]
909  fn set_n_rbe_per_loop(&mut self, n_rbe_per_loop: u32) -> PyResult<()> {
910    self.config.set_n_rbe_per_loop(n_rbe_per_loop);
911    Ok(())
912  }  
913  // TOF Event timescale window
914  #[getter]
915  fn get_te_timeout_sec(&self) -> Option<u32> {
916    self.config.te_timeout_sec
917  }
918  #[setter]
919  fn set_te_timeout_sec(&mut self, te_timeout_sec: u32) -> PyResult<()> {
920    self.config.set_te_timeout_sec(te_timeout_sec);
921    Ok(())
922  }
923  // Sort events
924  #[getter]
925  fn get_sort_events(&self) -> Option<bool> {
926    self.config.sort_events
927  }
928  #[setter]
929  fn set_sort_events(&mut self, sort_events: bool) -> PyResult<()> {
930    self.config.set_sort_events(sort_events);
931    Ok(())
932  }
933  // build strategy
934  #[getter] 
935  fn get_build_strategy(&self) -> Option<BuildStrategy> {
936    self.config.build_strategy
937  }
938
939  #[setter]
940  fn set_build_strategy(&mut self, build_strategy: BuildStrategy) -> PyResult<()> {
941    self.config.set_build_strategy(build_strategy);
942    Ok(())
943  }
944  
945  #[getter] 
946  fn get_hb_send_interval(&self) -> Option<u16> {
947    self.config.hb_send_interval
948  }
949
950  #[setter]
951  fn set_hb_send_interval(&mut self, hb_send_interval: u16) -> PyResult<()> {
952    self.config.set_hb_send_interval(hb_send_interval);
953    Ok(())
954  }
955
956  fn to_bytestream(&self) -> Vec<u8> {
957    self.config.to_bytestream()
958  }
959 
960  fn __getitem__<'a>(&self, py: Python<'a>, key: &str) -> PyResult<Option<Bound<'a,PyAny>>> {  
961    match key {
962      "cachesize"        => Ok(Some(self.config.cachesize       .into_pyobject(py).unwrap())),
963      "n_mte_per_loop"   => Ok(Some(self.config.n_mte_per_loop  .into_pyobject(py).unwrap())),
964      "n_rbe_per_loop"   => Ok(Some(self.config.n_rbe_per_loop  .into_pyobject(py).unwrap())),
965      "te_timeout_sec"   => Ok(Some(self.config.te_timeout_sec  .into_pyobject(py).unwrap())),
966      "sort_events"      => Ok(Some(self.config.sort_events     .into_pyobject(py).unwrap())),
967      "build_strategy"   => Ok(Some(self.config.build_strategy  .into_pyobject(py).unwrap())),
968      "wait_nrb"         => Ok(Some(self.config.wait_nrb        .into_pyobject(py).unwrap())),
969      "greediness"       => Ok(Some(self.config.greediness      .into_pyobject(py).unwrap())),
970      "hb_send_interval" => Ok(Some(self.config.hb_send_interval.into_pyobject(py).unwrap())),
971      _     => Err(PyKeyError::new_err(format!("Key '{}' not found", key)))
972    }
973  }
974
975  fn __setitem__(&mut self, key: &str, value: &Bound<'_, PyAny>) -> PyResult<()> {
976    match key {
977      "cachesize" => {
978          self.config.active_fields |= 1;
979          self.config.cachesize = Some(value.extract::<u32>()?);
980          Ok(())
981      }
982      "n_mte_per_loop" => {
983          self.config.active_fields |= 2;
984          self.config.n_mte_per_loop = Some(value.extract::<u32>()?);
985          Ok(())
986      }
987      "n_rbe_per_loop" => {
988          self.config.active_fields |= 4;
989          self.config.n_rbe_per_loop = Some(value.extract::<u32>()?);
990          Ok(())
991      }
992      "te_timeout_sec" => {
993          self.config.active_fields |= 8;
994          self.config.te_timeout_sec = Some(value.extract::<u32>()?);
995          Ok(())
996      }
997      "sort_events" => {
998          self.config.active_fields |= 16;
999          self.config.sort_events = Some(value.extract::<bool>()?);
1000          Ok(())
1001      }
1002      "build_strategy" => {
1003          self.config.active_fields |= 32;
1004          self.config.build_strategy = Some(value.extract::<BuildStrategy>()?);
1005          Ok(())
1006      }
1007      "wait_nrb" => {
1008          self.config.active_fields |= 64;
1009          self.config.wait_nrb = Some(value.extract::<u8>()?);
1010          Ok(())
1011      }
1012      "greediness" => {
1013          self.config.active_fields |= 128;
1014          self.config.greediness = Some(value.extract::<u8>()?);
1015          Ok(())
1016      }
1017      "hb_send_interval" => {
1018          self.config.active_fields |= 256;
1019          self.config.hb_send_interval = Some(value.extract::<u16>()?);
1020          Ok(())
1021      }
1022      _ => Err(PyKeyError::new_err(format!("Key '{}' not found", key))),
1023    }
1024  }
1025
1026  fn __repr__(&self) -> PyResult<String> {
1027    Ok(format!("<PyO3Wrapper: {}>", self.config)) 
1028  }
1029
1030}
1031
1032#[pyclass]
1033#[pyo3(name="AnalysisEngineConfig")]
1034pub struct PyAnalysisEngineConfig{
1035  pub config : AnalysisEngineConfig
1036}
1037
1038impl PyAnalysisEngineConfig {
1039  pub fn set_config(&mut self, cfg : AnalysisEngineConfig) {
1040    self.config = cfg;
1041  }
1042}
1043
1044#[pymethods]
1045impl PyAnalysisEngineConfig {
1046  #[new]
1047  fn new() -> Self {
1048    let cfg: AnalysisEngineConfig = AnalysisEngineConfig::new();
1049    Self {
1050      config : cfg
1051    }
1052  }
1053  // beginning with f32s
1054  // integration start
1055  #[getter]
1056  fn get_integration_start(&self) -> PyResult<f32> {
1057    Ok(self.config.integration_start)
1058  }
1059
1060  #[setter]
1061  fn set_integration_start(&mut self, integration_start: f32) -> PyResult<()> {
1062    self.config.integration_start = integration_start;
1063    Ok(())
1064  }
1065  // integration window
1066  #[getter]
1067  fn get_integration_window(&self) -> PyResult<f32> {
1068    Ok(self.config.integration_window)
1069  }
1070
1071  #[setter]
1072  fn set_integration_window(&mut self, integration_window: f32) -> PyResult<()> {
1073    self.config.integration_window = integration_window;
1074    Ok(())
1075  } 
1076  // pedestal threshold
1077  #[getter]
1078  fn get_pedestal_thresh(&self) -> PyResult<f32> {
1079    Ok(self.config.pedestal_thresh)
1080  }
1081
1082  #[setter]
1083  fn set_pedestal_thresh(&mut self, pedestal_thresh: f32) -> PyResult<()> {
1084    self.config.pedestal_thresh = pedestal_thresh;
1085    Ok(())
1086  }
1087  //Peakfinder time start
1088  #[getter]
1089  fn get_find_pks_t_start(&self) -> PyResult<f32> {
1090    Ok(self.config.find_pks_t_start)
1091  }
1092
1093  #[setter]
1094  fn set_find_pks_t_start(&mut self, find_pks_t_start: f32) -> PyResult<()> {
1095    self.config.find_pks_t_start = find_pks_t_start;
1096    Ok(())
1097  }
1098  //Peakfinder time window
1099  #[getter]
1100  fn get_find_pks_t_window(&self) -> PyResult<f32> {
1101    Ok(self.config.find_pks_t_window)
1102  }
1103
1104  #[setter]
1105  fn set_find_pks_t_window(&mut self, find_pks_t_window: f32) -> PyResult<()> {
1106    self.config.find_pks_t_window = find_pks_t_window;
1107    Ok(())
1108  }
1109  //Peakfinder threshold
1110  #[getter]
1111  fn get_find_pks_thresh(&self) -> PyResult<f32> {
1112    Ok(self.config.find_pks_thresh)
1113  }
1114
1115  #[setter]
1116  fn set_find_pks_thresh(&mut self, find_pks_thresh: f32) -> PyResult<()> {
1117    self.config.find_pks_thresh = find_pks_thresh;
1118    Ok(())
1119  }
1120  // CFD fraction
1121  #[getter]
1122  fn get_cfd_fraction(&self) -> PyResult<f32> {
1123    Ok(self.config.cfd_fraction)
1124  }
1125
1126  #[setter]
1127  fn set_cfd_fraction(&mut self, cfd_fraction: f32) -> PyResult<()> {
1128    self.config.cfd_fraction = cfd_fraction;
1129    Ok(())
1130  }
1131  //moving on to the bool
1132  // use zscore?
1133  #[getter] 
1134  fn get_use_zscore(&self) -> PyResult<bool> {
1135    Ok(self.config.use_zscore)
1136  }
1137
1138  #[setter]
1139  fn set_use_zscore(&mut self, use_zscore: bool) -> PyResult<()> {
1140    self.config.use_zscore = use_zscore;
1141    Ok(())
1142  }
1143  //finally, usize
1144  // pedestal start bin
1145  #[getter] 
1146  fn get_pedestal_begin_bin(&self) -> PyResult<usize> {
1147    Ok(self.config.pedestal_begin_bin)
1148  }
1149
1150  #[setter]
1151  fn set_pedestal_begin_bin(&mut self, pedestal_begin_bin: usize) -> PyResult<()> {
1152    self.config.pedestal_begin_bin = pedestal_begin_bin;
1153    Ok(())
1154  }
1155  // pedestal bin window
1156  #[getter] 
1157  fn get_pedestal_win_bins(&self) -> PyResult<usize> {
1158    Ok(self.config.pedestal_win_bins)
1159  }
1160
1161  #[setter]
1162  fn set_pedestal_win_bins(&mut self, pedestal_win_bins: usize) -> PyResult<()> {
1163    self.config.pedestal_win_bins = pedestal_win_bins;
1164    Ok(())
1165  }
1166  // min peak size
1167  #[getter] 
1168  fn get_min_oeak_size(&self) -> PyResult<usize> {
1169    Ok(self.config.min_peak_size)
1170  }
1171
1172  #[setter]
1173  fn set_min_peak_size(&mut self, min_peak_size: usize) -> PyResult<()> {
1174    self.config.min_peak_size = min_peak_size;
1175    Ok(())
1176  }
1177  // max peaks
1178  #[getter] 
1179  fn get_max_peaks(&self) -> PyResult<usize> {
1180    Ok(self.config.max_peaks)
1181  }
1182
1183  #[setter]
1184  fn set_max_peaks(&mut self, max_peaks: usize) -> PyResult<()> {
1185    self.config.max_peaks = max_peaks;
1186    Ok(())
1187  }
1188  
1189  fn __repr__(&self) -> PyResult<String> {
1190    Ok(format!("<PyO3Wrapper: {}>", self.config)) 
1191  }
1192}
1193
1194#[pyclass]
1195#[pyo3(name="TofCommand")]
1196pub struct PyTofCommand {
1197  pub command : TofCommandV2
1198}
1199
1200#[pymethods]
1201impl PyTofCommand {
1202  #[new]
1203  fn new() -> Self {
1204    let cmd =  TofCommandV2::new();
1205    Self {
1206      command : cmd
1207    }
1208  }
1209
1210  #[getter]
1211  fn get_command_code(&mut self) -> TofCommandCode {
1212    self.command.command_code
1213  }
1214  
1215  #[setter]
1216  fn set_command_code(&mut self, command_code : TofCommandCode) {
1217    self.command.command_code = command_code;
1218  }
1219
1220  /// Pack myself nicely in a TofPacket and 
1221  /// serialize myself
1222  ///
1223  /// Can be used to interface with BFSW/GSE
1224  /// systems
1225  fn wrap_n_pack(&self) -> Vec<u8> {
1226    self.pack().to_bytestream()
1227  }
1228
1229  /// An explicit getter for the 
1230  /// command code, to interface 
1231  /// with BFSW/GSE systems
1232  fn get_cc_u8(&self) -> u8 {
1233    self.command.command_code as u8
1234  }
1235
1236  fn to_bytestream(&self) -> Vec<u8> {
1237    self.command.to_bytestream()
1238  }
1239  
1240  fn from_tofpacket(&mut self, packet : &PyTofPacket) -> PyResult<()> {
1241    let tp = packet.get_tp();
1242    match tp.unpack::<TofCommandV2>() {
1243      Ok(cmd) => {
1244        self.command = cmd;
1245        return Ok(());
1246      }
1247      Err(err) => {
1248        let err_msg = format!("Unable to unpack TofPacket! {err}");
1249        return Err(PyIOError::new_err(err_msg));
1250      }
1251    }
1252  }
1253
1254  fn pack(&self) -> PyTofPacket {
1255    let packet   = self.command.pack();
1256    let mut pytp = PyTofPacket::new();
1257    pytp.set_tp(packet);
1258    pytp
1259  }
1260
1261  //fn change_next_runconfig(&mut self, key_values : Vec<String>) {
1262  //  self.command = TofCommandV2::forge_changerunconfig(key_values);
1263  //}
1264
1265  fn __repr__(&self) -> PyResult<String> {
1266    Ok(format!("<PyO3Wrapper: {}>", self.command)) 
1267  }
1268}
1269
1270#[pyclass]
1271#[pyo3(name="RBCalibration")]
1272pub struct PyRBCalibration {
1273  pub cali : RBCalibrations,
1274}
1275
1276#[pymethods]
1277impl PyRBCalibration {
1278  #[new]
1279  fn new() -> Self {
1280    let cali = RBCalibrations::new(0);
1281    Self {
1282      cali,
1283    }
1284  }
1285
1286  #[getter]
1287  fn rb_id(&self) -> u8 {
1288    self.cali.rb_id
1289  }
1290
1291  #[getter]
1292  fn d_v(&self) -> f32 {
1293    self.cali.d_v
1294  }
1295
1296  #[getter]
1297  fn vcal_data(&self) -> Vec<PyRBEvent> {
1298    let mut events = Vec::<PyRBEvent>::with_capacity(1000);
1299    for ev in &self.cali.vcal_data {
1300      let mut pyev = PyRBEvent::new();
1301      pyev.set_event(ev.clone());
1302      events.push(pyev);
1303    }
1304    events
1305  }
1306  
1307  #[getter]
1308  fn tcal_data(&self) -> Vec<PyRBEvent> {
1309    let mut events = Vec::<PyRBEvent>::with_capacity(1000);
1310    for ev in &self.cali.tcal_data {
1311      let mut pyev = PyRBEvent::new();
1312      pyev.set_event(ev.clone());
1313      events.push(pyev);
1314    }
1315    events
1316  }
1317  
1318  #[getter]
1319  fn noi_data(&self) -> Vec<PyRBEvent> {
1320    let mut events = Vec::<PyRBEvent>::with_capacity(1000);
1321    for ev in &self.cali.noi_data {
1322      let mut pyev = PyRBEvent::new();
1323      pyev.set_event(ev.clone());
1324      events.push(pyev);
1325    }
1326    events
1327  }
1328 
1329  #[getter]
1330  fn v_offsets<'_py>(&self, py: Python<'_py>) -> PyResult<Bound<'_py, PyArray2<f32>>> {  
1331    let mut data = Vec::<Vec<f32>>::with_capacity(9);
1332    for ch in 0..9 {
1333      data.push(self.cali.v_offsets[ch].to_vec());
1334    }
1335    let pyarray = PyArray::from_vec2(py, &data).unwrap();
1336    Ok(pyarray)
1337  }
1338  
1339  #[getter]
1340  fn v_dips<'_py>(&self, py: Python<'_py>) -> PyResult<Bound<'_py, PyArray2<f32>>> {  
1341    let mut data = Vec::<Vec<f32>>::with_capacity(9);
1342    for ch in 0..9 {
1343      data.push(self.cali.v_dips[ch].to_vec());
1344    }
1345    let pyarray = PyArray::from_vec2(py, &data).unwrap();
1346    Ok(pyarray)
1347  }
1348  
1349  #[getter]
1350  fn v_inc<'_py>(&self, py: Python<'_py>) -> PyResult<Bound<'_py, PyArray2<f32>>> {  
1351    let mut data = Vec::<Vec<f32>>::with_capacity(9);
1352    for ch in 0..9 {
1353      data.push(self.cali.v_inc[ch].to_vec());
1354    }
1355    let pyarray = PyArray::from_vec2(py, &data).unwrap();
1356    Ok(pyarray)
1357  }
1358  
1359  #[getter]
1360  fn tbin<'_py>(&self, py: Python<'_py>) -> PyResult<Bound<'_py, PyArray2<f32>>> {  
1361    let mut data = Vec::<Vec<f32>>::with_capacity(9);
1362    for ch in 0..9 {
1363      data.push(self.cali.tbin[ch].to_vec());
1364    }
1365    let pyarray = PyArray::from_vec2(py, &data).unwrap();
1366    Ok(pyarray)
1367  }
1368
1369  /// Load the calibration from a file with a 
1370  /// TofPacket of type RBCalibration in it
1371  ///
1372  /// # Arguments:
1373  ///
1374  /// * filename     : File with a TofPacket of type RBCalibration in it
1375  /// * discard_data : Throw away event data after loading
1376  #[pyo3(signature = (filename, discard_data = true))]
1377  fn from_file(&mut self, filename : String, discard_data : bool) -> PyResult<()> {
1378    let cali = RBCalibrations::from_file(filename, discard_data);
1379    match cali {
1380      Ok(c) => {
1381        self.cali = c;
1382      },
1383      Err(err) => {
1384        return Err(PyValueError::new_err(err.to_string()));
1385      }
1386    }
1387    Ok(())
1388  }
1389  
1390  fn __repr__(&self) -> PyResult<String> {
1391    Ok(format!("<PyO3Wrapper: {}>", self.cali)) 
1392  }
1393}
1394  
1395//#[getter]
1396//fn spike_cleaning_all_channel<'_py>(&self, py: Python<'_py>) -> PyResult<Bound<'_py, PyArray2<f32>>> {  
1397//  let mut data = Vec::<Vec<f32>>::with_capacity(9);
1398//  for ch in 0..9 {
1399//    data.push(self.cali.tbin[ch].to_vec());
1400//  }
1401//  let pyarray = PyArray::from_vec2(py, &data).unwrap();
1402//  Ok(pyarray)
1403//}
1404#[pyclass]
1405#[pyo3(name="PAMoniData")]
1406pub struct PyPAMoniData {
1407  moni : PAMoniData
1408}
1409
1410#[pymethods]
1411impl PyPAMoniData {
1412  #[new]
1413  fn new() -> Self {
1414    Self {
1415      moni : PAMoniData::new()
1416    }
1417  }
1418  
1419  fn from_tofpacket(&mut self, packet : &PyTofPacket) -> PyResult<()> {
1420    let tp = packet.get_tp();
1421    match tp.unpack::<PAMoniData>() {
1422      Ok(moni) => {
1423        self.moni = moni;
1424        return Ok(());
1425      }
1426      Err(err) => {
1427        let err_msg = format!("Unable to unpack TofPacket! {err}");
1428        return Err(PyIOError::new_err(err_msg));
1429      }
1430    }
1431  }
1432  
1433  #[getter]
1434  fn board_id(&self) ->  u8 {
1435    self.moni.board_id
1436  }
1437
1438  /// The temperature for the 16 preamp channels 
1439  #[getter]
1440  fn temps(&self) -> [f32;16] {
1441    self.moni.temps
1442  }
1443
1444  /// Pramp bias voltages (mV) for the 16 channels
1445  #[getter]
1446  fn biases(&self) -> [f32;16] {
1447    self.moni.biases
1448  }
1449  
1450  fn __repr__(&self) -> PyResult<String> {
1451    Ok(format!("<PyO3Wrapper: {}>", self.moni)) 
1452  }
1453
1454  fn keys(&self) -> Vec<&'static str> {
1455    PAMoniData::keys()
1456  }
1457
1458  /// Access the (data) members by name
1459  fn get(&self, varname : &str) -> PyResult<f32> {
1460    match self.moni.get(varname) {
1461      None => {
1462        let err_msg = format!("LTBMoniData does not have a key with name {}! See RBmoniData.keys() for a list of available keys!", varname);
1463        return Err(PyKeyError::new_err(err_msg));
1464      }
1465      Some(val) => {
1466        return Ok(val)
1467      }
1468    }
1469  }
1470}
1471
1472#[pyclass]
1473#[pyo3(name="PAMoniSeries")]
1474pub struct PyPAMoniSeries {
1475  pamoniseries : PAMoniDataSeries,
1476}
1477
1478#[pymethods]
1479impl PyPAMoniSeries {
1480  #[new]
1481  fn new() -> Self {
1482    let pamoniseries = PAMoniDataSeries::new();
1483    Self {
1484      pamoniseries,
1485    }
1486  }
1487  
1488  fn from_file(&mut self, filename : String) -> PyResult<PyDataFrame> {
1489    let mut reader = TofPacketReader::new(filename);
1490    reader.filter = PacketType::PAMoniData;
1491    for tp in reader {
1492      if let Ok(moni) =  tp.unpack::<PAMoniData>() {
1493        self.pamoniseries.add(moni);
1494      }
1495    }
1496    match self.pamoniseries.get_dataframe() {
1497      Ok(df) => {
1498        let pydf = PyDataFrame(df);
1499        return Ok(pydf);
1500      },
1501      Err(err) => {
1502        return Err(PyValueError::new_err(err.to_string()));
1503      }
1504    }
1505  }
1506}
1507
1508#[pyclass]
1509#[pyo3(name="PBMoniData")]
1510pub struct PyPBMoniData {
1511  moni : PBMoniData,
1512}
1513
1514#[pymethods]
1515impl PyPBMoniData {
1516  #[new]
1517  fn new() -> Self {
1518    Self {
1519      moni : PBMoniData::new()
1520    }
1521  }
1522  
1523  fn from_tofpacket(&mut self, packet : &PyTofPacket) -> PyResult<()> {
1524    let tp = packet.get_tp();
1525    match tp.unpack::<PBMoniData>() {
1526      Ok(moni) => {
1527        self.moni = moni;
1528        return Ok(());
1529      }
1530      Err(err) => {
1531        let err_msg = format!("Unable to unpack TofPacket! {err}");
1532        return Err(PyIOError::new_err(err_msg));
1533      }
1534    }
1535  }
1536  
1537  #[getter]
1538  fn board_id(&self) -> u8 {
1539    self.moni.board_id
1540  }
1541
1542  #[getter]
1543  fn p3v6_preamp_vcp(&self) -> [f32; 3] {
1544    self.moni.p3v6_preamp_vcp
1545  }
1546  
1547  #[getter]
1548  fn n1v6_preamp_vcp(&self) -> [f32; 3] {
1549    self.moni.n1v6_preamp_vcp
1550  }
1551  
1552  #[getter]
1553  fn p3v4f_ltb_vcp(&self) -> [f32; 3] {
1554    self.moni.p3v4f_ltb_vcp
1555  }
1556  
1557  #[getter]
1558  fn p3v4d_ltb_vcp(&self) -> [f32; 3] {
1559    self.moni.p3v4d_ltb_vcp
1560  }
1561  
1562  #[getter]
1563  fn p3v6_ltb_vcp(&self) -> [f32; 3] {
1564    self.moni.p3v6_ltb_vcp
1565  }
1566  
1567  #[getter]
1568  fn n1v6_ltb_vcp(&self) -> [f32; 3] {
1569    self.moni.n1v6_ltb_vcp
1570  }
1571  
1572  #[getter]
1573  fn pds_temp(&self) -> f32 {  
1574    self.moni.pds_temp
1575  }
1576  #[getter]
1577  fn pas_temp(&self) -> f32 {
1578    self.moni.pas_temp
1579  }
1580  #[getter]
1581  fn nas_temp(&self) -> f32 {
1582    self.moni.nas_temp
1583  }
1584
1585  #[getter]
1586  fn shv_temp(&self) -> f32 {
1587    self.moni.shv_temp
1588  }
1589
1590  fn __repr__(&self) -> PyResult<String> {
1591    Ok(format!("<PyO3Wrapper: {}>", self.moni)) 
1592  }
1593
1594  fn keys(&self) -> Vec<&'static str> {
1595    PBMoniData::keys()
1596  }
1597
1598  /// Access the (data) members by name
1599  fn get(&self, varname : &str) -> PyResult<f32> {
1600    match self.moni.get(varname) {
1601      None => {
1602        let err_msg = format!("LTBMoniData does not have a key with name {}! See RBmoniData.keys() for a list of available keys!", varname);
1603        return Err(PyKeyError::new_err(err_msg));
1604      }
1605      Some(val) => {
1606        return Ok(val)
1607      }
1608    }
1609  }
1610}
1611
1612#[pyclass]
1613#[pyo3(name="PBMoniSeries")]
1614pub struct PyPBMoniSeries {
1615  pbmoniseries : PBMoniDataSeries,
1616}
1617
1618#[pymethods]
1619impl PyPBMoniSeries {
1620  #[new]
1621  fn new() -> Self {
1622    let pbmoniseries = PBMoniDataSeries::new();
1623    Self {
1624      pbmoniseries,
1625    }
1626  }
1627  
1628  fn from_file(&mut self, filename : String) -> PyResult<PyDataFrame> {
1629    let mut reader = TofPacketReader::new(filename);
1630    reader.filter = PacketType::PBMoniData;
1631    for tp in reader {
1632      //if tp.packet_type == PacketType::PBMoniData {
1633      if let Ok(moni) =  tp.unpack::<PBMoniData>() {
1634        self.pbmoniseries.add(moni);
1635      }
1636      //}
1637    }
1638    match self.pbmoniseries.get_dataframe() {
1639      Ok(df) => {
1640        let pydf = PyDataFrame(df);
1641        return Ok(pydf);
1642      },
1643      Err(err) => {
1644        return Err(PyValueError::new_err(err.to_string()));
1645      }
1646    }
1647  }
1648}
1649
1650#[pyclass]
1651#[pyo3(name="LTBMoniData")]
1652pub struct PyLTBMoniData {
1653  moni : LTBMoniData,
1654}
1655
1656#[pymethods]
1657impl PyLTBMoniData {
1658  #[new]
1659  fn new() -> Self {
1660    let moni = LTBMoniData::new();
1661    Self {
1662      moni,
1663    }
1664  }
1665 
1666  fn from_tofpacket(&mut self, packet : &PyTofPacket) -> PyResult<()> {
1667    let tp = packet.get_tp();
1668    match tp.unpack::<LTBMoniData>() {
1669      Ok(moni) => {
1670        self.moni = moni;
1671        return Ok(());
1672      }
1673      Err(err) => {
1674        let err_msg = format!("Unable to unpack TofPacket! {err}");
1675        return Err(PyIOError::new_err(err_msg));
1676      }
1677    }
1678  }
1679
1680  fn __repr__(&self) -> PyResult<String> {
1681    Ok(format!("<PyO3Wrapper: {}>", self.moni)) 
1682  }
1683
1684  fn keys(&self) -> Vec<&'static str> {
1685    LTBMoniData::keys()
1686  }
1687
1688  /// Access the (data) members by name
1689  fn get(&self, varname : &str) -> PyResult<f32> {
1690    match self.moni.get(varname) {
1691      None => {
1692        let err_msg = format!("LTBMoniData does not have a key with name {}! See RBmoniData.keys() for a list of available keys!", varname);
1693        return Err(PyKeyError::new_err(err_msg));
1694      }
1695      Some(val) => {
1696        return Ok(val)
1697      }
1698    }
1699  }
1700
1701  #[getter]
1702  fn board_id      (&self)  -> u8  {
1703    self.moni.board_id
1704  }
1705
1706  #[getter]
1707  fn trenz_temp    (&self)  -> f32  {
1708    self.moni.trenz_temp
1709  }
1710
1711  #[getter]
1712  fn ltb_temp      (&self)  -> f32  {
1713    self.moni.ltb_temp
1714  }
1715  #[getter]
1716  fn thresh0       (&self)  -> f32  {
1717    self.moni.thresh[0]
1718  }
1719  #[getter]
1720  fn thresh1       (&self)  -> f32  {
1721    self.moni.thresh[1]
1722  }
1723  #[getter]
1724  fn thresh2       (&self)  -> f32  {
1725    self.moni.thresh[2]
1726  }
1727}
1728
1729#[pyclass]
1730#[pyo3(name="RBMoniData")]
1731pub struct PyRBMoniData {
1732  moni : RBMoniData,
1733}
1734
1735#[pymethods]
1736impl PyRBMoniData {
1737  #[new]
1738  fn new() -> Self {
1739    let moni = RBMoniData::new();
1740    Self {
1741      moni,
1742    }
1743  }
1744 
1745  fn from_tofpacket(&mut self, packet : &PyTofPacket) -> PyResult<()> {
1746    let tp = packet.get_tp();
1747    match tp.unpack::<RBMoniData>() {
1748      Ok(moni) => {
1749        self.moni = moni;
1750        return Ok(());
1751      }
1752      Err(err) => {
1753        let err_msg = format!("Unable to unpack TofPacket! {err}");
1754        return Err(PyIOError::new_err(err_msg));
1755      }
1756    }
1757  }
1758
1759  fn __repr__(&self) -> PyResult<String> {
1760    Ok(format!("<PyO3Wrapper: {}>", self.moni)) 
1761  }
1762
1763  fn keys(&self) -> Vec<&'static str> {
1764    RBMoniData::keys()
1765  }
1766
1767  /// Access the (data) members by name
1768  fn get(&self, varname : &str) -> PyResult<f32> {
1769    match self.moni.get(varname) {
1770      None => {
1771        let err_msg = format!("RBMoniData does not have a key with name {}! See RBmoniData.keys() for a list of available keys!", varname);
1772        return Err(PyKeyError::new_err(err_msg));
1773      }
1774      Some(val) => {
1775        return Ok(val)
1776      }
1777    }
1778  }
1779  
1780  #[getter]
1781  fn board_id         (&self)  -> u8  {
1782    self.moni.board_id
1783  }
1784  #[getter]
1785  fn rate             (&self)  -> u16 {
1786    self.moni.rate
1787  }
1788  #[getter]
1789  fn tmp_drs          (&self)  -> f32 {
1790    self.moni.tmp_drs
1791  }
1792  #[getter]
1793  fn tmp_clk          (&self)  -> f32 {
1794    self.moni.tmp_clk
1795  }
1796  #[getter]
1797  fn tmp_adc          (&self)  -> f32 {
1798    self.moni.tmp_adc
1799  }
1800  #[getter]
1801  fn tmp_zynq         (&self)  -> f32 {
1802    self.moni.tmp_zynq
1803  }
1804  #[getter]
1805  fn tmp_lis3mdltr    (&self)  -> f32 {
1806    self.moni.tmp_lis3mdltr
1807  }
1808  
1809  #[getter]
1810  fn tmp_bm280        (&self)  -> f32 {
1811    self.moni.tmp_bm280
1812  }
1813  #[getter]
1814  fn pressure         (&self)  -> f32 {
1815    self.moni.pressure
1816  }
1817  #[getter]
1818  fn humidity         (&self)  -> f32 {
1819    self.moni.humidity
1820  }
1821  #[getter]
1822  fn mag_x            (&self)  -> f32 {
1823    self.moni.mag_x
1824  }
1825  #[getter]
1826  fn mag_y            (&self)  -> f32 {
1827    self.moni.mag_y
1828  }
1829  #[getter]
1830  fn mag_z            (&self)  -> f32 {
1831    self.moni.mag_z
1832  }
1833  #[getter]
1834  fn drs_dvdd_voltage (&self)  -> f32 { 
1835    self.moni.drs_dvdd_voltage
1836  }
1837  #[getter]
1838  fn drs_dvdd_current (&self)  -> f32 {
1839    self.moni.drs_dvdd_current
1840  }
1841  #[getter]
1842  fn drs_dvdd_power   (&self)  -> f32 {
1843    self.moni.drs_dvdd_power
1844  }
1845  #[getter]
1846  fn p3v3_voltage     (&self)  -> f32 {
1847    self.moni.p3v3_voltage
1848  }
1849  #[getter]
1850  fn p3v3_current     (&self)  -> f32 {
1851    self.moni.p3v3_current
1852  }
1853  #[getter]
1854  fn p3v3_power       (&self)  -> f32 {
1855    self.moni.p3v3_current
1856  }
1857  #[getter]
1858  fn zynq_voltage     (&self)  -> f32 {
1859    self.moni.zynq_voltage
1860  }
1861  #[getter]
1862  fn zynq_current     (&self)  -> f32 {
1863    self.moni.zynq_current
1864  }
1865  #[getter]
1866  fn zynq_power       (&self)  -> f32 {
1867    self.moni.zynq_power
1868  }
1869  #[getter]
1870  fn p3v5_voltage     (&self)  -> f32 { 
1871    self.moni.p3v5_voltage
1872  }
1873  #[getter]
1874  fn p3v5_current     (&self)  -> f32 {
1875    self.moni.p3v5_current
1876  }
1877  #[getter]
1878  fn p3v5_power       (&self)  -> f32 {
1879    self.moni.p3v5_power
1880  }
1881  #[getter]
1882  fn adc_dvdd_voltage (&self)  -> f32 {
1883    self.moni.adc_dvdd_voltage
1884  }
1885  #[getter]
1886  fn adc_dvdd_current (&self)  -> f32 {
1887    self.moni.adc_dvdd_current
1888  }
1889  #[getter]
1890  fn adc_dvdd_power   (&self)  -> f32 {
1891    self.moni.adc_dvdd_power
1892  }
1893  #[getter]
1894  fn adc_avdd_voltage (&self)  -> f32 {
1895    self.moni.adc_avdd_voltage
1896  }
1897  #[getter]
1898  fn adc_avdd_current (&self)  -> f32 {
1899    self.moni.adc_avdd_current
1900  }
1901  #[getter]
1902  fn adc_avdd_power   (&self)  -> f32 {
1903    self.moni.adc_avdd_power
1904  }
1905  #[getter]
1906  fn drs_avdd_voltage (&self)  -> f32 { 
1907    self.moni.drs_avdd_voltage
1908  }
1909  #[getter]
1910  fn drs_avdd_current (&self)  -> f32 {
1911    self.moni.drs_avdd_current
1912  }
1913  #[getter]
1914  fn drs_avdd_power   (&self)  -> f32 {
1915    self.moni.drs_avdd_power
1916  }
1917  #[getter]
1918  fn n1v5_voltage     (&self)  -> f32 {
1919    self.moni.n1v5_voltage
1920  }
1921  #[getter]
1922  fn n1v5_current     (&self)  -> f32 {
1923    self.moni.n1v5_current
1924  }
1925  #[getter]
1926  fn n1v5_power       (&self)  -> f32 {
1927    self.moni.n1v5_power
1928  }
1929}
1930
1931#[pyclass]
1932#[pyo3(name="RBMoniSeries")]
1933pub struct PyRBMoniSeries {
1934  rbmoniseries : RBMoniDataSeries,
1935}
1936
1937#[pymethods]
1938impl PyRBMoniSeries {
1939  #[new]
1940  fn new() -> Self {
1941    let rbmoniseries = RBMoniDataSeries::new();
1942    Self {
1943      rbmoniseries,
1944    }
1945  }
1946  
1947  fn from_file(&mut self, filename : String) -> PyResult<PyDataFrame> {
1948    let mut reader = TofPacketReader::new(filename);
1949    reader.filter = PacketType::RBMoniData;
1950    for tp in reader {
1951      if let Ok(moni) =  tp.unpack::<RBMoniData>() {
1952        self.rbmoniseries.add(moni);
1953      }
1954    }
1955    match self.rbmoniseries.get_dataframe() {
1956      Ok(df) => {
1957        let pydf = PyDataFrame(df);
1958        return Ok(pydf);
1959      },
1960      Err(err) => {
1961        return Err(PyValueError::new_err(err.to_string()));
1962      }
1963    }
1964  }
1965}
1966
1967#[pyclass]
1968#[pyo3(name="HeartbeatDataSink")]
1969
1970pub struct PyHeartBeatDataSink{
1971  pub config : HeartBeatDataSink
1972}
1973
1974impl PyHeartBeatDataSink {
1975  pub fn set_config(&mut self, cfg : HeartBeatDataSink) {
1976    self.config = cfg;
1977  }
1978}
1979#[pymethods]
1980impl PyHeartBeatDataSink{
1981  #[new]
1982  fn new() -> Self {
1983    let cfg: HeartBeatDataSink = HeartBeatDataSink::new();
1984    Self {
1985      config : cfg
1986    }
1987  }
1988  //mission elapsed time
1989  #[getter]
1990  fn get_met(&self) -> PyResult<u64> {
1991    Ok(self.config.met)
1992  }
1993  // num. packets sent
1994  #[getter]
1995  fn get_n_packets_sent(&self) -> PyResult<u64> {
1996    Ok(self.config.n_packets_sent)
1997  }
1998  // num. packets incoming
1999  #[getter]
2000  fn get_n_packets_incoming(&self) -> PyResult<u64> {
2001    Ok(self.config.n_packets_incoming)
2002  }
2003  // num. bytes written
2004  #[getter]
2005  fn get_n_bytes_written(&self) -> PyResult<u64> {
2006    Ok(self.config.n_bytes_written)
2007  }
2008  // num. missing their event id
2009  #[getter]
2010  fn get_n_evid_chunksize(&self) -> PyResult<u64> {
2011    Ok(self.config.n_evid_chunksize)
2012  }
2013  // num. missing event id
2014  #[getter]
2015  fn get_evid_missing(&self) -> PyResult<u64> {
2016    Ok(self.config.evid_missing)
2017  }
2018  // probe size for missing evid check
2019  #[getter]
2020  fn get_evid_check_len(&self) -> PyResult<u64> {
2021    Ok(self.config.evid_check_len)
2022  }
2023  // num. packets written to disk
2024  #[getter]
2025  fn get_n_pack_write_disk(&self) -> PyResult<u64> {
2026    Ok(self.config.n_pack_write_disk)
2027  }
2028  
2029  fn from_tofpacket(&mut self, packet : &PyTofPacket) -> PyResult<()> {
2030    let tp = packet.get_tp();
2031    match tp.unpack::<HeartBeatDataSink>() {
2032      Ok(hb) => {
2033        self.config = hb;
2034        return Ok(());
2035      }
2036      Err(err) => {
2037        let err_msg = format!("Unable to unpack TofPacket! {err}");
2038        return Err(PyIOError::new_err(err_msg));
2039      }
2040    }
2041  }
2042  fn __repr__(&self) -> PyResult<String> {
2043    Ok(format!("<PyO3Wrapper: {}>", self.config)) 
2044  }
2045
2046}
2047
2048#[pyclass]
2049#[pyo3(name="MTBHeartbeat")]
2050pub struct PyMTBHeartbeat{
2051  pub config : MTBHeartbeat
2052}
2053
2054impl PyMTBHeartbeat {
2055  pub fn set_config(&mut self, cfg : MTBHeartbeat) {
2056    self.config = cfg;
2057  }
2058}
2059#[pymethods]
2060impl PyMTBHeartbeat {
2061  #[new]
2062  fn new () -> Self {
2063    let cfg: MTBHeartbeat = MTBHeartbeat::new();
2064    Self {
2065      config : cfg
2066    }
2067  }
2068  #[getter]
2069  fn get_total_elapsed(&self) -> PyResult<u64> {
2070    Ok(self.config.total_elapsed)
2071  }
2072  #[getter]
2073  fn get_n_events(&self) -> PyResult<u64> {
2074    Ok(self.config.n_events)
2075  }
2076  #[getter]
2077  fn get_evq_num_events_last(&self) -> PyResult<u64> {
2078    Ok(self.config.evq_num_events_last)
2079  }
2080  #[getter]
2081  fn get_evq_num_events_avg(&self) -> PyResult<u64> {
2082    Ok(self.config.evq_num_events_avg)
2083  }
2084  #[getter]
2085  fn get_n_ev_unsent(&self) -> PyResult<u64> {
2086    Ok(self.config.n_ev_unsent)
2087  }
2088  #[getter]
2089  fn get_n_ev_missed(&self) -> PyResult<u64> {
2090    Ok(self.config.n_ev_missed)
2091  }
2092  #[getter]
2093  fn get_trate(&self) -> PyResult<u64> {
2094    Ok(self.config.trate)
2095  }
2096  #[getter]
2097  fn get_lost_trate(&self) -> PyResult<u64> {
2098    Ok(self.config.lost_trate)
2099  }
2100  
2101  fn from_tofpacket(&mut self, packet : &PyTofPacket) -> PyResult<()> {
2102    let tp = packet.get_tp();
2103    match tp.unpack::<MTBHeartbeat>() {
2104      Ok(hb) => {
2105        self.config = hb;
2106        return Ok(());
2107      }
2108      Err(err) => {
2109        let err_msg = format!("Unable to unpack TofPacket! {err}");
2110        return Err(PyIOError::new_err(err_msg));
2111      }
2112    }
2113  }
2114  fn __repr__(&self) -> PyResult<String> {
2115    Ok(format!("<PyO3Wrapper: {}>", self.config)) 
2116  }
2117}
2118#[pyclass]
2119#[pyo3(name="EVTBLDRHeartbeat")]
2120pub struct PyEVTBLDRHeartbeat {
2121  pub config : EVTBLDRHeartbeat 
2122}
2123
2124impl PyEVTBLDRHeartbeat {
2125  pub fn set_config(&mut self, cfg : EVTBLDRHeartbeat) {
2126    self.config = cfg;
2127  }
2128}
2129#[pymethods]
2130impl PyEVTBLDRHeartbeat {
2131  #[new]
2132  fn new () -> Self {
2133    let hb: EVTBLDRHeartbeat = EVTBLDRHeartbeat::new();
2134    Self {
2135      config : hb
2136    }
2137  }
2138  #[getter]
2139  fn get_met_seconds(&self) -> PyResult<usize> {
2140    Ok(self.config.met_seconds)
2141  }
2142  #[getter]
2143  fn get_n_mte_received_tot(&self) -> PyResult<usize> {
2144    Ok(self.config.n_mte_received_tot)
2145  }
2146  #[getter]
2147  fn get_n_rbe_received_tot(&self) -> PyResult<usize> {
2148    Ok(self.config.n_rbe_received_tot )
2149  }
2150  #[getter]
2151  fn get_n_rbe_per_te(&self) -> PyResult<usize> {
2152    Ok(self.config.n_rbe_per_te)
2153  }
2154  #[getter]
2155  fn get_n_rbe_discarded_tot(&self) -> PyResult<usize> {
2156    Ok(self.config.n_rbe_discarded_tot)
2157  }
2158  #[getter]
2159  fn get_n_mte_skipped(&self) -> PyResult<usize> {
2160    Ok(self.config.n_mte_skipped)
2161  }
2162  #[getter]
2163  fn get_n_timed_out(&self) -> PyResult<usize> {
2164    Ok(self.config.n_timed_out)
2165  }
2166  #[getter]
2167  fn get_n_sent(&self) -> PyResult<usize> {
2168    Ok(self.config.n_sent)
2169  }
2170  #[getter]
2171  fn get_delta_mte_rbe(&self) -> PyResult<usize> {
2172    Ok(self.config.delta_mte_rbe)
2173  }
2174  #[getter]
2175  fn get_event_cache_size(&self) -> PyResult<usize> {
2176    Ok(self.config.event_cache_size)
2177  }
2178  #[getter]
2179  fn get_rbe_wo_mte(&self) -> PyResult<usize> {
2180    Ok(self.config.rbe_wo_mte)
2181  }
2182  #[getter]
2183  fn get_drs_bsy_lost_hg_hits(&self) -> PyResult<usize> {
2184    Ok(self.config.drs_bsy_lost_hg_hits)
2185  }
2186  #[getter]
2187  fn get_mte_receiver_cbc_len(&self) -> PyResult<usize> {
2188    Ok(self.config.mte_receiver_cbc_len)
2189  }
2190  #[getter]
2191  fn get_rbe_receiver_cbc_len(&self) -> PyResult<usize> {
2192    Ok(self.config.rbe_receiver_cbc_len)
2193  }
2194  #[getter]
2195  fn get_tp_sender_cbc_len(&self) -> PyResult<usize> {
2196    Ok(self.config.tp_sender_cbc_len)
2197  }
2198  #[getter]
2199  fn get_data_mangled_ev(&self) -> PyResult<usize> {
2200    Ok(self.config.data_mangled_ev)
2201  }
2202  
2203  fn from_tofpacket(&mut self, packet : &PyTofPacket) -> PyResult<()> {
2204    let tp = packet.get_tp();
2205    match tp.unpack::<EVTBLDRHeartbeat>() {
2206      Ok(hb) => {
2207        self.config = hb;
2208        return Ok(());
2209      }
2210      Err(err) => {
2211        let err_msg = format!("Unable to unpack TofPacket! {err}");
2212        return Err(PyIOError::new_err(err_msg));
2213      }
2214    }
2215  }
2216  fn __repr__(&self) -> PyResult<String> {
2217    Ok(format!("<PyO3Wrapper: {}>", self.config)) 
2218  }
2219}
2220
2221#[pyclass]
2222#[pyo3(name="MtbMoniData")]
2223pub struct PyMtbMoniData {
2224  moni : MtbMoniData,
2225}
2226
2227impl PyMtbMoniData {
2228  pub fn set_moni(&mut self, moni : MtbMoniData) {
2229    self.moni = moni;
2230  }
2231}
2232
2233#[pymethods]
2234impl PyMtbMoniData {
2235  
2236  #[new]
2237  fn new() -> Self {
2238    let moni = MtbMoniData::new();
2239    Self {
2240      moni,
2241    }
2242  }
2243
2244  fn from_tofpacket(&mut self, packet : &PyTofPacket) -> PyResult<()> {
2245    let tp = packet.get_tp();
2246    match tp.unpack::<MtbMoniData>() {
2247      Ok(moni) => {
2248        self.moni = moni;
2249        return Ok(());
2250      }
2251      Err(err) => {
2252        let err_msg = format!("Unable to unpack TofPacket! {err}");
2253        return Err(PyIOError::new_err(err_msg));
2254      }
2255    }
2256  }
2257
2258  #[getter]
2259  fn get_vccint(&self) -> f32 {
2260    MtbMoniData::adc_vcc_conversion(self.moni.vccint)
2261  }
2262
2263  #[getter]
2264  fn get_vccbram(&self) -> f32 {
2265    MtbMoniData::adc_vcc_conversion(self.moni.vccbram)
2266  }
2267
2268  #[getter]
2269  fn get_vccaux(&self) -> f32 {
2270    MtbMoniData::adc_vcc_conversion(self.moni.vccaux)
2271  }
2272
2273  #[getter]
2274  pub fn get_rate(&self) -> u16 {
2275    self.moni.rate
2276  }
2277  
2278  #[getter]
2279  pub fn get_lost_rate(&self) -> u16 {
2280    self.moni.lost_rate
2281  }
2282
2283  #[getter]
2284  /// Length of the received BUSY signal from 
2285  /// the TIU in nanoseconds
2286  pub fn get_tiu_busy_len(&self) -> u32 {
2287    self.moni.tiu_busy_len * 10
2288  }
2289
2290  #[getter]
2291  pub fn get_daq_queue_len(&self) -> u16 {
2292    self.moni.daq_queue_len
2293  }
2294
2295  #[getter]
2296  pub fn get_tiu_emulation_mode(&self) -> bool {
2297    self.moni.get_tiu_emulation_mode()
2298  }
2299  
2300  #[getter]
2301  pub fn get_tiu_use_aux_link(&self) -> bool {
2302    self.moni.get_tiu_use_aux_link()
2303  }
2304
2305  #[getter]
2306  pub fn get_tiu_bad(&self) -> bool { 
2307    self.moni.get_tiu_bad()
2308  }
2309
2310  #[getter]
2311  pub fn get_tiu_busy_stuck(&self) -> bool {
2312    self.moni.get_tiu_busy_stuck()
2313  }
2314
2315  #[getter]
2316  pub fn get_tiu_ignore_busy(&self) -> bool {
2317    self.moni.get_tiu_ignore_busy()
2318  }
2319
2320
2321  #[getter]
2322  pub fn get_fpga_temp(&self) -> f32 {
2323    self.moni.get_fpga_temp()
2324  }
2325
2326
2327  fn __repr__(&self) -> PyResult<String> {
2328    Ok(format!("<PyO3Wrapper: {}>", self.moni)) 
2329  }
2330}
2331
2332
2333#[pyclass]
2334#[pyo3(name="MtbMoniSeries")]
2335pub struct PyMtbMoniSeries {
2336  mtbmoniseries : MtbMoniDataSeries,
2337}
2338
2339#[pymethods]
2340impl PyMtbMoniSeries {
2341  #[new]
2342  fn new() -> Self {
2343    let mtbmoniseries = MtbMoniDataSeries::new();
2344    Self {
2345      mtbmoniseries,
2346    }
2347  }
2348
2349  /// If data has been acquired from Telemetry, 
2350  /// a timestamp is available
2351  #[getter]
2352  fn timestamps(&self) -> Vec<u64> {
2353    self.mtbmoniseries.timestamps.clone()
2354  }
2355
2356  #[cfg(feature = "caraspace-serial")]
2357  /// Add an additional (Caraspace) file to the series 
2358  ///
2359  /// # Arguments:
2360  ///   * filename : The name of the (caraspace) file to add
2361  ///   * soruce   : The address in the index of the individual
2362  ///                frame the MtbMoniPacket can be found. 
2363  ///                Currently this can either be 'PacketType.MtbMoniData'
2364  ///                or 'TelemetryPacketType.AnyTofHK' 
2365  fn add_crfile(&mut self, filename : String, source : String) {
2366    let mut reader = CRReader::new(filename).expect("Unable to open file!");
2367    // now we have a problem - from which frame should we get it?
2368    // if we get it from the dedicated TOF stream it will be much 
2369    // faster (if that is available) since it will be it's own 
2370    // presence in the frame
2371    let address = &source.clone();
2372    for frame in reader {
2373      if frame.has(address) {
2374        // FIXME - String argument
2375        if source == "PacketType.MtbMoniData" {
2376          let moni_res = frame.get::<TofPacket>(source.clone()).unwrap().unpack::<MtbMoniData>();
2377          match moni_res {
2378            Err(err) => {
2379              println!("Error unpacking MtbMoniData! {err}")
2380            }
2381            Ok(moni) => {
2382              self.mtbmoniseries.add(moni);
2383            }
2384          }
2385        }
2386        if source == "TelemetryPacketType.AnyTofHK" {
2387          let hk_res = frame.get::<TelemetryPacket>(source.clone());
2388          match hk_res {
2389            Err(err) => {
2390              println!("Error unpacking MtbMoniData! {err}");
2391            }
2392            Ok(hk) => {
2393              let mut pos = 0;
2394              let gcutime = hk.header.get_gcutime() as u64;
2395              match TofPacket::from_bytestream(&hk.payload, &mut pos) {
2396                Err(err) => {
2397                  println!("Error unpacking MtbMoniData! {err}");
2398                }
2399                Ok(tp) => {
2400                  if tp.packet_type == PacketType::MonitorMtb {
2401                    match tp.unpack::<MtbMoniData>() {
2402                      Err(err) => {
2403                        println!("Error unpacking MtbMoniData! {err}");
2404                      }
2405                      Ok(moni) => {
2406                        self.mtbmoniseries.add(moni);
2407                        self.mtbmoniseries.timestamps.push(gcutime);
2408                      }
2409                    }
2410                  }
2411                }
2412              }
2413            }
2414          }
2415        }
2416      }
2417    }
2418  }
2419
2420  /// Add an additional file to the series
2421  fn add_file(&mut self, filename : String) {
2422    let mut reader = TofPacketReader::new(filename);
2423    reader.filter = PacketType::MonitorMtb;
2424    for tp in reader {
2425      if let Ok(moni) =  tp.unpack::<MtbMoniData>() {
2426        self.mtbmoniseries.add(moni);
2427      }
2428    }
2429  }
2430
2431  /// Reduces the whole structure to
2432  /// a single polars data frame
2433  fn get_dataframe(&mut self) -> PyResult<PyDataFrame> {
2434    match self.mtbmoniseries.get_dataframe() {
2435      Ok(df) => {
2436        let pydf = PyDataFrame(df);
2437        return Ok(pydf);
2438      },
2439      Err(err) => {
2440        return Err(PyValueError::new_err(err.to_string()));
2441      }
2442    }
2443  }
2444
2445  /// Create a moni series from a file with TofPackets in it
2446  fn from_file(&mut self, filename : String) -> PyResult<PyDataFrame> {
2447    let mut reader = TofPacketReader::new(filename);
2448    reader.filter = PacketType::MonitorMtb;
2449    for tp in reader {
2450      if let Ok(moni) =  tp.unpack::<MtbMoniData>() {
2451        self.mtbmoniseries.add(moni);
2452      }
2453    }
2454    match self.mtbmoniseries.get_dataframe() {
2455      Ok(df) => {
2456        let pydf = PyDataFrame(df);
2457        return Ok(pydf);
2458      },
2459      Err(err) => {
2460        return Err(PyValueError::new_err(err.to_string()));
2461      }
2462    }
2463  }
2464}
2465
2466#[pyclass]
2467#[pyo3(name="CPUMoniSeries")]
2468pub struct PyCPUMoniSeries {
2469  cpumoniseries : CPUMoniDataSeries,
2470}
2471
2472#[pymethods]
2473impl PyCPUMoniSeries {
2474  #[new]
2475  fn new() -> Self {
2476    let cpumoniseries = CPUMoniDataSeries::new();
2477    Self {
2478      cpumoniseries,
2479    }
2480  }
2481  
2482  fn from_file(&mut self, filename : String) -> PyResult<PyDataFrame> {
2483    let mut reader = TofPacketReader::new(filename);
2484    reader.filter = PacketType::CPUMoniData;
2485    for tp in reader {
2486      if let Ok(moni) =  tp.unpack::<CPUMoniData>() {
2487        self.cpumoniseries.add(moni);
2488      }
2489    }
2490    match self.cpumoniseries.get_dataframe() {
2491      Ok(df) => {
2492        let pydf = PyDataFrame(df);
2493        return Ok(pydf);
2494      },
2495      Err(err) => {
2496        return Err(PyValueError::new_err(err.to_string()));
2497      }
2498    }
2499  }
2500}
2501
2502#[pyclass]
2503#[pyo3(name="LTBMoniSeries")]
2504pub struct PyLTBMoniSeries {
2505  ltbmoniseries : LTBMoniDataSeries,
2506}
2507
2508#[pymethods]
2509impl PyLTBMoniSeries {
2510  #[new]
2511  fn new() -> Self {
2512    let ltbmoniseries = LTBMoniDataSeries::new();
2513    Self {
2514      ltbmoniseries,
2515    }
2516  }
2517  
2518  fn from_file(&mut self, filename : String) -> PyResult<PyDataFrame> {
2519    let mut reader = TofPacketReader::new(filename);
2520    reader.filter = PacketType::LTBMoniData;
2521    for tp in reader {
2522      if let Ok(moni) =  tp.unpack::<LTBMoniData>() {
2523        self.ltbmoniseries.add(moni);
2524      }
2525    }
2526    match self.ltbmoniseries.get_dataframe() {
2527      Ok(df) => {
2528        let pydf = PyDataFrame(df);
2529        return Ok(pydf);
2530      },
2531      Err(err) => {
2532        return Err(PyValueError::new_err(err.to_string()));
2533      }
2534    }
2535  }
2536}
2537
2538
2539#[pyclass]
2540#[pyo3(name="TofPacket")]
2541#[derive(Clone)]
2542pub struct PyTofPacket {
2543  pub packet : TofPacket,
2544}
2545
2546impl PyTofPacket {
2547  pub fn set_tp(&mut self, tp : TofPacket) {
2548    self.packet = tp;
2549  }
2550
2551  pub fn get_tp(&self) -> &TofPacket {
2552    &self.packet
2553  }
2554}
2555
2556#[pymethods]
2557impl PyTofPacket {
2558  #[new]
2559  pub fn new() -> Self {
2560    Self {
2561      packet : TofPacket::new(),
2562    }
2563  }
2564 
2565  #[getter]
2566  fn packet_type(&self) -> PacketType {
2567    self.packet.packet_type
2568  }
2569
2570  fn to_bytestream(&self) -> Vec<u8> {
2571    self.packet.to_bytestream()
2572  }
2573
2574  fn from_bytestream(&mut self, stream : Vec<u8>, mut pos : usize) -> PyResult<()>{
2575    match TofPacket::from_bytestream(&stream, &mut pos) {
2576      Ok(tp) => {
2577        self.packet = tp;
2578        return Ok(());
2579      }
2580      Err(err) => {
2581        let err_msg = format!("Unable to TofPacket from bytestream! {err}");
2582        return Err(PyIOError::new_err(err_msg));
2583      }
2584    }
2585  }
2586
2587  fn __repr__(&self) -> PyResult<String> {
2588    Ok(format!("<PyO3Wrapper: {}>", self.packet)) 
2589  }
2590}
2591
2592#[pyclass]
2593#[pyo3(name="MasterTriggerEvent")]
2594pub struct PyMasterTriggerEvent {
2595  event : MasterTriggerEvent,
2596}
2597
2598impl PyMasterTriggerEvent {
2599  pub fn set_event(&mut self,event : MasterTriggerEvent) {
2600    self.event = event;
2601  }
2602}
2603
2604#[pymethods]
2605impl PyMasterTriggerEvent {
2606
2607  #[new]
2608  pub fn new() -> Self {
2609    Self {
2610      event : MasterTriggerEvent::new(),
2611    }
2612  }
2613
2614  #[getter]
2615  fn event_id(&self) -> u32 {
2616    self.event.event_id
2617  }
2618
2619  #[getter]
2620  fn status(&self) -> EventStatus {
2621    self.event.event_status
2622  }
2623
2624  /// Get the RB link IDs according to the mask
2625  #[getter]
2626  pub fn rb_link_ids(&self) -> Vec<u8> {
2627    self.event.get_rb_link_ids()
2628  }
2629
2630  /// Get the combination of triggered DSI/J/CH on 
2631  /// the MTB which formed the trigger. This does 
2632  /// not include further hits which fall into the 
2633  /// integration window. For those, se rb_link_mask
2634  ///
2635  /// The returned values follow the TOF convention
2636  /// to start with 1, so that we can use them to 
2637  /// look up LTB ids in the db.
2638  ///
2639  /// # Returns
2640  ///
2641  ///   Vec<(hit)> where hit is (DSI, J, (CH, CH), LTBThreshold) 
2642  #[getter]
2643  pub fn trigger_hits(&self) -> PyResult<Vec<(u8, u8, (u8, u8), LTBThreshold)>> {
2644    Ok(self.event.get_trigger_hits())
2645  }
2646
2647  #[getter]
2648  pub fn timestamp_mtb(&self) -> u32 {
2649    self.event.timestamp
2650  }
2651
2652
2653  #[getter]
2654  pub fn timestamp_gps32(&self) -> u32 {
2655    self.event.tiu_gps32
2656  }
2657  
2658  #[getter]
2659  pub fn timestamp_gps16(&self) -> u16 {
2660    self.event.tiu_gps16
2661  }
2662
2663  #[getter]
2664  pub fn timestamp_tiu(&self) -> u32 { 
2665    self.event.tiu_timestamp
2666  }
2667
2668  /// Get absolute timestamp as sent by the GPS
2669  #[getter]
2670  pub fn timestamp_abs48(&self) -> u64 {
2671    self.event.get_timestamp_abs48()
2672  }
2673  
2674  /// Get the trigger sources from trigger source byte
2675  /// This returns a list with the fired triggers for 
2676  /// this event
2677  #[getter]
2678  pub fn trigger_sources(&self) -> Vec<TriggerType> {
2679    self.event.get_trigger_sources() 
2680  }
2681  
2682  fn __repr__(&self) -> PyResult<String> {
2683    Ok(format!("<PyO3Wrapper: {}>", self.event))
2684  }
2685
2686}
2687
2688//--------------------------------------------------
2689
2690
2691
2692#[pyclass]
2693#[pyo3(name="RBEventHeader")]
2694pub struct PyRBEventHeader {
2695  header : RBEventHeader,
2696}
2697
2698impl PyRBEventHeader {
2699
2700  //pub rb_id                : u8   ,    
2701  //pub event_id             : u32  , 
2702  //pub status_byte          : u8   ,
2703  //// FIXME - channel mask still has space
2704  //// for the status_bytes, since it only
2705  //// uses 9bits
2706  //pub channel_mask         : u16  , 
2707  //pub stop_cell            : u16  , 
2708  //// we change this by keeping the byte
2709  //// order the same to accomodate the sine 
2710  //// values
2711  //pub ch9_amp              : u16, 
2712  //pub ch9_freq             : u16, 
2713  //pub ch9_phase            : u32, 
2714  ////pub crc32              : u32  , 
2715  ////pub dtap0              : u16  , 
2716  ////pub drs4_temp          : u16  , 
2717  //pub fpga_temp            : u16  , 
2718  //pub timestamp32          : u32  ,
2719} 
2720
2721#[pymethods]
2722impl PyRBEventHeader {
2723  #[new]
2724  pub fn new() -> Self {
2725    Self {
2726      header : RBEventHeader::new(),
2727    }
2728  }
2729
2730  #[getter]
2731  fn rb_id(&self) -> u8 {
2732    self.header.rb_id
2733  }
2734  
2735  #[getter]
2736  fn event_id(&self) -> u32 {
2737    self.header.event_id
2738  }
2739  
2740  //#[getter]
2741  //fn status_byte(&self) -> u8 {
2742  //  self.header.status_byte
2743  //}
2744  
2745  #[getter]
2746  fn channel_mask(&self) -> u16 {
2747    self.header.get_channel_mask()
2748  }
2749  
2750  #[getter]
2751  fn stop_cell(&self) -> u16 {
2752    self.header.stop_cell
2753  }
2754  
2755  #[getter]
2756  fn fpga_temp(&self) -> f32 {
2757    self.header.get_fpga_temp()
2758  }
2759  
2760  #[getter]
2761  fn drs_deadtime(&self) -> u16 {
2762    self.header.drs_deadtime 
2763  }
2764
2765  #[getter]
2766  fn timestamp32(&self) -> u32 {
2767    self.header.timestamp32
2768  }
2769  
2770  #[getter]
2771  fn timestamp16(&self) -> u16 {
2772    self.header.timestamp16
2773  }
2774
2775  //  pub ch9_amp: u16,
2776  //  pub ch9_freq: u16,
2777  //  pub ch9_phase: u32,
2778
2779  fn get_channels(&self) -> Vec<u8> {
2780    self.header.get_channels()
2781  }
2782
2783  #[getter]
2784  pub fn is_event_fragment(&self) -> bool {
2785    self.header.is_event_fragment()
2786  }
2787
2788  #[getter]
2789  pub fn drs_lost_trigger(&self) -> bool {
2790    self.header.drs_lost_trigger()
2791  }
2792
2793  #[getter]
2794  fn lost_lock(&self) -> bool {
2795    self.header.lost_lock()
2796  }
2797
2798  #[getter]
2799  fn lost_lock_last_sec(&self) -> bool {
2800    self.header.lost_lock_last_sec()
2801  }
2802
2803  #[getter]
2804  fn is_locked(&self) -> bool {
2805    self.header.is_locked()
2806  }
2807
2808  #[getter]
2809  fn is_locked_last_sec(&self) -> bool {
2810    self.header.is_locked_last_sec()
2811  }
2812
2813
2814  fn __repr__(&self) -> PyResult<String> {
2815    Ok(format!("<PyO3Wrapper: {}>", self.header)) 
2816  }
2817
2818}
2819
2820#[pyclass]
2821#[pyo3(name="TofEventSummary")]
2822pub struct PyTofEventSummary {
2823  pub event : TofEventSummary,
2824}
2825
2826#[pymethods]
2827impl PyTofEventSummary {
2828  #[new]
2829  pub fn new() -> Self {
2830    Self {
2831      event : TofEventSummary::new(),
2832    }
2833  }
2834  
2835  /// Emit a copy of self
2836  fn copy(&self) -> Self {
2837    let mut copy_event = PyTofEventSummary::new();
2838    copy_event.event = self.event.clone();
2839    copy_event
2840  }
2841
2842
2843  pub fn set_timing_offsets(&mut self, timing_offsets : HashMap<u8, f32>) {
2844    self.event.set_timing_offsets(&timing_offsets);
2845  }
2846
2847  pub fn normalize_hit_times(&mut self) {
2848    self.event.normalize_hit_times();
2849  }
2850
2851  /// Remove hits from the hitseries which can not 
2852  /// be caused by the same particle, which means 
2853  /// that for these two specific hits beta with 
2854  /// respect to the first hit in the event is 
2855  /// larger than one
2856  /// That this works, first hits need to be 
2857  /// "normalized" by calling normalize_hit_times
2858  pub fn lightspeed_cleaning(&mut self, t_err : f32) -> (Vec<u16>, Vec<f32>) {
2859    // return Vec<u16> here so that python does not 
2860    // interpret it as a byte
2861    let mut pids = Vec::<u16>::new();
2862    let (pids_rm, twindows) = self.event.lightspeed_cleaning(t_err);
2863    for pid in pids_rm {
2864      pids.push(pid as u16);
2865    }
2866    (pids, twindows)
2867  }
2868 
2869
2870  /// Remove all hits from the event's hit series which 
2871  /// do NOT obey causality. that is where the timings
2872  /// measured at ends A and B can not be correlated
2873  /// by the assumed speed of light in the paddle
2874  fn remove_non_causal_hits(&mut self) -> Vec<u16> {
2875    // return Vec<u16> here so that python does not 
2876    // interpret it as a byte
2877    let mut pids = Vec::<u16>::new();
2878    for pid in self.event.remove_non_causal_hits() {
2879      pids.push(pid as u16);
2880    }
2881    pids
2882  }
2883  
2884  #[getter]
2885  fn pointcloud(&self) -> Option<Vec<(f32,f32,f32,f32,f32)>> {
2886    self.event.get_pointcloud()
2887  }
2888
2889  #[getter]
2890  fn event_id(&self) -> u32 {
2891    self.event.event_id
2892  }
2893  
2894  #[getter]
2895  fn event_status(&self) -> EventStatus {
2896    self.event.status
2897  }
2898  
2899  /// Compare the hg hits of the event with the triggered paddles and 
2900  /// return the paddles which have at least a missing HG hit
2901  fn get_missing_paddles_hg(&self, mapping : DsiJChPidMapping) -> Vec<u8> {
2902    self.event.get_missing_paddles_hg(&mapping)
2903  }
2904
2905  /// Get all the paddle ids which have been triggered
2906  fn get_triggered_paddles(&self, mapping : DsiJChPidMapping) -> Vec<u8> {
2907    self.event.get_triggered_paddles(&mapping)
2908  }
2909
2910  /// The hits we were not able to read out because the DRS4 chip
2911  /// on the RBs was busy
2912  #[getter]
2913  fn lost_hits(&self) -> u16 {
2914    self.event.drs_dead_lost_hits
2915  }
2916
2917  /// RB Link IDS (not RB ids) which fall into the 
2918  /// trigger window
2919  #[getter]
2920  fn rb_link_ids(&self) -> Vec<u8> {
2921    self.event.get_rb_link_ids()
2922  }
2923
2924  /// Hits which formed a trigger
2925  #[getter]
2926  pub fn trigger_hits(&self) -> PyResult<Vec<(u8, u8, (u8, u8), LTBThreshold)>> {
2927    Ok(self.event.get_trigger_hits())
2928  }
2929  
2930  /// The active triggers in this event. This can be more than one, 
2931  /// if multiple trigger conditions are satisfied.
2932  #[getter]
2933  pub fn trigger_sources(&self) -> Vec<TriggerType> {
2934    self.event.get_trigger_sources()
2935  } 
2936
2937  #[getter]
2938  pub fn hits(&self) -> Vec<PyTofHit> {
2939    let mut hits = Vec::<PyTofHit>::new();
2940    for h in &self.event.hits {
2941      let mut pyhit = PyTofHit::new();
2942      pyhit.hit = h.clone();
2943      hits.push(pyhit);
2944    }
2945    hits
2946  }
2947  
2948  /// Total energy depostion in the Umbrella
2949  ///
2950  /// Utilizes Philip's formula based on 
2951  /// peak height
2952  #[getter]
2953  pub fn get_edep_umbrella(&self) -> f32 {
2954    self.event.get_edep_umbrella()
2955  }
2956  
2957  /// Total energy depostion in the Cube
2958  ///
2959  /// Utilizes Philip's formula based on 
2960  /// peak height
2961  #[getter]
2962  pub fn get_edep_cube(&self) -> f32 {
2963    self.event.get_edep_cube()
2964  }
2965  
2966  /// Total energy depostion in the Cortina
2967  ///
2968  /// Utilizes Philip's formula based on 
2969  /// peak height
2970  #[getter]
2971  pub fn get_edep_cortina(&self) -> f32 {
2972    self.event.get_edep_cortina()
2973  }
2974
2975  /// Total energy depostion in the complete TOF
2976  ///
2977  /// Utilizes Philip's formula based on 
2978  /// peak height
2979  #[getter]
2980  pub fn get_edep(&self) -> f32 {
2981    self.event.get_edep()
2982  }
2983
2984  #[getter]
2985  pub fn nhits(&self) -> usize {
2986    self.event.get_nhits()
2987  }
2988
2989  #[getter]
2990  pub fn nhits_umb(&self) -> usize {
2991    self.event.get_nhits_umb()
2992  }
2993
2994  #[getter]
2995  fn get_nhits_cbe(&self) -> usize {
2996    self.event.get_nhits_cbe()
2997  }
2998  
2999  #[getter]
3000  fn get_nhits_cor(&self) -> usize {
3001    self.event.get_nhits_cor()
3002  }
3003
3004  #[getter]
3005  fn timestamp16(&self) -> u16 {
3006    self.event.timestamp16
3007  }
3008  
3009  #[getter]
3010  fn timestamp32(&self) -> u32 {
3011    self.event.timestamp32
3012  }
3013  
3014  #[getter]
3015  fn timestamp48(&self) -> u64 {
3016    self.event.get_timestamp48()
3017  }
3018  
3019  #[getter]
3020  fn status(&self) -> EventStatus {
3021    self.event.status
3022  }
3023
3024  /// This can get a PyTofEventSummary from a TofPacket of 
3025  /// Type "TofEvent" and will discard all the waveforms 
3026  /// from that packet
3027  fn from_tofeventpacket(&mut self, packet : &PyTofPacket) -> PyResult<()> {
3028    let tp = packet.get_tp();
3029    match TofEventSummary::from_tofeventpacket(tp) {
3030      Ok(event) => {
3031        self.event = event;
3032        return Ok(());
3033      }
3034      Err(err) => {
3035        let err_msg = format!("Unable to unpack TofPacket! {err}");
3036        return Err(PyIOError::new_err(err_msg));
3037      }
3038    }
3039  }
3040
3041  /// Unpack a tofpacket
3042  fn from_tofpacket(&mut self, packet : &PyTofPacket) -> PyResult<()> {
3043    let tp = packet.get_tp();
3044    match tp.unpack::<TofEventSummary>() {
3045      Ok(event) => {
3046        self.event = event;
3047        return Ok(());
3048      }
3049      Err(err) => {
3050        let err_msg = format!("Unable to unpack TofPacket! {err}");
3051        return Err(PyIOError::new_err(err_msg));
3052      }
3053    }
3054  }
3055
3056  /// Pack a TofEventSummary into its corresponding TofPacket
3057  fn pack(&self) -> PyTofPacket {
3058    let mut py_packet = PyTofPacket::new();
3059    py_packet.packet  = self.event.pack();
3060    py_packet
3061  }
3062
3063
3064  fn __repr__(&self) -> PyResult<String> {
3065    Ok(format!("<PyO3Wrapper: {}>", self.event)) 
3066  }
3067}
3068
3069#[pyclass]
3070#[pyo3(name="TofEventHeader")]
3071#[derive(Debug, Clone)]
3072pub struct PyTofEventHeader {
3073  pub header : TofEventHeader
3074}
3075
3076impl PyTofEventHeader {
3077  fn __repr__(&self) -> PyResult<String> {
3078    Ok(format!("<PyO3Wrapper: {}>", self.header)) 
3079  }
3080}
3081
3082#[pyclass]
3083#[pyo3(name="TofEvent")]
3084#[derive(Debug, Clone)]
3085pub struct PyTofEvent {
3086  pub event : TofEvent,
3087}
3088
3089impl PyTofEvent {
3090  pub fn set_event(&mut self, event : TofEvent) {
3091    self.event = event;
3092  }
3093}
3094
3095
3096#[pymethods]
3097impl PyTofEvent {
3098  #[new]
3099  pub fn new() -> Self {
3100    Self {
3101      event : TofEvent::new(),
3102    }
3103  }
3104 
3105  #[getter]
3106  fn has_any_mangling(&self) -> bool {
3107    self.event.has_any_mangling()
3108  }
3109
3110  /// Emit a copy of self
3111  fn copy(&self) -> Self {
3112    let mut copy_event = PyTofEvent::new();
3113    copy_event.event = self.event.clone();
3114    copy_event
3115  }
3116  
3117  #[getter]
3118  fn pointcloud(&self) -> Option<Vec<(f32,f32,f32,f32,f32)>> {
3119    self.event.get_pointcloud()
3120  }
3121
3122  fn get_missing_paddles_hg(&self, mapping : DsiJChPidMapping) -> Vec<u8> {
3123    self.event.get_missing_paddles_hg(&mapping)
3124  }
3125
3126  fn get_missing_paddles_wf(&self, mapping : DsiJChPidMapping) -> Vec<u8> {
3127    self.event.get_missing_paddles_wf(&mapping)
3128  }
3129
3130  #[getter]
3131  fn event_id(&self) -> u32 {
3132    self.event.header.event_id
3133  }
3134
3135  //#[getter]
3136  //fn header(&self) -> Py
3137
3138  #[getter]
3139  fn mastertriggerevent(&self) ->  PyMasterTriggerEvent {
3140    let mut mte = PyMasterTriggerEvent::new();
3141    mte.set_event(self.event.mt_event.clone());
3142    mte
3143  }
3144  
3145  /// Get the combination of triggered DSI/J/CH on 
3146  /// the MTB which formed the trigger. This does 
3147  /// not include further hits which fall into the 
3148  /// integration window. For those, se rb_link_mask
3149  ///
3150  /// The returned values follow the TOF convention
3151  /// to start with 1, so that we can use them to 
3152  /// look up LTB ids in the db.
3153  ///
3154  /// # Returns
3155  ///
3156  ///   Vec<(hit)> where hit is (DSI, J, (CH, CH), LTBThreshold) 
3157  #[getter]
3158  pub fn trigger_hits(&self) -> PyResult<Vec<(u8, u8, (u8, u8), LTBThreshold)>> {
3159    Ok(self.event.mt_event.get_trigger_hits())
3160  }
3161  
3162  /// RB Link IDS (not RB ids) which fall into the 
3163  /// trigger window
3164  #[getter]
3165  fn rb_link_ids(&self) -> Vec<u8> {
3166    self.event.mt_event.get_rb_link_ids()
3167  }
3168
3169  #[getter]
3170  fn rbevents(&self) -> Vec<PyRBEvent> {
3171    // use a bit more than typically exepcted number of rbevents
3172    let mut rbevents = Vec::<PyRBEvent>::with_capacity(5);
3173    for k in &self.event.rb_events {
3174      let mut pyrbevent = PyRBEvent::new();
3175      pyrbevent.set_event(k.clone());
3176      rbevents.push(pyrbevent);
3177    }
3178    rbevents
3179  }
3180  
3181  #[getter]
3182  fn hits(&self) -> Vec<PyTofHit> {
3183    let mut hits = Vec::<PyTofHit>::new();
3184    for ev in &self.event.rb_events {
3185      for h in &ev.hits {
3186        let mut pyhit = PyTofHit::new();
3187        pyhit.hit = *h;
3188        hits.push(pyhit);
3189      }
3190    }
3191    hits
3192  }
3193
3194  #[getter]
3195  fn waveforms(&self) -> Vec<PyRBWaveform> {
3196    let mut wfs = Vec::<PyRBWaveform>::new();
3197    for wf in &self.event.get_rbwaveforms() {
3198      let mut pywf = PyRBWaveform::new();
3199      pywf.wf = wf.clone();
3200      wfs.push(pywf);
3201    }
3202    wfs
3203  }
3204
3205  fn get_summary(&self) -> PyTofEventSummary {
3206    let ts = self.event.get_summary();
3207    let mut pyts = PyTofEventSummary::new();
3208    pyts.event = ts;
3209    return pyts;
3210  }
3211
3212  fn pack(&self) -> PyTofPacket {
3213    let packet   = self.event.pack();
3214    let mut pytp = PyTofPacket::new();
3215    pytp.set_tp(packet);
3216    pytp
3217  }
3218
3219  fn from_tofpacket(&mut self, packet : &PyTofPacket) -> PyResult<()> {
3220    let tp = packet.get_tp();
3221    if tp.packet_type != PacketType::TofEvent {
3222      let err_msg = format!("This packet is of type {} but needs to be of type 'TofEvent'!", tp.packet_type);
3223      return Err(PyValueError::new_err(err_msg));
3224    }
3225    match tp.unpack::<TofEvent>() {
3226      Ok(ev) => {
3227        self.event = ev;
3228        return Ok(());
3229      }
3230      Err(err) => {
3231        let err_msg = format!("Unable to unpack TofPacket! {err}");
3232        return Err(PyIOError::new_err(err_msg));
3233      }
3234    }
3235  }
3236
3237  #[pyo3(signature = (filename, start=0, nevents=0))]
3238  fn from_file(&self, filename : String, start : usize, nevents : usize) -> Vec<PyTofEvent> {
3239    let mut reader    = TofPacketReader::new(filename);
3240    reader.filter     = PacketType::TofEvent;
3241    reader.skip_ahead = start;
3242    reader.stop_after = nevents;
3243    let mut capacity  = 1000;
3244    if nevents > 0 {
3245      capacity = nevents;
3246    }
3247    let mut events = Vec::<PyTofEvent>::with_capacity(capacity);
3248    while let Some(tp) = reader.get_next_packet() {
3249    //for tp in reader.get_next_packet() {
3250      if let Ok(ev) = tp.unpack::<TofEvent>() {
3251        let mut pyev = PyTofEvent::new();
3252        pyev.set_event(ev);
3253        events.push(pyev);
3254      } else {
3255        continue;
3256      }
3257    }
3258    events
3259  }
3260  
3261  #[getter]
3262  pub fn nhits(&self) -> usize {
3263    self.event.get_nhits()
3264  }
3265
3266  #[getter]
3267  pub fn nhits_umb(&self) -> usize {
3268    self.event.get_nhits_umb()
3269  }
3270  
3271  fn __repr__(&self) -> PyResult<String> {
3272    Ok(format!("<PyO3Wrapper: {}>", self.event)) 
3273  }
3274}
3275
3276
3277#[pyclass]
3278#[pyo3(name="RBEvent")]
3279pub struct PyRBEvent {
3280  event : RBEvent,
3281}
3282
3283impl PyRBEvent {
3284  pub fn set_event(&mut self, event : RBEvent) {
3285    self.event = event;
3286  }
3287}
3288
3289#[pymethods]
3290impl PyRBEvent {
3291  #[new]
3292  pub fn new() -> Self {
3293    Self {
3294      event : RBEvent::new(),
3295    }
3296  }
3297 
3298  #[getter]
3299  fn status(&self) -> EventStatus {
3300    self.event.status
3301  }
3302
3303  fn get_waveform<'_py>(&self, py: Python<'_py>, channel : usize) -> PyResult<Bound<'_py, PyArray1<u16>>> {  
3304    let wf  = self.event.get_channel_by_id(channel).unwrap().clone();
3305    let arr = PyArray1::<u16>::from_vec(py, wf);
3306    Ok(arr)
3307  }
3308  
3309  //#[getter]
3310  //fn hits(&self) -> Vec<PyTofHit> {
3311  //  let mut hits = Vec::<PyTofHit>::new();
3312  //  for h in &self.event.hits {
3313  //    let mut pyhit = PyTofHit::new();
3314  //    pyhit.set_hit(*h);
3315  //    hits.push(pyhit);
3316  //  }
3317  //  hits
3318  //}
3319  
3320  fn from_tofpacket(&mut self, packet : &PyTofPacket) -> PyResult<()> {
3321    let tp = packet.get_tp();
3322    match tp.unpack::<RBEvent>() {
3323      Ok(event) => {
3324        self.event = event;
3325        return Ok(());
3326      }
3327      Err(err) => {
3328        let err_msg = format!("Unable to unpack TofPacket! {err}");
3329        return Err(PyIOError::new_err(err_msg));
3330      }
3331    }
3332  }
3333  
3334  #[getter]
3335  fn header(&self) -> PyRBEventHeader {
3336    let mut py_header = PyRBEventHeader::new();
3337    //let mut header = self.event.header;
3338    py_header.header = self.event.header.clone();
3339    py_header
3340  }
3341  
3342  #[getter]
3343  fn waveforms(&self) -> Vec<PyRBWaveform> {
3344    let mut wfs = Vec::<PyRBWaveform>::new();
3345    for wf in &self.event.get_rbwaveforms() {
3346      let mut pywf = PyRBWaveform::new();
3347      pywf.wf = wf.clone();
3348      wfs.push(pywf);
3349    }
3350    wfs
3351  }
3352  
3353  fn __repr__(&self) -> PyResult<String> {
3354    Ok(format!("<PyO3Wrapper: {}>", self.event)) 
3355  }
3356}
3357
3358#[pyclass]
3359#[pyo3(name="TofHit")]
3360pub struct PyTofHit {
3361  hit : TofHit,
3362}
3363
3364#[pymethods]
3365impl PyTofHit {
3366  #[new]
3367  fn new() -> Self {
3368    Self {
3369      hit : TofHit::new(),
3370    }
3371  }
3372
3373  pub fn set_timing_offset(&mut self, offset : f32) {
3374    self.hit.timing_offset = offset;
3375  }
3376
3377  /// Calculate the distance to another hit. For this 
3378  /// to work, the hit coordinates have had to be 
3379  /// determined, so this will only return a 
3380  /// propper result after the paddle information 
3381  /// is added
3382  pub fn distance(&self, other : &PyTofHit) -> f32 {
3383    //((self.x - other.x).powi(2) + (self.y - other.y).powi(2) + (self.z - other.z).powi(2)).sqrt()
3384    self.hit.distance(&other.hit)
3385  }
3386 
3387  /// Set the length and cable length for the paddle
3388  /// FIXME - take gaps_online.db.Paddle as argument
3389  fn set_paddle(&mut self, plen : f32, clen : f32, coax_cbl_time : f32, hart_cbl_time : f32 ) {
3390    self.hit.paddle_len = plen;
3391    self.hit.cable_len  = clen;
3392    self.hit.coax_cable_time = coax_cbl_time;
3393    self.hit.hart_cable_time = hart_cbl_time;
3394  }
3395
3396  #[getter]
3397  fn x(&self) -> f32 {
3398    self.hit.x
3399  }
3400  
3401  #[getter]
3402  fn y(&self) -> f32 {
3403    self.hit.y
3404  }
3405
3406  #[getter]
3407  fn z(&self) -> f32 {
3408    self.hit.z
3409  }
3410
3411  #[getter]
3412  fn get_t0_uncorrected(&self) -> f32 {
3413    self.hit.get_t0_uncorrected()
3414  }
3415  
3416  /// Event t0 is the calculated interaction time based on 
3417  /// the RELATIVE phase shifts consdering ALL hits in this
3418  /// event. This might be of importance to catch rollovers
3419  /// in the phase of channel9. 
3420  /// In total, we are restricting ourselves to a time of 
3421  /// 50ns per events and adjust the phase in such a way that 
3422  /// everything fits into this interval. This will 
3423  /// significantly import the beta reconstruction for particles
3424  /// which hit the TOF within this timing window.
3425  ///
3426  /// If a timing offset is set, this will be added
3427  #[getter]
3428  fn get_event_t0(&self) -> f32 {
3429    self.hit.get_t0()
3430  }
3431
3432  #[getter]
3433  fn get_obeys_causality(&self) -> bool {
3434    self.hit.obeys_causality()
3435  }
3436
3437  #[getter]
3438  fn get_coax_cbl_time(&self) -> f32 {
3439    self.hit.coax_cable_time
3440  }
3441
3442  #[getter]
3443  fn get_hart_cbl_time(&self) -> f32 {
3444    self.hit.hart_cable_time
3445  }
3446
3447  /// Reconstructed particle interaction time,
3448  /// calculated from the waveforms of the two
3449  /// different paddle ends. If the paddle has 
3450  /// been set, this takes phase and cable 
3451  /// length into account
3452  #[getter]
3453  fn t0(&self) -> f32 {
3454    self.hit.get_t0()
3455  }
3456
3457  #[getter]
3458  fn get_phase_delay(&self) -> f32 {
3459    self.hit.get_phase_delay()
3460  }
3461
3462  #[getter]
3463  fn get_cable_delay(&self) -> f32 {
3464    self.hit.get_cable_delay()
3465  }
3466
3467  #[getter]
3468  fn version(&self) -> ProtocolVersion {
3469    self.hit.version
3470  }
3471
3472  #[getter]
3473  fn phase(&self) -> f32 {
3474    self.hit.phase.to_f32()
3475  }
3476
3477  #[getter]
3478  fn baseline_a(&self) -> f32 {
3479    self.hit.baseline_a.to_f32()
3480  }
3481
3482  #[getter]
3483  fn baseline_a_rms(&self) -> f32 {
3484    self.hit.baseline_a_rms.to_f32()
3485  }
3486  
3487  #[getter]
3488  fn baseline_b(&self) -> f32 {
3489    self.hit.baseline_b.to_f32()
3490  }
3491
3492  #[getter]
3493  fn baseline_b_rms(&self) -> f32 {
3494    self.hit.baseline_b_rms.to_f32()
3495  }
3496
3497  #[getter]
3498  fn peak_a(&self) -> f32 {
3499    self.hit.get_peak_a()
3500  }
3501  
3502  #[getter]
3503  fn peak_b(&self) -> f32 {
3504    self.hit.get_peak_b()
3505  }
3506  
3507  #[getter]
3508  fn charge_a(&self) -> f32 {
3509    self.hit.get_charge_a()
3510  }
3511  
3512  #[getter]
3513  fn charge_b(&self) -> f32 {
3514    self.hit.get_charge_b()
3515  }
3516
3517  #[getter]
3518  fn time_a(&self) -> f32 {
3519    self.hit.get_time_a()
3520  }
3521  
3522  #[getter]
3523  fn time_b(&self) -> f32 {
3524    self.hit.get_time_b()
3525  }
3526
3527  /// Reconstructed particle interaction position
3528  /// along the long axis of the paddle.
3529  /// For the other dimensions, there is no information
3530  /// about the position.
3531  /// Reconstructed with the waveforms of both paddle ends.
3532  #[getter]
3533  fn pos(&self) -> f32 {
3534    self.hit.get_pos()
3535  }
3536 
3537  /// The paddle id (1-160) of the hit paddle
3538  #[getter]
3539  fn paddle_id(&self) -> u8 {
3540    self.hit.paddle_id
3541  }
3542
3543  #[getter]
3544  fn edep(&self) -> f32 {
3545    self.hit.get_edep()
3546  }
3547
3548  #[getter]
3549  fn get_paddle_len(&self) -> f32 {
3550    self.hit.paddle_len
3551  }
3552
3553  fn __repr__(&self) -> PyResult<String> {
3554    Ok(format!("<PyO3Wrapper: {}>", self.hit)) 
3555  }
3556}
3557
3558#[pyclass]
3559#[pyo3(name="RBWaveform")]
3560pub struct PyRBWaveform {
3561  pub wf : RBWaveform,
3562}
3563
3564#[pymethods]
3565impl PyRBWaveform {
3566  #[new]
3567  fn new() -> Self {
3568    Self {
3569      wf : RBWaveform::new(),
3570    }
3571  }
3572  /// Time over threshold - waveform needs to be 
3573  /// calibrated. 
3574  /// Paddle end A
3575  ///
3576  /// # Arguments:
3577  ///   * threshold : value in mV
3578  fn get_tot_a(&self, threshold : f32) -> f32 {
3579    self.wf.time_over_threshold_a(threshold)
3580  }
3581  
3582  /// Time over threshold - waveform needs to be 
3583  /// calibrated. 
3584  /// Paddle end B
3585  ///
3586  /// # Arguments:
3587  ///   * threshold : value in mV
3588  fn get_tot_b(&self, threshold : f32) -> f32 {
3589    self.wf.time_over_threshold_b(threshold)
3590  }
3591 
3592  /// Apply the readoutboard calibration to convert adc/bins
3593  /// to millivolts and nanoseconds
3594  fn calibrate(&mut self, cali : &PyRBCalibration) -> PyResult<()> {
3595    match self.wf.calibrate(&cali.cali) {
3596      Ok(_) => {
3597        return Ok(());
3598      }
3599      Err(err) => {
3600        return Err(PyValueError::new_err(err.to_string()));
3601      }
3602    }
3603  }
3604
3605  /// Paddle ID of this wveform (1-160)
3606  #[getter]
3607  fn paddle_id(&self) -> u8 {
3608    self.wf.paddle_id
3609  }
3610
3611  #[getter]
3612  fn rb_id(&self) -> u8 {
3613    self.wf.rb_id
3614  }
3615  
3616  #[getter]
3617  fn event_id(&self) -> u32 {
3618    self.wf.event_id
3619  }
3620  
3621  #[getter]
3622  fn rb_channel_a(&self) -> u8 {
3623    self.wf.rb_channel_a
3624  }
3625  
3626  #[getter]
3627  fn rb_channel_b(&self) -> u8 {
3628    self.wf.rb_channel_b
3629  }
3630  
3631  #[getter]
3632  fn stop_cell(&self) -> u16 {
3633    self.wf.stop_cell
3634  }
3635  
3636  #[getter]
3637  fn adc_a<'_py>(&self, py: Python<'_py>) ->  PyResult<Bound<'_py, PyArray1<u16>>> {
3638    let wf  = self.wf.adc_a.clone();
3639    let arr = PyArray1::<u16>::from_vec(py, wf);
3640    Ok(arr)
3641  }
3642  
3643  #[getter]
3644  fn adc_b<'_py>(&self, py: Python<'_py>) ->  PyResult<Bound<'_py, PyArray1<u16>>> {
3645    let wf  = self.wf.adc_b.clone();
3646    let arr = PyArray1::<u16>::from_vec(py, wf);
3647    Ok(arr)
3648  }
3649  
3650  #[getter]
3651  fn voltages_a<'_py>(&self, py: Python<'_py>) ->  PyResult<Bound<'_py, PyArray1<f32>>> {
3652    let wf  = self.wf.voltages_a.clone();
3653    let arr = PyArray1::<f32>::from_vec(py, wf);
3654    Ok(arr)
3655  }
3656
3657  #[getter]
3658  fn times_a<'_py>(&self, py: Python<'_py>) ->  PyResult<Bound<'_py, PyArray1<f32>>> {
3659    let times  = self.wf.nanoseconds_a.clone();
3660    let arr    = PyArray1::<f32>::from_vec(py, times);
3661    Ok(arr)
3662  }
3663
3664  #[getter]
3665  fn voltages_b<'_py>(&self, py: Python<'_py>) ->  PyResult<Bound<'_py, PyArray1<f32>>> {
3666    let wf  = self.wf.voltages_b.clone();
3667    let arr = PyArray1::<f32>::from_vec(py, wf);
3668    Ok(arr)
3669  }
3670
3671  #[getter]
3672  fn times_b<'_py>(&self, py: Python<'_py>) ->  PyResult<Bound<'_py, PyArray1<f32>>> {
3673    let times  = self.wf.nanoseconds_b.clone();
3674    let arr    = PyArray1::<f32>::from_vec(py, times);
3675    Ok(arr)
3676  }
3677
3678  fn apply_spike_filter(&mut self) {
3679    self.wf.apply_spike_filter();
3680  }
3681 
3682  fn from_tofpacket(&mut self, packet : &PyTofPacket) -> PyResult<()> {
3683    let tp = packet.get_tp();
3684    match tp.unpack::<RBWaveform>() {
3685      Ok(wf) => {
3686        self.wf = wf;
3687        return Ok(());
3688      }
3689      Err(err) => {
3690        let err_msg = format!("Unable to unpack TofPacket of type {} ! {err}", packet.get_tp().packet_type);
3691        return Err(PyIOError::new_err(err_msg));
3692      }
3693    }
3694  }
3695
3696  fn __repr__(&self) -> PyResult<String> {
3697    Ok(format!("<PyO3Wrapper: {}>", self.wf)) 
3698  }
3699} 
3700
3701
3702