1#![allow(dead_code)]
2use std::collections::{HashMap, HashSet};
3use std::sync::{LazyLock, RwLock};
4use crate::color::{Color, ColorPropertyType, ColorType};
5use crate::fixture::ChannelReservation::{Empty, Pending, Reserved};
6use crate::fixture::FixtureError::{InvalidFixtureType, InvalidFixture};
7
8
9pub const MAX_CHANNEL: u16 = 512;
11
12
13struct FixtureList {
14 pub fixture_types: HashMap<String, FixtureType>,
15 pub fixtures: HashMap<String, Fixture>,
16}
17
18impl FixtureList {
19 fn new() -> Self {
20 Self {
21 fixture_types: HashMap::new(),
22 fixtures: HashMap::new(),
23 }
24 }
25}
26
27pub static DMX_CONFIGURATION: LazyLock<RwLock<Vec<[ChannelReservation<String, PropertyType>; MAX_CHANNEL as usize]>>> =
32 LazyLock::new(||{
33 RwLock::new(Vec::new())
34 });
35
36pub fn universe_count() -> usize {
38 DMX_CONFIGURATION.read().expect("Failed to lock DMX_CONFIGURATION").len()
39}
40
41pub fn ensure_universes_size(size: usize) {
45 if size > universe_count() {
46 let mut config = DMX_CONFIGURATION.write().expect("Failed to write \
47 DMX_CONFIGURATION");
48 config.resize_with(size, || {
49 std::array::from_fn(|_| Empty)
50 })
51 }
52}
53
54#[derive(Clone)]
60pub enum ChannelReservation<T, U> {
61 Empty,
62 Pending(T),
63 Reserved(T, U),
64}
65
66static FIXTURE_LIST: LazyLock<RwLock<FixtureList>> = LazyLock::new(|| {
67 RwLock::new(FixtureList::new())
68});
69
70pub(crate) struct Channel{
72 pub(crate) value: u16,
73 channel : u16,
74 fine_channel: Option<u16>,
75}
76
77impl Channel {
78
79 pub(crate) fn new(
91 channel_numbers: (u16, Option<u16>),
92 default_value: u16,
93 device_channel: u16
94 ) -> Result<Self, ChannelError> {
95
96 let channel = Self::checked_add(channel_numbers.0, device_channel)?;
97 let fine_channel = if let Some(fine) = channel_numbers.1 {
98 Some (
99 Self::checked_add(fine, device_channel)?
100 )
101 } else {
102 None
103 };
104
105
106 Ok(Channel {
107 value: default_value,
108 channel,
109 fine_channel,
110 })
111 }
112
113 fn checked_add(value1: u16, value2: u16) -> Result<u16, ChannelError> {
115 value1
116 .checked_add(value2)
117 .filter(|&x| x <= MAX_CHANNEL)
118 .ok_or(ChannelError::ChannelOutOfRange)
119 }
120
121 pub(crate) fn reserve_pending(&self, fixture_name: &str, universe: usize) -> Result<(), ChannelError> {
135 let mut dmx_config = DMX_CONFIGURATION.write().expect("Failed to write \
136 DMX_CONFIGURATION");
137
138 let universe = dmx_config.get_mut(universe)
141 .ok_or(ChannelError::UniverseOutOfRange).expect("Universe out of range");
142
143 if let Reserved(existing,_) = universe[self.channel as usize].clone() {
144 return Err(ChannelError::ChannelAlreadyInUse(existing));
145 }
146
147 if let Some(fine_channel) = self.fine_channel {
148 if let Reserved(existing,_) = universe[fine_channel as usize].clone() {
149 return Err(ChannelError::ChannelAlreadyInUse(existing));
150 }
151
152 universe[fine_channel as usize] = Pending(fixture_name.to_string());
153 }
154 universe[self.channel as usize] = Pending(fixture_name.to_string());
155
156 Ok(())
157 }
158
159 pub(crate) fn reserve_final(&self, fixture_name: &str, universe: usize, property_type: PropertyType) {
169 let mut dmx_config = DMX_CONFIGURATION.write().expect("Failed to write \
170 DMX_CONFIGURATION");
171
172 let universe = dmx_config.get_mut(universe)
175 .ok_or(ChannelError::UniverseOutOfRange).expect("Universe out of range.");
176
177 if let Pending(existing) = universe[self.channel as usize].clone() {
178 if existing == fixture_name {
179 universe[self.channel as usize] = Reserved(existing, property_type.clone());
180 } else {
181 panic!("A property of another fixture has been set to pending,\
182 cant reserve channel for {fixture_name}")
183 }
184 } else {
185 panic!("Error: In {fixture_name}, a channel has not correctly been set to Pending. \
186 This could happen if the fixture_type has multiple properties bound to the same channel.");
187 }
188
189 if let Some(fine_channel) = self.fine_channel {
190 if let Pending(existing) = universe[fine_channel as usize].clone() {
191 if existing == fixture_name {
192 universe[fine_channel as usize] = Reserved(existing, property_type);
193 } else {
194 panic!("A property of another fixture has been set to pending,\
195 cant reserve fine-channel for {fixture_name}")
196 }
197 } else {
198 panic!("Error: In {fixture_name}, a fine-channel has not correctly been set to Pending. \
199 This could happen if the fixture_type has multiple properties bound to the same channel.");
200 }
201 }
202 }
203
204 pub fn get_value(&self) -> (u16, u8) {
206 (self.channel, self.value.to_be_bytes()[0])
207 }
208
209 pub fn get_fine_value(&self) -> Option<(u16, u8)> {
212 if let Some(fine_channel) = self.fine_channel {
213 Some((fine_channel, self.value.to_be_bytes()[1]))
214 } else {
215 None
216 }
217 }
218
219 fn get_default_value(property_type: SimplePropertyType) -> u16 {
220 match property_type {
221 SimplePropertyType::Pan => u16::MAX/2,
222 SimplePropertyType::Tilt => u16::MAX/2,
223 _ => 0
224 }
225 }
226}
227
228#[derive(Debug, Hash,Eq,PartialEq,Clone)]
256pub enum SimplePropertyType {
257 Dimmer,
258 Strobe,
259 Zoom,
260 Focus,
261 Frost,
262 Prism,
263 PrismRotation,
264 PrismIndexation,
265 GoboRotation,
266 GoboRotationSpeed,
267 GoboWheelRotation,
268 GoboWheelRotationSpeed,
269 Pan,
270 Tilt,
271 FogIntensity,
272 FogFanSpeed,
273 Shutter,
274 UV,
275 Speed,
276 Other(String),
277}
278
279#[derive(Clone, Debug)]
284pub enum PropertyType {
285 Simple(SimplePropertyType),
286 Color(ColorPropertyType),
287}
288
289impl PropertyType {
290 fn from_str(property_type: &str) -> Result<PropertyType, FixtureError> {
291 if let Ok(property_type) = ColorPropertyType::from_string(property_type) {
292 Ok(PropertyType::Color(property_type))
293 } else if let Ok(property_type) = SimplePropertyType::from_string(property_type) {
294 Ok(PropertyType::Simple(property_type))
295 } else {
296 Err(FixtureError::InvalidPropertyType(property_type.to_string()))
297 }
298 }
299}
300
301pub struct FixtureType {
306 color: Option<ColorType>,
307 properties: HashMap<SimplePropertyType, (u16, Option<u16>)>,
308 name: String
309}
310
311
312pub struct Fixture {
317 fixture_type: String,
318 color: Option<Color>,
319 properties: HashMap<SimplePropertyType, Channel>,
320 start_channel: u16,
321 universe: usize,
322 name: String,
323}
324
325
326impl FixtureType {
327
328 pub fn new(name: String, properties: HashMap<String, (u16, Option<u16>)>) -> Result<(), FixtureError> {
351 let mut color = ColorType::new();
352 let mut new_properties = HashMap::new();
353 let mut seen_channels = HashSet::new();
354
355 for (key, value) in properties {
356
357 let mut seen_this_channel = !seen_channels.insert(value.0);
358 let mut out_of_range = value.0 > MAX_CHANNEL;
359
360
361 if let Some(channel) = value.1 {
362 seen_this_channel = seen_this_channel || !seen_channels.insert(channel);
363 out_of_range = out_of_range || channel > MAX_CHANNEL;
364 }
365
366 if seen_this_channel {
367 return Err(FixtureError::ChannelError(ChannelError::ChannelAlreadyInUse(key)));
368 }
369
370 if out_of_range {
371 return Err(FixtureError::ChannelError(ChannelError::ChannelOutOfRange))
372 }
373
374 if color.parse(key.clone(), value)? {
375 continue
376 }
377
378 let property_type = SimplePropertyType::from_string(&key)?;
379 new_properties.insert(property_type, value);
380 }
381
382 let color = if color.exists() {
383 Some(color)
384 } else {
385 None
386 };
387
388 let output = Self {
389 color,
390 properties: new_properties,
391 name: name.clone(),
392 };
393
394 let mut list = FIXTURE_LIST.write().unwrap();
395 match list.fixture_types.entry(name.clone()) {
396 std::collections::hash_map::Entry::Occupied(_) => {
397 Err(FixtureError::FixtureTypeNameAlreadyInUse(name))
398 },
399 std::collections::hash_map::Entry::Vacant(entry) => {
400 entry.insert(output);
401 Ok(())
402 }
403 }
404 }
405}
406
407impl Fixture {
408
409 pub fn new(fixture_type_name:String, start_channel:u16, universe: usize, name:String) -> Result<(), FixtureError> {
428 ensure_universes_size(universe + 1);
429
430 let list = FIXTURE_LIST.read().unwrap();
431
432
433 let fixture_type = list.fixture_types.get(fixture_type_name.as_str());
434 if let None = fixture_type {
435 return Err(InvalidFixtureType(fixture_type_name.clone()))
436 }
437
438 let fixture_type = fixture_type.unwrap();
439
440 let color = fixture_type.color.as_ref()
441 .map(|c| {
442 Color::new(c, start_channel, universe, &name)
443 })
444 .transpose()?;
445
446 let properties = fixture_type.properties
447 .iter()
448 .map(|(property_type, channel)| {
449 let default_value = Channel::get_default_value(property_type.clone());
450 let channel = Channel::new(*channel, default_value, start_channel)?;
451 channel.reserve_pending(&*name, universe)?;
452 Ok((property_type.clone(), channel))
453 }).collect::<Result<HashMap<SimplePropertyType, Channel>,ChannelError>>()?;
454
455 properties.iter().for_each(|(property_type, channel)| {
456 channel.reserve_final(&*name, universe, PropertyType::Simple(property_type.clone()));
457 });
458
459 let fixture = Self {
460 color,
461 fixture_type: fixture_type.name.clone(),
462 properties,
463 start_channel,
464 universe,
465 name: name.clone(),
466 };
467
468 drop(list);
471
472 let mut list = FIXTURE_LIST.write().unwrap();
473
474 match list.fixtures.entry(name.clone()) {
475 std::collections::hash_map::Entry::Occupied(_) => {
476 Err(FixtureError::FixtureNameAlreadyInUse(name))
477 }
478 std::collections::hash_map::Entry::Vacant(entry) => {
479 entry.insert(fixture);
480 Ok(())
481 }
482 }
483 }
484
485
486 pub fn set(fixture_name: String, property_type: &str, value: u16) -> Result<(), FixtureError> {
500 let property_type= PropertyType::from_str(property_type)?;
501
502 let mut list = FIXTURE_LIST.write().unwrap();
503 if let None = list.fixtures.get(&fixture_name) {
504 return Err(InvalidFixture(fixture_name.clone()));
505 }
506 let fixture = list.fixtures.get_mut(&fixture_name).unwrap();
507
508
509 if let PropertyType::Simple(property_type) = property_type {
510 let property = fixture.properties.get_mut(&property_type)
511 .ok_or(FixtureError::MissingProperty(PropertyType::Simple(property_type)))?;
512
513 property.value = value;
514 } else if let PropertyType::Color(property_type) = property_type {
515 if let Some(color) = &mut fixture.color {
516 color.set(property_type, value);
517 } else {
518 return Err(FixtureError::MissingProperty(PropertyType::Color(property_type)));
519 }
520 } else {
521 unreachable!()
522 }
523
524 Ok(())
525 }
526
527 fn get_channel_values(&self) -> Vec<(u16, u8)> {
528 let mut output = Vec::new();
529
530 self.properties.iter().for_each(|(_, channel)| {
531 output.push(channel.get_value());
532 if let Some(fine_value) = channel.get_fine_value() {
533 output.push(fine_value);
534 }
535 });
536
537 if let Some(color) = self.color.as_ref() {
538 output.append(&mut color.get_values())
539 }
540
541 output
542 }
543
544 pub fn get_universe(&self) -> usize {
546 self.universe
547 }
548
549 pub fn get_fixture_type_from_string(name: String) -> Result<String, FixtureError> {
555 let list = FIXTURE_LIST.read().unwrap();
556 match list.fixtures.get(&name) {
557 None => Err(InvalidFixture(name)),
558 Some(fixture) => Ok(fixture.fixture_type.clone())
559 }
560 }
561
562 fn get_fixture_type(&self) -> String {
563 self.fixture_type.clone()
564 }
565
566 pub fn get_name(&self) -> &str {&self.name}
568
569}
570
571impl SimplePropertyType {
572 fn from_string(s: &str) -> Result<SimplePropertyType, FixtureError> {
573 match s {
574 "dimmer" => Ok(SimplePropertyType::Dimmer),
575 "strobe" => Ok(SimplePropertyType::Strobe),
576 "zoom" => Ok(SimplePropertyType::Zoom),
577 "focus" => Ok(SimplePropertyType::Focus),
578 "frost" => Ok(SimplePropertyType::Frost),
579 "prism" => Ok(SimplePropertyType::Prism),
580 "prism-rotation" => Ok(SimplePropertyType::PrismRotation),
581 "prism-index" => Ok(SimplePropertyType::PrismIndexation),
582 "gobo" => Ok(SimplePropertyType::GoboRotation),
583 "gobo-rotation" => Ok(SimplePropertyType::GoboRotationSpeed),
584 "gobo-wheel-rotation" => Ok(SimplePropertyType::GoboWheelRotation),
585 "gobo-wheel-speed" => Ok(SimplePropertyType::GoboWheelRotationSpeed),
586 "pan" => Ok(SimplePropertyType::Pan),
587 "tilt" => Ok(SimplePropertyType::Tilt),
588 "fog-intensity" => Ok(SimplePropertyType::FogIntensity),
589 "fog-fan-speed" => Ok(SimplePropertyType::FogFanSpeed),
590 "shutter" => Ok(SimplePropertyType::Shutter),
591 "uv" => Ok(SimplePropertyType::UV),
592 "speed" => Ok(SimplePropertyType::Speed),
593 _ => {
594 if let Some(suffix) = s.strip_prefix("other_") {
595 Ok(SimplePropertyType::Other(suffix.to_string()))
596 } else {
597 Err(FixtureError::InvalidPropertyType(s.to_string()))
598 }
599 }
600 }
601 }
602}
603
604#[derive(Debug)]
606pub enum FixtureError {
607 InvalidPropertyType(String),
609 MultipleColorOutputTypes(String),
611 FixtureNameAlreadyInUse(String),
613 FixtureTypeNameAlreadyInUse(String),
615 InvalidFixtureType(String),
617 InvalidFixture(String),
619 MissingProperty(PropertyType),
621 OverlappingChannels,
623 ChannelError(ChannelError),
625}
626
627#[derive(Debug)]
629pub enum ChannelError {
630 ChannelOutOfRange,
632 UniverseOutOfRange,
634 ChannelAlreadyInUse(String),
636}
637
638impl From<ChannelError> for FixtureError {
639 fn from(e: ChannelError) -> Self {
640 FixtureError::ChannelError(e)
641 }
642}
643
644pub fn calculate_dmx_values() -> Vec<[u8;MAX_CHANNEL as usize]>{
654 let universe_count = universe_count();
655
656 let mut output = vec![[0u8; MAX_CHANNEL as usize]; universe_count];
657
658 let list = FIXTURE_LIST.read().unwrap();
659
660 list.fixtures.iter().for_each(|(_, fixture)| {
661 let universe_number = fixture.get_universe();
662 let fixture_type = fixture.get_fixture_type();
663 let fixture_name = fixture.get_name();
664
665 if universe_number < universe_count {
666 fixture
667 .get_channel_values()
668 .iter()
669 .for_each(|(channel, value)| {
670 *output.get_mut(universe_number).unwrap()
671 .get_mut(*channel as usize)
672 .ok_or(ChannelError::ChannelOutOfRange)
673 .unwrap_or_else(|_| panic!(
674 "Fixture \"{}\" of type {} has a channel that is out of bounds",
675 fixture_name, fixture_type
676 ))
677 = *value;
678
679 });
680 }
681
682 });
683
684 output
685}