oxihuman_export/
artnet_export.rs1#![allow(dead_code)]
4
5pub const ARTNET_ID: &[u8; 8] = b"Art-Net\0";
8pub const ARTNET_PORT: u16 = 6454;
9pub const ARTNET_OP_DMX: u16 = 0x5000;
10pub const ARTNET_PROTOCOL_VER: u16 = 14;
11
12#[allow(dead_code)]
14pub struct ArtDmxPacket {
15 pub universe: u16,
16 pub sequence: u8,
17 pub physical: u8,
18 pub data: Vec<u8>,
19}
20
21impl ArtDmxPacket {
22 #[allow(dead_code)]
23 pub fn new(universe: u16) -> Self {
24 Self {
25 universe,
26 sequence: 0,
27 physical: 0,
28 data: vec![0u8; 512],
29 }
30 }
31}
32
33#[allow(dead_code)]
35pub fn build_artnet_dmx_packet(pkt: &ArtDmxPacket) -> Vec<u8> {
36 let mut out = Vec::new();
37 out.extend_from_slice(ARTNET_ID);
38 out.extend_from_slice(&ARTNET_OP_DMX.to_le_bytes());
39 out.extend_from_slice(&ARTNET_PROTOCOL_VER.to_be_bytes());
40 out.push(pkt.sequence);
41 out.push(pkt.physical);
42 out.extend_from_slice(&pkt.universe.to_le_bytes());
43 let len = pkt.data.len().min(512) as u16;
44 out.extend_from_slice(&len.to_be_bytes());
45 out.extend_from_slice(&pkt.data[..len as usize]);
46 out
47}
48
49#[allow(dead_code)]
51pub fn artnet_set_channel(pkt: &mut ArtDmxPacket, channel: usize, value: u8) {
52 if channel >= 1 && channel <= pkt.data.len() {
53 pkt.data[channel - 1] = value;
54 }
55}
56
57#[allow(dead_code)]
59pub fn artnet_get_channel(pkt: &ArtDmxPacket, channel: usize) -> u8 {
60 if channel >= 1 && channel <= pkt.data.len() {
61 pkt.data[channel - 1]
62 } else {
63 0
64 }
65}
66
67#[allow(dead_code)]
69pub fn artnet_packet_size(pkt: &ArtDmxPacket) -> usize {
70 build_artnet_dmx_packet(pkt).len()
71}
72
73#[allow(dead_code)]
75pub fn artnet_active_channels(pkt: &ArtDmxPacket) -> usize {
76 pkt.data.iter().filter(|&&v| v != 0).count()
77}
78
79#[allow(dead_code)]
81pub fn artnet_clear(pkt: &mut ArtDmxPacket) {
82 pkt.data.fill(0);
83}
84
85#[allow(dead_code)]
87pub fn artnet_fill(pkt: &mut ArtDmxPacket, value: u8) {
88 pkt.data.fill(value);
89}
90
91#[allow(dead_code)]
93pub fn universe_to_subnet_address(universe: u16) -> (u8, u8) {
94 let subnet = ((universe >> 4) & 0x0F) as u8;
95 let net = (universe & 0x0F) as u8;
96 (subnet, net)
97}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102
103 #[test]
104 fn packet_starts_with_art_net_id() {
105 let pkt = ArtDmxPacket::new(0);
106 let bytes = build_artnet_dmx_packet(&pkt);
107 assert_eq!(&bytes[0..8], ARTNET_ID);
108 }
109
110 #[test]
111 fn packet_op_code_correct() {
112 let pkt = ArtDmxPacket::new(0);
113 let bytes = build_artnet_dmx_packet(&pkt);
114 let op = u16::from_le_bytes([bytes[8], bytes[9]]);
115 assert_eq!(op, ARTNET_OP_DMX);
116 }
117
118 #[test]
119 fn packet_header_length_minimum() {
120 let pkt = ArtDmxPacket::new(0);
121 let bytes = build_artnet_dmx_packet(&pkt);
122 assert!(bytes.len() >= 18);
123 }
124
125 #[test]
126 fn set_get_channel() {
127 let mut pkt = ArtDmxPacket::new(0);
128 artnet_set_channel(&mut pkt, 1, 200);
129 assert_eq!(artnet_get_channel(&pkt, 1), 200);
130 }
131
132 #[test]
133 fn active_channels_after_set() {
134 let mut pkt = ArtDmxPacket::new(0);
135 artnet_set_channel(&mut pkt, 5, 100);
136 assert_eq!(artnet_active_channels(&pkt), 1);
137 }
138
139 #[test]
140 fn artnet_clear_zeros() {
141 let mut pkt = ArtDmxPacket::new(0);
142 artnet_fill(&mut pkt, 255);
143 artnet_clear(&mut pkt);
144 assert_eq!(artnet_active_channels(&pkt), 0);
145 }
146
147 #[test]
148 fn artnet_fill_all_same() {
149 let mut pkt = ArtDmxPacket::new(0);
150 artnet_fill(&mut pkt, 42);
151 assert!(pkt.data.iter().all(|&v| v == 42));
152 }
153
154 #[test]
155 fn universe_subnet_address() {
156 let (subnet, net) = universe_to_subnet_address(0x23);
157 assert_eq!(subnet, 2);
158 assert_eq!(net, 3);
159 }
160
161 #[test]
162 fn packet_size_fixed() {
163 let pkt = ArtDmxPacket::new(0);
164 let sz = artnet_packet_size(&pkt);
165 assert!((18..=600).contains(&sz));
166 }
167
168 #[test]
169 fn default_port_correct() {
170 assert_eq!(ARTNET_PORT, 6454);
171 }
172}