1
0
forked from ROMEO/nexosim

Add instance name to model contexts

This commit is contained in:
Jaŭhien Piatlicki 2024-05-10 10:54:45 +02:00
parent 4b5195f981
commit 195bcdceba
13 changed files with 131 additions and 44 deletions

View File

@ -79,8 +79,8 @@ impl Model for MotorAssembly {
// clones. // clones.
motor.position = self.position.clone(); motor.position = self.position.clone();
setup_context.add_model(driver, driver_mbox); setup_context.add_model(driver, driver_mbox, "driver");
setup_context.add_model(motor, motor_mbox); setup_context.add_model(motor, motor_mbox, "motor");
} }
} }
@ -105,7 +105,9 @@ fn main() {
let t0 = MonotonicTime::EPOCH; let t0 = MonotonicTime::EPOCH;
// Assembly and initialization. // Assembly and initialization.
let mut simu = SimInit::new().add_model(assembly, assembly_mbox).init(t0); let mut simu = SimInit::new()
.add_model(assembly, assembly_mbox, "assembly")
.init(t0);
// ---------- // ----------
// Simulation. // Simulation.

View File

@ -368,9 +368,9 @@ fn main() {
// Assembly and initialization. // Assembly and initialization.
let mut simu = SimInit::new() let mut simu = SimInit::new()
.add_model(controller, controller_mbox) .add_model(controller, controller_mbox, "controller")
.add_model(pump, pump_mbox) .add_model(pump, pump_mbox, "pump")
.add_model(tank, tank_mbox) .add_model(tank, tank_mbox, "tank")
.init(t0); .init(t0);
// ---------- // ----------

View File

@ -140,10 +140,10 @@ fn main() {
// Assembly and initialization. // Assembly and initialization.
let mut simu = SimInit::new() let mut simu = SimInit::new()
.add_model(psu, psu_mbox) .add_model(psu, psu_mbox, "psu")
.add_model(load1, load1_mbox) .add_model(load1, load1_mbox, "load1")
.add_model(load2, load2_mbox) .add_model(load2, load2_mbox, "load2")
.add_model(load3, load3_mbox) .add_model(load3, load3_mbox, "load3")
.init(t0); .init(t0);
// ---------- // ----------

View File

@ -53,8 +53,15 @@ impl Motor {
/// For the sake of simplicity, we do as if the rotor rotates /// For the sake of simplicity, we do as if the rotor rotates
/// instantaneously. If the current is too weak to overcome the load or when /// instantaneously. If the current is too weak to overcome the load or when
/// attempting to move to an opposite phase, the position remains unchanged. /// attempting to move to an opposite phase, the position remains unchanged.
pub async fn current_in(&mut self, current: (f64, f64)) { pub async fn current_in(&mut self, current: (f64, f64), context: &Context<Self>) {
assert!(!current.0.is_nan() && !current.1.is_nan()); assert!(!current.0.is_nan() && !current.1.is_nan());
println!(
"Model instance {} at time {}: setting currents: {:.2} and {:.2}",
context.name(),
context.time(),
current.0,
current.1
);
let (target_phase, abs_current) = match (current.0 != 0.0, current.1 != 0.0) { let (target_phase, abs_current) = match (current.0 != 0.0, current.1 != 0.0) {
(false, false) => return, (false, false) => return,
@ -78,9 +85,16 @@ impl Motor {
} }
/// Torque applied by the load [N·m] -- input port. /// Torque applied by the load [N·m] -- input port.
pub fn load(&mut self, torque: f64) { pub fn load(&mut self, torque: f64, context: &Context<Self>) {
assert!(torque >= 0.0); assert!(torque >= 0.0);
println!(
"Model instance {} at time {}: setting load: {:.2}",
context.name(),
context.time(),
torque
);
self.torque = torque; self.torque = torque;
} }
} }
@ -124,6 +138,13 @@ impl Driver {
/// Sets the pulse rate (sign = direction) [Hz] -- input port. /// Sets the 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, context: &Context<Self>) {
println!(
"Model instance {} at time {}: setting pps: {:.2}",
context.name(),
context.time(),
pps
);
let pps = pps.signum() * pps.abs().clamp(Self::MIN_PPS, Self::MAX_PPS); let pps = pps.signum() * pps.abs().clamp(Self::MIN_PPS, Self::MAX_PPS);
if pps == self.pps { if pps == self.pps {
return; return;
@ -148,6 +169,12 @@ impl Driver {
_: (), _: (),
context: &'a Context<Self>, context: &'a Context<Self>,
) -> impl Future<Output = ()> + Send + 'a { ) -> impl Future<Output = ()> + Send + 'a {
println!(
"Model instance {} at time {}: sending pulse",
context.name(),
context.time()
);
async move { async move {
let current_out = match self.next_phase { let current_out = match self.next_phase {
0 => (self.current, 0.0), 0 => (self.current, 0.0),
@ -205,8 +232,8 @@ fn main() {
// Assembly and initialization. // Assembly and initialization.
let mut simu = SimInit::new() let mut simu = SimInit::new()
.add_model(driver, driver_mbox) .add_model(driver, driver_mbox, "driver")
.add_model(motor, motor_mbox) .add_model(motor, motor_mbox, "motor")
.init(t0); .init(t0);
// ---------- // ----------

View File

@ -231,10 +231,10 @@
//! // Pick an arbitrary simulation start time and build the simulation. //! // Pick an arbitrary simulation start time and build the simulation.
//! let t0 = MonotonicTime::EPOCH; //! let t0 = MonotonicTime::EPOCH;
//! let mut simu = SimInit::new() //! let mut simu = SimInit::new()
//! .add_model(multiplier1, multiplier1_mbox) //! .add_model(multiplier1, multiplier1_mbox, "multiplier1")
//! .add_model(multiplier2, multiplier2_mbox) //! .add_model(multiplier2, multiplier2_mbox, "multiplier2")
//! .add_model(delay1, delay1_mbox) //! .add_model(delay1, delay1_mbox, "delay1")
//! .add_model(delay2, delay2_mbox) //! .add_model(delay2, delay2_mbox, "delay2")
//! .init(t0); //! .init(t0);
//! ``` //! ```
//! //!
@ -319,10 +319,10 @@
//! # let input_address = multiplier1_mbox.address(); //! # let input_address = multiplier1_mbox.address();
//! # let t0 = MonotonicTime::EPOCH; //! # let t0 = MonotonicTime::EPOCH;
//! # let mut simu = SimInit::new() //! # let mut simu = SimInit::new()
//! # .add_model(multiplier1, multiplier1_mbox) //! # .add_model(multiplier1, multiplier1_mbox, "multiplier1")
//! # .add_model(multiplier2, multiplier2_mbox) //! # .add_model(multiplier2, multiplier2_mbox, "multiplier2")
//! # .add_model(delay1, delay1_mbox) //! # .add_model(delay1, delay1_mbox, "delay1")
//! # .add_model(delay2, delay2_mbox) //! # .add_model(delay2, delay2_mbox, "delay2")
//! # .init(t0); //! # .init(t0);
//! // Send a value to the first multiplier. //! // Send a value to the first multiplier.
//! simu.process_event(Multiplier::input, 21.0, &input_address); //! simu.process_event(Multiplier::input, 21.0, &input_address);

View File

@ -81,6 +81,7 @@ use super::Model;
// The self-scheduling caveat seems related to this issue: // The self-scheduling caveat seems related to this issue:
// https://github.com/rust-lang/rust/issues/78649 // https://github.com/rust-lang/rust/issues/78649
pub struct Context<M: Model> { pub struct Context<M: Model> {
name: String,
sender: Sender<M>, sender: Sender<M>,
scheduler_queue: Arc<Mutex<SchedulerQueue>>, scheduler_queue: Arc<Mutex<SchedulerQueue>>,
time: SyncCellReader<TearableAtomicTime>, time: SyncCellReader<TearableAtomicTime>,
@ -89,17 +90,24 @@ pub struct Context<M: Model> {
impl<M: Model> Context<M> { impl<M: Model> Context<M> {
/// Creates a new local context. /// Creates a new local context.
pub(crate) fn new( pub(crate) fn new(
name: String,
sender: Sender<M>, sender: Sender<M>,
scheduler_queue: Arc<Mutex<SchedulerQueue>>, scheduler_queue: Arc<Mutex<SchedulerQueue>>,
time: SyncCellReader<TearableAtomicTime>, time: SyncCellReader<TearableAtomicTime>,
) -> Self { ) -> Self {
Self { Self {
name,
sender, sender,
scheduler_queue, scheduler_queue,
time, time,
} }
} }
/// Returns the model instance name.
pub fn name(&self) -> &str {
&self.name
}
/// Returns the current simulation time. /// Returns the current simulation time.
/// ///
/// # Examples /// # Examples
@ -440,11 +448,13 @@ impl<M: Model> fmt::Debug for Context<M> {
/// let b = SubmodelB::default(); /// let b = SubmodelB::default();
/// let a_mbox = Mailbox::new(); /// let a_mbox = Mailbox::new();
/// let b_mbox = Mailbox::new(); /// let b_mbox = Mailbox::new();
/// let a_name = setup_context.name().to_string() + "::a";
/// let b_name = setup_context.name().to_string() + "::b";
/// ///
/// a.out.connect(SubmodelB::input, &b_mbox); /// a.out.connect(SubmodelB::input, &b_mbox);
/// ///
/// setup_context.add_model(a, a_mbox); /// setup_context.add_model(a, a_mbox, a_name);
/// setup_context.add_model(b, b_mbox); /// setup_context.add_model(b, b_mbox, b_name);
/// } /// }
/// } /// }
/// ///
@ -472,11 +482,25 @@ impl<'a, M: Model> SetupContext<'a, M> {
} }
} }
/// Returns the model instance name.
pub fn name(&self) -> &str {
&self.context.name
}
/// Adds a new model and its mailbox to the simulation bench. /// Adds a new model and its mailbox to the simulation bench.
pub fn add_model<N: Model>(&self, model: N, mailbox: Mailbox<N>) { ///
/// The `name` argument needs not be unique (it can be an empty string) and
/// is used for convenience for model instance identification (e.g. for
/// logging purposes).
pub fn add_model<N: Model>(&self, model: N, mailbox: Mailbox<N>, name: impl Into<String>) {
let mut submodel_name = name.into();
if !self.context.name().is_empty() && !submodel_name.is_empty() {
submodel_name = self.context.name().to_string() + "." + &submodel_name;
}
simulation::add_model( simulation::add_model(
model, model,
mailbox, mailbox,
submodel_name,
self.context.scheduler_queue.clone(), self.context.scheduler_queue.clone(),
self.context.time.clone(), self.context.time.clone(),
self.executor, self.executor,

View File

@ -65,7 +65,8 @@
//! let mut child = ChildModel::new(); //! let mut child = ChildModel::new();
//! let child_mbox = Mailbox::new(); //! let child_mbox = Mailbox::new();
//! child.output = self.output.clone(); //! child.output = self.output.clone();
//! setup_context.add_model(child, child_mbox); //! let child_name = setup_context.name().to_string() + "::child";
//! setup_context.add_model(child, child_mbox, child_name);
//! } //! }
//! } //! }
//! ``` //! ```

View File

@ -614,8 +614,12 @@ mod tests {
let dummy_priority_queue = Arc::new(Mutex::new(PriorityQueue::new())); let dummy_priority_queue = Arc::new(Mutex::new(PriorityQueue::new()));
let dummy_time = let dummy_time =
SyncCell::new(TearableAtomicTime::new(MonotonicTime::EPOCH)).reader(); SyncCell::new(TearableAtomicTime::new(MonotonicTime::EPOCH)).reader();
let dummy_context = let dummy_context = Context::new(
Context::new(dummy_address, dummy_priority_queue, dummy_time); String::new(),
dummy_address,
dummy_priority_queue,
dummy_time,
);
block_on(mailbox.recv(&mut counter, &dummy_context)).unwrap(); block_on(mailbox.recv(&mut counter, &dummy_context)).unwrap();
} }
}) })
@ -665,8 +669,12 @@ mod tests {
let dummy_priority_queue = Arc::new(Mutex::new(PriorityQueue::new())); let dummy_priority_queue = Arc::new(Mutex::new(PriorityQueue::new()));
let dummy_time = let dummy_time =
SyncCell::new(TearableAtomicTime::new(MonotonicTime::EPOCH)).reader(); SyncCell::new(TearableAtomicTime::new(MonotonicTime::EPOCH)).reader();
let dummy_context = let dummy_context = Context::new(
Context::new(dummy_address, dummy_priority_queue, dummy_time); String::new(),
dummy_address,
dummy_priority_queue,
dummy_time,
);
block_on(mailbox.recv(&mut counter, &dummy_context)).unwrap(); block_on(mailbox.recv(&mut counter, &dummy_context)).unwrap();
thread::sleep(std::time::Duration::from_millis(100)); thread::sleep(std::time::Duration::from_millis(100));
} }

View File

@ -497,8 +497,12 @@ mod tests {
let dummy_priority_queue = Arc::new(Mutex::new(PriorityQueue::new())); let dummy_priority_queue = Arc::new(Mutex::new(PriorityQueue::new()));
let dummy_time = let dummy_time =
SyncCell::new(TearableAtomicTime::new(MonotonicTime::EPOCH)).reader(); SyncCell::new(TearableAtomicTime::new(MonotonicTime::EPOCH)).reader();
let dummy_context = let dummy_context = Context::new(
Context::new(dummy_address, dummy_priority_queue, dummy_time); String::new(),
dummy_address,
dummy_priority_queue,
dummy_time,
);
block_on(mailbox.recv(&mut counter, &dummy_context)).unwrap(); block_on(mailbox.recv(&mut counter, &dummy_context)).unwrap();
} }
}) })
@ -548,8 +552,12 @@ mod tests {
let dummy_priority_queue = Arc::new(Mutex::new(PriorityQueue::new())); let dummy_priority_queue = Arc::new(Mutex::new(PriorityQueue::new()));
let dummy_time = let dummy_time =
SyncCell::new(TearableAtomicTime::new(MonotonicTime::EPOCH)).reader(); SyncCell::new(TearableAtomicTime::new(MonotonicTime::EPOCH)).reader();
let dummy_context = let dummy_context = Context::new(
Context::new(dummy_address, dummy_priority_queue, dummy_time); String::new(),
dummy_address,
dummy_priority_queue,
dummy_time,
);
block_on(mailbox.recv(&mut counter, &dummy_context)).unwrap(); block_on(mailbox.recv(&mut counter, &dummy_context)).unwrap();
thread::sleep(std::time::Duration::from_millis(100)); thread::sleep(std::time::Duration::from_millis(100));
} }

View File

@ -666,13 +666,14 @@ impl Error for QueryError {}
pub(crate) fn add_model<M: Model>( pub(crate) fn add_model<M: Model>(
mut model: M, mut model: M,
mailbox: Mailbox<M>, mailbox: Mailbox<M>,
name: String,
scheduler_queue: Arc<Mutex<SchedulerQueue>>, scheduler_queue: Arc<Mutex<SchedulerQueue>>,
time: SyncCellReader<TearableAtomicTime>, time: SyncCellReader<TearableAtomicTime>,
executor: &Executor, executor: &Executor,
) { ) {
let sender = mailbox.0.sender(); let sender = mailbox.0.sender();
let context = Context::new(sender, scheduler_queue, time); let context = Context::new(name, sender, scheduler_queue, time);
let setup_context = SetupContext::new(&mailbox, &context, executor); let setup_context = SetupContext::new(&mailbox, &context, executor);
model.setup(&setup_context); model.setup(&setup_context);

View File

@ -41,11 +41,27 @@ impl SimInit {
} }
/// Adds a model and its mailbox to the simulation bench. /// Adds a model and its mailbox to the simulation bench.
pub fn add_model<M: Model>(self, model: M, mailbox: Mailbox<M>) -> Self { ///
/// The `name` argument needs not be unique (it can be the empty string) and
/// is used for convenience for the model instance identification (e.g. for
/// logging purposes).
pub fn add_model<M: Model>(
self,
model: M,
mailbox: Mailbox<M>,
name: impl Into<String>,
) -> Self {
let scheduler_queue = self.scheduler_queue.clone(); let scheduler_queue = self.scheduler_queue.clone();
let time = self.time.reader(); let time = self.time.reader();
add_model(model, mailbox, scheduler_queue, time, &self.executor); add_model(
model,
mailbox,
name.into(),
scheduler_queue,
time,
&self.executor,
);
self self
} }

View File

@ -33,7 +33,7 @@ fn model_schedule_event() {
let addr = mbox.address(); let addr = mbox.address();
let t0 = MonotonicTime::EPOCH; let t0 = MonotonicTime::EPOCH;
let mut simu = SimInit::new().add_model(model, mbox).init(t0); let mut simu = SimInit::new().add_model(model, mbox, "").init(t0);
simu.process_event(TestModel::trigger, (), addr); simu.process_event(TestModel::trigger, (), addr);
simu.step(); simu.step();
@ -78,7 +78,7 @@ fn model_cancel_future_keyed_event() {
let addr = mbox.address(); let addr = mbox.address();
let t0 = MonotonicTime::EPOCH; let t0 = MonotonicTime::EPOCH;
let mut simu = SimInit::new().add_model(model, mbox).init(t0); let mut simu = SimInit::new().add_model(model, mbox, "").init(t0);
simu.process_event(TestModel::trigger, (), addr); simu.process_event(TestModel::trigger, (), addr);
simu.step(); simu.step();
@ -124,7 +124,7 @@ fn model_cancel_same_time_keyed_event() {
let addr = mbox.address(); let addr = mbox.address();
let t0 = MonotonicTime::EPOCH; let t0 = MonotonicTime::EPOCH;
let mut simu = SimInit::new().add_model(model, mbox).init(t0); let mut simu = SimInit::new().add_model(model, mbox, "").init(t0);
simu.process_event(TestModel::trigger, (), addr); simu.process_event(TestModel::trigger, (), addr);
simu.step(); simu.step();
@ -166,7 +166,7 @@ fn model_schedule_periodic_event() {
let addr = mbox.address(); let addr = mbox.address();
let t0 = MonotonicTime::EPOCH; let t0 = MonotonicTime::EPOCH;
let mut simu = SimInit::new().add_model(model, mbox).init(t0); let mut simu = SimInit::new().add_model(model, mbox, "").init(t0);
simu.process_event(TestModel::trigger, (), addr); simu.process_event(TestModel::trigger, (), addr);
@ -216,7 +216,7 @@ fn model_cancel_periodic_event() {
let addr = mbox.address(); let addr = mbox.address();
let t0 = MonotonicTime::EPOCH; let t0 = MonotonicTime::EPOCH;
let mut simu = SimInit::new().add_model(model, mbox).init(t0); let mut simu = SimInit::new().add_model(model, mbox, "").init(t0);
simu.process_event(TestModel::trigger, (), addr); simu.process_event(TestModel::trigger, (), addr);

View File

@ -38,7 +38,7 @@ fn passthrough_bench<T: Clone + Send + 'static>(
model.output.connect_sink(&out_stream); model.output.connect_sink(&out_stream);
let addr = mbox.address(); let addr = mbox.address();
let simu = SimInit::new().add_model(model, mbox).init(t0); let simu = SimInit::new().add_model(model, mbox, "").init(t0);
(simu, addr, out_stream) (simu, addr, out_stream)
} }
@ -246,7 +246,7 @@ fn timestamp_bench(
let addr = mbox.address(); let addr = mbox.address();
let simu = SimInit::new() let simu = SimInit::new()
.add_model(model, mbox) .add_model(model, mbox, "")
.set_clock(clock) .set_clock(clock)
.init(t0); .init(t0);