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