1use crate::asn1_parser::parse_asn1_to_json;
3use crate::interface::association::{AssociationRules, AssociationStatus, TraceMatcher, TraceRelation};
4use crate::interface::{
5 layer::Layer,
6 parse_config::FileMetadata,
7 parser::{
8 parser_gtpu::GTPUInfos, parser_nas::NASInfos, parser_ngap::NGAPInfos, parser_phy::PHYInfos, parser_rrc::RRCInfos,
9 },
10 types::Direction,
11};
12use core::fmt::Debug;
13
14#[derive(Debug)]
15pub struct Data {
17 pub events: Vec<Trace>,
19 pub current_index: usize,
21 pub metadata: FileMetadata,
23}
24
25impl Data {
26 pub fn get_current_trace(&self) -> Option<&Trace> {
28 self.events.get(self.current_index)
29 }
30
31 pub fn is_different_index(&self, index: usize) -> bool {
33 if index == 0 {
34 return true;
35 }
36 self.current_index != index
37 }
38
39 pub fn clear(&mut self) {
41 self.events.clear();
42 self.current_index = 0;
43 self.metadata = FileMetadata::default();
44 }
45
46 pub fn compute_parent(&mut self, index: usize, rules: &AssociationRules) -> Option<usize> {
56 if let Some(trace) = self.events.get(index)
58 && trace.relation.parent.is_computed()
59 {
60 return trace.relation.get_parent_index();
61 }
62
63 let layer = match self.events.get(index) {
65 Some(t) => t.layer.clone(),
66 None => return None,
67 };
68
69 let applicable_rules = rules.rules_for_layer(&layer);
71 for rule in applicable_rules {
72 let status = TraceMatcher::find_relative(index, &self.events, rule, &layer, &rule.target_layer());
73
74 if let AssociationStatus::Found(parent_indices) = status {
77 if let Some(trace) = self.events.get_mut(index) {
79 for &parent_idx in &parent_indices {
80 trace.relation.add_parent(parent_idx);
81 }
82 }
83 for &parent_idx in &parent_indices {
85 if let Some(parent_trace) = self.events.get_mut(parent_idx) {
86 parent_trace.relation.add_child(index);
87 }
88 }
89 return parent_indices.first().copied();
91 }
92 }
93
94 if let Some(trace) = self.events.get_mut(index)
96 && !trace.relation.parent.is_computed()
97 {
98 trace.relation.set_parent_not_applicable();
99 }
100
101 None
102 }
103
104 pub fn compute_all_associations(&mut self, rules: &AssociationRules) {
112 crate::interface::association::compute_associations(&mut self.events, rules, 0);
113 }
114
115 pub fn get_parent_trace(&mut self, index: usize, rules: &AssociationRules) -> Option<&Trace> {
125 let parent_idx = self.compute_parent(index, rules)?;
126 self.events.get(parent_idx)
127 }
128
129 pub fn get_child_trace(&self, index: usize) -> Option<&Trace> {
138 let trace = self.events.get(index)?;
139 let child_idx = trace.relation.get_child_index()?;
140 self.events.get(child_idx)
141 }
142
143 pub fn invalidate_associations(&mut self) {
145 for trace in &mut self.events {
146 trace.relation.invalidate();
147 }
148 }
149
150 pub fn invalidate_associations_in_range(&mut self, start_index: usize, window: usize) {
157 let start = start_index.saturating_sub(window);
158 let end = (start_index + window).min(self.events.len());
159
160 for trace in &mut self.events[start..end] {
161 trace.relation.invalidate();
162 }
163 }
164}
165
166impl Default for Data {
167 fn default() -> Self {
168 let default_data_size = 2048;
169 Self {
170 events: Vec::with_capacity(default_data_size),
171 current_index: 0,
172 metadata: FileMetadata::default(),
173 }
174 }
175}
176
177#[derive(Debug, Clone)]
178pub struct Trace {
180 pub timestamp: i64,
182
183 pub layer: Layer,
185
186 pub additional_infos: AdditionalInfos,
188
189 pub text: Option<Vec<String>>,
191
192 pub binary: Option<Vec<u8>>,
194
195 pub relation: TraceRelation,
197}
198
199impl Trace {
200 pub fn parse_asn1_to_json(&self) -> Option<serde_json::Value> {
209 if !matches!(self.layer, Layer::RRC) {
211 return None;
212 }
213
214 let text = self.text.as_ref()?;
216
217 let asn1_lines: Vec<&String> = text
220 .iter()
221 .skip_while(|line| {
222 let trimmed = line.trim();
223 !trimmed.starts_with('{')
225 })
226 .collect();
227
228 if asn1_lines.is_empty() {
229 log::debug!("No ASN.1 structure found in text");
230 return None;
231 }
232
233 let asn1_text: String = asn1_lines.iter().map(|s| s.as_str()).collect::<Vec<&str>>().join("\n");
235
236 match parse_asn1_to_json(&asn1_text) {
238 Ok(json) => {
239 Some(json)
241 }
242 Err(e) => {
243 log::warn!("Failed to parse ASN.1: {}", e);
244 None
245 }
246 }
247 }
248}
249
250#[derive(Debug, Clone)]
252pub enum AdditionalInfos {
253 RRCInfos(RRCInfos),
255 NASInfos(NASInfos),
257 NGAPInfos(NGAPInfos),
259 GTPUInfos(GTPUInfos),
261 PHYInfos(PHYInfos),
263 None,
265}
266
267impl AdditionalInfos {
268 pub fn get_direction(&self) -> Option<Direction> {
270 match self {
271 AdditionalInfos::RRCInfos(info) => Some(info.direction.clone()),
272 AdditionalInfos::NASInfos(info) => Some(info.direction.clone()),
273 AdditionalInfos::NGAPInfos(info) => Some(info.direction.clone()),
274 AdditionalInfos::GTPUInfos(info) => Some(info.direction.clone()),
275 AdditionalInfos::PHYInfos(info) => Some(info.direction.clone()),
276 AdditionalInfos::None => None,
277 }
278 }
279
280 pub fn get_message_name(&self) -> Option<String> {
282 match self {
283 AdditionalInfos::RRCInfos(info) => Some(info.canal_msg.clone()),
284 AdditionalInfos::NASInfos(info) => Some(info.message_type.clone()),
285 AdditionalInfos::NGAPInfos(info) => Some(info.message_type.clone()),
286 AdditionalInfos::GTPUInfos(info) => Some(info.message_type.clone()),
287 AdditionalInfos::PHYInfos(info) => Some(format!("{:?}", info.channel_type)),
288 AdditionalInfos::None => None,
289 }
290 }
291}
292
293#[cfg(test)]
294mod tests {
295 use super::*;
296 use crate::interface::parser::parser_nas::NASInfos;
297 use crate::interface::parser::parser_ngap::NGAPInfos;
298 use crate::interface::parser::parser_rrc::RRCInfos;
299
300 #[test]
301 fn test_additional_infos_get_direction() {
302 let rrc_infos = RRCInfos {
304 direction: Direction::DL,
305 canal: "BCCH".to_string(),
306 canal_msg: "SIB".to_string(),
307 };
308 let rrc_additional = AdditionalInfos::RRCInfos(rrc_infos);
309 assert_eq!(rrc_additional.get_direction(), Some(Direction::DL));
310
311 let nas_infos = NASInfos {
313 direction: Direction::UL,
314 message_type: "Registration request".to_string(),
315 };
316 let nas_additional = AdditionalInfos::NASInfos(nas_infos);
317 assert_eq!(nas_additional.get_direction(), Some(Direction::UL));
318
319 let ngap_infos = NGAPInfos {
321 direction: Direction::DL,
322 message_type: "Downlink NAS transport".to_string(),
323 connection_info: Some("127.0.1.100:38412".to_string()),
324 };
325 let ngap_additional = AdditionalInfos::NGAPInfos(ngap_infos);
326 assert_eq!(ngap_additional.get_direction(), Some(Direction::DL));
327
328 let none_additional = AdditionalInfos::None;
330 assert_eq!(none_additional.get_direction(), None);
331 }
332
333 #[test]
334 fn test_additional_infos_get_message_name() {
335 let rrc_infos = RRCInfos {
337 direction: Direction::DL,
338 canal: "BCCH".to_string(),
339 canal_msg: "SIB".to_string(),
340 };
341 let rrc_additional = AdditionalInfos::RRCInfos(rrc_infos);
342 assert_eq!(rrc_additional.get_message_name(), Some("SIB".to_string()));
343
344 let nas_infos = NASInfos {
346 direction: Direction::UL,
347 message_type: "Registration request".to_string(),
348 };
349 let nas_additional = AdditionalInfos::NASInfos(nas_infos);
350 assert_eq!(nas_additional.get_message_name(), Some("Registration request".to_string()));
351
352 let ngap_infos = NGAPInfos {
354 direction: Direction::DL,
355 message_type: "Downlink NAS transport".to_string(),
356 connection_info: Some("127.0.1.100:38412".to_string()),
357 };
358 let ngap_additional = AdditionalInfos::NGAPInfos(ngap_infos);
359 assert_eq!(ngap_additional.get_message_name(), Some("Downlink NAS transport".to_string()));
360
361 let none_additional = AdditionalInfos::None;
363 assert_eq!(none_additional.get_message_name(), None);
364 }
365}