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}