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}