dbc_gen/
lib.rs

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(EnumIter, EnumString, Display)]
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}