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 display;
|
||||||
mod fieldbus;
|
mod fieldbus;
|
||||||
|
mod util;
|
||||||
|
|
||||||
// Parameters
|
// Parameters
|
||||||
const CYCLE_TIME: u64 = 5000;
|
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