1use anyhow::Result;
2
3use crate::applayer::PayloadCodec;
4
5pub enum Cid {
6 PackageVersionReq,
7 PackageVersionAns,
8 AppTimeReq,
9 AppTimeAns,
10 DeviceAppTimePeropdicityReq,
11 DeviceAppTimePeropdicityAns,
12 ForceDeviceResyncCmd,
13}
14
15impl Cid {
16 pub fn from_u8(uplink: bool, value: u8) -> Result<Cid> {
17 Ok(match uplink {
18 true => match value {
19 0x00 => Cid::PackageVersionAns,
20 0x01 => Cid::AppTimeReq,
21 0x02 => Cid::DeviceAppTimePeropdicityAns,
22 _ => return Err(anyhow!("Invalid CID: {}", value)),
23 },
24 false => match value {
25 0x00 => Cid::PackageVersionReq,
26 0x01 => Cid::AppTimeAns,
27 0x02 => Cid::DeviceAppTimePeropdicityReq,
28 0x03 => Cid::ForceDeviceResyncCmd,
29 _ => return Err(anyhow!("Invalid CID: {}", value)),
30 },
31 })
32 }
33
34 pub fn to_u8(&self) -> u8 {
35 match self {
36 Cid::PackageVersionReq | Cid::PackageVersionAns => 0x00,
37 Cid::AppTimeReq | Cid::AppTimeAns => 0x01,
38 Cid::DeviceAppTimePeropdicityReq | Cid::DeviceAppTimePeropdicityAns => 0x02,
39 Cid::ForceDeviceResyncCmd => 0x03,
40 }
41 }
42}
43
44#[derive(Debug, PartialEq)]
45pub enum Payload {
46 PackageVersionReq,
47 PackageVersionAns(PackageVersionAnsPayload),
48 AppTimeReq(AppTimeReqPayload),
49 AppTimeAns(AppTimeAnsPayload),
50 DeviceAppTimePeropdicityReq(DeviceAppTimePeriodicityReqPayload),
51 DeviceAppTimePeropdicityAns(DeviceAppTimePeriodicityAnsPayload),
52 ForceDeviceResyncCmd(ForceDeviceResyncCmdPayload),
53}
54
55impl Payload {
56 pub fn cid(&self) -> Cid {
57 match self {
58 Self::PackageVersionReq => Cid::PackageVersionReq,
59 Self::PackageVersionAns(_) => Cid::PackageVersionAns,
60 Self::AppTimeReq(_) => Cid::AppTimeReq,
61 Self::AppTimeAns(_) => Cid::AppTimeAns,
62 Self::DeviceAppTimePeropdicityReq(_) => Cid::DeviceAppTimePeropdicityReq,
63 Self::DeviceAppTimePeropdicityAns(_) => Cid::DeviceAppTimePeropdicityAns,
64 Self::ForceDeviceResyncCmd(_) => Cid::ForceDeviceResyncCmd,
65 }
66 }
67
68 pub fn from_slice(uplink: bool, b: &[u8]) -> Result<Self> {
69 if b.is_empty() {
70 return Err(anyhow!("At least one byte is expected"));
71 }
72
73 let cid = Cid::from_u8(uplink, b[0])?;
74
75 Ok(match cid {
76 Cid::PackageVersionReq => Payload::PackageVersionReq,
77 Cid::PackageVersionAns => {
78 Payload::PackageVersionAns(PackageVersionAnsPayload::decode(&b[1..])?)
79 }
80 Cid::AppTimeReq => Payload::AppTimeReq(AppTimeReqPayload::decode(&b[1..])?),
81 Cid::AppTimeAns => Payload::AppTimeAns(AppTimeAnsPayload::decode(&b[1..])?),
82 Cid::DeviceAppTimePeropdicityReq => Payload::DeviceAppTimePeropdicityReq(
83 DeviceAppTimePeriodicityReqPayload::decode(&b[1..])?,
84 ),
85 Cid::DeviceAppTimePeropdicityAns => Payload::DeviceAppTimePeropdicityAns(
86 DeviceAppTimePeriodicityAnsPayload::decode(&b[1..])?,
87 ),
88 Cid::ForceDeviceResyncCmd => {
89 Payload::ForceDeviceResyncCmd(ForceDeviceResyncCmdPayload::decode(&b[1..])?)
90 }
91 })
92 }
93
94 pub fn to_vec(&self) -> Result<Vec<u8>> {
95 let mut out = vec![self.cid().to_u8()];
96
97 match self {
98 Self::PackageVersionReq => {}
99 Self::PackageVersionAns(pl) => out.extend_from_slice(&pl.encode()?),
100 Self::AppTimeReq(pl) => out.extend_from_slice(&pl.encode()?),
101 Self::AppTimeAns(pl) => out.extend_from_slice(&pl.encode()?),
102 Self::DeviceAppTimePeropdicityReq(pl) => out.extend_from_slice(&pl.encode()?),
103 Self::DeviceAppTimePeropdicityAns(pl) => out.extend_from_slice(&pl.encode()?),
104 Self::ForceDeviceResyncCmd(pl) => out.extend_from_slice(&pl.encode()?),
105 };
106
107 Ok(out)
108 }
109}
110
111#[derive(Debug, PartialEq, Clone)]
112pub struct PackageVersionAnsPayload {
113 pub package_identifier: u8,
114 pub package_version: u8,
115}
116
117impl PayloadCodec for PackageVersionAnsPayload {
118 fn decode(b: &[u8]) -> Result<Self> {
119 if b.len() != 2 {
120 return Err(anyhow!("Expected 2 bytes"));
121 }
122
123 Ok(PackageVersionAnsPayload {
124 package_identifier: b[0],
125 package_version: b[1],
126 })
127 }
128 fn encode(&self) -> Result<Vec<u8>> {
129 Ok(vec![self.package_identifier, self.package_version])
130 }
131}
132
133#[derive(Debug, PartialEq, Clone)]
134pub struct AppTimeReqPayload {
135 pub device_time: u32,
136 pub param: AppTimeReqPayloadParam,
137}
138
139impl PayloadCodec for AppTimeReqPayload {
140 fn decode(b: &[u8]) -> Result<Self> {
141 if b.len() != 5 {
142 return Err(anyhow!("Expected 5 bytes"));
143 }
144
145 Ok(AppTimeReqPayload {
146 device_time: {
147 let mut bytes = [0; 4];
148 bytes.copy_from_slice(&b[0..4]);
149 u32::from_le_bytes(bytes)
150 },
151 param: AppTimeReqPayloadParam {
152 token_req: b[4] & 0x0f,
153 ans_required: b[4] & 0x10 != 0,
154 },
155 })
156 }
157
158 fn encode(&self) -> Result<Vec<u8>> {
159 if self.param.token_req > 15 {
160 return Err(anyhow!("Max token_req value is 15"));
161 }
162
163 let mut b = vec![0; 5];
164 b[0..4].copy_from_slice(&self.device_time.to_le_bytes());
165 b[4] = self.param.token_req;
166
167 if self.param.ans_required {
168 b[4] |= 0x10;
169 }
170
171 Ok(b)
172 }
173}
174
175#[derive(Debug, PartialEq, Clone)]
176pub struct AppTimeReqPayloadParam {
177 pub token_req: u8,
178 pub ans_required: bool,
179}
180
181#[derive(Debug, PartialEq, Clone)]
182pub struct AppTimeAnsPayload {
183 pub time_correction: i32,
184 pub param: AppTimeAnsPayloadParam,
185}
186
187impl PayloadCodec for AppTimeAnsPayload {
188 fn decode(b: &[u8]) -> Result<Self> {
189 if b.len() != 5 {
190 return Err(anyhow!("Expected 5 bytes"));
191 }
192
193 Ok(AppTimeAnsPayload {
194 time_correction: {
195 let mut bytes = [0; 4];
196 bytes.copy_from_slice(&b[0..4]);
197 i32::from_le_bytes(bytes)
198 },
199 param: AppTimeAnsPayloadParam {
200 token_ans: b[4] & 0x0f,
201 },
202 })
203 }
204
205 fn encode(&self) -> Result<Vec<u8>> {
206 if self.param.token_ans > 15 {
207 return Err(anyhow!("Max token_ans value is 15"));
208 }
209
210 let mut b = vec![0; 5];
211 b[0..4].copy_from_slice(&self.time_correction.to_le_bytes());
212 b[4] = self.param.token_ans;
213
214 Ok(b)
215 }
216}
217
218#[derive(Debug, PartialEq, Clone)]
219pub struct AppTimeAnsPayloadParam {
220 pub token_ans: u8,
221}
222
223#[derive(Debug, PartialEq, Clone)]
224pub struct DeviceAppTimePeriodicityReqPayload {
225 pub period: u8,
226}
227
228impl PayloadCodec for DeviceAppTimePeriodicityReqPayload {
229 fn decode(b: &[u8]) -> Result<Self> {
230 if b.len() != 1 {
231 return Err(anyhow!("Expected 1 byte"));
232 }
233
234 Ok(DeviceAppTimePeriodicityReqPayload {
235 period: b[0] & 0x0f,
236 })
237 }
238
239 fn encode(&self) -> Result<Vec<u8>> {
240 if self.period > 15 {
241 return Err(anyhow!("Max period value is 15"));
242 }
243
244 Ok(vec![self.period])
245 }
246}
247
248#[derive(Debug, PartialEq, Clone)]
249pub struct DeviceAppTimePeriodicityAnsPayload {
250 pub status: DeviceAppTimePeriodicityAnsPayloadStatus,
251 pub time: u32,
252}
253
254impl PayloadCodec for DeviceAppTimePeriodicityAnsPayload {
255 fn decode(b: &[u8]) -> Result<Self> {
256 if b.len() != 5 {
257 return Err(anyhow!("Expected 5 bytes"));
258 }
259
260 Ok(DeviceAppTimePeriodicityAnsPayload {
261 status: DeviceAppTimePeriodicityAnsPayloadStatus {
262 not_supported: b[0] & 0x01 != 0,
263 },
264 time: {
265 let mut bytes = [0; 4];
266 bytes.copy_from_slice(&b[1..5]);
267 u32::from_le_bytes(bytes)
268 },
269 })
270 }
271
272 fn encode(&self) -> Result<Vec<u8>> {
273 let mut b = vec![0; 5];
274 if self.status.not_supported {
275 b[0] |= 0x01;
276 }
277
278 b[1..5].copy_from_slice(&self.time.to_le_bytes());
279 Ok(b)
280 }
281}
282
283#[derive(Debug, PartialEq, Clone)]
284pub struct DeviceAppTimePeriodicityAnsPayloadStatus {
285 pub not_supported: bool,
286}
287
288#[derive(Debug, PartialEq, Clone)]
289pub struct ForceDeviceResyncCmdPayload {
290 pub force_conf: ForceDeviceResyncCmdPayloadForceConf,
291}
292
293impl PayloadCodec for ForceDeviceResyncCmdPayload {
294 fn decode(b: &[u8]) -> Result<Self> {
295 if b.len() != 1 {
296 return Err(anyhow!("Expected 1 byte"));
297 }
298
299 Ok(ForceDeviceResyncCmdPayload {
300 force_conf: ForceDeviceResyncCmdPayloadForceConf {
301 nb_transmissions: b[0] & 0x07,
302 },
303 })
304 }
305
306 fn encode(&self) -> Result<Vec<u8>> {
307 if self.force_conf.nb_transmissions > 7 {
308 return Err(anyhow!("Max nb_transmissions is 7"));
309 }
310
311 Ok(vec![self.force_conf.nb_transmissions])
312 }
313}
314
315#[derive(Debug, PartialEq, Clone)]
316pub struct ForceDeviceResyncCmdPayloadForceConf {
317 pub nb_transmissions: u8,
318}
319
320#[cfg(test)]
321mod test {
322 use super::*;
323
324 struct CommandTest {
325 name: String,
326 uplink: bool,
327 command: Payload,
328 bytes: Vec<u8>,
329 expected_error: Option<String>,
330 }
331
332 #[test]
333 fn test_package_version_req() {
334 let encode_tests = [CommandTest {
335 name: "encode PackageVersionReq".into(),
336 uplink: false,
337 command: Payload::PackageVersionReq,
338 bytes: vec![0x00],
339 expected_error: None,
340 }];
341
342 let decode_tests = [CommandTest {
343 name: "decode PackageVersionReq".into(),
344 uplink: false,
345 command: Payload::PackageVersionReq,
346 bytes: vec![0x00],
347 expected_error: None,
348 }];
349
350 run_tests_encode(&encode_tests);
351 run_tests_decode(&decode_tests);
352 }
353
354 #[test]
355 fn test_package_version_ans() {
356 let encode_tests = [CommandTest {
357 name: "encode PackageVersionAns".into(),
358 uplink: true,
359 command: Payload::PackageVersionAns(PackageVersionAnsPayload {
360 package_identifier: 1,
361 package_version: 1,
362 }),
363 bytes: vec![0x00, 0x01, 0x01],
364 expected_error: None,
365 }];
366
367 let decode_tests = [CommandTest {
368 name: "decode PackageVersionAns".into(),
369 uplink: true,
370 command: Payload::PackageVersionAns(PackageVersionAnsPayload {
371 package_identifier: 1,
372 package_version: 1,
373 }),
374 bytes: vec![0x00, 0x01, 0x01],
375 expected_error: None,
376 }];
377
378 run_tests_encode(&encode_tests);
379 run_tests_decode(&decode_tests);
380 }
381
382 #[test]
383 fn test_app_time_req() {
384 let encode_tests = [CommandTest {
385 name: "encode AppTimeReq".into(),
386 uplink: true,
387 command: Payload::AppTimeReq(AppTimeReqPayload {
388 device_time: 1024,
389 param: AppTimeReqPayloadParam {
390 token_req: 15,
391 ans_required: true,
392 },
393 }),
394 bytes: vec![0x01, 0x00, 0x04, 0x00, 0x00, 0x1f],
395 expected_error: None,
396 }];
397
398 let decode_tests = [CommandTest {
399 name: "decode AppTimeReq".into(),
400 uplink: true,
401 command: Payload::AppTimeReq(AppTimeReqPayload {
402 device_time: 1024,
403 param: AppTimeReqPayloadParam {
404 token_req: 15,
405 ans_required: true,
406 },
407 }),
408 bytes: vec![0x01, 0x00, 0x04, 0x00, 0x00, 0x1f],
409 expected_error: None,
410 }];
411
412 run_tests_encode(&encode_tests);
413 run_tests_decode(&decode_tests);
414 }
415
416 #[test]
417 fn test_app_time_ans() {
418 let encode_tests = [CommandTest {
419 name: "encode AppTimeAns".into(),
420 uplink: false,
421 command: Payload::AppTimeAns(AppTimeAnsPayload {
422 time_correction: 1024,
423 param: AppTimeAnsPayloadParam { token_ans: 15 },
424 }),
425 bytes: vec![0x01, 0x00, 0x04, 0x00, 0x00, 0x0f],
426 expected_error: None,
427 }];
428
429 let decode_tests = [CommandTest {
430 name: "decode AppTimeAns".into(),
431 uplink: false,
432 command: Payload::AppTimeAns(AppTimeAnsPayload {
433 time_correction: 1024,
434 param: AppTimeAnsPayloadParam { token_ans: 15 },
435 }),
436 bytes: vec![0x01, 0x00, 0x04, 0x00, 0x00, 0x0f],
437 expected_error: None,
438 }];
439
440 run_tests_encode(&encode_tests);
441 run_tests_decode(&decode_tests);
442 }
443
444 #[test]
445 fn test_device_app_time_periodicity_req() {
446 let encode_tests = [CommandTest {
447 name: "encode DeviceAppTimePeropdicityReq".into(),
448 uplink: false,
449 command: Payload::DeviceAppTimePeropdicityReq(DeviceAppTimePeriodicityReqPayload {
450 period: 15,
451 }),
452 bytes: vec![0x02, 0x0f],
453 expected_error: None,
454 }];
455
456 let decode_tests = [CommandTest {
457 name: "decode DeviceAppTimePeropdicityReq".into(),
458 uplink: false,
459 command: Payload::DeviceAppTimePeropdicityReq(DeviceAppTimePeriodicityReqPayload {
460 period: 15,
461 }),
462 bytes: vec![0x02, 0x0f],
463 expected_error: None,
464 }];
465
466 run_tests_encode(&encode_tests);
467 run_tests_decode(&decode_tests);
468 }
469
470 #[test]
471 fn test_device_app_time_periodicity_ans() {
472 let encode_tests = [CommandTest {
473 name: "encode DeviceAppTimePeropdicityAns".into(),
474 uplink: true,
475 command: Payload::DeviceAppTimePeropdicityAns(DeviceAppTimePeriodicityAnsPayload {
476 status: DeviceAppTimePeriodicityAnsPayloadStatus {
477 not_supported: true,
478 },
479 time: 1024,
480 }),
481 bytes: vec![0x02, 0x01, 0x00, 0x04, 0x00, 0x00],
482 expected_error: None,
483 }];
484
485 let decode_tests = [CommandTest {
486 name: "decode DeviceAppTimePeropdicityAns".into(),
487 uplink: true,
488 command: Payload::DeviceAppTimePeropdicityAns(DeviceAppTimePeriodicityAnsPayload {
489 status: DeviceAppTimePeriodicityAnsPayloadStatus {
490 not_supported: true,
491 },
492 time: 1024,
493 }),
494 bytes: vec![0x02, 0x01, 0x00, 0x04, 0x00, 0x00],
495 expected_error: None,
496 }];
497
498 run_tests_encode(&encode_tests);
499 run_tests_decode(&decode_tests);
500 }
501
502 #[test]
503 fn test_force_device_resync_req() {
504 let encode_tests = [CommandTest {
505 name: "encode ForceDeviceResyncCmd".into(),
506 uplink: false,
507 command: Payload::ForceDeviceResyncCmd(ForceDeviceResyncCmdPayload {
508 force_conf: ForceDeviceResyncCmdPayloadForceConf {
509 nb_transmissions: 7,
510 },
511 }),
512 bytes: vec![0x03, 0x07],
513 expected_error: None,
514 }];
515
516 let decode_tests = [CommandTest {
517 name: "decode ForceDeviceResyncCmd".into(),
518 uplink: false,
519 command: Payload::ForceDeviceResyncCmd(ForceDeviceResyncCmdPayload {
520 force_conf: ForceDeviceResyncCmdPayloadForceConf {
521 nb_transmissions: 7,
522 },
523 }),
524 bytes: vec![0x03, 0x07],
525 expected_error: None,
526 }];
527
528 run_tests_encode(&encode_tests);
529 run_tests_decode(&decode_tests);
530 }
531
532 fn run_tests_encode(tests: &[CommandTest]) {
533 for tst in tests {
534 println!("> {}", tst.name);
535 let resp = tst.command.to_vec();
536 if let Some(e) = &tst.expected_error {
537 assert!(resp.is_err());
538 assert_eq!(e, &resp.err().unwrap().to_string());
539 } else {
540 assert_eq!(tst.bytes, resp.unwrap());
541 }
542 }
543 }
544
545 fn run_tests_decode(tests: &[CommandTest]) {
546 for tst in tests {
547 println!("> {}", tst.name);
548 let resp = Payload::from_slice(tst.uplink, &tst.bytes);
549 if let Some(e) = &tst.expected_error {
550 assert!(resp.is_err());
551 assert_eq!(e, &resp.err().unwrap().to_string());
552 } else {
553 assert_eq!(tst.command, resp.unwrap());
554 }
555 }
556 }
557}