1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
//! ## Protocol Types, Readers and Writers.
//!
//! All CITP protocol types can be written-to and read-from little-endian bytes using the
//! **WriteBytes** and **ReadBytes** traits respectively. These traits are implemented for all
//! types implementing the **std::io** **Write** and **Read** traits.
//!
//! Each layer of the protocol has it's own module. The "Base layer" is specified within this
//! module.
//!
//! *Note that not all types within these modules have a layout that exactly matches the C
//! specification, however the **WriteToBytes** and **ReadFromBytes** implementations should match
//! exactly. This is because some types can be much better expressed in rust via the std slice
//! types rather than separate "counter" and array pointer fields which require `unsafe` blocks to
//! use.*
//!
//! ## CITP - Base Layer
//!
//! This module also specifies the base layer as described within the protocol. The base layaer
//! does not define any packages, it merely adds a header that encapsulate all messages.
//!
//! ## Reading a Stream.
//!
//! To read the protocol from a stream of little endian bytes where the received messages are not
//! known ahead of time, the following steps may be followed:
//!
//! - Read the full base **Header** first.
//! - Match on the `content_type` field to determine the next layer to read.
//! - Read the header for the second layer.
//! - Match on the `content_type` field of the second layer to determine what type to read.

pub use byteorder::{LE, ReadBytesExt, WriteBytesExt};
use std::ffi::CString;
use std::{fmt, io, mem};
use std::hash::{Hash, Hasher};

/// ## CITP/PINF - Peer Information Layer
///
/// The Peer Information Layer is used to exchange peer information, both when connected and during
/// discovery.
///
/// The PINF/PNam message was originally broadcasted on UDP port 4810 as a means of discovery. This
/// was then replaced with the PINF/PLoc message being multicasted on address 244.0.0.180, port
/// 4809 instead. Since early 2014, the multicast address was changed to 239.224.0.180 with the
/// recommendation that systems also support using the previous 224.0.0.180 address during a
/// transitional period.
///
/// Once two peers have established a direct TCP connection, a PINF/PName message should
/// immediately be sent as the first message.
pub mod pinf;

/// The SDMX layer is used to transmit DMX information. CITP supports transmitting a single - wide
/// - universe of DMX channels with at most 65_536 channels. It also supports designating an
/// alternative DMX source such as ArtNet or ETCNet2 (see "connection strings").
pub mod sdmx;

/// ## CITP/FPTC - Fixture patch layer
///
/// The Fixture Patch layer is used to communicate fixture existence and patch information.
/// Fixtures are identified by 16-bit unsigned integers with a range of valid values between 1 and
/// 65535. In most consoles this value maps directly to a "Channel", "Unit" or "Device".
///
/// The FPTC layer is built on the following design decisions:
///
/// - Unpatched fixtures do not exist from the FPTC layer's point of view. When a fixture is
///   unpatched using the `UnPatch` message, it is deleted and seizes to exist. However, the
///   fixture may continue to live in the visualiser or the console, without association to a
///   universe.  Whenever the fixture is associated with a universe again, it is reintroduced
///   through the `Patch` message.
/// - When a fixture is repatched (ie. moved to another channel or universe) it does not pass
///   through an unpatched state.
/// - In the visualiser, it may possible to change the mode of a fixture. Different modes for one
///   fixture usually use different amounts of channels, however sometimes a different mode ony
///   changes the interpretation of one or more control channels. When a mode is changed in the
///   visualiser, an unpatch message is not sent, only a new patch message. If the new mode
///   consumes a different amount of channels, this can be told by the `ChannelCount` field of the
///   patch message. If it does not, there is no way of telling.
/// - A fixture can change its patch and mode, but never its make or name. The visualiser attempts
///   to map the fixture make and name against its library.
/// - Fixture identifiers must be persistent. When both the visualiser and the console have
///   reloaded a pair of matching projects, the fixture identifiers must still be the same.
/// - When a project is closed on either side, fixtures are not unpatched. The same applies to when
///   a universe in the visualiser is deleted or unassociated with a console.
/// - No synchronisation mechanism exists in CITP, which communicates project closing/opening
///   information. This must be handled by the user by opening and closing matching projects
///   simultaneously.
/// - When the visualiser or console takes automatic actions as a result of incoming patch
///   messages, it must not result in an echo.
pub mod fptc;

/// ## CITP/FSEL - Fixture Selection layer
///
/// The Fixture Selection layer is used to carry fixture selection information. Fixture
/// identification is discussed in the `fptc` documentation.
pub mod fsel;

/// ## CITP/FINF - Fixture Information layer
///
/// The Fixture Information layer is used to carry additional fixture information. Fixture
/// identification is discussed within the `fptc` documentation.
pub mod finf;

