use crate::protocol::{
self, ReadBytesExt, ReadFromBytes, SizeBytes, Ucs2, WriteBytes, WriteBytesExt, WriteToBytes, LE,
};
use std::{borrow::Cow, io, mem};
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[repr(C)]
pub struct Header {
pub citp_header: protocol::Header,
pub content_type: u32,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[repr(C)]
pub struct Nack {
pub reason: NackReason,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum NackReason {
UnknownRequest = 0x00,
IncorrectRequest = 0x01,
InternalError = 0x02,
RequestRefused = 0x03,
}
impl From<u8> for NackReason {
fn from(orig: u8) -> Self {
match orig {
0x00 => NackReason::UnknownRequest,
0x01 => NackReason::IncorrectRequest,
0x02 => NackReason::InternalError,
0x03 => NackReason::RequestRefused,
_ => unreachable!(),
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[repr(C)]
pub struct Message<T> {
pub caex_header: Header,
pub message: T,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[repr(C)]
pub struct EnterShow {
pub name: Ucs2,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[repr(C)]
pub struct LeaveShow {}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[repr(C)]
pub struct FixtureListRequest {}
#[derive(Clone, Debug, PartialEq)]
#[repr(C)]
pub struct FixtureList<'a> {
pub message_type: FixtureListMessageType,
pub fixture_count: u16,
pub fixtures: Cow<'a, [Fixture<'a>]>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum FixtureListMessageType {
ExistingPatchList = 0x00,
NewFixture = 0x01,
ExchangeFixture = 0x02,
}
impl From<u8> for FixtureListMessageType {
fn from(orig: u8) -> Self {
match orig {
0x00 => FixtureListMessageType::ExistingPatchList,
0x01 => FixtureListMessageType::NewFixture,
0x02 => FixtureListMessageType::ExchangeFixture,
_ => unreachable!(),
}
}
}
impl From<FixtureListMessageType> for u8 {
fn from(original: FixtureListMessageType) -> u8 {
match original {
FixtureListMessageType::ExistingPatchList => 0x00,
FixtureListMessageType::NewFixture => 0x01,
FixtureListMessageType::ExchangeFixture => 0x02,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum IdentifierType {
RDMDeviceModelId = 0x00, RDMPersonalityId = 0x01, AtlaBaseFixtureId = 0x02, AtlaBaseModeId = 0x03, CaptureInstanceId = 0x04, RDMManufacturerId = 0x05, }
impl From<u8> for IdentifierType {
fn from(orig: u8) -> Self {
match orig {
0x00 => IdentifierType::RDMDeviceModelId,
0x01 => IdentifierType::RDMPersonalityId,
0x02 => IdentifierType::AtlaBaseFixtureId,
0x03 => IdentifierType::AtlaBaseModeId,
0x04 => IdentifierType::CaptureInstanceId,
0x05 => IdentifierType::RDMManufacturerId,
_ => unreachable!(),
}
}
}
impl From<IdentifierType> for u8 {
fn from(original: IdentifierType) -> u8 {
match original {
IdentifierType::RDMDeviceModelId => 0x00,
IdentifierType::RDMPersonalityId => 0x01,
IdentifierType::AtlaBaseFixtureId => 0x02,
IdentifierType::AtlaBaseModeId => 0x03,
IdentifierType::CaptureInstanceId => 0x04,
IdentifierType::RDMManufacturerId => 0x05,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[repr(C)]
pub struct Identifier<'a> {
pub identifier_type: IdentifierType,
pub data_size: u16,
pub data: Cow<'a, [u8]>,
}
#[derive(Clone, Debug, PartialEq)]
#[repr(C)]
pub struct Fixture<'a> {
pub fixture_identifier: u32,
pub manufacturer_name: Ucs2,
pub fixture_name: Ucs2,
pub mode_name: Ucs2,
pub channel_count: u16,
pub is_dimmer: u8,
pub identifier_count: u8,
pub identifiers: Cow<'a, [Identifier<'a>]>,
pub data: FixtureData,
}
#[derive(Clone, Debug, PartialEq)]
#[repr(C)]
pub struct FixtureData {
pub patched: u8,
pub universe: u8,
pub universe_channel: u16,
pub unit: Ucs2,
pub channel: u16,
pub circuit: Ucs2,
pub note: Ucs2,
pub position: [f32; 3],
pub angles: [f32; 3],
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[repr(C)]
pub struct FixtureRemove<'a> {
pub fixture_count: u16,
pub fixture_identifiers: Cow<'a, [u32]>,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[repr(C)]
pub struct FixtureConsoleStatus<'a> {
pub fixture_count: u16,
pub fixtures_state: Cow<'a, [FixtureState]>,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[repr(C)]
pub struct FixtureState {
pub fixture_identifier: u32,
pub locked: u8,
pub clearable: u8,
}
impl EnterShow {
pub const CONTENT_TYPE: u32 = 0x00020100;
}
impl LeaveShow {
pub const CONTENT_TYPE: u32 = 0x00020101;
}
impl FixtureListRequest {
pub const CONTENT_TYPE: u32 = 0x00020200;
}
impl<'a> FixtureList<'a> {
pub const CONTENT_TYPE: u32 = 0x00020201;
}
impl<'a> FixtureRemove<'a> {
pub const CONTENT_TYPE: u32 = 0x00020203;
}
impl<'a> FixtureConsoleStatus<'a> {
pub const CONTENT_TYPE: u32 = 0x00020400;
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[repr(C)]
pub struct GetLaserFeedList {}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[repr(C)]
pub struct LaserFeedList<'a> {
pub source_key: u32,
pub feed_count: u8,
pub feed_names: Cow<'a, [Ucs2]>,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[repr(C)]
pub struct LaserFeedControl {
pub feed_index: u8,
pub frame_rate: u8,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[repr(C)]
pub struct LaserFeedFrame<'a> {
pub source_key: u32,
pub feed_index: u8,
pub frame_sequence: u32,
pub point_count: u16,
pub points: Cow<'a, [LaserPoint]>,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[repr(C)]
pub struct LaserPoint {
pub x_low_byte: u8,
pub y_low_byte: u8,
pub xy_high_nibbles: u8,
pub color: u16,
}
impl Header {
pub const CONTENT_TYPE: &'static [u8; 4] = b"CAEX";
}
impl Nack {
pub const CONTENT_TYPE: u32 = 0xFFFFFFFF;
}
impl GetLaserFeedList {
pub const CONTENT_TYPE: u32 = 0x00030100;
}
impl<'a> LaserFeedList<'a> {
pub const CONTENT_TYPE: u32 = 0x00030101;
}
impl LaserFeedControl {
pub const CONTENT_TYPE: u32 = 0x00030102;
}
impl<'a> LaserFeedFrame<'a> {
pub const CONTENT_TYPE: u32 = 0x00030200;
}
impl WriteToBytes for Header {
fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
writer.write_bytes(self.citp_header)?;
writer.write_u32::<LE>(self.content_type)?;
Ok(())
}
}
impl<T> WriteToBytes for Message<T>
where
T: WriteToBytes,
{
fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
writer.write_bytes(self.caex_header)?;
writer.write_bytes(&self.message)?;
Ok(())
}
}
impl WriteToBytes for EnterShow {
fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
self.name.write_to_bytes(&mut writer)?;
Ok(())
}
}
impl<'a> WriteToBytes for FixtureList<'a> {
fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
writer.write_u8(self.message_type.into())?;
writer.write_u16::<LE>(self.fixture_count)?;
for fixture in self.fixtures.iter() {
fixture.write_to_bytes(&mut writer)?;
}
Ok(())
}
}
impl<'a> WriteToBytes for Fixture<'a> {
fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
writer.write_u32::<LE>(self.fixture_identifier)?;
self.manufacturer_name.write_to_bytes(&mut writer)?;
self.fixture_name.write_to_bytes(&mut writer)?;
self.mode_name.write_to_bytes(&mut writer)?;
writer.write_u16::<LE>(self.channel_count)?;
writer.write_u8(self.is_dimmer)?;
writer.write_u8(self.identifier_count)?;
for identifier in self.identifiers.iter() {
identifier.write_to_bytes(&mut writer)?;
}
self.data.write_to_bytes(&mut writer)?;
Ok(())
}
}
impl<'a> WriteToBytes for Identifier<'a> {
fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
writer.write_u8(self.identifier_type.into())?;
writer.write_u16::<LE>(self.data_size)?;
for i in 0..self.data_size {
writer.write_u8(self.data[i as usize])?;
}
Ok(())
}
}
impl WriteToBytes for FixtureData {
fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
writer.write_u8(self.patched)?;
writer.write_u8(self.universe)?;
writer.write_u16::<LE>(self.universe_channel)?;
self.unit.write_to_bytes(&mut writer)?;
writer.write_u16::<LE>(self.channel)?;
self.circuit.write_to_bytes(&mut writer)?;
self.note.write_to_bytes(&mut writer)?;
writer.write_f32::<LE>(self.position[0])?;
writer.write_f32::<LE>(self.position[1])?;
writer.write_f32::<LE>(self.position[2])?;
writer.write_f32::<LE>(self.angles[0])?;
writer.write_f32::<LE>(self.angles[1])?;
writer.write_f32::<LE>(self.angles[2])?;
Ok(())
}
}
impl<'a> WriteToBytes for FixtureRemove<'a> {
fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
writer.write_u16::<LE>(self.fixture_count)?;
for id in self.fixture_identifiers.iter() {
writer.write_u32::<LE>(*id)?;
}
Ok(())
}
}
impl<'a> WriteToBytes for LaserFeedList<'a> {
fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
writer.write_u32::<LE>(self.source_key)?;
writer.write_u8(self.feed_names.len() as _)?;
for name in self.feed_names.iter() {
name.write_to_bytes(&mut writer)?;
}
Ok(())
}
}
impl WriteToBytes for LaserFeedControl {
fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
writer.write_u8(self.feed_index)?;
writer.write_u8(self.frame_rate)?;
Ok(())
}
}
impl<'a> WriteToBytes for LaserFeedFrame<'a> {
fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
writer.write_u32::<LE>(self.source_key)?;
writer.write_u8(self.feed_index)?;
writer.write_u32::<LE>(self.frame_sequence)?;
writer.write_u16::<LE>(self.points.len() as _)?;
for p in self.points.iter() {
p.write_to_bytes(&mut writer)?;
}
Ok(())
}
}
impl WriteToBytes for LaserPoint {
fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
writer.write_u8(self.x_low_byte)?;
writer.write_u8(self.y_low_byte)?;
writer.write_u8(self.xy_high_nibbles)?;
writer.write_u16::<LE>(self.color)?;
Ok(())
}
}
impl ReadFromBytes for LaserFeedControl {
fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
let feed_index = reader.read_u8()?;
let frame_rate = reader.read_u8()?;
let laser_feed_control = LaserFeedControl {
feed_index,
frame_rate,
};
Ok(laser_feed_control)
}
}
impl ReadFromBytes for EnterShow {
fn read_from_bytes<R: ReadBytesExt>(reader: R) -> io::Result<Self> {
let name = Ucs2::read_from_bytes(reader)?;
Ok(EnterShow { name })
}
}
impl<'a> ReadFromBytes for FixtureList<'a> {
fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
let message_type: FixtureListMessageType = reader.read_u8()?.into();
let fixture_count = reader.read_u16::<LE>()?;
let mut fixtures = Vec::new();
for _ in 0..fixture_count {
let fixture_identifier = reader.read_u32::<LE>()?;
let manufacturer_name = Ucs2::read_from_bytes(&mut reader)?;
let fixture_name = Ucs2::read_from_bytes(&mut reader)?;
let mode_name = Ucs2::read_from_bytes(&mut reader)?;
let channel_count = reader.read_u16::<LE>()?;
let is_dimmer = reader.read_u8()?;
let identifier_count = reader.read_u8()?;
let mut identifiers = Vec::new();
for _ in 0..identifier_count {
identifiers.push(Identifier::read_from_bytes(&mut reader)?);
}
let data = FixtureData::read_from_bytes(&mut reader)?;
fixtures.push(Fixture {
fixture_identifier,
manufacturer_name,
fixture_name,
mode_name,
channel_count,
is_dimmer,
identifier_count,
identifiers: Cow::Owned(identifiers),
data,
})
}
Ok(FixtureList {
message_type,
fixture_count,
fixtures: Cow::Owned(fixtures),
})
}
}
impl<'a> ReadFromBytes for Identifier<'a> {
fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
let identifier_type: IdentifierType = reader.read_u8()?.into();
let data_size = reader.read_u16::<LE>()?;
let mut data = vec![0u8; data_size.into()];
reader.read_exact(&mut data)?;
Ok(Identifier {
identifier_type,
data_size,
data: Cow::Owned(data),
})
}
}
impl ReadFromBytes for FixtureData {
fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
Ok(FixtureData {
patched: reader.read_u8()?,
universe: reader.read_u8()?,
universe_channel: reader.read_u16::<LE>()?,
unit: Ucs2::read_from_bytes(&mut reader)?,
channel: reader.read_u16::<LE>()?,
circuit: Ucs2::read_from_bytes(&mut reader)?,
note: Ucs2::read_from_bytes(&mut reader)?,
position: [
reader.read_f32::<LE>()?,
reader.read_f32::<LE>()?,
reader.read_f32::<LE>()?,
],
angles: [
reader.read_f32::<LE>()?,
reader.read_f32::<LE>()?,
reader.read_f32::<LE>()?,
],
})
}
}
impl<'a> ReadFromBytes for FixtureRemove<'a> {
fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
let fixture_count = reader.read_u16::<LE>()?;
let mut fixture_identifiers = Vec::new();
for _ in 0..fixture_count {
fixture_identifiers.push(reader.read_u32::<LE>()?);
}
Ok(FixtureRemove {
fixture_count,
fixture_identifiers: Cow::Owned(fixture_identifiers),
})
}
}
impl SizeBytes for EnterShow {
fn size_bytes(&self) -> usize {
self.name.size_bytes()
}
}
impl<'a> SizeBytes for FixtureList<'a> {
fn size_bytes(&self) -> usize {
let mut fixtures_size = 0;
for fixture in self.fixtures.iter() {
fixtures_size += fixture.size_bytes();
}
mem::size_of::<u8>() + mem::size_of::<u16>() + fixtures_size
}
}
impl<'a> SizeBytes for Identifier<'a> {
fn size_bytes(&self) -> usize {
let mut data_size = 0;
for _ in self.data.iter() {
data_size += mem::size_of::<u8>();
}
mem::size_of::<u8>() + mem::size_of::<u16>() + data_size
}
}
impl<'a> SizeBytes for Fixture<'a> {
fn size_bytes(&self) -> usize {
let mut identifiers_size = 0;
for identifier in self.identifiers.iter() {
identifiers_size += identifier.size_bytes();
}
mem::size_of::<u32>()
+ self.manufacturer_name.size_bytes()
+ self.fixture_name.size_bytes()
+ self.mode_name.size_bytes()
+ mem::size_of::<u16>()
+ mem::size_of::<u8>()
+ mem::size_of::<u8>()
+ identifiers_size
+ self.data.size_bytes()
}
}
impl SizeBytes for FixtureData {
fn size_bytes(&self) -> usize {
mem::size_of::<u8>()
+ mem::size_of::<u8>()
+ mem::size_of::<u16>()
+ self.unit.size_bytes()
+ mem::size_of::<u16>()
+ self.circuit.size_bytes()
+ self.note.size_bytes()
+ (mem::size_of::<f32>() * 3)
+ (mem::size_of::<f32>() * 3)
}
}
impl<'a> SizeBytes for FixtureRemove<'a> {
fn size_bytes(&self) -> usize {
let mut fixture_ids_size = 0;
for _ in 0..self.fixture_count {
fixture_ids_size += mem::size_of::<u32>();
}
mem::size_of::<u16>() + fixture_ids_size
}
}
impl<'a> SizeBytes for LaserFeedList<'a> {
fn size_bytes(&self) -> usize {
let mut feed_names_size = 0;
for name in self.feed_names.iter() {
feed_names_size += name.size_bytes();
}
mem::size_of::<u32>() + mem::size_of::<u8>() + feed_names_size
}
}
impl SizeBytes for LaserFeedControl {
fn size_bytes(&self) -> usize {
mem::size_of::<u8>() + mem::size_of::<u8>()
}
}
impl<'a> SizeBytes for LaserFeedFrame<'a> {
fn size_bytes(&self) -> usize {
let mut ps = 0;
for p in self.points.iter() {
ps += p.size_bytes();
}
mem::size_of::<u32>()
+ mem::size_of::<u8>()
+ mem::size_of::<u32>()
+ mem::size_of::<u16>()
+ ps
}
}
impl SizeBytes for LaserPoint {
fn size_bytes(&self) -> usize {
mem::size_of::<u8>() + mem::size_of::<u8>() + mem::size_of::<u8>() + mem::size_of::<u16>()
}
}