citp 0.2.1

A full implementation of CITP - Controller Interface Transport Protocol.
Documentation
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
428
use crate::protocol::{
    self, ReadBytes, ReadBytesExt, ReadFromBytes, SizeBytes, WriteBytes, WriteBytesExt,
    WriteToBytes, LE,
};
use std::borrow::Cow;
use std::ffi::CString;
use std::{self, io, mem};

/// ## The SDMX header.
///
/// The SDMX layer provides a standard, single, header used at the start of all SDMX packets.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[repr(C)]
pub struct Header {
    /// The CITP header. CITP ContentType is "SDMX".
    pub citp_header: protocol::Header,
    /// Cookie defining which SDMX message it is.
    pub content_type: u32,
}

/// SDMX messages are always prefixed with a CITP SDMX header.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[repr(C)]
pub struct Message<T> {
    /// The base header along with the SDMX content type.
    pub sdmx_header: Header,
    /// The unique contents of the message.
    pub message: T,
}

/// ## SDMX / Capa - Capabilities message.
///
/// The capabilities message can be sent by a peer to the remote peer upon connect to inform the
/// remote peer about the capabilities.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[repr(C)]
pub struct Capa<'a> {
    /// A list of capabilities.
    ///
    /// See the `Caps` associated constants for possible capabilities.
    ///
    /// - 1   - ChLs channel list.
    /// - 2   - SXSr external source.
    /// - 3   - SXUS per-universe external sources.
    /// - 101 - Art-Net external sources.
    /// - 102 - BSR E1.31 external sources.
    /// - 103 - ETC Net2 external sources.
    /// - 104 - MA-Net external sources.
    pub capabilities: Cow<'a, [u16]>,
}

/// ## SDMX / UNam - Universe Name message.
///
/// The universe name message can be sent by a DMX transmitting peer in order to provide the other
/// end with a displayable name of a universe.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[repr(C)]
pub struct UNam {
    /// `0`-based index of the universe.
    pub universe_index: u8,
    /// Name of the universe.
    pub universe_name: CString,
}

/// ## SDMX / EnId - Encryption identifier message.
///
/// The EncryptionIdentifier messages is used to agree on encryption schemes when transferring DMX
/// channels. The usage of this message depends completely on the peers communicating it; the
/// contents and results of this message is not part of the CITP specification - it must be agreed
/// upon a priori.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[repr(C)]
pub struct EnId {
    /// Encryption scheme identifier.
    pub identifier: CString,
}

/// ## SDMX / ChBk - Channel Block message.
///
/// The Channel Block message transmits raw DMX levels to the recipient. How to handle Blind DMX
/// levels is up to the recipient, but the recommended procedure for a visualiser is to switch over
/// to blind DMX whenever such is present and to revert back after some short timeout when it is no
/// longer transmitted.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[repr(C)]
pub struct ChBk<'a> {
    /// Set to `1` for blind preview dmx, `0` otherwise.
    pub blind: u8,
    /// `0`-based index of the universe.
    pub universe_index: u8,
    /// `0` based index of first channel in the universe.
    pub first_channel: u16,
    /// Raw channel levels.
    pub channel_levels: Cow<'a, [u8]>,
}

/// ## SDMX / ChLs - Channel List message
///
/// The Channel List message transmits a set of non-consecutive DMX levels. This message should
/// only be sent if the remote peer has acknowledged supporting it in a Capabilities message.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[repr(C)]
pub struct ChLs<'a> {
    /// The list of channel levels.
    pub channel_levels: Cow<'a, [ChannelLevel]>,
}

/// A single channel level within a list specified via a `ChLs` message.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[repr(C)]
pub struct ChannelLevel {
    /// `0`-based index of the universe.
    universe_index: u8,
    /// `0`-based index of the channel in the universe.
    channel: u16,
    /// DMX channel level.
    channel_level: u8,
}

