Skip to content

Commit

Permalink
Pass parent model to input handlers
Browse files Browse the repository at this point in the history
Add ModelStore struct for easier management of models

Signed-off-by: Tin Svagelj <tin.svagelj@live.com>
  • Loading branch information
Caellian committed Aug 23, 2023
1 parent a185a54 commit c59cc8b
Show file tree
Hide file tree
Showing 14 changed files with 437 additions and 166 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
/target
target/
/Cargo.lock
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"rust-analyzer.linkedProjects": []
}
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ rand = "0.8"
float-ord = { version = "0.3", optional = true }
chrono = { version = "0.4", optional = true, features = ["serde"] }

paste = "1.0"
#litesim_impl = { path = "./litesim_impl" }

thiserror = "1.0"

Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ LiteSim supports multiple different time values which can be controlled through
- **f64** - flag: `time_f64`
- [**chrono**](https://github.com/chronotope/chrono) - flag: `time_chrono`

### Planned features
### Wanted features
- **Serde** support for systems as well as simulations in progress.
- **Backtracking** support for systems with models that support it.
- **Multithreading** support

## Alternatives
- [sim](https://github.com/ndebuhr/sim) - initial inspiration for this library
Expand Down
36 changes: 32 additions & 4 deletions examples/ping_pong.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,26 @@ pub struct PingPongEvent;

pub struct Player;

/*
#[litesim]
impl<'s> Model<'s> for Player {
#[input]
fn receive(&mut self, _: Event<PingPongEvent>, ctx: SimulationCtx<'s>) {
ctx.schedule_change(In(ctx.rand_range(0.0..1.0)))?;
Ok(())
},
#[output]
fn send(&self, data: PingPongEvent);
fn handle_update(&mut self, ctx: SimulationCtx<'s>) -> Result<(), SimulationError> {
// TODO: output connector should be strongly typed here assuming we've provided the names and types in output_connectors
log::info!("Sending");
push_event!(ctx, "send", PingPongEvent)
}
}
*/

impl<'s> Model<'s> for Player {
fn input_connectors(&self) -> &'static [&'static str] {
static RESULT: &[&'static str] = &["receive"];
Expand All @@ -18,14 +38,18 @@ impl<'s> Model<'s> for Player {
RESULT
}

fn get_input_handler<'h>(&self, index: usize) -> Option<Box<dyn ErasedInputHandler<'h, 's>>> where 's: 'h {
fn get_input_handler<'h>(&self, index: usize) -> Option<Box<dyn ErasedInputHandler<'h, 's>>>
where
's: 'h,
{
let handler = match index {
0 => {
let handler: &dyn Fn(
&mut Player,
Event<PingPongEvent>,
SimulationCtx<'s>,
ModelCtx<'s>,
) -> Result<(), SimulationError> =
&|_: Event<PingPongEvent>, ctx: SimulationCtx<'s>| {
&|_: &mut Player, _: Event<PingPongEvent>, ctx: ModelCtx<'s>| {
ctx.schedule_change(In(ctx.rand_range(0.0..1.0)))?;
Ok(())
};
Expand All @@ -37,11 +61,15 @@ impl<'s> Model<'s> for Player {
Some(handler)
}

fn handle_update(&mut self, ctx: SimulationCtx<'s>) -> Result<(), SimulationError> {
fn handle_update(&mut self, ctx: ModelCtx<'s>) -> Result<(), SimulationError> {
// TODO: output connector should be strongly typed here assuming we've provided the names and types in output_connectors
log::info!("Sending");
push_event!(ctx, "send", PingPongEvent)
}

fn type_id(&self) -> std::any::TypeId {
const_type_id::<Player>()
}
}

fn main() {
Expand Down
47 changes: 47 additions & 0 deletions litesim_impl/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions litesim_impl/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ proc_macro = true
[dependencies]
syn = "2.0"
quote = "1.0"
proc-macro2 = "1.0"
5 changes: 5 additions & 0 deletions litesim_impl/src/data.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pub struct InputConnector {
pub name: String,
pub ty: String,
pub handler: ()
}
13 changes: 9 additions & 4 deletions litesim_impl/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
extern crate proc_macro;
mod data;

#[proc_macro_derive(Model)]
pub fn model_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {

#[proc_macro_attribute]
pub fn litesim_model(
_attr: proc_macro::TokenStream,
input: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
let input = proc_macro2::token_stream::TokenStream::from(input);

input.into()
}
29 changes: 27 additions & 2 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ pub enum ValidationError {
input_model: String,
input_connector: String,
},
#[error("Connector '{connector}' takes in a wrong model type")]
InvalidConnectorModel { connector: &'static str },
#[error("Model store error: {0}")]
ModelStore(
#[from]
#[source]
ModelStoreError,
),
}

#[derive(Debug, Error)]
Expand All @@ -28,18 +36,29 @@ pub enum SchedulerError {

#[derive(Debug, Error)]
pub enum RoutingError {
#[error("Connector '{connector}' got invalid event type: {event_type}; expected: {expected}")]
#[error("Connector got invalid event type: {event_type}; expected: {expected}")]
InvalidEventType {
connector: &'static str,
event_type: &'static str,
expected: &'static str,
},
#[error("Connector handler expected model to of type {expected}; got something else")]
InvalidModelType { expected: &'static str },
#[error("Called apply_event with context without a model")]
MissingModel,
#[error("Model '{model}' doesn't have an input connector named '{connector}'")]
UnknownModelConnector { model: String, connector: String },
#[error("Event generated by {model} is missing a target")]
MissingEventTarget { model: String },
}

#[derive(Debug, Error)]
pub enum ModelStoreError {
#[error("Tried taking a model from an empty slot")]
ModelMissing,
#[error("Tried returning a model into an occupied slot")]
SlotOccupied,
}

#[derive(Debug, Error)]
pub enum SimulationError {
#[error("Unable to locate model: {id}")]
Expand All @@ -63,6 +82,12 @@ pub enum SimulationError {
#[source]
RoutingError,
),
#[error("Model store error: {0}")]
ModelStore(
#[from]
#[source]
ModelStoreError,
),
#[error(transparent)]
Other(Box<dyn std::error::Error>),
}
77 changes: 51 additions & 26 deletions src/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use std::{any::TypeId, fmt::Debug};
use crate::{
error::{RoutingError, SimulationError},
event::{Event, Message},
prelude::ErasedEvent,
simulation::SimulationCtx,
prelude::{ConnectorCtx, ErasedEvent},
simulation::ModelCtx,
util::CowStr,
};

Expand Down Expand Up @@ -114,43 +114,59 @@ impl<'s> From<(&ConnectorPath<'s>, &ConnectorPath<'s>)> for Route<'s> {
}

pub trait InputHandler<'s>:
Fn(Event<Self::In>, SimulationCtx<'s>) -> Result<(), SimulationError>
Fn(&mut Self::Model, Event<Self::In>, ModelCtx<'s>) -> Result<(), SimulationError>
{
type Model: Model<'s> + 'static;
type In: Message;
}
impl<'s, M: Message> InputHandler<'s>
for &dyn Fn(Event<M>, SimulationCtx<'s>) -> Result<(), SimulationError>
impl<'s, S: Model<'s> + 'static, M: Message> InputHandler<'s>
for &dyn Fn(&mut S, Event<M>, ModelCtx<'s>) -> Result<(), SimulationError>
{
type Model = S;
type In = M;
}

pub trait ErasedInputHandler<'h, 's: 'h>: 'h {
fn apply_event(
&self,
event: ErasedEvent,
ctx: SimulationCtx<'s>,
) -> Result<(), SimulationError>;
fn type_id(&self) -> TypeId;
fn apply_event(&self, event: ErasedEvent, ctx: ConnectorCtx<'s>)
-> Result<(), SimulationError>;
fn model_type_id(&self) -> TypeId;
fn event_type_id(&self) -> TypeId;
}

impl<'h, 's: 'h, C: InputHandler<'s> + 'h> ErasedInputHandler<'h, 's> for C {
fn apply_event(
&self,
event: ErasedEvent,
ctx: SimulationCtx<'s>,
ctx: ConnectorCtx<'s>,
) -> Result<(), SimulationError> {
let casted = event
.try_restore_type()
.map_err(|got| RoutingError::InvalidEventType {
connector: std::any::type_name::<Self>(),
event_type: got.type_name,
expected: std::any::type_name::<C::In>(),
})?;
self(casted, ctx)?;

let ConnectorCtx {
model_ctx,
mut on_model,
} = ctx;

let model = unsafe {
on_model
.cast_mut::<C::Model>()
.ok_or_else(|| RoutingError::InvalidModelType {
expected: std::any::type_name::<C::Model>(),
})?
};
self(model, casted, model_ctx)?;
Ok(())
}

fn type_id(&self) -> TypeId {
fn model_type_id(&self) -> TypeId {
TypeId::of::<C::Model>()
}

fn event_type_id(&self) -> TypeId {
TypeId::of::<C::In>()
}
}
Expand All @@ -171,13 +187,15 @@ pub trait Model<'s> {
///
/// This method allows models like generators to schedule inital changes.
#[allow(unused_variables)]
fn init(&mut self, ctx: SimulationCtx<'s>) -> Result<(), SimulationError> {
fn init(&mut self, ctx: ModelCtx<'s>) -> Result<(), SimulationError> {
Ok(())
}

/// Handler for internal model changes when the elapsed time is supposed to affect
/// the state of the model.'
fn handle_update(&mut self, ctx: SimulationCtx<'s>) -> Result<(), SimulationError>;
fn handle_update(&mut self, ctx: ModelCtx<'s>) -> Result<(), SimulationError>;

fn type_id(&self) -> TypeId;
}

pub trait ModelImpl<'s>: Model<'s> {
Expand All @@ -200,7 +218,7 @@ pub trait ModelImpl<'s>: Model<'s> {

fn input_type_id(&self, name: impl AsRef<str>) -> Option<TypeId> {
let handler = self.get_input_handler_by_name(name)?;
Some(handler.type_id())
Some(handler.event_type_id())
}

fn output_type_id(&self, name: impl AsRef<str>) -> Option<TypeId> {
Expand All @@ -216,29 +234,36 @@ impl<'s, M: Model<'s> + ?Sized> ModelImpl<'s> for M {}
#[macro_export]
macro_rules! push_event {
($ctx: ident, $id: literal, $msg: expr) => {{
let connector_ctx = $ctx.on_connector(std::borrow::Cow::Borrowed($id));
connector_ctx.push_event(::litesim::event::Event::new($msg), None)?;
$ctx.push_event_with_source(
::litesim::event::Event::new($msg),
None,
std::borrow::Cow::Borrowed($id),
)?;
Ok(())
}};
($ctx: ident, $id: literal, $msg: expr, $time: expr) => {{
let connector_ctx = ctx.on_connector(std::borrow::Cow::Borrowed($id));
connector_ctx.push_event_with_time(::litesim::event::Event::new($msg), $time, None)?;
$ctx.push_event_with_time_and_source(
::litesim::event::Event::new($msg),
$time,
None,
std::borrow::Cow::Borrowed($id),
)?;
Ok(())
}};
($ctx: ident, $id: literal, $msg: expr, Now, $target: literal) => {{
let connector_ctx = ctx.on_connector(std::borrow::Cow::Borrowed($id));
connector_ctx.push_event(
$ctx.push_event_with_source(
::litesim::event::Event::new($msg),
Some(std::borrow::Cow::Borrowed($target)),
std::borrow::Cow::Borrowed($id),
)?;
Ok(())
}};
($ctx: ident, $id: literal, $msg: expr, $time: expr, $target: literal) => {{
let connector_ctx = ctx.on_connector(std::borrow::Cow::Borrowed($id));
connector_ctx.push_event_with_time(
$ctx.push_event_with_time_and_source(
::litesim::event::Event::new($msg),
$time,
Some(std::borrow::Cow::Borrowed($target)),
std::borrow::Cow::Borrowed($id),
)?;
Ok(())
}};
Expand Down
Loading

0 comments on commit c59cc8b

Please sign in to comment.