#![no_std]
extern crate tiny_artnet_bytes_no_atomic as bytes;
mod poll_reply;
pub use poll_reply::PollReply;
use core::ops::RangeInclusive;
use bytes::BufMut;
use nom::{
bytes::complete::tag,
number::complete as number,
number::complete::{be_u16, le_u16},
sequence::tuple,
IResult,
};
const ID: &'static [u8] = b"Art-Net\0";
pub const PORT: u16 = 0x1936;
const DEFAULT_4_BYTES: &'static [u8; 4] = &[0; 4];
const DEFAULT_6_BYTES: &'static [u8; 6] = &[0; 6];
#[derive(Debug)]
pub enum Art<'a> {
Poll(Poll),
Command(Command<'a>),
Dmx(Dmx<'a>),
Sync,
}
#[derive(Debug)]
pub enum Error<'a> {
UnsupportedProtocolVersion(u16),
UnsupportedOpCode(u16),
ParserError(nom::Err<nom::error::Error<&'a [u8]>>),
}
impl<'a> From<nom::Err<nom::error::Error<&'a [u8]>>> for Error<'a> {
fn from(err: nom::Err<nom::error::Error<&'a [u8]>>) -> Self {
Error::ParserError(err)
}
}
pub fn from_slice<'a>(s: &'a [u8]) -> Result<Art<'a>, Error<'a>> {
let (s, _) = tag(ID)(s)?;
let (s, op_code) = le_u16(s)?;
let (s, protocol_version): (&'a [u8], u16) = be_u16(s)?;
if protocol_version > 14 {
return Err(Error::UnsupportedProtocolVersion(protocol_version));
}
let message = match op_code {
0x2000 => Art::Poll(parse_poll(s)?),
0x2400 => Art::Command(parse_command(s)?),
0x5000 => Art::Dmx(parse_dmx(s)?),
0x5200 => parse_sync(s).map(|_| Art::Sync)?,
_ => return Err(Error::UnsupportedOpCode(op_code)),
};
Ok(message)
}
pub type ESTAManufacturerCode = (char, char);
fn parse_esta_manufacturer_code<'a>(s: &'a [u8]) -> IResult<&'a [u8], ESTAManufacturerCode> {
let (s, (lo, hi)) = tuple((number::u8, number::u8))(s)?;
Ok((s, (lo as char, hi as char)))
}
pub fn put_esta_manufacturer_code<B: BufMut>(
buf: &mut B,
manufacturer_code: &ESTAManufacturerCode,
) {
buf.put_u8(manufacturer_code.0 as u8);
buf.put_u8(manufacturer_code.1 as u8);
}
#[derive(Debug)]
pub struct PortAddress {
pub net: u8,
pub sub_net: u8,
pub universe: u8,
}
fn parse_port_address<'a>(s: &'a [u8]) -> IResult<&'a [u8], PortAddress> {
use nom::bits::complete as bits;
let (s, (sub_net, universe, _, net)): (&[u8], (u8, u8, u8, u8)) = nom::bits::bits(tuple((
bits::take::<&[u8], u8, usize, nom::error::Error<(&[u8], usize)>>(4usize),
bits::take(4usize),
bits::take(1usize),
bits::take(7usize),
)))(s)?;
let port_address = PortAddress {
net,
sub_net,
universe,
};
Ok((s, port_address))
}
impl PortAddress {
pub fn as_index(&self) -> usize {
(self.net as usize >> 14) + (self.sub_net as usize >> 7) + (self.universe as usize)
}
}
fn put_padded_str<const N: usize, B: BufMut>(mut buf: B, input: &str) {
let mut padded_bytes = [0; N];
let bytes = input.as_bytes();
let truncated_bytes = if bytes.len() > N - 1 {
&bytes[..N - 1]
} else {
&bytes[..]
};
(&mut padded_bytes[..]).put_slice(truncated_bytes);
buf.put_slice(&padded_bytes);
}
#[derive(Debug)]
pub struct Poll {
pub flags: u8,
pub min_diagnostic_priority: u8,
pub target_port_addresses: RangeInclusive<u16>,
}
fn parse_poll<'a>(s: &'a [u8]) -> Result<Poll, Error<'a>> {
let (s, flags) = number::u8(s)?;
let (s, min_diagnostic_priority) = number::u8(s)?;
let target_port_addresses = if !s.is_empty() {
let (s, target_port_top): (&'a [u8], u16) = be_u16(s)?;
let (_s, target_port_bottom): (&'a [u8], u16) = be_u16(s)?;
target_port_top..=target_port_bottom
} else {
0..=u16::MAX
};
Ok(Poll {
flags,
min_diagnostic_priority,
target_port_addresses,
})
}
#[derive(Debug)]
pub struct Command<'a> {
pub esta_manufacturer_code: ESTAManufacturerCode,
pub data: &'a [u8],
}
fn parse_command<'a>(s: &'a [u8]) -> Result<Command<'a>, Error<'a>> {
let (s, esta_manufacturer_code) = parse_esta_manufacturer_code(s)?;
let (s, length): (&'a [u8], u16) = le_u16(s)?;
let data = &s[..length as usize];
Ok(Command {
esta_manufacturer_code,
data,
})
}
#[derive(Debug)]
pub struct Dmx<'a> {
pub sequence: u8,
pub physical: u8,
pub port_address: PortAddress,
pub data: &'a [u8],
}
fn parse_dmx<'a>(s: &'a [u8]) -> Result<Dmx<'a>, Error<'a>> {
let (s, sequence) = number::u8(s)?;
let (s, physical) = number::u8(s)?;
let (s, port_address) = parse_port_address(s)?;
let (s, length): (&'a [u8], u16) = be_u16(s)?;
let data = &s[..length as usize];
Ok(Dmx {
sequence,
physical,
port_address,
data,
})
}
fn parse_sync<'a>(s: &'a [u8]) -> Result<(), Error<'a>> {
let (s, _aux1) = number::u8(s)?;
let (_s, _aux2) = number::u8(s)?;
Ok(())
}