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//!
31//!
32// This file is part of gaps-online-software and published 
33// under the GPLv3 license
34
35#[macro_use] extern crate log; 
36
37pub mod prelude;
38#[cfg(feature="random")]
39pub mod random;
40pub mod constants;
41pub mod events;
42pub mod packets;
43pub mod version;
44pub mod io;
45pub mod calibration;
46pub mod errors;
47pub mod tof;
48pub mod tracker;
49pub mod monitoring;
50pub mod stats;
51#[cfg(feature="pybindings")]
52pub mod python;
53#[cfg(feature="database")]
54pub mod database;
55
56// python convention
57pub const VERSION: &str = env!("CARGO_PKG_VERSION"); 
58
59/// A simple helper macro adding an as_str function 
60/// as well as the Display method to any enum.
61///
62/// Avoids writing boilerplate
63#[macro_export]
64macro_rules! expand_and_test_enum {
65  ($name:ident, $test_name:ident) => {
66    impl fmt::Display for $name {
67      fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
68        write!(f, "<{}: {}>",stringify!($name), self.as_ref())
69      }
70    }
71
72    impl From<u8> for $name {
73      fn from(value: u8) -> Self {
74        match Self::from_repr(value)  {
75          None => {
76            return Self::Unknown;
77          }
78          Some(variant) => {
79            return variant;
80          }
81        }
82      }
83    }
84
85    #[cfg(feature="random")]
86    impl FromRandom for $name {
87      fn from_random() -> Self {
88        let mut choices = Vec::<Self>::new();
89        for k in Self::iter() {
90          choices.push(k);
91        }
92        let mut rng  = rand::rng();
93        let idx = rng.random_range(0..choices.len());
94        choices[idx]
95      }
96    }
97
98    #[test]
99    fn $test_name() {
100      for _ in 0..100 {
101        let data = $name::from_random();
102        assert_eq!($name::from(data as u8), data);
103      }
104    }
105  };
106}
107
108//---------------------------------------
109// helpers to init the logging system
110//
111
112use colored::{
113    Colorize,
114    ColoredString
115};
116use chrono::Utc;
117use log::Level;
118use std::io::Write;
119
120/// Make sure that the loglevel is in color, even though not using pretty_env logger
121pub fn color_log(level : &Level) -> ColoredString {
122  match level {
123    Level::Error    => String::from(" ERROR!").red(),
124    Level::Warn     => String::from(" WARN  ").yellow(),
125    Level::Info     => String::from(" Info  ").green(),
126    Level::Debug    => String::from(" debug ").blue(),
127    Level::Trace    => String::from(" trace ").cyan(),
128  }
129}
130
131/// Set up the environmental (env) logger
132/// with our format
133///
134/// Ensure that the lines and module paths
135/// are printed in the logging output
136pub fn init_env_logger() {
137  env_logger::builder()
138    .format(|buf, record| {
139    writeln!( buf, "[{ts} - {level}][{module_path}:{line}] {args}",
140      ts    = Utc::now().format("%Y/%m/%d-%H:%M:%SUTC"), 
141      level = color_log(&record.level()),
142      module_path = record.module_path().unwrap_or("<unknown>"),
143      line  = record.line().unwrap_or(0),
144      args  = record.args()
145      )
146    }).init();
147}
148
149//-------------------------------------------------
150// Build the python library
151
152#[cfg(feature="pybindings")]
153pub use pyo3::prelude::*; 
154#[cfg(feature="pybindings")]
155pub use pyo3::wrap_pymodule; 
156#[cfg(feature="pybindings")]
157pub use pyo3::wrap_pyfunction; 
158
159#[cfg(feature="pybindings")]
160#[pymodule]
161#[pyo3(name = "tof")]
162fn tof_py<'_py>(m: &Bound<'_py, PyModule>) -> PyResult<()> {
163  use crate::tof::*;
164  m.add_class::<RBPaddleID>()?;
165  m.add_class::<TofDetectorStatus>()?;
166  m.add_class::<TofCommandCode>()?;
167  m.add_class::<TofCommand>()?;
168  m.add_class::<TofOperationMode>()?;
169  m.add_class::<BuildStrategy>()?;
170  m.add_class::<PreampBiasConfig>()?;
171  m.add_class::<RBChannelMaskConfig>()?;
172  m.add_class::<TriggerConfig>()?;
173  m.add_class::<TofRunConfig>()?;
174  m.add_class::<TofCuts>()?;
175  m.add_class::<PyMasterTrigger>()?;
176  m.add_function(wrap_pyfunction!(to_board_id_string, m)?)?;
177  // the commands
178  m.add_function(wrap_pyfunction!(start_run, m)?)?;
179  m.add_function(wrap_pyfunction!(stop_run, m)?)?;
180  m.add_function(wrap_pyfunction!(restart_liftofrb, m)?)?;
181  m.add_function(wrap_pyfunction!(enable_verification_run, m)?)?;
182  m.add_function(wrap_pyfunction!(shutdown_all_rbs, m)?)?;
183  m.add_function(wrap_pyfunction!(shutdown_rat, m)?)?;
184  m.add_function(wrap_pyfunction!(shutdown_ratpair, m)?)?;
185  m.add_function(wrap_pyfunction!(shutdown_rb, m)?)?;
186  m.add_function(wrap_pyfunction!(shutdown_tofcpu, m)?)?;
187
188
189
190  Ok(())
191}
192
193#[cfg(feature="pybindings")]
194#[pymodule]
195#[pyo3(name = "tracker")]
196fn tracker_py<'_py>(m: &Bound<'_py, PyModule>) -> PyResult<()> {
197  use crate::tracker::*;
198  //m.add_function(wrap_pyfunction!(mt_event_get_timestamp_abs48,m)?)?;
199  m.add_function(wrap_pyfunction!(strip_lines, m)?)?;
200  Ok(())
201}
202
203#[cfg(feature="pybindings")]
204#[pymodule]
205#[pyo3(name = "calibration")]
206fn calibration_py<'_py>(m: &Bound<'_py, PyModule>) -> PyResult<()> {
207  use crate::calibration::tof::*;
208  m.add_class::<RBCalibrations>()?;
209  Ok(())
210}
211
212#[cfg(feature="pybindings")]
213#[pymodule]
214#[pyo3(name = "events")]
215fn events_py<'_py>(m: &Bound<'_py, PyModule>) -> PyResult<()> {
216  use crate::events::*;
217  m.add_class::<TofHit>()?;
218  m.add_class::<TrackerHit>()?;
219  m.add_class::<RBEventHeader>()?;
220  m.add_class::<RBEvent>()?;
221  m.add_class::<RBWaveform>()?;
222  m.add_class::<EventStatus>()?;
223  m.add_class::<DataType>()?;
224  m.add_class::<TofEvent>()?;
225  m.add_class::<TelemetryEvent>()?;
226  m.add_function(wrap_pyfunction!(strip_id, m)?)?;
227  m.add_class::<EventQuality>()?;
228  m.add_class::<TriggerType>()?;
229  m.add_class::<LTBThreshold>()?;
230  m.add_function(wrap_pyfunction!(mt_event_get_timestamp_abs48,m)?)?;
231  Ok(())
232}
233
234#[cfg(feature="pybindings")]
235#[pymodule]
236#[pyo3(name = "packets")]
237fn packets_py<'_py>(m: &Bound<'_py, PyModule>) -> PyResult<()> {
238  use crate::packets::*;
239  m.add_class::<TofPacketType>()?;
240  m.add_class::<TofPacket>()?;
241  m.add_class::<TelemetryPacketType>()?;
242  m.add_class::<TelemetryPacket>()?;
243  m.add_class::<TelemetryPacketHeader>()?;
244  m.add_class::<TrackerHeader>()?;
245  m.add_function(wrap_pyfunction!(make_systime,m)?)?;
246  Ok(())
247}
248
249#[cfg(feature="pybindings")]
250#[pymodule]
251#[pyo3(name = "io")]
252fn io_py<'_py>(m: &Bound<'_py, PyModule>) -> PyResult<()> {
253  use crate::io::*;
254  use crate::io::caraspace::*;
255  #[cfg(feature="root")]
256  use crate::io::root_reader::read_example;
257  use crate::io::caraspace::frame::get_all_telemetry_event_names;
258  #[cfg(feature="root")]
259  m.add_function(wrap_pyfunction!(read_example, m)?)?;
260  m.add_function(wrap_pyfunction!(get_all_telemetry_event_names, m)?)?;
261  m.add_function(wrap_pyfunction!(get_runfilename, m)?)?;
262  m.add_function(wrap_pyfunction!(get_califilename, m)?)?;
263  m.add_function(wrap_pyfunction!(list_path_contents_sorted_py, m)?)?;
264  m.add_function(wrap_pyfunction!(get_utc_timestamp, m)?)?;
265  m.add_function(wrap_pyfunction!(get_utc_date, m)?)?;
266  m.add_function(wrap_pyfunction!(get_rundata_from_file, m)?)?;
267  m.add_function(wrap_pyfunction!(get_datetime, m)?)?;
268  m.add_function(wrap_pyfunction!(get_unix_timestamp, m)?)?;
269  m.add_function(wrap_pyfunction!(get_unix_timestamp_from_telemetry, m)?)?;
270  m.add_class::<CRFrameObject>()?;
271  m.add_class::<CRFrameObjectType>()?;
272  m.add_class::<CRFrame>()?;
273  m.add_class::<DataSourceKind>()?;
274  m.add_class::<CRReader>()?;
275  m.add_class::<CRWriter>()?;
276  m.add_class::<TofPacketReader>()?;
277  m.add_class::<TelemetryPacketReader>()?;
278  //m.add_class::<PyDataSource>()?;
279  Ok(())
280}
281
282#[cfg(feature="pybindings")]
283#[pymodule]
284#[pyo3(name = "monitoring")]
285fn monitoring_py<'_py>(m: &Bound<'_py, PyModule>) -> PyResult<()> {
286  use crate::monitoring::*;
287  m.add_class::<EventBuilderHB>()?;
288  m.add_class::<EventBuilderHBSeries>()?;
289  m.add_class::<DataSinkHB>()?;
290  m.add_class::<DataSinkHBSeries>()?;
291  m.add_class::<MasterTriggerHB>()?;
292  m.add_class::<MasterTriggerHBSeries>()?;
293  m.add_class::<PAMoniData>()?;
294  m.add_class::<PAMoniDataSeries>()?;
295  m.add_class::<PBMoniData>()?;
296  m.add_class::<PBMoniDataSeries>()?;
297  m.add_class::<MtbMoniData>()?;
298  m.add_class::<MtbMoniDataSeries>()?;
299  m.add_class::<LTBMoniData>()?;
300  m.add_class::<LTBMoniDataSeries>()?;
301  m.add_class::<RBMoniData>()?;
302  m.add_class::<RBMoniDataSeries>()?;
303  m.add_class::<CPUMoniData>()?;
304  m.add_class::<CPUMoniDataSeries>()?;
305  m.add_class::<RunStatistics>()?;
306  Ok(())
307}
308
309#[cfg(feature="pybindings")]
310#[pymodule]
311#[pyo3(name = "stats")]
312fn stats_py<'_py>(m: &Bound<'_py, PyModule>) -> PyResult<()> {
313  //use crate::io::*;
314  use crate::stats::py_gamma_pdf;
315  m.add_function(wrap_pyfunction!(py_gamma_pdf, m)?)?;
316  Ok(())
317}
318
319#[cfg(feature="pybindings")]
320#[pymodule]
321#[pyo3(name = "algo")]
322fn algo_py<'_py>(m: &Bound<'_py, PyModule>) -> PyResult<()> {
323  //use crate::io::*;
324  use crate::tof::algorithms::*;
325  m.add_function(wrap_pyfunction!(get_max_value_idx_py, m)?)?;
326  m.add_function(wrap_pyfunction!(interpolate_time_py, m)?)?;
327  m.add_function(wrap_pyfunction!(fit_sine_simple_py, m)?)?;
328  Ok(())
329}
330
331#[cfg(feature="database")]
332#[cfg(feature="pybindings")]
333#[pymodule]
334#[pyo3(name = "db")]
335fn db_py<'_py>(m: &Bound<'_py, PyModule>) -> PyResult<()> {
336  use crate::database::*;
337  m.add_class::<TofPaddle>()?;
338  m.add_class::<ReadoutBoard>()?;
339  m.add_class::<TrackerStrip>()?;
340  m.add_class::<TrackerStripMask>()?;
341  m.add_class::<TrackerStripPedestal>()?;
342  m.add_class::<TrackerStripTransferFunction>()?;
343  m.add_class::<TrackerStripCmnNoise>()?;
344  m.add_function(wrap_pyfunction!(get_all_rbids_in_db, m)?)?;
345  m.add_function(wrap_pyfunction!(get_hid_vid_map, m)?)?;
346  m.add_function(wrap_pyfunction!(get_vid_hid_map, m)?)?;
347  m.add_function(wrap_pyfunction!(get_dsi_j_ch_pid_map_py, m)?)?;
348  Ok(())
349}
350
351#[cfg(feature="pybindings")]
352#[pyfunction]
353fn get_version() -> &'static str {
354  return VERSION;
355}
356
357/// Python API to rust version of tof-dataclasses.
358///
359/// Currently, this contains only the analysis 
360/// functions
361#[cfg(feature="pybindings")]
362#[pymodule]
363#[pyo3(name = "gondola_core")]
364fn gondola_core_py<'_py>(m : &Bound<'_py, PyModule>) -> PyResult<()> { //: Python<'_>, m: &PyModule) -> PyResult<()> {
365  pyo3_log::init();
366  m.add_function(wrap_pyfunction!(get_version, m)?)?;
367  m.add_wrapped(wrap_pymodule!(events_py))?;
368  m.add_wrapped(wrap_pymodule!(monitoring_py))?;
369  m.add_wrapped(wrap_pymodule!(packets_py))?;
370  m.add_wrapped(wrap_pymodule!(tof_py))?;
371  m.add_wrapped(wrap_pymodule!(tracker_py))?;
372  m.add_wrapped(wrap_pymodule!(io_py))?;
373  m.add_wrapped(wrap_pymodule!(db_py))?;
374  m.add_wrapped(wrap_pymodule!(stats_py))?;
375  m.add_wrapped(wrap_pymodule!(algo_py))?;
376  m.add_wrapped(wrap_pymodule!(calibration_py))?;
377  Ok(())
378}