1#![no_std]
2extern crate tiny_artnet_bytes_no_atomic as bytes;
3
4mod poll_reply;
5pub use poll_reply::PollReply;
6
7use core::ops::RangeInclusive;
8
9use bytes::BufMut;
10use nom::{
11 bytes::complete::tag,
12 number::complete as number,
13 number::complete::{be_u16, le_u16},
14 sequence::tuple,
15 IResult,
16};
17
18const ID: &'static [u8] = b"Art-Net\0";
19pub const PORT: u16 = 0x1936;
20
21const DEFAULT_4_BYTES: &'static [u8; 4] = &[0; 4];
22const DEFAULT_6_BYTES: &'static [u8; 6] = &[0; 6];
23
24#[derive(Debug)]
25pub enum Art<'a> {
26 Poll(Poll),
27 Command(Command<'a>),
29 Dmx(Dmx<'a>),
30 Sync,
31}
32
33#[derive(Debug)]
34pub enum Error<'a> {
35 UnsupportedProtocolVersion(u16),
36 UnsupportedOpCode(u16),
37 ParserError(nom::Err<nom::error::Error<&'a [u8]>>),
38}
39
40impl<'a> From<nom::Err<nom::error::Error<&'a [u8]>>> for Error<'a> {
41 fn from(err: nom::Err<nom::error::Error<&'a [u8]>>) -> Self {
42 Error::ParserError(err)
43 }
44}
45
46pub fn from_slice<'a>(s: &'a [u8]) -> Result<Art<'a>, Error<'a>> {
47 let (s, _) = tag(ID)(s)?;
49
50 let (s, op_code) = le_u16(s)?;
51 let (s, protocol_version): (&'a [u8], u16) = be_u16(s)?;
52
53 if protocol_version > 14 {
54 return Err(Error::UnsupportedProtocolVersion(protocol_version));
55 }
56
57 let message = match op_code {
58 0x2000 => Art::Poll(parse_poll(s)?),
59 0x2400 => Art::Command(parse_command(s)?),
61 0x5000 => Art::Dmx(parse_dmx(s)?),
62 0x5200 => parse_sync(s).map(|_| Art::Sync)?,
63 _ => return Err(Error::UnsupportedOpCode(op_code)),
64 };
65
66 Ok(message)
67}
68
69pub type ESTAManufacturerCode = (char, char);
71
72fn parse_esta_manufacturer_code<'a>(s: &'a [u8]) -> IResult<&'a [u8], ESTAManufacturerCode> {
73 let (s, (lo, hi)) = tuple((number::u8, number::u8))(s)?;
74 Ok((s, (lo as char, hi as char)))
75}
76
77pub fn put_esta_manufacturer_code<B: BufMut>(
78 buf: &mut B,
79 manufacturer_code: &ESTAManufacturerCode,
80) {
81 buf.put_u8(manufacturer_code.0 as u8);
82 buf.put_u8(manufacturer_code.1 as u8);
83}
84
85#[derive(Debug)]
92pub struct PortAddress {
93 pub net: u8,
94 pub sub_net: u8,
95 pub universe: u8,
96}
97
98fn parse_port_address<'a>(s: &'a [u8]) -> IResult<&'a [u8], PortAddress> {
99 use nom::bits::complete as bits;
100
101 let (s, (sub_net, universe, _, net)): (&[u8], (u8, u8, u8, u8)) = nom::bits::bits(tuple((
102 bits::take::<&[u8], u8, usize, nom::error::Error<(&[u8], usize)>>(4usize),
104 bits::take(4usize),
105 bits::take(1usize),
107 bits::take(7usize),
108 )))(s)?;
109
110 let port_address = PortAddress {
111 net,
112 sub_net,
113 universe,
114 };
115
116 Ok((s, port_address))
117}
118
119impl PortAddress {
120 pub fn as_index(&self) -> usize {
122 (self.net as usize >> 14) + (self.sub_net as usize >> 7) + (self.universe as usize)
123 }
124}
125
126fn put_padded_str<const N: usize, B: BufMut>(mut buf: B, input: &str) {
128 let mut padded_bytes = [0; N];
129
130 let bytes = input.as_bytes();
131 let truncated_bytes = if bytes.len() > N - 1 {
133 &bytes[..N - 1]
134 } else {
135 &bytes[..]
136 };
137
138 (&mut padded_bytes[..]).put_slice(truncated_bytes);
140
141 buf.put_slice(&padded_bytes);
142}
143
144#[derive(Debug)]
145pub struct Poll {
146 pub flags: u8,
147 pub min_diagnostic_priority: u8,
148 pub target_port_addresses: RangeInclusive<u16>,
149}
150
151fn parse_poll<'a>(s: &'a [u8]) -> Result<Poll, Error<'a>> {
152 let (s, flags) = number::u8(s)?;
153 let (s, min_diagnostic_priority) = number::u8(s)?;
154
155 let target_port_addresses = if !s.is_empty() {
156 let (s, target_port_top): (&'a [u8], u16) = be_u16(s)?;
157 let (_s, target_port_bottom): (&'a [u8], u16) = be_u16(s)?;
158
159 target_port_top..=target_port_bottom
160 } else {
161 0..=u16::MAX
162 };
163
164 Ok(Poll {
165 flags,
166 min_diagnostic_priority,
167 target_port_addresses,
168 })
169}
170
171#[derive(Debug)]
172pub struct Command<'a> {
173 pub esta_manufacturer_code: ESTAManufacturerCode,
174 pub data: &'a [u8],
175}
176
177fn parse_command<'a>(s: &'a [u8]) -> Result<Command<'a>, Error<'a>> {
178 let (s, esta_manufacturer_code) = parse_esta_manufacturer_code(s)?;
179 let (s, length): (&'a [u8], u16) = le_u16(s)?;
180
181 let data = &s[..length as usize];
182
183 Ok(Command {
184 esta_manufacturer_code,
185 data,
186 })
187}
188
189#[derive(Debug)]
190pub struct Dmx<'a> {
191 pub sequence: u8,
204 pub physical: u8,
211 pub port_address: PortAddress,
218 pub data: &'a [u8],
219}
220
221fn parse_dmx<'a>(s: &'a [u8]) -> Result<Dmx<'a>, Error<'a>> {
222 let (s, sequence) = number::u8(s)?;
223 let (s, physical) = number::u8(s)?;
224 let (s, port_address) = parse_port_address(s)?;
225
226 let (s, length): (&'a [u8], u16) = be_u16(s)?;
227
228 let data = &s[..length as usize];
229
230 Ok(Dmx {
231 sequence,
232 physical,
233 port_address,
234 data,
235 })
236}
237
238fn parse_sync<'a>(s: &'a [u8]) -> Result<(), Error<'a>> {
239 let (s, _aux1) = number::u8(s)?;
240 let (_s, _aux2) = number::u8(s)?;
241
242 Ok(())
243}