dbc_rs/dbc/core.rs
1#[cfg(feature = "std")]
2use super::ValueDescriptionsMap;
3use crate::{
4 Dbc, ExtendedMultiplexing, MAX_EXTENDED_MULTIPLEXING, Nodes, Version, compat::Vec,
5 dbc::Messages,
6};
7
8impl Dbc {
9 pub(crate) fn new(
10 version: Option<Version>,
11 nodes: Nodes,
12 messages: Messages,
13 #[cfg(feature = "std")] value_descriptions: ValueDescriptionsMap,
14 extended_multiplexing: Vec<ExtendedMultiplexing, { MAX_EXTENDED_MULTIPLEXING }>,
15 ) -> Self {
16 // Validation should have been done prior (by builder)
17 Self {
18 version,
19 nodes,
20 messages,
21 #[cfg(feature = "std")]
22 value_descriptions,
23 extended_multiplexing,
24 }
25 }
26
27 /// Get the version of the DBC file
28 ///
29 /// # Examples
30 ///
31 /// ```rust,no_run
32 /// use dbc_rs::Dbc;
33 ///
34 /// let dbc = Dbc::parse("VERSION \"1.0\"\n\nBU_: ECM\n\nBO_ 256 Engine : 8 ECM")?;
35 /// if let Some(version) = dbc.version() {
36 /// // Version is available
37 /// let _ = version.as_str();
38 /// }
39 /// # Ok::<(), dbc_rs::Error>(())
40 /// ```
41 #[inline]
42 #[must_use = "return value should be used"]
43 pub fn version(&self) -> Option<&Version> {
44 self.version.as_ref()
45 }
46
47 /// Get a reference to the nodes collection
48 ///
49 /// # Examples
50 ///
51 /// ```rust,no_run
52 /// use dbc_rs::Dbc;
53 ///
54 /// let dbc = Dbc::parse("VERSION \"1.0\"\n\nBU_: ECM TCM\n\nBO_ 256 Engine : 8 ECM")?;
55 /// let nodes = dbc.nodes();
56 /// assert_eq!(nodes.len(), 2);
57 /// // Iterate over nodes
58 /// let mut iter = nodes.iter();
59 /// assert_eq!(iter.next(), Some("ECM"));
60 /// assert_eq!(iter.next(), Some("TCM"));
61 /// assert_eq!(iter.next(), None);
62 /// # Ok::<(), dbc_rs::Error>(())
63 /// ```
64 #[inline]
65 #[must_use = "return value should be used"]
66 pub fn nodes(&self) -> &Nodes {
67 &self.nodes
68 }
69
70 /// Get a reference to the messages collection
71 ///
72 /// # Examples
73 ///
74 /// ```rust,no_run
75 /// use dbc_rs::Dbc;
76 ///
77 /// let dbc = Dbc::parse("VERSION \"1.0\"\n\nBU_: ECM\n\nBO_ 256 Engine : 8 ECM")?;
78 /// let messages = dbc.messages();
79 /// assert_eq!(messages.len(), 1);
80 /// let message = messages.at(0).unwrap();
81 /// assert_eq!(message.name(), "Engine");
82 /// assert_eq!(message.id(), 256);
83 /// # Ok::<(), dbc_rs::Error>(())
84 /// ```
85 #[inline]
86 #[must_use = "return value should be used"]
87 pub fn messages(&self) -> &Messages {
88 &self.messages
89 }
90
91 /// Get value descriptions for a specific signal
92 ///
93 /// Value descriptions map numeric signal values to human-readable text.
94 /// Returns `None` if the signal has no value descriptions.
95 ///
96 /// **Global Value Descriptions**: According to the Vector DBC specification,
97 /// a message_id of `-1` (0xFFFFFFFF) in a `VAL_` statement means the value
98 /// descriptions apply to all signals with that name in ANY message. This
99 /// method will first check for a message-specific entry, then fall back to
100 /// a global entry if one exists.
101 ///
102 /// # Examples
103 ///
104 /// ```rust,no_run
105 /// # use dbc_rs::Dbc;
106 /// # let dbc = Dbc::parse(r#"VERSION "1.0"\n\nBU_: ECM\n\nBO_ 100 Engine : 8 ECM\n SG_ Gear : 0|8@1+ (1,0) [0|5] "" *\n\nVAL_ 100 Gear 0 "Park" 1 "Reverse" ;"#)?;
107 /// if let Some(value_descriptions) = dbc.value_descriptions_for_signal(100, "Gear") {
108 /// if let Some(desc) = value_descriptions.get(0) {
109 /// println!("Value 0 means: {}", desc);
110 /// }
111 /// }
112 /// # Ok::<(), dbc_rs::Error>(())
113 /// ```
114 /// Get a reference to the value descriptions list
115 ///
116 /// # Examples
117 ///
118 /// ```rust,no_run
119 /// use dbc_rs::Dbc;
120 ///
121 /// let dbc = Dbc::parse(r#"VERSION "1.0"
122 ///
123 /// BU_: ECM
124 ///
125 /// BO_ 100 Engine : 8 ECM
126 /// SG_ Gear : 0|8@1+ (1,0) [0|5] "" *
127 ///
128 /// VAL_ 100 Gear 0 "Park" 1 "Drive" ;"#)?;
129 /// let value_descriptions_list = dbc.value_descriptions();
130 /// assert_eq!(value_descriptions_list.len(), 1);
131 /// # Ok::<(), dbc_rs::Error>(())
132 /// ```
133 #[cfg(feature = "std")]
134 #[inline]
135 #[must_use = "return value should be used"]
136 pub fn value_descriptions(&self) -> &ValueDescriptionsMap {
137 &self.value_descriptions
138 }
139
140 #[cfg(feature = "std")]
141 #[must_use = "return value should be used"]
142 pub fn value_descriptions_for_signal(
143 &self,
144 message_id: u32,
145 signal_name: &str,
146 ) -> Option<&crate::value_descriptions::ValueDescriptions> {
147 self.value_descriptions.for_signal(message_id, signal_name)
148 }
149
150 /// Get extended multiplexing entries for a specific message
151 ///
152 /// Extended multiplexing (SG_MUL_VAL_) entries define which multiplexer switch values
153 /// activate specific multiplexed signals. This method returns all extended multiplexing
154 /// entries for the given message ID.
155 ///
156 /// # Examples
157 ///
158 /// ```rust,no_run
159 /// use dbc_rs::Dbc;
160 ///
161 /// let dbc = Dbc::parse(r#"VERSION "1.0"
162 ///
163 /// BU_: ECM
164 ///
165 /// BO_ 500 ComplexMux : 8 ECM
166 /// SG_ Mux1 M : 0|8@1+ (1,0) [0|255] ""
167 /// SG_ Signal_A m0 : 16|16@1+ (0.1,0) [0|100] ""
168 ///
169 /// SG_MUL_VAL_ 500 Signal_A Mux1 0-5,10-15 ;
170 /// "#)?;
171 /// let extended = dbc.extended_multiplexing_for_message(500);
172 /// assert_eq!(extended.len(), 1);
173 /// # Ok::<(), dbc_rs::Error>(())
174 /// ```
175 #[must_use = "return value should be used"]
176 pub fn extended_multiplexing_for_message(
177 &self,
178 message_id: u32,
179 ) -> Vec<ExtendedMultiplexing, { MAX_EXTENDED_MULTIPLEXING }> {
180 self.extended_multiplexing
181 .iter()
182 .filter(|ext_mux| ext_mux.message_id() == message_id)
183 .cloned()
184 .collect()
185 }
186}
187
188#[cfg(test)]
189mod tests {
190 use crate::Dbc;
191
192 #[test]
193 fn test_parse_extended_multiplexing() {
194 let dbc = Dbc::parse(
195 r#"VERSION "1.0"
196
197BU_: ECM
198
199BO_ 500 ComplexMux : 8 ECM
200 SG_ Mux1 M : 0|8@1+ (1,0) [0|255] ""
201 SG_ Signal_A m0 : 16|16@1+ (0.1,0) [0|100] "unit" *
202
203SG_MUL_VAL_ 500 Signal_A Mux1 5-10 ;
204"#,
205 )
206 .unwrap();
207
208 let ext_entries = dbc.extended_multiplexing_for_message(500);
209 assert_eq!(
210 ext_entries.len(),
211 1,
212 "Extended multiplexing entry should be parsed"
213 );
214 assert_eq!(ext_entries[0].signal_name(), "Signal_A");
215 assert_eq!(ext_entries[0].multiplexer_switch(), "Mux1");
216 assert_eq!(ext_entries[0].value_ranges(), [(5, 10)]);
217 }
218
219 #[test]
220 fn test_version() {
221 let dbc = Dbc::parse(
222 r#"VERSION "1.0"
223
224BU_: ECM
225
226BO_ 256 Engine : 8 ECM
227"#,
228 )
229 .unwrap();
230 assert_eq!(dbc.version().map(|v| v.as_str()), Some("1.0"));
231 }
232
233 #[test]
234 fn test_nodes() {
235 let dbc = Dbc::parse(
236 r#"VERSION "1.0"
237
238BU_: ECM TCM
239
240BO_ 256 Engine : 8 ECM
241"#,
242 )
243 .unwrap();
244 assert_eq!(dbc.nodes().len(), 2);
245 assert!(dbc.nodes().contains("ECM"));
246 assert!(dbc.nodes().contains("TCM"));
247 }
248
249 #[test]
250 fn test_messages() {
251 let dbc = Dbc::parse(
252 r#"VERSION "1.0"
253
254BU_: ECM
255
256BO_ 256 Engine : 8 ECM
257"#,
258 )
259 .unwrap();
260 assert_eq!(dbc.messages().len(), 1);
261 let message = dbc.messages().at(0).unwrap();
262 assert_eq!(message.name(), "Engine");
263 assert_eq!(message.id(), 256);
264 }
265}