use pictorus_block_data::BlockData as OldBlockData;
use pictorus_traits::{PassBy, ProcessBlock, Scalar};
#[derive(strum::EnumString)]
pub enum Method {
CountDown,
StopWatch,
}
pub struct Parameters {
pub method: Method,
pub interruptable: bool,
pub countdown_time_s: f64,
}
impl Parameters {
pub fn new(method: &str, interruptable: bool, countdown_time_s: f64) -> Parameters {
Parameters {
method: method.parse().expect("Faile to parse Timer Method"),
interruptable,
countdown_time_s,
}
}
}
pub struct TimerBlock<T> {
pub data: OldBlockData,
buffer: T,
timer_running: bool,
start_time_s: T,
}
impl<T> Default for TimerBlock<T>
where
T: Scalar + num_traits::Zero,
{
fn default() -> Self {
Self {
data: OldBlockData::from_scalar(0.0),
buffer: T::zero(),
timer_running: false,
start_time_s: T::zero(),
}
}
}
impl TimerBlock<f64> {
fn _do_countdown(&mut self, time_since_start: f64, countdown_time_s: f64) {
if time_since_start < countdown_time_s {
self.buffer = countdown_time_s - time_since_start;
} else {
self.buffer = 0.0;
self.timer_running = false;
}
}
fn _do_stopwatch(&mut self, time_since_start: f64) {
self.buffer = time_since_start;
}
}
impl ProcessBlock for TimerBlock<f64> {
type Inputs = f64;
type Output = f64;
type Parameters = Parameters;
fn process(
&mut self,
parameters: &Self::Parameters,
context: &dyn pictorus_traits::Context,
input: PassBy<Self::Inputs>,
) -> PassBy<Self::Output> {
let time = context.time().as_secs_f64();
let trigger_high = input > 0.0;
if !self.timer_running && !trigger_high {
self.data.set_scalar(self.buffer);
return self.buffer;
}
if trigger_high {
if !self.timer_running {
self.start_time_s = time;
self.timer_running = true;
} else if self.timer_running && parameters.interruptable {
self.start_time_s = time;
}
}
let time_since_start = time - self.start_time_s;
match parameters.method {
Method::CountDown => {
self._do_countdown(time_since_start, parameters.countdown_time_s);
}
Method::StopWatch => {
self._do_stopwatch(time_since_start);
}
}
self.data.set_scalar(self.buffer);
self.buffer
}
}
#[cfg(test)]
mod tests {
use crate::testing::StubRuntime;
use core::time;
use super::*;
#[test]
fn test_countdown_timer_non_interruptable() {
let mut runtime = StubRuntime::default();
let p = Parameters::new("CountDown", false, 5.0);
let mut block = TimerBlock::<f64>::default();
let output = block.process(&p, &runtime.context(), 0.0);
assert_eq!(block.data.scalar(), 0.0);
assert_eq!(output, 0.0);
runtime.set_time(time::Duration::from_secs_f64(1.0));
let output = block.process(&p, &runtime.context(), 1.0);
assert_eq!(block.data.scalar(), 5.0);
assert_eq!(output, 5.0);
runtime.set_time(time::Duration::from_secs_f64(2.0));
let output = block.process(&p, &runtime.context(), 0.0);
assert_eq!(block.data.scalar(), 4.0);
assert_eq!(output, 4.0);
runtime.set_time(time::Duration::from_secs_f64(3.0));
let output = block.process(&p, &runtime.context(), 1.0);
assert_eq!(block.data.scalar(), 3.0);
assert_eq!(output, 3.0);
runtime.set_time(time::Duration::from_secs_f64(10.0));
let output = block.process(&p, &runtime.context(), 0.0);
assert_eq!(block.data.scalar(), 0.0);
assert_eq!(output, 0.0);
runtime.set_time(time::Duration::from_secs_f64(11.0));
let output = block.process(&p, &runtime.context(), 0.0);
assert_eq!(block.data.scalar(), 0.0);
assert_eq!(output, 0.0);
}
#[test]
fn test_countdown_timer_interruptable() {
let mut runtime = StubRuntime::default();
let p = Parameters::new("CountDown", true, 5.0);
let mut block = TimerBlock::<f64>::default();
runtime.set_time(time::Duration::from_secs_f64(1.0));
let output = block.process(&p, &runtime.context(), 0.0);
assert_eq!(block.data.scalar(), 0.0);
assert_eq!(output, 0.0);
runtime.set_time(time::Duration::from_secs_f64(2.0));
let output = block.process(&p, &runtime.context(), 1.0);
assert_eq!(block.data.scalar(), 5.0);
assert_eq!(output, 5.0);
runtime.set_time(time::Duration::from_secs_f64(3.0));
let output = block.process(&p, &runtime.context(), 0.0);
assert_eq!(block.data.scalar(), 4.0);
assert_eq!(output, 4.0);
runtime.set_time(time::Duration::from_secs_f64(4.0));
let output = block.process(&p, &runtime.context(), 1.0);
assert_eq!(block.data.scalar(), 5.0);
assert_eq!(output, 5.0);
runtime.set_time(time::Duration::from_secs_f64(5.0));
let output = block.process(&p, &runtime.context(), 1.0);
assert_eq!(block.data.scalar(), 5.0);
assert_eq!(output, 5.0);
runtime.set_time(time::Duration::from_secs_f64(6.0));
let output = block.process(&p, &runtime.context(), 0.0);
assert_eq!(block.data.scalar(), 4.0);
assert_eq!(output, 4.0);
}
#[test]
fn test_stopwatch_timer_non_interruptable() {
let mut runtime = StubRuntime::default();
let p = Parameters::new("StopWatch", false, 5.0);
let mut block = TimerBlock::<f64>::default();
runtime.set_time(time::Duration::from_secs_f64(1.0));
let output = block.process(&p, &runtime.context(), 0.0);
assert_eq!(block.data.scalar(), 0.0);
assert_eq!(output, 0.0);
runtime.set_time(time::Duration::from_secs_f64(2.0));
let output = block.process(&p, &runtime.context(), 1.0);
assert_eq!(block.data.scalar(), 0.0);
assert_eq!(output, 0.0);
runtime.set_time(time::Duration::from_secs_f64(3.0));
let output = block.process(&p, &runtime.context(), 0.0);
assert_eq!(block.data.scalar(), 1.0);
assert_eq!(output, 1.0);
runtime.set_time(time::Duration::from_secs_f64(4.0));
let output = block.process(&p, &runtime.context(), 1.0);
assert_eq!(block.data.scalar(), 2.0);
assert_eq!(output, 2.0);
runtime.set_time(time::Duration::from_secs_f64(10.0));
let output = block.process(&p, &runtime.context(), 0.0);
assert_eq!(block.data.scalar(), 8.0);
assert_eq!(output, 8.0);
runtime.set_time(time::Duration::from_secs_f64(100.0));
let output = block.process(&p, &runtime.context(), 0.0);
assert_eq!(block.data.scalar(), 98.0);
assert_eq!(output, 98.0);
}
#[test]
fn test_stopwatch_timer_interruptable() {
let mut runtime = StubRuntime::default();
let p = Parameters::new("StopWatch", true, 5.0);
let mut block = TimerBlock::<f64>::default();
runtime.set_time(time::Duration::from_secs_f64(1.0));
let output = block.process(&p, &runtime.context(), 0.0);
assert_eq!(block.data.scalar(), 0.0);
assert_eq!(output, 0.0);
runtime.set_time(time::Duration::from_secs_f64(2.0));
let output = block.process(&p, &runtime.context(), 1.0);
assert_eq!(block.data.scalar(), 0.0);
assert_eq!(output, 0.0);
runtime.set_time(time::Duration::from_secs_f64(3.0));
let output = block.process(&p, &runtime.context(), 0.0);
assert_eq!(block.data.scalar(), 1.0);
assert_eq!(output, 1.0);
runtime.set_time(time::Duration::from_secs_f64(4.0));
let output = block.process(&p, &runtime.context(), 1.0);
assert_eq!(block.data.scalar(), 0.0);
assert_eq!(output, 0.0);
runtime.set_time(time::Duration::from_secs_f64(10.0));
let output = block.process(&p, &runtime.context(), 0.0);
assert_eq!(block.data.scalar(), 6.0);
assert_eq!(output, 6.0);
runtime.set_time(time::Duration::from_secs_f64(100.0));
let output = block.process(&p, &runtime.context(), 0.0);
assert_eq!(block.data.scalar(), 96.0);
assert_eq!(output, 96.0);
}
}