1
0
Fork 0

Implement logic utilities

Implement timers for use in the logic code.
This commit is contained in:
rahix 2024-11-03 15:50:17 +01:00
parent f603fb633f
commit a92f0c45f2
2 changed files with 263 additions and 0 deletions

View file

@ -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
View 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;
}
}
}