/// ## CITP/MSEX - Media Server Extensions layer
///
/// The Media Server EXtensions layer is used for communication with media servers.
///
/// For information about how peers find eachother and connect, see the Connectivity section.
/// Typically all packets are sent over a peer-to-peer TCP socket connection, except for the
/// MSEX/StFr message which is sent over the multicast address for all to process.
///
/// ### MSEX Versions
///
/// Currently acknowledged versions of MSEX are 1.0, 1.1 and 1.2. During a session, the appropriate
/// MSEX version that is common to both sides must be established and used for all communication -
/// different versions cannot be mixed in a single session. See the MSEX/SInf and MSEX/CInf
/// messages also regarding supported version signaling.
///
/// Prior to MSEX 1.2 it was expected that all client and server implementations check the MSEX
/// version of all received messages to ensure that the message format is acceptable. Starting with
/// MSEX 1.2 this is a mandatory requirement.
///
/// There is no requirement for an implementation of a specific MSEX version to support any
/// previous MSEX versions, for this reason the version returned by the MSEX/SInf message must be
/// used for all communication by both sides.
///
/// ### Establishing Connections
///
/// Prior to MSEX 1.2, a media server was expected to send a MSEX/SInf Server Information message
/// immediately after connecting to a lighting console or visualiser. This approach has the
/// drawback that the MSEX/SInf message format has to be fixed since the media server is unaware of
/// what MSEX version(s) the other side supports. Starting with MSEX 1.2, the lighting console or
/// visualiser must send a MSEX/CInf Client Information message to the server immediately after
/// connecting, and the server will respond with a version 1.2 or later MSEX/SInf message.
///
/// NB: Although the MSEX/CInf message format must be fixed, provision has been made to allow extra
/// data to be appended as a future-proofing measure.
///
/// ### Highest Common MSEX Version
///
/// For MSEX 1.2 and later, the server must establish the Highest Common MSEX Version when a
/// MSEX/CInf is received from a newly connected lighting console or media server. This is the
/// highest MSEX version that is supported on both sides, and must be used for all unsolicited
/// messages, such as MSEX/SInf, MSEX/LSta and MSEX/ELUp. The Highest Common MSEX version is at
/// least 1.2.
///
/// ### Mandatory messages
///
/// Implementations can choose to implement a subset of MSEX messages to suit their needs, but some
/// messages are essential for correct interoperation and are marked as mandatory. The mandatory
/// messages are:
///
/// 1. CInf - Client Information message
/// 2. SInf - Server Information message
/// 3. LSta - Layer Status message
/// 4. Nack - Negative acknowledge message
///
/// ### Image formats
///
/// MSEX supports three image formats for thumbnails and five image formats for video stream
/// frames:
///
/// - RGB8 - a raw array of 8-byte RGB triples (this is **not** BMP). In MSEX 1.0 the byte order
/// was BGR, but from MSEX 1.1 the byte order is RGB.
/// - JPEG - the well known file format (which does **not** include EXIF).
/// - PNG - the well known file format. Requires MSEX 1.2.
/// - Fragmented JPB - JPEG data fragments (for streams only). Requires MSEX 1.2.
/// - Fragmented PNG - PNG data fragments (for streams oly). Requires MSEX 1.2.
pub mod msex;

/// A trait for writing any of the CITP protocol types to little-endian bytes.
///
/// A blanket implementation is provided for all types that implement `byteorder::WriteBytesExt`.
pub trait WriteBytes {
    fn write_bytes<P: WriteToBytes>(&mut self, protocol: P) -> io::Result<()>;
}

/// A trait for reading any of the CITP protocol types from little-endian bytes.
///
/// A blanket implementation is provided for all types that implement `byteorder::ReadBytesExt`.
pub trait ReadBytes {
    fn read_bytes<P: ReadFromBytes>(&mut self) -> io::Result<P>;
}

/// Protocol types that may be written to little endian bytes.
pub trait WriteToBytes {
    /// Write the command to bytes.
    fn write_to_bytes<W: WriteBytesExt>(&self, W) -> io::Result<()>;
}

/// Protocol types that may be read from little endian bytes.
pub trait ReadFromBytes: Sized {
    /// Read the command from bytes.
    fn read_from_bytes<R: ReadBytesExt>(R) -> io::Result<Self>;
}

/// Types that have a constant size when written to or read from bytes.
pub trait ConstSizeBytes: SizeBytes {
    const SIZE_BYTES: usize;
}

/// Types whose size when written to bytes may be determined at runtime.
pub trait SizeBytes {
    fn size_bytes(&self) -> usize;
}

