1
0
forked from ROMEO/nexosim

Initial (g)RPC implementation

This commit is contained in:
Serge Barral
2024-04-25 11:12:54 +02:00
parent c984202005
commit e84e802f09
55 changed files with 5814 additions and 1996 deletions

View File

@ -35,9 +35,10 @@ use std::future::Future;
use std::pin::Pin;
use std::time::Duration;
use asynchronix::model::{InitializedModel, Model, Output};
use asynchronix::model::{InitializedModel, Model};
use asynchronix::ports::{EventSlot, Output};
use asynchronix::simulation::{Mailbox, SimInit};
use asynchronix::time::{EventKey, MonotonicTime, Scheduler};
use asynchronix::time::{ActionKey, MonotonicTime, Scheduler};
/// Water pump.
pub struct Pump {
@ -81,7 +82,7 @@ pub struct Controller {
water_sense: WaterSenseState,
/// Event key, which if present indicates that the machine is currently
/// brewing -- internal state.
stop_brew_key: Option<EventKey>,
stop_brew_key: Option<ActionKey>,
}
impl Controller {
@ -323,7 +324,7 @@ impl Model for Tank {
/// is non-zero.
struct TankDynamicState {
last_volume_update: MonotonicTime,
set_empty_key: EventKey,
set_empty_key: ActionKey,
flow_rate: f64,
}
@ -364,7 +365,8 @@ fn main() {
pump.flow_rate.connect(Tank::set_flow_rate, &tank_mbox);
// Model handles for simulation.
let mut flow_rate = pump.flow_rate.connect_slot().0;
let mut flow_rate = EventSlot::new();
pump.flow_rate.connect_sink(&flow_rate);
let controller_addr = controller_mbox.address();
let tank_addr = tank_mbox.address();
@ -387,48 +389,48 @@ fn main() {
assert_eq!(simu.time(), t);
// Brew one espresso shot with the default brew time.
simu.send_event(Controller::brew_cmd, (), &controller_addr);
assert_eq!(flow_rate.take(), Some(pump_flow_rate));
simu.process_event(Controller::brew_cmd, (), &controller_addr);
assert_eq!(flow_rate.next(), Some(pump_flow_rate));
simu.step();
t += Controller::DEFAULT_BREW_TIME;
assert_eq!(simu.time(), t);
assert_eq!(flow_rate.take(), Some(0.0));
assert_eq!(flow_rate.next(), Some(0.0));
// Drink too much coffee.
let volume_per_shot = pump_flow_rate * Controller::DEFAULT_BREW_TIME.as_secs_f64();
let shots_per_tank = (init_tank_volume / volume_per_shot) as u64; // YOLO--who cares about floating-point rounding errors?
for _ in 0..(shots_per_tank - 1) {
simu.send_event(Controller::brew_cmd, (), &controller_addr);
assert_eq!(flow_rate.take(), Some(pump_flow_rate));
simu.process_event(Controller::brew_cmd, (), &controller_addr);
assert_eq!(flow_rate.next(), Some(pump_flow_rate));
simu.step();
t += Controller::DEFAULT_BREW_TIME;
assert_eq!(simu.time(), t);
assert_eq!(flow_rate.take(), Some(0.0));
assert_eq!(flow_rate.next(), Some(0.0));
}
// Check that the tank becomes empty before the completion of the next shot.
simu.send_event(Controller::brew_cmd, (), &controller_addr);
simu.process_event(Controller::brew_cmd, (), &controller_addr);
simu.step();
assert!(simu.time() < t + Controller::DEFAULT_BREW_TIME);
t = simu.time();
assert_eq!(flow_rate.take(), Some(0.0));
assert_eq!(flow_rate.next(), Some(0.0));
// Try to brew another shot while the tank is still empty.
simu.send_event(Controller::brew_cmd, (), &controller_addr);
assert!(flow_rate.take().is_none());
simu.process_event(Controller::brew_cmd, (), &controller_addr);
assert!(flow_rate.next().is_none());
// Change the brew time and fill up the tank.
let brew_time = Duration::new(30, 0);
simu.send_event(Controller::brew_time, brew_time, &controller_addr);
simu.send_event(Tank::fill, 1.0e-3, tank_addr);
simu.send_event(Controller::brew_cmd, (), &controller_addr);
assert_eq!(flow_rate.take(), Some(pump_flow_rate));
simu.process_event(Controller::brew_time, brew_time, &controller_addr);
simu.process_event(Tank::fill, 1.0e-3, tank_addr);
simu.process_event(Controller::brew_cmd, (), &controller_addr);
assert_eq!(flow_rate.next(), Some(pump_flow_rate));
simu.step();
t += brew_time;
assert_eq!(simu.time(), t);
assert_eq!(flow_rate.take(), Some(0.0));
assert_eq!(flow_rate.next(), Some(0.0));
// Interrupt the brew after 15s by pressing again the brew button.
simu.schedule_event(
@ -438,11 +440,11 @@ fn main() {
&controller_addr,
)
.unwrap();
simu.send_event(Controller::brew_cmd, (), &controller_addr);
assert_eq!(flow_rate.take(), Some(pump_flow_rate));
simu.process_event(Controller::brew_cmd, (), &controller_addr);
assert_eq!(flow_rate.next(), Some(pump_flow_rate));
simu.step();
t += Duration::from_secs(15);
assert_eq!(simu.time(), t);
assert_eq!(flow_rate.take(), Some(0.0));
assert_eq!(flow_rate.next(), Some(0.0));
}

View File

@ -26,7 +26,8 @@
//! │ ├───────────────────────────────▶ Total power
//! └──────────┘
//! ```
use asynchronix::model::{Model, Output, Requestor};
use asynchronix::model::Model;
use asynchronix::ports::{EventSlot, Output, Requestor};
use asynchronix::simulation::{Mailbox, SimInit};
use asynchronix::time::MonotonicTime;
@ -124,10 +125,14 @@ fn main() {
psu.pwr_out.connect(Load::pwr_in, &load3_mbox);
// Model handles for simulation.
let mut psu_power = psu.power.connect_slot().0;
let mut load1_power = load1.power.connect_slot().0;
let mut load2_power = load2.power.connect_slot().0;
let mut load3_power = load3.power.connect_slot().0;
let mut psu_power = EventSlot::new();
let mut load1_power = EventSlot::new();
let mut load2_power = EventSlot::new();
let mut load3_power = EventSlot::new();
psu.power.connect_sink(&psu_power);
load1.power.connect_sink(&load1_power);
load2.power.connect_sink(&load2_power);
load3.power.connect_sink(&load3_power);
let psu_addr = psu_mbox.address();
// Start time (arbitrary since models do not depend on absolute time).
@ -153,14 +158,14 @@ fn main() {
// Vary the supply voltage, check the load and power supply consumptions.
for voltage in [10.0, 15.0, 20.0] {
simu.send_event(PowerSupply::voltage_setting, voltage, &psu_addr);
simu.process_event(PowerSupply::voltage_setting, voltage, &psu_addr);
let v_square = voltage * voltage;
assert!(same_power(load1_power.take().unwrap(), v_square / r1));
assert!(same_power(load2_power.take().unwrap(), v_square / r2));
assert!(same_power(load3_power.take().unwrap(), v_square / r3));
assert!(same_power(load1_power.next().unwrap(), v_square / r1));
assert!(same_power(load2_power.next().unwrap(), v_square / r2));
assert!(same_power(load3_power.next().unwrap(), v_square / r3));
assert!(same_power(
psu_power.take().unwrap(),
psu_power.next().unwrap(),
v_square * (1.0 / r1 + 1.0 / r2 + 1.0 / r3)
));
}

View File

@ -18,7 +18,8 @@ use std::future::Future;
use std::pin::Pin;
use std::time::Duration;
use asynchronix::model::{InitializedModel, Model, Output};
use asynchronix::model::{InitializedModel, Model};
use asynchronix::ports::{EventBuffer, Output};
use asynchronix::simulation::{Mailbox, SimInit};
use asynchronix::time::{MonotonicTime, Scheduler};
@ -200,7 +201,8 @@ fn main() {
driver.current_out.connect(Motor::current_in, &motor_mbox);
// Model handles for simulation.
let mut position = motor.position.connect_stream().0;
let mut position = EventBuffer::new();
motor.position.connect_sink(&position);
let motor_addr = motor_mbox.address();
let driver_addr = driver_mbox.address();
@ -258,7 +260,7 @@ fn main() {
assert!(position.next().is_none());
// Increase the load beyond the torque limit for a 1A driver current.
simu.send_event(Motor::load, 2.0, &motor_addr);
simu.process_event(Motor::load, 2.0, &motor_addr);
// Advance simulation time and check that the motor is blocked.
simu.step();
@ -274,7 +276,7 @@ fn main() {
// Decrease the load below the torque limit for a 1A driver current and
// advance simulation time.
simu.send_event(Motor::load, 0.5, &motor_addr);
simu.process_event(Motor::load, 0.5, &motor_addr);
simu.step();
t += Duration::new(0, 100_000_000);
@ -298,7 +300,7 @@ fn main() {
// Now make the motor rotate in the opposite direction. Note that this
// driver only accounts for a new PPS at the next pulse.
simu.send_event(Driver::pulse_rate, -10.0, &driver_addr);
simu.process_event(Driver::pulse_rate, -10.0, &driver_addr);
simu.step();
t += Duration::new(0, 100_000_000);
assert_eq!(simu.time(), t);