/// ## SDMX / SXSr - Set External Source message.
///
/// The Set External Source message can be sent as an alternative to sending `ChBk` messages when
/// DMX can be received over another protocol. In the event of handling multiple universes, the
/// external source specified should be treated as the base universe of a consecutive series of
/// universes.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[repr(C)]
pub struct SXSr {
    /// DMX-source connction string.
    ///
    /// The following connection strings are currently defined:
    ///
    /// - **Art-Net**: "ArtNet/<net>/<universe>/<channel>", ie. "ArtNet/0/0/1" is the first channel
    ///   of the first universe on the first network.
    /// - **BSR E1.31 / sACN**: "BSRE1.31/<universe>/<channel>", ie. "BSRE1.31/1/1" is the first
    ///   channel of the first universe.
    /// - **ETC Net2**: "EtcNet2/<channel>", ie. "ETCNet2/1" is the first ETCNet2 channel.
    /// - **MA-Net**: "MANet/<type>/<universe>/<channel>", ie. "MANet/2/0/1" is the first channel
    /// of the first MA-Net 2 universe.
    pub connection_string: CString,
}

/// ## SDMX/SXUS - Set External Universe Source message.
///
/// The Set External Universe Source message functions like the Set External Source mssage, but on
/// a universe level rather than a global level.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[repr(C)]
pub struct Sxus {
    /// `0`-based index of the universe.
    pub universe_index: u8,
    /// DMX-source connection string - as the SXSr message.
    pub connection_string: CString,
}

impl Header {
    pub const CONTENT_TYPE: &'static [u8; 4] = b"SDMX";
}

impl<'a> Capa<'a> {
    pub const CONTENT_TYPE: &'static [u8; 4] = b"Capa";

    pub const CHANNEL_LIST: u16 = 1;
    pub const EXTERNAL_SOURCE: u16 = 2;
    pub const PER_UNIVERSE_EXTERNAL_SOURCES: u16 = 3;
    pub const ART_NET_EXTERNAL_SOURCES: u16 = 101;
    pub const BSR_E131_EXTERNAL_SOURCES: u16 = 102;
    pub const ETC_NET2_EXTERNAL_SOURCES: u16 = 103;
    pub const MA_NET_EXTERNAL_SOURCES: u16 = 103;
}

impl UNam {
    pub const CONTENT_TYPE: &'static [u8; 4] = b"UNam";
}

impl EnId {
    pub const CONTENT_TYPE: &'static [u8; 4] = b"EnId";
}

impl<'a> ChBk<'a> {
    pub const CONTENT_TYPE: &'static [u8; 4] = b"ChBk";
}

impl<'a> ChLs<'a> {
    pub const CONTENT_TYPE: &'static [u8; 4] = b"ChLs";
}

impl SXSr {
    pub const CONTENT_TYPE: &'static [u8; 4] = b"SXSr";
}

impl Sxus {
    pub const CONTENT_TYPE: &'static [u8; 4] = b"SXUS";
}

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.sdmx_header)?;
        writer.write_bytes(&self.message)?;
        Ok(())
    }
}

impl<'a> WriteToBytes for Capa<'a> {
    fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
        if self.capabilities.len() > std::u16::MAX as usize {
            let err_msg = "the number of capabilities exceeds the maximum possible `u16` value";
            return Err(io::Error::new(io::ErrorKind::InvalidData, err_msg));
        }
        writer.write_u16::<LE>(self.capabilities.len() as u16)?;
        for &cap in self.capabilities.iter() {
            writer.write_u16::<LE>(cap)?;
        }
        Ok(())
    }
}

impl WriteToBytes for UNam {
    fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
        writer.write_u8(self.universe_index)?;
        writer.write_bytes(&self.universe_name)?;
        Ok(())
    }
}

impl WriteToBytes for EnId {
    fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
        writer.write_bytes(&self.identifier)?;
        Ok(())
    }
}

impl<'a> WriteToBytes for ChBk<'a> {
    fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
        writer.write_u8(self.blind)?;
        writer.write_u8(self.universe_index)?;
        writer.write_u16::<LE>(self.first_channel)?;
        writer.write_u16::<LE>(self.channel_levels.len() as _)?;
        for &lvl in self.channel_levels.iter() {
            writer.write_u8(lvl)?;
        }
        Ok(())
    }
}

impl WriteToBytes for ChannelLevel {
    fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
        writer.write_u8(self.universe_index)?;
        writer.write_u16::<LE>(self.channel)?;
        writer.write_u8(self.channel_level)?;
        Ok(())
    }
}

