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