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}