dmx512_rdm_protocol/dmx/
mod.rs

1//! Data types and functionality for encoding and decoding DMX512 packets
2//!
3//! ### DmxUniverse
4//!
5//! ```rust
6//! use dmx512_rdm_protocol::dmx::DmxUniverse;
7//!
8//! // Create a 512 channel universe
9//! let dmx_universe = DmxUniverse::default();
10//! // or create a smaller universe
11//! 
12//! // If you use the heap allocation implementations, you can create smaller universes
13//! #[cfg(feature = "alloc")]
14//! let dmx_universe = DmxUniverse::new(4).unwrap();
15//! // or decode a dmx packet
16//! let mut dmx_universe = DmxUniverse::decode(&[0, 255, 255, 0, 0]).unwrap();
17//!
18//! assert_eq!(&dmx_universe.as_slice()[..4], &[255, 255, 0, 0]);
19//!
20//! dmx_universe.set_channel_value(0, 64).unwrap();
21//! dmx_universe.set_channel_values(1, &[128, 192, 255]).unwrap();
22//!
23//! assert_eq!(dmx_universe.get_channel_value(0).unwrap(), 64);
24//! assert_eq!(dmx_universe.get_channel_values(1..=2).unwrap(), &[128, 192]);
25//! assert_eq!(&dmx_universe.as_slice()[..4], &[64, 128, 192, 255]);
26//! assert_eq!(&dmx_universe.encode()[..5], &[0, 64, 128, 192, 255]);
27//! ```
28
29pub mod error;
30pub const DMX_START_CODE: u8 = 0;
31#[cfg(feature = "alloc")]
32pub const MAXIMUM_CHANNEL_COUNT: u16 = 512;
33#[cfg(not(feature = "alloc"))]
34pub const MAXIMUM_CHANNEL_COUNT: usize = 512;
35
36use core::ops::{Index, IndexMut, RangeInclusive};
37use error::DmxError;
38
39#[cfg(not(feature = "alloc"))]
40use heapless::Vec;
41
42#[cfg(feature = "alloc")]
43#[derive(Clone, Debug, PartialEq)]
44pub struct DmxUniverse {
45    pub channel_count: u16,
46    channels: Vec<u8>,
47}
48
49#[cfg(not(feature = "alloc"))]
50#[derive(Clone, Debug, PartialEq)]
51pub struct DmxUniverse(Vec<u8, MAXIMUM_CHANNEL_COUNT>);
52
53impl DmxUniverse {
54    #[cfg(feature = "alloc")]
55    pub fn new(channel_count: u16) -> Result<Self, DmxError> {
56        if channel_count > MAXIMUM_CHANNEL_COUNT {
57            return Err(DmxError::InvalidChannelCount(channel_count));
58        }
59
60        Ok(Self {
61            channel_count,
62            channels: vec![0; channel_count as usize],
63        })
64    }
65    #[cfg(not(feature = "alloc"))]
66    pub fn new() -> Self {
67        Self(Vec::from_slice(&[0; MAXIMUM_CHANNEL_COUNT]).unwrap())
68    }
69
70    pub fn reset(&mut self) {
71        #[cfg(feature = "alloc")]
72        self.channels.fill(0);
73        #[cfg(not(feature = "alloc"))]
74        self.0.fill(0);
75    }
76
77    #[cfg(feature = "alloc")]
78    pub fn get_channel_value(&self, channel: u16) -> Result<u8, DmxError> {
79        if channel < self.channel_count {
80            Ok(self.channels[channel as usize])
81        } else {
82            Err(DmxError::ChannelOutOfBounds)
83        }
84    }
85    #[cfg(not(feature = "alloc"))]
86    pub fn get_channel_value(&self, channel: u16) -> Result<u8, DmxError> {
87        if channel < MAXIMUM_CHANNEL_COUNT as u16 {
88            Ok(self.0[channel as usize])
89        } else {
90            Err(DmxError::ChannelOutOfBounds)
91        }
92    }
93
94    #[cfg(feature = "alloc")]
95    pub fn get_channel_values(&self, range: RangeInclusive<u16>) -> Result<&[u8], DmxError> {
96        let start = *range.start();
97        let end = *range.end();
98        if end < self.channel_count {
99            Ok(&self.channels[start as usize..=end as usize])
100        } else {
101            Err(DmxError::ChannelOutOfBounds)
102        }
103    }
104
105    #[cfg(not(feature = "alloc"))]
106    pub fn get_channel_values(&self, range: RangeInclusive<u16>) -> Result<&[u8], DmxError> {
107        let start = *range.start();
108        let end = *range.end();
109        if end < MAXIMUM_CHANNEL_COUNT as u16 {
110            Ok(&self.0[start as usize..=end as usize])
111        } else {
112            Err(DmxError::ChannelOutOfBounds)
113        }
114    }
115
116    #[cfg(feature = "alloc")]
117    pub fn set_channel_value(&mut self, channel: u16, value: u8) -> Result<(), DmxError> {
118        if channel < self.channel_count {
119            self.channels[channel as usize] = value;
120
121            Ok(())
122        } else {
123            Err(DmxError::ChannelOutOfBounds)
124        }
125    }
126    #[cfg(not(feature = "alloc"))]
127    pub fn set_channel_value(&mut self, channel: u16, value: u8) -> Result<(), DmxError> {
128        if channel < MAXIMUM_CHANNEL_COUNT as u16 {
129            self.0[channel as usize] = value;
130            Ok(())
131        } else {
132            Err(DmxError::ChannelOutOfBounds)
133        }
134    }
135
136    #[cfg(feature = "alloc")]
137    pub fn set_channel_values(&mut self, channel: u16, values: &[u8]) -> Result<(), DmxError> {
138        if channel + (values.len() as u16) <= self.channel_count {
139            for (i, &value) in values.iter().enumerate() {
140                self.channels[channel as usize + i] = value;
141            }
142            Ok(())
143        } else {
144            Err(DmxError::ChannelOutOfBounds)
145        }
146    }
147    #[cfg(not(feature = "alloc"))]
148    pub fn set_channel_values(&mut self, channel: u16, values: &[u8]) -> Result<(), DmxError> {
149        if channel + (values.len() as u16) <= MAXIMUM_CHANNEL_COUNT as u16 {
150            for (i, &value) in values.iter().enumerate() {
151                self.0[channel as usize + i] = value;
152            }
153            Ok(())
154        } else {
155            Err(DmxError::ChannelOutOfBounds)
156        }
157    }
158
159    pub fn set_all_channel_values(&mut self, value: u8) {
160        #[cfg(feature = "alloc")]
161        self.channels.fill(value);
162        #[cfg(not(feature = "alloc"))]
163        self.0.fill(value);
164    }
165
166    pub fn as_slice(&self) -> &[u8] {
167        #[cfg(feature = "alloc")]
168        return self.channels.as_slice();
169        #[cfg(not(feature = "alloc"))]
170        self.0.as_slice()
171    }
172
173    #[cfg(not(feature = "alloc"))]
174    pub fn from_slice(bytes: &[u8]) -> Result<Self, DmxError> {
175        if bytes.len() > MAXIMUM_CHANNEL_COUNT {
176            return Err(DmxError::InvalidChannelCount(bytes.len() as u16));
177        }
178
179        let mut universe = Self::new();
180
181        universe.0[0..bytes.len()].copy_from_slice(bytes);
182
183        Ok(universe)
184    }
185
186    #[cfg(feature = "alloc")]
187    pub fn extend(&mut self, values: &[u8]) -> Result<(), DmxError> {
188        if self.channel_count as usize + values.len() > MAXIMUM_CHANNEL_COUNT as usize {
189            return Err(DmxError::InvalidChannelCount(
190                self.channels.len() as u16 + values.len() as u16,
191            ));
192        }
193
194        self.channels.extend(values);
195        self.channel_count += values.len() as u16;
196
197        Ok(())
198    }
199
200    #[cfg(feature = "alloc")]
201    pub fn decode(bytes: &[u8]) -> Result<Self, DmxError> {
202        if bytes.len() < 2 || bytes.len() > MAXIMUM_CHANNEL_COUNT as usize + 1 {
203            return Err(DmxError::InvalidFrameLength(bytes.len() as u16));
204        }
205
206        if bytes[0] != DMX_START_CODE {
207            return Err(DmxError::InvalidStartCode(bytes[0]));
208        }
209
210        Ok(Self {
211            channel_count: (bytes.len() - 1) as u16,
212            channels: bytes[1..].to_vec(),
213        })
214    }
215    #[cfg(not(feature = "alloc"))]
216    pub fn decode(bytes: &[u8]) -> Result<Self, DmxError> {
217        if bytes.len() < 2 || bytes.len() > MAXIMUM_CHANNEL_COUNT + 1 {
218            return Err(DmxError::InvalidFrameLength(bytes.len() as u16));
219        }
220
221        if bytes[0] != DMX_START_CODE {
222            return Err(DmxError::InvalidStartCode(bytes[0]));
223        }
224
225        Self::from_slice(&bytes[1..])
226    }
227
228    #[cfg(feature = "alloc")]
229    pub fn encode(&self) -> Vec<u8> {
230        let mut frame: Vec<u8> = Vec::with_capacity(self.channel_count as usize + 1);
231
232        frame.push(DMX_START_CODE);
233        frame.extend(self.channels.iter());
234
235        frame
236    }
237    #[cfg(not(feature = "alloc"))]
238    pub fn encode(&self) -> Vec<u8, 513> {
239        let mut frame = Vec::<u8, 513>::new();
240
241        frame.push(DMX_START_CODE).unwrap();
242        frame.extend_from_slice(&self.0[..]).unwrap();
243
244        frame
245    }
246}
247
248impl Default for DmxUniverse {
249    #[cfg(feature = "alloc")]
250    fn default() -> Self {
251        Self {
252            channel_count: MAXIMUM_CHANNEL_COUNT,
253            channels: vec![0; MAXIMUM_CHANNEL_COUNT as usize],
254        }
255    }
256    #[cfg(not(feature = "alloc"))]
257    fn default() -> Self {
258        Self::new()
259    }
260}
261
262impl Index<u16> for DmxUniverse {
263    type Output = u8;
264
265    #[cfg(feature = "alloc")]
266    fn index(&self, index: u16) -> &Self::Output {
267        &self.channels[index as usize]
268    }
269    #[cfg(not(feature = "alloc"))]
270    fn index(&self, index: u16) -> &Self::Output {
271        &self.0[index as usize]
272    }
273}
274
275impl IndexMut<u16> for DmxUniverse {
276    #[cfg(feature = "alloc")]
277    fn index_mut(&mut self, index: u16) -> &mut Self::Output {
278        &mut self.channels[index as usize]
279    }
280    #[cfg(not(feature = "alloc"))]
281    fn index_mut(&mut self, index: u16) -> &mut Self::Output {
282        &mut self.0[index as usize]
283    }
284}
285
286impl TryFrom<&[u8]> for DmxUniverse {
287    type Error = DmxError;
288
289    #[cfg(feature = "alloc")]
290    fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
291        if bytes.len() as u16 > MAXIMUM_CHANNEL_COUNT {
292            return Err(DmxError::InvalidChannelCount(bytes.len() as u16));
293        }
294
295        Ok(DmxUniverse {
296            channel_count: bytes.len() as u16,
297            channels: bytes.to_vec(),
298        })
299    }
300    #[cfg(not(feature = "alloc"))]
301    fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
302        Self::from_slice(bytes)
303    }
304}
305
306#[cfg(feature = "alloc")]
307impl TryFrom<Vec<u8>> for DmxUniverse {
308    type Error = DmxError;
309
310    fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
311        if bytes.len() as u16 > MAXIMUM_CHANNEL_COUNT {
312            return Err(DmxError::InvalidChannelCount(bytes.len() as u16));
313        }
314
315        Ok(DmxUniverse {
316            channel_count: bytes.len() as u16,
317            channels: bytes,
318        })
319    }
320}
321
322#[cfg(feature = "alloc")]
323impl From<DmxUniverse> for Vec<u8> {
324    fn from(universe: DmxUniverse) -> Self {
325        universe.channels
326    }
327}
328
329#[cfg(test)]
330mod tests {
331    use super::*;
332
333    #[cfg(feature = "alloc")]
334    #[test]
335    fn should_create_new_dmx_universe() {
336        let universe = DmxUniverse::new(4).unwrap();
337        assert_eq!(universe.channel_count, 4);
338        assert_eq!(universe.channels, vec![0; 4]);
339    }
340
341    #[cfg(not(feature = "alloc"))]
342    #[test]
343    fn should_create_new_dmx_universe() {
344        let universe = DmxUniverse::new();
345        assert_eq!(universe.0, Vec::<u8, 512>::from_slice(&[0; 512]).unwrap());
346    }
347
348    #[cfg(feature = "alloc")]
349    #[test]
350    fn should_create_new_dmx_universe_from_byte_slice() {
351        let bytes = [0_u8; 513];
352
353        let universe = DmxUniverse::try_from(&bytes[..]);
354        assert_eq!(universe, Err(DmxError::InvalidChannelCount(513)));
355
356        let bytes = [0x40, 0x80, 0xc0, 0xff];
357
358        let universe = DmxUniverse::try_from(&bytes[..]).unwrap();
359        assert_eq!(universe.channel_count, 4);
360        assert_eq!(universe.channels, vec![0x40, 0x80, 0xc0, 0xff]);
361
362        let universe: DmxUniverse = (&bytes[..]).try_into().unwrap();
363        assert_eq!(universe.channel_count, 4);
364        assert_eq!(universe.channels, vec![0x40, 0x80, 0xc0, 0xff]);
365    }
366
367    #[cfg(not(feature = "alloc"))]
368    #[test]
369    fn should_create_new_dmx_universe_from_byte_slice() {
370        let bytes = [0_u8; 513];
371
372        let universe = DmxUniverse::try_from(&bytes[..]);
373        assert_eq!(universe, Err(DmxError::InvalidChannelCount(513)));
374
375        let bytes = [0x40, 0x80, 0xc0, 0xff];
376
377        let mut expected = Vec::<u8, 512>::from_slice(&[0; 512]).unwrap();
378        expected[0..4].copy_from_slice(&bytes);
379
380        let universe = DmxUniverse::try_from(&bytes[..]).unwrap();
381        assert_eq!(universe.0, expected);
382
383        let universe: DmxUniverse = (&bytes[..]).try_into().unwrap();
384        assert_eq!(universe.0, expected);
385    }
386
387    #[cfg(feature = "alloc")]
388    #[test]
389    fn should_create_new_dmx_universe_from_byte_vec() {
390        let bytes = vec![0_u8; 513];
391
392        let universe = DmxUniverse::try_from(bytes);
393        assert_eq!(universe, Err(DmxError::InvalidChannelCount(513)));
394
395        let bytes = vec![0x40, 0x80, 0xc0, 0xff];
396
397        let universe = DmxUniverse::try_from(bytes.clone()).unwrap();
398        assert_eq!(universe.channel_count, 4);
399        assert_eq!(universe.channels, vec![0x40, 0x80, 0xc0, 0xff]);
400
401        let universe: DmxUniverse = bytes.try_into().unwrap();
402        assert_eq!(universe.channel_count, 4);
403        assert_eq!(universe.channels, vec![0x40, 0x80, 0xc0, 0xff]);
404    }
405
406    #[cfg(feature = "alloc")]
407    #[test]
408    fn should_create_byte_vec_from_new_dmx_universe() {
409        let universe = DmxUniverse {
410            channel_count: 4,
411            channels: vec![0x40, 0x80, 0xc0, 0xff],
412        };
413
414        assert_eq!(Vec::from(universe.clone()), vec![0x40, 0x80, 0xc0, 0xff]);
415
416        let bytes: Vec<u8> = universe.into();
417        assert_eq!(bytes, vec![0x40, 0x80, 0xc0, 0xff]);
418    }
419
420    #[cfg(feature = "alloc")]
421    #[test]
422    fn should_decode_dmx_frame() {
423        let bytes = [0_u8; 514];
424
425        let universe = DmxUniverse::decode(&bytes[..]);
426        assert_eq!(universe, Err(DmxError::InvalidFrameLength(514)));
427
428        let decoded = DmxUniverse::decode(&[0x00, 0x40, 0x80, 0xc0, 0xff]).unwrap();
429
430        let expected = DmxUniverse {
431            channel_count: 4,
432            channels: vec![0x40, 0x80, 0xc0, 0xff],
433        };
434
435        assert_eq!(decoded, expected);
436    }
437
438    #[cfg(not(feature = "alloc"))]
439    #[test]
440    fn should_decode_dmx_frame() {
441        let bytes = [0_u8; 514];
442
443        let universe = DmxUniverse::decode(&bytes[..]);
444        assert_eq!(universe, Err(DmxError::InvalidFrameLength(514)));
445
446        let decoded = DmxUniverse::decode(&[0x00, 0x40, 0x80, 0xc0, 0xff]).unwrap();
447
448        let mut expected = DmxUniverse(Vec::<u8, 512>::from_slice(&[0; 512]).unwrap());
449        expected.0[0..4].copy_from_slice(&[0x40, 0x80, 0xc0, 0xff]);
450
451        assert_eq!(decoded, expected);
452    }
453
454    #[cfg(feature = "alloc")]
455    #[test]
456    fn should_encode_dmx_universe() {
457        let encoded = DmxUniverse {
458            channel_count: 4,
459            channels: vec![0x40, 0x80, 0xc0, 0xff],
460        }
461        .encode();
462
463        let expected = vec![0x00, 0x40, 0x80, 0xc0, 0xff];
464
465        assert_eq!(encoded, expected);
466    }
467
468    #[cfg(not(feature = "alloc"))]
469    #[test]
470    fn should_encode_dmx_universe() {
471        let mut universe = DmxUniverse::new();
472        universe.0[0..4].copy_from_slice(&[0x40, 0x80, 0xc0, 0xff]);
473
474        let encoded = universe.encode();
475
476        let mut expected = Vec::<u8, 513>::from_slice(&[0; 513]).unwrap();
477        expected[0..5].copy_from_slice(&[0x00, 0x40, 0x80, 0xc0, 0xff]);
478
479        assert_eq!(encoded, expected);
480    }
481
482    #[cfg(feature = "alloc")]
483    #[test]
484    fn should_reset_dmx_universe() {
485        let mut universe = DmxUniverse {
486            channel_count: 4,
487            channels: vec![255; 4],
488        };
489
490        universe.reset();
491
492        assert_eq!(universe.channel_count, 4);
493        assert_eq!(universe.channels, vec![0; 4]);
494    }
495
496    #[cfg(not(feature = "alloc"))]
497    #[test]
498    fn should_reset_dmx_universe() {
499        let mut universe = DmxUniverse(Vec::<u8, 512>::from_slice(&[255; 512]).unwrap());
500
501        universe.reset();
502
503        assert_eq!(universe.0, Vec::<u8, 512>::from_slice(&[0; 512]).unwrap());
504    }
505
506    #[cfg(feature = "alloc")]
507    #[test]
508    fn should_get_channel_value() {
509        let universe = DmxUniverse {
510            channel_count: 4,
511            channels: vec![0x40, 0x80, 0xc0, 0xff],
512        };
513
514        assert_eq!(universe.get_channel_value(2).unwrap(), 192);
515
516        assert_eq!(
517            universe.get_channel_value(4),
518            Err(DmxError::ChannelOutOfBounds)
519        );
520    }
521
522    #[cfg(not(feature = "alloc"))]
523    #[test]
524    fn should_get_channel_value() {
525        let mut universe = DmxUniverse::new();
526        universe.0[0..4].copy_from_slice(&[0x40, 0x80, 0xc0, 0xff]);
527
528        assert_eq!(universe.get_channel_value(2).unwrap(), 192);
529
530        assert_eq!(
531            universe.get_channel_value(513),
532            Err(DmxError::ChannelOutOfBounds)
533        );
534    }
535
536    #[cfg(feature = "alloc")]
537    #[test]
538    fn should_get_channel_values() {
539        let universe = DmxUniverse {
540            channel_count: 4,
541            channels: vec![0x40, 0x80, 0xc0, 0xff],
542        };
543
544        assert_eq!(universe.get_channel_values(2..=3).unwrap(), &[192, 255]);
545
546        assert_eq!(
547            universe.get_channel_values(2..=5),
548            Err(DmxError::ChannelOutOfBounds)
549        );
550        assert_eq!(
551            universe.get_channel_values(4..=5),
552            Err(DmxError::ChannelOutOfBounds)
553        );
554    }
555
556    #[cfg(not(feature = "alloc"))]
557    #[test]
558    fn should_get_channel_values() {
559        let universe = DmxUniverse::from_slice(&[0x40, 0x80, 0xc0, 0xff]).unwrap();
560
561        assert_eq!(universe.get_channel_values(2..=3).unwrap(), &[192, 255]);
562        assert_eq!(
563            universe.get_channel_values(510..=513),
564            Err(DmxError::ChannelOutOfBounds)
565        );
566    }
567
568    #[cfg(feature = "alloc")]
569    #[test]
570    fn should_set_channel_value() {
571        let mut universe = DmxUniverse {
572            channel_count: 4,
573            channels: vec![0; 4],
574        };
575
576        universe.set_channel_value(2, 0xff).unwrap();
577
578        assert_eq!(universe.channels, vec![0x00, 0x00, 0xff, 0x00]);
579        assert_eq!(
580            universe.set_channel_value(4, 0xff),
581            Err(DmxError::ChannelOutOfBounds)
582        );
583    }
584
585    #[cfg(not(feature = "alloc"))]
586    #[test]
587    fn should_set_channel_value() {
588        let mut universe = DmxUniverse::new();
589
590        universe.set_channel_value(2, 0xff).unwrap();
591
592        assert_eq!(universe.0[2], 0xff);
593        assert_eq!(
594            universe.set_channel_value(512, 0xff),
595            Err(DmxError::ChannelOutOfBounds)
596        );
597    }
598
599    #[cfg(feature = "alloc")]
600    #[test]
601    fn should_set_channel_values() {
602        let mut universe = DmxUniverse {
603            channel_count: 4,
604            channels: vec![0; 4],
605        };
606
607        universe.set_channel_values(0, &[0x40, 0x80, 0xc0]).unwrap();
608
609        assert_eq!(universe.channels, vec![0x40, 0x80, 0xc0, 0]);
610
611        assert_eq!(
612            universe.set_channel_values(2, &[0xff, 0xff, 0xff]),
613            Err(DmxError::ChannelOutOfBounds)
614        );
615        assert_eq!(
616            universe.set_channel_values(4, &[0xff]),
617            Err(DmxError::ChannelOutOfBounds)
618        );
619    }
620
621    #[cfg(not(feature = "alloc"))]
622    #[test]
623    fn should_set_channel_values() {
624        let mut universe = DmxUniverse::new();
625
626        universe.set_channel_values(0, &[0x40, 0x80, 0xc0]).unwrap();
627
628        let mut expected = DmxUniverse::new();
629        expected.0[0..3].copy_from_slice(&[0x40, 0x80, 0xc0]);
630
631        assert_eq!(universe.0, expected.0);
632        assert_eq!(
633            universe.set_channel_values(510, &[0xff, 0xff, 0xff]),
634            Err(DmxError::ChannelOutOfBounds)
635        );
636    }
637
638    #[cfg(feature = "alloc")]
639    #[test]
640    fn should_set_all_channel_values() {
641        let mut universe = DmxUniverse {
642            channel_count: 4,
643            channels: vec![0; 4],
644        };
645
646        universe.set_all_channel_values(0xff);
647
648        assert_eq!(universe.channels, vec![0xff, 0xff, 0xff, 0xff]);
649    }
650
651    #[cfg(feature = "alloc")]
652    #[test]
653    fn should_return_all_channels_as_slice() {
654        let universe = DmxUniverse {
655            channel_count: 4,
656            channels: vec![255; 4],
657        };
658
659        assert_eq!(universe.as_slice(), &[0xff, 0xff, 0xff, 0xff]);
660    }
661
662    #[cfg(feature = "alloc")]
663    #[test]
664    fn should_extend_channels_with_byte_slice() {
665        let mut universe = DmxUniverse {
666            channel_count: 4,
667            channels: vec![255; 4],
668        };
669
670        universe.extend(&[0, 0, 0, 0]).unwrap();
671
672        assert_eq!(
673            universe.channels,
674            vec![0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00]
675        );
676        assert_eq!(universe.channel_count, 8);
677
678        assert_eq!(
679            universe.extend(&[0xff; 512][..]),
680            Err(DmxError::InvalidChannelCount(520))
681        );
682    }
683}