gondola_core/tof/
settings.rs

1//! Aggregate settings for the TOF system
2//!
3//! Control the settings for the C&C server
4//! as well as the liftof-clients on the RBs
5//!
6//! Different sections might represent different
7//! threads/aspects of the code
8//!
9// This file is part of gaps-online-software and published 
10// under the GPLv3 license
11
12use crate::prelude::*;
13
14/// Define which entitiy will configure another entity
15#[derive(Debug, Clone, PartialEq, serde::Deserialize, serde::Serialize)]
16pub enum ParameterSetStrategy {
17  /// Configuration through the TOF cpu
18  ControlServer,
19  /// Configuration through the relevant board itself
20  Board
21}
22
23/// Configure the trigger
24#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
25pub struct MTBSettings {
26  /// Select the trigger type for this run
27  pub trigger_type           : TriggerType,
28  /// Select the prescale factor for a run. The
29  /// prescale factor is between 0 (no events)
30  /// and 1.0 (all events). E.g. 0.1 means allow 
31  /// only 10% of the events
32  /// THIS DOES NOT APPLY TO THE GAPS OR POISSON 
33  /// TRIGGER!
34  pub trigger_prescale               : f32,
35  /// Set to true if we want a combo trigger run
36  pub use_combo_trigger              : bool,
37  /// Set the global trigger type. This has to be less 
38  /// strict than the trigger type   
39  pub global_trigger_type            : TriggerType,
40  /// Set the gloabl trigger prescale
41  pub global_trigger_prescale        : f32,
42
43  /// in case trigger_type = "Poisson", set rate here
44  pub poisson_trigger_rate           : u32,
45  /// in case trigger_type = "Gaps", set if we want to use 
46  /// beta
47  pub gaps_trigger_use_beta     : bool,
48  /// In case we are running the fixed rate trigger, set the
49  /// desired rate here
50  /// not sure
51  //pub gaps_trigger_inner_thresh : u32,
52  ///// not sure
53  //pub gaps_trigger_outer_thresh : u32, 
54  ///// not sure
55  //pub gaps_trigger_total_thresh : u32, 
56  ///// not sure
57  //pub gaps_trigger_hit_thresh   : u32,
58  /// Enable trace suppression on the MTB. If enabled, 
59  /// only those RB which hits will read out waveforms.
60  /// In case it is disabled, ALL RBs will readout events
61  /// ALL the time. For this, we need also the eventbuilder
62  /// strategy "WaitForNBoards(40)"
63  pub trace_suppression  : bool,
64  /// The number of seconds we want to wait
65  /// without hearing from the MTB before
66  /// we attempt a reconnect
67  pub mtb_timeout_sec    : u64,
68  /// Time in seconds between housekkeping 
69  /// packets
70  pub mtb_moni_interval  : u64,
71  pub rb_int_window      : u8,
72  pub tiu_emulation_mode : bool,
73  pub tiu_ignore_busy    : bool,
74  pub tofbot_webhook     : String,
75  pub hb_send_interval   : u64,
76  /// Instruct the MTB to ignore the tiu 
77  /// busy time and instead impose always 
78  /// the same deadtime of 600mu sec on 
79  /// itself
80  pub use_fixed_deadtime : Option<bool>,
81}
82
83impl MTBSettings {
84  pub fn new() -> Self {
85    Self {
86      trigger_type            : TriggerType::Unknown,
87      trigger_prescale        : 0.0,
88      poisson_trigger_rate    : 0,
89      gaps_trigger_use_beta   : true,
90      trace_suppression       : true,
91      mtb_timeout_sec         : 60,
92      mtb_moni_interval       : 30,
93      rb_int_window           : 1,
94      tiu_emulation_mode      : false,
95      tiu_ignore_busy         : false,
96      tofbot_webhook          : String::from(""),
97      hb_send_interval        : 30,
98      use_combo_trigger       : false,
99      global_trigger_type     : TriggerType::Unknown,
100      global_trigger_prescale : 1.0,
101      use_fixed_deadtime      : None,
102    }
103  }
104
105  /// Emit a config, so that infomraiton can be transported
106  /// over the wire
107  pub fn emit_triggerconfig(&self) -> TriggerConfig {
108    let mut cfg = TriggerConfig::new();
109    // all fields should be active, since the settings file 
110    // contains all fields per definition. We can already 
111    // be future proof and just set all of them
112    cfg.active_fields          = u32::MAX;
113    cfg.gaps_trigger_use_beta  = Some(self.gaps_trigger_use_beta);
114    cfg.prescale               = Some(self.trigger_prescale);
115    cfg.trigger_type           = Some(self.trigger_type);
116    cfg.use_combo_trigger      = Some(self.use_combo_trigger);
117    cfg.combo_trigger_type     = Some(self.global_trigger_type);
118    cfg.combo_trigger_prescale = Some(self.global_trigger_prescale);
119    cfg.trace_suppression      = Some(self.trace_suppression);
120    cfg.mtb_moni_interval      = Some((self.mtb_moni_interval & 0xffff) as u16); 
121    cfg.tiu_ignore_busy        = Some(self.tiu_ignore_busy); 
122    cfg.hb_send_interval       = Some((self.hb_send_interval & 0xffff) as u16); 
123    cfg
124  }
125
126  /// Change seetings accordingly to config 
127  pub fn from_triggerconfig(&mut self, cfg : &TriggerConfig) {
128    if cfg.gaps_trigger_use_beta.is_some() {
129      self.gaps_trigger_use_beta   = cfg.gaps_trigger_use_beta.unwrap() ;
130    }
131    if cfg.prescale.is_some() {
132      self.trigger_prescale        = cfg.prescale.unwrap()              ;
133    }
134    if cfg.trigger_type.is_some() {
135      self.trigger_type            = cfg.trigger_type.unwrap()          ; 
136    }
137    if cfg.use_combo_trigger.is_some() {
138      self.use_combo_trigger       = cfg.use_combo_trigger.unwrap()     ;
139    }
140    if cfg.combo_trigger_type.is_some() {
141      self.global_trigger_type     = cfg.combo_trigger_type.unwrap()    ;
142    }
143    if cfg.combo_trigger_prescale.is_some() {
144      self.global_trigger_prescale = cfg.combo_trigger_prescale.unwrap();
145    }
146    if cfg.trace_suppression.is_some() {
147      self.trace_suppression       = cfg.trace_suppression.unwrap()     ;
148    }
149    if cfg.mtb_moni_interval.is_some() {
150      self.mtb_moni_interval       = cfg.mtb_moni_interval.unwrap() as u64;
151    }
152    if cfg.tiu_ignore_busy.is_some() {
153      self.tiu_ignore_busy         = cfg.tiu_ignore_busy.unwrap()       ;
154    }
155    if cfg.hb_send_interval.is_some() {
156      self.hb_send_interval        = cfg.hb_send_interval.unwrap()  as u64;
157    }
158  }
159}
160
161impl fmt::Display for MTBSettings {
162  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
163    let disp = toml::to_string(self).unwrap_or(
164      String::from("-- DESERIALIZATION ERROR! --"));
165    write!(f, "<MTBSettings :\n{}>", disp)
166  }
167}
168
169impl Default for MTBSettings {
170  fn default() -> Self {
171    Self::new()
172  }
173}
174
175#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
176pub struct PreampSettings {
177  /// actually apply the below settings
178  pub set_preamp_voltages    : bool,
179  /// liftof-cc will send commands to set the 
180  /// preamp bias voltages
181  pub set_strategy           : ParameterSetStrategy,
182  /// preamp biases (one set of 16 values per RAT
183  pub rat_preamp_biases      : HashMap<String, [f32;16]>
184}
185
186impl PreampSettings {
187  pub fn new() -> Self {
188    //let default_biases = HashMap::<u8, [f32;16]>::new();
189    let default_biases = HashMap::from([
190      (String::from("RAT01"), [58.0;16]),
191      (String::from("RAT02"), [58.0;16]),
192      (String::from("RAT03"), [58.0;16]),
193      (String::from("RAT04"), [58.0;16]),
194      (String::from("RAT05"), [58.0;16]),
195      (String::from("RAT06"), [58.0;16]),
196      (String::from("RAT07"), [58.0;16]),
197      (String::from("RAT08"), [58.0;16]),
198      (String::from("RAT09"), [58.0;16]),
199      (String::from("RAT10"), [58.0;16]),
200      (String::from("RAT11"), [58.0;16]),
201      (String::from("RAT12"), [58.0;16]),
202      (String::from("RAT13"), [58.0;16]),
203      (String::from("RAT14"), [58.0;16]),
204      (String::from("RAT15"), [58.0;16]),
205      (String::from("RAT16"), [58.0;16]),
206      (String::from("RAT17"), [58.0;16]),
207      (String::from("RAT18"), [58.0;16]),
208      (String::from("RAT19"), [58.0;16]),
209      (String::from("RAT20"), [58.0;16])]);
210
211    Self {
212      set_preamp_voltages    : false,
213      set_strategy           : ParameterSetStrategy::ControlServer,
214      rat_preamp_biases      : default_biases,
215    }
216  }
217
218  #[cfg(feature="database")]
219  pub fn emit_pb_settings_packets(&self, rats : &HashMap<u8,RAT>) -> Vec<TofPacket> {
220    let mut packets = Vec::<TofPacket>::new();
221    for k in rats.keys() {
222      let rat          = &rats[&k];
223      let rat_key      = format!("RAT{:2}", rat);
224      let mut cmd      = TofCommand::new();
225      cmd.command_code = TofCommandCode::SetPreampBias;
226      let mut payload  = PreampBiasConfig::new();
227      payload.rb_id    = rat.rb2_id as u8;
228      if *k as usize >= self.rat_preamp_biases.len() {
229        error!("RAT ID {k} larger than 20!");
230        continue;
231      }
232      payload.biases = self.rat_preamp_biases[&rat_key];
233      cmd.payload = payload.to_bytestream();
234      let tp = cmd.pack();
235      packets.push(tp);
236    }
237    packets
238  }
239}
240
241impl fmt::Display for PreampSettings {
242  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
243    let disp : String;
244    match toml::to_string(self) {
245      Err(err) => {
246        error!("Deserialization error! {err}");
247        disp = String::from("-- DESERIALIZATION ERROR! --");
248      }
249      Ok(_disp) => {
250        disp = _disp;
251      }
252    }
253    write!(f, "<PreampBiasSettings :\n{}>", disp)
254  }
255}
256
257impl Default for PreampSettings {
258  fn default() -> Self {
259    Self::new()
260  }
261}
262
263#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
264pub struct LTBThresholdSettings {
265  /// actually apply the below settings
266  pub set_ltb_thresholds    : bool,
267  /// liftof-cc will send commands to set the 
268  /// ltb thresholds
269  pub set_strategy          : ParameterSetStrategy,
270  /// ltb threshold voltages (one set of 3 values per RAT)
271  pub rat_ltb_thresholds    : HashMap<String, [f32;3]>
272}
273
274impl LTBThresholdSettings {
275  pub fn new() -> Self {
276    let default_thresholds = HashMap::from([
277      (String::from("RAT01"), [40.0,32.0,375.0]),
278      (String::from("RAT02"), [40.0,32.0,375.0]),
279      (String::from("RAT03"), [40.0,32.0,375.0]),
280      (String::from("RAT04"), [40.0,32.0,375.0]),
281      (String::from("RAT05"), [40.0,32.0,375.0]),
282      (String::from("RAT06"), [40.0,32.0,375.0]),
283      (String::from("RAT07"), [40.0,32.0,375.0]),
284      (String::from("RAT08"), [40.0,32.0,375.0]),
285      (String::from("RAT09"), [40.0,32.0,375.0]),
286      (String::from("RAT10"), [40.0,32.0,375.0]),
287      (String::from("RAT11"), [40.0,32.0,375.0]),
288      (String::from("RAT12"), [40.0,32.0,375.0]),
289      (String::from("RAT13"), [40.0,32.0,375.0]),
290      (String::from("RAT14"), [40.0,32.0,375.0]),
291      (String::from("RAT15"), [40.0,32.0,375.0]),
292      (String::from("RAT16"), [40.0,32.0,375.0]),
293      (String::from("RAT17"), [40.0,32.0,375.0]),
294      (String::from("RAT18"), [40.0,32.0,375.0]),
295      (String::from("RAT19"), [40.0,32.0,375.0]),
296      (String::from("RAT20"), [40.0,32.0,375.0])]);
297
298      Self {
299        set_ltb_thresholds    : false,
300        set_strategy          : ParameterSetStrategy::ControlServer,
301        rat_ltb_thresholds    : default_thresholds,
302      }
303  }
304
305  #[cfg(feature="database")]
306  pub fn emit_ltb_settings_packets(&self, rats : &HashMap<u8,RAT>) -> Vec<TofPacket> {
307    let mut packets = Vec::<TofPacket>::new();
308    for k in rats.keys() {
309      let rat          = &rats[&k];
310      let rat_key      = format!("RAT{:2}", rat);
311      let mut cmd      = TofCommand::new();
312      cmd.command_code = TofCommandCode::SetLTBThresholds;
313      let mut payload  = LTBThresholdConfig::new();
314      payload.rb_id    = rat.rb1_id as u8;
315      if *k as usize >= self.rat_ltb_thresholds.len() {
316        error!("RAT ID {k} larger than 20!");
317        continue;
318      }
319      payload.thresholds = self.rat_ltb_thresholds[&rat_key];
320      cmd.payload = payload.to_bytestream();
321      let tp = cmd.pack();
322      packets.push(tp);
323    }
324    packets
325  }
326}
327
328impl fmt::Display for LTBThresholdSettings {
329  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
330    let disp : String;
331    match toml::to_string(self) {
332      Err(err) => {
333        error!("Deserialization error! {err}");
334        disp = String::from("-- DESERIALIZATION ERROR! --");
335      }
336      Ok(_disp) => {
337        disp = _disp;
338      }
339    }
340    write!(f, "<LTBThresholdSettings :\n{}>", disp)
341  }
342}
343
344impl Default for LTBThresholdSettings {
345  fn default() -> Self {
346    Self::new()
347  }
348}
349
350#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
351pub struct CommandDispatcherSettings {
352  /// Log all commands into this file
353  /// Set to "/dev/null" to turn off.
354  /// The mode will be always "append", since we don't 
355  /// expect a lot of logging
356  pub cmd_log_path               : String,
357  /// The address of the liftof-command & control server
358  /// that is the ip address on the RBNetwork which the 
359  /// liftof-cc instance runs on 
360  /// This address will be used as "PUB" for the CommandDispather
361  /// This address has to be within the RB network
362  pub cc_server_address          : String,   
363  /// The address ("tcp://xx.xx.xx.xx:xxxxx") the tof computer should subscribe to 
364  /// to get commands from the flight computer. This address is within the 
365  /// flight network
366  pub fc_sub_address             : String,
367  /// Interval of time that will elapse from a cmd check to the other
368  pub cmd_listener_interval_sec  : u64,
369  /// Safety mechanism - is this is on, the command listener will deny 
370  /// every request. E.g. in staging mode to guarantee no tinkering
371  pub deny_all_requests          : bool
372}
373
374impl CommandDispatcherSettings {
375  pub fn new() -> Self {
376    Self {
377      cmd_log_path                   : String::from("/home/gaps/log"),
378      cc_server_address              : String::from("tcp://10.0.1.10:42000"),   
379      fc_sub_address                 : String::from("tcp://192.168.37.200:41662"),
380      cmd_listener_interval_sec      : 1,
381      deny_all_requests              : false
382    }
383  }
384}
385
386impl fmt::Display for CommandDispatcherSettings {
387  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
388    let disp = toml::to_string(self).unwrap_or(
389      String::from("-- DESERIALIZATION ERROR! --"));
390    write!(f, "<CommandDispatcherSettings :\n{}>", disp)
391  }
392}
393
394impl Default for CommandDispatcherSettings {
395  fn default() -> Self {
396    Self::new()
397  }
398}
399
400/// Readout strategy for RB (onboard) (RAM) memory buffers
401#[derive(Clone, Copy, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
402pub enum RBBufferStrategy {
403  /// Readout and switch the buffers every
404  /// x events
405  NEvents(u16),
406  /// Readout the  buffers every NSeconds
407  /// (Argument is in seconds)
408  NSeconds(f32),
409  /// This will use the measured reate on the actual RB 
410  /// to set a sensitive buffer trip value
411  ActuallySmart, 
412}
413
414impl fmt::Display for RBBufferStrategy {
415  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
416    let r = serde_json::to_string(self).unwrap_or(
417      String::from("N.A. - Invalid RBBufferStrategy (error)"));
418    write!(f, "<RBBufferStrategy: {}>", r)
419  }
420}
421
422
423/// Settings for the specific clients on the RBs (liftof-rb)
424#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
425pub struct RBSettings {
426  /// Don't send events if they have issues. Requires
427  /// EventStatus::Perfect. This can not work in the
428  /// OperationMode RBHighThroughput
429  pub only_perfect_events           : bool,
430  /// Calculate the crc32 sum for each channel and set
431  /// the EventStatus flag accordingly.
432  pub calc_crc32                    : bool,
433  /// tof operation mode - either "StreamAny",
434  /// "RequestReply" or "RBHighThroughput"
435  pub tof_op_mode                   : TofOperationMode,
436  /// if different from 0, activate RB self trigger
437  /// in poisson mode
438  pub trigger_poisson_rate          : u32,
439  /// if different from 0, activate RB self trigger 
440  /// with fixed rate setting
441  pub trigger_fixed_rate            : u32,
442  pub data_type                     : DataType,
443  /// This allows for different strategies on how to readout 
444  /// the RB buffers. The following refers to the NEvent strategy.
445  /// The value when the readout of the RB buffers is triggered.
446  /// This number is in size of full events, which correspond to 
447  /// 18530 bytes. Maximum buffer size is a bit more than 3000 
448  /// events. Smaller buffer allows for a more snappy reaction, 
449  /// but might require more CPU resources (on the board)
450  /// For RBBufferStrategy::AdaptToRate(k), readout (and switch) the buffers every
451  /// k seconds. The size of the buffer will be determined
452  /// automatically depending on the rate.
453  /// This can be set for each RB individually
454  pub rb_buff_strategy               : HashMap<String,RBBufferStrategy>,
455  /// The general moni interval. Whenever this time in seconds has
456  /// passed, the RB will send a RBMoniData packet
457  pub rb_moni_interval               : f32,
458  /// Powerboard monitoring. Do it every multiple of rb_moni_interval
459  pub pb_moni_every_x                : f32,
460  /// Preamp monitoring. Do it every multiple of rb_moni_interval
461  pub pa_moni_every_x                : f32,
462  /// LTB monitoring. Do it every multiple of rb_moni_interval
463  pub ltb_moni_every_x               : f32,
464  /// Choose between drs deadtime or fpga 
465  pub drs_deadtime_instead_fpga_temp : bool
466}
467
468impl RBSettings {
469  pub fn new() -> Self {
470    let mut rb_buff_strategy  = HashMap::<String, RBBufferStrategy>::new();
471    let rb_ids : Vec<u8>  = vec![1,2,3,4,5,6,7,8,9,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,39,40,41,42,46];
472    if rb_ids.len() != 40 {
473      panic!("Something is wrong with the rb ids, we see {} of them!", rb_ids.len());
474    }
475    for k in rb_ids {
476      let key : String = format!("{k}");
477      rb_buff_strategy.insert(key, RBBufferStrategy::ActuallySmart);
478    }
479    Self {
480      only_perfect_events            : false,
481      calc_crc32                     : false,
482      tof_op_mode                    : TofOperationMode::Default,
483      trigger_fixed_rate             : 0,
484      trigger_poisson_rate           : 0,
485      data_type                      : DataType::Physics,
486      //rb_buff_strategy               : HashMap::<u8,RBBufferStrategy::AdaptToRate(5)>,
487      rb_buff_strategy               : rb_buff_strategy,
488      rb_moni_interval               : 0.0,
489      pb_moni_every_x                : 0.0,
490      pa_moni_every_x                : 0.0,
491      ltb_moni_every_x               : 0.0,
492      drs_deadtime_instead_fpga_temp : false
493    }
494  }
495
496  pub fn from_tofrbconfig(&mut self, cfg : &TofRBConfig) {
497    if cfg.rb_moni_interval.is_some() {
498      self.rb_moni_interval = cfg.rb_moni_interval.unwrap() as f32;              
499    }
500    if cfg.rb_moni_interval.is_some() {
501      self.pb_moni_every_x  = cfg.pb_moni_every_x.unwrap() as f32;              
502    }
503    if cfg.rb_moni_interval.is_some() {
504      self.pa_moni_every_x  = cfg.pa_moni_every_x.unwrap() as f32;              
505    }
506    if cfg.rb_moni_interval.is_some() {
507      self.ltb_moni_every_x = cfg.ltb_moni_every_x.unwrap() as f32;              
508    }
509    if cfg.rb_moni_interval.is_some() {
510      self.drs_deadtime_instead_fpga_temp = cfg.drs_deadtime_instead_fpga_temp.unwrap(); 
511    }
512  }
513
514  pub fn get_runconfig(&self, board_id : u8) -> RunConfig {
515    // missing here - run id, nevents, nseconds,
516    //
517    let mut rcfg              = RunConfig::new();
518    rcfg.is_active            = true;
519    rcfg.tof_op_mode          = self.tof_op_mode.clone();
520    rcfg.trigger_fixed_rate   = self.trigger_fixed_rate;
521    rcfg.trigger_poisson_rate = self.trigger_poisson_rate;
522    rcfg.data_type            = self.data_type.clone();
523    let key : String = format!("{board_id}");
524    let buffer_strategy = self.rb_buff_strategy.get(&key).unwrap();
525    match buffer_strategy {
526      RBBufferStrategy::NEvents(buff_size) => {
527        rcfg.rb_buff_size = Some(*buff_size as u16);
528      },
529      RBBufferStrategy::NSeconds(interval) => {
530        rcfg.rb_buff_empty_interval = Some(*interval as f32);
531      },
532      RBBufferStrategy::ActuallySmart => {
533        rcfg.rb_buff_strategy_smart = true;
534      }
535    }
536    rcfg
537  }
538}
539
540impl fmt::Display for RBSettings {
541  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
542    let disp = toml::to_string(self).unwrap_or(
543      String::from("-- DESERIALIZATION ERROR! --"));
544    write!(f, "<RBSettings :\n{}>", disp)
545  }
546}
547
548impl Default for RBSettings {
549  fn default() -> Self {
550    Self::new()
551  }
552}
553
554
555/// Settings to change the configuration of the analysis engine 
556/// (pulse extraction)
557#[derive(Debug, Copy, Clone, serde::Serialize, serde::Deserialize)]
558#[cfg_attr(feature="pybindings", pyclass)]
559pub struct AnalysisEngineSettings {
560  /// pulse integration start
561  pub integration_start      : f32,
562  /// pulse integration window
563  pub integration_window     : f32, 
564  /// Pedestal threshold
565  pub pedestal_thresh        : f32,
566  /// Pedestal begin bin
567  pub pedestal_begin_bin     : usize,
568  /// Pedestal width (bins)
569  pub pedestal_win_bins      : usize,
570  /// Use a zscore algorithm to find the peaks instead
571  /// of Jeff's algorithm
572  pub use_zscore             : bool,
573  /// Peakfinding start time
574  pub find_pks_t_start       : f32,
575  /// Peakfinding window
576  pub find_pks_t_window      : f32,
577  /// Minimum peaksize (bins)
578  pub min_peak_size          : usize,
579  /// Threshold for peak recognition
580  pub find_pks_thresh        : f32,
581  /// Max allowed peaks
582  pub max_peaks              : usize,
583  /// Timing CFG fraction
584  pub cfd_fraction           : f32,
585  /// Time over threshold threshold in mV for 
586  /// the lower
587  pub tot_threshold_high     : Option<f32>,
588  /// Time over threshold threshold in mV for 
589  /// the upper
590  pub tot_threshold_low      : Option<f32>
591}
592
593impl AnalysisEngineSettings {
594  pub fn new() -> Self {
595    Self {
596      integration_start         : 270.0,
597      integration_window        : 70.0, 
598      pedestal_thresh           : 10.0,
599      pedestal_begin_bin        : 10,
600      pedestal_win_bins         : 50,
601      use_zscore                : false,
602      find_pks_t_start          : 270.0,
603      find_pks_t_window         : 70.0,
604      min_peak_size             : 3,
605      find_pks_thresh           : 10.0,
606      max_peaks                 : 5,
607      cfd_fraction              : 0.2,
608      tot_threshold_low         : Some(250.0),
609      tot_threshold_high        : Some(750.0),
610    }
611  }
612}
613
614impl fmt::Display for AnalysisEngineSettings {
615  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
616    let disp = toml::to_string(self).unwrap_or(
617      String::from("-- DESERIALIZATION ERROR! --"));
618    write!(f, "<AnalysisEngineSettings :\n{}>", disp)
619  }
620}
621
622impl Default for AnalysisEngineSettings {
623  fn default() -> Self {
624    Self::new()
625  }
626}
627
628//--------------------------------------------------------------
629
630
631#[cfg(feature="pybindings")]
632#[pymethods]
633impl AnalysisEngineSettings {
634
635  #[getter]
636  fn get_integration_start(&self) -> f32 {
637    self.integration_start
638  }
639
640  #[setter]
641  fn set_integration_start(&mut self, value : f32) {
642    self.integration_start = value;
643  }
644  
645  #[getter]
646  fn get_integration_window(&self) -> f32 {
647    self.integration_window
648  }
649
650  #[setter]
651  fn set_integration_window(&mut self, value : f32) {
652    self.integration_window = value;
653  }
654  
655  #[getter]
656  fn get_pedestal_thresh(&self) -> f32 {
657    self.pedestal_thresh
658  }
659
660  #[setter]
661  fn set_pedestal_thresh(&mut self, value : f32) {
662    self.pedestal_thresh = value;
663  }
664  
665  #[getter]
666  fn get_pedestal_begin_bin(&self) -> usize {
667    self.pedestal_begin_bin 
668  }
669
670  #[setter]
671  fn set_pedestal_begin_bin(&mut self, value : usize) {
672    self.pedestal_begin_bin = value;
673  }
674  
675  #[getter]
676  fn get_pedestal_win_bins(&self) -> usize {
677    self.pedestal_win_bins
678  }
679
680  #[setter]
681  fn set_pedestal_win_bins(&mut self, value : usize) {
682    self.pedestal_win_bins = value;
683  }
684  
685  #[getter]
686  fn get_use_zscore(&self) -> bool {
687    self.use_zscore
688  }
689
690  #[setter]
691  fn set_use_zscore(&mut self, value : bool) {
692    self.use_zscore = value; 
693  }
694
695  #[getter]
696  fn get_find_pks_t_start(&self) -> f32 {
697    self.find_pks_t_start
698  }
699
700  #[setter]
701  fn set_find_pks_t_start(&mut self, value : f32) {
702    self.find_pks_t_start = value;
703  }
704
705  #[getter]
706  fn get_find_pks_t_window(&self) -> f32 {
707    self.find_pks_t_window
708  }
709
710  #[setter]
711  fn set_find_pks_t_window(&mut self, value : f32) {
712    self.find_pks_t_window = value;
713  }
714  
715  #[getter]
716  fn get_min_peak_size(&self) -> usize {
717    self.min_peak_size
718  }
719
720  #[setter]
721  fn set_min_peak_size(&mut self, value : usize) {
722    self.min_peak_size = value;
723  }
724  
725  #[getter]
726  fn get_find_pks_thresh(&self) -> f32 {
727    self.find_pks_thresh
728  }
729
730  #[setter]
731  fn set_find_pks_thresh(&mut self, value : f32) {
732    self.find_pks_thresh = value; 
733  }
734  
735  #[getter]
736  fn get_max_peaks(&self) -> usize {
737    self.max_peaks
738  }
739
740  #[setter]
741  fn set_max_peaks(&mut self, value : usize) {
742    self.max_peaks = value;
743  }
744
745  #[getter]
746  fn get_cfd_fraction(&self) -> f32 {
747    self.cfd_fraction
748  }
749
750  #[setter]
751  fn set_cfd_fraction(&mut self, value : f32) {
752    self.cfd_fraction = value;
753  }
754  
755  #[getter]
756  fn get_tot_threshold_low(&self) -> Option<f32> {
757    self.tot_threshold_low
758  }
759
760  #[setter]
761  fn set_tot_threshold_low(&mut self, value : Option<f32>) {
762    self.tot_threshold_low = value;
763  }
764  
765  #[getter]
766  fn get_tot_threshold_high(&self) -> Option<f32> {
767    self.tot_threshold_high
768  }
769
770  #[setter]
771  fn set_tot_threshold_high(&mut self, value : Option<f32>) {
772    self.tot_threshold_high = value;
773  }
774}
775
776//-------------------------------------------------------------
777
778#[cfg(feature="pybindings")]
779pythonize!(AnalysisEngineSettings);
780
781//--------------------------------------------------------------
782
783/// Settings to change the configuration of the TOF Eventbuilder
784#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
785pub struct TofEventBuilderSettings {
786  pub cachesize             : u32,
787  pub n_mte_per_loop        : u32,
788  pub n_rbe_per_loop        : u32,
789  /// The timeout parameter for the TofEvent. If not
790  /// complete after this time, send it onwards anyway
791  pub te_timeout_sec        : u32,
792  /// The timeout parameter for the TofEvent, but only 
793  /// for the combo trigger part. 
794  pub te_timeout_sec_combo  : Option<u32>,
795  /// Only do something when holdoff time has passed 
796  pub holdoff               : Option<u32>,
797  /// try to sort the events by id (uses more resources)
798  pub sort_events           : bool,
799  pub build_strategy        : BuildStrategy,
800  pub greediness            : u8,
801  pub wait_nrb              : u8,
802  pub hb_send_interval      : u16,
803  /// Analyze the trigger hits and check if the hits are expected
804  /// from a known dead RB. if so, then adjust the expectation
805  /// of number of readoutboards by subtracting the number of 
806  /// expected dead boards from the seen rb_link_ids in this event
807  pub no_expect_dead_rbs    : Option<bool>,
808  pub ignore_mtb_link_ids   : Option<Vec<u8>>,
809  /// Allows to restrict saving the event to disk
810  /// based on the interesting event parameters
811  /// (These are minimum values)
812  pub only_save_interesting : bool,
813  pub thr_n_hits_umb        : Option<u8>,
814  pub thr_n_hits_cbe        : Option<u8>,
815  pub thr_n_hits_cor        : Option<u8>,
816  pub thr_n_hits_outer      : Option<u8>,
817  pub thr_tot_edep_outer    : Option<f32>,
818  pub thr_tot_edep_umb      : Option<f32>,
819  pub thr_tot_edep_cbe      : Option<f32>,
820  pub thr_tot_edep_cor      : Option<f32>,
821  // level 1 purge
822  pub rbe_purge_limit1      : Option<u32>,
823  pub rbe_purge_limit1_n    : Option<u32>,
824  pub rbe_purge_ev_time1    : Option<u32>,
825  // level 2 purge
826  pub rbe_purge_limit2      : Option<u32>,
827  pub rbe_purge_limit2_n    : Option<i32>,
828  pub rbe_purge_ev_time2    : Option<u32>,
829  // level 3 purge
830  pub rbe_purge_limit3      : Option<u32>,
831  pub rbe_purge_limit3_n    : Option<i32>,
832  pub rbe_purge_ev_time3    : Option<u32>,
833}
834
835impl TofEventBuilderSettings {
836  pub fn new() -> TofEventBuilderSettings {
837    TofEventBuilderSettings {
838      cachesize             : 100000,
839      n_mte_per_loop        : 1,
840      n_rbe_per_loop        : 40,
841      te_timeout_sec        : 30,
842      te_timeout_sec_combo  : Some(30),
843      holdoff               : Some(0),
844      sort_events           : false,
845      build_strategy        : BuildStrategy::Adaptive,
846      greediness            : 3,
847      wait_nrb              : 40,
848      hb_send_interval      : 30,
849      only_save_interesting : false,
850      no_expect_dead_rbs    : None,
851      ignore_mtb_link_ids   : None,
852      thr_n_hits_umb        : None,
853      thr_n_hits_cbe        : None,
854      thr_n_hits_cor        : None,
855      thr_n_hits_outer      : None,
856      thr_tot_edep_umb      : None,
857      thr_tot_edep_cbe      : None,
858      thr_tot_edep_cor      : None,
859      thr_tot_edep_outer    : None,
860      rbe_purge_limit1      : None,
861      rbe_purge_limit1_n    : None,
862      rbe_purge_ev_time1    : None,
863      rbe_purge_limit2      : None,
864      rbe_purge_limit2_n    : None,
865      rbe_purge_ev_time2    : None,
866      rbe_purge_limit3      : None,
867      rbe_purge_limit3_n    : None,
868      rbe_purge_ev_time3    : None,
869    }
870  }
871
872  //pub fn from_tofeventbuilderconfig(&mut self, cfg : &TOFEventBuilderConfig) {
873  //  if cfg.cachesize.is_some() {
874  //    self.cachesize = cfg.cachesize.unwrap();
875  //  }
876  //  if cfg.n_mte_per_loop.is_some() {
877  //    self.n_mte_per_loop = cfg.n_mte_per_loop.unwrap();
878  //  }
879  //  if cfg.n_rbe_per_loop.is_some() {
880  //    self.n_rbe_per_loop = cfg.n_rbe_per_loop.unwrap();
881  //  }
882  //  if cfg.te_timeout_sec.is_some() {
883  //    self.te_timeout_sec = cfg.te_timeout_sec.unwrap();
884  //  }
885  //  if cfg.te_timeout_sec_combo.is_some() {
886  //    self.te_timeout_sec_combo = Some(cfg.te_timeout_sec_combo.unwrap());
887  //  }
888  //  if cfg.holdoff.is_some() {
889  //    self.holdoff = Some(cfg.holdoff.unwrap());
890  //  }
891  //  if cfg.sort_events.is_some() {
892  //    self.sort_events = cfg.sort_events.unwrap();
893  //  }
894  //  if cfg.build_strategy.is_some() {
895  //    self.build_strategy = cfg.build_strategy.unwrap();
896  //  }
897  //  if cfg.greediness.is_some() {
898  //    self.greediness = cfg.greediness.unwrap();
899  //  }
900  //  if cfg.wait_nrb.is_some() {
901  //    self.wait_nrb = cfg.wait_nrb.unwrap();
902  //  }
903  //  if cfg.hb_send_interval.is_some() {
904  //    self.hb_send_interval = cfg.hb_send_interval.unwrap();
905  //  }
906  //  if cfg.only_save_interesting.is_some() {
907  //    self.only_save_interesting = cfg.only_save_interesting.unwrap();
908  //  }
909  //  if cfg.thr_n_hits_umb.is_some() { 
910  //    self.thr_n_hits_umb = cfg.thr_n_hits_umb.unwrap();
911  //  }
912  //  if cfg.thr_n_hits_cbe.is_some() {      
913  //    self.thr_n_hits_cbe = cfg.thr_n_hits_cbe.unwrap();
914  //  }
915  //  if cfg.thr_n_hits_cor.is_some()   {
916  //    self.thr_n_hits_cor = cfg.thr_n_hits_cor.unwrap();
917  //  }
918  //  if cfg.thr_tot_edep_umb.is_some() {    
919  //    self.thr_tot_edep_umb = cfg.thr_tot_edep_umb.unwrap();
920  //  }
921  //  if cfg.thr_tot_edep_cbe.is_some() {    
922  //    self.thr_tot_edep_cbe = cfg.thr_tot_edep_cbe.unwrap();
923  //  }
924  //  if cfg.thr_tot_edep_cor.is_some() {    
925  //    self.thr_tot_edep_cor = cfg.thr_tot_edep_cor.unwrap();
926  //  }
927  //}
928}
929
930impl fmt::Display for TofEventBuilderSettings {
931  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
932    let disp = toml::to_string(self).unwrap_or(
933      String::from("-- DESERIALIZATION ERROR! --"));
934    write!(f, "<TofEventBuilderSettings :\n{}>", disp)
935  }
936}
937
938impl Default for TofEventBuilderSettings {
939  fn default() -> Self {
940    Self::new()
941  }
942}
943
944/// Configure data storage and packet publishing
945#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
946pub struct DataPublisherSettings {
947  /// location to store data on TOF computer
948  pub data_dir                  : String,
949  /// The data written on disk gets divided into 
950  /// files of a fixed size. 
951  pub mbytes_per_file           : usize,
952  /// The address the flight computer should subscribe 
953  /// to to get tof packets
954  pub fc_pub_address            : String,
955  /// Mark a certain fraction of events as to be discarded, 
956  /// that is not to be stored on disk
957  /// 1 = Throw away all events, 0 = throw away no events
958  pub discard_event_fraction    : f32,
959  ///// Don't save events which are non-interesting
960  //pub discard_non_interesting   : bool,
961  //pub filter_interesting_numb   : u8,
962  //pub filter_interesting_ncbe   : u8,
963  //pub filter_interesting_n
964  /// Send also MastertriggerPackets (this should be 
965  /// turned off in flight - only useful if 
966  /// send_flight_packets is true, otherwise
967  /// MTB events will get sent as part of TofEvents
968  pub send_mtb_event_packets    : bool,
969  /// switch off waveform sending (in case of we 
970  /// are sending flight packets)
971  pub send_rbwaveform_packets   : bool,
972  /// send only a fraction of all RBWaveform packets
973  /// 1 = all events, 1000 = every 1/1000 event
974  pub send_rbwf_every_x_event   : u32,
975  pub send_tof_summary_packets  : bool,
976  pub send_tof_event_packets    : bool,
977  /// Send the RBCalibration to ground
978  pub send_cali_packets         : bool,
979  pub hb_send_interval          : u16,
980}
981
982impl DataPublisherSettings {
983  pub fn new() -> Self {
984    Self {
985      data_dir                  : String::from(""),
986      mbytes_per_file           : 420,
987      fc_pub_address            : String::from(""),
988      discard_event_fraction    : 0.0,
989      send_mtb_event_packets    : false,
990      send_rbwaveform_packets   : false,
991      send_rbwf_every_x_event   : 1,
992      send_tof_summary_packets  : true,
993      send_tof_event_packets    : false,
994      send_cali_packets         : true,
995      hb_send_interval          : 30,
996    }
997  }
998  
999  pub fn from_datapublisherconfig(&mut self, cfg : &DataPublisherConfig) {
1000    if cfg.mbytes_per_file.is_some() {
1001      self.mbytes_per_file = cfg.mbytes_per_file.unwrap() as usize;
1002    }
1003    if cfg.discard_event_fraction.is_some() {
1004      self.discard_event_fraction = cfg.discard_event_fraction.unwrap();
1005    }
1006    if cfg.send_mtb_event_packets.is_some() {
1007      self.send_mtb_event_packets = cfg.send_mtb_event_packets.unwrap();
1008    }
1009    if cfg.send_rbwaveform_packets.is_some() {
1010      self.send_rbwaveform_packets = cfg.send_rbwaveform_packets.unwrap();
1011    }
1012    if cfg.send_rbwf_every_x_event.is_some() {
1013      self.send_rbwf_every_x_event = cfg.send_rbwf_every_x_event.unwrap();
1014    }
1015    if cfg.send_tof_summary_packets.is_some() {
1016      self.send_tof_summary_packets = cfg.send_tof_summary_packets.unwrap();
1017    }
1018    if cfg.send_tof_event_packets.is_some() {
1019      self.send_tof_event_packets = cfg.send_tof_event_packets.unwrap();
1020    }
1021    if cfg.hb_send_interval.is_some() {
1022      self.hb_send_interval = cfg.hb_send_interval.unwrap();
1023    }
1024  }
1025}
1026
1027impl fmt::Display for DataPublisherSettings {
1028  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1029    let disp = toml::to_string(self).unwrap_or(
1030      String::from("-- DESERIALIZATION ERROR! --"));
1031    write!(f, "<DataPublisherSettings :\n{}>", disp)
1032  }
1033}
1034
1035impl Default for DataPublisherSettings {
1036  fn default() -> Self {
1037    Self::new()
1038  }
1039}
1040
1041
1042#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
1043#[cfg_attr(feature="pybindings", pyclass)]
1044pub struct LiftofSettings {
1045  /// The config version indicates, which version of 
1046  /// Liftof of this file is intended for, e.g. "0.11"
1047  pub config_version             : Option<String>,
1048  /// read run .toml files from this directory and 
1049  /// automotically work through them 1by1
1050  pub staging_dir                : String,
1051  /// default location for RBCalibration files
1052  pub calibration_dir            : String,
1053  /// default location for the database
1054  pub db_path                    : String,
1055  /// Runtime in seconds
1056  pub runtime_sec                : u64,
1057  /// The UDP port to be used to get packets from the 
1058  /// MTB
1059  pub mtb_address                : String,
1060  /// The interval (in seconds) to retrive CPUMoniData from 
1061  /// the TOF CPU
1062  pub cpu_moni_interval_sec      : u64,
1063  /// In an intervall from 1-50, these RB simply do not exist
1064  /// or might have never existed. Always ingore these
1065  pub rb_ignorelist_always       : Vec<u8>,
1066  /// ignore these specific RB for this run
1067  pub rb_ignorelist_run          : Vec<u8>,
1068  /// Should TofHits be generated?
1069  pub run_analysis_engine        : bool,
1070  /// RB contols both, LTB and PB
1071  pub rb_controls_pb_and_ltb     : Option<Vec<u8>>,
1072  /// Run a full RB calibration before run 
1073  /// start?
1074  pub pre_run_calibration        : bool,
1075  /// Should the waveforms which go into te calibration 
1076  /// be saved in the package?
1077  pub save_cali_wf               : bool,
1078  /// Do a verification run before each run? The purpose 
1079  /// of the verification run is to generate a "DetectorStatus"
1080  /// packet. If a verification run is desired, change this 
1081  /// number to the number of seconds to do the verification 
1082  /// run
1083  //#[deprecated(since = "0.11", note = "Use flag verfication_rnn and runtime instead!")]
1084  //pub verification_runtime_sec   : Option<u32>,
1085  /// If this is set, don't save anything to disk 
1086  /// and just transmit the TofDetectorStatus packet
1087  pub verification_run           : Option<bool>,
1088  /// Settings to control the MTB
1089  pub mtb_settings               : MTBSettings,
1090  /// Settings for the TOF event builder
1091  pub event_builder_settings     : TofEventBuilderSettings,
1092  /// Settings for the analysis engine
1093  pub analysis_engine_settings   : AnalysisEngineSettings,
1094  /// Configure data publshing and saving on local disc
1095  pub data_publisher_settings    : DataPublisherSettings,
1096  /// Configure cmmand reception and sending
1097  pub cmd_dispatcher_settings    : CommandDispatcherSettings,
1098  /// Settings for the individual RBs
1099  pub rb_settings                : RBSettings,
1100  /// Mask individual channels (e.g. dead preamps) 
1101  /// for the readout boards
1102  pub rb_channel_mask            : ChannelMaskSettings,
1103  /// Preamp configuration
1104  pub preamp_settings            : PreampSettings,
1105  /// LTB threshold configuration
1106  pub ltb_settings               : LTBThresholdSettings
1107}
1108
1109impl LiftofSettings {
1110  pub fn new() -> Self {
1111    LiftofSettings {
1112      config_version            : None, 
1113      staging_dir               : String::from("/home/gaps/liftof-staging"),
1114      calibration_dir           : String::from(""),
1115      db_path                   : String::from("/home/gaps/config/gaps_flight.db"),
1116      runtime_sec               : 0,
1117      mtb_address               : String::from("10.0.1.10:50001"),
1118      cpu_moni_interval_sec     : 60,
1119      rb_ignorelist_always      : Vec::<u8>::new(),
1120      rb_ignorelist_run         : Vec::<u8>::new(),
1121      rb_controls_pb_and_ltb    : None,
1122      run_analysis_engine       : true,
1123      pre_run_calibration       : false,
1124      save_cali_wf              : false,
1125      //verification_runtime_sec  : None, // no verification run per default
1126      verification_run          : None,
1127      mtb_settings              : MTBSettings::new(),
1128      event_builder_settings    : TofEventBuilderSettings::new(),
1129      analysis_engine_settings  : AnalysisEngineSettings::new(),
1130      data_publisher_settings   : DataPublisherSettings::new(),
1131      cmd_dispatcher_settings   : CommandDispatcherSettings::new(),
1132      rb_settings               : RBSettings::new(),
1133      rb_channel_mask           : ChannelMaskSettings::new(),
1134      preamp_settings           : PreampSettings::new(),
1135      ltb_settings              : LTBThresholdSettings::new(),
1136    }
1137  }  
1138
1139  /// Change the settings according to the ones in the 
1140  /// given config 
1141  pub fn from_tofrunconfig(&mut self, cfg : &TofRunConfig) {
1142    if cfg.runtime.is_some() {
1143      self.runtime_sec = cfg.runtime.unwrap() as u64;
1144    }
1145  }
1146
1147  /// Write the settings to a toml file
1148  pub fn to_toml(&self, mut filename : String) {
1149    if !filename.ends_with(".toml") {
1150      filename += ".toml";
1151    }
1152    info!("Will write to file {}!", filename);
1153    match File::create(&filename) {
1154      Err(err) => {
1155        error!("Unable to open file {}! {}", filename, err);
1156      }
1157      Ok(mut file) => {
1158        match toml::to_string_pretty(&self) {
1159          Err(err) => {
1160            error!("Unable to serialize toml! {err}");
1161          }
1162          Ok(toml_string) => {
1163            match file.write_all(toml_string.as_bytes()) {
1164              Err(err) => error!("Unable to write to file {}! {}", filename, err),
1165              Ok(_)    => debug!("Wrote settings to {}!", filename)
1166            }
1167          }
1168        }
1169      }
1170    }
1171  }
1172
1173  /// Write the settings to a json file
1174  pub fn to_json(&self, mut filename : String) {
1175    if !filename.ends_with(".json") {
1176      filename += ".json";
1177    }
1178    info!("Will write to file {}!", filename);
1179    match File::create(&filename) {
1180      Err(err) => {
1181        error!("Unable to open file {}! {}", filename, err);
1182      }
1183      Ok(file) => {
1184        match serde_json::to_writer_pretty(file, &self) {
1185          Err(err) => {
1186            error!("Unable to serialize json! {err}");
1187          }
1188          Ok(_) => debug!("Wrote settings to {}!", filename)
1189        }
1190      }
1191    }
1192  }
1193
1194  pub fn from_toml(filename : &str) -> Result<LiftofSettings, SerializationError> {
1195    match File::open(filename) {
1196      Err(err) => {
1197        error!("Unable to open {}! {}", filename, err);
1198        return Err(SerializationError::TomlDecodingError);
1199      }
1200      Ok(mut file) => {
1201        let mut toml_string = String::from("");
1202        match file.read_to_string(&mut toml_string) {
1203          Err(err) => {
1204            error!("Unable to read {}! {}", filename, err);
1205            return Err(SerializationError::TomlDecodingError);
1206          }
1207          Ok(_) => {
1208            match toml::from_str(&toml_string) {
1209              Err(err) => {
1210                error!("Can't interpret toml! {}", err);
1211                return Err(SerializationError::TomlDecodingError);
1212              }
1213              Ok(settings) => {
1214                return Ok(settings);
1215              }
1216            }
1217          }
1218        }
1219      }
1220    }
1221  }
1222}
1223
1224impl fmt::Display for LiftofSettings {
1225  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1226    let disp : String;
1227    match toml::to_string(self) {
1228      Err(err) => {
1229        println!("Deserialization error! {err}");
1230        disp = String::from("-- DESERIALIZATION ERROR! --");
1231      }
1232      Ok(_disp) => {
1233        disp = _disp;
1234      }
1235    }
1236    write!(f, "<LiftofSettings :\n{}>", disp)
1237  }
1238}
1239
1240impl Default for LiftofSettings {
1241  fn default() -> Self {
1242    Self::new()
1243  }
1244}
1245
1246#[cfg(feature="pybindings")]
1247#[pymethods]
1248impl LiftofSettings {
1249
1250  /// Read settings from a .toml file
1251  ///
1252  /// # Arugments:
1253  ///
1254  /// * filename : A .toml file with settings fro the 
1255  ///              liftof flight suite
1256  #[staticmethod]
1257  fn from_file(filename : &str) -> PyResult<Self> {
1258    match LiftofSettings::from_toml(filename) {
1259      Ok(settings) => {
1260        return Ok(settings);
1261      }
1262      Err(err) => {
1263        return Err(PyValueError::new_err(err.to_string()));
1264      }
1265    }
1266  }
1267}
1268
1269#[cfg(feature="pybindings")]
1270pythonize!(LiftofSettings);
1271
1272//----------------------------------------------------
1273
1274/// Readoutboard configuration for a specific run
1275#[derive(Debug, Copy, Clone, PartialEq, serde::Deserialize, serde::Serialize)]
1276pub struct LiftofRBConfig {
1277  /// limit run time to number of seconds
1278  pub nseconds                : u32,
1279  /// tof operation mode - either "StreamAny",
1280  /// "RequestReply" or "RBHighThroughput"
1281  pub tof_op_mode             : TofOperationMode,
1282  /// if different from 0, activate RB self trigger
1283  /// in poisson mode
1284  pub trigger_poisson_rate    : u32,
1285  /// if different from 0, activate RB self trigger 
1286  /// with fixed rate setting
1287  pub trigger_fixed_rate      : u32,
1288  /// Either "Physics" or a calibration related 
1289  /// data type, e.g. "VoltageCalibration".
1290  /// <div class="warning">This might get deprecated in a future version!</div>
1291  pub data_type               : DataType,
1292  /// The value when the readout of the RB buffers is triggered.
1293  /// This number is in size of full events, which correspond to 
1294  /// 18530 bytes. Maximum buffer size is a bit more than 3000 
1295  /// events. Smaller buffer allows for a more snappy reaction, 
1296  /// but might require more CPU resources (on the board)
1297  pub rb_buff_size            : u16
1298}
1299
1300impl LiftofRBConfig {
1301
1302  pub fn new() -> Self {
1303    Self {
1304      nseconds                : 0,
1305      tof_op_mode             : TofOperationMode::Default,
1306      trigger_poisson_rate    : 0,
1307      trigger_fixed_rate      : 0,
1308      data_type               : DataType::Unknown, 
1309      rb_buff_size            : 0,
1310    }
1311  }
1312}
1313
1314impl Serialization for LiftofRBConfig {
1315  const HEAD               : u16   = 43690; //0xAAAA
1316  const TAIL               : u16   = 21845; //0x5555
1317  const SIZE               : usize = 24; // bytes including HEADER + FOOTER
1318  
1319  fn from_bytestream(bytestream : &Vec<u8>,
1320                     pos        : &mut usize)
1321    -> Result<Self, SerializationError> {
1322    let mut pars = Self::new();
1323    Self::verify_fixed(bytestream, pos)?;
1324    pars.nseconds                = parse_u32 (bytestream, pos);
1325    pars.tof_op_mode           
1326      = TofOperationMode::try_from(
1327          parse_u8(bytestream, pos))
1328      .unwrap_or_else(|_| TofOperationMode::Unknown);
1329    pars.trigger_poisson_rate    = parse_u32 (bytestream, pos);
1330    pars.trigger_fixed_rate      = parse_u32 (bytestream, pos);
1331    pars.data_type    
1332      = DataType::try_from(parse_u8(bytestream, pos))
1333      .unwrap_or_else(|_| DataType::Unknown);
1334    pars.rb_buff_size = parse_u16(bytestream, pos);
1335    *pos += 2; // for the tail 
1336    //_ = parse_u16(bytestream, pos);
1337    Ok(pars)
1338  }
1339  
1340  fn to_bytestream(&self) -> Vec<u8> {
1341    let mut stream = Vec::<u8>::with_capacity(Self::SIZE);
1342    stream.extend_from_slice(&Self::HEAD.to_le_bytes());
1343    stream.extend_from_slice(&self.  nseconds.to_le_bytes());
1344    stream.extend_from_slice(&(self.tof_op_mode as u8).to_le_bytes());
1345    stream.extend_from_slice(&self.trigger_poisson_rate.to_le_bytes());
1346    stream.extend_from_slice(&self.trigger_fixed_rate.to_le_bytes());
1347    stream.extend_from_slice(&(self.data_type as u8).to_le_bytes());
1348    stream.extend_from_slice(&self.rb_buff_size.to_le_bytes());
1349    stream.extend_from_slice(&Self::TAIL.to_le_bytes());
1350    stream
1351  }
1352}
1353
1354impl Default for LiftofRBConfig {
1355  fn default() -> Self {
1356    Self::new()
1357  }
1358}
1359
1360impl fmt::Display for LiftofRBConfig {
1361  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1362    write!(f, 
1363"<LiftofRBConfig -- is_active : true
1364    nseconds     : {}
1365    TOF op. mode : {}
1366    data type    : {}
1367    tr_poi_rate  : {}
1368    tr_fix_rate  : {}
1369    buff size    : {} [events]>",
1370      self.nseconds,
1371      self.tof_op_mode,
1372      self.data_type,
1373      self.trigger_poisson_rate,
1374      self.trigger_fixed_rate,
1375      self.rb_buff_size)
1376  }
1377}
1378
1379// #[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
1380// pub struct ChannelMaskSettings {
1381//   /// actually apply the below settings
1382//   pub set_channel_mask   : bool,
1383//   /// liftof-cc will send commands to set the 
1384//   /// preamp bias voltages
1385//   pub set_strategy           : ParameterSetStrategy,
1386//   /// channels to mask (one set of 18 values per RAT)
1387//   pub rat_channel_mask     : HashMap<String, [bool;18]>
1388// }
1389
1390/// Ignore RB channnels
1391///
1392/// The values in these arrays correspond to 
1393/// (physical) channels 1-9
1394#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
1395pub struct ChannelMaskSettings {
1396  /// actually apply the below settings
1397  pub set_channel_mask   : bool,
1398  /// The set strat defines who should acutally set
1399  /// the parameters. Will that be done by each board
1400  /// independently (ParameterSetStrategy::Board) or
1401  /// will a command be sent by liftof-cc 
1402  /// (ParameterSetStrategy::ControlServer)
1403  pub set_strategy           : ParameterSetStrategy,
1404  /// channels to mask (one set of 9 values per RB)
1405  /// "true" means the channel is enabled, "false", 
1406  /// disabled
1407  pub rb_channel_mask     : HashMap<String, [bool;9]>
1408}
1409
1410impl ChannelMaskSettings {
1411  pub fn new() -> Self {
1412    let mut default_thresholds = HashMap::<String, [bool; 9]>::new();
1413    for k in 1..51 {
1414      let key = format!("RB{k:02}");
1415      default_thresholds.insert(key, [true;9]);
1416    }
1417//    let default_thresholds = HashMap::from([
1418//      (String::from("RAT01"), [false; 9]),
1419//      (String::from("RAT02"), [false; 9]),
1420//      (String::from("RAT03"), [false; 9]),
1421//      (String::from("RAT04"), [false; 9]),
1422//      (String::from("RAT05"), [false; 9]),
1423//      (String::from("RAT06"), [false; 9]),
1424//      (String::from("RAT07"), [false; 9]),
1425//      (String::from("RAT08"), [false; 9]),
1426//      (String::from("RAT09"), [false; 9]),
1427//      (String::from("RAT10"), [false; 9]),
1428//      (String::from("RAT11"), [false; 9]),
1429//      (String::from("RAT12"), [false; 9]),
1430//      (String::from("RAT13"), [false; 9]),
1431//      (String::from("RAT14"), [false; 9]),
1432//      (String::from("RAT15"), [false; 9]),
1433//      (String::from("RAT16"), [false; 9]),
1434//      (String::from("RAT17"), [false; 9]),
1435//      (String::from("RAT18"), [false; 9]),
1436//      (String::from("RAT19"), [false; 9]),
1437//      (String::from("RAT20"), [false; 9])]);
1438
1439      Self {
1440        set_channel_mask    : false,
1441        set_strategy          : ParameterSetStrategy::ControlServer,
1442        rb_channel_mask    : default_thresholds,
1443      }
1444  }
1445
1446  #[cfg(feature="database")]
1447  pub fn emit_ch_mask_packets(&self, rbs : &HashMap<u8,RAT>) -> Vec<TofPacket> {
1448    let mut packets = Vec::<TofPacket>::new();
1449    for k in rbs.keys() {
1450      let rb          = &rbs[&k];
1451      let rb_key      = format!("RB{:2}", rb);
1452      let mut cmd      = TofCommand::new();
1453      cmd.command_code = TofCommandCode::SetRBChannelMask;
1454      let mut payload  = RBChannelMaskConfig::new();
1455      payload.rb_id    = rb.rb2_id as u8;
1456      if *k as usize >= self.rb_channel_mask.len() {
1457        error!("RB ID {k} larger than 46!");
1458        continue;
1459      }
1460      payload.channels = self.rb_channel_mask[&rb_key];
1461      cmd.payload = payload.to_bytestream();
1462      let tp = cmd.pack();
1463      packets.push(tp);
1464    }
1465    packets
1466  }
1467}
1468impl fmt::Display for ChannelMaskSettings {
1469  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1470    let disp : String;
1471    match toml::to_string(self) {
1472      Err(err) => {
1473        error!("Deserialization error! {err}");
1474        disp = String::from("-- DESERIALIZATION ERROR! --");
1475      }
1476      Ok(_disp) => {
1477        disp = _disp;
1478      }
1479    }
1480    write!(f, "<RBChannelMaskConfig :\n{}>", disp)
1481  }
1482}
1483
1484impl Default for ChannelMaskSettings {
1485  fn default() -> Self {
1486    Self::new()
1487  }
1488}
1489
1490#[cfg(feature="random")]
1491#[test]
1492fn mtb_config() {
1493
1494  for _ in 0..100 {
1495    let cfg  = TriggerConfig::from_random();
1496    let mut settings = MTBSettings::new();
1497    settings.from_triggerconfig(&cfg);
1498    let test = settings.emit_triggerconfig();
1499    if cfg.gaps_trigger_use_beta.is_some() {
1500      assert_eq!(cfg.gaps_trigger_use_beta, test.gaps_trigger_use_beta);
1501    }
1502    if cfg.prescale.is_some() {
1503      assert_eq!(cfg.prescale, test.prescale);
1504    }
1505    if cfg.trigger_type.is_some() {
1506      assert_eq!(cfg.trigger_type, test.trigger_type);
1507    }
1508    if cfg.use_combo_trigger.is_some() {
1509      assert_eq!(cfg.use_combo_trigger, test.use_combo_trigger);
1510    }
1511    if cfg.combo_trigger_type.is_some() {
1512      assert_eq!(cfg.combo_trigger_type, test.combo_trigger_type);
1513    }
1514    if cfg.combo_trigger_prescale.is_some() {
1515      assert_eq!(cfg.combo_trigger_prescale, test.combo_trigger_prescale);
1516    }
1517    if cfg.trace_suppression.is_some() {
1518      assert_eq!(cfg.trace_suppression, test.trace_suppression);
1519    }
1520    if cfg.mtb_moni_interval.is_some() {
1521      assert_eq!(cfg.mtb_moni_interval, test.mtb_moni_interval);
1522    }
1523    if cfg.tiu_ignore_busy.is_some() {
1524      assert_eq!(cfg.tiu_ignore_busy, test.tiu_ignore_busy);
1525    }
1526    if cfg.hb_send_interval.is_some() {
1527      assert_eq!(cfg.hb_send_interval, test.hb_send_interval);
1528    }
1529  }
1530}
1531
1532#[test]
1533fn write_config_file() {
1534  let settings = LiftofSettings::new();
1535  //println!("{}", settings);
1536  settings.to_toml(String::from("liftof-config-test.toml"));
1537}
1538
1539#[test] 
1540fn compress_uncompress_config_file() {
1541  write_config_file();
1542  let pth         = Path::new("liftof-config-test.toml");
1543  let bytestream  = compress_toml(&pth).unwrap();
1544  println!("Compressed .toml file to a bytestream of {} bytes!", bytestream.len());
1545  let output      = Path::new("liftof-config-decompressed.toml");
1546  decompress_toml(&bytestream.as_slice(), output); 
1547}
1548
1549#[test]
1550fn diff_config_file_compress_uncompress() {
1551  write_config_file();
1552  let mut settings = LiftofSettings::new();
1553  settings.to_toml(String::from("liftof-config-test.toml"));
1554  settings.staging_dir = String::from("/foo/bar");
1555  settings.to_toml(String::from("liftof-config-test-changed.toml"));
1556  let pth         = Path::new("liftof-config-test.toml");
1557  let pth_ch      = Path::new("liftof-config-test-changed.toml");
1558  let diff        = create_compressed_diff(&pth, &pth_ch).unwrap();
1559  println!("Diff has the size of {} bytes!", diff.len());
1560  let output      = Path::new("liftof-config.diff");
1561  decompress_toml(&diff.as_slice(), output);
1562}
1563
1564#[test]
1565fn read_config_file() {
1566  let _settings = LiftofSettings::from_toml("liftof-config-test.toml");
1567}
1568
1569