Implement logic utilities
Implement timers for use in the logic code.
This commit is contained in:
parent
f603fb633f
commit
a92f0c45f2
2 changed files with 263 additions and 0 deletions
|
|
@ -2,6 +2,7 @@ use process_image as pi;
|
|||
|
||||
mod display;
|
||||
mod fieldbus;
|
||||
mod util;
|
||||
|
||||
// Parameters
|
||||
const CYCLE_TIME: u64 = 5000;
|
||||
|
|
|
|||
262
src/util.rs
Normal file
262
src/util.rs
Normal file
|
|
@ -0,0 +1,262 @@
|
|||
use profirust::time;
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
struct BaseTimer<T> {
|
||||
change: Option<time::Instant>,
|
||||
last: T,
|
||||
}
|
||||
|
||||
impl<T: Copy + PartialEq> BaseTimer<T> {
|
||||
pub fn new(start: T) -> Self {
|
||||
Self {
|
||||
change: None,
|
||||
last: start,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(&mut self, now: time::Instant, value: T) {
|
||||
if self.last != value {
|
||||
self.change = Some(now);
|
||||
}
|
||||
self.last = value;
|
||||
}
|
||||
|
||||
/// Reset the "last" value without triggering change detection
|
||||
pub fn reset_value(&mut self, value: T) {
|
||||
self.last = value;
|
||||
}
|
||||
|
||||
pub fn timer(&self, now: time::Instant, preset: time::Duration) -> bool {
|
||||
self.change.map(|tt| now >= tt + preset).unwrap_or(true)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TimerResult {
|
||||
pub done: bool,
|
||||
pub timing: bool,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct TimerOn {
|
||||
base: BaseTimer<bool>,
|
||||
}
|
||||
|
||||
impl TimerOn {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn run(&mut self, now: time::Instant, value: bool, preset: time::Duration) -> TimerResult {
|
||||
self.base.run(now, value);
|
||||
TimerResult {
|
||||
done: value && self.base.timer(now, preset),
|
||||
timing: value && !self.base.timer(now, preset),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct TimerOff {
|
||||
base: BaseTimer<bool>,
|
||||
}
|
||||
|
||||
impl TimerOff {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn run(&mut self, now: time::Instant, value: bool, preset: time::Duration) -> TimerResult {
|
||||
self.base.run(now, value);
|
||||
TimerResult {
|
||||
done: value || !self.base.timer(now, preset),
|
||||
timing: !value && !self.base.timer(now, preset),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct PulseTimer {
|
||||
base: BaseTimer<bool>,
|
||||
}
|
||||
|
||||
impl PulseTimer {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn run(&mut self, now: time::Instant, value: bool, preset: time::Duration) -> TimerResult {
|
||||
let already_timing = !self.base.timer(now, preset);
|
||||
|
||||
if already_timing || value {
|
||||
self.base.run(now, true)
|
||||
} else {
|
||||
self.base.reset_value(false);
|
||||
}
|
||||
|
||||
TimerResult {
|
||||
done: !self.base.timer(now, preset),
|
||||
timing: !self.base.timer(now, preset),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait TimeExt {
|
||||
fn millis(&self) -> time::Duration;
|
||||
fn secs(&self) -> time::Duration;
|
||||
}
|
||||
|
||||
impl TimeExt for i32 {
|
||||
fn millis(&self) -> time::Duration {
|
||||
time::Duration::from_millis((*self).try_into().unwrap())
|
||||
}
|
||||
|
||||
fn secs(&self) -> time::Duration {
|
||||
time::Duration::from_secs((*self).try_into().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
const TIMESTEP: time::Duration = time::Duration::from_millis(10);
|
||||
const PRESET: time::Duration = time::Duration::from_millis(20);
|
||||
|
||||
#[test]
|
||||
fn timer_on() {
|
||||
let inp = [0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0];
|
||||
let done = [0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0];
|
||||
let timing = [0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0];
|
||||
|
||||
let mut timer = TimerOn::new();
|
||||
let mut now = time::Instant::now();
|
||||
|
||||
for (i, ((inp, done), timing)) in inp
|
||||
.iter()
|
||||
.map(|i| *i != 0)
|
||||
.zip(done.iter().map(|i| *i != 0))
|
||||
.zip(timing.iter().map(|i| *i != 0))
|
||||
.enumerate()
|
||||
{
|
||||
let res = timer.run(now, inp, PRESET);
|
||||
assert_eq!(res.done, done, "`done` mismatch at timestep #{i}");
|
||||
assert_eq!(res.timing, timing, "`timing` mismatch at timestep #{i}");
|
||||
now += TIMESTEP;
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn timer_on_start() {
|
||||
let inp = [1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0];
|
||||
let done = [0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0];
|
||||
let timing = [1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0];
|
||||
|
||||
let mut timer = TimerOn::new();
|
||||
let mut now = time::Instant::now();
|
||||
|
||||
for (i, ((inp, done), timing)) in inp
|
||||
.iter()
|
||||
.map(|i| *i != 0)
|
||||
.zip(done.iter().map(|i| *i != 0))
|
||||
.zip(timing.iter().map(|i| *i != 0))
|
||||
.enumerate()
|
||||
{
|
||||
let res = timer.run(now, inp, PRESET);
|
||||
assert_eq!(res.done, done, "`done` mismatch at timestep #{i}");
|
||||
assert_eq!(res.timing, timing, "`timing` mismatch at timestep #{i}");
|
||||
now += TIMESTEP;
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn timer_off() {
|
||||
let inp = [0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0];
|
||||
let done = [0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0];
|
||||
let timing = [0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0];
|
||||
|
||||
let mut timer = TimerOff::new();
|
||||
let mut now = time::Instant::now();
|
||||
|
||||
for (i, ((inp, done), timing)) in inp
|
||||
.iter()
|
||||
.map(|i| *i != 0)
|
||||
.zip(done.iter().map(|i| *i != 0))
|
||||
.zip(timing.iter().map(|i| *i != 0))
|
||||
.enumerate()
|
||||
{
|
||||
let res = timer.run(now, inp, PRESET);
|
||||
assert_eq!(res.done, done, "`done` mismatch at timestep #{i}");
|
||||
assert_eq!(res.timing, timing, "`timing` mismatch at timestep #{i}");
|
||||
now += TIMESTEP;
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn timer_off_start() {
|
||||
let inp = [1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0];
|
||||
let done = [1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0];
|
||||
let timing = [0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0];
|
||||
|
||||
let mut timer = TimerOff::new();
|
||||
let mut now = time::Instant::now();
|
||||
|
||||
for (i, ((inp, done), timing)) in inp
|
||||
.iter()
|
||||
.map(|i| *i != 0)
|
||||
.zip(done.iter().map(|i| *i != 0))
|
||||
.zip(timing.iter().map(|i| *i != 0))
|
||||
.enumerate()
|
||||
{
|
||||
let res = timer.run(now, inp, PRESET);
|
||||
assert_eq!(res.done, done, "`done` mismatch at timestep #{i}");
|
||||
assert_eq!(res.timing, timing, "`timing` mismatch at timestep #{i}");
|
||||
now += TIMESTEP;
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn timer_pulse() {
|
||||
let inp = [0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0];
|
||||
let done = [0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0];
|
||||
let timing = done.clone();
|
||||
|
||||
let mut timer = PulseTimer::new();
|
||||
let mut now = time::Instant::now();
|
||||
|
||||
for (i, ((inp, done), timing)) in inp
|
||||
.iter()
|
||||
.map(|i| *i != 0)
|
||||
.zip(done.iter().map(|i| *i != 0))
|
||||
.zip(timing.iter().map(|i| *i != 0))
|
||||
.enumerate()
|
||||
{
|
||||
let res = timer.run(now, inp, time::Duration::from_millis(30));
|
||||
assert_eq!(res.done, done, "`done` mismatch at timestep #{i}");
|
||||
assert_eq!(res.timing, timing, "`timing` mismatch at timestep #{i}");
|
||||
now += TIMESTEP;
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn timer_pulse_start() {
|
||||
let inp = [1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0];
|
||||
let done = [1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0];
|
||||
let timing = done.clone();
|
||||
|
||||
let mut timer = PulseTimer::new();
|
||||
let mut now = time::Instant::now();
|
||||
|
||||
for (i, ((inp, done), timing)) in inp
|
||||
.iter()
|
||||
.map(|i| *i != 0)
|
||||
.zip(done.iter().map(|i| *i != 0))
|
||||
.zip(timing.iter().map(|i| *i != 0))
|
||||
.enumerate()
|
||||
{
|
||||
let res = timer.run(now, inp, time::Duration::from_millis(30));
|
||||
assert_eq!(res.done, done, "`done` mismatch at timestep #{i}");
|
||||
assert_eq!(res.timing, timing, "`timing` mismatch at timestep #{i}");
|
||||
now += TIMESTEP;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue