lin_ldf/ldf/
ldf_signals.rs

1use crate::ldf::ldf_comment::skip_whitespace;
2use nom::{
3    bytes::complete::{tag, take_until, take_while},
4    IResult,
5};
6
7/// The init_value specifies the signal value that shall be used by all subscriber nodes.
8/// The init_value_scalar is used for scalar signals and the init_value_array is used for byte array signals.
9#[derive(Debug, PartialEq)]
10#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
11pub enum LdfSignalInitValue {
12    Scalar(u8),
13    Array(Vec<u8>),
14}
15
16/// `Signals` section of a LIN Description File (LDF) for LIN 2.1
17/// ```text
18/// Signals {
19///  Signal1: 10, 0, Master, Slave1 ;
20///  Signal2: 10, 0, Master, Slave1 ;
21///  Signal3: 10, 0, Master, Slave1 ;
22///  Signal4: 10, 0, Slave1, Master ;
23///  Signal5: 2, 0, Slave1, Master ;
24///  Signal6: 1, 0, Slave1, Master ;
25/// }
26/// ```
27/// ---
28/// Signal in the `Signals` section of a LIN Description File (LDF) for LIN 2.1
29/// ```text
30/// Signal1: 10, 0, Master, Slave1 ;
31/// ```
32/// Reads as:
33/// ```text
34/// <signal_name>: <signal_size>, <init_value>, <published_by> [, <subscribed_by>] ;
35/// ```
36#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
37pub struct LdfSignal {
38    /// All identifiers must be unique within the LDF file.
39    pub name: String,
40
41    /// The signal_size specifies the size of the signal. It shall be in the range 1 to 16 bits for
42    /// scalar signals and 8, 16, 24, 32, 40, 48, 56 or 64 for byte array signals.
43    pub signal_size: u8,
44
45    /// ```text
46    /// <init_value> ::= <init_value_scalar> | <init_value_array>
47    /// <init_value_scalar> ::= integer
48    /// <init_value_array> ::= {integer ([, integer])}
49    /// ```
50    ///
51    /// The init_value specifies the signal value that shall be used by all subscriber nodes
52    /// until the frame containing the signal is received. The init_value_scalar is used for
53    /// scalar signals and the init_value_array is used for byte array signals. The initial_value for
54    /// byte arrays shall be arranged in big-endian order (i.e. with the most significant byte
55    /// first).
56    ///
57    /// The only way to describe if a signal with size 8 or 16 is a byte array with one or two
58    /// elements or a scalar signal is by analyzing the init_value, i.e. the curly parenthesis are
59    /// very important to distinguish between arrays and scalar values.
60    pub init_value: LdfSignalInitValue,
61
62    /// The published_by specifies the node that is publishing the signal.
63    /// The published_by identifier shall exist in the node identifier set.
64    pub published_by: String,
65
66    /// The subscribed_by specifies the node(s) that is subscribing to the signal.
67    /// The subscribed_by identifiers shall exist in the node identifier set.
68    pub subscribed_by: Vec<String>,
69}
70
71/*
72Signals {
73    Signal1: 10, 0, Master, Slave1 ;
74    Signal2: 10, 0, Master, Slave1 ;
75    Signal3: 10, 0, Slave1, Master ;
76    Signal4: 10, 0, Slave1, Master ;
77    Signal5: 2, 0, Slave1, Master ;
78    Signal6: 1, 0, Slave1, Master ;
79}
80*/
81
82pub fn parse_ldf_signals(s: &str) -> IResult<&str, Vec<LdfSignal>> {
83    // `Signals {` or `Signals{` or ...
84    // - May be any number of spaces before and after the "Signals" tag
85    // - May be any number of spaces before and after the opening curly brace
86    let (s, _) = skip_whitespace(s)?;
87    let (s, _) = tag("Signals")(s)?;
88    let (s, _) = skip_whitespace(s)?;
89    let (s, _) = tag("{")(s)?;
90
91    let mut signals = Vec::new();
92    let mut remaining = s;
93    while !remaining.starts_with('}') {
94        // `Signal1: 10, 0, Master, Slave1 ;` or `Signal1: 10, 0, Master, Slave1;` or ...
95        // - May be any number of spaces before and after the signal name
96        // - May be any number of spaces before and after the colon
97        // - May be any number of spaces before and after the signal size
98        // - May be any number of spaces before and after the comma
99        // - May be any number of spaces before and after the init value
100        // - May be any number of spaces before and after the comma
101        // - May be any number of spaces before and after the published_by node
102        // - May be any number of spaces before and after the comma
103        // - May be any number of spaces before and after the subscribed_by node
104        // - May be any number of spaces before and after the semicolon
105        let (s, _) = skip_whitespace(remaining)?;
106        let (s, signal_name) = take_while(|c: char| c.is_alphanumeric() || c == '_')(s)?;
107        let (s, _) = skip_whitespace(s)?;
108        let (s, _) = tag(":")(s)?;
109        let (s, _) = skip_whitespace(s)?;
110        let (s, signal_size) = take_while(|c: char| c.is_numeric())(s)?;
111        let (s, _) = skip_whitespace(s)?;
112        let (s, _) = tag(",")(s)?;
113        let (s, _) = skip_whitespace(s)?;
114        let (s, init_value) = take_while(|c: char| c.is_numeric())(s)?;
115        let (s, _) = skip_whitespace(s)?;
116        let (s, _) = tag(",")(s)?;
117        let (s, _) = skip_whitespace(s)?;
118        let (s, published_by) = take_while(|c: char| c.is_alphanumeric() || c == '_')(s)?;
119        let (s, _) = skip_whitespace(s)?;
120
121        let (s, symbol) = take_while(|c: char| c == ',' || c == ';')(s)?;
122        let mut subscribed_by = Vec::new();
123        let s = match symbol {
124            "," => {
125                // There is at least one subscribed_by node
126                let (s, subscribed_by_str) = take_until(";")(s)?;
127                subscribed_by = subscribed_by_str.split(',').map(|s| s.trim().to_string()).collect();
128                let (s, _) = tag(";")(s)?;
129                s
130            }
131            ";" => {
132                // There are no subscribed_by nodes
133                s
134            }
135            _ => {
136                // This should not happen, but we can handle it gracefully
137                s
138            }
139        };
140        let (s, _) = skip_whitespace(s)?;
141
142        remaining = s;
143
144        let signal = LdfSignal {
145            name: signal_name.to_string(),
146            signal_size: signal_size.parse().unwrap(),
147            init_value: LdfSignalInitValue::Scalar(init_value.parse().unwrap()),
148            published_by: published_by.to_string(),
149            subscribed_by,
150        };
151
152        signals.push(signal);
153    }
154
155    // `}` or `} ;` or ...
156    // - May be any number of spaces before and after the closing curly brace
157    let (s, _) = skip_whitespace(remaining)?;
158    let (s, _) = tag("}")(s)?;
159
160    Ok((s, signals))
161}
162
163#[cfg(test)]
164mod tests {
165    use super::*;
166
167    #[test]
168    fn test_parse_ldf_signals() {
169        let input = r#"
170            Signals {
171                Signal1: 10, 0, Master, Slave1 , Slave2 ;
172                Signal2: 10, 0, Master, Slave1 ;
173                Signal3: 10, 0, Slave1, Master ;
174                Signal4: 10, 0, Slave1, Master ;
175                Signal5: 2, 0, Slave1, Master ;
176                Signal6: 1, 0, Slave1, Master ;
177            }
178        "#;
179
180        let (_, signals) = parse_ldf_signals(input).unwrap();
181        assert_eq!(signals.len(), 6);
182        assert_eq!(signals[0].name, "Signal1");
183        assert_eq!(signals[0].signal_size, 10);
184        assert_eq!(signals[0].published_by, "Master");
185        assert_eq!(signals[0].subscribed_by, vec!["Slave1", "Slave2"]);
186        assert_eq!(signals[1].name, "Signal2");
187    }
188}