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
use crate::{
    error::AUTDInternalError,
    firmware::operation::{Operation, TypeTag},
    geometry::Device,
};

use super::cast;

#[repr(u8)]
pub enum FirmwareVersionType {
    CPUVersionMajor = 0x01,
    CPUVersionMinor = 0x02,
    FPGAVersionMajor = 0x03,
    FPGAVersionMinor = 0x04,
    FPGAFunctions = 0x05,
    Clear = 0x06,
}

#[repr(C, align(2))]
struct FirmInfo {
    tag: TypeTag,
    ty: FirmwareVersionType,
}

pub struct FirmInfoOp {
    remains: usize,
}

impl Default for FirmInfoOp {
    fn default() -> Self {
        Self { remains: 6 }
    }
}

impl Operation for FirmInfoOp {
    fn pack(&mut self, _: &Device, tx: &mut [u8]) -> Result<usize, AUTDInternalError> {
        *cast::<FirmInfo>(tx) = FirmInfo {
            tag: TypeTag::FirmwareVersion,
            ty: match self.remains {
                6 => FirmwareVersionType::CPUVersionMajor,
                5 => FirmwareVersionType::CPUVersionMinor,
                4 => FirmwareVersionType::FPGAVersionMajor,
                3 => FirmwareVersionType::FPGAVersionMinor,
                2 => FirmwareVersionType::FPGAFunctions,
                1 => FirmwareVersionType::Clear,
                _ => unreachable!(),
            },
        };

        self.remains -= 1;
        Ok(std::mem::size_of::<FirmInfo>())
    }

    fn required_size(&self, _: &Device) -> usize {
        std::mem::size_of::<FirmInfo>()
    }

    fn is_done(&self) -> bool {
        self.remains == 0
    }
}

#[cfg(test)]
mod tests {
    use std::mem::size_of;

    use super::*;
    use crate::geometry::tests::create_device;

    const NUM_TRANS_IN_UNIT: usize = 249;

    #[test]
    fn test() {
        let device = create_device(0, NUM_TRANS_IN_UNIT);

        let mut tx = [0x00u8; size_of::<FirmInfo>()];

        let mut op = FirmInfoOp::default();

        assert_eq!(op.required_size(&device), size_of::<FirmInfo>());
        assert_eq!(op.remains, 6);

        assert!(op.pack(&device, &mut tx).is_ok());
        assert_eq!(op.remains, 5);
        assert_eq!(tx[0], TypeTag::FirmwareVersion as u8);
        assert_eq!(tx[1], FirmwareVersionType::CPUVersionMajor as u8);

        assert!(op.pack(&device, &mut tx).is_ok());
        assert_eq!(op.remains, 4);
        assert_eq!(tx[0], TypeTag::FirmwareVersion as u8);
        assert_eq!(tx[1], FirmwareVersionType::CPUVersionMinor as u8);

        assert!(op.pack(&device, &mut tx).is_ok());
        assert_eq!(op.remains, 3);
        assert_eq!(tx[0], TypeTag::FirmwareVersion as u8);
        assert_eq!(tx[1], FirmwareVersionType::FPGAVersionMajor as u8);

        assert!(op.pack(&device, &mut tx).is_ok());
        assert_eq!(op.remains, 2);
        assert_eq!(tx[0], TypeTag::FirmwareVersion as u8);
        assert_eq!(tx[1], FirmwareVersionType::FPGAVersionMinor as u8);

        assert!(op.pack(&device, &mut tx).is_ok());
        assert_eq!(op.remains, 1);
        assert_eq!(tx[0], TypeTag::FirmwareVersion as u8);
        assert_eq!(tx[1], FirmwareVersionType::FPGAFunctions as u8);

        assert!(op.pack(&device, &mut tx).is_ok());
        assert!(op.is_done());
        assert_eq!(tx[0], TypeTag::FirmwareVersion as u8);
        assert_eq!(tx[1], FirmwareVersionType::Clear as u8);
    }

    #[test]
    #[should_panic]
    fn test_panic() {
        let device = create_device(0, NUM_TRANS_IN_UNIT);
        let mut tx = [0x00u8; size_of::<FirmInfo>()];

        let mut op = FirmInfoOp::default();

        (0..7).for_each(|_| {
            assert!(op.pack(&device, &mut tx[0..]).is_ok());
        });
    }
}