impl<'a> WriteToBytes for ChLs<'a> {
    fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
        writer.write_u16::<LE>(self.channel_levels.len() as _)?;
        for ch in self.channel_levels.iter() {
            writer.write_bytes(ch)?;
        }
        Ok(())
    }
}

impl WriteToBytes for SXSr {
    fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
        writer.write_bytes(&self.connection_string)?;
        Ok(())
    }
}

impl WriteToBytes for Sxus {
    fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
        writer.write_u8(self.universe_index)?;
        writer.write_bytes(&self.connection_string)?;
        Ok(())
    }
}

impl ReadFromBytes for Capa<'static> {
    fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
        let capability_count: u16 = reader.read_bytes()?;
        let capabilities = protocol::read_new_vec(reader, capability_count as _)?;
        let capabilities = Capa {
            capabilities: Cow::Owned(capabilities),
        };
        Ok(capabilities)
    }
}

impl ReadFromBytes for UNam {
    fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
        let universe_index = reader.read_u8()?;
        let universe_name = reader.read_bytes()?;
        let unam = UNam {
            universe_index,
            universe_name,
        };
        Ok(unam)
    }
}

impl ReadFromBytes for EnId {
    fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
        let identifier = reader.read_bytes()?;
        let enid = EnId { identifier };
        Ok(enid)
    }
}

impl ReadFromBytes for ChBk<'static> {
    fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
        let blind = reader.read_u8()?;
        let universe_index = reader.read_u8()?;
        let first_channel = reader.read_u16::<LE>()?;
        let channel_level_count: u16 = reader.read_u16::<LE>()?;
        let channel_levels = protocol::read_new_vec(reader, channel_level_count as _)?;
        let channel_levels = Cow::Owned(channel_levels);
        let chbk = ChBk {
            blind,
            universe_index,
            first_channel,
            channel_levels,
        };
        Ok(chbk)
    }
}

impl ReadFromBytes for ChannelLevel {
    fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
        let universe_index = reader.read_u8()?;
        let channel = reader.read_u16::<LE>()?;
        let channel_level = reader.read_u8()?;
        let ch = ChannelLevel {
            universe_index,
            channel,
            channel_level,
        };
        Ok(ch)
    }
}

impl ReadFromBytes for ChLs<'static> {
    fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
        let channel_level_count = reader.read_u16::<LE>()?;
        let channel_levels = protocol::read_new_vec(reader, channel_level_count as _)?;
        let channel_levels = Cow::Owned(channel_levels);
        let chls = ChLs { channel_levels };
        Ok(chls)
    }
}

impl ReadFromBytes for SXSr {
    fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
        let connection_string = reader.read_bytes()?;
        let sxsr = SXSr { connection_string };
        Ok(sxsr)
    }
}

impl ReadFromBytes for Sxus {
    fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
        let universe_index = reader.read_u8()?;
        let connection_string = reader.read_bytes()?;
        let sxus = Sxus {
            universe_index,
            connection_string,
        };
        Ok(sxus)
    }
}

impl<'a> SizeBytes for Capa<'a> {
    fn size_bytes(&self) -> usize {
        mem::size_of::<u16>() + self.capabilities.len() * mem::size_of::<u16>()
    }
}

impl SizeBytes for UNam {
    fn size_bytes(&self) -> usize {
        mem::size_of::<u8>() + self.universe_name.size_bytes()
    }
}

impl SizeBytes for EnId {
    fn size_bytes(&self) -> usize {
        self.identifier.size_bytes()
    }
}

impl<'a> SizeBytes for ChBk<'a> {
    fn size_bytes(&self) -> usize {
        mem::size_of::<u8>()
            + mem::size_of::<u8>()
            + mem::size_of::<u16>()
            + mem::size_of::<u16>()
            + self.channel_levels.len() * mem::size_of::<u8>()
    }
}

impl<'a> SizeBytes for ChLs<'a> {
    fn size_bytes(&self) -> usize {
        mem::size_of::<u16>() + self.channel_levels.len() * mem::size_of::<ChannelLevel>()
    }
}

impl SizeBytes for SXSr {
    fn size_bytes(&self) -> usize {
        self.connection_string.size_bytes()
    }
}

impl SizeBytes for Sxus {
    fn size_bytes(&self) -> usize {
        mem::size_of::<u8>() + self.connection_string.size_bytes()
    }
}