gondola_core/tof/
config.rs

1//! Payloads for commands that configure an entity of 
2//! the TOF system.
3//!
4// This file is part of gaps-online-software and published 
5// under the GPLv3 license
6
7use crate::prelude::*;
8
9/// How to operate the readout Default mode is to request
10/// events from the MasterTrigger. However, we can also stream
11/// all the waveforms.
12/// CAVEAT: For the whole tof, this will cap the rate at 
13/// 112 Hz, because of the capacity of the switches.
14#[derive(Debug, Copy, Clone, PartialEq, FromRepr, AsRefStr, EnumIter, serde::Deserialize, serde::Serialize)]
15#[cfg_attr(feature = "pybindings", pyclass(eq, eq_int))]
16#[repr(u8)]
17pub enum TofOperationMode {
18  Unknown          = 0u8,
19  Default          = 1u8,
20  /// Don't decode any of the event 
21  /// data on the RB, just push it 
22  /// onward
23  RBHighThroughput = 30u8,
24  RBCalcCRC32      = 40u8,
25  RBWaveform       = 50u8,
26}
27
28expand_and_test_enum!(TofOperationMode, test_tofoperationmode_repr);
29
30//-------------------------------------------------
31
32/// Build Strategy
33/// 
34#[derive(Debug, Copy, Clone, PartialEq, serde::Serialize, serde::Deserialize, FromRepr, AsRefStr, EnumIter)]
35#[cfg_attr(feature = "pybindings", pyclass(eq, eq_int))]
36#[repr(u8)]
37pub enum BuildStrategy {
38  Unknown   = 0,
39  Smart     = 100,
40  /// adjust the number of boards based on nrbes/mtb
41  Adaptive  = 101,
42  /// Same as adaptive, but check if the rb events follow the 
43  /// mapping
44  AdaptiveThorough = 102,
45  /// like adaptive, but add usize to the expected number of boards
46  AdaptiveGreedy   = 1,
47  WaitForNBoards   = 2,
48}
49
50expand_and_test_enum!(BuildStrategy, test_buildstrategy_repr);
51
52//-------------------------------------------------
53
54/// Set preamp voltages
55#[derive(Copy, Clone, Debug, PartialEq)]
56#[cfg_attr(feature="pybindings", pyclass)]
57pub struct PreampBiasConfig {
58  pub rb_id   : u8,
59  pub biases  : [f32;16]
60}
61
62impl PreampBiasConfig {
63  pub fn new() -> Self { 
64    Self {
65      rb_id   : 0,
66      biases  : [0.0;16]
67    }
68  }
69}
70
71impl Default for PreampBiasConfig {
72  fn default() -> Self {
73    Self::new()
74  }
75}
76
77impl fmt::Display for PreampBiasConfig {
78  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
79    //let cc = RBCommand::command_code_to_string(self.command_code);
80    let mut repr = String::from("<PreampBiasConfig");
81    repr += &(format!("\n  RB ID      : {}", self.rb_id)); 
82    repr += "  -- biases per channel:";
83    for k in 0..self.biases.len() {
84      repr += &(format!("\n    Ch{} : {:.2}", k+1, self.biases[k]));
85    }
86    write!(f, "{}", repr)
87  }
88}
89
90impl TofPackable for PreampBiasConfig {
91  const TOF_PACKET_TYPE : TofPacketType = TofPacketType::PreampBiasConfig;
92}
93
94impl Serialization for PreampBiasConfig {
95  
96  const HEAD : u16 = 0xAAAA;
97  const TAIL : u16 = 0x5555;
98  const SIZE : usize = 69; // nice! 
99  
100  fn from_bytestream(stream    : &Vec<u8>, 
101                     pos       : &mut usize) 
102    -> Result<Self, SerializationError>{
103    Self::verify_fixed(stream, pos)?;  
104    let mut cfg = PreampBiasConfig::new();
105    cfg.rb_id   = parse_u8(stream, pos);
106    for k in 0..16 {
107      cfg.biases[k] = parse_f32(stream, pos);
108    }
109    *pos += 2;
110    Ok(cfg)
111  }
112  
113  fn to_bytestream(&self) -> Vec<u8> {
114    let mut bs = Vec::<u8>::with_capacity(Self::SIZE);
115    bs.extend_from_slice(&Self::HEAD.to_le_bytes());
116    bs.push(self.rb_id);
117    for k in 0..16 {
118      bs.extend_from_slice(&self.biases[k].to_le_bytes());
119    }
120    bs.extend_from_slice(&Self::TAIL.to_le_bytes());
121    bs
122  }
123}
124
125#[cfg(feature = "random")]
126impl FromRandom for PreampBiasConfig {
127  fn from_random() -> Self {
128    let mut cfg  = PreampBiasConfig::new();
129    let mut rng  = rand::rng();
130    cfg.rb_id    = rng.random::<u8>();
131    for k in 0..16 {
132      cfg.biases[k] = rng.random::<f32>();
133    }
134    cfg
135  }
136}
137
138//---------------------------------------------------
139//
140#[derive(Copy, Clone, Debug, PartialEq)]
141#[cfg_attr(feature="pybindings", pyclass)]
142pub struct RBChannelMaskConfig {
143  pub rb_id    : u8,
144  pub channels : [bool;9],
145}
146
147impl RBChannelMaskConfig {
148  pub fn new() -> Self {
149    Self {
150      rb_id     : 0,
151      channels    : [false;9],
152    }
153  }
154}
155
156impl Default for RBChannelMaskConfig {
157  fn default() -> Self {
158    Self::new()
159  }
160}
161
162impl fmt::Display for RBChannelMaskConfig {
163  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
164    let mut repr = String::from("<RBCHannelMaskConfig");
165    repr += &(format!("\n  RB ID      : {}", self.rb_id));
166    repr += &(format!("\n Problematic Channels >:( {:?}", self.channels));
167    write!(f, "{}", repr)
168  }
169}
170
171impl TofPackable for RBChannelMaskConfig {
172  const TOF_PACKET_TYPE : TofPacketType = TofPacketType::RBChannelMaskConfig;
173}
174
175impl Serialization for RBChannelMaskConfig {
176
177  const HEAD : u16 = 0xAAAA;
178  const TAIL : u16 = 0x5555;
179  const SIZE : usize = 14;
180
181  fn from_bytestream(stream     : &Vec<u8>,
182                     pos        : &mut usize)
183    -> Result<Self, SerializationError>{
184      Self::verify_fixed(stream, pos)?;
185      let mut cfg = RBChannelMaskConfig::new();
186      cfg.rb_id   = parse_u8(stream, pos);
187      for k in 0..9 {
188        cfg.channels[k] = parse_bool(stream, pos);
189      }
190      *pos += 2;
191      Ok(cfg)
192    }
193
194  fn to_bytestream(&self) -> Vec<u8> {
195    let mut bs = Vec::<u8>::with_capacity(Self::SIZE);
196    bs.extend_from_slice(&Self::HEAD.to_le_bytes());
197    bs.push(self.rb_id);
198    for k in 0..9 {
199      bs.push(self.channels[k] as u8);
200    }
201    bs.extend_from_slice(&Self::TAIL.to_le_bytes());
202    bs
203  }
204} 
205
206#[cfg(feature = "random")]
207impl FromRandom for RBChannelMaskConfig {
208  fn from_random() -> Self {
209    let mut cfg   = RBChannelMaskConfig::new();
210    let mut rng   = rand::rng();
211    cfg.rb_id     = rng.random::<u8>();
212    for k in 0..9 {
213      cfg.channels[k] = rng.random::<bool>();
214    }
215    cfg
216  }
217}
218
219///////////////////////////////////////////////////////
220
221
222/// Set ltb thresholds
223#[derive(Copy, Clone, Debug, PartialEq)]
224#[cfg_attr(feature="pybindings", pyclass)]
225pub struct LTBThresholdConfig {
226  pub rb_id       : u8,
227  pub thresholds  : [f32;3]
228}
229
230impl LTBThresholdConfig {
231  pub fn new() -> Self {
232    Self {
233      rb_id       : 0,
234      thresholds  : [0.0;3]
235    }
236  }
237}
238
239impl Default for LTBThresholdConfig {
240  fn default() -> Self {
241    Self::new()
242  }
243}
244
245impl fmt::Display for LTBThresholdConfig {
246  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
247    let mut repr = String::from("<LTBThresholdConfig");
248    repr += &(format!("\n  RB ID      : {}", self.rb_id));
249    repr += "  -- thresholds per channel:";
250    for k in 0..self.thresholds.len() {
251      repr += &(format!("\n    Ch{} : {:.3}", k, self.thresholds[k]));
252    }
253    write!(f, "{}", repr)
254  }
255}
256
257impl TofPackable for LTBThresholdConfig {
258  const TOF_PACKET_TYPE : TofPacketType = TofPacketType::LTBThresholdConfig;
259}
260
261impl Serialization for LTBThresholdConfig {
262
263  const HEAD : u16 = 0xAAAA;
264  const TAIL : u16 = 0x5555;
265  const SIZE : usize = 17;
266
267  fn from_bytestream(stream     : &Vec<u8>,
268                     pos        : &mut usize)
269    -> Result<Self, SerializationError>{
270      Self::verify_fixed(stream, pos)?;
271      let mut cfg = LTBThresholdConfig::new();
272      cfg.rb_id   = parse_u8(stream, pos);
273      for k in 0..3 {
274        cfg.thresholds[k] = parse_f32(stream, pos);
275      }
276      *pos += 2;
277      Ok(cfg)
278    }
279
280  fn to_bytestream(&self) -> Vec<u8> {
281    let mut bs = Vec::<u8>::with_capacity(Self::SIZE);
282    bs.extend_from_slice(&Self::HEAD.to_le_bytes());
283    bs.push(self.rb_id);
284    for k in 0..3 {
285      bs.extend_from_slice(&self.thresholds[k].to_le_bytes());
286    }
287    bs.extend_from_slice(&Self::TAIL.to_le_bytes());
288    bs
289  }
290}
291
292#[cfg(feature = "random")]
293impl FromRandom for LTBThresholdConfig {
294  fn from_random() -> Self {
295    let mut cfg   = LTBThresholdConfig::new();
296    let mut rng   = rand::rng();
297    cfg.rb_id     = rng.random::<u8>();
298    for k in 0..3 {
299      cfg.thresholds[k] = rng.random::<f32>();
300    }
301    cfg
302  }
303}
304
305
306/// Readoutboard configuration for a specific run
307#[derive(Debug, Copy, Clone, PartialEq, serde::Deserialize, serde::Serialize)]
308#[cfg_attr(feature="pybindings", pyclass)]
309pub struct RunConfig {
310  /// an unique identifier for this run
311  pub runid                   : u32,
312  /// start/stop run
313  /// <div class="warning">This might get deprecated in a future version!</div>
314  pub is_active               : bool,
315  /// limit run to number of events
316  pub nevents                 : u32,
317  /// limit run time to number of seconds
318  pub nseconds                : u32,
319  /// tof operation mode - either "StreamAny",
320  /// "RequestReply" or "RBHighThroughput"
321  pub tof_op_mode             : TofOperationMode,
322  /// if different from 0, activate RB self trigger
323  /// in poisson mode
324  pub trigger_poisson_rate    : u32,
325  /// if different from 0, activate RB self trigger 
326  /// with fixed rate setting
327  pub trigger_fixed_rate      : u32,
328  /// Either "Physics" or a calibration related 
329  /// data type, e.g. "VoltageCalibration".
330  /// <div class="warning">This might get deprecated in a future version!</div>
331  pub data_type               : DataType,
332  /// The value when the readout of the RB buffers is triggered.
333  /// This number is in size of full events, which correspond to 
334  /// 18530 bytes. Maximum buffer size is a bit more than 3000 
335  /// events. Smaller buffer allows for a more snappy reaction, 
336  /// but might require more CPU resources (on the board)
337  pub rb_buff_size            : Option<u16>,
338  /// The time interval we want to empty the readoutboard buffers
339  pub rb_buff_empty_interval  : Option<f32>,
340  /// This will set the RB to figure it out by itself
341  pub rb_buff_strategy_smart  : bool
342}
343
344impl RunConfig {
345
346  pub fn new() -> Self {
347    Self {
348      runid                   : 0,
349      is_active               : false,
350      nevents                 : 0,
351      nseconds                : 0,
352      tof_op_mode             : TofOperationMode::Default,
353      trigger_poisson_rate    : 0,
354      trigger_fixed_rate      : 0,
355      data_type               : DataType::Unknown, 
356      rb_buff_size            : None,
357      rb_buff_empty_interval  : None,
358      rb_buff_strategy_smart  : true,
359    }
360  }
361}
362
363//impl Serialization for RunConfig {
364//  const HEAD               : u16   = 43690; //0xAAAA
365//  const TAIL               : u16   = 21845; //0x5555
366//  const SIZE               : usize = 29; // bytes including HEADER + FOOTER
367//  
368//  fn from_bytestream(bytestream : &Vec<u8>,
369//                     pos        : &mut usize)
370//    -> Result<Self, SerializationError> {
371//    let mut pars = Self::new();
372//    Self::verify_fixed(bytestream, pos)?;
373//    pars.runid                   = parse_u32 (bytestream, pos);
374//    pars.is_active               = parse_bool(bytestream, pos);
375//    pars.nevents                 = parse_u32 (bytestream, pos);
376//    pars.nseconds                = parse_u32 (bytestream, pos);
377//    pars.tof_op_mode           
378//      = TofOperationMode::try_from(
379//          parse_u8(bytestream, pos))
380//      .unwrap_or_else(|_| TofOperationMode::Unknown);
381//    pars.trigger_poisson_rate    = parse_u32 (bytestream, pos);
382//    pars.trigger_fixed_rate      = parse_u32 (bytestream, pos);
383//    pars.data_type    
384//      = DataType::try_from(parse_u8(bytestream, pos))
385//      .unwrap_or_else(|_| DataType::Unknown);
386//    pars.rb_buff_size = parse_u16(bytestream, pos);
387//    *pos += 2; // for the tail 
388//    //_ = parse_u16(bytestream, pos);
389//    Ok(pars)
390//  }
391//  
392//  fn to_bytestream(&self) -> Vec<u8> {
393//    let mut stream = Vec::<u8>::with_capacity(Self::SIZE);
394//    stream.extend_from_slice(&Self::HEAD.to_le_bytes());
395//    stream.extend_from_slice(&self.runid.to_le_bytes());
396//    stream.push(self.  is_active as u8);
397//    stream.extend_from_slice(&self.nevents.to_le_bytes());    
398//    stream.extend_from_slice(&self.  nseconds.to_le_bytes());
399//    stream.extend_from_slice(&(self.tof_op_mode as u8).to_le_bytes());
400//    stream.extend_from_slice(&self.trigger_poisson_rate.to_le_bytes());
401//    stream.extend_from_slice(&self.trigger_fixed_rate.to_le_bytes());
402//    stream.push(self.data_type as u8);
403//    stream.extend_from_slice(&self.rb_buff_size.to_le_bytes());
404//    stream.extend_from_slice(&Self::TAIL.to_le_bytes());
405//    stream
406//  }
407//}
408
409impl Default for RunConfig {
410  fn default() -> Self {
411    Self::new()
412  }
413}
414
415impl fmt::Display for RunConfig {
416  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
417    if !self.is_active {
418      return write!(f, "<RunConfig -- is_active : false>");
419    } else {
420      write!(f, 
421"<RunConfig -- is_active : true
422    nevents           : {}
423    nseconds          : {}
424    TOF op. mode      : {}
425    data type         : {}
426    tr_poi_rate       : {}
427    tr_fix_rate       : {}
428    buff size         : {} [events],
429    buff_empty_interv : {} [seconds],
430    buff_strat_smart  : {}>",
431      self.nevents,
432      self.nseconds,
433      self.tof_op_mode,
434      self.data_type,
435      self.trigger_poisson_rate,
436      self.trigger_fixed_rate,
437      self.rb_buff_size.unwrap_or(0),
438      self.rb_buff_empty_interval.unwrap_or(0.0),
439      self.rb_buff_strategy_smart)
440    }
441  }
442}
443
444impl TofPackable for RunConfig {
445  const TOF_PACKET_TYPE : TofPacketType = TofPacketType::RunConfig;
446}
447
448//#[cfg(feature = "random")]
449//impl FromRandom for RunConfig {
450//    
451//  fn from_random() -> Self {
452//    let mut cfg = Self::new();
453//    let mut rng  = rand::rng();
454//    cfg.runid                   = rng.random::<u32>();
455//    cfg.is_active               = rng.random::<bool>();
456//    cfg.nevents                 = rng.random::<u32>();
457//    cfg.nseconds                = rng.random::<u32>();
458//    cfg.tof_op_mode             = TofOperationMode::from_random();
459//    cfg.trigger_poisson_rate    = rng.random::<u32>();
460//    cfg.trigger_fixed_rate      = rng.random::<u32>();
461//    cfg.data_type               = DataType::from_random();
462//    cfg.rb_buff_size            = rng.random::<u16>();
463//    
464//    cfg
465//  }
466//}
467
468//-------------------------------------------------
469
470
471#[derive(Copy, Clone, Debug, PartialEq)]
472#[cfg_attr(feature="pybindings", pyclass)]
473pub struct TriggerConfig{
474  /// When we create the LiftofConfig from 
475  /// the TriggerConfig, this allows us to 
476  /// deactivate fields, so we would can 
477  /// only change a single field
478  pub active_fields          : u32,
479  /// Shall the gaps trigger use beta?
480  pub gaps_trigger_use_beta  : Option<bool>, //1
481  pub prescale               : Option<f32>, //4
482  pub trigger_type           : Option<TriggerType>, //1 
483  pub use_combo_trigger      : Option<bool>,
484  pub combo_trigger_type     : Option<TriggerType>,
485  pub combo_trigger_prescale : Option<f32>,
486  pub trace_suppression      : Option<bool>,
487  pub mtb_moni_interval      : Option<u16>,
488  pub tiu_ignore_busy        : Option<bool>,
489  pub hb_send_interval       : Option<u16>,
490}
491
492impl TriggerConfig {
493  pub fn new() -> Self { 
494    Self {
495      active_fields           : 0,
496      gaps_trigger_use_beta   : None,
497      prescale                : None,
498      trigger_type            : None,
499      use_combo_trigger       : None,
500      combo_trigger_type      : None,
501      combo_trigger_prescale  : None,
502      trace_suppression       : None,
503      mtb_moni_interval       : None,
504      tiu_ignore_busy         : None,
505      hb_send_interval        : None,
506    }
507  }
508
509  pub fn set_gaps_trigger_use_beta(&mut self, use_it : bool) {
510    self.active_fields |= 1;
511    self.gaps_trigger_use_beta = Some(use_it);
512  }
513
514  pub fn set_prescale(&mut self, prescale : f32) {
515    self.active_fields |= 2;
516    self.prescale = Some(prescale);
517  }
518
519  pub fn set_trigger_type(&mut self, ttype : TriggerType) {
520    self.active_fields |= 4;
521    self.trigger_type = Some(ttype);
522  }
523
524  pub fn set_use_combo_trigger(&mut self, combo : bool) {
525    self.active_fields |= 8;
526    self.use_combo_trigger = Some(combo);
527  }
528
529  pub fn set_combo_trigger_type(&mut self, ttype : TriggerType) {
530    self.active_fields |= 16;
531    self.combo_trigger_type = Some(ttype)
532  }
533
534  pub fn set_combo_trigger_prescale(&mut self, prescale : f32) {
535    self.active_fields |= 32;
536    self.combo_trigger_prescale = Some(prescale);
537  }
538
539  pub fn set_trace_suppression(&mut self, tsup : bool) {
540    self.active_fields |= 64;
541    self.trace_suppression = Some(tsup);
542  }
543
544  pub fn set_mtb_moni_interval(&mut self, interval : u16) {
545    self.active_fields |= 128;
546    self.mtb_moni_interval = Some(interval);
547  }
548
549  pub fn set_tiu_ignore_busy(&mut self, busy : bool) {
550    self.active_fields |= 256;
551    self.tiu_ignore_busy = Some(busy);
552  }
553
554  pub fn set_hb_send_interval(&mut self, interval : u16) {
555    self.active_fields |= 512;
556    self.hb_send_interval = Some(interval);
557  }
558}
559
560impl Default for TriggerConfig {
561  fn default() -> Self {
562    Self::new()
563  }
564}
565
566impl fmt::Display for TriggerConfig {
567  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
568    let mut repr = String::from("<TriggerConfig: ");
569    repr += &(format!("(active fields {:x})", self.active_fields));
570    if self. gaps_trigger_use_beta.is_some() {
571        repr += &(format!("\n  Beta is used by trigger      : {}", self.gaps_trigger_use_beta.unwrap())); 
572    }
573    if self. prescale.is_some() {
574      repr += &(format!("\n  Prescale           : {:.3}", self.prescale.unwrap()));
575    }
576    if self.trigger_type.is_some() {
577      repr += &(format!("\n  Trigger type       : {}",    self.trigger_type.unwrap()));
578    }
579    if self.use_combo_trigger.is_some() {
580      if self.use_combo_trigger.unwrap() {
581        repr += &(format!("\n  -- using combo trigger!"));
582      } 
583    }
584    if self.combo_trigger_prescale.is_some() {
585      repr += &(format!("\n  -- -- Combo Prescale     : {:.3}", self.combo_trigger_prescale.unwrap()));
586    }
587    if self.combo_trigger_type.is_some() { 
588      repr += &(format!("\n  -- -- Combo Trigger type : {}",    self.combo_trigger_type.unwrap()));
589    }
590    if self. trace_suppression.is_some() {
591      repr += &(format!("\n  trace_suppression       : {}", self.trace_suppression.unwrap()));
592    }
593    if self.mtb_moni_interval.is_some() {
594      repr += &(format!("\n  mtb_moni_interval       : {}", self.mtb_moni_interval.unwrap()));
595    }
596    if self.tiu_ignore_busy.is_some() {
597      repr += &(format!("\n  tiu_ignore_busy         : {}", self.tiu_ignore_busy.unwrap()));
598    }
599    if self.hb_send_interval.is_some() {
600      repr += &(format!("\n  hb_send_interval        : {}", self.hb_send_interval.unwrap()));
601    }
602    repr += ">";
603    write!(f, "{}", repr)
604  }
605}
606
607impl TofPackable for TriggerConfig {
608  const TOF_PACKET_TYPE : TofPacketType = TofPacketType::TriggerConfig;
609}
610
611impl Serialization for TriggerConfig {
612  
613  const HEAD : u16 = 0xAAAA;
614  const TAIL : u16 = 0x5555;
615  const SIZE : usize = 26; 
616  
617  fn from_bytestream(stream    : &Vec<u8>, 
618                     pos       : &mut usize) 
619    -> Result<Self, SerializationError>{
620    Self::verify_fixed(stream, pos)?;  
621    let mut cfg = TriggerConfig::new();
622    cfg.active_fields          = parse_u32(stream, pos);
623    cfg.gaps_trigger_use_beta  = Some(parse_bool(stream, pos));
624    cfg.prescale               = Some(parse_f32 (stream, pos));
625    cfg.trigger_type           = Some(TriggerType::from(parse_u8(stream, pos)));
626    cfg.use_combo_trigger      = Some(parse_bool(stream, pos));
627    cfg.combo_trigger_type     = Some(TriggerType::from(parse_u8(stream, pos)));
628    cfg.combo_trigger_prescale = Some(parse_f32(stream, pos));
629    cfg.trace_suppression      = Some(parse_bool(stream, pos));
630    cfg.mtb_moni_interval      = Some(parse_u16(stream, pos));
631    cfg.tiu_ignore_busy        = Some(parse_bool(stream, pos));
632    cfg.hb_send_interval       = Some(parse_u16(stream, pos));
633    // disable fields which where not explicitly marked as 
634    // active
635    if cfg.active_fields & 1 != 1 {
636      cfg.gaps_trigger_use_beta = None;
637    }
638    if cfg.active_fields & 2 != 2 {
639      cfg.prescale = None;
640    }
641    if cfg.active_fields & 4 != 4 {
642      cfg.trigger_type = None;
643    }
644    if cfg.active_fields & 8 != 8 {
645      cfg.use_combo_trigger = None;
646    }
647    if cfg.active_fields & 16 != 16 {
648      cfg.combo_trigger_type = None;
649    }
650    if cfg.active_fields & 32 != 32 {
651      cfg.combo_trigger_prescale = None;
652    }
653    if cfg.active_fields & 64 != 64 {
654      cfg.trace_suppression = None;
655    }
656    if cfg.active_fields & 128 != 128 {
657      cfg.mtb_moni_interval = None;
658    }
659    if cfg.active_fields & 256 != 256 {
660      cfg.tiu_ignore_busy   = None;
661    }
662    if cfg.active_fields & 512 != 512 {
663      cfg.hb_send_interval  = None;
664    }
665    *pos += 2;
666    Ok(cfg)
667  }
668
669  fn to_bytestream(&self) -> Vec<u8> {
670    let mut bs = Vec::<u8>::with_capacity(Self::SIZE);
671    bs.extend_from_slice(&Self::HEAD        .to_le_bytes());
672    bs.extend_from_slice(&self.active_fields.to_le_bytes());
673    bs.push             (self.gaps_trigger_use_beta.unwrap_or(false) as u8);
674    bs.extend_from_slice(&self.prescale.unwrap_or(0.0)     .to_le_bytes());
675    bs.push             (self.trigger_type.unwrap_or(TriggerType::Unknown) as u8);
676    bs.push             (self.use_combo_trigger.unwrap_or(false) as u8);
677    bs.push             (self.combo_trigger_type.unwrap_or(TriggerType::Unknown) as u8);
678    bs.extend_from_slice(&self.combo_trigger_prescale.unwrap_or(0.0).to_le_bytes());
679    bs.push             (self.trace_suppression.unwrap_or(false) as u8);
680    bs.extend_from_slice(&self.mtb_moni_interval.unwrap_or(30).to_le_bytes());
681    bs.push             (self.tiu_ignore_busy.unwrap_or(false) as u8);
682    bs.extend_from_slice(&self.hb_send_interval.unwrap_or(30).to_le_bytes());
683    bs.extend_from_slice(&Self::TAIL.to_le_bytes());
684    bs
685  }
686}
687
688#[cfg(feature = "random")]
689impl FromRandom for TriggerConfig {
690  fn from_random() -> Self {
691    let mut cfg                 = TriggerConfig::new();
692    let mut rng                 = rand::rng();
693    let active_fields           = rng.random::<u32>();
694    cfg.active_fields           = active_fields;
695    if active_fields & 1 == 1 {
696      cfg.gaps_trigger_use_beta   = Some(rng.random::<bool>());
697    } else {
698      cfg.gaps_trigger_use_beta = None;
699    }
700    if active_fields & 2 == 2 {
701      cfg.prescale                = Some(rng.random::<f32>());
702    } else {
703      cfg.prescale = None;
704    }
705    if active_fields & 4 == 4 {
706      cfg.trigger_type            = Some(TriggerType::from_random());
707    } else {
708      cfg.trigger_type = None;
709    }
710    if active_fields & 8 == 8 {
711      cfg.use_combo_trigger       = Some(rng.random::<bool>());
712    } else {
713      cfg.use_combo_trigger = None;
714    }
715    if active_fields & 16 == 16 {
716      cfg.combo_trigger_type      = Some(TriggerType::from_random());
717    } else {
718      cfg.combo_trigger_type = None;
719    }
720    if active_fields & 32 == 32 {
721      cfg.combo_trigger_prescale  = Some(rng.random::<f32>());
722    } else {
723      cfg.combo_trigger_prescale = None;
724    }
725    if active_fields & 64 == 64 {
726      cfg.trace_suppression       = Some(rng.random::<bool>());
727    } else {
728      cfg.trace_suppression = None;
729    }
730    if active_fields & 128 == 128 {
731      cfg.mtb_moni_interval       = Some(rng.random::<u16>());
732    } else {
733      cfg.mtb_moni_interval = None;
734    }
735    if active_fields & 256 == 256 {
736      cfg.tiu_ignore_busy         = Some(rng.random::<bool>());
737    } else {
738      cfg.tiu_ignore_busy = None;
739    }
740    if active_fields & 512 == 512 {
741      cfg.hb_send_interval        = Some(rng.random::<u16>());
742    } else {
743      cfg.hb_send_interval = None;
744    }
745    cfg
746  }
747}
748
749#[cfg(feature="pybindings")]
750#[pymethods]
751impl TriggerConfig {
752
753  #[getter] 
754  fn get_prescale(&self) -> Option<f32> {
755    self.prescale
756  }
757  
758  #[setter]
759  #[pyo3(name="set_prescale")]
760  fn set_prescale_py(&mut self, prescale: f32) -> PyResult<()> {
761    self.set_prescale (prescale);
762    Ok(())
763  }
764
765  #[getter] 
766  fn get_gaps_trigger_use_beta(&self) -> Option<bool> {
767    self.gaps_trigger_use_beta
768  }
769  
770  #[setter]
771  #[pyo3(name="set_gaps_trigger_use_beta")]
772  fn set_gaps_trigger_use_beta_py(&mut self, gaps_trigger_use_beta: bool) -> PyResult<()> {
773    self.set_gaps_trigger_use_beta(gaps_trigger_use_beta);
774    Ok(())
775  }
776
777  #[getter] 
778  fn get_trigger_type(&self) -> Option<TriggerType> {
779    self.trigger_type
780  }
781
782  #[setter]
783  #[pyo3(name="set_trigger_type")]
784  fn set_trigger_type_py(&mut self, trigger_type: TriggerType) -> PyResult<()> {
785    self.set_trigger_type(trigger_type);
786    Ok(())
787  }
788  
789  #[getter]
790  fn get_use_combo_trigger(&self) -> Option<bool> {
791    self.use_combo_trigger 
792  }
793  #[setter]
794  #[pyo3(name="set_use_combo_trigger")]
795  fn set_use_combo_trigger_py(&mut self, combo : bool) {
796    self.set_use_combo_trigger(combo);
797  }
798  #[getter]
799  fn get_combo_trigger_type(&self) -> Option<TriggerType> {
800    self.combo_trigger_type
801  }
802  #[setter]
803  #[pyo3(name="set_combo_trigger_type")]
804  fn set_combo_trigger_type_py(&mut self, combo_trigger_type : TriggerType) {
805    self.set_combo_trigger_type(combo_trigger_type);
806  }
807  #[getter]
808  fn get_combo_trigger_prescale(&self) -> Option<f32> {
809    self.combo_trigger_prescale
810  }
811  #[setter]
812  #[pyo3(name="set_combo_trigger_prescale")]
813  fn set_combo_trigger_prescale_py(&mut self, prescale : f32) {
814    self.set_combo_trigger_prescale(prescale);
815  }
816  #[getter]
817  fn get_trace_suppression(&self) -> Option<bool> {
818    self.trace_suppression
819  }
820  #[setter]
821  #[pyo3(name="set_trace_suppression")]
822  fn set_trace_suppression_py(&mut self, tsup : bool) {
823    self.set_trace_suppression(tsup);
824  }
825  #[getter]
826  fn get_mtb_moni_interval(&mut self) -> Option<u16> {
827    self.mtb_moni_interval
828  }
829  #[setter]
830  #[pyo3(name="set_mtb_moni_interval")]
831  fn set_mtb_moni_interval_py(&mut self, moni_int : u16) {
832    self.set_mtb_moni_interval(moni_int);
833  }
834  #[getter]
835  fn get_tiu_ignore_busy(&self) -> Option<bool> {
836    self.tiu_ignore_busy
837  }
838
839  #[setter]
840  #[pyo3(name="set_tiu_ignore_busy")]
841  fn set_tiu_ignore_busy_py(&mut self, ignore_busy : bool) {
842    self.set_tiu_ignore_busy(ignore_busy);
843  }
844  #[getter]
845  fn get_hb_send_interval(&self) -> Option<u16> {
846    self.hb_send_interval
847  }
848
849  #[pyo3(name="to_bytestream")]
850  fn to_bytestream_py(&self) -> Vec<u8> {
851    self.to_bytestream()
852  }
853
854  #[setter]
855  #[pyo3(name="set_hb_send_interval")]
856  fn set_hb_send_interval_py(&mut self, hb_int :  Option<u16>) {
857    self.hb_send_interval = hb_int;
858  }
859
860  fn __getitem__<'a>(&self, py: Python<'a>, key: &str) -> PyResult<Option<Bound<'a,PyAny>>> {  
861    match key {
862      "gaps_trigger_use_beta"  => Ok(Some(self.gaps_trigger_use_beta .into_pyobject(py).unwrap())),
863      "prescale"               => Ok(Some(self.prescale              .into_pyobject(py).unwrap())),
864      "trigger_type"           => Ok(Some(self.trigger_type          .into_pyobject(py).unwrap())),
865      "use_combo_trigger"      => Ok(Some(self.use_combo_trigger     .into_pyobject(py).unwrap())),
866      "combo_trigger_type"     => Ok(Some(self.combo_trigger_type    .into_pyobject(py).unwrap())),
867      "combo_trigger_prescale" => Ok(Some(self.combo_trigger_prescale.into_pyobject(py).unwrap())),
868      "trace_suppression"      => Ok(Some(self.trace_suppression     .into_pyobject(py).unwrap())),
869      "mtb_moni_interval"      => Ok(Some(self.mtb_moni_interval     .into_pyobject(py).unwrap())),
870      "tiu_ignore_busy"        => Ok(Some(self.tiu_ignore_busy       .into_pyobject(py).unwrap())),
871      "hb_send_interval"       => Ok(Some(self.hb_send_interval      .into_pyobject(py).unwrap())),
872      _     => Err(PyKeyError::new_err(format!("Key '{}' not found", key)))
873    }
874  }
875
876  fn __setitem__(&mut self, key: &str, value: &Bound<'_, PyAny>) -> PyResult<()> {
877    match key {
878      "gaps_trigger_use_beta" => {
879          self.active_fields |= 1;
880          self.gaps_trigger_use_beta = Some(value.extract::<bool>()?);
881          Ok(())
882      }
883      "prescale" => {
884          self.active_fields |= 2;
885          self.prescale = Some(value.extract::<f32>()?);
886          Ok(())
887      }
888      "trigger_type" => {
889          self.active_fields |= 4;
890          self.trigger_type = Some(value.extract::<TriggerType>()?);
891          Ok(())
892      }
893      "use_combo_trigger" => {
894          self.active_fields |= 8;
895          self.use_combo_trigger = Some(value.extract::<bool>()?);
896          Ok(())
897      }
898      "combo_trigger_type" => {
899          self.active_fields |= 16;
900          self.combo_trigger_type = Some(value.extract::<TriggerType>()?);
901          Ok(())
902      }
903      "combo_trigger_prescale" => {
904          self.active_fields |= 32;
905          self.combo_trigger_prescale = Some(value.extract::<f32>()?);
906          Ok(())
907      }
908      "trace_suppression" => {
909          self.active_fields |= 64;
910          self.trace_suppression = Some(value.extract::<bool>()?);
911          Ok(())
912      }
913      "mtb_moni_interval" => {
914          self.active_fields |= 128;
915          self.mtb_moni_interval = Some(value.extract::<u16>()?);
916          Ok(())
917      }
918      "tiu_ignore_busy" => {
919          self.active_fields |= 256;
920          self.tiu_ignore_busy = Some(value.extract::<bool>()?);
921          Ok(())
922      }
923      "hb_send_interval" => {
924          self.active_fields |= 512;
925          self.hb_send_interval = Some(value.extract::<u16>()?);
926          Ok(())
927      }
928      _ => Err(PyKeyError::new_err(format!("Key '{}' not found", key))),
929    }
930  }
931}
932
933#[cfg(feature="pybindings")]
934pythonize_packable!(TriggerConfig);
935
936//-------------------------------------------------
937
938#[derive(Copy, Clone, Debug, PartialEq)]
939#[cfg_attr(feature="pybindings", pyclass)]
940pub struct TofRunConfig {
941  pub active_fields            : u32,
942  pub runtime                  : Option<u32>, 
943}
944
945impl TofRunConfig {
946  pub fn new() -> Self {
947    Self {
948      active_fields : 0,
949      runtime       : None
950    }
951  }
952
953  pub fn set_runtime(&mut self, runtime : u32) {
954    self.active_fields |= 1;
955    self.runtime = Some(runtime);
956  }
957}
958
959impl Default for TofRunConfig {
960  fn default() -> Self {
961    Self::new()
962  }
963}
964
965impl fmt::Display for TofRunConfig {
966  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
967    let mut repr = String::from("<TofRunConfig: ");
968    repr += &(format!("(active fields {:x})", self.active_fields));
969    if self.runtime.is_some() {
970      repr += &(format!("\n  Run time        : {} [s]", self.runtime.unwrap())); 
971    }
972    repr += ">";
973    write!(f, "{}", repr)
974  }
975}
976
977impl TofPackable for TofRunConfig {
978  const TOF_PACKET_TYPE : TofPacketType = TofPacketType::TofRunConfig;
979}
980
981impl Serialization for TofRunConfig {
982  
983  const HEAD : u16   = 0xAAAA;
984  const TAIL : u16   = 0x5555;
985  const SIZE : usize = 12; 
986  
987  fn from_bytestream(stream    : &Vec<u8>, 
988                     pos       : &mut usize) 
989    -> Result<Self, SerializationError>{
990    Self::verify_fixed(stream, pos)?;  
991    let mut cfg        = TofRunConfig::new();
992    cfg.active_fields  = parse_u32(stream, pos);
993    cfg.runtime        = Some(parse_u32 (stream, pos));
994    // disable fields which where not explicitly marked as 
995    // active
996    if cfg.active_fields & 1 != 1 {
997      cfg.runtime = None;
998    }
999    *pos += 2;
1000    Ok(cfg)
1001  }
1002
1003  fn to_bytestream(&self) -> Vec<u8> {
1004    let mut bs = Vec::<u8>::with_capacity(Self::SIZE);
1005    bs.extend_from_slice(&Self::HEAD        .to_le_bytes());
1006    bs.extend_from_slice(&self.active_fields.to_le_bytes());
1007    bs.extend_from_slice(&self.runtime.unwrap_or(0).to_le_bytes());
1008    bs.extend_from_slice(&Self::TAIL.to_le_bytes());
1009    bs
1010  }
1011}
1012
1013#[cfg(feature = "random")]
1014impl FromRandom for TofRunConfig {
1015  fn from_random() -> Self {
1016    let mut cfg                 = Self::new();
1017    let mut rng                 = rand::rng();
1018    let active_fields           = rng.random::<u32>();
1019    cfg.active_fields           = active_fields;
1020    if active_fields & 1 == 1 {
1021      cfg.runtime   = Some(rng.random::<u32>());
1022    }
1023    cfg
1024  }
1025}
1026
1027#[cfg(feature="pybindings")]
1028#[pymethods]
1029impl TofRunConfig {
1030
1031  #[getter]
1032  fn get_runtime(&self) -> Option<u32> {
1033    self.runtime
1034  }
1035
1036  #[setter]
1037  #[pyo3(name="set_runtime")]
1038  fn set_runtime_py(&mut self, runtime : u32) {
1039    self.set_runtime(runtime);    
1040  }
1041}
1042
1043#[cfg(feature="pybindings")]
1044pythonize_packable!(TofRunConfig);
1045
1046//-------------------------------------------------
1047
1048#[derive(Copy, Clone, Debug, PartialEq)]
1049#[cfg_attr(feature="pybindings", pyclass)]
1050pub struct TofRBConfig {
1051  pub active_fields                  : u32,
1052  pub rb_moni_interval               : Option<u32>, 
1053  pub pb_moni_every_x                : Option<u32>,
1054  pub pa_moni_every_x                : Option<u32>,
1055  pub ltb_moni_every_x               : Option<u32>,
1056  pub drs_deadtime_instead_fpga_temp : Option<bool>,
1057}
1058
1059impl TofRBConfig {
1060  pub fn new() -> Self {
1061    Self {
1062     active_fields                 : 0,
1063     rb_moni_interval               : None, 
1064     pb_moni_every_x                : None,
1065     pa_moni_every_x                : None,
1066     ltb_moni_every_x               : None,
1067     drs_deadtime_instead_fpga_temp : None,
1068    }
1069  }
1070
1071  pub fn set_rb_moni_interval(&mut self, interval : u32) {
1072    self.active_fields |= 1;
1073    self.rb_moni_interval = Some(interval);
1074  }
1075  
1076  pub fn set_pb_moni_every_x(&mut self, interval : u32) {
1077    self.active_fields |= 2;
1078    self.pb_moni_every_x = Some(interval);
1079  }
1080  
1081  pub fn set_pa_moni_every_x(&mut self, interval : u32) {
1082    self.active_fields |= 4;
1083    self.pa_moni_every_x = Some(interval);
1084  }
1085  
1086  pub fn set_ltb_moni_every_x(&mut self, interval : u32) {
1087    self.active_fields |= 8;
1088    self.ltb_moni_every_x = Some(interval);
1089  }
1090  
1091  pub fn set_drs_deadtime_instead_fpga_temp(&mut self, apply : bool) {
1092    self.active_fields |= 16;
1093    self.drs_deadtime_instead_fpga_temp = Some(apply);
1094  }
1095}
1096
1097impl Default for TofRBConfig {
1098  fn default() -> Self {
1099    Self::new()
1100  }
1101}
1102
1103impl fmt::Display for TofRBConfig {
1104  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1105    let mut repr = String::from("<TofRBConfig: ");
1106    repr += &(format!("(active fields {:x})", self.active_fields));
1107    if self.rb_moni_interval.is_some() {
1108      repr += &(format!("\n  RBMoni interval : {} [s]", self.rb_moni_interval.unwrap())); 
1109    }
1110    if self.pa_moni_every_x.is_some() {
1111      repr += &(format!("\n  PAMoni interval : {} [xRBMoni]", self.pa_moni_every_x.unwrap())); 
1112    }
1113    if self.pb_moni_every_x.is_some() {
1114      repr += &(format!("\n  PBMoni interval : {} [xRBMoni]", self.pb_moni_every_x.unwrap())); 
1115    }
1116    if self.ltb_moni_every_x.is_some() {
1117      repr += &(format!("\n  LTBMoni interval : {} [xRBMoni]", self.ltb_moni_every_x.unwrap())); 
1118    }
1119    if self.drs_deadtime_instead_fpga_temp.is_some() {
1120      if self.drs_deadtime_instead_fpga_temp.unwrap() {
1121        repr += &(format!("\n  -- using the fpga temp field to store drs deadtime values")); 
1122      } 
1123    }
1124    repr += ">";
1125    write!(f, "{}", repr)
1126  }
1127}
1128
1129impl TofPackable for TofRBConfig {
1130  const TOF_PACKET_TYPE : TofPacketType = TofPacketType::TofRBConfig;
1131}
1132
1133impl Serialization for TofRBConfig {
1134  
1135  const HEAD : u16   = 0xAAAA;
1136  const TAIL : u16   = 0x5555;
1137  const SIZE : usize = 25; 
1138  
1139  fn from_bytestream(stream    : &Vec<u8>, 
1140                     pos       : &mut usize) 
1141    -> Result<Self, SerializationError>{
1142    Self::verify_fixed(stream, pos)?;  
1143    let mut cfg          = Self::new();
1144    cfg.active_fields    = parse_u32(stream, pos);
1145    cfg.rb_moni_interval = Some(parse_u32 (stream, pos));
1146    cfg.pa_moni_every_x = Some(parse_u32 (stream, pos));
1147    cfg.pb_moni_every_x = Some(parse_u32 (stream, pos));
1148    cfg.ltb_moni_every_x = Some(parse_u32 (stream, pos));
1149    cfg.drs_deadtime_instead_fpga_temp = Some(parse_bool (stream, pos));
1150    // disable fields which where not explicitly marked as 
1151    // active
1152    if cfg.active_fields & 1 != 1 {
1153      cfg.rb_moni_interval = None;
1154    }
1155    if cfg.active_fields & 2 != 2 {
1156      cfg.pa_moni_every_x = None;
1157    }
1158    if cfg.active_fields & 4 != 4 {
1159      cfg.pb_moni_every_x = None;
1160    }
1161    if cfg.active_fields & 8 != 8 {
1162      cfg.ltb_moni_every_x = None;
1163    }
1164    if cfg.active_fields & 16 != 16 {
1165      cfg.drs_deadtime_instead_fpga_temp = None;
1166    }
1167    *pos += 2;
1168    Ok(cfg)
1169  }
1170
1171  fn to_bytestream(&self) -> Vec<u8> {
1172    let mut bs = Vec::<u8>::with_capacity(Self::SIZE);
1173    bs.extend_from_slice(&Self::HEAD        .to_le_bytes());
1174    bs.extend_from_slice(&self.active_fields.to_le_bytes());
1175    bs.extend_from_slice(&self.rb_moni_interval.unwrap_or(0).to_le_bytes());
1176    bs.extend_from_slice(&self.pa_moni_every_x.unwrap_or(0).to_le_bytes());
1177    bs.extend_from_slice(&self.pb_moni_every_x.unwrap_or(0).to_le_bytes());
1178    bs.extend_from_slice(&self.ltb_moni_every_x.unwrap_or(0).to_le_bytes());
1179    bs.push             (self.drs_deadtime_instead_fpga_temp.unwrap_or(false) as u8);
1180    bs.extend_from_slice(&Self::TAIL.to_le_bytes());
1181    bs
1182  }
1183}
1184
1185#[cfg(feature = "random")]
1186impl FromRandom for TofRBConfig {
1187  fn from_random() -> Self {
1188    let mut cfg          = Self::new();
1189    let mut rng          = rand::rng();
1190    let active_fields    = rng.random::<u32>();
1191    cfg.active_fields    = active_fields;
1192    if active_fields & 1 == 1 {
1193      cfg.rb_moni_interval   = Some(rng.random::<u32>());
1194    }
1195    if active_fields & 2 == 2 {
1196      cfg.pa_moni_every_x   = Some(rng.random::<u32>());
1197    }
1198    if active_fields & 4 == 4 {
1199      cfg.pb_moni_every_x   = Some(rng.random::<u32>());
1200    }
1201    if active_fields & 8 == 8 {
1202      cfg.ltb_moni_every_x   = Some(rng.random::<u32>());
1203    }
1204    if active_fields & 16 == 16 {
1205      cfg.drs_deadtime_instead_fpga_temp  = Some(rng.random::<bool>());
1206    }
1207    cfg
1208  }
1209}
1210
1211/////////////////////////////////////
1212
1213
1214
1215#[derive(Copy, Clone, Debug, PartialEq)]
1216#[cfg_attr(feature="pybindings", pyclass)]
1217pub struct DataPublisherConfig {
1218  pub active_fields            : u32,
1219  pub mbytes_per_file          : Option<u16>,
1220  pub discard_event_fraction   : Option<f32>, 
1221  pub send_mtb_event_packets   : Option<bool>,
1222  pub send_rbwaveform_packets  : Option<bool>,
1223  pub send_rbwf_every_x_event  : Option<u32>,
1224  pub send_tof_summary_packets : Option<bool>,
1225  pub send_tof_event_packets   : Option<bool>,
1226  pub hb_send_interval         : Option<u16>,
1227}
1228
1229impl DataPublisherConfig {
1230  pub fn new() -> Self {
1231    Self {
1232      active_fields            : 0,
1233      mbytes_per_file          : None, 
1234      discard_event_fraction   : None, 
1235      send_mtb_event_packets   : None, 
1236      send_rbwaveform_packets  : None, 
1237      send_rbwf_every_x_event  : None, 
1238      send_tof_summary_packets : None, 
1239      send_tof_event_packets   : None, 
1240      hb_send_interval         : None, 
1241    }
1242  }
1243      
1244  pub fn set_mbytes_per_file(&mut self, mbytes : u16) {
1245    self.active_fields |= 1;
1246    self.mbytes_per_file = Some(mbytes);
1247  }
1248
1249  pub fn set_discard_event_fraction(&mut self, frac : f32) {
1250    self.active_fields |= 2;
1251    self.discard_event_fraction = Some(frac);
1252  }
1253
1254  pub fn set_send_mtb_event_packets(&mut self, send : bool) {
1255    self.active_fields |= 4;
1256    self.send_mtb_event_packets = Some(send);
1257  }
1258
1259  pub fn set_send_rbwaveform_packets(&mut self, send : bool) {
1260    self.active_fields |= 8;
1261    self.send_rbwaveform_packets = Some(send);
1262  }
1263
1264  pub fn set_send_rbwf_every_x_event(&mut self, x : u32) {
1265    self.active_fields |= 16;
1266    self.send_rbwf_every_x_event = Some(x);
1267  }
1268
1269  pub fn set_send_tof_summary_packets(&mut self, send : bool) {
1270    self.active_fields |= 32;
1271    self.send_tof_summary_packets = Some(send);
1272  }
1273  
1274  pub fn send_tof_event_packets(&mut self, send : bool) {
1275    self.active_fields |= 64;
1276    self.send_tof_event_packets = Some(send);
1277  }
1278
1279  pub fn set_hb_send_interval(&mut self, interval : u16) {
1280    self.active_fields |= 128;
1281    self.hb_send_interval = Some(interval);
1282  }
1283}
1284
1285impl Default for DataPublisherConfig {
1286  fn default() -> Self {
1287    Self::new()
1288  }
1289}
1290
1291impl fmt::Display for DataPublisherConfig {
1292  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1293    let mut repr = String::from("<DataPublisherConfig: ");
1294    repr += &(format!("(active fields {:x})", self.active_fields));
1295    if self.mbytes_per_file.is_some() {
1296      repr += &(format!("\n  MBytes/FIle        : {}", self.mbytes_per_file.unwrap())); 
1297    }
1298    if self.discard_event_fraction.is_some() {
1299      repr += &(format!("\n  DIsc. event frac   : {}", self.discard_event_fraction.unwrap())); 
1300    }
1301    if self.send_mtb_event_packets.is_some() {
1302      repr += &(format!("\n  Send MTBPack       : {}", self.send_mtb_event_packets.unwrap())); 
1303    }
1304    if self.send_rbwaveform_packets.is_some() {
1305      repr += &(format!("\n  Send RBWfPack      : {}", self.send_rbwaveform_packets.unwrap())); 
1306    }
1307    if self.send_rbwf_every_x_event.is_some() {
1308      repr += &(format!("\n  RBWf every x event : {}", self.send_rbwf_every_x_event.unwrap())); 
1309    }
1310    if self.send_tof_summary_packets.is_some() {
1311      repr += &(format!("\n  Send TofSum        : {}", self.send_tof_summary_packets.unwrap())); 
1312    }
1313    if self.send_tof_event_packets.is_some() {
1314      repr += &(format!("\n  Send TOfEvent      : {}", self.send_tof_event_packets.unwrap())); 
1315    }
1316    if self.hb_send_interval.is_some() {
1317      repr += &(format!("\n  HeartBeat send int  : {}", self.hb_send_interval.unwrap())); 
1318    }
1319    repr += ">";
1320    write!(f, "{}", repr)
1321  }
1322}
1323
1324impl TofPackable for DataPublisherConfig {
1325  const TOF_PACKET_TYPE : TofPacketType = TofPacketType::DataPublisherConfig;
1326}
1327
1328impl Serialization for DataPublisherConfig {
1329  
1330  const HEAD : u16 = 0xAAAA;
1331  const TAIL : u16 = 0x5555;
1332  const SIZE : usize = 24; 
1333  
1334  fn from_bytestream(stream    : &Vec<u8>, 
1335                     pos       : &mut usize) 
1336    -> Result<Self, SerializationError>{
1337    Self::verify_fixed(stream, pos)?;  
1338    let mut cfg                = DataPublisherConfig::new();
1339    cfg.active_fields          = parse_u32(stream, pos);
1340    cfg.mbytes_per_file          = Some(parse_u16 (stream, pos));
1341    cfg.discard_event_fraction   = Some(parse_f32 (stream, pos));
1342    cfg.send_mtb_event_packets   = Some(parse_bool(stream, pos));
1343    cfg.send_rbwaveform_packets  = Some(parse_bool(stream, pos));
1344    cfg.send_rbwf_every_x_event  = Some(parse_u32 (stream, pos));
1345    cfg.send_tof_summary_packets = Some(parse_bool(stream, pos));
1346    cfg.send_tof_event_packets   = Some(parse_bool(stream, pos));
1347    cfg.hb_send_interval         = Some(parse_u16 (stream, pos));
1348    // disable fields which where not explicitly marked as 
1349    // active
1350    if cfg.active_fields & 1 != 1 {
1351      cfg.mbytes_per_file = None;
1352    }
1353    if cfg.active_fields & 2 != 2 {
1354      cfg.discard_event_fraction = None;
1355    }
1356    if cfg.active_fields & 4 != 4 {
1357      cfg.send_mtb_event_packets = None;
1358    }
1359    if cfg.active_fields & 8 != 8 {
1360      cfg.send_rbwaveform_packets = None;
1361    }
1362    if cfg.active_fields & 16 != 16 {
1363      cfg.send_rbwf_every_x_event = None;
1364    }
1365    if cfg.active_fields & 32 != 32 {
1366      cfg.send_tof_summary_packets = None;
1367    }
1368    if cfg.active_fields & 64 != 64 {
1369      cfg.send_tof_event_packets = None;
1370    }
1371    if cfg.active_fields & 128 != 128 {
1372      cfg.hb_send_interval = None;
1373    }
1374    *pos += 2;
1375    Ok(cfg)
1376  }
1377
1378  fn to_bytestream(&self) -> Vec<u8> {
1379    let mut bs = Vec::<u8>::with_capacity(Self::SIZE);
1380    bs.extend_from_slice(&Self::HEAD        .to_le_bytes());
1381    bs.extend_from_slice(&self.active_fields.to_le_bytes());
1382    bs.extend_from_slice(&self.mbytes_per_file.unwrap_or(0).to_le_bytes());
1383    bs.extend_from_slice(&self.discard_event_fraction.unwrap_or(0.0).to_le_bytes());
1384    bs.push             (self .send_mtb_event_packets.unwrap_or(false)  as u8);
1385    bs.push             (self .send_rbwaveform_packets.unwrap_or(false) as u8);
1386    bs.extend_from_slice(&self.send_rbwf_every_x_event.unwrap_or(0).to_le_bytes());
1387    bs.push             (self.send_tof_summary_packets.unwrap_or(false) as u8);
1388    bs.push             (self .send_tof_event_packets.unwrap_or(false) as u8);
1389    bs.extend_from_slice(&self.hb_send_interval.unwrap_or(30).to_le_bytes());
1390    bs.extend_from_slice(&Self::TAIL.to_le_bytes());
1391    bs
1392  }
1393}
1394
1395#[cfg(feature = "random")]
1396impl FromRandom for DataPublisherConfig {
1397  fn from_random() -> Self {
1398    let mut cfg                 = DataPublisherConfig::new();
1399    let mut rng                 = rand::rng();
1400    let active_fields           = rng.random::<u32>();
1401    cfg.active_fields           = active_fields;
1402    if active_fields & 1 == 1 {
1403      cfg.mbytes_per_file   = Some(rng.random::<u16>());
1404    } else {
1405      cfg.mbytes_per_file = None;
1406    }
1407    if active_fields & 2 == 2 {
1408      cfg.discard_event_fraction = Some(rng.random::<f32>());
1409    } else {
1410      cfg.discard_event_fraction = None;
1411    }
1412    if active_fields & 4 == 4 {
1413      cfg.send_mtb_event_packets = Some(rng.random::<bool>());
1414    } else {
1415      cfg.send_mtb_event_packets = None;
1416    }
1417    if active_fields & 8 == 8 {
1418      cfg.send_rbwaveform_packets = Some(rng.random::<bool>());
1419    } else {
1420      cfg.send_rbwaveform_packets = None;
1421    }
1422    if active_fields & 16 == 16 {
1423      cfg.send_rbwf_every_x_event = Some(rng.random::<u32>());
1424    } else {
1425      cfg.send_rbwf_every_x_event = None;
1426    }
1427    if active_fields & 32 == 32 {
1428      cfg.send_tof_summary_packets  = Some(rng.random::<bool>());
1429    } else {
1430      cfg.send_tof_summary_packets = None;
1431    }
1432    if active_fields & 64 == 64 {
1433      cfg.send_tof_event_packets       = Some(rng.random::<bool>());
1434    } else {
1435      cfg.send_tof_event_packets = None;
1436    }
1437    if active_fields & 128 == 128 {
1438      cfg.hb_send_interval       = Some(rng.random::<u16>());
1439    } else {
1440      cfg.hb_send_interval = None;
1441    }
1442    cfg
1443  }
1444}
1445
1446
1447
1448///Analysis Engine Config
1449/// Settings to change the configuration of the analysis engine 
1450/// (pulse extraction)
1451#[derive(Copy, Clone, Debug, PartialEq)]
1452#[cfg_attr(feature="pybindings", pyclass)]
1453pub struct AnalysisEngineConfig{
1454  pub integration_start  : f32, //4
1455  pub integration_window : f32, //4
1456  pub pedestal_thresh    : f32, //4
1457  pub pedestal_begin_bin : usize, //8
1458  pub pedestal_win_bins  : usize, //8
1459  pub use_zscore         : bool, //1
1460  pub find_pks_t_start   : f32, //4
1461  pub find_pks_t_window  : f32, //4
1462  pub min_peak_size      : usize, //8
1463  pub find_pks_thresh    : f32, //4
1464  pub max_peaks          : usize, //8
1465  pub cfd_fraction       : f32 //4
1466}
1467
1468impl AnalysisEngineConfig {
1469  pub fn new() -> Self {
1470    Self {
1471      integration_start         : 270.0,
1472      integration_window        : 70.0, 
1473      pedestal_thresh           : 10.0,
1474      pedestal_begin_bin        : 10,
1475      pedestal_win_bins         : 50,
1476      use_zscore                : false,
1477      find_pks_t_start          : 270.0,
1478      find_pks_t_window         : 70.0,
1479      min_peak_size             : 3,
1480      find_pks_thresh           : 10.0,
1481      max_peaks                 : 5, //max peak size?? ask
1482      cfd_fraction              : 0.2
1483    }
1484  }
1485}
1486
1487impl Default for AnalysisEngineConfig {
1488  fn default() -> Self {
1489    Self::new()
1490  }
1491}
1492
1493impl fmt::Display for AnalysisEngineConfig {
1494  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1495    let mut repr: String = String::from("<AnalysisEngineConfig");
1496    repr += &(format!("\n Integration start         : {:.1}", self.integration_start));
1497    repr += &(format!("\n Integration window        : {:.1}", self.integration_window));
1498    repr += &(format!("\n Pedestal threshold        : {:.1}", self.pedestal_thresh));
1499    repr += &(format!("\n Pedestal start bin        : {}", self.pedestal_begin_bin));
1500    repr += &(format!("\n Pedestal window num. bins : {}", self.pedestal_win_bins));
1501    repr += &(format!("\n Use zscore?               : {}", self.use_zscore));
1502    repr += &(format!("\n Peakfinder start time     : {:.1}", self.find_pks_t_start));
1503    repr += &(format!("\n Peakfinder window         : {:.1}", self.find_pks_t_window));
1504    repr += &(format!("\n Peakfinder threshold      : {:.1}", self.find_pks_thresh));
1505    repr += &(format!("\n Min. peak size            : {}", self.min_peak_size));
1506    repr += &(format!("\n Max num. peaks            : {}", self.max_peaks));
1507    repr += &(format!("\n CFD fraction              : {:.2}", self.cfd_fraction));
1508    write!(f, "{}", repr)
1509  }
1510}
1511
1512impl TofPackable for AnalysisEngineConfig {
1513  const TOF_PACKET_TYPE : TofPacketType = TofPacketType::AnalysisEngineConfig;
1514}
1515
1516impl Serialization for AnalysisEngineConfig {
1517  
1518  const HEAD : u16 = 0xAAAA; //2
1519  const TAIL : u16 = 0x5555; //2
1520  const SIZE : usize = 65; //61+2+2 = 65
1521  
1522  fn from_bytestream(stream    : &Vec<u8>, 
1523                     pos       : &mut usize) 
1524    -> Result<Self, SerializationError>{
1525    Self::verify_fixed(stream, pos)?;  
1526    let mut cfg: AnalysisEngineConfig = AnalysisEngineConfig::new();
1527      cfg.integration_start  = parse_f32(stream, pos);
1528      cfg.integration_window = parse_f32(stream, pos);
1529      cfg.pedestal_thresh    = parse_f32(stream, pos);
1530      cfg.pedestal_begin_bin = parse_u64(stream, pos) as usize;
1531      cfg.pedestal_win_bins  = parse_u64(stream, pos) as usize;
1532      cfg.use_zscore         = parse_bool(stream, pos);
1533      cfg.find_pks_t_start   = parse_f32(stream, pos);
1534      cfg.find_pks_t_window  = parse_f32(stream, pos);
1535      cfg.find_pks_thresh    = parse_f32(stream, pos);
1536      cfg.min_peak_size      = parse_u64(stream, pos) as usize;
1537      cfg.max_peaks          = parse_u64(stream, pos) as usize;
1538      cfg.cfd_fraction       = parse_f32(stream, pos);
1539    *pos += 2;
1540    Ok(cfg)
1541  }
1542
1543  fn to_bytestream(&self) -> Vec<u8> {
1544    let mut bs = Vec::<u8>::with_capacity(Self::SIZE);
1545    bs.extend_from_slice(&Self::HEAD.to_le_bytes());
1546    bs.extend_from_slice(&self.integration_start.to_le_bytes());
1547    bs.extend_from_slice(&self.integration_window.to_le_bytes());
1548    bs.extend_from_slice(&self.pedestal_thresh.to_le_bytes());
1549    bs.extend_from_slice(&(self.pedestal_begin_bin as u64).to_le_bytes());
1550    bs.extend_from_slice(&(self.pedestal_win_bins as u64).to_le_bytes());
1551    bs.push(self.use_zscore as u8);
1552    bs.extend_from_slice(&self.find_pks_t_start.to_le_bytes());
1553    bs.extend_from_slice(&self.find_pks_t_window.to_le_bytes());
1554    bs.extend_from_slice(&self.find_pks_thresh.to_le_bytes());
1555    bs.extend_from_slice(&(self.min_peak_size as u64).to_le_bytes());
1556    bs.extend_from_slice(&(self.max_peaks as u64).to_le_bytes());
1557    bs.extend_from_slice(&self.cfd_fraction.to_le_bytes());
1558    bs.extend_from_slice(&Self::TAIL.to_le_bytes());
1559    bs
1560  }
1561}
1562
1563#[cfg(feature = "random")]
1564impl FromRandom for AnalysisEngineConfig {
1565  fn from_random() -> Self {
1566    let mut cfg  = AnalysisEngineConfig::new();
1567    let mut rng            = rand::rng();
1568    cfg.integration_start  = rng.random::<f32>();
1569    cfg.integration_window = rng.random::<f32>();
1570    cfg.pedestal_thresh    = rng.random::<f32>();
1571    cfg.pedestal_begin_bin = rng.random::<u64>() as usize;
1572    cfg.pedestal_win_bins  = rng.random::<u64>() as usize;
1573    cfg.use_zscore         = rng.random::<bool>();
1574    cfg.find_pks_t_start   = rng.random::<f32>();
1575    cfg.find_pks_t_window  = rng.random::<f32>();
1576    cfg.find_pks_thresh    = rng.random::<f32>();
1577    cfg.min_peak_size      = rng.random::<u64>() as usize;
1578    cfg.max_peaks          = rng.random::<u64>() as usize;
1579    cfg.cfd_fraction       = rng.random::<f32>();
1580    cfg
1581  }
1582}
1583
1584
1585
1586/// TOF Event Builder Settings
1587/// Configuring the TOF event builder during flight
1588/// If a setting is set to None, it will keep the 
1589/// previous setting
1590#[derive(Copy, Clone, Debug, PartialEq)]
1591#[cfg_attr(feature="pybindings", pyclass)]
1592pub struct TOFEventBuilderConfig{
1593  pub active_fields         : u32, // supports up to 32 active components
1594  pub cachesize             : Option<u32>, 
1595  pub n_mte_per_loop        : Option<u32>, 
1596  pub n_rbe_per_loop        : Option<u32>, 
1597  pub te_timeout_sec        : Option<u32>,
1598  pub te_timeout_sec_combo  : Option<u32>,
1599  pub holdoff               : Option<u32>,
1600  pub sort_events           : Option<bool>,
1601  pub build_strategy        : Option<BuildStrategy>, 
1602  pub wait_nrb              : Option<u8>, 
1603  pub greediness            : Option<u8>, 
1604  pub hb_send_interval      : Option<u16>,
1605  // NEW - mark events as not to be sent!
1606  pub only_save_interesting : Option<bool>,
1607  pub thr_n_hits_umb        : Option<u8>,
1608  pub thr_n_hits_cbe        : Option<u8>,
1609  pub thr_n_hits_cor        : Option<u8>,
1610  pub thr_tot_edep_umb      : Option<f32>,
1611  pub thr_tot_edep_cbe      : Option<f32>,
1612  pub thr_tot_edep_cor      : Option<f32>
1613}
1614
1615impl TOFEventBuilderConfig {
1616  pub fn new() -> Self { 
1617    Self {
1618      active_fields         : 0,
1619      cachesize             : None,
1620      n_mte_per_loop        : None,
1621      n_rbe_per_loop        : None,
1622      te_timeout_sec        : None,
1623      te_timeout_sec_combo  : None,
1624      holdoff               : None,
1625      sort_events           : None,
1626      build_strategy        : None,
1627      wait_nrb              : None, 
1628      greediness            : None,  
1629      hb_send_interval      : None,
1630      only_save_interesting : None,
1631      thr_n_hits_umb        : None,
1632      thr_n_hits_cbe        : None,
1633      thr_n_hits_cor        : None,
1634      thr_tot_edep_umb      : None,
1635      thr_tot_edep_cbe      : None,
1636      thr_tot_edep_cor      : None,
1637    }
1638  }
1639      
1640  pub fn set_cachesize(&mut self, csize : u32) {
1641    self.active_fields |= 1;
1642    self.cachesize = Some(csize);
1643  }
1644  
1645  pub fn set_n_mte_per_loop(&mut self, n : u32) {
1646    self.active_fields |= 2;
1647    self.n_mte_per_loop = Some(n);
1648  }
1649
1650  pub fn set_n_rbe_per_loop(&mut self, n : u32) {
1651    self.active_fields |= 4;
1652    self.n_rbe_per_loop = Some(n);
1653  }
1654
1655  pub fn set_te_timeout_sec(&mut self, te : u32) {
1656    self.active_fields |= 8;
1657    self.te_timeout_sec = Some(te);
1658  }
1659
1660  pub fn set_sort_events(&mut self, sort : bool) {
1661    self.active_fields |= 16;
1662    self.sort_events = Some(sort);
1663  }
1664
1665  pub fn set_build_strategy(&mut self, bs : BuildStrategy) {
1666    self.active_fields |= 32;
1667    self.build_strategy = Some(bs);
1668  }
1669
1670  pub fn set_wait_nrb(&mut self, nrb : u8) {
1671    self.active_fields |= 64;
1672    self.wait_nrb = Some(nrb);
1673  }
1674
1675  pub fn set_greediness(&mut self, greed : u8) {
1676    self.active_fields |= 128;
1677    self.greediness = Some(greed);
1678  }
1679
1680  pub fn set_hb_send_interval(&mut self, interval : u16) {
1681    self.active_fields |= 256;
1682    self.hb_send_interval = Some(interval);
1683  }
1684
1685  pub fn set_only_save_interesting(&mut self, do_it : bool) {
1686    self.active_fields |= 512;
1687    self.only_save_interesting = Some(do_it);
1688  }
1689
1690  pub fn thr_n_hits_umb(&mut self, nhit : u8) {
1691    self.active_fields |= 1024;
1692    self.thr_n_hits_umb = Some(nhit);
1693  }
1694  
1695  pub fn thr_n_hits_cbe(&mut self, nhit : u8) {
1696    self.active_fields |= 2048;
1697    self.thr_n_hits_cbe = Some(nhit);
1698  }
1699  
1700  pub fn thr_n_hits_cor(&mut self, nhit : u8) {
1701    self.active_fields |= 2u32.pow(12);
1702    self.thr_n_hits_cor = Some(nhit);
1703  }
1704  
1705  pub fn thr_tot_edep_umb(&mut self, thr : f32) {
1706    self.active_fields |= 2u32.pow(13);
1707    self.thr_tot_edep_umb = Some(thr);
1708  }
1709  
1710  pub fn thr_tot_edep_cbe(&mut self, thr : f32) {
1711    self.active_fields |= 2u32.pow(14);
1712    self.thr_tot_edep_cbe = Some(thr);
1713  }
1714  
1715  pub fn thr_tot_edep_cor(&mut self, thr : f32) {
1716    self.active_fields |= 2u32.pow(15);
1717    self.thr_tot_edep_cor = Some(thr);
1718  }
1719}
1720
1721impl Default for TOFEventBuilderConfig {
1722  fn default() -> Self {
1723    Self::new()
1724  }
1725}
1726
1727impl fmt::Display for TOFEventBuilderConfig {
1728  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1729    let mut repr = String::from("<TOFEventBuilderConfig");
1730    repr += &(format!(" (active_fields {:x}", self.active_fields)); 
1731    if self.cachesize.is_some() {
1732      repr += &(format!("\n Cache size                              : {}", self.cachesize.unwrap())); 
1733    }
1734    if self.n_mte_per_loop.is_some() {
1735      repr += &(format!("\n Num. master trigger events per loop     : {}", self.n_mte_per_loop.unwrap()));
1736    }
1737    if self.n_rbe_per_loop.is_some() {
1738      repr += &(format!("\n Num. readout board events per loop      : {}", self.n_rbe_per_loop.unwrap()));
1739    }
1740    if self.te_timeout_sec.is_some() {
1741      repr += &(format!("\n TOF Event timeout window [sec]          : {:.3}", self.te_timeout_sec.unwrap()));
1742    }
1743    if self.sort_events.is_some() {
1744      repr += &(format!("\n Sort events by ID (high resource load!) : {}", self.sort_events.unwrap()));
1745    }
1746    if self.build_strategy.is_some() {
1747      repr += &(format!("\n Build strategy                          : {}", self.build_strategy.unwrap()));
1748      if self.build_strategy.unwrap() == BuildStrategy::AdaptiveGreedy {
1749        if self.greediness.is_some() {
1750          repr += &(format!("\n Additional RBs considered (greediness)  : {}", self.greediness.unwrap()));
1751        }
1752      } else if self.build_strategy.unwrap() == BuildStrategy::WaitForNBoards {
1753        if self.wait_nrb.is_some() {
1754          repr += &(format!("\n Waiting for {} boards", self.wait_nrb.unwrap()))
1755        }
1756      }
1757    }
1758    if self.hb_send_interval.is_some() {
1759      repr += &(format!("\n Heartbeat send interval : {}", self.hb_send_interval.unwrap()));
1760    }
1761    if self.only_save_interesting.is_some() {
1762      repr += &(format!("\n Saving only interesting events : {}", self.only_save_interesting.unwrap()));
1763    }
1764    if self.thr_n_hits_umb.is_some() {
1765      repr += &(format!("\n Interesting threshold for nhit umb : {}", self.thr_n_hits_umb.unwrap()));
1766    }
1767    if self.thr_n_hits_cbe.is_some() {
1768      repr += &(format!("\n Interesting threshold for nhit cbe : {}", self.thr_n_hits_cbe.unwrap()));
1769    }
1770    if self.thr_n_hits_cor.is_some() {
1771      repr += &(format!("\n Interesting threshold for nhit cor : {}", self.thr_n_hits_cor.unwrap()));
1772    }
1773    if self.thr_tot_edep_umb.is_some() {
1774      repr += &(format!("\n Interesting threshold for tot edep umb : {}", self.thr_tot_edep_umb.unwrap()));
1775    }
1776    if self.thr_tot_edep_cbe.is_some() {
1777      repr += &(format!("\n Interesting threshold for tot edep cbe : {}", self.thr_tot_edep_cbe.unwrap()));
1778    }
1779    if self.thr_tot_edep_cor.is_some() {
1780      repr += &(format!("\n Interesting threshold for tot edep cor : {}", self.thr_tot_edep_cor.unwrap()));
1781    }
1782    write!(f, "{}", repr)
1783  }
1784}
1785
1786impl TofPackable for TOFEventBuilderConfig {
1787  const TOF_PACKET_TYPE : TofPacketType = TofPacketType::TOFEventBuilderConfig;
1788}
1789
1790impl Serialization for TOFEventBuilderConfig {
1791  
1792  const HEAD : u16 = 0xAAAA;
1793  const TAIL : u16 = 0x5555;
1794  const SIZE : usize = 46; 
1795  
1796  fn from_bytestream(stream    : &Vec<u8>, 
1797                     pos       : &mut usize) 
1798    -> Result<Self, SerializationError> {
1799    Self::verify_fixed(stream, pos)?;  
1800    let mut cfg = TOFEventBuilderConfig::new();
1801    cfg.active_fields    = parse_u32(stream, pos);
1802    cfg.cachesize        = Some(parse_u32(stream, pos));
1803    cfg.n_mte_per_loop   = Some(parse_u32(stream, pos));
1804    cfg.n_rbe_per_loop   = Some(parse_u32(stream, pos));
1805    cfg.te_timeout_sec   = Some(parse_u32(stream, pos));
1806    cfg.sort_events      = Some(parse_bool(stream, pos));
1807    cfg.build_strategy   = Some(BuildStrategy::from(parse_u8(stream, pos)));
1808    cfg.wait_nrb         = Some(parse_u8(stream, pos));
1809    cfg.greediness       = Some(parse_u8(stream, pos));
1810    cfg.hb_send_interval = Some(parse_u16(stream, pos));
1811    // new stuff
1812    cfg.only_save_interesting = Some(parse_bool(stream, pos));
1813    cfg.thr_n_hits_umb = Some(parse_u8(stream, pos));
1814    cfg.thr_n_hits_cbe = Some(parse_u8(stream, pos));
1815    cfg.thr_n_hits_cor = Some(parse_u8(stream, pos));
1816    cfg.thr_tot_edep_umb = Some(parse_f32(stream, pos));
1817    cfg.thr_tot_edep_cbe = Some(parse_f32(stream, pos));
1818    cfg.thr_tot_edep_cor = Some(parse_f32(stream, pos));
1819    
1820    if cfg.active_fields & 1 != 1 {
1821      cfg.cachesize      = None;
1822    }
1823    if cfg.active_fields & 2 != 2 {
1824      cfg.n_mte_per_loop = None;
1825    }
1826    if cfg.active_fields & 4 != 4 {
1827      cfg.n_rbe_per_loop = None;
1828    }
1829    if cfg.active_fields & 8 != 8 {
1830      cfg.te_timeout_sec = None;
1831    }
1832    if cfg.active_fields & 16 != 16 {
1833      cfg.sort_events    = None;
1834    }
1835    if cfg.active_fields & 32 != 32 {
1836      cfg.build_strategy = None;
1837    }
1838    if cfg.active_fields & 64 != 64 {
1839      cfg.wait_nrb       = None;
1840    }
1841    if cfg.active_fields & 128 != 128 {
1842      cfg.greediness     = None;
1843    }
1844    if cfg.active_fields & 256 != 256 {
1845      cfg.hb_send_interval = None;
1846    }
1847    if cfg.active_fields & 512 != 512 {
1848      cfg.only_save_interesting = None;
1849    }
1850    if cfg.active_fields & 1024 != 1024 {
1851      cfg.thr_n_hits_umb = None;
1852    }
1853    if cfg.active_fields & 2048 != 2048 {
1854      cfg.thr_n_hits_cbe = None;
1855    }
1856    if cfg.active_fields & 2u32.pow(12) != 2u32.pow(12) {
1857      cfg.thr_n_hits_cor = None;
1858    }
1859    if cfg.active_fields & 2u32.pow(13) != 2u32.pow(13) {
1860      cfg.thr_tot_edep_umb = None;
1861    }
1862    if cfg.active_fields & 2u32.pow(14) != 2u32.pow(14) {
1863      cfg.thr_tot_edep_cbe = None;
1864    }
1865    if cfg.active_fields & 2u32.pow(15) != 2u32.pow(15) {
1866      cfg.thr_tot_edep_cor = None;
1867    }
1868    *pos += 2;
1869    Ok(cfg)
1870  }
1871 
1872  fn to_bytestream(&self) -> Vec<u8> {
1873    let mut bs = Vec::<u8>::with_capacity(Self::SIZE);
1874    bs.extend_from_slice(&Self::HEAD.to_le_bytes());
1875    bs.extend_from_slice(&self.active_fields.to_le_bytes());
1876    bs.extend_from_slice(&self.cachesize.unwrap_or(0).to_le_bytes());
1877    bs.extend_from_slice(&self.n_mte_per_loop.unwrap_or(0).to_le_bytes());
1878    bs.extend_from_slice(&self.n_rbe_per_loop.unwrap_or(0).to_le_bytes());
1879    bs.extend_from_slice(&self.te_timeout_sec.unwrap_or(0).to_le_bytes());
1880    bs.push(self.sort_events.unwrap_or(false) as u8);
1881    bs.push(self.build_strategy.unwrap_or(BuildStrategy::Unknown) as u8);
1882    bs.push(self.wait_nrb.unwrap_or(0));
1883    bs.push(self.greediness.unwrap_or(0));
1884    bs.extend_from_slice(&self.hb_send_interval.unwrap_or(0).to_le_bytes());
1885    // new stuff
1886    bs.push(self.only_save_interesting.unwrap_or(false) as u8);
1887    bs.extend_from_slice(&self.thr_n_hits_umb.unwrap_or(0).to_le_bytes());
1888    bs.extend_from_slice(&self.thr_n_hits_cbe.unwrap_or(0).to_le_bytes());
1889    bs.extend_from_slice(&self.thr_n_hits_cor.unwrap_or(0).to_le_bytes());
1890    bs.extend_from_slice(&self.thr_tot_edep_umb.unwrap_or(0.0).to_le_bytes());
1891    bs.extend_from_slice(&self.thr_tot_edep_cbe.unwrap_or(0.0).to_le_bytes());
1892    bs.extend_from_slice(&self.thr_tot_edep_cor.unwrap_or(0.0).to_le_bytes());
1893    bs.extend_from_slice(&Self::TAIL.to_le_bytes());
1894    bs
1895  }
1896}
1897
1898#[cfg(feature = "random")]
1899impl FromRandom for TOFEventBuilderConfig {
1900  fn from_random() -> Self {
1901    let mut cfg             = TOFEventBuilderConfig::new();
1902    let mut rng             = rand::rng();
1903    cfg.active_fields       = rng.random::<u32>();
1904    if cfg.active_fields & 1 == 1 {
1905      cfg.cachesize         = Some(rng.random::<u32>());
1906    }
1907    if cfg.active_fields & 2 == 2 {
1908      cfg.n_mte_per_loop      = Some(rng.random::<u32>());
1909    }
1910    if cfg.active_fields & 4 == 4 {
1911      cfg.n_rbe_per_loop      = Some(rng.random::<u32>());
1912    }
1913    if cfg.active_fields & 8 == 8 {
1914      cfg.te_timeout_sec      = Some(rng.random::<u32>());
1915    }
1916    if cfg.active_fields & 16 == 16 {
1917      cfg.sort_events         = Some(rng.random::<bool>());
1918    }
1919    if cfg.active_fields & 32 == 32 {
1920      cfg.build_strategy      = Some(BuildStrategy::from_random());
1921    }
1922    if cfg.active_fields & 64 == 64 {
1923      cfg.wait_nrb = Some(rng.random::<u8>());
1924    }
1925    if cfg.active_fields & 128 == 128 {
1926      cfg.greediness = Some(rng.random::<u8>());
1927    }
1928    if cfg.active_fields & 256 == 256 {
1929      cfg.hb_send_interval = Some(rng.random::<u16>());
1930    }
1931    if cfg.active_fields & 512 == 512 {
1932      cfg.only_save_interesting = Some(rng.random::<bool>());
1933    }
1934    if cfg.active_fields & 1024 == 1024 {
1935      cfg.thr_n_hits_umb = Some(rng.random::<u8>());
1936    }
1937    if cfg.active_fields & 2048 == 2048 {
1938      cfg.thr_n_hits_cbe = Some(rng.random::<u8>());
1939    }
1940    if cfg.active_fields & 2u32.pow(12) == 2u32.pow(12) {
1941      cfg.thr_n_hits_cor = Some(rng.random::<u8>());
1942    }
1943    if cfg.active_fields & 2u32.pow(13) == 2u32.pow(13) {
1944      cfg.thr_tot_edep_umb = Some(rng.random::<f32>());
1945    }
1946    if cfg.active_fields & 2u32.pow(14) == 2u32.pow(14) {
1947      cfg.thr_tot_edep_cbe = Some(rng.random::<f32>());
1948    }
1949    if cfg.active_fields & 2u32.pow(15) == 2u32.pow(15) {
1950      cfg.thr_tot_edep_cor = Some(rng.random::<f32>());
1951    }
1952    cfg
1953  }
1954}
1955
1956
1957
1958#[cfg(feature = "random")]
1959#[test]
1960fn pack_analysisengineconfig() {
1961  for _ in 0..100 {
1962    let cfg  = AnalysisEngineConfig::from_random();
1963    let test : AnalysisEngineConfig = cfg.pack().unpack().unwrap();
1964    assert_eq!(cfg, test);
1965  }
1966}
1967
1968
1969
1970//////////////////////////////////////////
1971// TESTS
1972
1973#[cfg(feature = "random")]
1974#[test]
1975fn pack_preampbiasconfig() {
1976  for _ in 0..100 {
1977    let cfg  = PreampBiasConfig::from_random();
1978    let test : PreampBiasConfig = cfg.pack().unpack().unwrap();
1979    assert_eq!(cfg, test);
1980  }
1981}
1982
1983#[cfg(feature = "random")]
1984#[test]
1985fn pack_rb_channel_mask_config() {
1986  for _ in 0..100 {
1987    let cfg   = RBChannelMaskConfig::from_random();
1988    let test : RBChannelMaskConfig = cfg.pack().unpack().unwrap();
1989    assert_eq!(cfg, test);
1990  }
1991}
1992
1993
1994#[cfg(feature = "random")]
1995#[test]
1996fn pack_ltbthresholdconfig() {
1997  for _ in 0..100 {
1998    let cfg   = LTBThresholdConfig::from_random();
1999    let test : LTBThresholdConfig = cfg.pack().unpack().unwrap();
2000    assert_eq!(cfg, test);
2001  }
2002}
2003
2004// we don't serialize the config anymore
2005//#[cfg(feature = "random")]
2006//#[test]
2007//fn pack_runconfig() {
2008//  for _ in 0..100 {
2009//    let cfg  = RunConfig::from_random();
2010//    let test = cfg.pack().unpack().unwrap();
2011//    //let test = RunConfig::from_bytestream(&cfg.to_bytestream(), &mut 0).unwrap();
2012//    assert_eq!(cfg, test);
2013//
2014//    let cfg_json = serde_json::to_string(&cfg).unwrap();
2015//    let test_json 
2016//      = serde_json::from_str::<RunConfig>(&cfg_json).unwrap();
2017//    assert_eq!(cfg, test_json);
2018//  }
2019//}
2020
2021#[cfg(feature = "random")]
2022#[test]
2023fn pack_triggerconfig() {
2024  for _ in 0..100 {
2025    let cfg  = TriggerConfig::from_random();
2026    let test : TriggerConfig = cfg.pack().unpack().unwrap();
2027    assert_eq!(cfg, test);
2028  }
2029}
2030
2031#[cfg(feature = "random")]
2032#[test]
2033fn pack_tofeventbuilderconfig() {
2034  for _ in 0..100 {
2035    let cfg  = TOFEventBuilderConfig::from_random();
2036    let test : TOFEventBuilderConfig = cfg.pack().unpack().unwrap();
2037    assert_eq!(cfg, test);
2038  }
2039}
2040
2041#[cfg(feature = "random")]
2042#[test]
2043fn pack_datapublisherconfig() {
2044  for _ in 0..100 {
2045    let cfg  = DataPublisherConfig::from_random();
2046    let test : DataPublisherConfig = cfg.pack().unpack().unwrap();
2047    assert_eq!(cfg, test);
2048  }
2049}
2050
2051#[cfg(feature = "random")]
2052#[test]
2053fn pack_tofrunconfig() {
2054  for _ in 0..100 {
2055    let cfg  = TofRunConfig::from_random();
2056    let test : TofRunConfig = cfg.pack().unpack().unwrap();
2057    assert_eq!(cfg, test);
2058  }
2059}
2060
2061#[cfg(feature = "random")]
2062#[test]
2063fn pack_tofrbconfig() {
2064  for _ in 0..100 {
2065    let cfg  = TofRBConfig::from_random();
2066    let test : TofRBConfig = cfg.pack().unpack().unwrap();
2067    assert_eq!(cfg, test);
2068  }
2069}
2070