gondola_core/tof/
commands.rs

1// This file is part of gaps-online-software and published 
2// under the GPLv3 license
3
4use crate::prelude::*;
5
6#[derive(Debug, Copy, Clone, PartialEq, FromRepr, AsRefStr, EnumIter)]
7#[cfg_attr(feature = "pybindings", pyclass(eq, eq_int))]
8#[repr(u8)]
9pub enum TofCommandCode {
10  Unknown                  = 0u8,
11  /// en empty command just to check if stuff is online
12  Ping                     = 1u8,
13  /// command code for getting the monitoring data from the component
14  Moni                     = 2u8,
15  /// Kill myself
16  Kill                     = 4u8, // Shi!
17  /// Reload a default (to be defined) config file
18  ResetConfigWDefault      = 5u8,
19  /// Make the current editable config the active config
20  SubmitConfig           = 6u8,
21  /// command code to configure the data publisher thread
22  SetDataPublisherConfig   = 20u8,
23  /// command code for "Set LTB Thresholds"
24  SetLTBThresholds         = 21u8,         
25  /// command code for "Configure MTB"
26  SetMTConfig              = 22u8,     
27  /// command code for chaning general run parameters
28  SetTofRunConfig          = 23u8,
29  /// command code for changing RB parameters
30  SetTofRBConfig           = 24u8,
31  /// command code for AnalysisEngineConfig
32  SetAnalysisEngineConfig  = 27u8,   
33  /// command code for "Set preamp bias"
34  SetPreampBias            = 28u8,         
35  /// Change the settings of the event builder
36  SetTOFEventBuilderConfig = 29u8,
37  /// command code for "Stop Data taking"
38  DataRunStop              = 30u8,  
39  /// command code for "Start Data taking"
40  DataRunStart             = 31u8,    
41  /// command code for "Start validation run"
42  StartValidationRun       = 32u8,         
43  /// command code for "Get all waveforms"
44  GetFullWaveforms         = 41u8,
45  /// command code for "Send the whole event cache over the wire"
46  UnspoolEventCache        = 44u8,
47  /// command code for "Run full calibration"
48  RBCalibration            = 53u8, 
49  /// command code for restarting systemd
50  RestartLiftofRBClients  = 60u8,
51  /// command code for putting liftof-cc in listening mode
52  Listen                  = 70u8,
53  /// command code for putting liftof-cc in staging mode
54  Staging                 = 71u8,
55  /// lock the cmd dispatcher
56  Lock                    = 80u8,
57  /// unlock the cmd dispatcher
58  Unlock                  = 81u8,
59  /// Enable sending of TOF packets
60  SendTofEvents           = 90u8,
61  /// Diesable sending of TofEventPacket
62  NoSendTofEvents         = 91u8,
63  /// Enable sending of RBWaveform packets
64  SendRBWaveforms         = 92u8,
65  /// Disable sending of RBWaveform packets
66  NoSendRBWaveforms       = 93u8,
67  /// Enable RB Channel Masks
68  SetRBChannelMask        = 99u8,
69  /// Shutdown RB - send shutdown now to RB
70  ShutdownRB              = 100u8,
71  /// Change the config file for the next run
72  ChangeNextRunConfig     = 101u8,
73  /// Shutdown RAT - send shutdown command to 2RBs in the same RAT
74  ShutdownRAT             = 102u8,
75  /// Shutdown a pair of RATs (as always two of them are hooked up to the 
76  /// same PDU channel)
77  ShutdownRATPair         = 103u8,
78  /// Shutdown the TOF CPU
79  ShutdownCPU             = 104u8,
80  /// Upload a new config file
81  UploadConfig            = 105u8,
82}
83
84expand_and_test_enum!(TofCommandCode, test_tofcommandcode_repr);
85
86/// A general command class with an arbitrary payload
87///
88/// Since the commands should in general be small
89/// the maixmal payload size is limited to 256 bytes
90///
91/// All commands will get broadcasted and the 
92/// receiver has to figure out if they have 
93/// to rect to that command
94#[derive(Debug, Clone, PartialEq)]
95#[cfg_attr(feature="pybindings", pyclass)]
96pub struct TofCommand {
97  pub command_code : TofCommandCode,
98  pub payload      : Vec<u8>,
99}
100
101impl TofCommand {
102  // BFSW command header 144, 235, 86, 248, 70, 41, 7, 15,
103  pub fn new() -> Self {
104    Self {
105      command_code : TofCommandCode::Unknown,
106      payload      : Vec::<u8>::new(),
107    }
108  }
109
110  //pub fn from_config(cfg_file : String) -> Self {
111  //  let mut cmd = TofCommand::new();
112  //  cmd.command_code = TofCommandCode::UploadConfig:
113
114  //}
115
116  /// In case the command is supposed to change the next run configuration
117  /// we can create it with this function
118  ///
119  /// # Arguments
120  ///
121  ///   * key_value :  a list of keys and a single value (last item of the 
122  ///                  list
123  pub fn forge_changerunconfig(key_value : &Vec<String>) -> Self {
124    let mut cmd = TofCommand::new();
125    cmd.command_code = TofCommandCode::ChangeNextRunConfig;
126    if key_value.len() == 0 {
127      error!("Empty command!");
128      return cmd;
129    }
130    let mut payload_string = String::from("");
131    for k in 0..key_value.len() - 1 {
132      payload_string += &format!("{}::", key_value[k]);
133    }
134    payload_string += key_value.last().unwrap();
135    let mut payload = Vec::<u8>::new();
136    payload.extend_from_slice(payload_string.as_bytes());
137    cmd.payload = payload;
138    cmd
139  }
140
141  /// After the command has been unpackt, reconstruct the 
142  /// key/value string
143  pub fn extract_changerunconfig(&self) -> Option<Vec<String>> {
144    if self.command_code != TofCommandCode::ChangeNextRunConfig {
145      error!("Unable to extract configuration file changes from {}", self);
146      return None;
147    }
148    let mut liftof_config = Vec::<String>::new();
149    match String::from_utf8(self.payload.clone()) {
150      Err(err) => {
151        error!("Unable to extract the String payload! {err}");
152      }
153      Ok(concat_string) => {
154        let foo = concat_string.split("::").collect::<Vec<&str>>().into_iter();
155        for k in foo {
156          liftof_config.push(String::from(k));
157        }
158      }
159    }
160    Some(liftof_config)
161  }
162}
163
164impl TofPackable for TofCommand {
165  const TOF_PACKET_TYPE : TofPacketType = TofPacketType::TofCommandV2;
166}
167
168impl Serialization for TofCommand {
169  
170  const HEAD : u16 = 0xAAAA;
171  const TAIL : u16 = 0x5555;
172
173  fn from_bytestream(stream    : &Vec<u8>, 
174                     pos       : &mut usize) 
175    -> Result<Self, SerializationError>{
176    let mut command = TofCommand::new();
177    if parse_u16(stream, pos) != Self::HEAD {
178      error!("The given position {} does not point to a valid header signature of {}", pos, Self::HEAD);
179      return Err(SerializationError::HeadInvalid {});
180    }
181    command.command_code = TofCommandCode::from(parse_u8(stream, pos));
182    let payload_size     = parse_u8(stream, pos);
183    let payload          = stream[*pos..*pos + payload_size as usize].to_vec();
184    command.payload      = payload;
185    *pos += payload_size as usize;
186    let tail = parse_u16(stream, pos);
187    if tail != Self::TAIL {
188      error!("After parsing the event, we found an invalid tail signature {}", tail);
189      return Err(SerializationError::TailInvalid);
190    }
191    Ok(command)
192  }
193
194  fn to_bytestream(&self) -> Vec<u8> {
195    let mut stream = Vec::<u8>::with_capacity(9);
196    stream.extend_from_slice(&Self::HEAD.to_le_bytes());
197    stream.push(self.command_code as u8);
198    stream.push(self.payload.len() as u8);
199    stream.extend_from_slice(self.payload.as_slice());
200    stream.extend_from_slice(&Self::TAIL.to_le_bytes());
201    stream
202  }
203}
204
205impl Default for TofCommand {
206  fn default() -> Self {
207    Self::new()
208  }
209}
210
211#[cfg(feature = "random")]
212impl FromRandom for TofCommand {
213  fn from_random() -> Self {
214    let mut rng      = rand::rng();
215    let command_code = TofCommandCode::from_random();
216    let payload_size = rng.random::<u8>();
217    let mut payload  = Vec::<u8>::with_capacity(payload_size as usize);
218    for _ in 0..payload_size {
219      payload.push(rng.random::<u8>());
220    }
221    Self {
222      command_code : command_code,
223      payload      : payload
224    }
225  }
226}
227
228impl fmt::Display for TofCommand {
229  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
230    //let cc = RBCommand::command_code_to_string(self.command_code);
231    let mut repr = String::from("<TofCommand");
232    repr += &(format!("\n  cmd code : {}", self.command_code)); 
233    match self.command_code {
234      TofCommandCode::ShutdownRB 
235      | TofCommandCode::ShutdownRAT 
236      | TofCommandCode::ShutdownRATPair => {
237        repr += &(format!("\n Sending shutdown command to RBs {:?}>", self.payload));
238      }
239      _ => {
240        repr += ">";
241      }
242    }
243    write!(f, "{}", repr)
244  }
245}
246
247#[cfg(feature="pybindings")]
248#[pymethods]
249impl TofCommand {
250
251  #[getter]
252  fn get_command_code(&mut self) -> TofCommandCode {
253    self.command_code
254  }
255  
256  #[setter]
257  fn set_command_code(&mut self, command_code : TofCommandCode) {
258    self.command_code = command_code;
259  }
260
261  /// Pack myself nicely in a TofPacket and 
262  /// serialize myself
263  ///
264  /// Can be used to interface with BFSW/GSE
265  /// systems
266  fn wrap_n_pack(&self) -> Vec<u8> {
267    self.pack().to_bytestream()
268  }
269
270  /// An explicit getter for the 
271  /// command code, to interface 
272  /// with BFSW/GSE systems
273  fn get_cc_u8(&self) -> u8 {
274    self.command_code as u8
275  }
276
277  #[pyo3(name="to_bytestream")]
278  fn to_bytestream_py(&self) -> Vec<u8> {
279    self.to_bytestream()
280  }  
281}
282
283#[cfg(feature="pybindings")]
284pythonize_packable!(TofCommand);
285
286/// A hardwired map of RB -> RAT
287#[cfg_attr(feature="pybindings", pyfunction)]
288pub fn get_rbratmap_hardcoded() ->  HashMap<u8,u8> {
289  warn!("Using hardcoded rbratmap!");
290  let mapping = HashMap::<u8,u8>::from(
291      [(1, 10), 
292       (2, 15), 
293       (3,  1),  
294       (4, 15), 
295       (5, 20), 
296       (6, 19), 
297       (7, 17), 
298       (8,  9),
299       (9, 13),  
300       (11,10),
301       (13, 4), 
302       (14, 2), 
303       (15, 1), 
304       (16, 8), 
305       (17,17),
306       (18,13),
307       (19, 7), 
308       (20, 7), 
309       (21, 5), 
310       (22,11),
311       (23, 5), 
312       (24, 6), 
313       (25, 8), 
314       (26,11),
315       (27, 6), 
316       (28,20),
317       (29, 3), 
318       (30, 9), 
319       (31, 3), 
320       (32, 2), 
321       (33,18),
322       (34,18),
323       (35, 4), 
324       (36,19),
325       (39,12),
326       (40,12),
327       (41,14),
328       (42,14),
329       (44,16),
330       (46,16)]);
331  mapping
332}
333
334/// A hardwired map of RAT -> (RB1, RB2)
335#[cfg_attr(feature="pybindings", pyfunction)]
336pub fn get_ratrbmap_hardcoded() ->  HashMap<u8,(u8,u8)> {
337  warn!("Using hardcoded ratrb map!");
338  let mapping = HashMap::<u8,(u8,u8)>::from(
339      [(1, (3,15)), 
340       (2, (32,14)), 
341       (3, (31,29)),  
342       (4, (35,13)), 
343       (5, (23,21)), 
344       (6, (27,24)), 
345       (7, (20,19)), 
346       (8, (16,25)),  
347       (9, (8,30)),
348       (10,(1,11)), 
349       (11,(26,22)), 
350       (12,(39,40)),
351       (13,(9,18)), 
352       (14,(41,42)),
353       (15,(2,4)),
354       (16,(46,44)), 
355       (17,(7,17)), 
356       (18,(33,34)), 
357       (19,(36,6)), 
358       (20,(28,5))]); 
359  mapping
360}
361
362/// A hardwired map of PDU #id PDUCHANNEL #id to (RAT,RAT)
363///
364/// Can be used to synchronize powering down proces for 
365/// RATs
366#[cfg_attr(feature="pybindings", pyfunction)]
367pub fn get_ratpdumap_hardcoded() ->  HashMap<u8,HashMap::<u8, (u8,u8)>> {
368  warn!("Using hardcoded rat-pdu map!");
369  let mut mapping = HashMap::<u8,HashMap::<u8,(u8,u8)>>::new();
370  let mut ch_map = HashMap::<u8, (u8,u8)>::from([(3, (15,16)), (7, (8,9))]);
371  mapping.insert(0u8, ch_map.clone());
372  ch_map = HashMap::<u8, (u8, u8)>::from([(2, (2,17)), (3, (4,5)), (5, (13,14))]);
373  mapping.insert(1u8, ch_map.clone());
374  ch_map = HashMap::<u8, (u8, u8)>::from([(3, (12,20)), (4, (10,11)), (5, (8,9))]);
375  mapping.insert(2u8, ch_map.clone());
376  ch_map = HashMap::<u8, (u8, u8)>::from([(2, (6,7)), (3, (1,3))]);
377  mapping.insert(3u8, ch_map.clone());
378  mapping
379}
380
381/// Send the 'sudo shutdown now' command to a single RB
382///
383/// # Arguements:
384///   * rb :  The RB id of the RB to be shutdown 
385///           (NOT RAT)
386#[cfg_attr(feature="pybindings", pyfunction)]
387pub fn shutdown_rb(rb : u8) -> Option<TofCommand> {
388  let code = TofCommandCode::ShutdownRB;
389  let mut cmd  = TofCommand::new();
390  cmd.command_code = code;
391  cmd.payload = vec![rb];
392  Some(cmd)
393}
394
395/// Send the 'sudo shutdown now' command to all RBs in a RAT
396///
397/// # Arguments:
398///   * rat : The RAT id for the rat the RBs to be 
399///           shutdown live in 
400#[cfg_attr(feature="pybindings", pyfunction)]
401pub fn shutdown_rat(rat : u8) -> Option<TofCommand> {
402  let code = TofCommandCode::ShutdownRAT;
403  let mut cmd  = TofCommand::new();
404  cmd.command_code = code;
405  cmd.payload = Vec::<u8>::new();
406  let ratmap = get_ratrbmap_hardcoded();
407  match ratmap.get(&rat) {
408    None => {
409      error!("Don't know RBs in RAT {}", rat);
410      return None
411    }
412    Some(pair) => {
413      cmd.payload.push(pair.0);
414      cmd.payload.push(pair.1);
415    }
416  }
417  Some(cmd)
418}
419
420/// Send the 'sudo shutdown now' command to all RBs in a RAT
421/// 
422/// This will prepare the shutdown command for the RBs in the 
423/// RATs which are connected to a specific pdu channel
424///
425/// # Arguments:
426///   * pdu        : PDU ID (0-3)
427///   * pduchannel : PDU Channel (0-7)
428#[cfg_attr(feature="pybindings", pyfunction)]
429pub fn shutdown_ratpair(pdu : u8, pduchannel : u8) -> Option<TofCommand> {
430  let code     = TofCommandCode::ShutdownRATPair;
431  let mut cmd  = TofCommand::new();
432  cmd.command_code = code;
433  cmd.payload = Vec::<u8>::new();
434  let ratmap    = get_ratrbmap_hardcoded();
435  let ratpdumap = get_ratpdumap_hardcoded();
436  match ratpdumap.get(&pdu) {
437    None => {
438      error!("Don't know that there is a RAT connected to PDU {}!", pdu);
439      return None;
440    }
441    Some(select_pdu) => {
442      match select_pdu.get(&pduchannel) {
443        None => {
444          error!("Don't know that there is a RAT connected to PDU {}, channel {}!", pdu, pduchannel);
445          return None;
446        }
447        Some(rats) => {
448          match ratmap.get(&rats.0) {
449            Some(rbs) => {
450              cmd.payload.push(rbs.0);
451              cmd.payload.push(rbs.1);
452            }
453            None => {
454              error!("RAT mapping incorrect!");
455              return None;
456            }
457          }
458          match ratmap.get(&rats.1) {
459            Some(rbs) => {
460              cmd.payload.push(rbs.0);
461              cmd.payload.push(rbs.1);
462            },
463            None => {
464              error!("RAT mapping incorrect!");
465              return None;
466            }
467          }
468        }
469      }
470    }
471  }
472  Some(cmd)
473}
474
475/// Send the 'sudo shutdown now' command to
476/// ALL RBs
477#[cfg_attr(feature="pybindings", pyfunction)]
478pub fn shutdown_all_rbs() -> Option<TofCommand> {
479  Some(TofCommand {
480    command_code : TofCommandCode::ShutdownRB,
481    payload      : Vec::<u8>::new()
482  })
483}
484
485/// Send the 'sudo shutdown now command to
486/// the TOF main computer ("TOFCPU")
487#[cfg_attr(feature="pybindings", pyfunction)]
488pub fn shutdown_tofcpu() -> Option<TofCommand> {
489  Some(TofCommand {
490    command_code : TofCommandCode::ShutdownCPU,
491    payload      : Vec::<u8>::new()
492  })
493}
494
495/// Restart the liftof-rb clients on the given boards
496///
497/// # Arguments
498///   * rbs: restart the client on the given rb ids, 
499///          if empty, restart on all of them
500#[cfg_attr(feature="pybindings", pyfunction)]
501pub fn restart_liftofrb(rbs : Vec<u8>) -> Option<TofCommand> {
502  // We don't use & for Vec here, since we need to give it to payload 
503  // so there would be a .clone() anyway and so python can understand
504  // the function argument
505  Some(TofCommand {
506    command_code : TofCommandCode::RestartLiftofRBClients,
507    payload      : rbs
508  })
509}
510
511/// Trigger the start of a new data run with 
512/// the next active config
513#[cfg_attr(feature="pybindings", pyfunction)]
514pub fn start_run() -> Option<TofCommand> {
515  Some(TofCommand {
516    command_code : TofCommandCode::DataRunStart,
517    payload      : Vec::<u8>::new(),
518  })
519}
520
521/// Stop the current active run and idle
522#[cfg_attr(feature="pybindings", pyfunction)]
523pub fn stop_run() -> Option<TofCommand> {
524  Some(TofCommand {
525    command_code : TofCommandCode::DataRunStop,
526    payload      : Vec::<u8>::new(),
527  })
528}
529
530/// Enable verfication runs before every run start
531/// 
532/// A verification run will not send any event
533/// packets, but only a TofDetectorStatus frame
534#[cfg_attr(feature="pybindings", pyfunction)]
535pub fn enable_verification_run(enabled : bool) -> Option<TofCommand> {
536  Some(TofCommand {
537    command_code : TofCommandCode::StartValidationRun,
538    payload      : vec![enabled as u8],
539  })
540}
541
542
543
544/// Run a calibration of all RBs
545///
546/// # Arguments:
547///   * pre_run_calibration : Run the RBCalibration routine before 
548///                           every run start
549    ///   * send_packetes       : Send the RBCalibration packets
550///   * save_events         : Save the events to the RBCalibration
551///                           packets
552#[cfg_attr(feature="pybindings", pyfunction)]
553pub fn calibrate_rbs(pre_run_calibration : bool, send_packets : bool, save_events : bool) -> Option<TofCommand> {
554  let payload = vec![pre_run_calibration as u8, send_packets as u8, save_events as u8];
555  Some(TofCommand {
556    command_code : TofCommandCode::RBCalibration,
557    payload      : payload,
558  })
559}
560
561/// Change the MTBSettings in the config file with relevant trigger settings
562#[cfg_attr(feature="pybindings", pyfunction)]
563pub fn change_triggerconfig(cfg : &TriggerConfig) -> Option<TofCommand> {
564  let payload = cfg.to_bytestream();
565  Some(TofCommand {
566    command_code : TofCommandCode::SetMTConfig,
567    payload      : payload,
568  })
569}
570
571/// Change the EventBuilderSettings in the config file
572#[cfg_attr(feature="pybindings", pyfunction)]
573pub fn change_tofeventbuilderconfig(cfg : &TOFEventBuilderConfig) -> Option<TofCommand> {
574  let payload = cfg.to_bytestream();
575  Some(TofCommand {
576    command_code : TofCommandCode::SetTOFEventBuilderConfig,
577    payload      : payload,
578  })
579}
580
581/// Change the data publisher config part of the config file
582#[cfg_attr(feature="pybindings", pyfunction)]
583pub fn change_datapublisherconfig(cfg : &DataPublisherConfig) -> Option<TofCommand> {
584  let payload = cfg.to_bytestream();
585  Some(TofCommand {
586    command_code : TofCommandCode::SetDataPublisherConfig,
587    payload      : payload,
588  })
589}
590
591/// Change the data publisher config part of the config file
592#[cfg_attr(feature="pybindings", pyfunction)]
593pub fn change_tofrunconfig(cfg : &TofRunConfig) -> Option<TofCommand> {
594  let payload = cfg.to_bytestream();
595  Some(TofCommand {
596    command_code : TofCommandCode::SetTofRunConfig,
597    payload      : payload,
598  })
599}
600
601#[cfg_attr(feature="pybindings", pyfunction)]
602pub fn change_tofrbconfig(cfg : &TofRBConfig) -> Option<TofCommand> {
603  let payload = cfg.to_bytestream();
604  Some(TofCommand {
605    command_code : TofCommandCode::SetTofRBConfig,
606    payload      : payload,
607  })
608}
609
610///// Send the 'sudo shutdown now' command to a single RB
611/////
612///// # Arguements:
613/////   * rb :  The RB id of the RB to be shutdown 
614/////           (NOT RAT)
615//#[pyfunction]
616//#[pyo3(name="shutdown_rb")]
617//pub fn py_shutdown_rb(rb : u8) -> PyResult<TofCommand> {
618//  let cmd = shutdown_rb(rb).unwrap();
619//  Ok(TofCommand { 
620//    command : cmd
621//  })
622//}
623//
624//
625///// Send the 'sudo shutdown now' command to
626///// ALL RBs
627//#[pyfunction]
628//#[pyo3(name="shutdown_all_rbs")]
629//pub fn py_shutdown_all_rbs() -> PyResult<TofCommand> {
630//  let cmd = shutdown_all_rbs().unwrap();
631//  let pycmd = TofCommand { 
632//    command : cmd
633//  };
634//  return Ok(pycmd);
635//}
636//
637///// Send the 'sudo shutdown now' command to all RBs in a RAT
638/////
639///// # Arguments:
640/////   * rat : The RAT id for the rat the RBs to be 
641/////           shutdown live in 
642//#[pyfunction]
643//#[pyo3(name="shutdown_rat")]
644//pub fn py_shutdown_rat(rat : u8) -> PyResult<TofCommand> {
645//  match shutdown_rat(rat) {
646//    None => {
647//      return Err(PyValueError::new_err(format!("There might not be a RAT{}!", rat)));
648//    }
649//    Some(cmd) => {
650//      let pycmd = TofCommand { 
651//       command : cmd
652//      };
653//      return Ok(pycmd);
654//    }
655//  }
656//}
657//
658///// Send the 'sudo shutdown now' command to all RBs 
659///// in the 2 RATs connected to a certain PDU channel
660///// 
661///// This will prepare the shutdown command for the RBs in the 
662///// RATs which are connected to a specific pdu channel
663/////
664///// # Arguments:
665/////   * pdu        : PDU ID (0-3)
666/////   * pduchannel : PDU Channel (0-7)
667//#[pyfunction]
668//#[pyo3(name="shutdown_ratpair")]
669//pub fn py_shutdown_ratpair(pdu : u8, pduchannel : u8) -> PyResult<TofCommand> {
670//  match shutdown_ratpair(pdu, pduchannel) {
671//    None => {
672//      return Err(PyValueError::new_err(format!("There might be an issue with the pdu mapping. Can nto find RATs at PDU {} channel {}!", pdu, pduchannel)));
673//    }
674//    Some(cmd) => {
675//      let pycmd = TofCommand { 
676//       command : cmd
677//      };
678//      return Ok(pycmd);
679//    }
680//  }
681//}
682//
683///// Send the 'sudo shutdown now command to
684///// the TOF main computer ("TOFCPU")
685//#[pyfunction]
686//#[pyo3(name="shutdown_cpu")]
687//pub fn py_shutdown_tofcpu() -> PyResult<TofCommand> {
688//  match shutdown_tofcpu() {
689//    None => {
690//      return Err(PyValueError::new_err(format!("You encounterd a dragon \u{1f409}! We don't know what's going on either.")));
691//    }
692//    Some(cmd) => {
693//      let pycmd = TofCommand { 
694//       command : cmd
695//      };
696//      return Ok(pycmd);
697//    }
698//  }
699//}
700//
701//
702///// Restart the liftof-rb clients on the given boards
703/////
704///// # Arguments
705/////   * rbs: restart the client on the given rb ids, 
706/////          if empty, restart on all of them
707//#[pyfunction]
708//#[pyo3(name="restart_liftofrb")]
709//pub fn py_restart_liftofrb(rbs : Vec<u8>) -> PyResult<TofCommand> {
710//  match restart_liftofrb(&rbs) {
711//    None => {
712//      return Err(PyValueError::new_err(format!("You encounterd a dragon \u{1f409}! We don't know what's going on either.")));
713//    }
714//    Some(cmd) => {
715//      let pycmd = TofCommand { 
716//       command : cmd
717//      };
718//      return Ok(pycmd);
719//    }
720//  }
721//}
722//
723///// Trigger the start of a new data run with 
724///// the next active config
725//#[pyfunction]
726//#[pyo3(name="start_run")]
727//pub fn py_start_run() -> PyResult<TofCommand> {
728//  match start_run() {
729//    None => {
730//      return Err(PyValueError::new_err(format!("You encounterd a dragon \u{1f409}! We don't know what's going on either.")));
731//    }
732//    Some(cmd) => {
733//      let pycmd = TofCommand { 
734//       command : cmd
735//      };
736//      return Ok(pycmd);
737//    }
738//  }
739//}
740//
741///// Stop the current active run and idle
742//#[pyfunction]
743//#[pyo3(name="stop_run")]
744//pub fn py_stop_run() -> PyResult<TofCommand> {
745//  match stop_run() {
746//    None => {
747//      return Err(PyValueError::new_err(format!("You encounterd a dragon \u{1f409}! We don't know what's going on either.")));
748//    }
749//    Some(cmd) => {
750//      let pycmd = TofCommand { 
751//       command : cmd
752//      };
753//      return Ok(pycmd);
754//    }
755//  }
756//}
757//
758///// Run a calibration of all RBs
759/////
760///// # Arguments:
761/////   * pre_run_calibration : Run the RBCalibration routine before 
762/////                           every run start
763/////   * send_packetes       : Send the RBCalibration packets
764/////   * save_events         : Save the events to the RBCalibration
765/////                           packets
766//#[pyfunction]
767//#[pyo3(name="rb_calibration")]
768//pub fn py_rb_calibration(pre_run_calibration : bool, send_packets : bool, save_events : bool) -> PyResult<TofCommand> {
769//  match rb_calibration(pre_run_calibration,send_packets, save_events) {
770//    None => {
771//      return Err(PyValueError::new_err(format!("You encounterd a dragon \u{1f409}! We don't know what's going on either.")));
772//    }
773//    Some(cmd) => {
774//      let pycmd = TofCommand { 
775//       command : cmd
776//      };
777//      return Ok(pycmd);
778//    }
779//  }
780//}
781//
782//
783///// Change the MTBSettings in the config file with relevant trigger settings
784//#[pyfunction]
785//#[pyo3(name="change_triggerconfig")]
786//pub fn py_change_triggerconfig(cfg : &PyTriggerConfig) -> PyResult<TofCommand> {
787//  match change_triggerconfig(&cfg.config) {
788//    None => {
789//      return Err(PyValueError::new_err(format!("You encounterd a dragon \u{1f409}! We don't know what's going on either.")));
790//    }
791//    Some(cmd) => {
792//      let pycmd = TofCommand { 
793//       command : cmd
794//      };
795//      return Ok(pycmd);
796//    }
797//  }
798//}
799//
800//
801///// Change the TOFEventBuilderSettings in the config
802//#[pyfunction]
803//#[pyo3(name="change_tofeventbuilderconfig")]
804//pub fn py_change_tofeventbuilderconfig(cfg : &PyTOFEventBuilderConfig) -> PyResult<TofCommand> {
805//  match change_tofeventbuilderconfig(&cfg.config) {
806//    None => {
807//      return Err(PyValueError::new_err(format!("You encounterd a dragon \u{1f409}! We don't know what's going on either.")));
808//    }
809//    Some(cmd) => {
810//      let pycmd = TofCommand { 
811//       command : cmd
812//      };
813//      return Ok(pycmd);
814//    }
815//  }
816//}
817//
818///// Change the data publisher config part of the config file
819//#[pyfunction]
820//#[pyo3(name="change_datapublisherconfig")]
821//pub fn py_change_datapublisherconfig(cfg : &PyDataPublisherConfig) -> PyResult<TofCommand> {
822//  match change_datapublisherconfig(&cfg.config) {
823//    None => {
824//      return Err(PyValueError::new_err(format!("You encounterd a dragon \u{1f409}! We don't know what's going on either.")));
825//    }
826//    Some(cmd) => {
827//      let pycmd = TofCommand { 
828//       command : cmd
829//      };
830//      return Ok(pycmd);
831//    }
832//  }
833//}
834//
835///// Change the run config part of the config file
836//#[pyfunction]
837//#[pyo3(name="change_tofrunconfig")]
838//pub fn py_change_tofrunconfig(cfg : &PyTofRunConfig) -> PyResult<TofCommand> {
839//  match change_tofrunconfig(&cfg.config) {
840//    None => {
841//      return Err(PyValueError::new_err(format!("You encounterd a dragon \u{1f409}! We don't know what's going on either.")));
842//    }
843//    Some(cmd) => {
844//      let pycmd = TofCommand { 
845//       command : cmd
846//      };
847//      return Ok(pycmd);
848//    }
849//  }
850//}
851//
852///// Change the RB config part of the config file
853//#[pyfunction]
854//#[pyo3(name="change_tofrbconfig")]
855//pub fn py_change_tofrbconfig(cfg : &PyTofRBConfig) -> PyResult<TofCommand> {
856//  match change_tofrbconfig(&cfg.config) {
857//    None => {
858//      return Err(PyValueError::new_err(format!("You encounterd a dragon \u{1f409}! We don't know what's going on either.")));
859//    }
860//    Some(cmd) => {
861//      let pycmd = TofCommand { 
862//       command : cmd
863//      };
864//      return Ok(pycmd);
865//    }
866//  }
867//}
868