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
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
use nom::{
bytes::complete::{tag, take_until, take_while},
IResult,
};
use crate::ldf::ldf_comment::skip_whitespace;
/// Header of a LIN Description File (LDF) for LIN
/// ```text
/// LIN_description_file;
/// LIN_protocol_version = "2.1" ;
/// LIN_language_version = "2.1" ;
/// LIN_speed = 19.2 kbps ;
/// Channel_name = "DB";
/// ```
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct LdfHeader {
/// LIN protocol version number (e.g. 2.1).
/// Shall be in the range of "0.01" to "99.99".
pub lin_protocol_version: String,
/// LIN language version number (e.g. 2.1)
/// Shall be in the range of "0.01" to "99.99".
pub lin_language_version: String,
/// LIN speed in bits per second (e.g. 19.2 kbps = 19200).
/// This sets the nominal bit rate for the cluster. It shall be in the range of 1 to 20 kbit/second.
pub lin_speed: u16,
/// Channel_name is optional and was added in LIN 2.2 version.
pub channel_name: Option<String>,
}
/*
LIN_description_file;
LIN_protocol_version = "2.1" ;
LIN_language_version = "2.1" ;
LIN_speed = 19.2 kbps ;
Channel_name = "DB";
*/
pub fn parse_ldf_header(s: &str) -> IResult<&str, LdfHeader> {
// Skip anything before "LIN_description_file"
let (s, _) = take_until("LIN_description_file")(s)?;
// `LIN_description_file;` or `LIN_description_file ;` or ...
// - May be any number of spaces before and after the "LIN_description_file" tag
// - May be any number of spaces before and after the semicolon
let (s, _) = tag("LIN_description_file")(s)?;
let (s, _) = skip_whitespace(s)?;
let (s, _) = tag(";")(s)?;
// `LIN_protocol_version = "2.1" ;` or `LIN_protocol_version = "2.1";` or ...
// - May be any number of spaces before and after the "LIN_protocol_version" tag
// - May be any number of spaces before and after the equal sign
// - May be any number of spaces before and after the version number
// - May be any number of spaces before and after the semicolon
let (s, _) = skip_whitespace(s)?;
let (s, _) = tag("LIN_protocol_version")(s)?;
let (s, _) = skip_whitespace(s)?;
let (s, _) = tag("=")(s)?;
let (s, _) = skip_whitespace(s)?;
let (s, _) = tag("\"")(s)?;
let (s, protocol_major) = take_while(|c: char| c.is_numeric())(s)?;
let (s, _) = tag(".")(s)?;
let (s, protocol_minor) = take_while(|c: char| c.is_numeric())(s)?;
let (s, _) = tag("\"")(s)?;
let (s, _) = skip_whitespace(s)?;
let (s, _) = tag(";")(s)?;
let lin_protocol_version = format!("{}.{}", protocol_major, protocol_minor);
// `LIN_language_version = "2.1" ;` or `LIN_language_version = "2.1";` or ...
// - May be any number of spaces before and after the "LIN_language_version" tag
// - May be any number of spaces before and after the equal sign
// - May be any number of spaces before and after the version number
// - May be any number of spaces before and after the semicolon
let (s, _) = skip_whitespace(s)?;
let (s, _) = tag("LIN_language_version")(s)?;
let (s, _) = skip_whitespace(s)?;
let (s, _) = tag("=")(s)?;
let (s, _) = skip_whitespace(s)?;
let (s, _) = tag("\"")(s)?;
let (s, language_major) = take_while(|c: char| c.is_numeric())(s)?;
let (s, _) = tag(".")(s)?;
let (s, language_minor) = take_while(|c: char| c.is_numeric())(s)?;
let (s, _) = tag("\"")(s)?;
let (s, _) = skip_whitespace(s)?;
let (s, _) = tag(";")(s)?;
let lin_language_version = format!("{}.{}", language_major, language_minor);
// LIN_speed = 19.2 kbps ;
// - May be any number of spaces before and after the "LIN_speed" tag
// - May be any number of spaces before and after the equal sign
// - May be any number of spaces before and after the speed number
// - May be any number of spaces before and after the speed unit
// - May be any number of spaces before and after the semicolon
let (s, _) = skip_whitespace(s)?;
let (s, _) = tag("LIN_speed")(s)?;
let (s, _) = skip_whitespace(s)?;
let (s, _) = tag("=")(s)?;
let (s, _) = skip_whitespace(s)?;
let (s, lin_speed) = take_while(|c: char| c.is_numeric() || c == '.')(s)?;
let (s, _) = skip_whitespace(s)?;
let (s, _) = tag("kbps")(s)?;
let (s, _) = skip_whitespace(s)?;
let (s, _) = tag(";")(s)?;
let lin_speed = (lin_speed.parse::<f32>().unwrap() * 1000.0) as u16;
let (s, channel_name) = parse_channel_name(s).unwrap_or((s, None));
Ok((
s,
LdfHeader {
lin_protocol_version,
lin_language_version,
lin_speed,
channel_name,
},
))
}
pub fn parse_channel_name(s: &str) -> IResult<&str, Option<String>> {
// Channel_name = "DB";
// - May be any number of spaces before and after the "Channel_name" tag
// - May be any number of spaces before and after the equal sign
// - May be any number of spaces before and after the channel name
// - May be any number of spaces before and after the semicolon
let (s, _) = skip_whitespace(s)?;
let (s, _) = tag("Channel_name")(s)?;
let (s, _) = skip_whitespace(s)?;
let (s, _) = tag("=")(s)?;
let (s, _) = skip_whitespace(s)?;
let (s, _) = tag("\"")(s)?;
let (s, channel_name) = take_while(|c: char| c.is_alphanumeric() || c == '_')(s)?;
let (s, _) = tag("\"")(s)?;
let (s, _) = skip_whitespace(s)?;
let (s, _) = tag(";")(s)?;
Ok((s, Some(channel_name.to_string())))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse() {
let s = r#"
LIN_description_file ;
LIN_protocol_version = "2.1" ;
LIN_language_version = "2.1" ;
LIN_speed = 19.2 kbps ;
Channel_name = "DB";
"#;
let (_, header) = parse_ldf_header(s).unwrap();
assert_eq!(header.lin_protocol_version, "2.1");
assert_eq!(header.lin_language_version, "2.1");
assert_eq!(header.lin_speed, 19200);
assert_eq!(header.channel_name, Some("DB".to_string()));
}
#[test]
fn test_parse_channel_name() {
let s = r#"Channel_name = "DB";"#;
let (_, channel_name) = parse_channel_name(s).unwrap();
assert_eq!(channel_name, Some("DB".to_string()));
}
#[test]
fn test_parse_header_no_channel_name() {
let s = r#"
LIN_description_file ;
LIN_protocol_version = "2.1" ;
LIN_language_version = "2.1" ;
LIN_speed = 19.2 kbps ;
"#;
let (_, header) = parse_ldf_header(s).unwrap();
assert_eq!(header.lin_protocol_version, "2.1");
assert_eq!(header.lin_language_version, "2.1");
assert_eq!(header.lin_speed, 19200);
assert_eq!(header.channel_name, None);
}
}