use crate::machine::Board;
use crate::time::VirtualNanos;
pub trait Edge: Board {
type Signals;
const OUTPUTS: &'static [&'static str];
fn sense(&mut self, signals: &Self::Signals);
}
pub trait Coupled: Board {
fn outputs(&self) -> &'static [&'static str];
fn sense_wire(&mut self, bytes: &[u8]) -> Result<(), crate::link::WireError>;
}
impl<B: Edge> Coupled for B
where
B::Signals: crate::link::Wire,
{
fn outputs(&self) -> &'static [&'static str] {
B::OUTPUTS
}
fn sense_wire(&mut self, bytes: &[u8]) -> Result<(), crate::link::WireError> {
let signals = <B::Signals as crate::link::Wire>::decode(&mut &*bytes)?;
self.sense(&signals);
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Pulse {
pub width: VirtualNanos,
pub period: VirtualNanos,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Curve {
pub zero: VirtualNanos,
pub full: VirtualNanos,
}
pub const FULL: u32 = 1000;
impl Curve {
#[must_use]
pub fn command(self, pulse: Pulse) -> u32 {
let span = self.full.0.saturating_sub(self.zero.0).max(1);
let above = pulse.width.0.saturating_sub(self.zero.0).min(span);
u32::try_from(above * u64::from(FULL) / span).expect("at most the whole")
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Actuator {
pub name: &'static str,
pub pulse: Option<Pulse>,
pub command: Option<u32>,
}
impl Actuator {
#[must_use]
pub fn on(name: &'static str, pulse: Option<Pulse>, curve: Curve) -> Self {
Self {
name,
pulse,
command: pulse.map(|pulse| curve.command(pulse)),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
const SERVO: Curve = Curve {
zero: VirtualNanos(1_000_000),
full: VirtualNanos(2_000_000),
};
fn width(nanos: u64) -> Pulse {
Pulse {
width: VirtualNanos(nanos),
period: VirtualNanos(20_000_000),
}
}
#[test]
fn a_command_is_where_the_width_falls_between_the_ends_and_never_past_them() {
assert_eq!(SERVO.command(width(1_000_000)), 0);
assert_eq!(SERVO.command(width(1_250_000)), 250);
assert_eq!(SERVO.command(width(2_000_000)), FULL);
assert_eq!(SERVO.command(width(0)), 0, "narrower than nothing");
assert_eq!(
SERVO.command(width(3_000_000)),
FULL,
"wider than everything"
);
}
#[test]
fn an_output_without_a_pulse_commands_nothing() {
let off = Actuator::on("OUT", None, SERVO);
assert_eq!(off.command, None);
let on = Actuator::on("OUT", Some(width(1_500_000)), SERVO);
assert_eq!(on.command, Some(500));
}
}