use std::vec::Vec;
use crate::foundation::{HostTime, Signal};
use crate::runtime_impl::bind::Runtime;
use crate::runtime_impl::error::HandleError;
use crate::runtime_impl::handles::{CmdId, HostPathId, SenseId};
use crate::runtime_impl::outbox::{Outbox, PortWriter};
#[derive(Copy, Clone, Debug, PartialEq)]
#[non_exhaustive]
pub enum HostCommand {
SetLevel {
path: HostPathId,
value: Signal,
},
Emit {
cmd: CmdId,
payload: Signal,
},
}
pub fn append_commands(outbox: Outbox<'_>, out: &mut Vec<HostCommand>) {
out.reserve(outbox.signals().len() + outbox.emits().len());
for s in outbox.signals() {
out.push(HostCommand::SetLevel {
path: s.path,
value: s.value,
});
}
for e in outbox.emits() {
out.push(HostCommand::Emit {
cmd: e.cmd,
payload: e.payload,
});
}
}
pub fn outbox_to_commands(outbox: Outbox<'_>) -> Vec<HostCommand> {
let mut cmds = Vec::new();
append_commands(outbox, &mut cmds);
cmds
}
pub trait Host {
fn time(&self) -> HostTime;
fn sample_into(&mut self, ports: &mut PortWriter<'_>) -> Result<(), HandleError>;
fn apply(&mut self, outbox: Outbox<'_>);
}
pub fn tick_once(host: &mut impl Host, rt: &mut Runtime) -> Result<(), HandleError> {
rt.begin_frame(host.time());
{
let mut w = rt.port_writer();
host.sample_into(&mut w)?;
}
rt.loom();
host.apply(rt.outbox());
Ok(())
}
#[derive(Clone, Debug, Default)]
pub struct NullHost {
pub tick: u64,
}
impl Host for NullHost {
fn time(&self) -> HostTime {
HostTime { tick: self.tick }
}
fn sample_into(&mut self, _ports: &mut PortWriter<'_>) -> Result<(), HandleError> {
Ok(())
}
fn apply(&mut self, _outbox: Outbox<'_>) {
self.tick = self.tick.wrapping_add(1);
}
}
#[derive(Clone, Debug, Default)]
pub struct ScriptedHost {
pub tick: u64,
pub frames: Vec<Vec<(SenseId, Signal)>>,
pub commands: Vec<HostCommand>,
pub commands_per_tick: Vec<usize>,
}
impl ScriptedHost {
pub fn new() -> Self {
Self::default()
}
pub fn push_frame(&mut self, senses: impl IntoIterator<Item = (SenseId, Signal)>) {
self.frames.push(senses.into_iter().collect());
}
pub fn last_commands(&self) -> &[HostCommand] {
let Some(&n) = self.commands_per_tick.last() else {
return &[];
};
let start = self.commands.len().saturating_sub(n);
&self.commands[start..]
}
}
impl Host for ScriptedHost {
fn time(&self) -> HostTime {
HostTime { tick: self.tick }
}
fn sample_into(&mut self, ports: &mut PortWriter<'_>) -> Result<(), HandleError> {
let i = self.tick as usize;
let Some(frame) = self.frames.get(i) else {
return Ok(());
};
for &(id, v) in frame {
ports.set_sense(id, v)?;
}
Ok(())
}
fn apply(&mut self, outbox: Outbox<'_>) {
let before = self.commands.len();
append_commands(outbox, &mut self.commands);
self.commands_per_tick
.push(self.commands.len().saturating_sub(before));
self.tick = self.tick.wrapping_add(1);
}
}