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
//! Routine management wrapper for KWP2000
use crate::{DiagError, DiagServerResult, DiagnosticServer};
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
/// Routine Identifier
pub enum RoutineID {
/// Routine local identifier (Range 0x01 - 0xDF)
LocalIdentifier(u8),
/// Flash erase routine
FlashErase,
/// Flash check routine
FlashCheck,
/// Request Diagnostic trouble codes from ECU shadow error memory
RequestDTCFromShadowErrorMem,
/// Request environmental data from shadow error memory
RequestEnvDataFromShadowErrorMem,
/// Request event information
RequestEventInformation,
/// Request Software module information
RequestSWModuleInformation,
/// Clear tell-tale retention stack
ClearTellTaleRetentionStack,
/// System supplier specific
SystemSupplierSpecific(u8),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
/// Routine exit status
pub enum RoutineExitStatus {
/// Unknown exit type
Unknown(u8),
/// Normal exit with results available. Call [KwpRoutineManager::request_routine_results] to obtain the results
NormalExitWithResults,
/// Normal exit, the routine does not return any result data
NormalExitWithoutResults,
/// Abnormal or premature exit. No results available.
AbnormalExitWithoutResults,
}
impl From<u8> for RoutineExitStatus {
fn from(x: u8) -> Self {
match x {
0x61 => Self::NormalExitWithResults,
0x62 => Self::NormalExitWithoutResults,
0x64 => Self::AbnormalExitWithoutResults,
_ => Self::Unknown(x),
}
}
}
impl RoutineID {
pub(crate) fn as_start_byte(&self) -> u8 {
match self {
RoutineID::LocalIdentifier(x) => *x,
RoutineID::FlashErase => 0xE0,
RoutineID::FlashCheck => 0xE1,
RoutineID::RequestDTCFromShadowErrorMem => 0xE3,
RoutineID::RequestEnvDataFromShadowErrorMem => 0xE4,
RoutineID::RequestEventInformation => 0xE5,
RoutineID::RequestSWModuleInformation => 0xE6,
RoutineID::ClearTellTaleRetentionStack => 0xE7,
RoutineID::SystemSupplierSpecific(x) => *x,
}
}
pub(crate) fn as_result_byte(&self) -> u8 {
match self {
RoutineID::LocalIdentifier(x) => *x,
RoutineID::FlashErase => 0xE0,
RoutineID::FlashCheck => 0xE1,
RoutineID::RequestDTCFromShadowErrorMem => 0xE3,
RoutineID::RequestEnvDataFromShadowErrorMem => 0xE4,
RoutineID::RequestEventInformation => 0xE5,
RoutineID::RequestSWModuleInformation => 0xE6,
RoutineID::ClearTellTaleRetentionStack => 0xE2,
RoutineID::SystemSupplierSpecific(x) => *x,
}
}
}
#[derive(Debug)]
/// KWP2000 Routine execution wrapper
pub struct KwpRoutineManager<'a> {
server: &'a mut super::Kwp2000DiagnosticServer,
r_id: RoutineID,
}
impl<'a> KwpRoutineManager<'a> {
/// Creates a new routine manager. Upon creation, the KWP2000 diagnostic server will automatically
/// attempt to enter extended diagnostic session mode, which is required for routine execution and
/// management.
///
/// # Parameters
/// * rid - The routine ID
/// * server - Reference to running KWP2000 diagnostic server
///
/// # Returns
/// If an error of [DiagError::ParameterInvalid] is returned, then it means that the value of `rid` is invalid
/// and violates the KWP2000 specification. Other [DiagError]'s will come from the attempt to set the ECU
/// into extended diagnostic session mode.
pub fn new(
rid: RoutineID,
server: &'a mut super::Kwp2000DiagnosticServer,
) -> DiagServerResult<Self> {
let x: u8 = rid.as_start_byte();
if x == 0x00 || x == 0xE2 || x == 0xFF || (0xEA..=0xF9).contains(&x) {
return Err(DiagError::ParameterInvalid); // Unsupported by the spec, might have undefined behavior. Ignore!
}
// We have to be in extended mode for routine management to work!
server.set_diagnostic_session_mode(super::SessionType::ExtendedDiagnostics)?;
Ok(Self { server, r_id: rid })
}
/// Attempts to start the routine
pub fn start_routine(&mut self, entry_options: &[u8]) -> DiagServerResult<()> {
let mut p: Vec<u8> = vec![self.r_id.as_start_byte()];
p.extend_from_slice(entry_options);
self.server
.execute_command_with_response(
crate::kwp2000::KWP2000Command::StartRoutineByLocalIdentifier,
&p,
)
.map(|_| ())
}
/// Attempts to stop the routine. Note that some routines automatically exit themselves
/// and do NOT need to be manually stopped
pub fn stop_routine(&mut self, exit_options: &[u8]) -> DiagServerResult<RoutineExitStatus> {
let mut p: Vec<u8> = vec![self.r_id.as_start_byte()];
p.extend_from_slice(exit_options);
self.server
.execute_command_with_response(
crate::kwp2000::KWP2000Command::StopRoutineByLocalIdentifier,
&p,
)
.map(|x| x[1].into())
}
/// Requests the results of the routine. If the routine was manually stopped prior to running this,
/// it is best practice to check the [RoutineExitStatus] to see if the routine exited with
/// [RoutineExitStatus::NormalExitWithResults] first.
pub fn request_routine_results(&mut self) -> DiagServerResult<Vec<u8>> {
self.server.execute_command_with_response(
crate::kwp2000::KWP2000Command::RequestRoutineResultsByLocalIdentifier,
&[self.r_id.as_result_byte()],
)
}
}