1use can_dbc::{
2 AccessNode, AccessType, AttributeDefault, AttributeDefinition, AttributeValue,
3 AttributeValueForDatabase, AttributeValueForEnvVariable, AttributeValueForMessage,
4 AttributeValueForNode, AttributeValueForSignal, AttributeValueType, Baudrate, ByteOrder,
5 Comment, Dbc, EnvType, EnvironmentVariable, EnvironmentVariableData, Message, Node,
6 NumericValue, Signal, Symbol, Transmitter, ValueType, Version,
7};
8use strum::{Display, EnumIter, EnumString, IntoEnumIterator};
9
10#[derive(Debug, Copy, Clone, PartialEq, Eq, EnumIter, EnumString, Display, Hash)]
11pub enum DbcSection {
12 Version,
13 NewSymbols,
14 BitTiming,
15 Nodes,
16 ValueTables,
17 Messages,
18 MessageTransmitters,
19 EnvironmentVariables,
20 EnvironmentVariableData,
21 SignalTypes,
22 Comments,
23 AttributeDefinitions,
24 RelationAttributeDefinitions,
25 AttributeDefaults,
26 RelationAttributeDefaults,
27 RelationAttributeValues,
28 AttributeValuesDatabase,
29 AttributeValuesNode,
30 AttributeValuesMessage,
31 AttributeValuesSignal,
32 AttributeValuesEnv,
33 ValueDescriptions,
34 SignalTypeRefs,
35 SignalGroups,
36 SignalExtendedValueTypeList,
37 ExtendedMultiplex,
38}
39
40pub fn dbc_to_string(dbc: &Dbc) -> String {
41 dbc_sections_to_string(dbc, &Vec::from_iter(DbcSection::iter()))
42}
43
44pub fn dbc_sections_to_string(dbc: &Dbc, sections: &[DbcSection]) -> String {
45 let mut lines = Vec::new();
46
47 for section in sections {
48 let section_text = match section {
49 DbcSection::Version => version_to_string(&dbc.version),
50 DbcSection::NewSymbols => new_symbols_to_string(&dbc.new_symbols),
51 DbcSection::BitTiming => maybe_bit_timings_to_string(dbc.bit_timing.as_deref()),
52 DbcSection::Nodes => nodes_to_string(&dbc.nodes),
53 DbcSection::ValueTables => String::default(),
54 DbcSection::Messages => messages_to_string(&dbc.messages),
55 DbcSection::MessageTransmitters => String::default(),
56 DbcSection::EnvironmentVariables => {
57 environment_variables_to_string(&dbc.environment_variables)
58 }
59 DbcSection::EnvironmentVariableData => {
60 environment_variable_datas_to_string(&dbc.environment_variable_data)
61 }
62 DbcSection::SignalTypes => String::default(),
63 DbcSection::Comments => comments_to_string(&dbc.comments),
64 DbcSection::AttributeDefinitions => {
65 attribute_definitions_to_string(&dbc.attribute_definitions)
66 }
67 DbcSection::RelationAttributeDefinitions => String::default(),
68 DbcSection::AttributeDefaults => attribute_defaults_to_string(&dbc.attribute_defaults),
69 DbcSection::RelationAttributeDefaults => String::default(),
70 DbcSection::RelationAttributeValues => String::default(),
71 DbcSection::AttributeValuesDatabase => {
72 attribute_values_database_to_string(&dbc.attribute_values_database)
73 }
74 DbcSection::AttributeValuesNode => {
75 attribute_values_node_to_string(&dbc.attribute_values_node)
76 }
77 DbcSection::AttributeValuesMessage => {
78 attribute_values_message_to_string(&dbc.attribute_values_message)
79 }
80 DbcSection::AttributeValuesSignal => {
81 attribute_values_signal_to_string(&dbc.attribute_values_signal)
82 }
83 DbcSection::AttributeValuesEnv => {
84 attribute_values_env_to_string(&dbc.attribute_values_env)
85 }
86 DbcSection::ValueDescriptions => String::default(),
87 DbcSection::SignalTypeRefs => String::default(),
88 DbcSection::SignalGroups => String::default(),
89 DbcSection::SignalExtendedValueTypeList => String::default(),
90 DbcSection::ExtendedMultiplex => String::default(),
91 };
92
93 if !section_text.is_empty() {
94 lines.push(format!("{}\n", section_text));
95 }
96 }
97
98 lines.join("\n")
99}
100
101pub fn version_to_string(version: &Version) -> String {
102 format!("VERSION \"{}\"", version.0)
103}
104
105pub fn new_symbols_to_string(symbols: &[Symbol]) -> String {
106 let mut lines = Vec::new();
107
108 lines.push(String::from("NS_:"));
109
110 for symbol in symbols {
111 lines.push(format!("\t{}", symbol.0));
112 }
113
114 lines.join("\n")
115}
116
117pub fn maybe_bit_timings_to_string(maybe_bit_timings: Option<&[Baudrate]>) -> String {
118 if let Some(bit_timings) = maybe_bit_timings {
119 bit_timings_to_string(bit_timings)
120 } else {
121 String::from("BS_:")
122 }
123}
124
125pub fn bit_timings_to_string(bit_timings: &[Baudrate]) -> String {
126 slice_to_string(bit_timings, bit_timing_to_string)
127}
128
129pub fn bit_timing_to_string(bit_timing: &Baudrate) -> String {
130 format!("BS_: {} : 0,0", bit_timing.0)
131}
132
133pub fn nodes_to_string(nodes: &[Node]) -> String {
134 let mut lines = Vec::new();
135
136 lines.push(String::from("BU_:"));
137
138 for node in nodes {
139 lines.push(format!("\t{}", node.0));
140 }
141
142 lines.join("\n")
143}
144
145pub fn messages_to_string(messages: &[Message]) -> String {
146 slice_to_string(messages, |message| format!("{}\n", message_to_string(message)))
147}
148
149pub fn message_to_string(message: &Message) -> String {
150 let mut lines = Vec::new();
151
152 lines.push(format!(
153 "BO_ {} {}: {} {}",
154 message.id.raw(),
155 message.name,
156 message.size,
157 match &message.transmitter {
158 Transmitter::NodeName(node_name) => {
159 node_name.clone()
160 }
161 Transmitter::VectorXXX => {
162 String::from("Vector__XXX")
163 }
164 }
165 ));
166
167 for signal in &message.signals {
168 lines.push(format!("\t{}", signal_to_string(&signal)));
169 }
170
171 lines.join("\n")
172}
173
174pub fn signal_to_string(signal: &Signal) -> String {
175 format!(
176 "SG_ {} : {}|{}@{}{} ({},{}) [{}|{}] \"{}\" {}",
177 signal.name,
178 signal.start_bit,
179 signal.size,
180 match signal.byte_order {
181 ByteOrder::BigEndian => 0,
182 ByteOrder::LittleEndian => 1,
183 },
184 match signal.value_type {
185 ValueType::Signed => "-",
186 ValueType::Unsigned => "+",
187 },
188 signal.factor,
189 signal.offset,
190 signal.min,
191 signal.max,
192 signal.unit,
193 signal.receivers.join(","),
194 )
195}
196
197pub fn comments_to_string(comments: &[Comment]) -> String {
198 slice_to_string(comments, comment_to_string)
199}
200
201pub fn comment_to_string(comment: &Comment) -> String {
202 match comment {
203 Comment::EnvVar { name, comment } => {
204 format!("CM_ EV_ {} \"{}\";", name, comment,)
205 }
206 Comment::Message { id, comment } => {
207 format!("CM_ BO_ {} \"{}\";", id.raw(), comment,)
208 }
209 Comment::Node { name, comment } => {
210 format!("CM_ BU_ {} \"{}\";", name, comment,)
211 }
212 Comment::Plain { comment } => {
213 format!("CM_ \"{}\";", comment,)
214 }
215 Comment::Signal {
216 message_id,
217 name,
218 comment,
219 } => {
220 format!("CM_ SG_ {} {} \"{}\";", message_id.raw(), name, comment,)
221 }
222 }
223}
224
225pub fn environment_variables_to_string(environment_variables: &[EnvironmentVariable]) -> String {
226 slice_to_string(environment_variables, environment_variable_to_string)
227}
228
229pub fn environment_variable_to_string(environment_variable: &EnvironmentVariable) -> String {
230 format!(
231 "EV_ {} : {} [{}|{}] \"{}\" {} {} {} {};",
232 environment_variable.name,
233 match environment_variable.typ {
234 EnvType::Integer => 0,
235 EnvType::Float => 1,
236 EnvType::String => 2,
237 },
238 environment_variable.min,
239 environment_variable.max,
240 environment_variable.unit,
241 environment_variable.initial_value,
242 environment_variable.ev_id,
243 match environment_variable.access_type {
244 AccessType::DummyNodeVector0 => "DUMMY_NODE_VECTOR0",
245 AccessType::DummyNodeVector1 => "DUMMY_NODE_VECTOR1",
246 AccessType::DummyNodeVector2 => "DUMMY_NODE_VECTOR2",
247 AccessType::DummyNodeVector3 => "DUMMY_NODE_VECTOR3",
248 },
249 environment_variable
250 .access_nodes
251 .iter()
252 .map(|access_node| match access_node {
253 AccessNode::Name(name) => name.clone(),
254 AccessNode::VectorXXX => String::from("VECTOR__XXX"),
255 })
256 .collect::<Vec<_>>()
257 .join(", ")
258 )
259}
260
261pub fn environment_variable_datas_to_string(
262 environment_variable_datas: &[EnvironmentVariableData],
263) -> String {
264 slice_to_string(
265 environment_variable_datas,
266 environment_variable_data_to_string,
267 )
268}
269
270pub fn environment_variable_data_to_string(
271 environment_variable_data: &EnvironmentVariableData,
272) -> String {
273 format!(
274 "ENVVAR_DATA_ {} : {};",
275 environment_variable_data.env_var_name, environment_variable_data.data_size
276 )
277}
278
279pub fn attribute_definitions_to_string(attribute_definitions: &[AttributeDefinition]) -> String {
280 slice_to_string(attribute_definitions, attribute_definition_to_string)
281}
282
283pub fn attribute_definition_to_string(attribute_definition: &AttributeDefinition) -> String {
284 match attribute_definition {
285 AttributeDefinition::EnvironmentVariable(name, attribute_value_type) => {
286 format!(
287 "BA_DEF_ EV_ \"{}\" {};",
288 name,
289 attribute_value_type_to_string(attribute_value_type)
290 )
291 }
292 AttributeDefinition::Message(name, attribute_value_type) => {
293 format!(
294 "BA_DEF_ BO_ \"{}\" {};",
295 name,
296 attribute_value_type_to_string(attribute_value_type)
297 )
298 }
299 AttributeDefinition::Node(name, attribute_value_type) => {
300 format!(
301 "BA_DEF_ BU_ \"{}\" {};",
302 name,
303 attribute_value_type_to_string(attribute_value_type)
304 )
305 }
306 AttributeDefinition::Plain(name, attribute_value_type) => {
307 format!(
308 "BA_DEF_ EV_ \"{}\" {};",
309 name,
310 attribute_value_type_to_string(attribute_value_type)
311 )
312 }
313 AttributeDefinition::Signal(name, attribute_value_type) => {
314 format!(
315 "BA_DEF_ SG_ \"{}\" {};",
316 name,
317 attribute_value_type_to_string(attribute_value_type)
318 )
319 }
320 }
321}
322
323pub fn attribute_defaults_to_string(attribute_defaults: &[AttributeDefault]) -> String {
324 slice_to_string(attribute_defaults, attribute_default_to_string)
325}
326
327pub fn attribute_default_to_string(attribute_default: &AttributeDefault) -> String {
328 format!(
329 "BA_DEF_DEF_ \"{}\" {};",
330 attribute_default.name,
331 attribute_value_to_string(&attribute_default.value)
332 )
333}
334
335pub fn attribute_value_type_to_string(attribute_value_type: &AttributeValueType) -> String {
336 match attribute_value_type {
337 AttributeValueType::Enum(values) => {
338 format!(
339 "ENUM {}",
340 values
341 .iter()
342 .map(|value| format!("\"{}\"", value))
343 .collect::<Vec<_>>()
344 .join(", ")
345 )
346 }
347 AttributeValueType::Float(min, max) => {
348 format!(
349 "FLOAT {} {}",
350 numeric_value_to_string(min),
351 numeric_value_to_string(max)
352 )
353 }
354 AttributeValueType::Hex(min, max) => {
355 format!(
356 "HEX {} {}",
357 numeric_value_to_string(min),
358 numeric_value_to_string(max)
359 )
360 }
361 AttributeValueType::Int(min, max) => {
362 format!(
363 "INT {} {}",
364 numeric_value_to_string(min),
365 numeric_value_to_string(max)
366 )
367 }
368 AttributeValueType::String => String::from("STRING"),
369 }
370}
371
372pub fn attribute_values_database_to_string(
373 attribute_values: &[AttributeValueForDatabase],
374) -> String {
375 slice_to_string(attribute_values, attribute_value_database_to_string)
376}
377
378pub fn attribute_value_database_to_string(
379 attribute_value_database: &AttributeValueForDatabase,
380) -> String {
381 format!(
382 "BA_ \"{}\" {};",
383 attribute_value_database.name,
384 attribute_value_to_string(&attribute_value_database.value)
385 )
386}
387
388pub fn attribute_values_env_to_string(attribute_values: &[AttributeValueForEnvVariable]) -> String {
389 slice_to_string(attribute_values, attribute_value_env_to_string)
390}
391
392pub fn attribute_value_env_to_string(attribute_value_env: &AttributeValueForEnvVariable) -> String {
393 format!(
394 "BA_ \"{}\" EV_ {} {};",
395 attribute_value_env.name,
396 attribute_value_env.variable_name,
397 attribute_value_to_string(&attribute_value_env.value)
398 )
399}
400
401pub fn attribute_values_message_to_string(attribute_values: &[AttributeValueForMessage]) -> String {
402 slice_to_string(attribute_values, attribute_value_message_to_string)
403}
404
405pub fn attribute_value_message_to_string(
406 attribute_value_message: &AttributeValueForMessage,
407) -> String {
408 format!(
409 "BA_ \"{}\" BO_ {} {};",
410 attribute_value_message.name,
411 attribute_value_message.message_id.raw(),
412 attribute_value_to_string(&attribute_value_message.value)
413 )
414}
415
416pub fn attribute_values_node_to_string(attribute_values: &[AttributeValueForNode]) -> String {
417 slice_to_string(attribute_values, attribute_value_node_to_string)
418}
419
420pub fn attribute_value_node_to_string(attribute_value_node: &AttributeValueForNode) -> String {
421 format!(
422 "BA_ \"{}\" BU_ {} {};",
423 attribute_value_node.name,
424 attribute_value_node.node_name,
425 attribute_value_to_string(&attribute_value_node.value)
426 )
427}
428
429pub fn attribute_values_signal_to_string(attribute_values: &[AttributeValueForSignal]) -> String {
430 slice_to_string(attribute_values, attribute_value_signal_to_string)
431}
432
433pub fn attribute_value_signal_to_string(
434 attribute_value_signal: &AttributeValueForSignal,
435) -> String {
436 format!(
437 "BA_ \"{}\" SG_ {} {} {};",
438 attribute_value_signal.name,
439 attribute_value_signal.message_id.raw(),
440 attribute_value_signal.signal_name,
441 attribute_value_to_string(&attribute_value_signal.value)
442 )
443}
444
445pub fn attribute_value_to_string(attribute_value: &AttributeValue) -> String {
446 match attribute_value {
447 AttributeValue::Double(value) => numeric_value_to_string(&NumericValue::Double(*value)),
448 AttributeValue::Int(value) => numeric_value_to_string(&NumericValue::Int(*value)),
449 AttributeValue::String(value) => format!("\"{}\"", value),
450 AttributeValue::Uint(value) => numeric_value_to_string(&NumericValue::Uint(*value)),
451 }
452}
453
454pub fn numeric_value_to_string(numeric_value: &NumericValue) -> String {
455 match numeric_value {
456 NumericValue::Double(value) => value.to_string(),
457 NumericValue::Int(value) => value.to_string(),
458 NumericValue::Uint(value) => value.to_string(),
459 }
460}
461
462pub fn slice_to_string<T, F>(slice: &[T], map: F) -> String
463where
464 F: Fn(&T) -> String,
465{
466 slice.iter().map(map).collect::<Vec<String>>().join("\n")
467}
468
469#[cfg(test)]
470mod tests {
471 use can_dbc::{MessageId, MultiplexIndicator, Transmitter};
472
473 use super::*;
474
475 #[test]
476 fn test_comments() {
477 assert_eq!(
478 comment_to_string(&Comment::EnvVar {
479 name: String::from("name"),
480 comment: String::from("comment"),
481 }),
482 r#"CM_ EV_ name "comment";"#
483 );
484
485 assert_eq!(
486 comment_to_string(&Comment::Node {
487 name: String::from("name"),
488 comment: String::from("comment"),
489 }),
490 r#"CM_ BU_ name "comment";"#
491 );
492
493 assert_eq!(
494 comment_to_string(&Comment::Message {
495 id: MessageId::Extended(0),
496 comment: String::from("comment"),
497 }),
498 r#"CM_ BO_ 2147483648 "comment";"#
499 );
500
501 assert_eq!(
502 comment_to_string(&Comment::Plain {
503 comment: String::from("comment"),
504 }),
505 r#"CM_ "comment";"#
506 );
507
508 assert_eq!(
509 comment_to_string(&Comment::Signal {
510 message_id: MessageId::Extended(0),
511 name: String::from("name"),
512 comment: String::from("comment"),
513 }),
514 r#"CM_ SG_ 2147483648 name "comment";"#
515 );
516 }
517
518 #[test]
519 pub fn test_signal() {
520 assert_eq!(
521 signal_to_string(&Signal {
522 name: String::from("name"),
523 multiplexer_indicator: MultiplexIndicator::Plain,
524 start_bit: 0,
525 size: 1,
526 byte_order: ByteOrder::BigEndian,
527 value_type: ValueType::Unsigned,
528 factor: 1.5,
529 offset: 2.5,
530 min: -10.0,
531 max: 10.0,
532 unit: String::from("unit"),
533 receivers: vec![String::from("Vector__XXX")]
534 }),
535 r#"SG_ name : 0|1@0+ (1.5,2.5) [-10|10] "unit" Vector__XXX"#
536 );
537 }
538
539 #[test]
540 pub fn test_message() {
541 assert_eq!(
542 message_to_string(&Message {
543 id: MessageId::Extended(0),
544 name: String::from("name"),
545 size: 0,
546 transmitter: Transmitter::VectorXXX,
547 signals: Vec::default()
548 }),
549 r#"BO_ 2147483648 name: 0 Vector__XXX"#
550 );
551 }
552
553 #[test]
554 pub fn test_nodes() {
555 assert_eq!(
556 nodes_to_string(&[Node(String::from("name"))]),
557 r#"BU_:
558 name"#
559 );
560 }
561}