1#[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
64pub 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#[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
133use colored::{
138 Colorize,
139 ColoredString
140};
141use chrono::Utc;
142use log::Level;
143use std::io::Write;
144
145pub 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
156pub 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#[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 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!(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 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 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 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::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::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#[cfg(feature="pybindings")]
464#[pyfunction]
465fn get_version_major() -> u8 {
466 return VERSION_MAJ.parse().unwrap();
467}
468
469#[cfg(feature="pybindings")]
473#[pyfunction]
474fn get_version_minor() -> u8 {
475 return VERSION_MIN.parse().unwrap();
476}
477
478#[cfg(feature="pybindings")]
482#[pyfunction]
483fn get_version_patch() -> u8 {
484 return VERSION_PCH.parse().unwrap();
485}
486
487#[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 $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#[cfg(feature="pybindings")]
576#[pymodule]
577#[pyo3(name = "gondola_core")]
578fn gondola_core_py<'_py>(m : &Bound<'_py, PyModule>) -> PyResult<()> { 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 Ok(())
598}
599
600
601
602