forked from ROMEO/nexosim
Finalize the Context and BuildContext API
The API style is now more uniform: both are passed by mutable ref, and only expose accessors. Additionally, the methods that were initially accessed through the scheduler field are now directly implemented on `Context`.
This commit is contained in:
@ -85,7 +85,7 @@ impl Model for MotorAssembly {}
|
||||
impl ProtoModel for ProtoMotorAssembly {
|
||||
type Model = MotorAssembly;
|
||||
|
||||
fn build(self, ctx: &mut BuildContext<Self>) -> MotorAssembly {
|
||||
fn build(self, cx: &mut BuildContext<Self>) -> MotorAssembly {
|
||||
let mut assembly = MotorAssembly::new();
|
||||
let mut motor = Motor::new(self.init_pos);
|
||||
let mut driver = Driver::new(1.0);
|
||||
@ -105,8 +105,8 @@ impl ProtoModel for ProtoMotorAssembly {
|
||||
motor.position = self.position;
|
||||
|
||||
// Add the submodels to the simulation.
|
||||
ctx.add_submodel(driver, driver_mbox, "driver");
|
||||
ctx.add_submodel(motor, motor_mbox, "motor");
|
||||
cx.add_submodel(driver, driver_mbox, "driver");
|
||||
cx.add_submodel(motor, motor_mbox, "motor");
|
||||
|
||||
assembly
|
||||
}
|
||||
|
@ -120,7 +120,7 @@ impl Controller {
|
||||
}
|
||||
|
||||
/// Starts brewing or cancels the current brew -- input port.
|
||||
pub async fn brew_cmd(&mut self, _: (), context: &Context<Self>) {
|
||||
pub async fn brew_cmd(&mut self, _: (), cx: &mut Context<Self>) {
|
||||
// If a brew was ongoing, sending the brew command is interpreted as a
|
||||
// request to cancel it.
|
||||
if let Some(key) = self.stop_brew_key.take() {
|
||||
@ -139,9 +139,7 @@ impl Controller {
|
||||
|
||||
// Schedule the `stop_brew()` method and turn on the pump.
|
||||
self.stop_brew_key = Some(
|
||||
context
|
||||
.scheduler
|
||||
.schedule_keyed_event(self.brew_time, Self::stop_brew, ())
|
||||
cx.schedule_keyed_event(self.brew_time, Self::stop_brew, ())
|
||||
.unwrap(),
|
||||
);
|
||||
self.pump_cmd.send(PumpCommand::On).await;
|
||||
@ -189,7 +187,7 @@ impl Tank {
|
||||
}
|
||||
|
||||
/// Water volume added [m³] -- input port.
|
||||
pub async fn fill(&mut self, added_volume: f64, context: &Context<Self>) {
|
||||
pub async fn fill(&mut self, added_volume: f64, cx: &mut Context<Self>) {
|
||||
// Ignore zero and negative values. We could also impose a maximum based
|
||||
// on tank capacity.
|
||||
if added_volume <= 0.0 {
|
||||
@ -207,11 +205,11 @@ impl Tank {
|
||||
state.set_empty_key.cancel();
|
||||
|
||||
// Update the volume, saturating at 0 in case of rounding errors.
|
||||
let time = context.scheduler.time();
|
||||
let time = cx.time();
|
||||
let elapsed_time = time.duration_since(state.last_volume_update).as_secs_f64();
|
||||
self.volume = (self.volume - state.flow_rate * elapsed_time).max(0.0);
|
||||
|
||||
self.schedule_empty(state.flow_rate, time, context).await;
|
||||
self.schedule_empty(state.flow_rate, time, cx).await;
|
||||
|
||||
// There is no need to broadcast the state of the water sense since
|
||||
// it could not be previously `Empty` (otherwise the dynamic state
|
||||
@ -229,10 +227,10 @@ impl Tank {
|
||||
/// # Panics
|
||||
///
|
||||
/// This method will panic if the flow rate is negative.
|
||||
pub async fn set_flow_rate(&mut self, flow_rate: f64, context: &Context<Self>) {
|
||||
pub async fn set_flow_rate(&mut self, flow_rate: f64, cx: &mut Context<Self>) {
|
||||
assert!(flow_rate >= 0.0);
|
||||
|
||||
let time = context.scheduler.time();
|
||||
let time = cx.time();
|
||||
|
||||
// If the flow rate was non-zero up to now, update the volume.
|
||||
if let Some(state) = self.dynamic_state.take() {
|
||||
@ -244,7 +242,7 @@ impl Tank {
|
||||
self.volume = (self.volume - state.flow_rate * elapsed_time).max(0.0);
|
||||
}
|
||||
|
||||
self.schedule_empty(flow_rate, time, context).await;
|
||||
self.schedule_empty(flow_rate, time, cx).await;
|
||||
}
|
||||
|
||||
/// Schedules a callback for when the tank becomes empty.
|
||||
@ -257,7 +255,7 @@ impl Tank {
|
||||
&mut self,
|
||||
flow_rate: f64,
|
||||
time: MonotonicTime,
|
||||
context: &Context<Self>,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
// Determine when the tank will be empty at the current flow rate.
|
||||
let duration_until_empty = if self.volume == 0.0 {
|
||||
@ -274,10 +272,7 @@ impl Tank {
|
||||
let duration_until_empty = Duration::from_secs_f64(duration_until_empty);
|
||||
|
||||
// Schedule the next update.
|
||||
match context
|
||||
.scheduler
|
||||
.schedule_keyed_event(duration_until_empty, Self::set_empty, ())
|
||||
{
|
||||
match cx.schedule_keyed_event(duration_until_empty, Self::set_empty, ()) {
|
||||
Ok(set_empty_key) => {
|
||||
let state = TankDynamicState {
|
||||
last_volume_update: time,
|
||||
@ -304,7 +299,7 @@ impl Tank {
|
||||
|
||||
impl Model for Tank {
|
||||
/// Broadcasts the initial state of the water sense.
|
||||
async fn init(mut self, _: &Context<Self>) -> InitializedModel<Self> {
|
||||
async fn init(mut self, _: &mut Context<Self>) -> InitializedModel<Self> {
|
||||
self.water_sense
|
||||
.send(if self.volume == 0.0 {
|
||||
WaterSenseState::Empty
|
||||
|
@ -136,11 +136,9 @@ impl Listener {
|
||||
|
||||
impl Model for Listener {
|
||||
/// Initialize model.
|
||||
async fn init(self, context: &Context<Self>) -> InitializedModel<Self> {
|
||||
async fn init(self, cx: &mut Context<Self>) -> InitializedModel<Self> {
|
||||
// Schedule periodic function that processes external events.
|
||||
context
|
||||
.scheduler
|
||||
.schedule_periodic_event(DELTA, PERIOD, Listener::process, ())
|
||||
cx.schedule_periodic_event(DELTA, PERIOD, Listener::process, ())
|
||||
.unwrap();
|
||||
|
||||
self.into()
|
||||
|
@ -90,7 +90,7 @@ impl Motor {
|
||||
|
||||
impl Model for Motor {
|
||||
/// Broadcasts the initial position of the motor.
|
||||
async fn init(mut self, _: &Context<Self>) -> InitializedModel<Self> {
|
||||
async fn init(mut self, _: &mut Context<Self>) -> InitializedModel<Self> {
|
||||
self.position.send(self.pos).await;
|
||||
self.into()
|
||||
}
|
||||
@ -126,7 +126,7 @@ impl Driver {
|
||||
}
|
||||
|
||||
/// Pulse rate (sign = direction) [Hz] -- input port.
|
||||
pub async fn pulse_rate(&mut self, pps: f64, context: &Context<Self>) {
|
||||
pub async fn pulse_rate(&mut self, pps: f64, cx: &mut Context<Self>) {
|
||||
let pps = pps.signum() * pps.abs().clamp(Self::MIN_PPS, Self::MAX_PPS);
|
||||
if pps == self.pps {
|
||||
return;
|
||||
@ -138,7 +138,7 @@ impl Driver {
|
||||
// Trigger the rotation if the motor is currently idle. Otherwise the
|
||||
// new value will be accounted for at the next pulse.
|
||||
if is_idle {
|
||||
self.send_pulse((), context).await;
|
||||
self.send_pulse((), cx).await;
|
||||
}
|
||||
}
|
||||
|
||||
@ -149,7 +149,7 @@ impl Driver {
|
||||
fn send_pulse<'a>(
|
||||
&'a mut self,
|
||||
_: (),
|
||||
context: &'a Context<Self>,
|
||||
cx: &'a mut Context<Self>,
|
||||
) -> impl Future<Output = ()> + Send + 'a {
|
||||
async move {
|
||||
let current_out = match self.next_phase {
|
||||
@ -170,9 +170,7 @@ impl Driver {
|
||||
let pulse_duration = Duration::from_secs_f64(1.0 / self.pps.abs());
|
||||
|
||||
// Schedule the next pulse.
|
||||
context
|
||||
.scheduler
|
||||
.schedule_event(pulse_duration, Self::send_pulse, ())
|
||||
cx.schedule_event(pulse_duration, Self::send_pulse, ())
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user