go_pybindings/
lib.rs

1//! Pybindings for the RUST dataclasses
2//!
3pub mod analysis;
4pub mod io;
5pub mod dataclasses;
6pub mod command_factory;
7#[cfg(feature="telemetry")]
8pub mod telemetry;
9#[cfg(feature="liftof")]
10pub mod liftof;
11#[cfg(feature="liftof")]
12pub mod liftof_dataclasses;
13#[cfg(feature="liftof")]
14pub mod master_trigger;
15#[cfg(feature="caraspace-serial")]
16pub mod caraspace;
17#[cfg(feature="telemetry")]
18use telemetry_dataclasses::packets as tel_api;
19
20use std::env;
21
22use tof_dataclasses::events::EventStatus;
23use tof_dataclasses::commands::config::BuildStrategy;
24
25cfg_if::cfg_if! {
26  if #[cfg(feature = "telemetry")] {
27    use crate::telemetry::{
28      PyTelemetryPacket,
29      PyTelemetryPacketReader,
30      PyMergedEvent,
31      PyTrackerHit,
32      PyTrackerHitV2,
33      PyTrackerEvent,
34      PyTrackerPacket,
35      PyTrackerTempLeakPacket,
36      PyGPSPacket,
37      PyTrackerDAQTempPacket,
38      PyTrackerDAQHSKPacket,
39      PyTrackerEventIDEchoPacket,
40    };
41  }
42}
43
44cfg_if::cfg_if! {
45  if #[cfg(feature = "caraspace-serial")] {
46    use crate::caraspace::{
47      py_parse_u8,
48      py_parse_u16,
49      py_parse_u32,
50      py_parse_u64,
51      PyCRFrame,
52      PyCRReader,
53      PyCRWriter,
54    };
55  }
56}
57
58cfg_if::cfg_if! {
59  if #[cfg(feature = "liftof")] {
60    use crate::liftof::{
61      py_waveform_analysis,
62      wrap_prescale_to_u32,
63      wrap_calc_edep_simple,
64      wrap_fit_sine_sydney,
65      test_db,
66      PyLiftofSettings,
67      PyIPBus,
68      PyMasterTrigger
69    };
70  }
71}
72
73use pyo3::prelude::*;
74use pyo3::wrap_pymodule;
75use pyo3::exceptions::{
76    PyIOError,
77};
78
79use crate::analysis::*;
80use crate::dataclasses::*;
81use crate::io::*;
82use crate::command_factory::*;
83
84// these are already wrapped in a pyclass (enum)
85use tof_dataclasses::packets::PacketType;
86use tof_dataclasses::commands::TofCommandCode;
87use tof_dataclasses::events::master_trigger::{
88  LTBThreshold,
89  TriggerType
90};
91
92// additionally, let's add this functionality
93use tof_dataclasses::database::{
94  get_dsi_j_ch_pid_map,
95  DsiJChPidMapping,
96  RbChPidMapping,
97  get_rb_ch_pid_map,
98  get_rb_ch_pid_a_map,
99  get_rb_ch_pid_b_map,
100  Paddle,
101  TrackerStrip,
102  connect_to_db
103};
104
105///// Add the __repr__ function and 
106///// other common items
107//pub trait Pythonize {
108//}
109
110/// Create a map from the database which allows to map
111/// DSI,J,LTB channel for a connected LTB to the respective
112/// Paddle ID
113///
114/// This will query the database and then create a map
115/// structure, which can then be used for further queries
116///
117/// # Arguments:
118///     db_path : Path to the gaps_flight.db (or similar 
119///               db with paddle information)
120#[pyfunction]
121#[pyo3(name="create_mtb_connection_to_pid_map")]
122fn py_create_mtb_connection_to_pid_map() -> PyResult<DsiJChPidMapping> {
123  let db_path = env::var("DATABASE_URL").unwrap_or_else(|_| "".to_string());
124  match connect_to_db(db_path) {
125    Err(err) => {
126      return Err(PyIOError::new_err(err.to_string()));
127    }
128    Ok(mut conn) => {
129      match Paddle::all(&mut conn) {
130        None => {
131          return Err(PyIOError::new_err("Unable to retrieve paddle information from DB!"));
132        }
133        Some(paddles) => {
134          let mapping = get_dsi_j_ch_pid_map(&paddles);
135          Ok(mapping)
136        }
137      }
138    }
139  }
140}
141
142/// Map RB ID/Ch to Paddle ID. A side exclusively.
143///
144/// This will query the database and then create a map
145/// structure, which can then be used for further queries
146///
147/// # Arguments:
148///     db_path : Path to the gaps_flight.db (or similar 
149///               db with paddle information)
150#[pyfunction]
151#[pyo3(name="create_rb_ch_to_pid_map")]
152fn py_create_rb_ch_to_pid_map(db_path : String) -> PyResult<RbChPidMapping> {
153  match connect_to_db(db_path) {
154    Err(err) => {
155      return Err(PyIOError::new_err(err.to_string()));
156    }
157    Ok(mut conn) => {
158      match Paddle::all(&mut conn) {
159        None => {
160          return Err(PyIOError::new_err("Unable to retrieve paddle information from DB!"));
161        }
162        Some(paddles) => {
163          let mapping = get_rb_ch_pid_map(&paddles);
164          Ok(mapping)
165        }
166      }
167    }
168  }
169}
170
171/// Map RB ID/Ch to Paddle ID. A side exclusively.
172///
173/// This will query the database and then create a map
174/// structure, which can then be used for further queries
175///
176/// # Arguments:
177///     db_path : Path to the gaps_flight.db (or similar 
178///               db with paddle information)
179#[pyfunction]
180#[pyo3(name="create_rb_ch_to_pid_a_map")]
181fn py_create_rb_ch_to_pid_a_map(db_path : String) -> PyResult<RbChPidMapping> {
182  match connect_to_db(db_path) {
183    Err(err) => {
184      return Err(PyIOError::new_err(err.to_string()));
185    }
186    Ok(mut conn) => {
187      match Paddle::all(&mut conn) {
188        None => {
189          return Err(PyIOError::new_err("Unable to retrieve paddle information from DB!"));
190        }
191        Some(paddles) => {
192          let mapping = get_rb_ch_pid_a_map(&paddles);
193          Ok(mapping)
194        }
195      }
196    }
197  }
198}
199
200
201/// Map RB ID/Ch to Paddle ID. A side exclusively.
202///
203/// This will query the database and then create a map
204/// structure, which can then be used for further queries
205///
206/// # Arguments:
207///     db_path : Path to the gaps_flight.db (or similar 
208///               db with paddle information)
209#[pyfunction]
210#[pyo3(name="create_rb_ch_to_pid_b_map")]
211fn py_create_rb_ch_to_pid_b_map(db_path : String) -> PyResult<RbChPidMapping> {
212  match connect_to_db(db_path) {
213    Err(err) => {
214      return Err(PyIOError::new_err(err.to_string()));
215    }
216    Ok(mut conn) => {
217      match Paddle::all(&mut conn) {
218        None => {
219          return Err(PyIOError::new_err("Unable to retrieve paddle information from DB!"));
220        }
221        Some(paddles) => {
222          let mapping = get_rb_ch_pid_b_map(&paddles);
223          Ok(mapping)
224        }
225      }
226    }
227  }
228}
229
230
231/// Create a map of strip id to tracker strip
232#[pyfunction]
233#[pyo3(name="create_tracker_strip_map")]
234fn py_create_tracker_strip_map(db_path : String) -> PyResult<()> {
235  match connect_to_db(db_path) {
236    Err(err) => {
237      return Err(PyIOError::new_err(err.to_string()));
238    }
239    Ok(mut conn) => {
240      match TrackerStrip::all(&mut conn) {
241        None => {
242          return Err(PyIOError::new_err("Unable to retrieve tracker strip information from DB!"));
243        }
244        Some(strips) => {
245          //let mapping = get_rb_ch_pid_b_map(&paddles);
246          //Ok(mapping)
247          for k in strips {
248            println!("Strip : {}", k);
249          }
250          Ok(())
251        }
252      }
253    }
254  }
255}
256
257#[pymodule]
258#[pyo3(name = "analysis")]
259fn tof_analysis<'_py>(m: &Bound<'_py, PyModule>) -> PyResult<()> {
260  m.add_function(wrap_pyfunction!(py_get_periods, m)?)?;
261  m.add_function(wrap_pyfunction!(py_time2bin,m)?)?;
262  m.add_function(wrap_pyfunction!(py_find_peaks,m)?)?;
263  m.add_function(wrap_pyfunction!(py_find_peaks_zscore,m)?)?;
264  m.add_function(wrap_pyfunction!(py_integrate,m)?)?;
265  m.add_function(wrap_pyfunction!(py_interpolate_time,m)?)?;
266  m.add_function(wrap_pyfunction!(py_cfd_simple,m)?)?;
267  m.add_function(wrap_pyfunction!(py_find_zero_crossings,m)?)?;
268  Ok(())
269}
270
271#[pymodule]
272#[pyo3(name = "moni")]
273fn tof_moni<'_py>(m: &Bound<'_py, PyModule>) -> PyResult<()> {
274  m.add_class::<PyPAMoniSeries>()?;
275  m.add_class::<PyPBMoniSeries>()?;
276  m.add_class::<PyRBMoniSeries>()?;
277  m.add_class::<PyMtbMoniSeries>()?;
278  m.add_class::<PyCPUMoniSeries>()?;
279  m.add_class::<PyLTBMoniSeries>()?;
280  m.add_class::<PyRBMoniData>()?;
281  m.add_class::<PyPAMoniData>()?;
282  m.add_class::<PyPBMoniData>()?;
283  m.add_class::<PyLTBMoniData>()?;
284  m.add_class::<PyMtbMoniData>()?;
285  m.add_class::<PyTofDetectorStatus>()?;
286  Ok(())
287}
288
289
290/// I/O features to read TofPackets from disk
291#[pymodule]
292#[pyo3(name = "io")]
293fn tof_io<'_py>(m: &Bound<'_py, PyModule>) -> PyResult<()> {
294  m.add_function(wrap_pyfunction!(py_create_rb_ch_to_pid_map, m)?)?;
295  m.add_function(wrap_pyfunction!(py_create_rb_ch_to_pid_a_map, m)?)?;
296  m.add_function(wrap_pyfunction!(py_create_rb_ch_to_pid_b_map, m)?)?;
297  m.add_function(wrap_pyfunction!(py_create_mtb_connection_to_pid_map, m)?)?;
298  m.add_function(wrap_pyfunction!(py_create_tracker_strip_map, m)?)?;
299  m.add_function(wrap_pyfunction!(py_summarize_toffile, m)?)?;
300  //m.add_class::<PyAdapter>()?;
301  m.add_class::<PyTofPacket>()?;
302  m.add_class::<PyTofPacketReader>()?;
303  m.add_class::<PacketType>()?;
304  Ok(())
305}
306
307/// Event structures for the TOF part of the GAPS experiment
308#[pymodule]
309#[pyo3(name = "events")]
310fn tof_events<'_py>(m: &Bound<'_py, PyModule>) -> PyResult<()> {
311  m.add_class::<PyMasterTriggerEvent>()?;
312  m.add_class::<PyRBEvent>()?;
313  m.add_class::<PyRBEventHeader>()?;
314  m.add_class::<PyTofEvent>()?;
315  m.add_class::<PyTofHit>()?;
316  m.add_class::<PyRBWaveform>()?;
317  m.add_class::<PyRBCalibration>()?;
318  m.add_class::<PyTofEventSummary>()?;
319  m.add_class::<LTBThreshold>()?;
320  m.add_class::<EventStatus>()?;
321  m.add_class::<TriggerType>()?;
322  Ok(())
323}
324
325cfg_if::cfg_if! {
326  if #[cfg(feature = "telemetry")] {
327    #[pymodule]
328    #[pyo3(name = "telemetry")]
329    fn py_telemetry<'_py> (m: &Bound<'_py, PyModule>) -> PyResult<()> {
330      //m.add_function(wrap_pyfunction!(get_gapsevents,m)?)?;
331      m.add_class::<tel_api::TelemetryPacketType>()?;
332      m.add_class::<PyTelemetryPacket>()?;
333      m.add_class::<PyTelemetryPacketReader>()?;
334      m.add_class::<PyMergedEvent>()?;
335      //m.add_class::<PyGapsEvent>()?;
336      //m.add_class::<PyTofHit>()?;
337      //m.add_class::<PyTofEventSummary>()?;
338      m.add_class::<PyTrackerHit>()?;
339      m.add_class::<PyTrackerHitV2>()?;
340      m.add_class::<PyTrackerEvent>()?;
341      m.add_class::<PyTrackerPacket>()?;
342      m.add_class::<PyTrackerTempLeakPacket>()?;
343      m.add_class::<PyGPSPacket>()?;
344      m.add_class::<PyTrackerDAQTempPacket>()?;
345      m.add_class::<PyTrackerDAQHSKPacket>()?;
346      m.add_class::<PyTrackerEventIDEchoPacket>()?;
347      Ok(())
348    }
349  }
350}
351
352cfg_if::cfg_if! {
353  if #[cfg(feature = "liftof")] {
354   /// Python API to rust version of tof-dataclasses.
355   ///
356   /// Currently, this contains only the analysis 
357   /// functions
358    #[pymodule]
359    #[pyo3(name = "liftof")]
360    fn py_liftof<'_py> (m: &Bound<'_py, PyModule>) -> PyResult<()> {
361      m.add_function(wrap_pyfunction!(py_waveform_analysis,m)?)?;
362      m.add_function(wrap_pyfunction!(wrap_calc_edep_simple,m)?)?;
363      m.add_function(wrap_pyfunction!(test_db,m)?)?;
364      m.add_function(wrap_pyfunction!(wrap_prescale_to_u32,m)?)?;
365      m.add_function(wrap_pyfunction!(wrap_fit_sine_sydney,m)?)?;
366      m.add_class::<PyLiftofSettings>()?;
367      m.add_class::<PyIPBus>()?;
368      m.add_class::<PyMasterTrigger>()?;
369      Ok(())
370    }
371  }
372}
373
374cfg_if::cfg_if! {
375  if #[cfg(feature = "caraspace-serial")] {
376    #[pymodule]
377    #[pyo3(name = "caraspace")]
378    fn py_caraspace<'_py> (m: &Bound<'_py, PyModule>) -> PyResult<()> {
379      m.add_function(wrap_pyfunction!(py_parse_u8, m)?)?;
380      m.add_function(wrap_pyfunction!(py_parse_u16, m)?)?;
381      m.add_function(wrap_pyfunction!(py_parse_u32, m)?)?;
382      m.add_function(wrap_pyfunction!(py_parse_u64, m)?)?;
383      //m.add_class::<tel_api::TelemetryPacketType>()?;
384      m.add_class::<PyCRFrame>()?;
385      m.add_class::<PyCRReader>()?;
386      m.add_class::<PyCRWriter>()?;
387      Ok(())
388    }
389  }
390}
391
392#[pymodule]
393#[pyo3(name = "factory")]
394fn tof_command_factory<'_py>(m: &Bound<'_py, PyModule>) -> PyResult<()> {
395  m.add_function(wrap_pyfunction!(py_get_rbratmap_hardcoded, m)?)?;
396  m.add_function(wrap_pyfunction!(py_get_ratrbmap_hardcoded, m)?)?;
397  m.add_function(wrap_pyfunction!(py_get_ratpdumap_hardcoded, m)?)?;
398  m.add_function(wrap_pyfunction!(py_shutdown_rb, m)?)?;
399  m.add_function(wrap_pyfunction!(py_shutdown_all_rbs, m)?)?;
400  m.add_function(wrap_pyfunction!(py_shutdown_rat, m)?)?;
401  m.add_function(wrap_pyfunction!(py_shutdown_ratpair, m)?)?;
402  m.add_function(wrap_pyfunction!(py_shutdown_tofcpu, m)?)?;
403  m.add_function(wrap_pyfunction!(py_restart_liftofrb, m)?)?;
404  m.add_function(wrap_pyfunction!(py_start_run, m)?)?;
405  m.add_function(wrap_pyfunction!(py_stop_run, m)?)?;
406  m.add_function(wrap_pyfunction!(py_rb_calibration, m)?)?;
407  m.add_function(wrap_pyfunction!(py_change_triggerconfig, m)?)?;
408  m.add_function(wrap_pyfunction!(py_change_datapublisherconfig, m)?)?;
409  m.add_function(wrap_pyfunction!(py_change_tofeventbuilderconfig, m)?)?;
410  m.add_function(wrap_pyfunction!(py_change_tofrunconfig, m)?)?;
411  m.add_function(wrap_pyfunction!(py_change_tofrbconfig, m)?)?;
412  Ok(())
413}
414
415
416/// Commands for the whole TOF system
417#[pymodule]
418#[pyo3(name = "commands")]
419fn tof_commands<'_py>(m: &Bound<'_py, PyModule>) -> PyResult<()> {
420  m.add_class::<TofCommandCode>()?;
421  m.add_class::<PyTofCommand>()?;
422  
423  m.add_class::<BuildStrategy>()?;
424
425  m.add_class::<PyTriggerConfig>()?;
426  m.add_class::<PyAnalysisEngineConfig>()?;
427  m.add_class::<PyTOFEventBuilderConfig>()?;
428  m.add_class::<PyDataPublisherConfig>()?;
429  m.add_class::<PyTofRunConfig>()?;
430  m.add_class::<PyTofRBConfig>()?;
431 
432  m.add_class::<PyHeartBeatDataSink>()?;
433  m.add_class::<PyMTBHeartbeat>()?;
434  m.add_class::<PyEVTBLDRHeartbeat>()?;
435  m.add_wrapped(wrap_pymodule!(tof_command_factory))?;
436  Ok(())
437}
438
439/// Python API to rust version of tof-dataclasses.
440///
441/// Currently, this contains only the analysis 
442/// functions
443#[pymodule]
444#[pyo3(name = "go_pybindings")]
445fn go_pybindings<'_py>(m : &Bound<'_py, PyModule>) -> PyResult<()> { //: Python<'_>, m: &PyModule) -> PyResult<()> {
446  pyo3_log::init();
447  //m.add_function(wrap_pyfunction!(py_get_periods,m)?)?;
448  m.add_wrapped(wrap_pymodule!(tof_analysis))?;
449  m.add_wrapped(wrap_pymodule!(tof_moni))?;
450  m.add_wrapped(wrap_pymodule!(tof_io))?;
451  m.add_wrapped(wrap_pymodule!(tof_events))?;
452  m.add_wrapped(wrap_pymodule!(tof_commands))?;
453  #[cfg(feature="telemetry")]
454  m.add_wrapped(wrap_pymodule!(py_telemetry))?;
455  #[cfg(feature="liftof")]
456  m.add_wrapped(wrap_pymodule!(py_liftof))?;
457  #[cfg(feature="caraspace-serial")]
458  m.add_wrapped(wrap_pymodule!(py_caraspace))?;
459  Ok(())
460}