iso14229_1/response/request_file_transfer.rs
1//! response of Service 38
2
3
4use std::collections::HashSet;
5use lazy_static::lazy_static;
6use crate::{ByteOrder, Configuration, DataFormatIdentifier, error::Error, LengthFormatIdentifier, ModeOfOperation, Placeholder, response::Code, ResponseData, utils};
7
8lazy_static!(
9 pub static ref REQUEST_FILE_TRANSFER_NEGATIVES: HashSet<Code> = HashSet::from([
10 Code::IncorrectMessageLengthOrInvalidFormat,
11 Code::ConditionsNotCorrect,
12 #[cfg(any(feature = "std2020"))]
13 Code::RequestSequenceError, // ResumeFile only
14 Code::RequestOutOfRange,
15 Code::SecurityAccessDenied,
16 #[cfg(any(feature = "std2020"))]
17 Code::AuthenticationRequired,
18 Code::UploadDownloadNotAccepted,
19 ]);
20);
21
22/*
23// Specifies the length (number of bytes) of the maxNumberOfBlockLength parameter.
24// If the modeOfOperation parameter equals to 02 (DeleteFile) this parameter shall not be included in the
25// response message.
26// */
27// lengthFormatIdentifier
28/*
29This parameter is used by the requestFileTransfer positive response message to inform the client how many
30data bytes (maxNumberOfBlockLength) to include in each TransferData request message from the client or how
31many data bytes the server will include in a TransferData positive response when uploading data. This length
32reflects the complete message length, including the service identifier and the data parameters present in the
33TransferData request message or positive response message. This parameter allows either the client to adapt to
34the receive buffer size of the server before it starts transferring data to the server or to indicate how many data
35bytes will be included in each TransferData positive response in the event that data is uploaded. A server is
36required to accept transferData requests that are equal in length to its reported maxNumberOfBlockLength. It is
37server specific what transferData request lengths less than maxNumberOfBlockLength are accepted (if any).
38NOTE The last transferData request within a given block can be required to be less than
39maxNumberOfBlockLength. It is not allowed for a server to write additional data bytes (i.e. pad bytes) not
40contained within the transferData message (either in a compressed or uncompressed format), as this would
41affect the memory address of where the subsequent transferData request data would be written.
42If the modeOfOperation parameter equals to 02 (DeleteFile) this parameter shall be not be included in the
43response message.
44*/
45// maxNumberOfBlockLength
46/*
47This is parameter echoes the value of the request.
48If the modeOfOperation parameter equals to 02 (DeleteFile) this parameter shall not be included in the
49response message.)
50If the modeOfOperation parameter equals to 05 (ReadDir) the value of this parameter shall be equal to 00.
51*/
52// dataFormatIdentifier
53/*
54Specifies the length in bytes for both parameters fileSizeUncompressedOrDirInfoLength and fileSizeCompressed.
55If the modeOfOperation parameter equals to 01 (AddFile), 02 (DeleteFile), 03 (ReplaceFile) or 06 (ResumeFile)
56this parameter shall not be included in the response message.
57*/
58// fileSizeOrDirInfoParameterLength
59/*
60Specifies the length in bytes for both parameters fileSizeUncompressedOrDirInfoLength and fileSizeCompressed.
61If the modeOfOperation parameter equals to 01 (AddFile), 02 (DeleteFile), 03 (ReplaceFile) or 06 ResumeFile)
62this parameter shall not be included in the response message.
63*/
64// fileSizeUncompressedOrDirInfoLength
65/*
66Specifies the size of the compressed file in bytes.
67If the modeOfOperation parameter equals to 01 (AddFile), 02 (DeleteFile, 03 (ReplaceFile)), 05 (ReadDir)
68or 06 (ResumeFile) this parameter shall not be included in the response message.
69*/
70// fileSizeCompressed
71/*
72Specifies the byte position within the file at which the Tester will resume downloading after an initial download
73is suspended. A download is suspended when the ECU stops receiving TransferData requests and does not
74receive the RequestTransferExit request before returning to the defaultSession.
75The filePosition is relative to the compressed size or uncompressed size, depending if the file was originally sent
76compressed or uncompressed during the initiating ModeOfOperation = AddFile or ReplaceFile.
77If the modeOfOperation parameter equals to 01 (AddFile), 02 (DeleteFile), 03 (ReplaceFile), 04 (ReadFile),
78or 05 (ReadDir) this parameter shall not be included in the request.
79*/
80// filePosition
81#[derive(Debug, Clone)]
82pub enum RequestFileTransferData {
83 AddFile { // 1 modeOfOperation
84 lfi: u8,
85 max_block_len: u128,
86 dfi: DataFormatIdentifier,
87 },
88 DeleteFile, // 2 modeOfOperation
89 ReplaceFile { // 3 modeOfOperation
90 lfi: u8,
91 max_block_len: u128,
92 dfi: DataFormatIdentifier,
93 },
94 ReadFile { // 4 modeOfOperation
95 lfi: u8,
96 max_block_len: u128,
97 dfi: DataFormatIdentifier,
98 filesize_or_dir_param_len: u16,
99 uncompressed_size_or_dir_len: u128,
100 compressed_size: u128,
101 },
102 ReadDir { // 5 modeOfOperation
103 lfi: u8,
104 max_block_len: u128,
105 dfi: DataFormatIdentifier, // always 0x00
106 filesize_or_dir_param_len: u16,
107 uncompressed_size_or_dir_len: u128,
108 },
109 ResumeFile { // 6 modeOfOperation
110 lfi: u8,
111 max_block_len: u128,
112 dfi: DataFormatIdentifier,
113 file_pos: [u8; 8],
114 },
115}
116
117impl ResponseData for RequestFileTransferData {
118 type SubFunc = ModeOfOperation;
119 fn try_parse(data: &[u8], sub_func: Option<Self::SubFunc>, _: &Configuration) -> Result<Self, Error> {
120 match sub_func {
121 Some(mode) => {
122 let data_len = data.len();
123 utils::data_length_check(data_len, 1, false)?;
124 let mut offset = 0;
125
126 match mode {
127 ModeOfOperation::AddFile => {
128 utils::data_length_check(data_len, offset + 1, false)?;
129
130 let lfi = data[offset];
131 offset += 1;
132 utils::data_length_check(data_len, offset + lfi as usize + 1, false)?;
133
134 let max_block_len = utils::slice_to_u128(&data[offset..offset + lfi as usize], ByteOrder::Big);
135 offset += lfi as usize;
136 let dfi = DataFormatIdentifier::from(data[offset]);
137 Ok(Self::AddFile {
138 lfi, max_block_len, dfi
139 })
140 },
141 ModeOfOperation::DeleteFile => Ok(Self::DeleteFile),
142 ModeOfOperation::ReplaceFile => {
143 utils::data_length_check(data_len, offset + 1, false)?;
144
145 let lfi = data[offset];
146 offset += 1;
147 utils::data_length_check(data_len, offset + lfi as usize + 1, false)?;
148
149 let max_block_len = utils::slice_to_u128(&data[offset..offset + lfi as usize], ByteOrder::Big);
150 offset += lfi as usize;
151 let dfi = DataFormatIdentifier::from(data[offset]);
152 Ok(Self::ReplaceFile {
153 lfi, max_block_len, dfi
154 })
155 },
156 ModeOfOperation::ReadFile => {
157 utils::data_length_check(data_len, offset + 1, false)?;
158
159 let lfi = data[offset];
160 offset += 1;
161 utils::data_length_check(data_len, offset + lfi as usize + 4, false)?;
162
163 let max_block_len = utils::slice_to_u128(&data[offset..offset + lfi as usize], ByteOrder::Big);
164 offset += lfi as usize;
165
166 let dfi = DataFormatIdentifier::from(data[offset]);
167 offset += 1;
168
169 let filesize_or_dir_param_len = u16::from_be_bytes([data[offset], data[offset + 1]]);
170 offset += 2;
171
172 utils::data_length_check(data_len, offset + filesize_or_dir_param_len as usize + 1, false)?;
173
174 let uncompressed_size_or_dir_len = utils::slice_to_u128(&data[offset..offset + filesize_or_dir_param_len as usize], ByteOrder::Big);
175 offset += filesize_or_dir_param_len as usize;
176
177 let compressed_size = utils::slice_to_u128(&data[offset..], ByteOrder::Big);
178
179 Ok(Self::ReadFile {
180 lfi,
181 max_block_len,
182 dfi,
183 filesize_or_dir_param_len,
184 uncompressed_size_or_dir_len,
185 compressed_size,
186 })
187 },
188 ModeOfOperation::ReadDir => {
189 utils::data_length_check(data_len, offset + 1, false)?;
190
191 let lfi = data[offset];
192 offset += 1;
193 utils::data_length_check(data_len, offset + lfi as usize + 4, false)?;
194
195 let max_block_len = utils::slice_to_u128(&data[offset..offset + lfi as usize], ByteOrder::Big);
196 offset += lfi as usize;
197
198 let dfi = data[offset];
199 offset += 1;
200 if dfi != 0x00 {
201 return Err(Error::InvalidData(hex::encode(data)));
202 }
203 let dfi = DataFormatIdentifier(dfi);
204
205 let filesize_or_dir_param_len = u16::from_be_bytes([data[offset], data[offset + 1]]);
206 offset += 2;
207 let uncompressed_size_or_dir_len = utils::slice_to_u128(&data[offset..offset + filesize_or_dir_param_len as usize], ByteOrder::Big);
208
209 Ok(Self::ReadDir {
210 lfi,
211 max_block_len,
212 dfi,
213 filesize_or_dir_param_len,
214 uncompressed_size_or_dir_len,
215 })
216 },
217 ModeOfOperation::ResumeFile => {
218 utils::data_length_check(data_len, offset + 1, false)?;
219
220 let lfi = data[offset];
221 offset += 1;
222 utils::data_length_check(data_len, offset + lfi as usize + 9, false)?;
223
224 let max_block_len = utils::slice_to_u128(&data[offset..offset + lfi as usize], ByteOrder::Big);
225 offset += lfi as usize;
226 let dfi = DataFormatIdentifier::from(data[offset]);
227 offset += 1;
228 let file_pos = <[u8; 8]>::try_from(&data[offset..])
229 .map_err(|_| Error::InvalidData(hex::encode(data)))?;
230
231 Ok(Self::ResumeFile {
232 lfi,
233 max_block_len,
234 dfi,
235 file_pos,
236 })
237 },
238 }
239 },
240 None => panic!("Sub-function required"),
241 }
242 }
243 #[inline]
244 fn to_vec(self, _: &Configuration) -> Vec<u8> {
245 self.into()
246 }
247}
248
249impl Into<Vec<u8>> for RequestFileTransferData {
250 fn into(self) -> Vec<u8> {
251 let mut result = Vec::new();
252 match self {
253 Self::AddFile { lfi, max_block_len, dfi } => {
254 result.push(lfi);
255 result.extend(utils::u128_to_vec_fix(max_block_len, ByteOrder::Big));
256 result.push(dfi.into());
257 },
258 Self::DeleteFile => {},
259 Self::ReplaceFile { lfi, max_block_len, dfi } => {
260 result.push(lfi);
261 result.extend(utils::u128_to_vec_fix(max_block_len, ByteOrder::Big));
262 result.push(dfi.into());
263 },
264 Self::ReadFile {
265 lfi,
266 max_block_len,
267 dfi,
268 filesize_or_dir_param_len,
269 uncompressed_size_or_dir_len,
270 compressed_size
271 } => {
272 result.push(lfi);
273 result.extend(utils::u128_to_vec_fix(max_block_len, ByteOrder::Big));
274 result.push(dfi.into());
275 result.extend(filesize_or_dir_param_len.to_be_bytes());
276 result.extend(utils::u128_to_vec_fix(uncompressed_size_or_dir_len, ByteOrder::Big));
277 result.extend(utils::u128_to_vec_fix(compressed_size, ByteOrder::Big));
278 },
279 Self::ReadDir {
280 lfi,
281 max_block_len,
282 dfi,
283 filesize_or_dir_param_len,
284 uncompressed_size_or_dir_len,
285 } => {
286 result.push(lfi);
287 result.extend(utils::u128_to_vec_fix(max_block_len, ByteOrder::Big));
288 result.push(dfi.into());
289 result.extend(filesize_or_dir_param_len.to_be_bytes());
290 result.extend(utils::u128_to_vec_fix(uncompressed_size_or_dir_len, ByteOrder::Big));
291 },
292 Self::ResumeFile {
293 lfi,
294 max_block_len,
295 dfi,
296 file_pos,
297 } => {
298 result.push(lfi);
299 result.extend(utils::u128_to_vec_fix(max_block_len, ByteOrder::Big));
300 result.push(dfi.into());
301 result.extend(file_pos);
302 },
303 }
304
305 result
306 }
307}