fastpasta/analyze/validators/its/
alpide.rs

1//! This module is the parent module for all ALPIDE data validation.
2//!
3//! It contains some utility functions, and then it publishes modules with more specific ALPIDE related functionality.
4
5pub mod alpide_readout_frame;
6pub mod lane_alpide_frame_analyzer;
7
8use crate::util::*;
9
10// Helper struct to group lanes and bunch counters, used for comparing bunch counters between lanes
11struct ValidatedLane {
12    lane_id: u8,
13    bunch_counter: u8,
14}
15
16/// Process ALPIDE data for a readout frame, per lane.
17///
18/// Returns a tuple of a vector of lane ids with errors, and a vector of error messages.
19pub fn check_alpide_data_frame(
20    alpide_readout_frame: &AlpideReadoutFrame,
21    custom_checks: &'static impl CustomChecksOpt,
22) -> (Vec<u8>, Vec<String>, AlpideStats, Option<Vec<u8>>) {
23    let mut lane_error_msgs: Vec<String> = Vec::new();
24    let mut lane_error_ids: Vec<u8> = Vec::new();
25    let mut validated_lanes: Vec<ValidatedLane> = Vec::new();
26    let mut fatal_lanes: Option<Vec<u8>> = None;
27
28    let frame_from_layer = alpide_readout_frame.from_layer();
29
30    let mut total_alpide_stats = AlpideStats::default();
31
32    alpide_readout_frame
33        .lane_data_frames_as_slice()
34        .iter()
35        .for_each(|lane_data_frame| {
36            // Process data for each lane
37            // New decoder for each lane
38            let mut analyzer = LaneAlpideFrameAnalyzer::new(
39                frame_from_layer,
40                custom_checks.chip_orders_ob(),
41                custom_checks.chip_count_ob(),
42            );
43
44            let lane_number = lane_data_frame.lane_number(frame_from_layer);
45            log::trace!("Processing lane #{lane_number}");
46
47            if let Err(mut error_msgs) = analyzer.analyze_alpide_frame(lane_data_frame) {
48                error_msgs.insert_str(0, &format!("\n\tLane {lane_number} errors: "));
49                lane_error_msgs.push(error_msgs);
50                lane_error_ids.push(lane_number);
51            } else if analyzer.is_fatal_lane() {
52                log::warn!("Lane {lane_number} is in FATAL state, now expecting 1 fewer lane in data frames");
53                if fatal_lanes.is_none() {
54                    fatal_lanes = Some(Vec::new());
55                }
56                fatal_lanes.as_mut().unwrap().push(lane_number);
57            } else {
58                // If the bunch counter is validated for this lane, add it to the list of validated lanes.
59                validated_lanes.push(ValidatedLane {
60                    lane_id: lane_number,
61                    bunch_counter: analyzer
62                        .validated_bc()
63                        .expect("No validated bunch counter in lane readout frame with no errors"),
64                });
65            }
66            total_alpide_stats.sum(*analyzer.alpide_stats()); // Add the just recorded stats to the running stats
67        });
68
69    // Compare all validated bunch counters to each other across lanes
70    validate_lane_bcs(&validated_lanes, &mut lane_error_msgs, &mut lane_error_ids);
71
72    (
73        lane_error_ids,
74        lane_error_msgs,
75        total_alpide_stats,
76        fatal_lanes,
77    )
78}
79
80/// Compare all validated bunch counters to each other across lanes
81fn validate_lane_bcs(
82    validated_lanes: &[ValidatedLane],
83    lane_error_msgs: &mut Vec<String>, // Just to reduce the amount of copying...
84    lane_error_ids: &mut Vec<u8>,      // Just to reduce the amount of copying...
85) {
86    let unique_bunch_counters: Vec<u8> = validated_lanes
87        .iter()
88        .map(|lane| lane.bunch_counter)
89        .collect::<Vec<u8>>()
90        .into_iter()
91        .unique()
92        .collect();
93    if unique_bunch_counters.len() > 1 {
94        let mut error_string = format!(
95            "\n\tLane {:?} error: Mismatching bunch counters between lanes in same readout frame",
96            validated_lanes
97                .iter()
98                .map(|lane| lane.lane_id)
99                .collect::<Vec<u8>>()
100        );
101        // Find the lanes with each bunch counter
102        let mut lanes_to_bunch_counter: Vec<(u8, Vec<u8>)> = Vec::new();
103        // Iterate through each unique bunch counter
104        unique_bunch_counters.iter().for_each(|bunch_counter| {
105            // Collect all lanes with this bunch counter
106            lanes_to_bunch_counter.push((
107                *bunch_counter,
108                validated_lanes
109                    .iter()
110                    .filter(|lane| lane.bunch_counter == *bunch_counter)
111                    .map(|lane| lane.lane_id)
112                    .collect::<Vec<u8>>(),
113            ));
114        });
115        if !Cfg::global().mute_errors() {
116            add_context_to_unique_bc_error_msg(&lanes_to_bunch_counter, &mut error_string);
117        }
118
119        lane_error_msgs.push(error_string);
120        lane_error_ids.extend(lanes_to_bunch_counter.iter().flat_map(|(_, lanes)| lanes));
121    }
122}
123
124fn add_context_to_unique_bc_error_msg(
125    lanes_to_bunch_counter: &[(u8, Vec<u8>)],
126    error_string: &mut String,
127) {
128    // Add the lanes to the error string
129    lanes_to_bunch_counter
130        .iter()
131        .for_each(|(bunch_counter, lanes)| {
132            error_string.push_str(&format!(
133                "\n\t\tBunch counter: {bunch_counter:>3?} | Lanes: {lanes:?}",
134                bunch_counter = bunch_counter,
135                lanes = lanes
136            ));
137        });
138}