autd3_rs/mirror/
transition.rs1use crate::error::Error;
2use crate::params::NUM_BANKS;
3use crate::value::{LoopBehavior, TransitionMode};
4
5#[derive(Clone, Copy, Debug, PartialEq, Eq)]
6#[non_exhaustive]
7pub enum BankLoop {
8 Infinite,
9 Finite,
10}
11
12impl BankLoop {
13 #[must_use]
14 pub const fn of(loop_behavior: LoopBehavior) -> Self {
15 match loop_behavior {
16 LoopBehavior::Infinite => BankLoop::Infinite,
17 LoopBehavior::Finite(_) => BankLoop::Finite,
18 }
19 }
20
21 #[must_use]
22 pub const fn accepts(self, mode: TransitionMode) -> bool {
23 if mode.is_later() {
24 return true;
25 }
26 match self {
27 BankLoop::Infinite => {
28 matches!(mode, TransitionMode::Immediate | TransitionMode::Ext)
29 }
30 BankLoop::Finite => matches!(
31 mode,
32 TransitionMode::SyncIdx
33 | TransitionMode::SysTime {
34 time: _,
35 margin: None
36 }
37 | TransitionMode::Gpio(_)
38 ),
39 }
40 }
41}
42
43#[derive(Clone, Copy, Debug, PartialEq, Eq)]
44pub struct TransitionGuardState {
45 pub mod_loop: [BankLoop; NUM_BANKS],
46 pub pattern_loop: [BankLoop; NUM_BANKS],
47}
48
49impl TransitionGuardState {
50 #[must_use]
51 pub fn boot_default() -> Self {
52 Self {
53 mod_loop: [BankLoop::Infinite; NUM_BANKS],
54 pattern_loop: [BankLoop::Infinite; NUM_BANKS],
55 }
56 }
57
58 pub fn note_mod_loop(&mut self, bank: u8, loop_behavior: LoopBehavior) {
59 self.mod_loop[usize::from(bank)] = BankLoop::of(loop_behavior);
60 }
61
62 pub fn note_pattern_loop(&mut self, bank: u8, loop_behavior: LoopBehavior) {
63 self.pattern_loop[usize::from(bank)] = BankLoop::of(loop_behavior);
64 }
65
66 pub fn check_mod_bank(
67 &self,
68 device: usize,
69 bank: u8,
70 mode: TransitionMode,
71 ) -> Result<(), Error> {
72 check(device, self.mod_loop[usize::from(bank)], mode)
73 }
74
75 pub fn check_pattern_bank(
76 &self,
77 device: usize,
78 bank: u8,
79 mode: TransitionMode,
80 ) -> Result<(), Error> {
81 check(device, self.pattern_loop[usize::from(bank)], mode)
82 }
83}
84
85fn check(device: usize, bank_loop: BankLoop, mode: TransitionMode) -> Result<(), Error> {
86 if bank_loop.accepts(mode) {
87 Ok(())
88 } else {
89 Err(Error::TransitionConstraint {
90 device,
91 transition_mode: mode,
92 bank_loop,
93 })
94 }
95}
96
97#[cfg(test)]
98mod tests {
99 use super::*;
100 use crate::value::{DcSysTime, GpioIn};
101 use core::num::NonZeroU16;
102
103 const FINITE: LoopBehavior = LoopBehavior::ONCE;
104 const INFINITE: LoopBehavior = LoopBehavior::Infinite;
105
106 #[test]
107 fn boot_default_is_all_infinite() {
108 let g = TransitionGuardState::boot_default();
109 assert_eq!(g.mod_loop, [BankLoop::Infinite; NUM_BANKS]);
110 assert_eq!(g.pattern_loop, [BankLoop::Infinite; NUM_BANKS]);
111 }
112
113 #[test]
114 fn infinite_accepts_only_immediate_and_ext() {
115 assert!(BankLoop::Infinite.accepts(TransitionMode::Immediate));
116 assert!(BankLoop::Infinite.accepts(TransitionMode::Ext));
117 assert!(!BankLoop::Infinite.accepts(TransitionMode::SyncIdx));
118 assert!(!BankLoop::Infinite.accepts(TransitionMode::SysTime {
119 time: DcSysTime::from_nanos(0),
120 margin: None
121 }));
122 assert!(!BankLoop::Infinite.accepts(TransitionMode::Gpio(GpioIn::I0)));
123 }
124
125 #[test]
126 fn finite_accepts_only_timed_modes() {
127 assert!(BankLoop::Finite.accepts(TransitionMode::SyncIdx));
128 assert!(BankLoop::Finite.accepts(TransitionMode::SysTime {
129 time: DcSysTime::from_nanos(0),
130 margin: None
131 }));
132 assert!(BankLoop::Finite.accepts(TransitionMode::Gpio(GpioIn::I0)));
133 assert!(!BankLoop::Finite.accepts(TransitionMode::Immediate));
134 assert!(!BankLoop::Finite.accepts(TransitionMode::Ext));
135 }
136
137 #[test]
138 fn later_is_outside_the_loop_constraint() {
139 assert!(BankLoop::Infinite.accepts(TransitionMode::Later));
140 assert!(BankLoop::Finite.accepts(TransitionMode::Later));
141 }
142
143 #[test]
144 fn check_mod_bank_uses_target_bank_loop() {
145 let mut g = TransitionGuardState::boot_default();
146 g.note_mod_loop(1, LoopBehavior::Finite(NonZeroU16::new(5).unwrap()));
147
148 assert!(g.check_mod_bank(0, 0, TransitionMode::Immediate).is_ok());
149 assert!(matches!(
150 g.check_mod_bank(2, 1, TransitionMode::Immediate),
151 Err(Error::TransitionConstraint {
152 device: 2,
153 transition_mode: TransitionMode::Immediate,
154 bank_loop: BankLoop::Finite,
155 })
156 ));
157 assert!(g.check_mod_bank(0, 1, TransitionMode::SyncIdx).is_ok());
158 }
159
160 #[test]
161 fn check_pattern_bank_uses_target_bank_loop() {
162 let mut g = TransitionGuardState::boot_default();
163 g.note_pattern_loop(0, FINITE);
164 g.note_pattern_loop(1, INFINITE);
165
166 assert!(
167 g.check_pattern_bank(0, 0, TransitionMode::Gpio(GpioIn::I1))
168 .is_ok()
169 );
170 assert!(g.check_pattern_bank(0, 0, TransitionMode::Ext).is_err());
171 assert!(g.check_pattern_bank(0, 1, TransitionMode::Ext).is_ok());
172 assert!(g.check_pattern_bank(0, 1, TransitionMode::SyncIdx).is_err());
173 }
174}