lin_ldf/
lib.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
//! # lin-ldf
//!
//! LIN Description File (.ldf) parser using Rust's `nom` parser combinator library.
//! LIN is an automotive serial protocol used for communication between ECUs in a vehicle.
//! The LDF file is used to describe the network configuration, including the different nodes and signals sent between them.
//!
//! > ⚠️ WARNING:
//! > This crate may not be suitable for production use. It was written as hands-on learning exercise of a well-documented specification. 
//! > It may not cover all edge cases or vendor-specific implementations. Please use with caution.
//!
//! <br>
//! <a href="https://crates.io/crates/lin-ldf">
//! <img src="https://img.shields.io/crates/v/lin-ldf.svg" alt="Crates.io">
//! </a>
//! <a href="https://docs.rs/lin-ldf">
//! <img src="https://docs.rs/lin-ldf/badge.svg" alt="Documentation">
//! </a>
//! <a href="">
//! <img src="https://img.shields.io/badge/license-MIT-blue.svg">
//! </a>
//! <br><br>
//!
//! This parser attempts to be a simple reflection of the well-documented instructions from the LIN specification:
//! [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)
//!
//! ## Alternatives
//!
//! There are some existing alternatives that have been around for years if you need something more robust:
//! - [https://github.com/c4deszes/ldfparser](https://github.com/c4deszes/ldfparser) (Python) (**most popular**)
//! - [https://github.com/uCAN-LIN/LinUSBConverter/tree/master/python_lib](https://github.com/uCAN-LIN/LinUSBConverter/tree/master/python_lib) (Python)
//! - [https://github.com/TrippW/LDF-Parser](https://github.com/TrippW/LDF-Parser) (Python)
//! - [https://bitbucket.org/tobylorenz/lin/src/master](https://bitbucket.org/tobylorenz/lin/src/master) (C++)
//!
//! Here are more recent alternatives I found:
//! - [https://github.com/dragonlock2/autodbconv](https://github.com/dragonlock2/autodbconv) (Rust)
//!
//! ## Supported LDF sections (so far)
//!  
//! - [x] LIN_protocol_version
//! - [x] LIN_language_version
//! - [x] LIN_speed
//! - [x] (Channel_name)
//! - [x] Nodes
//! - [ ] (Node_composition)
//! - [x] Signals
//! - [x] (Diagnostic_signals)
//! - [x] Frames
//! - [ ] (Sporadic_frame)
//! - [ ] (Event_triggered_frame)
//! - [x] (Diagnostic_frames)
//! - [x] Node_attributes
//! - [x] Schedule_table
//! - [ ] (Signal_groups)
//! - [x] (Signal_encoding_type)
//! - [x] (Signal_representation)
//!
//! (optional sections are in parentheses)
//!
//! # Example
//!
//! ```
//! use lin_ldf::parse_ldf;
//!
//! let ldf = r#"
//! LIN_description_file ;
//! LIN_protocol_version = "2.1" ;
//! LIN_language_version = "2.1" ;
//! LIN_speed = 19.2 kbps ;
//!
//! /* PARSING IGNORES BLOCK COMMENTS */
//!
//! Nodes {
//!     Master: Master, 5 ms, 0.1 ms ;
//!     Slaves: Slave1, Slave2, Slave3 ;
//! }
//!
//! 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 ;
//! }
//!
//! Frames {
//!     Frame1: 0, Master, 8 {
//!         Signal1, 0 ;
//!         Signal2, 10 ;
//!     }
//!     Frame2: 0x16, Slave1, 8 {
//!         Signal3, 0 ;
//!         Signal4, 10 ;
//!     }
//! }
//!
//! Node_attributes {
//!    Slave1 {
//!        LIN_protocol = "2.1" ;
//!        configured_NAD = 0xB ;
//!        initial_NAD = 0xB ;
//!        product_id = 0x123, 0x4567, 8 ;
//!        response_error = Signal1 ;
//!        P2_min = 100 ms ;
//!        ST_min = 0 ms ;
//!        N_As_timeout = 1000 ms ;
//!        N_Cr_timeout = 1000 ms ;
//!        configurable_frames {
//!            Frame1 ;
//!            Frame2 ;  
//!        }
//!    }
//!    Slave2 {
//!        LIN_protocol = "2.1" ;
//!        configured_NAD = 0xC ;
//!        initial_NAD = 0xC ;
//!        product_id = 0x124, 0x4568, 0x66 ;
//!        response_error = Signal2 ;
//!        P2_min = 100 ms ;
//!        ST_min = 0 ms ;
//!        N_As_timeout = 1000 ms ;
//!        N_Cr_timeout = 1000 ms ;
//!        configurable_frames {
//!            Frame1 ;
//!            Frame2 ;
//!        }
//!    }
//! }
//!
//! Schedule_tables {
//!     AllFrames {
//!         Frame1 delay 10 ms ;
//!         Frame2 delay 10 ms ;
//!     }
//! }
//! "#;
//!
//! let parsed_ldf = parse_ldf(ldf).expect("Failed to parse LDF file");
//! let total_signal_count = parsed_ldf.signals.len(); // 6
//!
//! for frame in parsed_ldf.frames {
//!     println!("Frame: {} is {} bytes long", frame.frame_name, frame.frame_size);
//!     for signal in frame.signals {
//!         println!("\tSignal: `{}` at bit position {}", signal.signal_name, signal.start_bit);
//!     }
//! }
//! ```
mod ldf;

pub use ldf::ldf_signal_encoding_types::LdfSignalEncodingTypeValue;
pub use ldf::ldf_signals::LdfSignalInitValue;
pub use ldf::LinLdf;

pub fn parse_ldf(ldf: &str) -> Result<LinLdf, &'static str> {
    ldf::LinLdf::parse(ldf)
}