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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
use bytes::BufMut;

use crate::put_esta_manufacturer_code;

const OP_POLL_REPLY: u16 = 0x2100;

#[derive(Debug)]
pub struct PollReply<'a> {
    pub ip_address: &'a [u8; 4],
    pub port: u16,
    pub firmware_version: u16,
    // Note: I am less sure about how the following fields work in practice. If there are ergonomic improvements to be had I am open to Pull Requests:
    /// Bits 14-8 of the 15 bit Port-Address are encoded into the bottom 7 bits of this field.
    pub net_switch: u8,
    /// Bits 7-4 of the 15 bit Port-Address are encoded into the bottom 4 bits of this field.
    pub sub_switch: u8,
    /// The Oem word describes the equipment vendor and the feature set available. Bit 15 high indicates extended features available.
    pub oem: u16,
    /// The firmware version of the User Bios Extension Area (UBEA) If the UBEA is not programmed, this field contains zero.
    pub ubea_version: u8,
    /// General Status register containing bit fields as follows:
    /// 7-6 Indicator state.
    ///     00 Indicator state unknown.
    ///     01 Indicators in Locate / Identify Mode.
    ///     10 Indicators in Mute Mode.
    ///     11 Indicators in Normal Mode.
    /// 5-4 Port-Address Programming Authority
    ///     00 Port-Address Programming Authority unknown.
    ///     01 All Port-Address set by front panel controls.
    ///     10 All or part of Port-Address programmed by network or Web browser.
    ///     11 Not used.
    /// 3 Not implemented, transmit as zero, receivers do not test.
    /// 2
    ///     0 = Normal firmware boot (from flash). Nodes that do not support dual boot, clear this field to zero.
    ///     1 = Booted from ROM.
    /// 1
    ///     0 = Not capable of Remote Device Management (RDM).
    ///     1 = Capable of Remote Device Management (RDM).
    /// 0
    ///     0 = UBEA not present or corrupt
    ///     1 = UBEA present
    pub status1: u8,
    pub esta_manufacturer_code: crate::ESTAManufacturerCode,
    /// Note: The spec specifies ASCII characters only
    pub short_name: &'a str,
    /// Note: The spec specifies ASCII characters only
    pub long_name: &'a str,
    /// Note: The spec specifies ASCII characters only
    pub node_report: &'a str,
    pub num_ports: u16,
    pub port_types: &'a [u8; 4],
    pub good_input: &'a [u8; 4],
    pub good_output_a: &'a [u8; 4],
    pub swin: &'a [u8; 4],
    pub swout: &'a [u8; 4],
    pub acn_priority: u8,
    pub sw_macro: u8,
    pub sw_remote: u8,
    pub style: u8,
    pub mac_address: &'a [u8; 6],
    pub bind_ip_address: &'a [u8; 4],
    pub bind_index: u8,
    pub status2: u8,
    pub good_output_b: &'a [u8; 4],
    pub status3: u8,
    /// RDMnet & LLRP Default Responder UID
    pub default_responder_uid: &'a [u8; 6],
}

impl<'a> Default for PollReply<'a> {
    fn default() -> Self {
        Self {
            ip_address: crate::DEFAULT_4_BYTES,
            port: Default::default(),
            firmware_version: Default::default(),
            net_switch: Default::default(),
            sub_switch: Default::default(),
            oem: Default::default(),
            ubea_version: Default::default(),
            status1: 0b1100_0000, // Indicator Mode: Normal
            esta_manufacturer_code: Default::default(),
            short_name: Default::default(),
            long_name: Default::default(),
            node_report: Default::default(),
            num_ports: Default::default(),
            port_types: crate::DEFAULT_4_BYTES,
            good_input: crate::DEFAULT_4_BYTES,
            good_output_a: crate::DEFAULT_4_BYTES,
            swin: crate::DEFAULT_4_BYTES,
            swout: crate::DEFAULT_4_BYTES,
            acn_priority: Default::default(),
            sw_macro: Default::default(),
            sw_remote: Default::default(),
            style: Default::default(),
            mac_address: crate::DEFAULT_6_BYTES,
            bind_ip_address: crate::DEFAULT_4_BYTES,
            bind_index: Default::default(),
            status2: Default::default(),
            good_output_b: crate::DEFAULT_4_BYTES,
            status3: Default::default(),
            default_responder_uid: crate::DEFAULT_6_BYTES,
        }
    }
}

impl<'a> PollReply<'a> {
    /// Serializes the PollReply into the provided buffer.
    ///
    /// Note: short name, long name and report will be truncated to 18, 64, and 64 bytes respectively
    pub fn serialize(&self, mut buf: &mut [u8]) -> usize {
        let initial_buf_len = buf.len();

        buf.put_slice(crate::ID);
        buf.put_u16_le(OP_POLL_REPLY);
        buf.put_slice(self.ip_address);
        buf.put_u16_le(self.port);
        buf.put_u16(self.firmware_version);
        buf.put_u8(self.net_switch);
        buf.put_u8(self.sub_switch);
        buf.put_u16(self.oem);
        buf.put_u8(self.ubea_version);
        buf.put_u8(self.status1);
        put_esta_manufacturer_code(&mut buf, &self.esta_manufacturer_code);

        crate::put_padded_str::<18, _>(&mut buf, &self.short_name);
        crate::put_padded_str::<64, _>(&mut buf, &self.long_name);
        crate::put_padded_str::<64, _>(&mut buf, &self.node_report);

        buf.put_u16(self.num_ports);
        buf.put_slice(self.port_types);
        buf.put_slice(self.good_input);
        buf.put_slice(self.good_output_a);
        buf.put_slice(self.swin);
        buf.put_slice(self.swout);
        buf.put_u8(self.acn_priority);
        buf.put_u8(self.sw_macro);
        buf.put_u8(self.sw_remote);
        // Spare
        buf.put_slice(&[0u8; 3]);
        buf.put_u8(self.style);
        buf.put_slice(self.mac_address);
        buf.put_slice(self.bind_ip_address);
        buf.put_u8(self.bind_index);
        buf.put_u8(self.status2);
        buf.put_slice(self.good_output_b);
        buf.put_u8(self.status3);
        buf.put_slice(self.default_responder_uid);
        // Filler
        buf.put_slice(&[0u8; 15]);

        return initial_buf_len - buf.len();
    }
}

// TODO: Poll Reply Parser
// pub fn from_str<'a>(s: &'a [u8]) -> Result<PollReply<'a>, crate::Error<'a>> {
// }