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::<EventQuality>()?;
273 m.add_class::<TriggerType>()?;
274 m.add_class::<LTBThreshold>()?;
275 m.add_function(wrap_pyfunction!(mt_event_get_timestamp_abs48,m)?)?;
276 Ok(())
277}
278
279#[cfg(feature="pybindings")]
280#[pymodule]
281#[pyo3(name = "packets")]
282fn packets_py<'_py>(m: &Bound<'_py, PyModule>) -> PyResult<()> {
283 use crate::packets::*;
284 m.add_class::<TofPacketType>()?;
285 m.add_class::<TofPacket>()?;
286 m.add_class::<TelemetryPacketType>()?;
287 m.add_class::<TelemetryPacket>()?;
288 m.add_class::<TelemetryPacketHeader>()?;
289 m.add_class::<TrackerHeader>()?;
290 m.add_class::<PduChannel>()?;
294 m.add_class::<Pac1934>()?;
295 m.add_class::<PduHKPacket>()?;
296 m.add_function(wrap_pyfunction!(make_systime,m)?)?;
297 m.add_class::<TrackerEventIDEchoPacket>()?;
298 m.add_class::<TrackerTempLeakPacket>()?;
299 m.add_class::<TrackerDAQTempPacket>()?;
300 m.add_class::<TrackerDAQHSKPacket>()?;
301 Ok(())
302}
303
304#[cfg(feature="pybindings")]
305#[pymodule]
306#[pyo3(name = "io")]
307fn io_py<'_py>(m: &Bound<'_py, PyModule>) -> PyResult<()> {
308 use crate::io::*;
309 use crate::io::caraspace::*;
310 #[cfg(feature="root")]
311 use crate::io::root_reader::read_example;
312 use crate::io::caraspace::frame::get_all_telemetry_event_names;
313 #[cfg(feature="root")]
314 m.add_function(wrap_pyfunction!(read_example, m)?)?;
315 m.add_function(wrap_pyfunction!(get_all_telemetry_event_names, m)?)?;
316 m.add_function(wrap_pyfunction!(get_runfilename, m)?)?;
317 m.add_function(wrap_pyfunction!(get_califilename, m)?)?;
318 m.add_function(wrap_pyfunction!(list_path_contents_sorted_py, m)?)?;
319 m.add_function(wrap_pyfunction!(get_utc_timestamp, m)?)?;
320 m.add_function(wrap_pyfunction!(get_utc_date, m)?)?;
321 m.add_function(wrap_pyfunction!(get_rundata_from_file, m)?)?;
322 m.add_function(wrap_pyfunction!(get_datetime, m)?)?;
323 m.add_function(wrap_pyfunction!(get_utc_timestamp_from_unix, m)?)?;
324 m.add_function(wrap_pyfunction!(get_unix_timestamp, m)?)?;
325 m.add_function(wrap_pyfunction!(get_unix_timestamp_from_telemetry, m)?)?;
326 m.add_function(wrap_pyfunction!(apply_diff_to_file_py, m)?)?;
328 m.add_function(wrap_pyfunction!(compress_toml_py, m)?)?;
329 m.add_function(wrap_pyfunction!(decompress_toml_py, m)?)?;
330 m.add_function(wrap_pyfunction!(create_compressed_diff_py, m)?)?;
331
332 m.add_class::<CRFrameObject>()?;
333 m.add_class::<CRFrameObjectType>()?;
334 m.add_class::<CRFrame>()?;
335 m.add_class::<DataSourceKind>()?;
336 m.add_class::<CRReader>()?;
337 m.add_class::<CRWriter>()?;
338 m.add_class::<TofPacketReader>()?;
339 m.add_class::<TofPacketWriter>()?;
340 m.add_class::<TelemetryPacketReader>()?;
341 m.add_class::<TelemetryPacketWriter>()?;
342 Ok(())
344}
345
346#[cfg(feature="pybindings")]
347#[pymodule]
348#[pyo3(name = "monitoring")]
349fn monitoring_py<'_py>(m: &Bound<'_py, PyModule>) -> PyResult<()> {
350 use crate::monitoring::*;
351 m.add_class::<EventBuilderHB>()?;
352 m.add_class::<EventBuilderHBSeries>()?;
353 m.add_class::<DataSinkHB>()?;
354 m.add_class::<DataSinkHBSeries>()?;
355 m.add_class::<MasterTriggerHB>()?;
356 m.add_class::<MasterTriggerHBSeries>()?;
357 m.add_class::<PAMoniData>()?;
358 m.add_class::<PAMoniDataSeries>()?;
359 m.add_class::<PBMoniData>()?;
360 m.add_class::<PBMoniDataSeries>()?;
361 m.add_class::<MtbMoniData>()?;
362 m.add_class::<MtbMoniDataSeries>()?;
363 m.add_class::<LTBMoniData>()?;
364 m.add_class::<LTBMoniDataSeries>()?;
365 m.add_class::<RBMoniData>()?;
366 m.add_class::<RBMoniDataSeries>()?;
367 m.add_class::<CPUMoniData>()?;
368 m.add_class::<CPUMoniDataSeries>()?;
369 m.add_class::<SipPresMoniData>()?;
370 m.add_class::<SipPresMoniDataSeries>()?;
371 m.add_class::<SipPosMoniData>()?;
372 m.add_class::<SipPosMoniDataSeries>()?;
373 m.add_class::<SipTimeMoniData>()?;
374 m.add_class::<SipTimeMoniDataSeries>()?;
375 m.add_class::<TrackerGpsMoniData>()?;
376 m.add_class::<TrackerGpsMoniDataSeries>()?;
377 m.add_class::<GcuEvBldStatsMoniData>()?;
378 m.add_class::<GcuEvBldStatsMoniDataSeries>()?;
379 m.add_class::<CoolingMoniData>()?;
380 m.add_class::<CoolingMoniDataSeries>()?;
381 m.add_class::<RunStatistics>()?;
382 m.add_class::<WastieMoniData>()?;
383 m.add_class::<WastieMoniDataSeries>()?;
384 Ok(())
385}
386
387#[cfg(feature="pybindings")]
388#[pymodule]
389#[pyo3(name = "stats")]
390fn stats_py<'_py>(m: &Bound<'_py, PyModule>) -> PyResult<()> {
391 use crate::stats::gamma_pdf_py;
393 use crate::stats::mean_py;
394 m.add_function(wrap_pyfunction!(gamma_pdf_py, m)?)?;
395 m.add_function(wrap_pyfunction!(mean_py, m)?)?;
396 Ok(())
397}
398
399#[cfg(feature="pybindings")]
400#[pymodule]
401#[pyo3(name = "algo")]
402fn algo_py<'_py>(m: &Bound<'_py, PyModule>) -> PyResult<()> {
403 use crate::tof::algorithms::*;
405 m.add_function(wrap_pyfunction!(get_max_value_idx_py, m)?)?;
406 m.add_function(wrap_pyfunction!(interpolate_time_py, m)?)?;
407 m.add_function(wrap_pyfunction!(fit_sine_simple_py, m)?)?;
408 Ok(())
409}
410
411#[cfg(feature="database")]
412#[cfg(feature="pybindings")]
413#[pymodule]
414#[pyo3(name = "db")]
415fn db_py<'_py>(m: &Bound<'_py, PyModule>) -> PyResult<()> {
416 use crate::database::*;
417 m.add_class::<ReadoutBoard>()?;
418 m.add_class::<RAT>()?;
419 m.add_class::<TofPaddle>()?;
420 m.add_class::<TrackerStrip>()?;
421 m.add_class::<TrackerStripMask>()?;
422 m.add_class::<TrackerStripPedestal>()?;
423 m.add_class::<TrackerStripTransferFunction>()?;
424 m.add_class::<TrackerStripCmnNoise>()?;
425 m.add_class::<TrackerStripGain>()?;
426 m.add_class::<TrackerStripPulse>()?;
427 m.add_class::<TofPaddleTimingConstant>()?;
428 m.add_class::<TrackerCalibrationFile>()?;
429 m.add_class::<TrackerCalibrationFileType>()?;
430 m.add_function(wrap_pyfunction!(load_calibration_db_elena, m)?)?;
431 m.add_function(wrap_pyfunction!(get_all_rbids_in_db, m)?)?;
432 m.add_function(wrap_pyfunction!(get_all_pbids_in_db, m)?)?;
433 m.add_function(wrap_pyfunction!(get_hid_vid_maps, m)?)?;
434 m.add_function(wrap_pyfunction!(get_vid_hid_maps, m)?)?;
435 m.add_function(wrap_pyfunction!(get_rbids_for_pbid, m)?)?;
436 m.add_function(wrap_pyfunction!(get_dsi_j_ch_pid_map_py, m)?)?;
437 m.add_function(wrap_pyfunction!(get_rbid_pbchannel_pid_map_py, m)?)?;
438 m.add_function(wrap_pyfunction!(load_calibration_db_elena, m)?)?;
439 m.add_function(wrap_pyfunction!(create_trk_mask_table, m)?)?;
440 m.add_function(wrap_pyfunction!(create_trk_pedestal_table, m)?)?;
441 m.add_function(wrap_pyfunction!(create_trk_transfer_fn_table, m)?)?;
442 m.add_function(wrap_pyfunction!(create_trk_pulse_table, m)?)?;
443 m.add_function(wrap_pyfunction!(create_trk_gain_table, m)?)?;
444 Ok(())
445}
446
447#[cfg(feature="pybindings")]
448#[pyfunction]
449fn get_version() -> &'static str {
450 return VERSION;
451}
452
453#[cfg(feature="pybindings")]
457#[pyfunction]
458fn get_version_major() -> u8 {
459 return VERSION_MAJ.parse().unwrap();
460}
461
462#[cfg(feature="pybindings")]
466#[pyfunction]
467fn get_version_minor() -> u8 {
468 return VERSION_MIN.parse().unwrap();
469}
470
471#[cfg(feature="pybindings")]
475#[pyfunction]
476fn get_version_patch() -> u8 {
477 return VERSION_PCH.parse().unwrap();
478}
479
480#[cfg(feature="pybindings")]
484#[pyfunction]
485fn version_at_least(version_string : String) -> bool {
486 let parts: Vec<u32> = version_string.split('.')
487 .map(|s| s.parse().expect("Input is not a valid version string. Please follow the MAJOR.MINOR.PATCH format!"))
488 .collect();
489 let major : u8 = parts[0] as u8;
490 let minor : u8 = parts[1] as u8;
491 let patch : u8 = parts[2] as u8;
492 if major < get_version_major() {
493 return true;
494 }
495 if major > get_version_major() {
496 println!("Required major version of {} is larger than current version {}", major, VERSION);
497 return false;
498 }
499 if major == get_version_major() {
500 if minor < get_version_minor() {
501 return true;
502 }
503 if minor > get_version_minor() {
504 println!("Required minor version of {} is larger than current version {}", minor, VERSION);
505 return false;
506 }
507 if minor == get_version_minor() {
508 if patch > get_version_patch() {
509 println!("Required patch version of {} is larger than current version {}", patch, VERSION);
510 return false
511 }
512 }
513 }
514 true
515}
516
517#[cfg(feature="pybindings")]
518pyo3::create_exception!(gondola_core_py, PyMasterTriggerError, pyo3::exceptions::PyException);
519#[cfg(feature="pybindings")]
520pyo3::create_exception!(gondola_core_py, PyRunError, pyo3::exceptions::PyException);
521#[cfg(feature="pybindings")]
522pyo3::create_exception!(gondola_core_py, PyTofError, pyo3::exceptions::PyException);
523#[cfg(feature="pybindings")]
524pyo3::create_exception!(gondola_core_py, PyStagingError, pyo3::exceptions::PyException);
525#[cfg(feature="pybindings")]
526pyo3::create_exception!(gondola_core_py, PySensorError, pyo3::exceptions::PyException);
527#[cfg(feature="pybindings")]
528pyo3::create_exception!(gondola_core_py, PyUserError, pyo3::exceptions::PyException);
529#[cfg(feature="pybindings")]
530pyo3::create_exception!(gondola_core_py, PyCalibrationError, pyo3::exceptions::PyException);
531#[cfg(feature="pybindings")]
532pyo3::create_exception!(gondola_core_py, PyWaveformError, pyo3::exceptions::PyException);
533#[cfg(feature="pybindings")]
534pyo3::create_exception!(gondola_core_py, PyIPBusError, pyo3::exceptions::PyException);
535#[cfg(feature="pybindings")]
536pyo3::create_exception!(gondola_core_py, PySerializationError, pyo3::exceptions::PyException);
537#[cfg(feature="pybindings")]
538pyo3::create_exception!(gondola_core_py, PyAnalysisError, pyo3::exceptions::PyException);
539
540#[macro_export]
541macro_rules! pythonize_error {
542 ($name:ident, $pyname:ident) => {
543
544 impl From<$name> for PyErr {
545 fn from(err: $name) -> PyErr {
546 $pyname::new_err(format!("<GondolaCoreException: {}>", err))
550 }
551 }
552 }
553}
554
555#[cfg(feature="pybindings")]
556pythonize_error!(SerializationError, PySerializationError);
557#[cfg(feature="pybindings")]
558pythonize_error!(AnalysisError, PyAnalysisError);
559#[cfg(feature="pybindings")]
560pythonize_error!(CalibrationError, PyCalibrationError);
561
562
563#[cfg(feature="pybindings")]
568#[pymodule]
569#[pyo3(name = "gondola_core")]
570fn gondola_core_py<'_py>(m : &Bound<'_py, PyModule>) -> PyResult<()> { pyo3_log::init();
572 m.add_function(wrap_pyfunction!(get_version, m)?)?;
573 m.add_function(wrap_pyfunction!(get_version_major, m)?)?;
574 m.add_function(wrap_pyfunction!(get_version_minor, m)?)?;
575 m.add_function(wrap_pyfunction!(get_version_patch, m)?)?;
576 m.add_function(wrap_pyfunction!(version_at_least, m)?)?;
577 m.add_wrapped(wrap_pymodule!(events_py))?;
578 m.add_wrapped(wrap_pymodule!(monitoring_py))?;
579 m.add_wrapped(wrap_pymodule!(packets_py))?;
580 m.add_wrapped(wrap_pymodule!(tof_py))?;
581 m.add_wrapped(wrap_pymodule!(tracker_py))?;
582 m.add_wrapped(wrap_pymodule!(io_py))?;
583 m.add_wrapped(wrap_pymodule!(db_py))?;
584 m.add_wrapped(wrap_pymodule!(stats_py))?;
585 m.add_wrapped(wrap_pymodule!(algo_py))?;
586 m.add_wrapped(wrap_pymodule!(calibration_py))?;
587 Ok(())
590}
591
592
593
594