Skip to main content

agent_can/can/
dbc.rs

1use crate::sim::types::{CAN_FLAG_EXTENDED, CAN_FLAG_FD, SimCanFrame};
2use can_dbc::{ByteOrder, Dbc, MessageId, MultiplexIndicator, ValueType};
3use std::collections::{BTreeMap, HashMap};
4use std::path::Path;
5
6#[derive(Debug, Clone)]
7pub struct DecodedMessage {
8    pub name: String,
9    pub extended: bool,
10    pub signals: Vec<DecodedSignal>,
11}
12
13#[derive(Debug, Clone)]
14pub struct DecodedSignal {
15    pub name: String,
16    pub value: f64,
17    pub unit: Option<String>,
18}
19
20#[derive(Debug, Clone)]
21pub struct DbcSignalDef {
22    pub name: String,
23    pub extended: bool,
24    pub message_size: u8,
25    pub start_bit: u64,
26    pub size: u64,
27    pub byte_order: ByteOrder,
28    pub value_type: ValueType,
29    pub factor: f64,
30    pub offset: f64,
31    pub min: f64,
32    pub max: f64,
33    pub unit: Option<String>,
34}
35
36#[derive(Debug, Clone)]
37pub struct DbcMessageDef {
38    pub name: String,
39    pub arb_id: u32,
40    pub extended: bool,
41    pub message_size: u8,
42    pub signals: Vec<DbcSignalDef>,
43}
44
45#[derive(Debug, Clone)]
46pub struct DbcBusOverlay {
47    messages_by_name: HashMap<String, DbcMessageDef>,
48    messages_by_frame_key: HashMap<u32, DbcMessageDef>,
49}
50
51impl DbcBusOverlay {
52    pub fn load(path: &Path) -> Result<Self, String> {
53        let content = std::fs::read_to_string(path)
54            .map_err(|e| format!("failed to read DBC '{}': {e}", path.display()))?;
55        let dbc = Dbc::try_from(content.as_str())
56            .map_err(|e| format!("failed to parse DBC '{}': {e}", path.display()))?;
57        let mut messages_by_name = HashMap::new();
58        let mut messages_by_frame_key = HashMap::new();
59
60        for message in dbc.messages {
61            let (arb_id, extended) = match message.id {
62                MessageId::Standard(id) => (u32::from(id), false),
63                MessageId::Extended(id) => (id, true),
64            };
65            let message_size = u8::try_from(message.size).map_err(|_| {
66                format!(
67                    "DBC message '{}' has invalid size {}; max supported is 255 bytes",
68                    message.name, message.size
69                )
70            })?;
71            if message_size > 64 {
72                return Err(format!(
73                    "DBC message '{}' size {} exceeds CAN FD max payload (64)",
74                    message.name, message_size
75                ));
76            }
77
78            let mut signals = Vec::new();
79            for signal in message.signals {
80                if signal.multiplexer_indicator != MultiplexIndicator::Plain {
81                    return Err(format!(
82                        "DBC signal '{}.{}' uses multiplexing, which is unsupported in this version",
83                        message.name, signal.name
84                    ));
85                }
86                let size = signal.size;
87                if size == 0 || size > 64 {
88                    return Err(format!(
89                        "DBC signal '{}.{}' has invalid size {}",
90                        message.name, signal.name, size
91                    ));
92                }
93                signals.push(DbcSignalDef {
94                    name: signal.name,
95                    extended,
96                    message_size,
97                    start_bit: signal.start_bit,
98                    size,
99                    byte_order: signal.byte_order,
100                    value_type: signal.value_type,
101                    factor: signal.factor,
102                    offset: signal.offset,
103                    min: signal.min,
104                    max: signal.max,
105                    unit: if signal.unit.is_empty() {
106                        None
107                    } else {
108                        Some(signal.unit)
109                    },
110                });
111            }
112
113            let message_def = DbcMessageDef {
114                name: message.name,
115                arb_id,
116                extended,
117                message_size,
118                signals,
119            };
120            let key = frame_key(arb_id, extended);
121            messages_by_name.insert(message_def.name.clone(), message_def.clone());
122            messages_by_frame_key.insert(key, message_def);
123        }
124
125        Ok(Self {
126            messages_by_name,
127            messages_by_frame_key,
128        })
129    }
130
131    pub fn decode_message(&self, frame: &SimCanFrame) -> Result<Option<DecodedMessage>, String> {
132        let key = frame_key_from_frame(frame);
133        let Some(message) = self.messages_by_frame_key.get(&key) else {
134            return Ok(None);
135        };
136        let mut signals = Vec::with_capacity(message.signals.len());
137        for signal in &message.signals {
138            signals.push(DecodedSignal {
139                name: signal.name.clone(),
140                value: decode_signal(frame, signal)?,
141                unit: signal.unit.clone(),
142            });
143        }
144        Ok(Some(DecodedMessage {
145            name: message.name.clone(),
146            extended: message.extended,
147            signals,
148        }))
149    }
150
151    pub fn encode_message(
152        &self,
153        message_name: &str,
154        signal_values: &BTreeMap<String, f64>,
155    ) -> Result<SimCanFrame, String> {
156        let message = self
157            .messages_by_name
158            .get(message_name)
159            .ok_or_else(|| format!("DBC message '{message_name}' not found"))?;
160        let mut frame = SimCanFrame {
161            arb_id: message.arb_id,
162            len: message.message_size,
163            flags: 0,
164            data: [0; 64],
165        };
166        if message.extended {
167            frame.flags |= CAN_FLAG_EXTENDED;
168        }
169        if message.message_size > 8 {
170            frame.flags |= CAN_FLAG_FD;
171        }
172
173        for (signal_name, value) in signal_values {
174            let signal = message
175                .signals
176                .iter()
177                .find(|candidate| candidate.name == *signal_name)
178                .ok_or_else(|| {
179                    format!(
180                        "signal '{signal_name}' is not part of DBC message '{}'",
181                        message.name
182                    )
183                })?;
184            encode_signal(&mut frame, signal, *value)?;
185        }
186        Ok(frame)
187    }
188}
189
190pub fn decode_signal(frame: &SimCanFrame, signal: &DbcSignalDef) -> Result<f64, String> {
191    let raw = extract_raw(frame, signal)?;
192    let numeric = match signal.value_type {
193        ValueType::Signed => signed_from_raw(raw, signal.size) as f64,
194        ValueType::Unsigned => raw as f64,
195    };
196    Ok(numeric * signal.factor + signal.offset)
197}
198
199pub fn encode_signal(
200    frame: &mut SimCanFrame,
201    signal: &DbcSignalDef,
202    value: f64,
203) -> Result<(), String> {
204    if !value.is_finite() {
205        return Err(format!(
206            "invalid value for CAN signal '{}': {value}",
207            signal.name
208        ));
209    }
210    let has_explicit_range = signal.min != 0.0 || signal.max != 0.0;
211    if has_explicit_range && (value < signal.min || value > signal.max) {
212        return Err(format!(
213            "value {value} is out of DBC range [{}, {}] for CAN signal '{}'",
214            signal.min, signal.max, signal.name
215        ));
216    }
217    if signal.factor == 0.0 {
218        return Err(format!(
219            "DBC signal '{}' has zero factor, cannot encode",
220            signal.name
221        ));
222    }
223
224    let raw_float = (value - signal.offset) / signal.factor;
225    let raw_i64 = raw_float.round() as i64;
226    let raw_u64 = match signal.value_type {
227        ValueType::Signed => {
228            let min = -(1_i128 << (signal.size - 1));
229            let max = (1_i128 << (signal.size - 1)) - 1;
230            let val = i128::from(raw_i64);
231            if val < min || val > max {
232                return Err(format!(
233                    "encoded raw value {raw_i64} exceeds signed {}-bit range for signal '{}'",
234                    signal.size, signal.name
235                ));
236            }
237            let mask = if signal.size == 64 {
238                u64::MAX
239            } else {
240                (1_u64 << signal.size) - 1
241            };
242            (raw_i64 as i128 as u64) & mask
243        }
244        ValueType::Unsigned => {
245            if raw_i64 < 0 {
246                return Err(format!(
247                    "encoded raw value {raw_i64} is negative for unsigned signal '{}'",
248                    signal.name
249                ));
250            }
251            let max = if signal.size == 64 {
252                u64::MAX
253            } else {
254                (1_u64 << signal.size) - 1
255            };
256            let raw = raw_i64 as u64;
257            if raw > max {
258                return Err(format!(
259                    "encoded raw value {raw} exceeds unsigned {}-bit range for signal '{}'",
260                    signal.size, signal.name
261                ));
262            }
263            raw
264        }
265    };
266
267    insert_raw(frame, signal, raw_u64)?;
268    if signal.message_size > frame.len {
269        frame.len = signal.message_size;
270    }
271    if frame.len > 8 {
272        frame.flags |= CAN_FLAG_FD;
273    } else {
274        frame.flags &= !CAN_FLAG_FD;
275    }
276    if signal.extended {
277        frame.flags |= CAN_FLAG_EXTENDED;
278    } else {
279        frame.flags &= !CAN_FLAG_EXTENDED;
280    }
281    Ok(())
282}
283
284pub fn frame_key_from_frame(frame: &SimCanFrame) -> u32 {
285    frame_key(frame.arb_id, (frame.flags & CAN_FLAG_EXTENDED) != 0)
286}
287
288fn frame_key(arb_id: u32, extended: bool) -> u32 {
289    if extended { arb_id | (1 << 31) } else { arb_id }
290}
291
292fn extract_raw(frame: &SimCanFrame, signal: &DbcSignalDef) -> Result<u64, String> {
293    if signal.size > 64 {
294        return Err(format!(
295            "signal '{}' size {} exceeds 64 bits",
296            signal.name, signal.size
297        ));
298    }
299    match signal.byte_order {
300        ByteOrder::LittleEndian => {
301            let mut raw = 0_u64;
302            for idx in 0..signal.size {
303                let frame_bit = signal.start_bit + idx;
304                let bit = get_bit(&frame.data, frame_bit)?;
305                raw |= u64::from(bit) << idx;
306            }
307            Ok(raw)
308        }
309        ByteOrder::BigEndian => {
310            let mut raw = 0_u64;
311            let mut bit_pos = signal.start_bit as i64;
312            for _ in 0..signal.size {
313                let bit = get_bit(&frame.data, bit_pos as u64)?;
314                raw = (raw << 1) | u64::from(bit);
315                bit_pos = next_motorola_bit(bit_pos);
316            }
317            Ok(raw)
318        }
319    }
320}
321
322fn insert_raw(frame: &mut SimCanFrame, signal: &DbcSignalDef, raw: u64) -> Result<(), String> {
323    match signal.byte_order {
324        ByteOrder::LittleEndian => {
325            for idx in 0..signal.size {
326                let frame_bit = signal.start_bit + idx;
327                let bit = ((raw >> idx) & 1) as u8;
328                set_bit(&mut frame.data, frame_bit, bit)?;
329            }
330        }
331        ByteOrder::BigEndian => {
332            let mut bit_pos = signal.start_bit as i64;
333            for idx in 0..signal.size {
334                let shift = signal.size - 1 - idx;
335                let bit = ((raw >> shift) & 1) as u8;
336                set_bit(&mut frame.data, bit_pos as u64, bit)?;
337                bit_pos = next_motorola_bit(bit_pos);
338            }
339        }
340    }
341    Ok(())
342}
343
344fn get_bit(data: &[u8; 64], index: u64) -> Result<u8, String> {
345    let byte_index = usize::try_from(index / 8).map_err(|_| "bit index overflow".to_string())?;
346    if byte_index >= data.len() {
347        return Err(format!("bit index {index} is out of bounds"));
348    }
349    let bit_index = (index % 8) as u8;
350    Ok((data[byte_index] >> bit_index) & 1)
351}
352
353fn set_bit(data: &mut [u8; 64], index: u64, bit: u8) -> Result<(), String> {
354    let byte_index = usize::try_from(index / 8).map_err(|_| "bit index overflow".to_string())?;
355    if byte_index >= data.len() {
356        return Err(format!("bit index {index} is out of bounds"));
357    }
358    let mask = 1_u8 << (index % 8);
359    if bit == 0 {
360        data[byte_index] &= !mask;
361    } else {
362        data[byte_index] |= mask;
363    }
364    Ok(())
365}
366
367fn next_motorola_bit(current: i64) -> i64 {
368    if current % 8 == 0 {
369        current + 15
370    } else {
371        current - 1
372    }
373}
374
375fn signed_from_raw(raw: u64, bits: u64) -> i64 {
376    if bits == 0 {
377        return 0;
378    }
379    if bits >= 64 {
380        return raw as i64;
381    }
382    let sign_bit = 1_u64 << (bits - 1);
383    if (raw & sign_bit) == 0 {
384        raw as i64
385    } else {
386        (raw as i64) - ((1_u64 << bits) as i64)
387    }
388}
389
390#[cfg(test)]
391mod tests {
392    use super::{DbcSignalDef, decode_signal, encode_signal};
393    use crate::sim::types::SimCanFrame;
394    use can_dbc::{ByteOrder, ValueType};
395
396    fn sample_signal(min: f64, max: f64) -> DbcSignalDef {
397        DbcSignalDef {
398            name: "EngineRPM".to_string(),
399            extended: false,
400            message_size: 8,
401            start_bit: 0,
402            size: 16,
403            byte_order: ByteOrder::LittleEndian,
404            value_type: ValueType::Unsigned,
405            factor: 0.25,
406            offset: 0.0,
407            min,
408            max,
409            unit: Some("rpm".to_string()),
410        }
411    }
412
413    #[test]
414    fn encode_then_decode_roundtrips() {
415        let signal = sample_signal(0.0, 16000.0);
416        let mut frame = SimCanFrame {
417            arb_id: 0x123,
418            len: 8,
419            flags: 0,
420            data: [0; 64],
421        };
422        encode_signal(&mut frame, &signal, 1200.0).expect("encode should work");
423        let decoded = decode_signal(&frame, &signal).expect("decode should work");
424        assert_eq!(decoded, 1200.0);
425    }
426}