Skip to main content

sig_net/
tlv.rs

1#![allow(clippy::too_many_arguments)]
2use crate::*;
3
4pub fn encode_tlv(buffer: &mut PacketBuffer, tlv: &TLVBlock) -> Result<()> {
5    buffer.write_u16(tlv.type_id)?;
6    buffer.write_u16(tlv.length())?;
7    buffer.write_bytes(tlv.value)
8}
9
10pub fn encode_tid_level(buffer: &mut PacketBuffer, dmx_data: &[u8]) -> Result<()> {
11    if dmx_data.is_empty() || dmx_data.len() > MAX_DMX_SLOTS as usize {
12        return Err(SigNetError::InvalidArgument);
13    }
14    let tlv = TLVBlock {
15        type_id: TID_LEVEL,
16        value: dmx_data,
17    };
18    encode_tlv(buffer, &tlv)
19}
20
21pub fn encode_tid_priority(buffer: &mut PacketBuffer, priority_data: &[u8]) -> Result<()> {
22    if priority_data.is_empty() || priority_data.len() > MAX_DMX_SLOTS as usize {
23        return Err(SigNetError::InvalidArgument);
24    }
25    let tlv = TLVBlock {
26        type_id: TID_PRIORITY,
27        value: priority_data,
28    };
29    encode_tlv(buffer, &tlv)
30}
31
32pub fn encode_tid_sync(buffer: &mut PacketBuffer) -> Result<()> {
33    buffer.write_u16(TID_SYNC)?;
34    buffer.write_u16(0)
35}
36
37pub fn encode_tid_poll(
38    buffer: &mut PacketBuffer,
39    manager_tuid: &[u8; TUID_LENGTH],
40    soem_code: SoemCode,
41    tuid_lo: &[u8; TUID_LENGTH],
42    tuid_hi: &[u8; TUID_LENGTH],
43    target_endpoint: u16,
44    query_level: u8,
45) -> Result<()> {
46    buffer.write_u16(TID_POLL)?;
47    buffer.write_u16(TID_POLL_VALUE_LEN)?;
48    buffer.write_bytes(manager_tuid)?;
49    buffer.write_u32(soem_code)?;
50    buffer.write_bytes(tuid_lo)?;
51    buffer.write_bytes(tuid_hi)?;
52    buffer.write_u16(target_endpoint)?;
53    buffer.write_byte(query_level)
54}
55
56pub fn encode_tid_poll_reply(
57    buffer: &mut PacketBuffer,
58    tuid: &[u8; TUID_LENGTH],
59    soem_code: SoemCode,
60    change_count: u16,
61) -> Result<()> {
62    buffer.write_u16(TID_POLL_REPLY)?;
63    buffer.write_u16(TID_POLL_REPLY_VALUE_LEN)?;
64    buffer.write_bytes(tuid)?;
65    buffer.write_u32(soem_code)?;
66    buffer.write_u16(change_count)
67}
68
69pub fn encode_tid_rt_protocol_version(buffer: &mut PacketBuffer, protocol_version: u8) -> Result<()> {
70    buffer.write_u16(TID_RT_PROTOCOL_VERSION)?;
71    buffer.write_u16(1)?;
72    buffer.write_byte(protocol_version)
73}
74
75pub fn encode_tid_rt_firmware_version(
76    buffer: &mut PacketBuffer,
77    machine_version_id: u32,
78    version_string: &str,
79) -> Result<()> {
80    let vs = version_string.as_bytes();
81    buffer.write_u16(TID_RT_FIRMWARE_VERSION)?;
82    buffer.write_u16(4 + vs.len() as u16)?;
83    buffer.write_u32(machine_version_id)?;
84    buffer.write_bytes(vs)
85}
86
87pub fn encode_tid_rt_role_capability(buffer: &mut PacketBuffer, role_capability_bits: u8) -> Result<()> {
88    buffer.write_u16(TID_RT_ROLE_CAPABILITY)?;
89    buffer.write_u16(1)?;
90    buffer.write_byte(role_capability_bits)
91}
92
93pub fn encode_tid_preview(buffer: &mut PacketBuffer, dmx_data: &[u8]) -> Result<()> {
94    if dmx_data.is_empty() || dmx_data.len() > MAX_DMX_SLOTS as usize {
95        return Err(SigNetError::InvalidArgument);
96    }
97    let tlv = TLVBlock {
98        type_id: TID_PREVIEW,
99        value: dmx_data,
100    };
101    encode_tlv(buffer, &tlv)
102}
103
104/// Timecode: 5 bytes = [hours][minutes][seconds][frames][tc_type]
105pub fn encode_tid_timecode(
106    buffer: &mut PacketBuffer,
107    hours: u8,
108    minutes: u8,
109    seconds: u8,
110    frames: u8,
111    tc_type: u8,
112) -> Result<()> {
113    buffer.write_u16(TID_TIMECODE)?;
114    buffer.write_u16(5)?;
115    buffer.write_byte(hours)?;
116    buffer.write_byte(minutes)?;
117    buffer.write_byte(seconds)?;
118    buffer.write_byte(frames)?;
119    buffer.write_byte(tc_type)
120}
121
122/// TID_UNIVERSE: 7 bytes = [universe(u16)][command(u8)][multicast_ip(4)]
123pub fn encode_tid_universe(
124    buffer: &mut PacketBuffer,
125    universe: u16,
126    command: u8,
127    multicast_ip: &[u8; 4],
128) -> Result<()> {
129    buffer.write_u16(TID_UNIVERSE)?;
130    buffer.write_u16(7)?;
131    buffer.write_u16(universe)?;
132    buffer.write_byte(command)?;
133    buffer.write_bytes(multicast_ip)
134}
135
136/// TID_RT_MULT_OVERRIDE: 1 byte = [state]
137pub fn encode_tid_rt_mult_override(buffer: &mut PacketBuffer, state: u8) -> Result<()> {
138    buffer.write_u16(TID_RT_MULT_OVERRIDE)?;
139    buffer.write_u16(1)?;
140    buffer.write_byte(state)
141}
142
143/// TID_RT_OTW_CAPABILITY: 3 bytes = [listener_port(u16)][protocols_bitfield(u8)]
144pub fn encode_tid_rt_otw_capability(
145    buffer: &mut PacketBuffer,
146    listener_port: u16,
147    protocols_bitfield: u8,
148) -> Result<()> {
149    buffer.write_u16(TID_RT_OTW_CAPABILITY)?;
150    buffer.write_u16(3)?;
151    buffer.write_u16(listener_port)?;
152    buffer.write_byte(protocols_bitfield)
153}
154
155/// TID_RT_REBOOT: 5 bytes = [reboot_type(u8)][b'B'][b'O'][b'O'][b'T']
156pub fn encode_tid_rt_reboot(buffer: &mut PacketBuffer, reboot_type: u8) -> Result<()> {
157    buffer.write_u16(TID_RT_REBOOT)?;
158    buffer.write_u16(5)?;
159    buffer.write_byte(reboot_type)?;
160    buffer.write_bytes(b"BOOT")
161}
162
163/// Build boot notification payload in canonical TLV order (ยง10.2.5).
164pub fn build_startup_announce_payload(
165    buffer: &mut PacketBuffer,
166    tuid: &[u8; TUID_LENGTH],
167    soem_code: SoemCode,
168    firmware_version_id: u32,
169    firmware_version_string: &str,
170    protocol_version: u8,
171    role_capability_bits: u8,
172    endpoint_count: u16,
173    change_count: u16,
174    mult_override_state: u8,
175    otw_capability: Option<(u16, u8)>,
176) -> Result<()> {
177    // 1. TID_POLL_REPLY
178    encode_tid_poll_reply(buffer, tuid, soem_code, change_count)?;
179    // 2. TID_RT_FIRMWARE_VERSION
180    encode_tid_rt_firmware_version(buffer, firmware_version_id, firmware_version_string)?;
181    // 3. TID_RT_PROTOCOL_VERSION
182    encode_tid_rt_protocol_version(buffer, protocol_version)?;
183    // 4. TID_RT_ROLE_CAPABILITY
184    encode_tid_rt_role_capability(buffer, role_capability_bits)?;
185    // 5. TID_RT_ENDPOINT_COUNT
186    let ec = endpoint_count.to_be_bytes();
187    encode_tlv(buffer, &TLVBlock { type_id: TID_RT_ENDPOINT_COUNT, value: &ec })?;
188    // 6. TID_RT_MULT_OVERRIDE
189    encode_tid_rt_mult_override(buffer, mult_override_state)?;
190    // 7. TID_RT_OTW_CAPABILITY (only if supported)
191    if let Some((port, protocols)) = otw_capability {
192        encode_tid_rt_otw_capability(buffer, port, protocols)?;
193    }
194    Ok(())
195}