Skip to main content

gondola_core/
lib.rs

1//! Dataclasses provides structures to facilitate the work with data drom 
2//! the GAPS experiment. Most noticeably, there are
3//!
4//! * events       - TOF/Tracker data, TOF events on disk, MergedEvents send over telemetry
5//!
6//! * packets      - containers to serialize/deserialize the described structures so that 
7//!                  these can be stored on disk or send over the network
8//!
9//! * calibration  - TOF/Tracker related calibration routines and containers to hold results
10//! 
11//! * io           - read/write packets to/from disk or receive them over the network
12//! 
13//! * random       - random numbers for software tests
14//! 
15//! * tof          - Very specific TOF related code which does not fall under a different 
16//!                  category
17//! 
18//! * tracker      - Very specific TRK related code which does not fall under any other 
19//!                  category
20//!
21//! # features:
22//!
23//! * random              - allow random number generated data classes for 
24//!                         testing
25//!
26//! * database            - access a data base for advanced paddle
27//!                         mapping, readoutboard and ltb information etc.
28//!                         This will introduce a dependency on sqlite and 
29//!                         diesel
30//! * tof-control         - allows to control LTB & Powerboard from the RBs 
31//!                         over i2c. Since the i2c protocoll is not supported 
32//!                         on Mac, code with this feature enable won't build 
33//!                         on Apple systems. 
34//! * advanced-algorithms - allows to use a different algorithm for pulse extraction 
35//! * pybindings          - build the python library "gondola". Most of the entitities 
36//!                         within "gondola-core" have pybindings, e.g. events, hits.
37//! * tof-liftof          - build the code which is required to build liftof, the flight
38//!                         code utiilizing this library 
39// This file is part of gaps-online-software and published 
40// under the GPLv3 license
41
42#[macro_use] extern crate log; 
43
44pub mod prelude;
45#[cfg(feature="random")]
46pub mod random;
47pub mod constants;
48pub mod events;
49pub mod packets;
50pub mod version;
51pub mod io;
52pub mod calibration;
53pub mod errors;
54pub mod tof;
55pub mod tracker;
56pub mod monitoring;
57pub mod stats;
58pub mod physics;
59#[cfg(feature="pybindings")]
60pub mod python;
61#[cfg(feature="database")]
62pub mod database;
63
64// python convention
65pub const VERSION: &str = env!("CARGO_PKG_VERSION"); 
66pub const VERSION_MAJ: &str = env!("CARGO_PKG_VERSION_MAJOR");
67pub const VERSION_MIN: &str = env!("CARGO_PKG_VERSION_MINOR");
68pub const VERSION_PCH: &str = env!("CARGO_PKG_VERSION_PATCH");
69
70#[cfg(feature="pybindings")]
71use crate::errors::*;
72
73/// A simple helper macro adding an as_str function 
74/// as well as the Display method to any enum.
75///
76/// Avoids writing boilerplate
77#[macro_export]
78macro_rules! expand_and_test_enum {
79  ($name:ident, $test_name:ident) => {
80    impl fmt::Display for $name {
81      fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
82        write!(f, "<{}: {}>",stringify!($name), self.as_ref())
83      }
84    }
85
86    impl From<u8> for $name {
87      fn from(value: u8) -> Self {
88        match Self::from_repr(value)  {
89          None => {
90            return Self::Unknown;
91          }
92          Some(variant) => {
93            return variant;
94          }
95        }
96      }
97    }
98
99    #[cfg(feature="random")]
100    impl FromRandom for $name {
101      fn from_random() -> Self {
102        let mut choices = Vec::<Self>::new();
103        for k in Self::iter() {
104          choices.push(k);
105        }
106        let mut rng  = rand::rng();
107        let idx = rng.random_range(0..choices.len());
108        choices[idx]
109      }
110    }
111
112    #[test]
113    fn $test_name() {
114      for _ in 0..100 {
115        let data = $name::from_random();
116        assert_eq!($name::from(data as u8), data);
117      }
118    }
119
120    #[cfg(feature="pybindings")]
121    #[pymethods]
122    impl $name {
123    
124      #[staticmethod]  
125      #[pyo3(name = "from_u8")]
126      fn from_py(byte : u8) -> Self {
127        Self::from(byte) 
128      }
129    }
130  };
131}
132
133//---------------------------------------
134// helpers to init the logging system
135//
136
137use colored::{
138    Colorize,
139    ColoredString
140};
141use chrono::Utc;
142use log::Level;
143use std::io::Write;
144
145/// Make sure that the loglevel is in color, even though not using pretty_env logger
146pub fn color_log(level : &Level) -> ColoredString {
147  match level {
148    Level::Error    => String::from(" ERROR!").red(),
149    Level::Warn     => String::from(" WARN  ").yellow(),
150    Level::Info     => String::from(" Info  ").green(),
151    Level::Debug    => String::from(" debug ").blue(),
152    Level::Trace    => String::from(" trace ").cyan(),
153  }
154}
155
156/// Set up the environmental (env) logger
157/// with our format
158///
159/// Ensure that the lines and module paths
160/// are printed in the logging output
161pub fn init_env_logger() {
162  env_logger::builder()
163    .format(|buf, record| {
164    writeln!( buf, "[{ts} - {level}][{module_path}:{line}] {args}",
165      ts    = Utc::now().format("%Y/%m/%d-%H:%M:%SUTC"), 
166      level = color_log(&record.level()),
167      module_path = record.module_path().unwrap_or("<unknown>"),
168      line  = record.line().unwrap_or(0),
169      args  = record.args()
170      )
171    }).init();
172}
173
174//-------------------------------------------------
175// Build the python library
176
177#[cfg(feature="pybindings")]
178pub use pyo3::prelude::*; 
179#[cfg(feature="pybindings")]
180pub use pyo3::wrap_pymodule; 
181#[cfg(feature="pybindings")]
182pub use pyo3::wrap_pyfunction; 
183
184#[cfg(feature="pybindings")]
185#[pymodule]
186#[pyo3(name = "tof")]
187fn tof_py<'_py>(m: &Bound<'_py, PyModule>) -> PyResult<()> {
188  use crate::tof::*;
189  m.add_class::<RBPaddleID>()?;
190  m.add_class::<TofDetectorStatus>()?;
191  m.add_class::<TofCommandCode>()?;
192  m.add_class::<TofCommand>()?;
193  m.add_class::<TofOperationMode>()?;
194  m.add_class::<BuildStrategy>()?;
195  m.add_class::<PreampBiasConfig>()?;
196  m.add_class::<RBChannelMaskConfig>()?;
197  m.add_class::<TriggerConfig>()?;
198  m.add_class::<TofRunConfig>()?;
199  m.add_class::<TofCuts>()?;
200  m.add_class::<TofAnalysis>()?;
201  m.add_class::<TofAnalysisCache>()?;
202  m.add_class::<TofAnalysisPaddleCache>()?;
203  m.add_class::<AnalysisEngineSettings>()?;
204  #[cfg(feature="tof-liftof")]
205  m.add_class::<PyMasterTrigger>()?;
206  m.add_function(wrap_pyfunction!(waveform_analysis, m)?)?;
207  m.add_function(wrap_pyfunction!(to_board_id_string, m)?)?;
208  // the commands
209  m.add_function(wrap_pyfunction!(start_run, m)?)?;
210  m.add_function(wrap_pyfunction!(stop_run, m)?)?;
211  m.add_function(wrap_pyfunction!(restart_liftofrb, m)?)?;
212  m.add_function(wrap_pyfunction!(enable_verification_run, m)?)?;
213  m.add_function(wrap_pyfunction!(shutdown_all_rbs, m)?)?;
214  m.add_function(wrap_pyfunction!(shutdown_rat, m)?)?;
215  m.add_function(wrap_pyfunction!(shutdown_ratpair, m)?)?;
216  m.add_function(wrap_pyfunction!(shutdown_rb, m)?)?;
217  m.add_function(wrap_pyfunction!(shutdown_tofcpu, m)?)?;
218  m.add_function(wrap_pyfunction!(run_action_alfa, m)?)?;
219  m.add_function(wrap_pyfunction!(run_action_bravo, m)?)?;
220  m.add_function(wrap_pyfunction!(run_action_charlie, m)?)?;
221  m.add_function(wrap_pyfunction!(run_action_whiskey, m)?)?;
222  m.add_function(wrap_pyfunction!(run_action_tango, m)?)?;
223  m.add_function(wrap_pyfunction!(run_action_foxtrott, m)?)?;
224  m.add_function(wrap_pyfunction!(request_liftof_settings, m)?)?;
225  m.add_function(wrap_pyfunction!(restore_default_config, m)?)?;
226  m.add_function(wrap_pyfunction!(apply_settings_diff, m)?)?;
227  Ok(())
228}
229
230#[cfg(feature="pybindings")]
231#[pymodule]
232#[pyo3(name = "tracker")]
233fn tracker_py<'_py>(m: &Bound<'_py, PyModule>) -> PyResult<()> {
234  use crate::tracker::*;
235  //m.add_function(wrap_pyfunction!(mt_event_get_timestamp_abs48,m)?)?;
236  m.add_function(wrap_pyfunction!(strip_lines, m)?)?;
237  m.add_class::<TrackerOnlineCalibration>()?;
238  Ok(())
239}
240
241#[cfg(feature="pybindings")]
242#[pymodule]
243#[pyo3(name = "calibration")]
244fn calibration_py<'_py>(m: &Bound<'_py, PyModule>) -> PyResult<()> {
245  use crate::calibration::*;
246  m.add_class::<RBCalibrations>()?;
247  m.add_class::<TrackerOfflineCalibration>()?;
248  Ok(())
249}
250
251#[cfg(feature="pybindings")]
252#[pymodule]
253#[pyo3(name = "events")]
254fn events_py<'_py>(m: &Bound<'_py, PyModule>) -> PyResult<()> {
255  use crate::events::*;
256  m.add_class::<McHit>()?;
257  m.add_class::<McEvent>()?;
258  m.add_class::<McTrack>()?;
259  m.add_class::<McTree>()?;
260  m.add_class::<TofHit>()?;
261  m.add_class::<TrackerHit>()?;
262  m.add_class::<RBEventHeader>()?;
263  m.add_class::<RBEvent>()?;
264  m.add_class::<RBWaveform>()?;
265  m.add_class::<EventStatus>()?;
266  m.add_class::<DataType>()?;
267  m.add_class::<TofEvent>()?;
268  m.add_class::<TrackerDAQEvent>()?;
269  m.add_class::<TrackerDAQEventPacket>()?;
270  m.add_class::<TelemetryEvent>()?;
271  m.add_function(wrap_pyfunction!(strip_id, m)?)?;
272  m.add_class::<HitQuality>()?;
273  m.add_class::<EventQuality>()?;
274  m.add_class::<TriggerType>()?;
275  m.add_class::<LTBThreshold>()?;
276  m.add_function(wrap_pyfunction!(mt_event_get_timestamp_abs48,m)?)?;
277  Ok(())
278}
279
280#[cfg(feature="pybindings")]
281#[pymodule]
282#[pyo3(name = "packets")]
283fn packets_py<'_py>(m: &Bound<'_py, PyModule>) -> PyResult<()> {
284  use crate::packets::*;
285  m.add_class::<TofPacketType>()?;
286  m.add_class::<TofPacket>()?;
287  m.add_class::<TelemetryPacketType>()?;
288  m.add_class::<TelemetryPacket>()?;
289  m.add_class::<TelemetryPacketHeader>()?;
290  m.add_class::<TrackerHeader>()?;
291  // FIXME - this is monitoring data, we should 
292  // move this over to the monitoring part of the 
293  // code and they should become MoniData/Series 
294  m.add_class::<PduChannel>()?;
295  m.add_class::<Pac1934>()?;
296  m.add_class::<PduHKPacket>()?;
297  m.add_function(wrap_pyfunction!(make_systime,m)?)?;
298  m.add_class::<TrackerEventIDEchoPacket>()?;
299  m.add_class::<TrackerTempLeakPacket>()?;
300  m.add_class::<TrackerDAQTempPacket>()?;
301  m.add_class::<TrackerDAQHSKPacket>()?;
302  Ok(())
303}
304
305#[cfg(feature="pybindings")]
306#[pymodule]
307#[pyo3(name = "io")]
308fn io_py<'_py>(m: &Bound<'_py, PyModule>) -> PyResult<()> {
309  use crate::io::*;
310  use crate::io::caraspace::*;
311  #[cfg(feature="root")]
312  use crate::io::root_reader::read_example;
313  use crate::io::caraspace::frame::get_all_telemetry_event_names;
314  #[cfg(feature="root")]
315  m.add_function(wrap_pyfunction!(read_example, m)?)?;
316  m.add_function(wrap_pyfunction!(get_all_telemetry_event_names, m)?)?;
317  m.add_function(wrap_pyfunction!(get_runfilename, m)?)?;
318  m.add_function(wrap_pyfunction!(get_califilename, m)?)?;
319  m.add_function(wrap_pyfunction!(list_path_contents_sorted_py, m)?)?;
320  m.add_function(wrap_pyfunction!(get_utc_timestamp, m)?)?;
321  m.add_function(wrap_pyfunction!(get_utc_date, m)?)?;
322  m.add_function(wrap_pyfunction!(get_rundata_from_file, m)?)?;
323  m.add_function(wrap_pyfunction!(get_datetime, m)?)?;
324  m.add_function(wrap_pyfunction!(get_utc_timestamp_from_unix, m)?)?;
325  m.add_function(wrap_pyfunction!(get_unix_timestamp, m)?)?;
326  m.add_function(wrap_pyfunction!(get_unix_timestamp_from_telemetry, m)?)?;
327  // these are the config file manipulators
328  m.add_function(wrap_pyfunction!(apply_diff_to_file_py, m)?)?;
329  m.add_function(wrap_pyfunction!(compress_toml_py, m)?)?;
330  m.add_function(wrap_pyfunction!(decompress_toml_py, m)?)?;
331  m.add_function(wrap_pyfunction!(create_compressed_diff_py, m)?)?;
332
333  m.add_class::<CRFrameObject>()?;
334  m.add_class::<CRFrameObjectType>()?;
335  m.add_class::<CRFrame>()?;
336  m.add_class::<DataSourceKind>()?;
337  m.add_class::<CRReader>()?;
338  m.add_class::<CRWriter>()?;
339  m.add_class::<TofPacketReader>()?;
340  m.add_class::<TofPacketWriter>()?;
341  m.add_class::<TelemetryPacketReader>()?;
342  m.add_class::<TelemetryPacketWriter>()?;
343  //m.add_class::<PyDataSource>()?;
344  Ok(())
345}
346
347#[cfg(feature="pybindings")]
348#[pymodule]
349#[pyo3(name = "monitoring")]
350fn monitoring_py<'_py>(m: &Bound<'_py, PyModule>) -> PyResult<()> {
351  use crate::monitoring::*;
352  m.add_class::<EventBuilderHB>()?;
353  m.add_class::<EventBuilderHBSeries>()?;
354  m.add_class::<DataSinkHB>()?;
355  m.add_class::<DataSinkHBSeries>()?;
356  m.add_class::<MasterTriggerHB>()?;
357  m.add_class::<MasterTriggerHBSeries>()?;
358  m.add_class::<PAMoniData>()?;
359  m.add_class::<PAMoniDataSeries>()?;
360  m.add_class::<PBMoniData>()?;
361  m.add_class::<PBMoniDataSeries>()?;
362  m.add_class::<MtbMoniData>()?;
363  m.add_class::<MtbMoniDataSeries>()?;
364  m.add_class::<LTBMoniData>()?;
365  m.add_class::<LTBMoniDataSeries>()?;
366  m.add_class::<RBMoniData>()?;
367  m.add_class::<RBMoniDataSeries>()?;
368  m.add_class::<CPUMoniData>()?;
369  m.add_class::<CPUMoniDataSeries>()?;
370  m.add_class::<SipPresMoniData>()?;
371  m.add_class::<SipPresMoniDataSeries>()?;
372  m.add_class::<SipPosMoniData>()?;
373  m.add_class::<SipPosMoniDataSeries>()?;
374  m.add_class::<SipTimeMoniData>()?;
375  m.add_class::<SipTimeMoniDataSeries>()?;
376  m.add_class::<TrackerGpsMoniData>()?;
377  m.add_class::<TrackerGpsMoniDataSeries>()?;
378  m.add_class::<GcuEvBldStatsMoniData>()?;
379  m.add_class::<GcuEvBldStatsMoniDataSeries>()?;
380  m.add_class::<CoolingMoniData>()?;
381  m.add_class::<CoolingMoniDataSeries>()?;
382  m.add_class::<RunStatistics>()?;
383  m.add_class::<WastieMoniData>()?;
384  m.add_class::<WastieMoniDataSeries>()?;
385  Ok(())
386}
387
388#[cfg(feature="pybindings")]
389#[pymodule]
390#[pyo3(name = "stats")]
391fn stats_py<'_py>(m: &Bound<'_py, PyModule>) -> PyResult<()> {
392  //use crate::io::*;
393  use crate::stats::gamma_pdf_py;
394  use crate::stats::mean_py;
395  m.add_function(wrap_pyfunction!(gamma_pdf_py, m)?)?;
396  m.add_function(wrap_pyfunction!(mean_py, m)?)?;
397  Ok(())
398}
399
400#[cfg(feature="pybindings")]
401#[pymodule]
402#[pyo3(name = "algo")]
403fn algo_py<'_py>(m: &Bound<'_py, PyModule>) -> PyResult<()> {
404  //use crate::io::*;
405  use crate::tof::algorithms::*;
406  m.add_function(wrap_pyfunction!(get_max_value_idx_py, m)?)?;
407  m.add_function(wrap_pyfunction!(interpolate_time_py, m)?)?;
408  m.add_function(wrap_pyfunction!(fit_sine_simple_py, m)?)?;
409  Ok(())
410}
411
412#[cfg(feature="database")]
413#[cfg(feature="pybindings")]
414#[pymodule]
415#[pyo3(name = "db")]
416fn db_py<'_py>(m: &Bound<'_py, PyModule>) -> PyResult<()> {
417  use crate::database::*;
418  m.add_class::<ReadoutBoard>()?;
419  m.add_class::<RAT>()?;
420  m.add_class::<TofPaddle>()?;
421  m.add_class::<TrackerStrip>()?;
422  m.add_class::<TrackerStripMask>()?;
423  m.add_class::<TrackerStripPedestal>()?;
424  m.add_class::<TrackerStripTransferFunction>()?;
425  m.add_class::<TrackerStripCmnNoise>()?;
426  m.add_class::<TrackerStripGain>()?;
427  m.add_class::<TrackerStripPulse>()?;
428  m.add_class::<TofPaddleTimingConstant>()?;
429  m.add_class::<TrackerCalibrationFile>()?;
430  m.add_class::<TrackerCalibrationFileType>()?;
431  m.add_class::<TrackerCaliTimeWindow>()?;
432  m.add_class::<TofPaddleTemp>()?;
433  m.add_class::<TofPaddleTempDataSeries>()?;
434  m.add_function(wrap_pyfunction!(load_calibration_db_elena, m)?)?;
435  m.add_function(wrap_pyfunction!(get_all_rbids_in_db, m)?)?;
436  m.add_function(wrap_pyfunction!(get_all_pbids_in_db, m)?)?;
437  m.add_function(wrap_pyfunction!(get_hid_vid_maps, m)?)?;
438  m.add_function(wrap_pyfunction!(get_vid_hid_maps, m)?)?;
439  m.add_function(wrap_pyfunction!(get_rbids_for_pbid, m)?)?;
440  m.add_function(wrap_pyfunction!(get_dsi_j_ch_pid_map_py, m)?)?;
441  m.add_function(wrap_pyfunction!(get_rbid_pbchannel_pid_map_py, m)?)?;
442  m.add_function(wrap_pyfunction!(load_calibration_db_elena, m)?)?;
443  m.add_function(wrap_pyfunction!(create_trk_mask_table, m)?)?;
444  m.add_function(wrap_pyfunction!(create_trk_pedestal_table, m)?)?;
445  m.add_function(wrap_pyfunction!(create_trk_transfer_fn_table, m)?)?;
446  m.add_function(wrap_pyfunction!(create_trk_pulse_table, m)?)?;
447  m.add_function(wrap_pyfunction!(create_trk_gain_table, m)?)?;
448  m.add_function(wrap_pyfunction!(is_in_tracker_cali_window, m)?)?;
449  m.add_function(wrap_pyfunction!(create_trk_cali_tw_table, m)?)?;
450  m.add_function(wrap_pyfunction!(create_tof_paddle_temp_table, m)?)?;
451  Ok(())
452}
453
454#[cfg(feature="pybindings")]
455#[pyfunction]
456fn get_version() -> &'static str {
457  return VERSION;
458}
459
460/// Major version of the package 
461/// from the version string
462/// (Major.Minor.Patch)
463#[cfg(feature="pybindings")]
464#[pyfunction]
465fn get_version_major() -> u8 {
466  return VERSION_MAJ.parse().unwrap();
467}
468
469/// Minor version of the package 
470/// from the version string
471/// (Major.Minor.Patch)
472#[cfg(feature="pybindings")]
473#[pyfunction]
474fn get_version_minor() -> u8 {
475  return VERSION_MIN.parse().unwrap();
476}
477
478/// Patch level of the package 
479/// from the version string
480/// (Major.Minor.Patch)
481#[cfg(feature="pybindings")]
482#[pyfunction]
483fn get_version_patch() -> u8 {
484  return VERSION_PCH.parse().unwrap();
485}
486
487/// Check if the version is at least the 
488/// required, given string following the 
489/// format MAJOR.MINOR.PATCH 
490#[cfg(feature="pybindings")]
491#[pyfunction]
492fn version_at_least(version_string : String) -> bool {
493  let parts: Vec<u32> = version_string.split('.')
494    .map(|s| s.parse().expect("Input is not a valid version string. Please follow the MAJOR.MINOR.PATCH format!"))
495    .collect();
496  let major : u8 = parts[0] as u8;
497  let minor : u8 = parts[1] as u8;
498  let patch : u8 = parts[2] as u8;
499  if major < get_version_major() {
500    return true;
501  }
502  if major > get_version_major() {
503    println!("Required major version of {} is larger than current version {}", major, VERSION);
504    return false;
505  }
506  if major == get_version_major() {
507    if minor < get_version_minor() {
508      return true;
509    }
510    if minor > get_version_minor() {
511      println!("Required minor version of {} is larger than current version {}", minor, VERSION);
512      return false;
513    }
514    if minor == get_version_minor() {
515      if patch > get_version_patch() {
516        println!("Required patch version of {} is larger than current version {}", patch, VERSION);
517        return false 
518      }
519    }
520  } 
521  true
522}
523
524#[cfg(feature="pybindings")]
525pyo3::create_exception!(gondola_core_py, PyMasterTriggerError, pyo3::exceptions::PyException);
526#[cfg(feature="pybindings")]
527pyo3::create_exception!(gondola_core_py, PyRunError, pyo3::exceptions::PyException);
528#[cfg(feature="pybindings")]
529pyo3::create_exception!(gondola_core_py, PyTofError, pyo3::exceptions::PyException);
530#[cfg(feature="pybindings")]
531pyo3::create_exception!(gondola_core_py, PyStagingError, pyo3::exceptions::PyException);
532#[cfg(feature="pybindings")]
533pyo3::create_exception!(gondola_core_py, PySensorError, pyo3::exceptions::PyException);
534#[cfg(feature="pybindings")]
535pyo3::create_exception!(gondola_core_py, PyUserError, pyo3::exceptions::PyException);
536#[cfg(feature="pybindings")]
537pyo3::create_exception!(gondola_core_py, PyCalibrationError, pyo3::exceptions::PyException);
538#[cfg(feature="pybindings")]
539pyo3::create_exception!(gondola_core_py, PyWaveformError, pyo3::exceptions::PyException);
540#[cfg(feature="pybindings")]
541pyo3::create_exception!(gondola_core_py, PyIPBusError, pyo3::exceptions::PyException);
542#[cfg(feature="pybindings")]
543pyo3::create_exception!(gondola_core_py, PySerializationError, pyo3::exceptions::PyException);
544#[cfg(feature="pybindings")]
545pyo3::create_exception!(gondola_core_py, PyAnalysisError, pyo3::exceptions::PyException); 
546
547#[macro_export]
548macro_rules! pythonize_error {
549  ($name:ident, $pyname:ident) => {
550
551    impl From<$name> for PyErr {
552      fn from(err: $name) -> PyErr {
553        // You can use a standard Python exception if you prefer, 
554        // e.g., pyo3::exceptions::PyValueError::new_err(...)
555        // But mapping to your custom PyAnalysisError is usually better. 
556        $pyname::new_err(format!("<GondolaCoreException: {}>", err))
557      }
558    }
559  }
560}
561
562#[cfg(feature="pybindings")]
563pythonize_error!(SerializationError, PySerializationError);
564#[cfg(feature="pybindings")]
565pythonize_error!(AnalysisError, PyAnalysisError);
566#[cfg(feature="pybindings")]
567pythonize_error!(CalibrationError, PyCalibrationError);
568#[cfg(feature="pybindings")]
569pythonize_error!(UserError, PyUserError);
570
571/// Python API to rust version of tof-dataclasses.
572///
573/// Currently, this contains only the analysis 
574/// functions
575#[cfg(feature="pybindings")]
576#[pymodule]
577#[pyo3(name = "gondola_core")]
578fn gondola_core_py<'_py>(m : &Bound<'_py, PyModule>) -> PyResult<()> { //: Python<'_>, m: &PyModule) -> PyResult<()> {
579  pyo3_log::init();
580  m.add_function(wrap_pyfunction!(get_version, m)?)?;
581  m.add_function(wrap_pyfunction!(get_version_major, m)?)?;
582  m.add_function(wrap_pyfunction!(get_version_minor, m)?)?;
583  m.add_function(wrap_pyfunction!(get_version_patch, m)?)?;
584  m.add_function(wrap_pyfunction!(version_at_least, m)?)?;
585  m.add_wrapped(wrap_pymodule!(events_py))?;
586  m.add_wrapped(wrap_pymodule!(monitoring_py))?;
587  m.add_wrapped(wrap_pymodule!(packets_py))?;
588  m.add_wrapped(wrap_pymodule!(tof_py))?;
589  m.add_wrapped(wrap_pymodule!(tracker_py))?;
590  m.add_wrapped(wrap_pymodule!(io_py))?;
591  m.add_wrapped(wrap_pymodule!(db_py))?;
592  m.add_wrapped(wrap_pymodule!(stats_py))?;
593  m.add_wrapped(wrap_pymodule!(algo_py))?;
594  m.add_wrapped(wrap_pymodule!(calibration_py))?;
595  //m.add("SerializationError",  pyo3::types::PyAnyMethods::<PySerializationError>::get_type(m))?;
596  //m.add("AnalysisError",  pyo3::types::PyAnyMethods::<PyAnalysisError>::get_type(m))?;
597  Ok(())
598}
599
600
601
602