pictorus_blocks/core_blocks/
timer_block.rs1use pictorus_block_data::BlockData as OldBlockData;
2use pictorus_traits::{PassBy, ProcessBlock, Scalar};
3
4#[derive(strum::EnumString)]
5pub enum Method {
6 CountDown,
7 StopWatch,
8}
9
10pub struct Parameters {
12 pub method: Method,
15 pub interruptable: bool,
17 pub countdown_time_s: f64,
19}
20
21impl Parameters {
22 pub fn new(method: &str, interruptable: bool, countdown_time_s: f64) -> Parameters {
23 Parameters {
24 method: method.parse().expect("Faile to parse Timer Method"),
25 interruptable,
26 countdown_time_s,
27 }
28 }
29}
30
31pub struct TimerBlock<T> {
40 pub data: OldBlockData,
41 buffer: T,
42 timer_running: bool,
43 start_time_s: T,
44}
45
46impl<T> Default for TimerBlock<T>
47where
48 T: Scalar + num_traits::Zero,
49{
50 fn default() -> Self {
51 Self {
52 data: OldBlockData::from_scalar(0.0),
53 buffer: T::zero(),
54 timer_running: false,
55 start_time_s: T::zero(),
56 }
57 }
58}
59
60impl TimerBlock<f64> {
61 fn _do_countdown(&mut self, time_since_start: f64, countdown_time_s: f64) {
62 if time_since_start < countdown_time_s {
63 self.buffer = countdown_time_s - time_since_start;
64 } else {
65 self.buffer = 0.0;
66 self.timer_running = false;
67 }
68 }
69
70 fn _do_stopwatch(&mut self, time_since_start: f64) {
71 self.buffer = time_since_start;
72 }
73}
74
75impl ProcessBlock for TimerBlock<f64> {
76 type Inputs = f64;
77 type Output = f64;
78 type Parameters = Parameters;
79
80 fn process(
81 &mut self,
82 parameters: &Self::Parameters,
83 context: &dyn pictorus_traits::Context,
84 input: PassBy<Self::Inputs>,
85 ) -> PassBy<Self::Output> {
86 let time = context.time().as_secs_f64();
87
88 let trigger_high = input > 0.0;
89 if !self.timer_running && !trigger_high {
91 self.data.set_scalar(self.buffer);
92 return self.buffer;
93 }
94
95 if trigger_high {
96 if !self.timer_running {
97 self.start_time_s = time;
99 self.timer_running = true;
100 } else if self.timer_running && parameters.interruptable {
101 self.start_time_s = time;
103 }
104 }
105
106 let time_since_start = time - self.start_time_s;
107
108 match parameters.method {
109 Method::CountDown => {
110 self._do_countdown(time_since_start, parameters.countdown_time_s);
111 }
112 Method::StopWatch => {
113 self._do_stopwatch(time_since_start);
114 }
115 }
116
117 self.data.set_scalar(self.buffer);
118 self.buffer
119 }
120}
121
122#[cfg(test)]
123mod tests {
124 use crate::testing::StubRuntime;
125 use core::time;
126
127 use super::*;
128
129 #[test]
130 fn test_countdown_timer_non_interruptable() {
131 let mut runtime = StubRuntime::default();
132 let p = Parameters::new("CountDown", false, 5.0);
133 let mut block = TimerBlock::<f64>::default();
134
135 let output = block.process(&p, &runtime.context(), 0.0);
136 assert_eq!(block.data.scalar(), 0.0);
137 assert_eq!(output, 0.0);
138
139 runtime.set_time(time::Duration::from_secs_f64(1.0));
140 let output = block.process(&p, &runtime.context(), 1.0);
141 assert_eq!(block.data.scalar(), 5.0);
142 assert_eq!(output, 5.0);
143
144 runtime.set_time(time::Duration::from_secs_f64(2.0));
145 let output = block.process(&p, &runtime.context(), 0.0);
146 assert_eq!(block.data.scalar(), 4.0);
147 assert_eq!(output, 4.0);
148
149 runtime.set_time(time::Duration::from_secs_f64(3.0));
151 let output = block.process(&p, &runtime.context(), 1.0);
152 assert_eq!(block.data.scalar(), 3.0);
153 assert_eq!(output, 3.0);
154
155 runtime.set_time(time::Duration::from_secs_f64(10.0));
156 let output = block.process(&p, &runtime.context(), 0.0);
157 assert_eq!(block.data.scalar(), 0.0);
158 assert_eq!(output, 0.0);
159
160 runtime.set_time(time::Duration::from_secs_f64(11.0));
161 let output = block.process(&p, &runtime.context(), 0.0);
162 assert_eq!(block.data.scalar(), 0.0);
163 assert_eq!(output, 0.0);
164 }
165
166 #[test]
167 fn test_countdown_timer_interruptable() {
168 let mut runtime = StubRuntime::default();
169 let p = Parameters::new("CountDown", true, 5.0);
170 let mut block = TimerBlock::<f64>::default();
171
172 runtime.set_time(time::Duration::from_secs_f64(1.0));
174 let output = block.process(&p, &runtime.context(), 0.0);
175 assert_eq!(block.data.scalar(), 0.0);
176 assert_eq!(output, 0.0);
177
178 runtime.set_time(time::Duration::from_secs_f64(2.0));
180 let output = block.process(&p, &runtime.context(), 1.0);
181 assert_eq!(block.data.scalar(), 5.0);
182 assert_eq!(output, 5.0);
183
184 runtime.set_time(time::Duration::from_secs_f64(3.0));
185 let output = block.process(&p, &runtime.context(), 0.0);
186 assert_eq!(block.data.scalar(), 4.0);
187 assert_eq!(output, 4.0);
188
189 runtime.set_time(time::Duration::from_secs_f64(4.0));
191 let output = block.process(&p, &runtime.context(), 1.0);
192 assert_eq!(block.data.scalar(), 5.0);
193 assert_eq!(output, 5.0);
194
195 runtime.set_time(time::Duration::from_secs_f64(5.0));
197 let output = block.process(&p, &runtime.context(), 1.0);
198 assert_eq!(block.data.scalar(), 5.0);
199 assert_eq!(output, 5.0);
200
201 runtime.set_time(time::Duration::from_secs_f64(6.0));
203 let output = block.process(&p, &runtime.context(), 0.0);
204 assert_eq!(block.data.scalar(), 4.0);
205 assert_eq!(output, 4.0);
206 }
207
208 #[test]
209 fn test_stopwatch_timer_non_interruptable() {
210 let mut runtime = StubRuntime::default();
211 let p = Parameters::new("StopWatch", false, 5.0);
212 let mut block = TimerBlock::<f64>::default();
213
214 runtime.set_time(time::Duration::from_secs_f64(1.0));
216 let output = block.process(&p, &runtime.context(), 0.0);
217 assert_eq!(block.data.scalar(), 0.0);
218 assert_eq!(output, 0.0);
219
220 runtime.set_time(time::Duration::from_secs_f64(2.0));
222 let output = block.process(&p, &runtime.context(), 1.0);
223 assert_eq!(block.data.scalar(), 0.0);
224 assert_eq!(output, 0.0);
225
226 runtime.set_time(time::Duration::from_secs_f64(3.0));
227 let output = block.process(&p, &runtime.context(), 0.0);
228 assert_eq!(block.data.scalar(), 1.0);
229 assert_eq!(output, 1.0);
230
231 runtime.set_time(time::Duration::from_secs_f64(4.0));
233 let output = block.process(&p, &runtime.context(), 1.0);
234 assert_eq!(block.data.scalar(), 2.0);
235 assert_eq!(output, 2.0);
236
237 runtime.set_time(time::Duration::from_secs_f64(10.0));
238 let output = block.process(&p, &runtime.context(), 0.0);
239 assert_eq!(block.data.scalar(), 8.0);
240 assert_eq!(output, 8.0);
241
242 runtime.set_time(time::Duration::from_secs_f64(100.0));
243 let output = block.process(&p, &runtime.context(), 0.0);
244 assert_eq!(block.data.scalar(), 98.0);
245 assert_eq!(output, 98.0);
246 }
247
248 #[test]
249 fn test_stopwatch_timer_interruptable() {
250 let mut runtime = StubRuntime::default();
251 let p = Parameters::new("StopWatch", true, 5.0);
252 let mut block = TimerBlock::<f64>::default();
253
254 runtime.set_time(time::Duration::from_secs_f64(1.0));
256 let output = block.process(&p, &runtime.context(), 0.0);
257 assert_eq!(block.data.scalar(), 0.0);
258 assert_eq!(output, 0.0);
259
260 runtime.set_time(time::Duration::from_secs_f64(2.0));
262 let output = block.process(&p, &runtime.context(), 1.0);
263 assert_eq!(block.data.scalar(), 0.0);
264 assert_eq!(output, 0.0);
265
266 runtime.set_time(time::Duration::from_secs_f64(3.0));
267 let output = block.process(&p, &runtime.context(), 0.0);
268 assert_eq!(block.data.scalar(), 1.0);
269 assert_eq!(output, 1.0);
270
271 runtime.set_time(time::Duration::from_secs_f64(4.0));
273 let output = block.process(&p, &runtime.context(), 1.0);
274 assert_eq!(block.data.scalar(), 0.0);
275 assert_eq!(output, 0.0);
276
277 runtime.set_time(time::Duration::from_secs_f64(10.0));
279 let output = block.process(&p, &runtime.context(), 0.0);
280 assert_eq!(block.data.scalar(), 6.0);
281 assert_eq!(output, 6.0);
282
283 runtime.set_time(time::Duration::from_secs_f64(100.0));
284 let output = block.process(&p, &runtime.context(), 0.0);
285 assert_eq!(block.data.scalar(), 96.0);
286 assert_eq!(output, 96.0);
287 }
288}