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
//! Routine management wrapper for KWP2000
use crate::{dynamic_diag::DynamicDiagSession, DiagError, DiagServerResult};
use automotive_diag::kwp2000::{KwpCommand, KwpSessionType, RoutineExitStatusByte};
#[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),
}
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 DynamicDiagSession,
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 DynamicDiagSession) -> 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.kwp_set_session(KwpSessionType::ExtendedDiagnostics.into())?;
Ok(Self { server, r_id: rid })
}
/// Attempts to start the routine
pub fn start_routine(&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
.send_command_with_response(KwpCommand::StartRoutineByLocalIdentifier, &p)?;
Ok(())
}
/// Attempts to stop the routine. Note that some routines automatically exit themselves
/// and do NOT need to be manually stopped
pub fn stop_routine(&self, exit_options: &[u8]) -> DiagServerResult<RoutineExitStatusByte> {
let mut p: Vec<u8> = vec![self.r_id.as_start_byte()];
p.extend_from_slice(exit_options);
self.server
.send_command_with_response(KwpCommand::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(&self) -> DiagServerResult<Vec<u8>> {
self.server.send_command_with_response(
KwpCommand::RequestRoutineResultsByLocalIdentifier,
&[self.r_id.as_result_byte()],
)
}
}