use crate::util::*;
pub struct ItsReadoutFrameValidator<C: CustomChecksOpt + 'static> {
custom_checks_config: &'static C,
alpide_readout_frame: Option<AlpideReadoutFrame>,
is_readout_frame: bool,
from_stave: Option<Stave>,
fatal_lanes: Option<Vec<u8>>,
}
impl<C: CustomChecksOpt> ItsReadoutFrameValidator<C> {
pub fn new(custom_checks: &'static C) -> Self {
Self {
custom_checks_config: custom_checks,
alpide_readout_frame: None,
is_readout_frame: false,
from_stave: None,
fatal_lanes: None,
}
}
pub fn stave(&self) -> Option<&Stave> {
self.from_stave.as_ref()
}
pub fn set_stave(&mut self, from_stave: Stave) {
debug_assert!(self.from_stave.is_none());
self.from_stave = Some(from_stave);
}
pub fn add_fatal_lanes(&mut self, new_fatal_lanes: Vec<u8>) {
if let Some(current_fatal_lanes) = &mut self.fatal_lanes {
current_fatal_lanes.extend(new_fatal_lanes);
} else {
self.fatal_lanes = Some(new_fatal_lanes);
}
}
pub fn fatal_lanes(&self) -> Option<&[u8]> {
self.fatal_lanes.as_deref()
}
pub fn is_in_frame(&self) -> bool {
self.is_readout_frame
}
pub fn new_frame(&mut self, current_mem_pos: u64) {
self.alpide_readout_frame = Some(AlpideReadoutFrame::new(current_mem_pos));
self.is_readout_frame = true;
}
pub fn store_lane_data(&mut self, data_word: &[u8]) {
self.alpide_readout_frame
.as_mut()
.unwrap()
.store_lane_data(data_word, Layer::from_stave(&self.from_stave.unwrap()));
}
pub fn try_close_frame(&mut self, end_mem_pos: u64) -> Result<(), ()> {
self.is_readout_frame = false;
if let Some(frame) = self.alpide_readout_frame.as_mut() {
frame.close_frame(end_mem_pos);
Ok(())
} else {
Err(())
}
}
pub fn process_frame(
&mut self,
err_chan: &flume::Sender<StatType>,
status_words: &StatusWordContainer,
current_rdh: &impl RDH,
) {
let frame = self.alpide_readout_frame.take().unwrap();
debug_assert!(!self.is_readout_frame);
debug_assert!(frame.start_mem_pos() != 0, "Frame start mem pos not set");
let mem_pos_start = frame.start_mem_pos();
let mem_pos_end = frame.end_mem_pos();
if frame.is_empty() {
self.report_empty_alpide_frame_error(&frame, err_chan, status_words, current_rdh);
return;
}
let is_ib = frame.from_layer() == Layer::Inner;
let (lanes_in_error_ids, lane_error_msgs, alpide_stats, fatal_lanes) =
alpide::check_alpide_data_frame(&frame, self.custom_checks_config);
if let Some(new_fatal_lanes) = fatal_lanes {
self.add_fatal_lanes(new_fatal_lanes);
}
if let Err(err_msg) = frame.check_frame_lanes_valid(self.fatal_lanes()) {
let err_code = if is_ib { "E72" } else { "E73" };
let err_msg = format!(
"{mem_pos_start:#X}: [{err_code}] FEE ID:{feeid} ALPIDE data frame ending at {mem_pos_end:#X} {err_msg}. Lanes: {lanes:?}",
feeid=current_rdh.fee_id(),
lanes = frame.lane_data_frames_as_slice().iter().map(|lane|
lane_id_to_lane_number(lane.id(), is_ib)).collect::<Vec<u8>>(),
);
err_chan
.send(StatType::Error(err_msg.into()))
.expect("Failed to send error to stats channel");
}
err_chan
.send(StatType::AlpideStats(alpide_stats))
.expect("Failed to send error to stats channel");
if !lane_error_msgs.is_empty() {
let err_code = if is_ib { "E74" } else { "E75" };
let lane_error_numbers = lanes_in_error_ids
.iter()
.map(|lane_id| lane_id_to_lane_number(*lane_id, is_ib))
.collect_vec();
let mut error_string = format!(
"{mem_pos_start:#X}: [{err_code}] FEE ID:{feeid} ALPIDE data frame ending at {mem_pos_end:#X} has errors in lane {lane_error_numbers:?}:", feeid=current_rdh.fee_id()
);
if !Cfg::global().mute_errors() {
lane_error_msgs.into_iter().for_each(|lane_error_msg| {
error_string.push_str(&lane_error_msg);
});
}
err_chan
.send(StatType::Error(error_string.into()))
.expect("Failed to send error to stats channel");
}
}
fn report_empty_alpide_frame_error(
&self,
frame: &AlpideReadoutFrame,
err_chan: &flume::Sender<StatType>,
status_words: &StatusWordContainer,
current_rdh: &impl RDH,
) {
let (mem_pos_start, mem_pos_end) = (frame.start_mem_pos(), frame.end_mem_pos());
log::warn!("ALPIDE data frame at {mem_pos_start:#X} - {mem_pos_end:#X} is empty",);
let ddw_lane_status_str = if let Some(ddw0) = status_words.ddw() {
format!("Last DDW [{ddw0}] lane status: {:#X}", ddw0.lane_status())
} else {
"No DDW seen yet".to_string()
};
let tdt_lane_status_str = {
let curr_tdt = status_words.tdt().unwrap();
format!("\
Frame closing TDT [{curr_tdt}] lane status: 0:15={lane_0_15:#X} 16:23={lane_16_23:#X} 24:27={lane_24_27:#X}\
",
lane_0_15 = curr_tdt.lane_status_15_0(),
lane_16_23 = curr_tdt.lane_status_23_16(),
lane_24_27 = curr_tdt.lane_status_27_24(),
)
};
let error_string = format!(
"\
{mem_pos_start:#X}: [E701] FEE ID:{feeid} ALPIDE data frame ending at {mem_pos_end:#X} has no data words.\
\n\t Additional information:\
\n\t\t - Lanes in error (as indicated by APEs): {fatal_lanes:?}\
\n\t\t - {ddw_lane_status_str}\
\n\t\t - {tdt_lane_status_str}",
feeid = current_rdh.fee_id(),
fatal_lanes = self.fatal_lanes()
);
err_chan
.send(StatType::Error(error_string.into()))
.expect("Failed to send error to stats channel");
}
}