use byteorder::{ReadBytesExt, WriteBytesExt, LE};
use std::io;
pub use self::command::Command;
pub const COMMUNICATION_PORT: u16 = 7765;
pub const BROADCAST_PORT: u16 = 7654;
pub trait WriteBytes {
fn write_bytes<P: WriteToBytes>(&mut self, protocol: P) -> io::Result<()>;
}
pub trait ReadBytes {
fn read_bytes<P: ReadFromBytes>(&mut self) -> io::Result<P>;
}
pub trait WriteToBytes {
fn write_to_bytes<W: WriteBytesExt>(&self, writer: W) -> io::Result<()>;
}
pub trait ReadFromBytes: Sized {
fn read_from_bytes<R: ReadBytesExt>(reader: R) -> io::Result<Self>;
}
pub trait SizeBytes {
const SIZE_BYTES: usize;
}
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct DacStatus {
pub protocol: u8,
pub light_engine_state: u8,
pub playback_state: u8,
pub source: u8,
pub light_engine_flags: u16,
pub playback_flags: u16,
pub source_flags: u16,
pub buffer_fullness: u16,
pub point_rate: u32,
pub point_count: u32,
}
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct DacBroadcast {
pub mac_address: [u8; 6],
pub hw_revision: u16,
pub sw_revision: u16,
pub buffer_capacity: u16,
pub max_point_rate: u32,
pub dac_status: DacStatus,
}
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct DacPoint {
pub control: u16,
pub x: i16,
pub y: i16,
pub r: u16,
pub g: u16,
pub b: u16,
pub i: u16,
pub u1: u16,
pub u2: u16,
}
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct DacResponse {
pub response: u8,
pub command: u8,
pub dac_status: DacStatus,
}
impl DacStatus {
pub const LIGHT_ENGINE_READY: u8 = 0;
pub const LIGHT_ENGINE_WARMUP: u8 = 1;
pub const LIGHT_ENGINE_COOLDOWN: u8 = 2;
pub const LIGHT_ENGINE_EMERGENCY_STOP: u8 = 3;
pub const PLAYBACK_IDLE: u8 = 0;
pub const PLAYBACK_PREPARED: u8 = 1;
pub const PLAYBACK_PLAYING: u8 = 2;
pub const SOURCE_NETWORK_STREAMING: u8 = 0;
pub const SOURCE_ILDA_PLAYBACK_SD: u8 = 1;
pub const SOURCE_INTERNAL_ABSTRACT_GENERATOR: u8 = 2;
}
impl DacResponse {
pub const ACK: u8 = 0x61;
pub const NAK_FULL: u8 = 0x46;
pub const NAK_INVALID: u8 = 0x49;
pub const NAK_STOP_CONDITION: u8 = 0x21;
}
impl WriteToBytes for DacStatus {
fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
writer.write_u8(self.protocol)?;
writer.write_u8(self.light_engine_state)?;
writer.write_u8(self.playback_state)?;
writer.write_u8(self.source)?;
writer.write_u16::<LE>(self.light_engine_flags)?;
writer.write_u16::<LE>(self.playback_flags)?;
writer.write_u16::<LE>(self.source_flags)?;
writer.write_u16::<LE>(self.buffer_fullness)?;
writer.write_u32::<LE>(self.point_rate)?;
writer.write_u32::<LE>(self.point_count)?;
Ok(())
}
}
impl WriteToBytes for DacBroadcast {
fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
for &byte in &self.mac_address {
writer.write_u8(byte)?;
}
writer.write_u16::<LE>(self.hw_revision)?;
writer.write_u16::<LE>(self.sw_revision)?;
writer.write_u16::<LE>(self.buffer_capacity)?;
writer.write_u32::<LE>(self.max_point_rate)?;
writer.write_bytes(&self.dac_status)?;
Ok(())
}
}
impl WriteToBytes for DacPoint {
fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
writer.write_u16::<LE>(self.control)?;
writer.write_i16::<LE>(self.x)?;
writer.write_i16::<LE>(self.y)?;
writer.write_u16::<LE>(self.r)?;
writer.write_u16::<LE>(self.g)?;
writer.write_u16::<LE>(self.b)?;
writer.write_u16::<LE>(self.i)?;
writer.write_u16::<LE>(self.u1)?;
writer.write_u16::<LE>(self.u2)?;
Ok(())
}
}
impl WriteToBytes for DacResponse {
fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
writer.write_u8(self.response)?;
writer.write_u8(self.command)?;
writer.write_bytes(&self.dac_status)?;
Ok(())
}
}
impl ReadFromBytes for DacStatus {
fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
let protocol = reader.read_u8()?;
let light_engine_state = reader.read_u8()?;
let playback_state = reader.read_u8()?;
let source = reader.read_u8()?;
let light_engine_flags = reader.read_u16::<LE>()?;
let playback_flags = reader.read_u16::<LE>()?;
let source_flags = reader.read_u16::<LE>()?;
let buffer_fullness = reader.read_u16::<LE>()?;
let point_rate = reader.read_u32::<LE>()?;
let point_count = reader.read_u32::<LE>()?;
let dac_status = DacStatus {
protocol,
light_engine_state,
playback_state,
source,
light_engine_flags,
playback_flags,
source_flags,
buffer_fullness,
point_rate,
point_count,
};
Ok(dac_status)
}
}
impl ReadFromBytes for DacBroadcast {
fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
let mac_address = [
reader.read_u8()?,
reader.read_u8()?,
reader.read_u8()?,
reader.read_u8()?,
reader.read_u8()?,
reader.read_u8()?,
];
let hw_revision = reader.read_u16::<LE>()?;
let sw_revision = reader.read_u16::<LE>()?;
let buffer_capacity = reader.read_u16::<LE>()?;
let max_point_rate = reader.read_u32::<LE>()?;
let dac_status = reader.read_bytes::<DacStatus>()?;
let dac_broadcast = DacBroadcast {
mac_address,
hw_revision,
sw_revision,
buffer_capacity,
max_point_rate,
dac_status,
};
Ok(dac_broadcast)
}
}
impl ReadFromBytes for DacPoint {
fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
let control = reader.read_u16::<LE>()?;
let x = reader.read_i16::<LE>()?;
let y = reader.read_i16::<LE>()?;
let r = reader.read_u16::<LE>()?;
let g = reader.read_u16::<LE>()?;
let b = reader.read_u16::<LE>()?;
let i = reader.read_u16::<LE>()?;
let u1 = reader.read_u16::<LE>()?;
let u2 = reader.read_u16::<LE>()?;
let dac_point = DacPoint {
control,
x,
y,
i,
r,
g,
b,
u1,
u2,
};
Ok(dac_point)
}
}
impl ReadFromBytes for DacResponse {
fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
let response = reader.read_u8()?;
let command = reader.read_u8()?;
let dac_status = reader.read_bytes::<DacStatus>()?;
let dac_response = DacResponse {
response,
command,
dac_status,
};
Ok(dac_response)
}
}
impl SizeBytes for DacStatus {
const SIZE_BYTES: usize = 20;
}
impl SizeBytes for DacBroadcast {
const SIZE_BYTES: usize = DacStatus::SIZE_BYTES + 16;
}
impl SizeBytes for DacPoint {
const SIZE_BYTES: usize = 18;
}
impl SizeBytes for DacResponse {
const SIZE_BYTES: usize = DacStatus::SIZE_BYTES + 2;
}
impl<'a, P> WriteToBytes for &'a P
where
P: WriteToBytes,
{
fn write_to_bytes<W: WriteBytesExt>(&self, writer: W) -> io::Result<()> {
(*self).write_to_bytes(writer)
}
}
impl<W> WriteBytes for W
where
W: WriteBytesExt,
{
fn write_bytes<P: WriteToBytes>(&mut self, protocol: P) -> io::Result<()> {
protocol.write_to_bytes(self)
}
}
impl<R> ReadBytes for R
where
R: ReadBytesExt,
{
fn read_bytes<P: ReadFromBytes>(&mut self) -> io::Result<P> {
P::read_from_bytes(self)
}
}
pub mod command {
use super::{DacPoint, ReadBytes, ReadFromBytes, SizeBytes, WriteBytes, WriteToBytes};
use byteorder::{ReadBytesExt, WriteBytesExt, LE};
use std::borrow::Cow;
use std::{self, io};
pub trait Command {
const START_BYTE: u8;
fn start_byte(&self) -> u8 {
Self::START_BYTE
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct PrepareStream;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct Begin {
pub low_water_mark: u16,
pub point_rate: u32,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct PointRate(pub u32);
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Data<'a> {
pub points: Cow<'a, [DacPoint]>,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct Stop;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct EmergencyStop;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct EmergencyStopAlt;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct ClearEmergencyStop;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct Ping;
impl Begin {
pub fn read_fields<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
let low_water_mark = reader.read_u16::<LE>()?;
let point_rate = reader.read_u32::<LE>()?;
let begin = Begin {
low_water_mark,
point_rate,
};
Ok(begin)
}
}
impl PointRate {
pub fn read_fields<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
let point_rate = PointRate(reader.read_u32::<LE>()?);
Ok(point_rate)
}
}
impl<'a> Data<'a> {
pub fn read_n_points<R>(mut reader: R) -> io::Result<u16>
where
R: ReadBytesExt,
{
reader.read_u16::<LE>()
}
pub fn read_points<R>(
mut reader: R,
mut n_points: u16,
points: &mut Vec<DacPoint>,
) -> io::Result<()>
where
R: ReadBytesExt,
{
while n_points > 0 {
let dac_point = reader.read_bytes::<DacPoint>()?;
points.push(dac_point);
n_points -= 1;
}
Ok(())
}
}
impl Data<'static> {
pub fn read_fields<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
let n_points = Self::read_n_points(&mut reader)?;
let mut data = Vec::with_capacity(n_points as _);
Self::read_points(reader, n_points, &mut data)?;
let data = Data {
points: Cow::Owned(data),
};
Ok(data)
}
}
impl<'a, C> Command for &'a C
where
C: Command,
{
const START_BYTE: u8 = C::START_BYTE;
}
impl Command for PrepareStream {
const START_BYTE: u8 = 0x70;
}
impl Command for Begin {
const START_BYTE: u8 = 0x62;
}
impl Command for PointRate {
const START_BYTE: u8 = 0x74;
}
impl<'a> Command for Data<'a> {
const START_BYTE: u8 = 0x64;
}
impl Command for Stop {
const START_BYTE: u8 = 0x73;
}
impl Command for EmergencyStop {
const START_BYTE: u8 = 0x00;
}
impl Command for EmergencyStopAlt {
const START_BYTE: u8 = 0xff;
}
impl Command for ClearEmergencyStop {
const START_BYTE: u8 = 0x63;
}
impl Command for Ping {
const START_BYTE: u8 = 0x3f;
}
impl SizeBytes for PrepareStream {
const SIZE_BYTES: usize = 1;
}
impl SizeBytes for Begin {
const SIZE_BYTES: usize = 7;
}
impl SizeBytes for PointRate {
const SIZE_BYTES: usize = 5;
}
impl SizeBytes for Stop {
const SIZE_BYTES: usize = 1;
}
impl SizeBytes for EmergencyStop {
const SIZE_BYTES: usize = 1;
}
impl SizeBytes for ClearEmergencyStop {
const SIZE_BYTES: usize = 1;
}
impl SizeBytes for Ping {
const SIZE_BYTES: usize = 1;
}
impl WriteToBytes for PrepareStream {
fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
writer.write_u8(Self::START_BYTE)?;
Ok(())
}
}
impl WriteToBytes for Begin {
fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
writer.write_u8(Self::START_BYTE)?;
writer.write_u16::<LE>(self.low_water_mark)?;
writer.write_u32::<LE>(self.point_rate)?;
Ok(())
}
}
impl WriteToBytes for PointRate {
fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
writer.write_u8(Self::START_BYTE)?;
writer.write_u32::<LE>(self.0)?;
Ok(())
}
}
impl<'a> WriteToBytes for Data<'a> {
fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
if self.points.len() > std::u16::MAX as usize {
let err_msg = "the number of points exceeds the maximum possible `u16` value";
return Err(io::Error::new(io::ErrorKind::InvalidData, err_msg));
}
writer.write_u8(Self::START_BYTE)?;
writer.write_u16::<LE>(self.points.len() as u16)?;
for point in self.points.iter() {
writer.write_bytes(point)?;
}
Ok(())
}
}
impl WriteToBytes for Stop {
fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
writer.write_u8(Self::START_BYTE)?;
Ok(())
}
}
impl WriteToBytes for EmergencyStop {
fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
writer.write_u8(Self::START_BYTE)?;
Ok(())
}
}
impl WriteToBytes for EmergencyStopAlt {
fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
writer.write_u8(Self::START_BYTE)?;
Ok(())
}
}
impl WriteToBytes for ClearEmergencyStop {
fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
writer.write_u8(Self::START_BYTE)?;
Ok(())
}
}
impl WriteToBytes for Ping {
fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
writer.write_u8(Self::START_BYTE)?;
Ok(())
}
}
impl ReadFromBytes for PrepareStream {
fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
let command = reader.read_u8()?;
if command != Self::START_BYTE {
let err_msg = "invalid \"prepare stream\" command byte";
return Err(io::Error::new(io::ErrorKind::InvalidData, err_msg));
}
Ok(PrepareStream)
}
}
impl ReadFromBytes for Begin {
fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
if reader.read_u8()? != Self::START_BYTE {
let err_msg = "invalid \"begin\" command byte";
return Err(io::Error::new(io::ErrorKind::InvalidData, err_msg));
}
Self::read_fields(reader)
}
}
impl ReadFromBytes for PointRate {
fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
if reader.read_u8()? != Self::START_BYTE {
let err_msg = "invalid \"queue change\" command byte";
return Err(io::Error::new(io::ErrorKind::InvalidData, err_msg));
}
Self::read_fields(reader)
}
}
impl ReadFromBytes for Data<'static> {
fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
if reader.read_u8()? != Self::START_BYTE {
let err_msg = "invalid \"data\" command byte";
return Err(io::Error::new(io::ErrorKind::InvalidData, err_msg));
}
Self::read_fields(reader)
}
}
impl ReadFromBytes for Stop {
fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
let command = reader.read_u8()?;
if command != Self::START_BYTE {
let err_msg = "invalid \"stop\" command byte";
return Err(io::Error::new(io::ErrorKind::InvalidData, err_msg));
}
Ok(Stop)
}
}
impl ReadFromBytes for EmergencyStop {
fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
let command = reader.read_u8()?;
if command != Self::START_BYTE && command != EmergencyStopAlt::START_BYTE {
let err_msg = "invalid \"emergency stop\" command byte";
return Err(io::Error::new(io::ErrorKind::InvalidData, err_msg));
}
Ok(EmergencyStop)
}
}
impl ReadFromBytes for ClearEmergencyStop {
fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
let command = reader.read_u8()?;
if command != Self::START_BYTE {
let err_msg = "invalid \"clear emergency stop\" command byte";
return Err(io::Error::new(io::ErrorKind::InvalidData, err_msg));
}
Ok(ClearEmergencyStop)
}
}
impl ReadFromBytes for Ping {
fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
let command = reader.read_u8()?;
if command != Self::START_BYTE {
let err_msg = "invalid \"ping\" command byte";
return Err(io::Error::new(io::ErrorKind::InvalidData, err_msg));
}
Ok(Ping)
}
}
}