gondola_core/tof/
commands.rs

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