/// The CITP layer provides a standard, single, header used at the start of all CITP packets.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[repr(C)]
pub struct Header {
    /// Set to "CITP"
    pub cookie: u32,
    /// Set to 1.
    pub version_major: u8,
    /// Set to 0.
    pub version_minor: u8,
    /// These allow request/response message pairs to be better associated and is particularly useful
    /// for debugging purposes. A node that sends request messages (such as a Lighting Console
    /// requesting info from a Media Server) should maintain a request counter, and increment this with
    /// every request message sent. When the other side sends a response to a specific request message,
    /// it should set this field to the same value as was found in the corresponding request message.
    ///
    /// The value of `0` is taken to mean `ignored`, so proper `RequestIndex` values should start
    /// at `1` (and wrap back around to `1`, avoiding the `0` "ignored" value). This was introduced
    /// for MSEX 1.2 and was previously a reserved 2-byte alignment field.
    pub kind: Kind,
    /// The size of the entire message, including this header.
    pub message_size: u32,
    /// Number of message fragments.
    pub message_part_count: u16,
    /// Index of this message fragment (0-based).
    pub message_part: u16,
    /// Cookie identifying the type of contents (the name of the second layer).
    pub content_type: u32,
}

#[derive(Copy, Clone)]
#[repr(C)]
pub union Kind {
    request_index: u16,
    in_response_to: u16,
}

impl WriteToBytes for Kind {
    fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
        unsafe {
            writer.write_u16::<LE>(self.request_index)
        }
    }
}

impl WriteToBytes for Header {
    fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
        writer.write_u32::<LE>(self.cookie)?;
        writer.write_u8(self.version_major)?;
        writer.write_u8(self.version_minor)?;
        writer.write_bytes(self.kind)?;
        writer.write_u32::<LE>(self.message_size)?;
        writer.write_u16::<LE>(self.message_part_count)?;
        writer.write_u16::<LE>(self.message_part)?;
        writer.write_u32::<LE>(self.content_type)?;
        Ok(())
    }
}

impl ReadFromBytes for Kind {
    fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
        let request_index = reader.read_u16::<LE>()?;
        Ok(Kind { request_index })
    }
}

impl ReadFromBytes for Header {
    fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
        let cookie = reader.read_u32::<LE>()?;
        let version_major = reader.read_u8()?;
        let version_minor = reader.read_u8()?;
        let kind = reader.read_bytes()?;
        let message_size = reader.read_u32::<LE>()?;
        let message_part_count = reader.read_u16::<LE>()?;
        let message_part = reader.read_u16::<LE>()?;
        let content_type = reader.read_u32::<LE>()?;
        let header = Header {
            cookie,
            version_major,
            version_minor,
            kind,
            message_size,
            message_part_count,
            message_part,
            content_type,
        };
        Ok(header)
    }
}

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)
    }
}

impl<'a, T> WriteToBytes for &'a T
where
    T: WriteToBytes,
{
    fn write_to_bytes<W: WriteBytesExt>(&self, writer: W) -> io::Result<()> {
        (**self).write_to_bytes(writer)
    }
}

impl WriteToBytes for CString {
    fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
        let bytes = self.as_bytes_with_nul();
        for &byte in bytes {
            writer.write_u8(byte)?;
        }
        Ok(())
    }
}

impl ReadFromBytes for CString {
    fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
        let mut bytes = vec![];
        loop {
            match reader.read_u8()? {
                b'\0' => break,
                byte => bytes.push(byte),
            }
        }
        let cstring = unsafe {
            CString::from_vec_unchecked(bytes)
        };
        Ok(cstring)
    }
}

impl ReadFromBytes for u8 {
    fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
        reader.read_u8()
    }
}

impl ReadFromBytes for u16 {
    fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
        reader.read_u16::<LE>()
    }
}

impl SizeBytes for CString {
    fn size_bytes(&self) -> usize {
        self.as_bytes_with_nul().len()
    }
}

impl SizeBytes for Kind {
    fn size_bytes(&self) -> usize {
        mem::size_of::<Kind>()
    }
}

impl SizeBytes for Header {
    fn size_bytes(&self) -> usize {
        mem::size_of::<Header>()
    }
}

impl fmt::Debug for Kind {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        unsafe {
            write!(f, "{:?}", self.request_index)
        }
    }
}

impl Eq for Kind {}

impl PartialEq for Kind {
    fn eq(&self, other: &Self) -> bool {
        unsafe {
            self.request_index == other.request_index
        }
    }
}

impl Hash for Kind {
    fn hash<H: Hasher>(&self, state: &mut H) {
        unsafe {
            self.request_index.hash(state);
        }
    }
}

/// Read **len** elements of type **T** into the given **vec**.
pub fn read_vec<R, T>(mut reader: R, mut len: usize, vec: &mut Vec<T>) -> io::Result<()>
where
    R: ReadBytesExt,
    T: ReadFromBytes,
{
    while len > 0 {
        let elem = reader.read_bytes()?;
        vec.push(elem);
        len -= 1;
    }
    Ok(())
}

/// Read **len** elements of type **T** into a new **Vec**.
pub fn read_new_vec<R, T>(reader: R, len: usize) -> io::Result<Vec<T>>
where
    R: ReadBytesExt,
    T: ReadFromBytes,
{
    let mut vec = Vec::with_capacity(len);
    read_vec(reader, len, &mut vec)?;
    Ok(vec)
}