1pub 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}