Skip to main content

lrwn/applayer/fragmentation/
v1.rs

1use anyhow::Result;
2
3use crate::applayer::PayloadCodec;
4
5pub enum Cid {
6    PackageVersionReq,
7    PackageVersionAns,
8    FragSessionStatusReq,
9    FragSessionStatusAns,
10    FragSessionSetupReq,
11    FragSessionSetupAns,
12    FragSessionDeleteReq,
13    FragSessionDeleteAns,
14    DataFragment,
15}
16
17impl Cid {
18    pub fn from_u8(uplink: bool, value: u8) -> Result<Self> {
19        Ok(match uplink {
20            true => match value {
21                0x00 => Cid::PackageVersionAns,
22                0x01 => Cid::FragSessionStatusAns,
23                0x02 => Cid::FragSessionSetupAns,
24                0x03 => Cid::FragSessionDeleteAns,
25                _ => return Err(anyhow!("Invalid CID: {}", value)),
26            },
27            false => match value {
28                0x00 => Cid::PackageVersionReq,
29                0x01 => Cid::FragSessionStatusReq,
30                0x02 => Cid::FragSessionSetupReq,
31                0x03 => Cid::FragSessionDeleteReq,
32                0x08 => Cid::DataFragment,
33                _ => return Err(anyhow!("Invalid CID: {}", value)),
34            },
35        })
36    }
37
38    pub fn to_u8(&self) -> u8 {
39        match self {
40            Cid::PackageVersionReq | Cid::PackageVersionAns => 0x00,
41            Cid::FragSessionStatusReq | Cid::FragSessionStatusAns => 0x01,
42            Cid::FragSessionSetupReq | Cid::FragSessionSetupAns => 0x02,
43            Cid::FragSessionDeleteReq | Cid::FragSessionDeleteAns => 0x03,
44            Cid::DataFragment => 0x08,
45        }
46    }
47}
48
49#[derive(Debug, PartialEq)]
50pub enum Payload {
51    PackageVersionReq,
52    PackageVersionAns(PackageVersionAnsPayload),
53    FragSessionStatusReq(FragSessionStatusReqPayload),
54    FragSessionStatusAns(FragSessionStatusAnsPayload),
55    FragSessionSetupReq(FragSessionSetupReqPayload),
56    FragSessionSetupAns(FragSessionSetupAnsPayload),
57    FragSessionDeleteReq(FragSessionDeleteReqPayload),
58    FragSessionDeleteAns(FragSessionDeleteAnsPayload),
59    DataFragment(DataFragmentPayload),
60}
61
62impl Payload {
63    pub fn cid(&self) -> Cid {
64        match self {
65            Self::PackageVersionReq => Cid::PackageVersionReq,
66            Self::PackageVersionAns(_) => Cid::PackageVersionAns,
67            Self::FragSessionStatusReq(_) => Cid::FragSessionStatusReq,
68            Self::FragSessionStatusAns(_) => Cid::FragSessionStatusAns,
69            Self::FragSessionSetupReq(_) => Cid::FragSessionSetupReq,
70            Self::FragSessionSetupAns(_) => Cid::FragSessionSetupAns,
71            Self::FragSessionDeleteReq(_) => Cid::FragSessionDeleteReq,
72            Self::FragSessionDeleteAns(_) => Cid::FragSessionDeleteAns,
73            Self::DataFragment(_) => Cid::DataFragment,
74        }
75    }
76
77    pub fn from_slice(uplink: bool, b: &[u8]) -> Result<Self> {
78        if b.is_empty() {
79            return Err(anyhow!("At least one byte is expected"));
80        }
81
82        let cid = Cid::from_u8(uplink, b[0])?;
83
84        Ok(match cid {
85            Cid::PackageVersionReq => Payload::PackageVersionReq,
86            Cid::PackageVersionAns => {
87                Payload::PackageVersionAns(PackageVersionAnsPayload::decode(&b[1..])?)
88            }
89            Cid::FragSessionStatusReq => {
90                Payload::FragSessionStatusReq(FragSessionStatusReqPayload::decode(&b[1..])?)
91            }
92            Cid::FragSessionStatusAns => {
93                Payload::FragSessionStatusAns(FragSessionStatusAnsPayload::decode(&b[1..])?)
94            }
95            Cid::FragSessionSetupReq => {
96                Payload::FragSessionSetupReq(FragSessionSetupReqPayload::decode(&b[1..])?)
97            }
98            Cid::FragSessionSetupAns => {
99                Payload::FragSessionSetupAns(FragSessionSetupAnsPayload::decode(&b[1..])?)
100            }
101            Cid::FragSessionDeleteReq => {
102                Payload::FragSessionDeleteReq(FragSessionDeleteReqPayload::decode(&b[1..])?)
103            }
104            Cid::FragSessionDeleteAns => {
105                Payload::FragSessionDeleteAns(FragSessionDeleteAnsPayload::decode(&b[1..])?)
106            }
107            Cid::DataFragment => Payload::DataFragment(DataFragmentPayload::decode(&b[1..])?),
108        })
109    }
110
111    pub fn to_vec(&self) -> Result<Vec<u8>> {
112        let mut out = vec![self.cid().to_u8()];
113
114        match self {
115            Self::PackageVersionReq => {}
116            Self::PackageVersionAns(pl) => out.extend_from_slice(&pl.encode()?),
117            Self::FragSessionStatusReq(pl) => out.extend_from_slice(&pl.encode()?),
118            Self::FragSessionStatusAns(pl) => out.extend_from_slice(&pl.encode()?),
119            Self::FragSessionSetupReq(pl) => out.extend_from_slice(&pl.encode()?),
120            Self::FragSessionSetupAns(pl) => out.extend_from_slice(&pl.encode()?),
121            Self::FragSessionDeleteReq(pl) => out.extend_from_slice(&pl.encode()?),
122            Self::FragSessionDeleteAns(pl) => out.extend_from_slice(&pl.encode()?),
123            Self::DataFragment(pl) => out.extend_from_slice(&pl.encode()?),
124        }
125
126        Ok(out)
127    }
128}
129
130#[derive(Debug, PartialEq)]
131pub struct PackageVersionAnsPayload {
132    pub package_identifier: u8,
133    pub package_version: u8,
134}
135
136impl PayloadCodec for PackageVersionAnsPayload {
137    fn decode(b: &[u8]) -> Result<Self> {
138        if b.len() != 2 {
139            return Err(anyhow!("Expected 2 bytes"));
140        }
141
142        Ok(PackageVersionAnsPayload {
143            package_identifier: b[0],
144            package_version: b[1],
145        })
146    }
147    fn encode(&self) -> Result<Vec<u8>> {
148        Ok(vec![self.package_identifier, self.package_version])
149    }
150}
151
152#[derive(Debug, PartialEq)]
153pub struct FragSessionStatusReqPayload {
154    pub participants: bool,
155    pub frag_index: u8,
156}
157
158impl PayloadCodec for FragSessionStatusReqPayload {
159    fn decode(b: &[u8]) -> Result<Self> {
160        if b.len() != 1 {
161            return Err(anyhow!("Expected 1 byte"));
162        }
163
164        Ok(FragSessionStatusReqPayload {
165            participants: b[0] & 0x01 != 0,
166            frag_index: (b[0] >> 1) & 0x03,
167        })
168    }
169
170    fn encode(&self) -> Result<Vec<u8>> {
171        if self.frag_index > 3 {
172            return Err(anyhow!("Max frag_index value is 3"));
173        }
174
175        let mut b = vec![self.frag_index << 1];
176        if self.participants {
177            b[0] |= 0x01;
178        }
179
180        Ok(b)
181    }
182}
183
184#[derive(Debug, PartialEq)]
185pub struct FragSessionStatusAnsPayload {
186    pub received_and_index: FragSessionStatusAnsPayloadReceivedAndIndex,
187    pub missing_frag: u8,
188    pub status: FragSessionStatusAnsPayloadStatus,
189}
190
191impl PayloadCodec for FragSessionStatusAnsPayload {
192    fn decode(b: &[u8]) -> Result<Self> {
193        if b.len() != 4 {
194            return Err(anyhow!("Expected 4 bytes"));
195        }
196
197        Ok(FragSessionStatusAnsPayload {
198            received_and_index: FragSessionStatusAnsPayloadReceivedAndIndex {
199                nb_frag_received: {
200                    let mut bytes = [0; 2];
201                    bytes.copy_from_slice(&b[0..2]);
202                    u16::from_le_bytes(bytes) & 0x3fff
203                },
204                frag_index: b[1] >> 6,
205            },
206            missing_frag: b[2],
207            status: FragSessionStatusAnsPayloadStatus {
208                not_enough_matrix_memory: b[3] & 0x01 != 0,
209            },
210        })
211    }
212
213    fn encode(&self) -> Result<Vec<u8>> {
214        if self.received_and_index.nb_frag_received > 16383 {
215            return Err(anyhow!("Max nb_frag_received value us 16383"));
216        }
217
218        if self.received_and_index.frag_index > 3 {
219            return Err(anyhow!("Max frag_index value is 3"));
220        }
221
222        let mut b = Vec::with_capacity(4);
223        b.extend_from_slice(&self.received_and_index.nb_frag_received.to_le_bytes());
224        b[1] |= self.received_and_index.frag_index << 6;
225
226        b.push(self.missing_frag);
227
228        b.push(0x00);
229        if self.status.not_enough_matrix_memory {
230            b[3] |= 0x01;
231        }
232
233        Ok(b)
234    }
235}
236
237#[derive(Debug, PartialEq)]
238pub struct FragSessionStatusAnsPayloadStatus {
239    pub not_enough_matrix_memory: bool,
240}
241
242#[derive(Debug, PartialEq)]
243pub struct FragSessionStatusAnsPayloadReceivedAndIndex {
244    pub nb_frag_received: u16,
245    pub frag_index: u8,
246}
247
248#[derive(Debug, PartialEq)]
249pub struct FragSessionSetupReqPayload {
250    pub frag_session: FragSessionSetuReqPayloadFragSession,
251    pub nb_frag: u16,
252    pub frag_size: u8,
253    pub control: FragSessionSetuReqPayloadControl,
254    pub padding: u8,
255    pub descriptor: [u8; 4],
256}
257
258impl PayloadCodec for FragSessionSetupReqPayload {
259    fn decode(b: &[u8]) -> Result<Self> {
260        if b.len() != 10 {
261            return Err(anyhow!("Expected 10 bytes"));
262        }
263
264        Ok(FragSessionSetupReqPayload {
265            frag_session: FragSessionSetuReqPayloadFragSession {
266                mc_group_bit_mask: {
267                    let mut mask = [false; 4];
268                    for (i, v) in mask.iter_mut().enumerate() {
269                        *v = b[0] & (1 << i) != 0;
270                    }
271                    mask
272                },
273                frag_index: (b[0] >> 4) & 0x03,
274            },
275            nb_frag: {
276                let mut bytes = [0; 2];
277                bytes.copy_from_slice(&b[1..3]);
278                u16::from_le_bytes(bytes)
279            },
280            frag_size: b[3],
281            control: FragSessionSetuReqPayloadControl {
282                block_ack_delay: b[4] & 0x07,
283                fragmentation_matrix: (b[4] >> 3) & 0x07,
284            },
285            padding: b[5],
286            descriptor: {
287                let mut bytes = [0; 4];
288                bytes.copy_from_slice(&b[6..10]);
289                bytes
290            },
291        })
292    }
293
294    fn encode(&self) -> Result<Vec<u8>> {
295        if self.frag_session.frag_index > 3 {
296            return Err(anyhow!("Max frag_index value is 3"));
297        }
298
299        if self.control.block_ack_delay > 7 {
300            return Err(anyhow!("Max block_ack_delay value is 7"));
301        }
302
303        if self.control.fragmentation_matrix > 7 {
304            return Err(anyhow!("Max fragmentation_matrix value is 7"));
305        }
306
307        let mut b = Vec::with_capacity(10);
308
309        b.push(self.frag_session.frag_index << 4);
310        for (i, v) in self.frag_session.mc_group_bit_mask.iter().enumerate() {
311            if *v {
312                b[0] |= 1 << i;
313            }
314        }
315        b.extend_from_slice(&self.nb_frag.to_le_bytes());
316        b.push(self.frag_size);
317        b.push(self.control.block_ack_delay | (self.control.fragmentation_matrix << 3));
318        b.push(self.padding);
319        b.extend_from_slice(&self.descriptor);
320
321        Ok(b)
322    }
323}
324
325#[derive(Debug, PartialEq)]
326pub struct FragSessionSetuReqPayloadFragSession {
327    pub mc_group_bit_mask: [bool; 4],
328    pub frag_index: u8,
329}
330
331#[derive(Debug, PartialEq)]
332pub struct FragSessionSetuReqPayloadControl {
333    pub block_ack_delay: u8,
334    pub fragmentation_matrix: u8,
335}
336
337#[derive(Debug, PartialEq)]
338pub struct FragSessionSetupAnsPayload {
339    pub encoding_unsupported: bool,
340    pub not_enough_memory: bool,
341    pub frag_session_index_not_supported: bool,
342    pub wrong_descriptor: bool,
343    pub frag_index: u8,
344}
345
346impl PayloadCodec for FragSessionSetupAnsPayload {
347    fn decode(b: &[u8]) -> Result<Self> {
348        if b.len() != 1 {
349            return Err(anyhow!("Expected 1 byte"));
350        }
351
352        Ok(FragSessionSetupAnsPayload {
353            encoding_unsupported: b[0] & 0x01 != 0,
354            not_enough_memory: b[0] & 0x02 != 0,
355            frag_session_index_not_supported: b[0] & 0x04 != 0,
356            wrong_descriptor: b[0] & 0x08 != 0,
357            frag_index: (b[0] >> 6),
358        })
359    }
360
361    fn encode(&self) -> Result<Vec<u8>> {
362        if self.frag_index > 3 {
363            return Err(anyhow!("Max frag_index value is 3"));
364        }
365
366        let mut b = vec![self.frag_index << 6];
367        if self.encoding_unsupported {
368            b[0] |= 0x01;
369        }
370        if self.not_enough_memory {
371            b[0] |= 0x02;
372        }
373        if self.frag_session_index_not_supported {
374            b[0] |= 0x04;
375        }
376        if self.wrong_descriptor {
377            b[0] |= 0x08;
378        }
379
380        Ok(b)
381    }
382}
383
384#[derive(Debug, PartialEq)]
385pub struct FragSessionDeleteReqPayload {
386    pub frag_index: u8,
387}
388
389impl PayloadCodec for FragSessionDeleteReqPayload {
390    fn decode(b: &[u8]) -> Result<Self> {
391        if b.len() != 1 {
392            return Err(anyhow!("Expected 1 byte"));
393        }
394
395        Ok(FragSessionDeleteReqPayload {
396            frag_index: b[0] & 0x03,
397        })
398    }
399
400    fn encode(&self) -> Result<Vec<u8>> {
401        if self.frag_index > 3 {
402            return Err(anyhow!("Max frag_index value is 3"));
403        }
404
405        Ok(vec![self.frag_index])
406    }
407}
408
409#[derive(Debug, PartialEq)]
410pub struct FragSessionDeleteAnsPayload {
411    pub frag_index: u8,
412    pub session_does_not_exist: bool,
413}
414
415impl PayloadCodec for FragSessionDeleteAnsPayload {
416    fn decode(b: &[u8]) -> Result<Self> {
417        if b.len() != 1 {
418            return Err(anyhow!("Expected 1 byte"));
419        }
420
421        Ok(FragSessionDeleteAnsPayload {
422            frag_index: b[0] & 0x03,
423            session_does_not_exist: b[0] & 0x04 != 0,
424        })
425    }
426
427    fn encode(&self) -> Result<Vec<u8>> {
428        if self.frag_index > 3 {
429            return Err(anyhow!("Max frag_index value is 3"));
430        }
431
432        let mut b = vec![self.frag_index];
433        if self.session_does_not_exist {
434            b[0] |= 0x04;
435        }
436
437        Ok(b)
438    }
439}
440
441#[derive(Debug, PartialEq)]
442pub struct DataFragmentPayload {
443    pub index_and_n: DataFragmentPayloadIndexAndN,
444    pub data: Vec<u8>,
445}
446
447impl PayloadCodec for DataFragmentPayload {
448    fn decode(b: &[u8]) -> Result<Self> {
449        if b.len() < 2 {
450            return Err(anyhow!("At least 2 bytes expected"));
451        }
452
453        Ok(DataFragmentPayload {
454            index_and_n: DataFragmentPayloadIndexAndN {
455                n: {
456                    let mut bytes = [0; 2];
457                    bytes.copy_from_slice(&b[0..2]);
458                    u16::from_le_bytes(bytes) & 0x3fff
459                },
460                frag_index: b[1] >> 6,
461            },
462            data: b[2..].to_vec(),
463        })
464    }
465
466    fn encode(&self) -> Result<Vec<u8>> {
467        if self.index_and_n.n > 16383 {
468            return Err(anyhow!("Max n value us 16383"));
469        }
470
471        if self.index_and_n.frag_index > 3 {
472            return Err(anyhow!("Max frag_index value is 3"));
473        }
474
475        let mut b = Vec::with_capacity(2 + self.data.len());
476        b.extend_from_slice(&self.index_and_n.n.to_le_bytes());
477        b[1] |= self.index_and_n.frag_index << 6;
478        b.extend_from_slice(&self.data);
479
480        Ok(b)
481    }
482}
483
484#[derive(Debug, PartialEq)]
485pub struct DataFragmentPayloadIndexAndN {
486    pub n: u16,
487    pub frag_index: u8,
488}
489
490// Encode the given slice of bytes to fragments including forward error correction.
491// This is based on the proposed FEC code from the Fragmented Data Block Transport over
492// LoRaWAN recommendation.
493pub fn encode(payload: &[u8], fragment_size: usize, redundancy: usize) -> Result<Vec<Vec<u8>>> {
494    if payload.len() % fragment_size != 0 {
495        return Err(anyhow!("Payload size must be a multiple of fragment_size"));
496    }
497
498    // fragment the data into rows
499    let mut data_rows: Vec<Vec<u8>> = payload.chunks(fragment_size).map(|v| v.to_vec()).collect();
500    let w = data_rows.len();
501
502    for y in 0..redundancy {
503        let mut s = vec![0; fragment_size];
504        let a = matrix_line(y + 1, w);
505
506        for x in 0..w {
507            if a[x] == 1 {
508                for (m, s_val) in s.iter_mut().enumerate() {
509                    *s_val ^= data_rows[x][m];
510                }
511            }
512        }
513
514        data_rows.push(s);
515    }
516
517    Ok(data_rows)
518}
519
520fn prbs23(x: usize) -> usize {
521    let b0 = x & 1;
522    let b1 = (x & 32) / 32;
523    (x / 2) + (b0 ^ b1) * (1 << 22)
524}
525
526fn is_power_2(num: usize) -> bool {
527    num != 0 && (num & (num - 1)) == 0
528}
529
530fn matrix_line(n: usize, m: usize) -> Vec<usize> {
531    let mut line = vec![0; m];
532
533    let mm = if is_power_2(m) { 1 } else { 0 };
534
535    let mut x = 1 + (1001 * n);
536
537    for _nb_coeff in 0..(m / 2) {
538        let mut r = 1 << 16;
539        while r >= m {
540            x = prbs23(x);
541            r = x % (m + mm);
542        }
543        line[r] = 1;
544    }
545
546    line
547}
548
549#[cfg(test)]
550mod test {
551    use super::*;
552
553    struct CommandTest {
554        name: String,
555        uplink: bool,
556        command: Payload,
557        bytes: Vec<u8>,
558        expected_error: Option<String>,
559    }
560
561    #[test]
562    fn test_package_version_req() {
563        let encode_tests = [CommandTest {
564            name: "encode PackageVersionReq".into(),
565            uplink: false,
566            command: Payload::PackageVersionReq,
567            bytes: vec![0x00],
568            expected_error: None,
569        }];
570
571        let decode_tests = [CommandTest {
572            name: "decode PackageVersionReq".into(),
573            uplink: false,
574            command: Payload::PackageVersionReq,
575            bytes: vec![0x00],
576            expected_error: None,
577        }];
578
579        run_tests_encode(&encode_tests);
580        run_tests_decode(&decode_tests);
581    }
582
583    #[test]
584    fn test_frag_session_status_req() {
585        let encode_tests = [CommandTest {
586            name: "encode FragSessionStatusReq".into(),
587            uplink: false,
588            command: Payload::FragSessionStatusReq(FragSessionStatusReqPayload {
589                participants: true,
590                frag_index: 2,
591            }),
592            bytes: vec![0x01, 0x05],
593            expected_error: None,
594        }];
595
596        let decode_tests = [CommandTest {
597            name: "decode FragSessionStatusReq".into(),
598            uplink: false,
599            command: Payload::FragSessionStatusReq(FragSessionStatusReqPayload {
600                participants: true,
601                frag_index: 2,
602            }),
603            bytes: vec![0x01, 0x05],
604            expected_error: None,
605        }];
606
607        run_tests_encode(&encode_tests);
608        run_tests_decode(&decode_tests);
609    }
610
611    #[test]
612    fn test_frag_session_status_ans() {
613        let encode_tests = [CommandTest {
614            name: "encode FragSessionStatusAns".into(),
615            uplink: true,
616            command: Payload::FragSessionStatusAns(FragSessionStatusAnsPayload {
617                received_and_index: FragSessionStatusAnsPayloadReceivedAndIndex {
618                    nb_frag_received: 1024,
619                    frag_index: 3,
620                },
621                missing_frag: 128,
622                status: FragSessionStatusAnsPayloadStatus {
623                    not_enough_matrix_memory: true,
624                },
625            }),
626            bytes: vec![0x01, 0x00, 0xc4, 0x80, 0x01],
627            expected_error: None,
628        }];
629
630        let decode_tests = [CommandTest {
631            name: "decode FragSessionStatusAns".into(),
632            uplink: true,
633            command: Payload::FragSessionStatusAns(FragSessionStatusAnsPayload {
634                received_and_index: FragSessionStatusAnsPayloadReceivedAndIndex {
635                    nb_frag_received: 1024,
636                    frag_index: 3,
637                },
638                missing_frag: 128,
639                status: FragSessionStatusAnsPayloadStatus {
640                    not_enough_matrix_memory: true,
641                },
642            }),
643            bytes: vec![0x01, 0x00, 0xc4, 0x80, 0x01],
644            expected_error: None,
645        }];
646
647        run_tests_encode(&encode_tests);
648        run_tests_decode(&decode_tests);
649    }
650
651    #[test]
652    fn test_frag_session_setup_req() {
653        let encode_tests = [CommandTest {
654            name: "encode FragSessionSetupReq".into(),
655            uplink: false,
656            command: Payload::FragSessionSetupReq(FragSessionSetupReqPayload {
657                frag_session: FragSessionSetuReqPayloadFragSession {
658                    mc_group_bit_mask: [true, false, false, false],
659                    frag_index: 3,
660                },
661                nb_frag: 1024,
662                frag_size: 128,
663                control: FragSessionSetuReqPayloadControl {
664                    block_ack_delay: 5,
665                    fragmentation_matrix: 1,
666                },
667                padding: 64,
668                descriptor: [0x01, 0x02, 0x03, 0x04],
669            }),
670            bytes: vec![
671                0x02, 0x31, 0x00, 0x04, 0x80, 0x0d, 0x40, 0x01, 0x02, 0x03, 0x04,
672            ],
673            expected_error: None,
674        }];
675
676        let decode_tests = [CommandTest {
677            name: "encode FragSessionSetupReq".into(),
678            uplink: false,
679            command: Payload::FragSessionSetupReq(FragSessionSetupReqPayload {
680                frag_session: FragSessionSetuReqPayloadFragSession {
681                    mc_group_bit_mask: [true, false, false, false],
682                    frag_index: 3,
683                },
684                nb_frag: 1024,
685                frag_size: 128,
686                control: FragSessionSetuReqPayloadControl {
687                    block_ack_delay: 5,
688                    fragmentation_matrix: 1,
689                },
690                padding: 64,
691                descriptor: [0x01, 0x02, 0x03, 0x04],
692            }),
693            bytes: vec![
694                0x02, 0x31, 0x00, 0x04, 0x80, 0x0d, 0x40, 0x01, 0x02, 0x03, 0x04,
695            ],
696            expected_error: None,
697        }];
698
699        run_tests_encode(&encode_tests);
700        run_tests_decode(&decode_tests);
701    }
702
703    #[test]
704    fn test_frag_session_setup_ans() {
705        let encode_tests = [CommandTest {
706            name: "encode FragSessionSetupAns".into(),
707            uplink: true,
708            command: Payload::FragSessionSetupAns(FragSessionSetupAnsPayload {
709                encoding_unsupported: true,
710                not_enough_memory: true,
711                frag_session_index_not_supported: false,
712                wrong_descriptor: true,
713                frag_index: 2,
714            }),
715            bytes: vec![0x02, 0x8B],
716            expected_error: None,
717        }];
718
719        let decode_tests = [CommandTest {
720            name: "decode FragSessionSetupAns".into(),
721            uplink: true,
722            command: Payload::FragSessionSetupAns(FragSessionSetupAnsPayload {
723                encoding_unsupported: true,
724                not_enough_memory: true,
725                frag_session_index_not_supported: false,
726                wrong_descriptor: true,
727                frag_index: 2,
728            }),
729            bytes: vec![0x02, 0x8B],
730            expected_error: None,
731        }];
732
733        run_tests_encode(&encode_tests);
734        run_tests_decode(&decode_tests);
735    }
736
737    #[test]
738    fn test_frag_session_delete_req() {
739        let encode_tests = [CommandTest {
740            name: "encode FragSessionDelete".into(),
741            uplink: false,
742            command: Payload::FragSessionDeleteReq(FragSessionDeleteReqPayload { frag_index: 3 }),
743            bytes: vec![0x03, 0x03],
744            expected_error: None,
745        }];
746
747        let decode_tests = [CommandTest {
748            name: "decode FragSessionDelete".into(),
749            uplink: false,
750            command: Payload::FragSessionDeleteReq(FragSessionDeleteReqPayload { frag_index: 3 }),
751            bytes: vec![0x03, 0x03],
752            expected_error: None,
753        }];
754
755        run_tests_encode(&encode_tests);
756        run_tests_decode(&decode_tests);
757    }
758
759    #[test]
760    fn test_frag_session_delete_ans() {
761        let encode_tests = [CommandTest {
762            name: "encode FragSessionDeleteAns".into(),
763            uplink: true,
764            command: Payload::FragSessionDeleteAns(FragSessionDeleteAnsPayload {
765                frag_index: 3,
766                session_does_not_exist: true,
767            }),
768            bytes: vec![0x03, 0x07],
769            expected_error: None,
770        }];
771
772        let decode_tests = [CommandTest {
773            name: "decode FragSessionDeleteAns".into(),
774            uplink: true,
775            command: Payload::FragSessionDeleteAns(FragSessionDeleteAnsPayload {
776                frag_index: 3,
777                session_does_not_exist: true,
778            }),
779            bytes: vec![0x03, 0x07],
780            expected_error: None,
781        }];
782
783        run_tests_encode(&encode_tests);
784        run_tests_decode(&decode_tests);
785    }
786
787    #[test]
788    fn test_data_fragment() {
789        let encode_tests = [CommandTest {
790            name: "encode DataFragment".into(),
791            uplink: false,
792            command: Payload::DataFragment(DataFragmentPayload {
793                index_and_n: DataFragmentPayloadIndexAndN {
794                    n: 1024,
795                    frag_index: 2,
796                },
797                data: vec![0x01, 0x02, 0x03, 0x04],
798            }),
799            bytes: vec![0x08, 0x00, 0x84, 0x01, 0x02, 0x03, 0x04],
800            expected_error: None,
801        }];
802
803        let decode_tests = [CommandTest {
804            name: "decode DataFragment".into(),
805            uplink: false,
806            command: Payload::DataFragment(DataFragmentPayload {
807                index_and_n: DataFragmentPayloadIndexAndN {
808                    n: 1024,
809                    frag_index: 2,
810                },
811                data: vec![0x01, 0x02, 0x03, 0x04],
812            }),
813            bytes: vec![0x08, 0x00, 0x84, 0x01, 0x02, 0x03, 0x04],
814            expected_error: None,
815        }];
816
817        run_tests_encode(&encode_tests);
818        run_tests_decode(&decode_tests);
819    }
820
821    #[test]
822    fn test_encode() {
823        struct EncodeTest {
824            name: String,
825            data: Vec<u8>,
826            fragment_size: usize,
827            redundancy: usize,
828            expected_fragments: Vec<Vec<u8>>,
829            expected_error: Option<String>,
830        }
831
832        let mut data = vec![0; 100];
833        for (i, v) in data.iter_mut().enumerate() {
834            *v = i as u8;
835        }
836
837        let tests = [
838            EncodeTest {
839                name: "invalid fragment size".into(),
840                data: vec![0; 5],
841                fragment_size: 10,
842                redundancy: 0,
843                expected_fragments: vec![],
844                expected_error: Some("Payload size must be a multiple of fragment_size".into()),
845            },
846            EncodeTest {
847                name: "fragment size 10, redundancy 10".into(),
848                data: data.clone(),
849                fragment_size: 10,
850                redundancy: 10,
851                expected_fragments: vec![
852                    vec![0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9],
853                    vec![0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10, 0x11, 0x12, 0x13],
854                    vec![0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d],
855                    vec![0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27],
856                    vec![0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31],
857                    vec![0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b],
858                    vec![0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45],
859                    vec![0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f],
860                    vec![0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59],
861                    vec![0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63],
862                    vec![0x26, 0x26, 0x22, 0x22, 0x2e, 0x2e, 0x22, 0x22, 0x26, 0x26],
863                    vec![0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x6a, 0x6b, 0x7c, 0x7d],
864                    vec![0x5c, 0x5d, 0x6e, 0x6f, 0x10, 0x11, 0x2, 0x3, 0x4, 0x5],
865                    vec![0x36, 0x36, 0x32, 0x32, 0x3e, 0x3e, 0x22, 0x22, 0x36, 0x36],
866                    vec![0x3a, 0x3a, 0xe, 0xe, 0xa, 0xa, 0x6, 0x6, 0xa, 0xa],
867                    vec![0xe, 0xe, 0x32, 0x32, 0x36, 0x36, 0x22, 0x22, 0x3e, 0x3e],
868                    vec![0x2, 0x2, 0xe, 0xe, 0x72, 0x72, 0x76, 0x76, 0x62, 0x62],
869                    vec![0x1e, 0x1e, 0x1a, 0x1a, 0x66, 0x66, 0x5a, 0x5a, 0x4e, 0x4e],
870                    vec![0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x30, 0x31, 0x22, 0x23],
871                    vec![0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x18, 0x19, 0xa, 0xb],
872                ],
873                expected_error: None,
874            },
875            EncodeTest {
876                name: "fragment size 10, redundancy 5".into(),
877                data: data.clone(),
878                fragment_size: 10,
879                redundancy: 5,
880                expected_fragments: vec![
881                    vec![0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9],
882                    vec![0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10, 0x11, 0x12, 0x13],
883                    vec![0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d],
884                    vec![0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27],
885                    vec![0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31],
886                    vec![0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b],
887                    vec![0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45],
888                    vec![0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f],
889                    vec![0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59],
890                    vec![0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63],
891                    vec![0x26, 0x26, 0x22, 0x22, 0x2e, 0x2e, 0x22, 0x22, 0x26, 0x26],
892                    vec![0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x6a, 0x6b, 0x7c, 0x7d],
893                    vec![0x5c, 0x5d, 0x6e, 0x6f, 0x10, 0x11, 0x2, 0x3, 0x4, 0x5],
894                    vec![0x36, 0x36, 0x32, 0x32, 0x3e, 0x3e, 0x22, 0x22, 0x36, 0x36],
895                    vec![0x3a, 0x3a, 0xe, 0xe, 0xa, 0xa, 0x6, 0x6, 0xa, 0xa],
896                ],
897                expected_error: None,
898            },
899        ];
900
901        for tst in &tests {
902            println!("> {}", tst.name);
903
904            let res = encode(&tst.data, tst.fragment_size, tst.redundancy);
905            if let Some(e) = &tst.expected_error {
906                assert_eq!(e, &res.err().unwrap().to_string());
907            } else {
908                assert_eq!(tst.expected_fragments, res.unwrap());
909            }
910        }
911    }
912
913    fn run_tests_encode(tests: &[CommandTest]) {
914        for tst in tests {
915            println!("> {}", tst.name);
916            let resp = tst.command.to_vec();
917            if let Some(e) = &tst.expected_error {
918                assert!(resp.is_err());
919                assert_eq!(e, &resp.err().unwrap().to_string());
920            } else {
921                assert_eq!(tst.bytes, resp.unwrap());
922            }
923        }
924    }
925
926    fn run_tests_decode(tests: &[CommandTest]) {
927        for tst in tests {
928            println!("> {}", tst.name);
929            let resp = Payload::from_slice(tst.uplink, &tst.bytes);
930            if let Some(e) = &tst.expected_error {
931                assert!(resp.is_err());
932                assert_eq!(e, &resp.err().unwrap().to_string());
933            } else {
934                assert_eq!(tst.command, resp.unwrap());
935            }
936        }
937    }
938}