asomeip/
utils_can.rs

1// copyright Matthias Behr, (c) 2022
2//
3// todos:
4//
5use super::utils::decode_frame_payload;
6use crate::utils::buf_as_hex_to_io_write;
7use afibex::fibex::{FibexData, FibexError};
8use lazy_static::lazy_static;
9use std::io::Write;
10
11lazy_static! {
12    static ref NO_CHANNEL_NAME: String = "<no CHANNEL NAME>".to_string();
13}
14/// find the frame_ref and return the channel short_name and the frame_ref
15fn find_frame_ref(
16    fd: &FibexData,
17    channel_id: &Option<&String>,
18    frame_id: u32,
19) -> Option<(String, String)> {
20    if let Some(channel_id) = channel_id {
21        if let Some(channel) = fd.elements.channels.get(*channel_id) {
22            return channel
23                .frame_ref_by_frame_triggering_identifier
24                .get(&frame_id)
25                .map(|frame_id| {
26                    (
27                        channel.short_name.as_ref().unwrap_or(channel_id).to_owned(),
28                        frame_id.to_owned(),
29                    )
30                });
31        }
32    } else {
33        // wildcard search
34        for (channel_name, channel) in &fd.elements.channels {
35            if let Some(frame_ref) = channel
36                .frame_ref_by_frame_triggering_identifier
37                .get(&frame_id)
38            {
39                return Some((
40                    channel
41                        .short_name
42                        .as_ref()
43                        .unwrap_or(channel_name)
44                        .to_owned(),
45                    frame_ref.to_owned(),
46                ));
47            }
48        }
49    }
50    None
51}
52
53/// decode a CAN frame and payload according
54/// into a string that follows the conventions:
55/// - symbol for rx (>), tx(<)
56/// - channel short-name
57/// - (frame-id in hex)
58/// - frame name
59/// - payload (as json parseable string). Line breaks in strings will be replaced by spaces
60///
61/// channel_id can be empty, then the first channel with that frame_id is used
62pub fn decode_can_frame(
63    fd: &FibexData,
64    is_rx: bool,
65    channel_id: &Option<&String>,
66    frame_id: u32,
67    payload: &[u8],
68    decode_payload: bool,
69    decode_compu_methods: bool,
70) -> Result<String, FibexError> {
71    if frame_id == 0 {
72        Err(FibexError::new("can: frame_id 0 is invalid"))
73    } else {
74        let mut writer = Vec::with_capacity(2 * 1024); // todo better heuristics?
75
76        // find frame for channel_id/frame_id
77        if let Some((channel_name, frame_ref)) = find_frame_ref(fd, channel_id, frame_id) {
78            // find that frame:
79            write!(
80                writer,
81                "{} {} 0x{:03x}",
82                if is_rx { '>' } else { '<' },
83                channel_name,
84                frame_id,
85            )
86            .map_err(|e| FibexError { msg: e.to_string() })?;
87            if let Some(frame) = fd.elements.frames_map_by_id.get(&frame_ref) {
88                if let Some(short_name) = &frame.short_name {
89                    write!(writer, " {} ", short_name)
90                        .map_err(|e| FibexError { msg: e.to_string() })?;
91                } else {
92                    write!(writer, " <no short-name id={}> ", frame.id)
93                        .map_err(|e| FibexError { msg: e.to_string() })?;
94                }
95                // write raw payload
96                write!(writer, "[").map_err(|e| FibexError { msg: e.to_string() })?;
97                buf_as_hex_to_io_write(&mut writer, payload)
98                    .map_err(|e| FibexError { msg: e.to_string() })?;
99                write!(writer, "]").map_err(|e| FibexError { msg: e.to_string() })?;
100
101                if decode_payload {
102                    write!(writer, ":").map_err(|e| FibexError { msg: e.to_string() })?;
103                    decode_frame_payload(frame, &mut writer, fd, payload, decode_compu_methods)
104                        .map_err(|e| FibexError { msg: e.to_string() })?;
105                }
106            } else {
107                write!(
108                    writer,
109                    " <frame_ref {} not found!> {:?}",
110                    frame_ref, payload
111                )
112                .map_err(|e| FibexError { msg: e.to_string() })?;
113            }
114        } else {
115            // frame id unknown: output just the payload
116            write!(
117                writer,
118                "{} {} 0x{:03x} <unknown frame> ",
119                if is_rx { '>' } else { '<' },
120                if let Some(channel_id) = channel_id {
121                    let name = fd
122                        .elements
123                        .channels
124                        .get(*channel_id)
125                        .and_then(|c| c.short_name.as_ref())
126                        .unwrap_or(channel_id);
127                    name
128                } else {
129                    &NO_CHANNEL_NAME
130                },
131                frame_id,
132            )
133            .map_err(|e| FibexError { msg: e.to_string() })?;
134            write!(writer, "[").map_err(|e| FibexError { msg: e.to_string() })?;
135            buf_as_hex_to_io_write(&mut writer, payload)
136                .map_err(|e| FibexError { msg: e.to_string() })?;
137            write!(writer, "]").map_err(|e| FibexError { msg: e.to_string() })?;
138        }
139        let res = unsafe { String::from_utf8_unchecked(writer) }; // we do know its proper utf8 strings... (todo ensure for each encoding!)
140        Ok(res)
141    }
142}
143
144#[cfg(test)]
145mod tests {
146    use super::*;
147
148    #[test]
149    fn frame_id_0() {
150        let fd = FibexData::new();
151
152        let r = decode_can_frame(&fd, true, &None, 0, &[], false, false);
153        assert!(r.is_err());
154    }
155
156    // todo create test fibex...
157}