lin_ldf/lib.rs
1//! # lin-ldf
2//!
3//! LIN Description File (.ldf) parser using Rust's `nom` parser combinator library.
4//! LIN is an automotive serial protocol used for communication between ECUs in a vehicle.
5//! The LDF file is used to describe the network configuration, including the different nodes and signals sent between them.
6//!
7//! > ⚠️ WARNING:
8//! > This crate may not be suitable for production use. It was written as hands-on learning exercise of a well-documented specification.
9//! > It may not cover all edge cases or vendor-specific implementations. Please use with caution.
10//!
11//! <br>
12//! <a href="https://crates.io/crates/lin-ldf">
13//! <img src="https://img.shields.io/crates/v/lin-ldf.svg" alt="Crates.io">
14//! </a>
15//! <a href="https://docs.rs/lin-ldf">
16//! <img src="https://docs.rs/lin-ldf/badge.svg" alt="Documentation">
17//! </a>
18//! <a href="">
19//! <img src="https://img.shields.io/badge/license-MIT-blue.svg">
20//! </a>
21//! <br><br>
22//!
23//! This parser attempts to be a simple reflection of the well-documented instructions from the LIN specification:
24//! [https://www.lin-cia.org/fileadmin/microsites/lin-cia.org/resources/documents/LIN_2.2A.pdf](https://www.lin-cia.org/fileadmin/microsites/lin-cia.org/resources/documents/LIN_2.2A.pdf)
25//!
26//! ## Alternatives
27//!
28//! There are some existing alternatives that have been around for years if you need something more robust:
29//! - [https://github.com/c4deszes/ldfparser](https://github.com/c4deszes/ldfparser) (Python) (**most popular**)
30//! - [https://github.com/uCAN-LIN/LinUSBConverter/tree/master/python_lib](https://github.com/uCAN-LIN/LinUSBConverter/tree/master/python_lib) (Python)
31//! - [https://github.com/TrippW/LDF-Parser](https://github.com/TrippW/LDF-Parser) (Python)
32//! - [https://bitbucket.org/tobylorenz/lin/src/master](https://bitbucket.org/tobylorenz/lin/src/master) (C++)
33//!
34//! Here are more recent alternatives I found:
35//! - [https://github.com/dragonlock2/autodbconv](https://github.com/dragonlock2/autodbconv) (Rust)
36//!
37//! ## Supported LDF sections (so far)
38//!
39//! - [x] LIN_protocol_version
40//! - [x] LIN_language_version
41//! - [x] LIN_speed
42//! - [x] (Channel_name)
43//! - [x] Nodes
44//! - [ ] (Node_composition)
45//! - [x] Signals
46//! - [x] (Diagnostic_signals)
47//! - [x] Frames
48//! - [ ] (Sporadic_frame)
49//! - [ ] (Event_triggered_frame)
50//! - [x] (Diagnostic_frames)
51//! - [x] Node_attributes
52//! - [x] Schedule_table
53//! - [ ] (Signal_groups)
54//! - [x] (Signal_encoding_type)
55//! - [x] (Signal_representation)
56//!
57//! (optional sections are in parentheses)
58//!
59//! # Example
60//!
61//! ```
62//! use lin_ldf::parse_ldf;
63//!
64//! let ldf = r#"
65//! LIN_description_file ;
66//! LIN_protocol_version = "2.1" ;
67//! LIN_language_version = "2.1" ;
68//! LIN_speed = 19.2 kbps ;
69//!
70//! /* PARSING IGNORES BLOCK COMMENTS */
71//!
72//! Nodes {
73//! Master: Master, 5 ms, 0.1 ms ;
74//! Slaves: Slave1, Slave2, Slave3 ;
75//! }
76//!
77//! Signals {
78//! Signal1: 10, 0, Master, Slave1 , Slave2 ;
79//! Signal2: 10, 0, Master, Slave1 ;
80//! Signal3: 10, 0, Slave1, Master ;
81//! Signal4: 10, 0, Slave1, Master ;
82//! Signal5: 2, 0, Slave1, Master ;
83//! Signal6: 1, 0, Slave1, Master ;
84//! }
85//!
86//! Frames {
87//! Frame1: 0, Master, 8 {
88//! Signal1, 0 ;
89//! Signal2, 10 ;
90//! }
91//! Frame2: 0x16, Slave1, 8 {
92//! Signal3, 0 ;
93//! Signal4, 10 ;
94//! }
95//! }
96//!
97//! Node_attributes {
98//! Slave1 {
99//! LIN_protocol = "2.1" ;
100//! configured_NAD = 0xB ;
101//! initial_NAD = 0xB ;
102//! product_id = 0x123, 0x4567, 8 ;
103//! response_error = Signal1 ;
104//! P2_min = 100 ms ;
105//! ST_min = 0 ms ;
106//! N_As_timeout = 1000 ms ;
107//! N_Cr_timeout = 1000 ms ;
108//! configurable_frames {
109//! Frame1 ;
110//! Frame2 ;
111//! }
112//! }
113//! Slave2 {
114//! LIN_protocol = "2.1" ;
115//! configured_NAD = 0xC ;
116//! initial_NAD = 0xC ;
117//! product_id = 0x124, 0x4568, 0x66 ;
118//! response_error = Signal2 ;
119//! P2_min = 100 ms ;
120//! ST_min = 0 ms ;
121//! N_As_timeout = 1000 ms ;
122//! N_Cr_timeout = 1000 ms ;
123//! configurable_frames {
124//! Frame1 ;
125//! Frame2 ;
126//! }
127//! }
128//! }
129//!
130//! Schedule_tables {
131//! AllFrames {
132//! Frame1 delay 10 ms ;
133//! Frame2 delay 10 ms ;
134//! }
135//! }
136//! "#;
137//!
138//! let parsed_ldf = parse_ldf(ldf).expect("Failed to parse LDF file");
139//! let total_signal_count = parsed_ldf.signals.len(); // 6
140//!
141//! for frame in parsed_ldf.frames {
142//! println!("Frame: {} is {} bytes long", frame.frame_name, frame.frame_size);
143//! for signal in frame.signals {
144//! println!("\tSignal: `{}` at bit position {}", signal.signal_name, signal.start_bit);
145//! }
146//! }
147//! ```
148mod ldf;
149
150pub use ldf::ldf_signal_encoding_types::LdfSignalEncodingTypeValue;
151pub use ldf::ldf_signals::LdfSignalInitValue;
152pub use ldf::LinLdf;
153
154pub fn parse_ldf(ldf: &str) -> Result<LinLdf, &'static str> {
155 ldf::LinLdf::parse(ldf)
156}