lin_ldf/ldf/
ldf_signals.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
use crate::ldf::ldf_comment::skip_whitespace;
use nom::{
    bytes::complete::{tag, take_until, take_while},
    IResult,
};

/// The init_value specifies the signal value that shall be used by all subscriber nodes.
/// The init_value_scalar is used for scalar signals and the init_value_array is used for byte array signals.
#[derive(Debug, PartialEq)]
pub enum LdfSignalInitValue {
    Scalar(u8),
    Array(Vec<u8>),
}

/// `Signals` section of a LIN Description File (LDF) for LIN 2.1
/// ```text
/// Signals {
///  Signal1: 10, 0, Master, Slave1 ;
///  Signal2: 10, 0, Master, Slave1 ;
///  Signal3: 10, 0, Master, Slave1 ;
///  Signal4: 10, 0, Slave1, Master ;
///  Signal5: 2, 0, Slave1, Master ;
///  Signal6: 1, 0, Slave1, Master ;
/// }
/// ```
/// ---
/// Signal in the `Signals` section of a LIN Description File (LDF) for LIN 2.1
/// ```text
/// Signal1: 10, 0, Master, Slave1 ;
/// ```
/// Reads as:
/// ```text
/// <signal_name>: <signal_size>, <init_value>, <published_by> [, <subscribed_by>] ;
/// ```
pub struct LdfSignal {
    /// All identifiers must be unique within the LDF file.
    pub name: String,

    /// The signal_size specifies the size of the signal. It shall be in the range 1 to 16 bits for
    /// scalar signals and 8, 16, 24, 32, 40, 48, 56 or 64 for byte array signals.
    pub signal_size: u8,

    /// ```text
    /// <init_value> ::= <init_value_scalar> | <init_value_array>
    /// <init_value_scalar> ::= integer
    /// <init_value_array> ::= {integer ([, integer])}
    /// ```
    ///
    /// The init_value specifies the signal value that shall be used by all subscriber nodes
    /// until the frame containing the signal is received. The init_value_scalar is used for
    /// scalar signals and the init_value_array is used for byte array signals. The initial_value for
    /// byte arrays shall be arranged in big-endian order (i.e. with the most significant byte
    /// first).
    ///
    /// The only way to describe if a signal with size 8 or 16 is a byte array with one or two
    /// elements or a scalar signal is by analyzing the init_value, i.e. the curly parenthesis are
    /// very important to distinguish between arrays and scalar values.
    pub init_value: LdfSignalInitValue,

    /// The published_by specifies the node that is publishing the signal.
    /// The published_by identifier shall exist in the node identifier set.
    pub published_by: String,

    /// The subscribed_by specifies the node(s) that is subscribing to the signal.
    /// The subscribed_by identifiers shall exist in the node identifier set.
    pub subscribed_by: Vec<String>,
}

/*
Signals {
    Signal1: 10, 0, Master, Slave1 ;
    Signal2: 10, 0, Master, Slave1 ;
    Signal3: 10, 0, Slave1, Master ;
    Signal4: 10, 0, Slave1, Master ;
    Signal5: 2, 0, Slave1, Master ;
    Signal6: 1, 0, Slave1, Master ;
}
*/

pub fn parse_ldf_signals(s: &str) -> IResult<&str, Vec<LdfSignal>> {
    // `Signals {` or `Signals{` or ...
    // - May be any number of spaces before and after the "Signals" tag
    // - May be any number of spaces before and after the opening curly brace
    let (s, _) = skip_whitespace(s)?;
    let (s, _) = tag("Signals")(s)?;
    let (s, _) = skip_whitespace(s)?;
    let (s, _) = tag("{")(s)?;

    let mut signals = Vec::new();
    let mut remaining = s;
    while !remaining.starts_with('}') {
        // `Signal1: 10, 0, Master, Slave1 ;` or `Signal1: 10, 0, Master, Slave1;` or ...
        // - May be any number of spaces before and after the signal name
        // - May be any number of spaces before and after the colon
        // - May be any number of spaces before and after the signal size
        // - May be any number of spaces before and after the comma
        // - May be any number of spaces before and after the init value
        // - May be any number of spaces before and after the comma
        // - May be any number of spaces before and after the published_by node
        // - May be any number of spaces before and after the comma
        // - May be any number of spaces before and after the subscribed_by node
        // - May be any number of spaces before and after the semicolon
        let (s, _) = skip_whitespace(remaining)?;
        let (s, signal_name) = take_while(|c: char| c.is_alphanumeric() || c == '_')(s)?;
        let (s, _) = skip_whitespace(s)?;
        let (s, _) = tag(":")(s)?;
        let (s, _) = skip_whitespace(s)?;
        let (s, signal_size) = take_while(|c: char| c.is_numeric())(s)?;
        let (s, _) = skip_whitespace(s)?;
        let (s, _) = tag(",")(s)?;
        let (s, _) = skip_whitespace(s)?;
        let (s, init_value) = take_while(|c: char| c.is_numeric())(s)?;
        let (s, _) = skip_whitespace(s)?;
        let (s, _) = tag(",")(s)?;
        let (s, _) = skip_whitespace(s)?;
        let (s, published_by) = take_while(|c: char| c.is_alphanumeric() || c == '_')(s)?;
        let (s, _) = skip_whitespace(s)?;
        let (s, _) = tag(",")(s)?;
        let (s, _) = skip_whitespace(s)?;
        let (s, subscribed_by_str) = take_until(";")(s)?;
        let subscribed_by: Vec<String> = subscribed_by_str.split(',').map(|s| s.trim().to_string()).collect();
        let (s, _) = tag(";")(s)?;
        let (s, _) = skip_whitespace(s)?;

        remaining = s;

        let signal = LdfSignal {
            name: signal_name.to_string(),
            signal_size: signal_size.parse().unwrap(),
            init_value: LdfSignalInitValue::Scalar(init_value.parse().unwrap()),
            published_by: published_by.to_string(),
            subscribed_by,
        };

        signals.push(signal);
    }

    // `}` or `} ;` or ...
    // - May be any number of spaces before and after the closing curly brace
    let (s, _) = skip_whitespace(remaining)?;
    let (s, _) = tag("}")(s)?;

    Ok((s, signals))
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_parse_ldf_signals() {
        let input = r#"
            Signals {
                Signal1: 10, 0, Master, Slave1 , Slave2 ;
                Signal2: 10, 0, Master, Slave1 ;
                Signal3: 10, 0, Slave1, Master ;
                Signal4: 10, 0, Slave1, Master ;
                Signal5: 2, 0, Slave1, Master ;
                Signal6: 1, 0, Slave1, Master ;
            }
        "#;

        let (_, signals) = parse_ldf_signals(input).unwrap();
        assert_eq!(signals.len(), 6);
        assert_eq!(signals[0].name, "Signal1");
        assert_eq!(signals[0].signal_size, 10);
        assert_eq!(signals[0].published_by, "Master");
        assert_eq!(signals[0].subscribed_by, vec!["Slave1", "Slave2"]);
        assert_eq!(signals[1].name, "Signal2");
    }
}