use std::fmt;
use std::marker::PhantomData;
use std::sync::Mutex;
use std::sync::{Arc, atomic::AtomicBool};
use std::time::Duration;
use serde::{Deserialize, Serialize, de::DeserializeOwned};
use crate::executor::{Executor, Signal};
use crate::path::Path;
use crate::ports::InputFn;
use crate::simulation::{
self, Address, EventId, EventIdErased, EventKey, GlobalScheduler, InjectorQueue, InputSource,
Mailbox, ModelInjector, SchedulerRegistry, SchedulingError,
};
use crate::time::{ClockReader, Deadline, MonotonicTime};
use super::{Model, ProtoModel, RegisteredModel};
#[cfg(all(test, not(nexosim_loom)))]
use crate::channel::Receiver;
pub struct Context<M: Model> {
path: Path,
scheduler: GlobalScheduler,
address: Address<M>,
origin_id: usize,
model_registry: Arc<ModelRegistry>,
}
impl<M: Model> Context<M> {
pub(crate) fn new(
path: Path,
scheduler: GlobalScheduler,
address: Address<M>,
origin_id: usize,
model_registry: Arc<ModelRegistry>,
) -> Self {
Self {
path,
scheduler,
address,
origin_id,
model_registry,
}
}
pub fn path(&self) -> &Path {
&self.path
}
pub fn time(&self) -> MonotonicTime {
self.scheduler.time()
}
pub fn schedule_event<T>(
&self,
deadline: impl Deadline,
schedulable_id: &SchedulableId<M, T>,
arg: T,
) -> Result<(), SchedulingError>
where
T: Send + Clone + 'static,
{
self.scheduler.schedule_event_from(
deadline,
&schedulable_id.source_id(&self.model_registry),
arg,
self.origin_id,
)
}
pub fn schedule_keyed_event<T>(
&self,
deadline: impl Deadline,
schedulable_id: &SchedulableId<M, T>,
arg: T,
) -> Result<EventKey, SchedulingError>
where
T: Send + Clone + 'static,
{
let event_key = self.scheduler.schedule_keyed_event_from(
deadline,
&schedulable_id.source_id(&self.model_registry),
arg,
self.origin_id,
)?;
Ok(event_key)
}
pub fn schedule_periodic_event<T>(
&self,
deadline: impl Deadline,
period: Duration,
schedulable_id: &SchedulableId<M, T>,
arg: T,
) -> Result<(), SchedulingError>
where
T: Send + Clone + 'static,
{
self.scheduler.schedule_periodic_event_from(
deadline,
period,
&schedulable_id.source_id(&self.model_registry),
arg,
self.origin_id,
)
}
pub fn schedule_keyed_periodic_event<T>(
&self,
deadline: impl Deadline,
period: Duration,
schedulable_id: &SchedulableId<M, T>,
arg: T,
) -> Result<EventKey, SchedulingError>
where
T: Send + Clone + 'static,
{
let event_key = self.scheduler.schedule_keyed_periodic_event_from(
deadline,
period,
&schedulable_id.source_id(&self.model_registry),
arg,
self.origin_id,
)?;
Ok(event_key)
}
}
#[cfg(all(test, not(nexosim_loom)))]
impl<M: Model<Env = ()>> Context<M> {
pub(crate) fn new_dummy() -> Self {
let dummy_address = Receiver::new(1).sender();
Context::new(
Path::from(""),
GlobalScheduler::new_dummy(),
Address(dummy_address),
0,
Arc::new(ModelRegistry::default()),
)
}
}
impl<M: Model> fmt::Debug for Context<M> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Context")
.field("path", &self.path())
.field("time", &self.time())
.field("address", &self.address)
.field("origin_id", &self.origin_id)
.finish_non_exhaustive()
}
}
pub struct BuildContext<'a, P: ProtoModel> {
mailbox: &'a Mailbox<P::Model>,
path: &'a Path,
scheduler: &'a GlobalScheduler,
scheduler_registry: &'a mut SchedulerRegistry,
injector: &'a Arc<Mutex<InjectorQueue>>,
origin_id: usize,
executor: &'a Executor,
abort_signal: &'a Signal,
registered_models: &'a mut Vec<RegisteredModel>,
is_resumed: Arc<AtomicBool>,
model_registry: Option<&'a Arc<ModelRegistry>>,
}
impl<'a, P: ProtoModel> BuildContext<'a, P> {
#[allow(clippy::too_many_arguments)]
pub(crate) fn new(
mailbox: &'a Mailbox<P::Model>,
path: &'a Path,
scheduler: &'a GlobalScheduler,
scheduler_registry: &'a mut SchedulerRegistry,
injector: &'a Arc<Mutex<InjectorQueue>>,
origin_id: usize,
executor: &'a Executor,
abort_signal: &'a Signal,
registered_models: &'a mut Vec<RegisteredModel>,
is_resumed: Arc<AtomicBool>,
) -> Self {
Self {
mailbox,
path,
scheduler,
scheduler_registry,
injector,
origin_id,
executor,
abort_signal,
registered_models,
is_resumed,
model_registry: None,
}
}
pub fn path(&self) -> &Path {
self.path
}
pub fn address(&self) -> Address<P::Model> {
self.mailbox.address()
}
pub fn register_schedulable<F, T, S>(&mut self, func: F) -> SchedulableId<P::Model, T>
where
F: for<'f> InputFn<'f, P::Model, T, S> + Clone + Sync,
T: Serialize + DeserializeOwned + Clone + Send + 'static,
S: Send + Sync + 'static,
{
let source = InputSource::new(func, self.address().clone());
let id = self.scheduler_registry.add_event_source(source);
SchedulableId(id.0, PhantomData, PhantomData)
}
pub fn add_submodel<S>(&mut self, model: S, mailbox: Mailbox<S::Model>, name: &str)
where
S: ProtoModel,
{
let submodel_path = self.path.join(name);
simulation::add_model(
model,
mailbox,
submodel_path,
self.scheduler.clone(),
self.scheduler_registry,
self.injector,
self.executor,
self.abort_signal,
self.registered_models,
self.is_resumed.clone(),
);
}
pub fn clock_reader(&self) -> ClockReader {
self.scheduler.clock_reader()
}
pub fn injector(&self) -> ModelInjector<P::Model> {
ModelInjector::new(
self.injector.clone(),
self.origin_id,
self.model_registry.unwrap().clone(),
)
}
pub(crate) fn set_model_registry(&mut self, model_registry: &'a Arc<ModelRegistry>) {
self.model_registry = Some(model_registry);
}
}
impl<'a, P: ProtoModel> fmt::Debug for BuildContext<'a, P> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("BuildContext")
.field("path", &self.path)
.field("origin_id", &self.origin_id)
.field("is_resumed", &self.is_resumed)
.finish()
}
}
#[derive(Debug, Default)]
pub struct ModelRegistry(Vec<EventIdErased>);
impl ModelRegistry {
#[doc(hidden)]
pub fn add<M: Model, T>(&mut self, schedulable_id: SchedulableId<M, T>) {
self.0.push(EventIdErased(schedulable_id.0));
}
pub(crate) fn get<M: Model, T>(&self, idx: usize) -> SchedulableId<M, T> {
SchedulableId(self.0[idx].0, PhantomData, PhantomData)
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct SchedulableId<M, T>(usize, PhantomData<M>, PhantomData<T>);
impl<M: Model, T> SchedulableId<M, T> {
const REGISTRY_MASK: usize = 1 << (usize::BITS - 1);
#[doc(hidden)]
pub const fn __from_decorated(id: usize) -> Self {
Self(id | Self::REGISTRY_MASK, PhantomData, PhantomData)
}
pub(crate) fn source_id(&self, registry: &ModelRegistry) -> EventId<T> {
match self.0 & Self::REGISTRY_MASK {
0 => EventId(self.0, PhantomData),
_ => EventId(
registry.get::<M, T>(self.0 ^ Self::REGISTRY_MASK).0,
PhantomData,
),
}
}
}
impl<M, T> Clone for SchedulableId<M, T> {
fn clone(&self) -> Self {
*self
}
}
impl<M, T> Copy for SchedulableId<M, T> {}