tramex_tools/interface/
onelog.rs

1//! This module contains the definition of the OneLog struct.
2
3use std::str::FromStr;
4
5use crate::data::{AdditionalInfos, Trace};
6use crate::errors::TramexError;
7use crate::interface::association::TraceRelation;
8use crate::interface::functions::extract_hexe;
9use crate::interface::parser::hex_extractor::extract_binary_from_lines;
10
11use crate::interface::{layer::Layer, types::SourceLog};
12use crate::tramex_error;
13
14use super::parser::parser_basic::BasicParser;
15use super::parser::parser_nas::NASInfos;
16use super::parser::parser_rrc::RRCInfos;
17use super::types::Direction; // to use the FileParser trait and implementations
18
19#[derive(serde::Deserialize, Debug)]
20/// Data structure to store the log.
21pub struct OneLog {
22    /// Each item is a string representing a line of log.
23    pub data: Vec<String>,
24
25    /// Milliseconds since January 1st 1970.
26    pub timestamp: i64,
27
28    /// log layer
29    pub layer: Layer,
30
31    /// Source of the log.
32    pub src: SourceLog,
33
34    /// index
35    pub idx: u64,
36
37    /// index
38    pub dir: Option<String>,
39}
40
41impl OneLog {
42    /// Extract the hexadecimal representation of the log.
43    /// # Errors
44    /// Returns a TramexError if the hexe representation could not be extracted.
45    pub fn extract_hexe(&self) -> Result<Vec<u8>, TramexError> {
46        extract_hexe(&self.data)
47    }
48
49    /// Extract the canal message of the log.
50    pub fn extract_canal_msg(&self) -> Option<String> {
51        // TODO implement this function correctly
52        if let Some(data_line) = self.data.first() {
53            log::debug!("{data_line:?}");
54            return Some(data_line.to_owned());
55        }
56        None
57    }
58
59    /// Extract the data of the log.
60    /// # Errors
61    /// Returns a TramexError if the data could not be extracted.
62    pub fn extract_data(&self) -> Result<Trace, TramexError> {
63        match self.layer {
64            Layer::RRC => {
65                // log::debug!("self: {:?}", self);
66                let dir = match &self.dir {
67                    Some(opt_dir) => match Direction::from_str(opt_dir) {
68                        Ok(d) => d,
69                        Err(_) => {
70                            log::debug!("Direction: {:?}", self.dir);
71                            return Err(tramex_error!(
72                                format!("Can't format direction {}", opt_dir),
73                                crate::errors::ErrorCode::WebSocketErrorDecodingMessage
74                            ));
75                        }
76                    },
77                    None => {
78                        return Err(tramex_error!(
79                            "Direction not found".to_owned(),
80                            crate::errors::ErrorCode::WebSocketErrorDecodingMessage
81                        ));
82                    }
83                };
84                let firs_line = self.data[0].split(':').collect::<Vec<&str>>();
85                if firs_line.len() < 2 {
86                    return Err(tramex_error!(
87                        format!("Invalid first line {}", self.data[0]),
88                        crate::errors::ErrorCode::WebSocketErrorDecodingMessage
89                    ));
90                }
91                let rrc: RRCInfos = RRCInfos {
92                    direction: dir,
93                    canal: firs_line[0].to_owned(),
94                    canal_msg: firs_line[1][1..].to_owned(),
95                };
96                let infos = AdditionalInfos::RRCInfos(rrc);
97                let text_lines: Vec<String> = self.data[1..].iter().map(|x| x.to_string()).collect();
98                let binary = extract_binary_from_lines(&self.data);
99                let trace = Trace {
100                    timestamp: self.timestamp,
101                    layer: Layer::RRC,
102                    additional_infos: infos,
103                    text: Some(text_lines),
104                    binary,
105                    relation: TraceRelation::default(),
106                };
107                Ok(trace)
108            }
109            Layer::NAS => {
110                let dir = match &self.dir {
111                    Some(opt_dir) => match Direction::from_str(opt_dir) {
112                        Ok(d) => d,
113                        Err(_) => {
114                            return Err(tramex_error!(
115                                format!("Can't format direction {}", opt_dir),
116                                crate::errors::ErrorCode::WebSocketErrorDecodingMessage
117                            ));
118                        }
119                    },
120                    None => {
121                        return Err(tramex_error!(
122                            "Direction not found".to_owned(),
123                            crate::errors::ErrorCode::WebSocketErrorDecodingMessage
124                        ));
125                    }
126                };
127
128                // First line contains the message type
129                // Example: "5GMM: Service request" or just "Service request"
130                let message_type = if self.data.is_empty() {
131                    "Unknown".to_string()
132                } else {
133                    // Remove protocol prefix if present (e.g., "5GMM: ")
134                    let first_line = &self.data[0];
135                    if let Some(colon_pos) = first_line.find(':') {
136                        first_line[colon_pos + 1..].trim().to_string()
137                    } else {
138                        first_line.trim().to_string()
139                    }
140                };
141
142                let nas = NASInfos {
143                    direction: dir,
144                    message_type,
145                };
146                let infos = AdditionalInfos::NASInfos(nas);
147                let text_lines: Vec<String> = self.data[1..].iter().map(|x| x.to_string()).collect();
148                let binary = extract_binary_from_lines(&self.data);
149                let trace = Trace {
150                    timestamp: self.timestamp,
151                    layer: Layer::NAS,
152                    additional_infos: infos,
153                    text: Some(text_lines),
154                    binary,
155                    relation: TraceRelation::default(),
156                };
157                Ok(trace)
158            }
159            _ => {
160                // Use BasicParser for all other layers (PHY, RLC, MAC, PDCP, SDAP, etc.)
161                let mut trace = BasicParser::parse_with_layer(&self.data, self.layer.clone())
162                    .map_err(|e| tramex_error!(e.message, crate::errors::ErrorCode::ParsingLayerNotImplemented))?;
163                // Set the timestamp from the WebSocket log
164                trace.timestamp = self.timestamp;
165                Ok(trace)
166            }
167        }
168    }
169}