From 4526cf6ef46a7d5ea3490fd90caa64aefd5bb58e Mon Sep 17 00:00:00 2001 From: Joseph Meadows Date: Tue, 26 Jul 2022 20:06:07 -0700 Subject: [PATCH 1/9] Restructuring new function - pt 1 --- sim/src/models/eom/eom.rs | 36 ++++++++++++++-------------- sim/src/models/eom/mod.rs | 2 +- sim/src/models/force_effector/mod.rs | 2 +- sim/src/simfrastructure/models.rs | 20 ++++++++++++++++ 4 files changed, 40 insertions(+), 20 deletions(-) diff --git a/sim/src/models/eom/eom.rs b/sim/src/models/eom/eom.rs index 3d4f873..0dbfa5f 100644 --- a/sim/src/models/eom/eom.rs +++ b/sim/src/models/eom/eom.rs @@ -4,26 +4,26 @@ use crate::simfrastructure::models::{ModelDetails, SimModelTrait, ModelFromInput use crate::simfrastructure::{PyAny, PyErr}; use crate::simfrastructure::{ModelPtr}; -pub fn new( input: &PyAny ) -> Result { - Ok( - Rc::new( - EOM { - // EOM-specific properties - x: input.getattr( "x" )?.extract()?, - y: input.getattr( "y" )?.extract()?, - z: input.getattr( "z" )?.extract()?, +// pub fn new( input: &PyAny ) -> Result { +// Ok( +// Rc::new( +// EOM { +// // EOM-specific properties +// x: input.getattr( "x" )?.extract()?, +// y: input.getattr( "y" )?.extract()?, +// z: input.getattr( "z" )?.extract()?, - force_effectors: vec![], +// force_effectors: vec![], - // General Model Properties - model_details: ModelDetails { - name: input.getattr( "name" )?.extract()?, - order: input.getattr( "order" )?.extract()?, - } - } - ) - ) -} +// // General Model Properties +// model_details: ModelDetails { +// name: input.getattr( "name" )?.extract()?, +// order: input.getattr( "order" )?.extract()?, +// } +// } +// ) +// ) +// } #[derive(std::fmt::Debug)] pub struct EOM { diff --git a/sim/src/models/eom/mod.rs b/sim/src/models/eom/mod.rs index 3c109e6..166dc08 100644 --- a/sim/src/models/eom/mod.rs +++ b/sim/src/models/eom/mod.rs @@ -1,2 +1,2 @@ pub mod eom; -pub use eom::{new, EOM}; \ No newline at end of file +pub use eom::{EOM}; \ No newline at end of file diff --git a/sim/src/models/force_effector/mod.rs b/sim/src/models/force_effector/mod.rs index e72cc67..96bf61b 100644 --- a/sim/src/models/force_effector/mod.rs +++ b/sim/src/models/force_effector/mod.rs @@ -1,2 +1,2 @@ pub mod force_effector; -pub use force_effector::{new, ForceEffector}; \ No newline at end of file +pub use force_effector::{ForceEffector}; \ No newline at end of file diff --git a/sim/src/simfrastructure/models.rs b/sim/src/simfrastructure/models.rs index 83be596..4ca807a 100644 --- a/sim/src/simfrastructure/models.rs +++ b/sim/src/simfrastructure/models.rs @@ -19,6 +19,26 @@ pub trait ModelFromInput { fn new( input: &PyAny ) -> Result; } +// Empty reference model +#[derive(Debug)] +pub struct Reference { + pub details: ModelDetails, +} + +impl SimModelTrait for Reference { + fn finalize( &mut self ) -> bool { + false + } + + fn update( &mut self ) -> bool { + false + } + + fn get_model( &mut self ) -> &ModelDetails { + &self.details + } +} + #[cfg(test)] mod tests { #[test] From 8b14e9ec9b97e1feae15ff0604a94c0e9bb4bcad Mon Sep 17 00:00:00 2001 From: Joseph Meadows Date: Tue, 26 Jul 2022 21:03:33 -0700 Subject: [PATCH 2/9] Switched to absolute paths --- source.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/source.sh b/source.sh index cec1f30..fd7cfd3 100755 --- a/source.sh +++ b/source.sh @@ -1,17 +1,17 @@ # Find important directories -sim_directory="$(dirname $BASH_SOURCE)/sim" -parent_dir="$(dirname $BASH_SOURCE)" +file="$(realpath $BASH_SOURCE)" +sim_directory="$(dirname $file)/sim" +parent_dir="$(dirname $file)" # Create venv -if [ ! -d "$(pwd)/$parent_dir/sim-env" ]; then +if [ ! -d "$parent_dir/sim-env" ]; then echo "Generating Python venv..." - python -m venv $(pwd)/$parent_dir/sim-env && source sim-env/bin/activate && pip install -r $(pwd)/$parent_dir/pyRequirements.txt - echo "Done!" + python -m venv $parent_dir/sim-env && source $parent_dir/sim-env/bin/activate && pip install -r $parent_dir/pyRequirements.txt && echo "Done!" fi # Add to venv PYTHONPATH -echo "$(pwd)/$sim_directory/tools" > "$parent_dir/sim-env/lib/python3.8/site-packages/sim_tools.pth" -echo "$(pwd)/$sim_directory/src" > "$parent_dir/sim-env/lib/python3.8/site-packages/sim_src.pth" +echo "$sim_directory/tools" > "$parent_dir/sim-env/lib/python3.8/site-packages/sim_tools.pth" +echo "$sim_directory/src" > "$parent_dir/sim-env/lib/python3.8/site-packages/sim_src.pth" # echo "$(pwd)/$sim_directory/src/simfrastructure" > "$parent_dir/sim-env/lib/python3.8/site-packages/sim_infrastructure.pth" # Source python venv From 1ecd1e46effe95a4189503d2a97ad5d68791eb7b Mon Sep 17 00:00:00 2001 From: Joseph Meadows Date: Tue, 26 Jul 2022 21:04:24 -0700 Subject: [PATCH 3/9] Updated to increase Rust-Python cohesion --- sim/scenarios/basic/runconfig.py | 9 ++- sim/src/models/__init__.py | 10 --- sim/src/models/eom/eom.rs | 22 ------ .../models/force_effector/ForceEffector.py | 8 +- .../models/force_effector/force_effector.rs | 74 +++++++++++-------- sim/src/simfrastructure/__init__.py | 2 +- sim/src/simfrastructure/models.rs | 3 +- sim/src/simfrastructure/python/runconfig.py | 20 ++++- 8 files changed, 74 insertions(+), 74 deletions(-) diff --git a/sim/scenarios/basic/runconfig.py b/sim/scenarios/basic/runconfig.py index c2c13af..d8a5c09 100644 --- a/sim/scenarios/basic/runconfig.py +++ b/sim/scenarios/basic/runconfig.py @@ -2,12 +2,15 @@ from simfrastructure import RunConfig # Import input models -from input import eom, force_effector +from models.force_effector import ForceEffector +from simfrastructure.python.runconfig import ModelDetails +# from input import eom, force_effector config = RunConfig( model_list=[ - eom.model, - force_effector.model, + # eom.model, + # force_effector.model, + ForceEffector( 0, 10, 20, ModelDetails( 100, 100 ) ) ], description="A basic system to simulate!" ) \ No newline at end of file diff --git a/sim/src/models/__init__.py b/sim/src/models/__init__.py index bb02bef..e69de29 100644 --- a/sim/src/models/__init__.py +++ b/sim/src/models/__init__.py @@ -1,10 +0,0 @@ -# Load all files in the current directory into this namespace -# from pathlib import Path - -# model_directory = Path( __file__ ).parent -# model_files = model_directory.glob("**/*.py") - -# # Remove unwanted files -# model_files = [file for file in model_files] - -# print(list(model_files)) \ No newline at end of file diff --git a/sim/src/models/eom/eom.rs b/sim/src/models/eom/eom.rs index 0dbfa5f..4cc3901 100644 --- a/sim/src/models/eom/eom.rs +++ b/sim/src/models/eom/eom.rs @@ -4,27 +4,6 @@ use crate::simfrastructure::models::{ModelDetails, SimModelTrait, ModelFromInput use crate::simfrastructure::{PyAny, PyErr}; use crate::simfrastructure::{ModelPtr}; -// pub fn new( input: &PyAny ) -> Result { -// Ok( -// Rc::new( -// EOM { -// // EOM-specific properties -// x: input.getattr( "x" )?.extract()?, -// y: input.getattr( "y" )?.extract()?, -// z: input.getattr( "z" )?.extract()?, - -// force_effectors: vec![], - -// // General Model Properties -// model_details: ModelDetails { -// name: input.getattr( "name" )?.extract()?, -// order: input.getattr( "order" )?.extract()?, -// } -// } -// ) -// ) -// } - #[derive(std::fmt::Debug)] pub struct EOM { pub x: i128, @@ -50,7 +29,6 @@ impl ModelFromInput for EOM { // General Model Properties model_details: ModelDetails { - name: input.getattr( "name" )?.extract()?, order: input.getattr( "order" )?.extract()?, } } diff --git a/sim/src/models/force_effector/ForceEffector.py b/sim/src/models/force_effector/ForceEffector.py index 3d03f9b..4cbe4ca 100644 --- a/sim/src/models/force_effector/ForceEffector.py +++ b/sim/src/models/force_effector/ForceEffector.py @@ -2,12 +2,14 @@ a ForceEffector object in the simulation configuration.""" from dataclasses import dataclass -from simfrastructure.python.runconfig import RunModel +from simfrastructure import Model, ModelDetails @dataclass -class ForceEffector( RunModel ): +class ForceEffector( Model ): """Generates an input class for the ForceEffector model""" fx: int fy: int - fz: int \ No newline at end of file + fz: int + + model_details: ModelDetails \ No newline at end of file diff --git a/sim/src/models/force_effector/force_effector.rs b/sim/src/models/force_effector/force_effector.rs index a39ef91..3169e64 100644 --- a/sim/src/models/force_effector/force_effector.rs +++ b/sim/src/models/force_effector/force_effector.rs @@ -1,10 +1,12 @@ use std::rc::Rc; +use pyo3::FromPyObject; use crate::simfrastructure::models::{ModelDetails, SimModelTrait, ModelFromInput}; use crate::simfrastructure::{PyAny, PyErr}; use crate::simfrastructure::{ModelPtr}; #[derive(std::fmt::Debug)] +#[derive(FromPyObject)] pub struct ForceEffector { pub fx: i128, pub fy: i128, @@ -14,22 +16,33 @@ pub struct ForceEffector { } pub fn new( input: &PyAny ) -> Result { - Ok( - Rc::new( - ForceEffector { - // EOM-specific properties - fx: input.getattr( "fx" )?.extract()?, - fy: input.getattr( "fy" )?.extract()?, - fz: input.getattr( "fz" )?.extract()?, + // Ok( + // Rc::new( + // ForceEffector { + // // EOM-specific properties + // fx: input.getattr( "fx" )?.extract()?, + // fy: input.getattr( "fy" )?.extract()?, + // fz: input.getattr( "fz" )?.extract()?, - // General Model Properties - model_details: ModelDetails { - name: input.getattr( "name" )?.extract()?, - order: input.getattr( "order" )?.extract()?, - } - } - ) - ) + // // General Model Properties + // model_details: ModelDetails { + // // name: input.getattr( "name" )?.extract()?, + // order: input.getattr( "order" )?.extract()?, + // } + // } + // ) + // ) + // let model: ForceEffector = input.extract()?; + // let modelPtr = Rc::new( model ); + + // let model: ForceEffector = ; + + Ok( Rc::::new( input.extract()? ) ) + + // let model: Rc = Rc::from( + // input.extract()? + // ); + // Ok( modelPtr ) } impl SimModelTrait for ForceEffector { @@ -48,22 +61,23 @@ impl SimModelTrait for ForceEffector { impl ModelFromInput for ForceEffector { fn new( input: &PyAny ) -> Result { - Ok( - Rc::new( - ForceEffector { - // EOM-specific properties - fx: input.getattr( "fx" )?.extract()?, - fy: input.getattr( "fy" )?.extract()?, - fz: input.getattr( "fz" )?.extract()?, + // Ok( + // Rc::new( + // ForceEffector { + // // EOM-specific properties + // fx: input.getattr( "fx" )?.extract()?, + // fy: input.getattr( "fy" )?.extract()?, + // fz: input.getattr( "fz" )?.extract()?, - // General Model Properties - model_details: ModelDetails { - name: input.getattr( "name" )?.extract()?, - order: input.getattr( "order" )?.extract()?, - } - } - ) - ) + // // General Model Properties + // model_details: ModelDetails { + // // name: input.getattr( "name" )?.extract()?, + // order: input.getattr( "order" )?.extract()?, + // } + // } + // ) + // ) + Ok( Rc::::new( input.extract()? ) ) } } diff --git a/sim/src/simfrastructure/__init__.py b/sim/src/simfrastructure/__init__.py index 52aeb2c..a517a9b 100644 --- a/sim/src/simfrastructure/__init__.py +++ b/sim/src/simfrastructure/__init__.py @@ -1,2 +1,2 @@ -from .python.runconfig import RunConfig +from .python.runconfig import RunConfig, Model, ModelDetails from .python.utilities import unit_conversion \ No newline at end of file diff --git a/sim/src/simfrastructure/models.rs b/sim/src/simfrastructure/models.rs index 4ca807a..5a242c9 100644 --- a/sim/src/simfrastructure/models.rs +++ b/sim/src/simfrastructure/models.rs @@ -1,11 +1,12 @@ use std::fmt::Debug; +use pyo3::prelude::FromPyObject; use crate::simfrastructure::{ModelPtr, PyAny, PyErr}; #[derive(std::fmt::Debug)] +#[derive(FromPyObject)] pub struct ModelDetails { pub order: i8, - pub name: String, } pub trait SimModelTrait: Debug { diff --git a/sim/src/simfrastructure/python/runconfig.py b/sim/src/simfrastructure/python/runconfig.py index d8c601b..d26a00f 100644 --- a/sim/src/simfrastructure/python/runconfig.py +++ b/sim/src/simfrastructure/python/runconfig.py @@ -1,18 +1,30 @@ """This module specifies the interface for a run configuration to take""" -from abc import ABC +from abc import ABC, abstractmethod from dataclasses import dataclass from typing import List @dataclass -class RunModel: - name: str +class ModelDetails: + """ + Contains metadata that the simulation uses to process the model. + Every model must contain this. + """ + order: int + rate: int + +class Model( ABC ): + "The 'Adam' of all models" + + # @abstractmethod + # def getModelDetails( ) -> ModelDetails: + # "Returns a set of model-details to the sim" @dataclass class RunConfig( ABC ): """The runconfig is what initializes the Rust simulation. It defines the models to use, and how to set them up!""" - model_list: List[ RunModel ] + model_list: List[ Model ] description: str \ No newline at end of file From 616a29dd536787d558adb4252642e138194d73c4 Mon Sep 17 00:00:00 2001 From: Joseph Meadows Date: Wed, 27 Jul 2022 22:16:46 -0700 Subject: [PATCH 4/9] Updated tools to enable passing arguments into python infrastructure --- .gitignore | 3 ++- sim/tools/sim | 15 +++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index e3bb340..3d418e3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ **/output Cargo.lock -sim-env \ No newline at end of file +sim-env +target \ No newline at end of file diff --git a/sim/tools/sim b/sim/tools/sim index 7ca420f..20f3b4e 100755 --- a/sim/tools/sim +++ b/sim/tools/sim @@ -40,18 +40,25 @@ def parse_arguments( args=sys.argv[ 1: ] ) -> ArgumentParser: subparsers.add_parser( "build", help="Builds or rebuilds the simulation" ) subparsers.add_parser( "test", help="Runs python unit tests" ) - return parser.parse_args( args ) + return parser.parse_known_args( args ) + # return parser.parse_args( args ) def main(): # Change into the main directory os.chdir( Path( __file__ ).parent.parent.parent ) # Parse the arguments - args = parse_arguments() - + args, unknown = parse_arguments() + # Perform the action if args.subcommand == "run": - os.system( f"{Path( './output/debug/sim' )} {Path( __file__ ).parent.parent / 'scenarios' / args.scenario}" ) + simargs = "" + for un in unknown: + if ' ' in un: + simargs = simargs + ' "' + un + '"' + else: + simargs = simargs + " " + un + os.system( f"{Path( './output/debug/sim' )} {Path( __file__ ).parent.parent / 'scenarios' / args.scenario}" + simargs ) elif args.subcommand == "build": os.system( "cargo build" ) elif args.subcommand == "test": From e63c14703bbb8303d2e6642da3fe9fb4aecb9be5 Mon Sep 17 00:00:00 2001 From: Joseph Meadows Date: Wed, 27 Jul 2022 22:17:04 -0700 Subject: [PATCH 5/9] First reference created! Now to polish... --- sim/scenarios/basic/runconfig.py | 32 ++++++++-- sim/src/main.rs | 8 +-- sim/src/models/eom/eom.py | 11 +++- sim/src/models/eom/eom.rs | 38 +++++------- .../models/force_effector/ForceEffector.py | 4 +- .../models/force_effector/force_effector.rs | 18 ++++-- sim/src/models/force_effector/mod.rs | 2 +- sim/src/simfrastructure/__init__.py | 2 +- sim/src/simfrastructure/mod.rs | 3 + sim/src/simfrastructure/models.rs | 59 ++++++++++++------ sim/src/simfrastructure/python/runconfig.py | 61 +++++++++++++++++-- sim/src/simfrastructure/runtime.rs | 34 ++++++++--- sim/src/simfrastructure/simtypes.rs | 24 +++++++- 13 files changed, 217 insertions(+), 79 deletions(-) diff --git a/sim/scenarios/basic/runconfig.py b/sim/scenarios/basic/runconfig.py index d8a5c09..244e54c 100644 --- a/sim/scenarios/basic/runconfig.py +++ b/sim/scenarios/basic/runconfig.py @@ -1,16 +1,36 @@ # Import simulation infrastructure components -from simfrastructure import RunConfig +from simfrastructure import RunConfig, sim_args + +# print("Sim args:", sim_args) # Import input models from models.force_effector import ForceEffector -from simfrastructure.python.runconfig import ModelDetails +from models.eom import EOM +from simfrastructure.python.runconfig import ModelBase, ReferenceList # from input import eom, force_effector +# print( ForceEffector( 0, 10, 20, ModelDetails( 100, 100 ) ).model_details.id ) +# print( ForceEffector( 10, 10, 10, ModelDetails( 0, 0 ) ).model_details.id ) + +force = ForceEffector( 0, 10, 20, ModelBase( 100, 100 ) ) +eom = EOM( + x=0, + y=0, + z=0, + force_effectors=ReferenceList( force ), + base=ModelBase( 100, 100, ReferenceList( force ) ) +) + +# print(force) +print(eom.base) +# print(eom.force_effectors.reference_ids) + config = RunConfig( model_list=[ - # eom.model, - # force_effector.model, - ForceEffector( 0, 10, 20, ModelDetails( 100, 100 ) ) + force, + eom ], description="A basic system to simulate!" -) \ No newline at end of file +) + +# print( config.model_list[0].model_details.id ) \ No newline at end of file diff --git a/sim/src/main.rs b/sim/src/main.rs index 561a816..c7c58db 100644 --- a/sim/src/main.rs +++ b/sim/src/main.rs @@ -6,16 +6,16 @@ use sim::models::register_models; fn main() { let args: Vec<_> = env::args().collect(); println!( "Starting simulation..." ); - for arg in args.iter() { - println!( "Argument: {}", arg ); - } + // for arg in args.iter() { + // println!( "Argument: {}", arg ); + // } // First, build registry of available model types let mut model_registry: ModelCreatorMap = HashMap::new(); register_models( &mut model_registry ); // Now generate the runtime with the given list of models - let runtime = Runtime::from_config( &args[ 1 ], &model_registry ); + let runtime = Runtime::from_config( &args[ 1 ], &model_registry, &args ); for model in runtime.model_list.iter() { println!("Model {:?}", model); diff --git a/sim/src/models/eom/eom.py b/sim/src/models/eom/eom.py index e61fde4..a5c14ab 100644 --- a/sim/src/models/eom/eom.py +++ b/sim/src/models/eom/eom.py @@ -2,11 +2,16 @@ an EOM object in the simulation configuration.""" from dataclasses import dataclass -from simfrastructure.python.runconfig import RunModel +from simfrastructure.python.runconfig import Model, ModelBase, ReferenceList @dataclass -class EOM( RunModel ): +class EOM( Model ): """Generates an input class for the EOM model""" + x: int y: int - z: int \ No newline at end of file + z: int + + force_effectors: ReferenceList + + base: ModelBase \ No newline at end of file diff --git a/sim/src/models/eom/eom.rs b/sim/src/models/eom/eom.rs index 4cc3901..3fdc846 100644 --- a/sim/src/models/eom/eom.rs +++ b/sim/src/models/eom/eom.rs @@ -1,43 +1,37 @@ +use std::cell::RefCell; use std::rc::Rc; +use pyo3::FromPyObject; -use crate::simfrastructure::models::{ModelDetails, SimModelTrait, ModelFromInput}; +use crate::models::force_effector::ForceEffector; +use crate::simfrastructure::models::*; use crate::simfrastructure::{PyAny, PyErr}; use crate::simfrastructure::{ModelPtr}; #[derive(std::fmt::Debug)] +#[derive(FromPyObject)] pub struct EOM { pub x: i128, pub y: i128, pub z: i128, - pub force_effectors: Vec, + pub force_effectors: ReferenceList, + // pub test: ReferenceList, - pub model_details: ModelDetails, + pub base: ModelBase, } impl ModelFromInput for EOM { fn new( input: &PyAny ) -> Result { - Ok( - Rc::new( - EOM { - // EOM-specific properties - x: input.getattr( "x" )?.extract()?, - y: input.getattr( "y" )?.extract()?, - z: input.getattr( "z" )?.extract()?, - - force_effectors: vec![], - - // General Model Properties - model_details: ModelDetails { - order: input.getattr( "order" )?.extract()?, - } - } - ) - ) + Ok( Rc::>::new( RefCell::new( input.extract()? ) ) ) } } impl SimModelTrait for EOM { + fn initialize( &mut self ) -> bool { + println!( "{:?}", self.force_effectors ); + true + } + fn update( &mut self ) -> bool { true } @@ -46,8 +40,8 @@ impl SimModelTrait for EOM { true } - fn get_model( &mut self ) -> &ModelDetails { - &self.model_details + fn get_details( &mut self ) -> &mut ModelBase { + &mut self.base } } diff --git a/sim/src/models/force_effector/ForceEffector.py b/sim/src/models/force_effector/ForceEffector.py index 4cbe4ca..4929604 100644 --- a/sim/src/models/force_effector/ForceEffector.py +++ b/sim/src/models/force_effector/ForceEffector.py @@ -2,7 +2,7 @@ a ForceEffector object in the simulation configuration.""" from dataclasses import dataclass -from simfrastructure import Model, ModelDetails +from simfrastructure import Model, ModelBase @dataclass class ForceEffector( Model ): @@ -12,4 +12,4 @@ class ForceEffector( Model ): fy: int fz: int - model_details: ModelDetails \ No newline at end of file + model_details: ModelBase \ No newline at end of file diff --git a/sim/src/models/force_effector/force_effector.rs b/sim/src/models/force_effector/force_effector.rs index 3169e64..89f4074 100644 --- a/sim/src/models/force_effector/force_effector.rs +++ b/sim/src/models/force_effector/force_effector.rs @@ -1,7 +1,8 @@ +use std::cell::RefCell; use std::rc::Rc; use pyo3::FromPyObject; -use crate::simfrastructure::models::{ModelDetails, SimModelTrait, ModelFromInput}; +use crate::simfrastructure::models::{ModelBase, SimModelTrait, ModelFromInput}; use crate::simfrastructure::{PyAny, PyErr}; use crate::simfrastructure::{ModelPtr}; @@ -12,7 +13,7 @@ pub struct ForceEffector { pub fy: i128, pub fz: i128, - pub model_details: ModelDetails, + pub model_details: ModelBase, } pub fn new( input: &PyAny ) -> Result { @@ -37,7 +38,8 @@ pub fn new( input: &PyAny ) -> Result { // let model: ForceEffector = ; - Ok( Rc::::new( input.extract()? ) ) + // Ok( Rc::::new( input.extract()? ) ) + Ok( Rc::>::new( RefCell::new( input.extract()? ) ) ) // let model: Rc = Rc::from( // input.extract()? @@ -46,6 +48,10 @@ pub fn new( input: &PyAny ) -> Result { } impl SimModelTrait for ForceEffector { + fn initialize( &mut self ) -> bool { + true + } + fn update( &mut self ) -> bool { true } @@ -54,8 +60,8 @@ impl SimModelTrait for ForceEffector { true } - fn get_model( &mut self ) -> &ModelDetails { - &self.model_details + fn get_details( &mut self ) -> &mut ModelBase { + &mut self.model_details } } @@ -77,7 +83,7 @@ impl ModelFromInput for ForceEffector { // } // ) // ) - Ok( Rc::::new( input.extract()? ) ) + Ok( Rc::>::new( RefCell::new( input.extract()? ) ) ) } } diff --git a/sim/src/models/force_effector/mod.rs b/sim/src/models/force_effector/mod.rs index 96bf61b..f900531 100644 --- a/sim/src/models/force_effector/mod.rs +++ b/sim/src/models/force_effector/mod.rs @@ -1,2 +1,2 @@ pub mod force_effector; -pub use force_effector::{ForceEffector}; \ No newline at end of file +pub use force_effector::ForceEffector; \ No newline at end of file diff --git a/sim/src/simfrastructure/__init__.py b/sim/src/simfrastructure/__init__.py index a517a9b..7a9f208 100644 --- a/sim/src/simfrastructure/__init__.py +++ b/sim/src/simfrastructure/__init__.py @@ -1,2 +1,2 @@ -from .python.runconfig import RunConfig, Model, ModelDetails +from .python.runconfig import RunConfig, Model, ModelBase, sim_args from .python.utilities import unit_conversion \ No newline at end of file diff --git a/sim/src/simfrastructure/mod.rs b/sim/src/simfrastructure/mod.rs index 4b96a6c..e32b6d5 100644 --- a/sim/src/simfrastructure/mod.rs +++ b/sim/src/simfrastructure/mod.rs @@ -1,3 +1,6 @@ +/// # Simfrastructure +/// Simfrastructure is the infrastructure of the simulation + pub mod models; // The simulation infrastructure that powers everything diff --git a/sim/src/simfrastructure/models.rs b/sim/src/simfrastructure/models.rs index 5a242c9..361c79b 100644 --- a/sim/src/simfrastructure/models.rs +++ b/sim/src/simfrastructure/models.rs @@ -1,44 +1,67 @@ -use std::fmt::Debug; -use pyo3::prelude::FromPyObject; +use std::{fmt::Debug, collections::HashMap, rc::Rc}; +use pyo3::prelude::*; -use crate::simfrastructure::{ModelPtr, PyAny, PyErr}; +use super::*; #[derive(std::fmt::Debug)] #[derive(FromPyObject)] -pub struct ModelDetails { +pub struct ModelBase { pub order: i8, + pub id: ModelID, + pub local_refs: ReferenceList, } pub trait SimModelTrait: Debug { + fn resolve_references( &mut self, global_model_list: &HashMap ) { + let locals = &mut self.get_details().local_refs; + for id in &locals.reference_ids { + locals.reference_list.push( + Rc::downgrade( global_model_list.get( id ).unwrap() ) + ) + } + } + + // Runtime methods + fn initialize( &mut self ) -> bool; fn update( &mut self ) -> bool; fn finalize( &mut self ) -> bool; - fn get_model( &mut self ) -> &ModelDetails; + // Necessary return functions + fn get_details( &mut self ) -> &mut ModelBase; } pub trait ModelFromInput { fn new( input: &PyAny ) -> Result; } -// Empty reference model #[derive(Debug)] -pub struct Reference { - pub details: ModelDetails, +pub struct ReferenceList { + pub reference_ids: Vec, + pub reference_list: Vec>, } -impl SimModelTrait for Reference { - fn finalize( &mut self ) -> bool { - false +impl FromPyObject<'_> for ReferenceList { + fn extract(ob: &PyAny) -> PyResult< Self > { + Ok( + Self { + reference_ids: ob.getattr( "reference_ids" )?.extract()?, + reference_list: vec![], + } + ) } +} - fn update( &mut self ) -> bool { - false - } +// #[derive(Debug, FromPyObject)] +// pub enum ModelRef { +// Preref( i16 ), +// // Reference( ModelPtr ), +// } - fn get_model( &mut self ) -> &ModelDetails { - &self.details - } -} +// impl ModelFromInput for Reference { +// fn new( input: &PyAny ) -> Result { +// Ok( Rc::::new( input.extract()? ) ) +// } +// } #[cfg(test)] mod tests { diff --git a/sim/src/simfrastructure/python/runconfig.py b/sim/src/simfrastructure/python/runconfig.py index d26a00f..fc67540 100644 --- a/sim/src/simfrastructure/python/runconfig.py +++ b/sim/src/simfrastructure/python/runconfig.py @@ -1,11 +1,48 @@ """This module specifies the interface for a run configuration to take""" -from abc import ABC, abstractmethod +from abc import ABC from dataclasses import dataclass from typing import List +import sys + +sim_args = sys.argv[1:] + +class IndexService: + """ + The IndexService is how we assign a unique ID to every entity + in the simulation. This service makes it all automatic in the + background. + """ + + current_id = 0 + + @classmethod + def take_id( cls ) -> int: + id = cls.current_id + cls.current_id += 1 + return id + +@dataclass +class ReferenceList: + """ + An object that helps connect references in the simulation + """ + + references: List[ "Model" ] = None + + def __post_init__( self ): + if self.references == None: + self.references = [] + self.reference_ids = [] + + if type( self.references ) == list: + for model in self.references: + self.reference_ids.append( model.base.id ) + else: + self.reference_ids.append( self.references.model_details.id ) @dataclass -class ModelDetails: +class ModelBase: """ Contains metadata that the simulation uses to process the model. Every model must contain this. @@ -13,13 +50,23 @@ class ModelDetails: order: int rate: int + local_refs: "ReferenceList" = ReferenceList() + + def __post_init__( self ): + self.id = IndexService.take_id() class Model( ABC ): "The 'Adam' of all models" - # @abstractmethod - # def getModelDetails( ) -> ModelDetails: - # "Returns a set of model-details to the sim" + base = ModelBase( 0, 0 ) + +# class Reference( Model ): +# "A dumb reference that will later be replaced" + +# def __init__( self, model: Model ): +# "Creates a reference to an already-existing model." + +# self.target_id = model.model_details.id @dataclass class RunConfig( ABC ): @@ -27,4 +74,6 @@ class RunConfig( ABC ): It defines the models to use, and how to set them up!""" model_list: List[ Model ] - description: str \ No newline at end of file + description: str + +# def ModelGroup( *args ) -> \ No newline at end of file diff --git a/sim/src/simfrastructure/runtime.rs b/sim/src/simfrastructure/runtime.rs index 2490547..c3ea607 100644 --- a/sim/src/simfrastructure/runtime.rs +++ b/sim/src/simfrastructure/runtime.rs @@ -1,23 +1,25 @@ +use std::collections::HashMap; + // External imports use pyo3::{prelude::*, types::PyList}; // Local crate imports -use crate::simfrastructure::{ModelCreatorMap, ModelPtr}; +use crate::simfrastructure::{ModelCreatorMap, ModelPtr, ModelID}; pub struct Runtime { pub run: i8, - pub model_list: Vec + pub model_list: HashMap } impl Runtime { - pub fn from_config( path: &String, model_registry: &ModelCreatorMap ) -> Self { + pub fn from_config( path: &String, model_registry: &ModelCreatorMap, args: &Vec ) -> Self { let config_path = path.clone()+"/runconfig.py"; println!( "Generating runtime from {}!", config_path ); // Create runtime let mut runtime = Runtime { run: 0, - model_list: vec![], + model_list: HashMap::new(), }; // Start python interpreter @@ -25,6 +27,13 @@ impl Runtime { // Add current scenario folder to path let syspath: &PyList = py.import( "sys" )?.getattr( "path" )?.extract()?; + let sysargs: &PyList = py.import( "sys" )?.getattr( "argv" )?.extract()?; + + + args[2..args.len()].iter().for_each(|arg| { + sysargs.append( arg ).unwrap(); + }); + syspath.insert(0, path)?; // Run the configuration module @@ -39,18 +48,29 @@ impl Runtime { println!("Simulation Description: {}", description); for py_model in model_list { + // Get model identifier let model_type: String = py_model.getattr("__class__")?.getattr("__name__")?.extract()?; println!( "Attempting to generate {}", &model_type ); - // Generate model - runtime.model_list.push( model_registry.get( &model_type ).unwrap()( py_model )? ); + // Generate model and add it to the runtime list by ID + let new_model: ModelPtr = model_registry.get( &model_type ).unwrap()( py_model )?; + let id = new_model.borrow_mut().get_details().id; + runtime.model_list.insert( + id, + new_model + ); } Ok(()) }).unwrap(); - runtime + // Connect references + for ( id, model ) in &runtime.model_list { + model.borrow_mut().resolve_references( &runtime.model_list ) + } + + return runtime } } \ No newline at end of file diff --git a/sim/src/simfrastructure/simtypes.rs b/sim/src/simfrastructure/simtypes.rs index 91dd258..477f901 100644 --- a/sim/src/simfrastructure/simtypes.rs +++ b/sim/src/simfrastructure/simtypes.rs @@ -1,12 +1,30 @@ +use std::cell::RefCell; // External imports -use std::collections::HashMap; +use std::{collections::HashMap, rc::Weak}; use std::rc::Rc; // // Local crate imports use crate::simfrastructure::models::SimModelTrait; use pyo3::{ PyAny, PyErr }; -// Type shorthand to make code cleaner -pub type ModelPtr = Rc; +// Type shorthand to make code cleaner and more maintainable + +/// An ID for a model, simply a 16 bit int, but if this ever +/// needs to change it can be done in one location! +pub type ModelID = i16; + +/// A reference-counted pointer for anything implementing SimModelTrait +pub type ModelPtr = Rc>; + +/// A weak reference to something implementing SimModelTrait. Models should +/// refer to other models with a weak model pointer! +pub type WeakModelPtr = Weak>; + +/// A function that is used to create a Rust model, given an equivalent Python +/// input model. pub type ModelCreatorFn = fn( &PyAny )->Result; + +/// A map of model creation functions that sorts them by the type name +/// string. This can easily be called on a Python object to get its type +/// and then delegate it to the proper Rust model. pub type ModelCreatorMap = HashMap; \ No newline at end of file From 38cb3f9a0401727be9cbed0fcbe0f07a5d86f741 Mon Sep 17 00:00:00 2001 From: Joseph Meadows Date: Thu, 28 Jul 2022 22:49:12 -0700 Subject: [PATCH 6/9] Updated test calling method --- sim/tools/sim | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sim/tools/sim b/sim/tools/sim index 20f3b4e..04bd116 100755 --- a/sim/tools/sim +++ b/sim/tools/sim @@ -45,6 +45,7 @@ def parse_arguments( args=sys.argv[ 1: ] ) -> ArgumentParser: def main(): # Change into the main directory + starting_dir = Path.cwd() os.chdir( Path( __file__ ).parent.parent.parent ) # Parse the arguments @@ -62,8 +63,10 @@ def main(): elif args.subcommand == "build": os.system( "cargo build" ) elif args.subcommand == "test": + # Change back to current dir + os.chdir( starting_dir ) print( colored( "Starting Tests:", "white", attrs=['bold'] ) ) - os.system( "python3 -m unittest discover -p '*.py'" ) + os.system( "python -m unittest discover -p '*.py'" ) if __name__ == "__main__": main() \ No newline at end of file From f37e469d384d5e9a5f704dd35c526b40395771de Mon Sep 17 00:00:00 2001 From: Joseph Meadows Date: Thu, 28 Jul 2022 22:49:33 -0700 Subject: [PATCH 7/9] Reference architecture in a working state --- sim/scenarios/basic/runconfig.py | 33 +++---- sim/src/models/eom/eom.rs | 23 ++++- .../models/force_effector/ForceEffector.py | 2 +- .../models/force_effector/force_effector.rs | 9 +- sim/src/simfrastructure/models.rs | 8 +- sim/src/simfrastructure/python/runconfig.py | 92 ++++++++++++++++--- sim/src/simfrastructure/runtime.rs | 7 +- 7 files changed, 127 insertions(+), 47 deletions(-) diff --git a/sim/scenarios/basic/runconfig.py b/sim/scenarios/basic/runconfig.py index 244e54c..a5d4c3d 100644 --- a/sim/scenarios/basic/runconfig.py +++ b/sim/scenarios/basic/runconfig.py @@ -1,36 +1,27 @@ # Import simulation infrastructure components from simfrastructure import RunConfig, sim_args -# print("Sim args:", sim_args) - # Import input models from models.force_effector import ForceEffector from models.eom import EOM -from simfrastructure.python.runconfig import ModelBase, ReferenceList -# from input import eom, force_effector - -# print( ForceEffector( 0, 10, 20, ModelDetails( 100, 100 ) ).model_details.id ) -# print( ForceEffector( 10, 10, 10, ModelDetails( 0, 0 ) ).model_details.id ) +from simfrastructure.python.runconfig import * -force = ForceEffector( 0, 10, 20, ModelBase( 100, 100 ) ) +# Create models here +base = ModelBase( 100, 200 ) +force = ForceEffector( 0, 10, 20, ModelBase( 100, 200 ) ) eom = EOM( x=0, y=0, z=0, - force_effectors=ReferenceList( force ), - base=ModelBase( 100, 100, ReferenceList( force ) ) + force_effectors=ReferenceList( ), + base=base ) -# print(force) -print(eom.base) -# print(eom.force_effectors.reference_ids) - +# Create the actual configuration config = RunConfig( - model_list=[ - force, - eom - ], - description="A basic system to simulate!" -) + # Description + "A basic system to simulate!", -# print( config.model_list[0].model_details.id ) \ No newline at end of file + # Define models: + ModelGroup( eom, force ) +) \ No newline at end of file diff --git a/sim/src/models/eom/eom.rs b/sim/src/models/eom/eom.rs index 3fdc846..4b3b1b9 100644 --- a/sim/src/models/eom/eom.rs +++ b/sim/src/models/eom/eom.rs @@ -1,11 +1,14 @@ -use std::cell::RefCell; +use std::any::Any; +use std::cell::{RefCell}; +use std::ops::Deref; use std::rc::Rc; use pyo3::FromPyObject; use crate::models::force_effector::ForceEffector; -use crate::simfrastructure::models::*; +use crate::simfrastructure::{models::*}; use crate::simfrastructure::{PyAny, PyErr}; use crate::simfrastructure::{ModelPtr}; +use crate::simfrastructure::models::SimModelTrait; #[derive(std::fmt::Debug)] #[derive(FromPyObject)] @@ -15,7 +18,6 @@ pub struct EOM { pub z: i128, pub force_effectors: ReferenceList, - // pub test: ReferenceList, pub base: ModelBase, } @@ -28,7 +30,16 @@ impl ModelFromInput for EOM { impl SimModelTrait for EOM { fn initialize( &mut self ) -> bool { - println!( "{:?}", self.force_effectors ); + println!( "EOM Model is referencing:" ); + for reference in &self.base.local_refs.reference_list { + let upgraded = reference.upgrade().unwrap(); + let mut contents = upgraded.deref().borrow_mut(); + if let Some( force_model ) = contents.as_any().downcast_mut::() { + println!( " - Force ID: {}", force_model.get_details().id ); + force_model.get_details().id = 5; + println!( " - Force ID (new): {}", force_model.get_details().id ); + } + } true } @@ -43,6 +54,10 @@ impl SimModelTrait for EOM { fn get_details( &mut self ) -> &mut ModelBase { &mut self.base } + + fn as_any(&mut self) -> &mut dyn Any { + self + } } diff --git a/sim/src/models/force_effector/ForceEffector.py b/sim/src/models/force_effector/ForceEffector.py index 4929604..00fb365 100644 --- a/sim/src/models/force_effector/ForceEffector.py +++ b/sim/src/models/force_effector/ForceEffector.py @@ -12,4 +12,4 @@ class ForceEffector( Model ): fy: int fz: int - model_details: ModelBase \ No newline at end of file + base: ModelBase \ No newline at end of file diff --git a/sim/src/models/force_effector/force_effector.rs b/sim/src/models/force_effector/force_effector.rs index 89f4074..be6cdb6 100644 --- a/sim/src/models/force_effector/force_effector.rs +++ b/sim/src/models/force_effector/force_effector.rs @@ -1,3 +1,4 @@ +use std::any::Any; use std::cell::RefCell; use std::rc::Rc; use pyo3::FromPyObject; @@ -13,7 +14,7 @@ pub struct ForceEffector { pub fy: i128, pub fz: i128, - pub model_details: ModelBase, + pub base: ModelBase, } pub fn new( input: &PyAny ) -> Result { @@ -61,7 +62,11 @@ impl SimModelTrait for ForceEffector { } fn get_details( &mut self ) -> &mut ModelBase { - &mut self.model_details + &mut self.base + } + + fn as_any(&mut self) -> &mut dyn Any { + self } } diff --git a/sim/src/simfrastructure/models.rs b/sim/src/simfrastructure/models.rs index 361c79b..f896b9f 100644 --- a/sim/src/simfrastructure/models.rs +++ b/sim/src/simfrastructure/models.rs @@ -1,9 +1,9 @@ -use std::{fmt::Debug, collections::HashMap, rc::Rc}; +use std::{fmt::Debug, collections::HashMap, rc::Rc, any::Any}; use pyo3::prelude::*; use super::*; -#[derive(std::fmt::Debug)] +#[derive(Debug)] #[derive(FromPyObject)] pub struct ModelBase { pub order: i8, @@ -15,8 +15,9 @@ pub trait SimModelTrait: Debug { fn resolve_references( &mut self, global_model_list: &HashMap ) { let locals = &mut self.get_details().local_refs; for id in &locals.reference_ids { + // println!( "{}", id ); locals.reference_list.push( - Rc::downgrade( global_model_list.get( id ).unwrap() ) + Rc::downgrade( &global_model_list.get( id ).unwrap() ) ) } } @@ -25,6 +26,7 @@ pub trait SimModelTrait: Debug { fn initialize( &mut self ) -> bool; fn update( &mut self ) -> bool; fn finalize( &mut self ) -> bool; + fn as_any(&mut self) -> &mut dyn Any; // Necessary return functions fn get_details( &mut self ) -> &mut ModelBase; diff --git a/sim/src/simfrastructure/python/runconfig.py b/sim/src/simfrastructure/python/runconfig.py index fc67540..681d03d 100644 --- a/sim/src/simfrastructure/python/runconfig.py +++ b/sim/src/simfrastructure/python/runconfig.py @@ -4,6 +4,7 @@ from dataclasses import dataclass from typing import List import sys +import unittest sim_args = sys.argv[1:] @@ -14,12 +15,12 @@ class IndexService: background. """ - current_id = 0 + current_id = 1 @classmethod def take_id( cls ) -> int: id = cls.current_id - cls.current_id += 1 + cls.current_id = cls.current_id + 1 return id @dataclass @@ -58,22 +59,83 @@ def __post_init__( self ): class Model( ABC ): "The 'Adam' of all models" - base = ModelBase( 0, 0 ) + base: ModelBase -# class Reference( Model ): -# "A dumb reference that will later be replaced" - -# def __init__( self, model: Model ): -# "Creates a reference to an already-existing model." - -# self.target_id = model.model_details.id - -@dataclass class RunConfig( ABC ): """The runconfig is what initializes the Rust simulation. It defines the models to use, and how to set them up!""" - model_list: List[ Model ] - description: str + def __init__( self, description: str, *args: Model ) -> None: + """ + Creates a run configuration given a description and a + list of models. + """ + + self.description = description + + self.model_list = [] + for item in args: + if type( item ) == list: + self.model_list.extend( item ) + elif isinstance( item, Model ): + self.model_list.append( item ) + +def ModelGroup( *args: Model ) -> List[ Model ]: + """ + Models grouped together automatically hold references + to each other inside of their base.local_refs property. + + This allows for pretty advanced systems without the need + to manually define many references. + """ -# def ModelGroup( *args ) -> \ No newline at end of file + # Add the ID's to a model's references, skipping over itself. + for model in args: + small_list = [ neighbor for neighbor in args if neighbor != model ] + model.base.local_refs = ReferenceList( small_list ) + + return list( args ) + +from termcolor import colored +class tests(unittest.TestCase): + def test_indexing( self ): + "Tests that model IDs are handled correctly" + + print( colored( "RunConfig:test_indexing: ", "white" ), end="" ) + try: + index0 = IndexService.take_id() + index1 = IndexService.take_id() + index2 = IndexService.take_id() + index3 = IndexService.take_id() + index4 = IndexService.take_id() + self.assertEqual( index0, 2 ) + self.assertEqual( index1, 3 ) + self.assertEqual( index2, 4 ) + self.assertEqual( index3, 5 ) + self.assertEqual( index4, 6 ) + print( colored( "OK", "green", attrs=["bold"] ) ) + except Exception as e: + print( colored( "Failed!", "red", attrs=['bold'] ) ) + raise e + + def test_model_id( self ): + "Test that model IDs are handled correctly" + + print( colored( "RunConfig:test_model_id: ", "white" ), end="" ) + try: + base0 = ModelBase( 0, 10 ) + self.assertEqual( ModelBase( 0, 10 ).id, 8 ) + base1 = ModelBase( 0, 10 ) + self.assertEqual( ModelBase( 0, 10 ).id, 10 ) + base2 = ModelBase( 0, 10 ) + base3 = ModelBase( 0, 10 ) + base4 = ModelBase( 0, 10 ) + self.assertEqual( base0.id, 7 ) + self.assertEqual( base1.id, 9 ) + self.assertEqual( base2.id, 11 ) + self.assertEqual( base3.id, 12 ) + self.assertEqual( base4.id, 13 ) + print( colored( "OK", "green", attrs=["bold"] ) ) + except Exception as e: + print( colored( "Failed!", "red", attrs=['bold'] ) ) + raise e \ No newline at end of file diff --git a/sim/src/simfrastructure/runtime.rs b/sim/src/simfrastructure/runtime.rs index c3ea607..0f50522 100644 --- a/sim/src/simfrastructure/runtime.rs +++ b/sim/src/simfrastructure/runtime.rs @@ -67,10 +67,15 @@ impl Runtime { }).unwrap(); // Connect references - for ( id, model ) in &runtime.model_list { + for ( _id, model ) in &runtime.model_list { model.borrow_mut().resolve_references( &runtime.model_list ) } + // Initialize models + for ( _id, model ) in &runtime.model_list { + model.borrow_mut().initialize(); + } + return runtime } } \ No newline at end of file From 3a6887b0ef8782a4dbc29d559f8275472570428f Mon Sep 17 00:00:00 2001 From: Joseph Meadows Date: Thu, 28 Jul 2022 23:57:12 -0700 Subject: [PATCH 8/9] Removed old variables --- sim/scenarios/basic/runconfig.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sim/scenarios/basic/runconfig.py b/sim/scenarios/basic/runconfig.py index a5d4c3d..ae42170 100644 --- a/sim/scenarios/basic/runconfig.py +++ b/sim/scenarios/basic/runconfig.py @@ -7,14 +7,13 @@ from simfrastructure.python.runconfig import * # Create models here -base = ModelBase( 100, 200 ) force = ForceEffector( 0, 10, 20, ModelBase( 100, 200 ) ) eom = EOM( x=0, y=0, z=0, - force_effectors=ReferenceList( ), - base=base + force_effectors=ReferenceList( force ), + base=ModelBase( 100, 200 ) ) # Create the actual configuration From 7449dcd3728207fb743b3c443b11c5b44d37ece6 Mon Sep 17 00:00:00 2001 From: Joseph Meadows Date: Sun, 31 Jul 2022 17:17:55 -0700 Subject: [PATCH 9/9] Last attempt at cleaning up a "dyn trait" implementation. Now switching to Model enum --- sim/src/models/eom/eom.rs | 21 ++- .../models/force_effector/force_effector.rs | 3 +- sim/src/simfrastructure/mod.rs | 8 +- sim/src/simfrastructure/models.rs | 55 ++++--- sim/src/simfrastructure/python/runconfig.py | 2 +- sim/src/simfrastructure/references.rs | 155 ++++++++++++++++++ sim/src/simfrastructure/runtime.rs | 9 +- sim/src/simfrastructure/simtypes.rs | 3 +- 8 files changed, 221 insertions(+), 35 deletions(-) create mode 100644 sim/src/simfrastructure/references.rs diff --git a/sim/src/models/eom/eom.rs b/sim/src/models/eom/eom.rs index 4b3b1b9..0fc1e4c 100644 --- a/sim/src/models/eom/eom.rs +++ b/sim/src/models/eom/eom.rs @@ -1,15 +1,18 @@ use std::any::Any; +use std::borrow::BorrowMut; use std::cell::{RefCell}; +use std::collections::HashMap; use std::ops::Deref; use std::rc::Rc; -use pyo3::FromPyObject; +use pyo3::{FromPyObject, PyAny, PyErr}; use crate::models::force_effector::ForceEffector; use crate::simfrastructure::{models::*}; -use crate::simfrastructure::{PyAny, PyErr}; use crate::simfrastructure::{ModelPtr}; use crate::simfrastructure::models::SimModelTrait; +use crate::simfrastructure::*; + #[derive(std::fmt::Debug)] #[derive(FromPyObject)] pub struct EOM { @@ -17,7 +20,7 @@ pub struct EOM { pub y: i128, pub z: i128, - pub force_effectors: ReferenceList, + pub force_effectors: ReferenceList, pub base: ModelBase, } @@ -29,6 +32,16 @@ impl ModelFromInput for EOM { } impl SimModelTrait for EOM { + + fn resolve_references( &mut self, global_model_list: &std::collections::HashMap ) { + // self.force_effectors.populate_refs( global_model_list as &HashMap>> ).expect( + // "Failed to connect references!" + // ); + + // let jj = self.force_effectors.reference_list[0].upgrade().borrow_mut(); + + } + fn initialize( &mut self ) -> bool { println!( "EOM Model is referencing:" ); for reference in &self.base.local_refs.reference_list { @@ -60,6 +73,8 @@ impl SimModelTrait for EOM { } } +impl Model for EOM {} + #[cfg(test)] mod tests { diff --git a/sim/src/models/force_effector/force_effector.rs b/sim/src/models/force_effector/force_effector.rs index be6cdb6..3593653 100644 --- a/sim/src/models/force_effector/force_effector.rs +++ b/sim/src/models/force_effector/force_effector.rs @@ -1,10 +1,9 @@ use std::any::Any; use std::cell::RefCell; use std::rc::Rc; -use pyo3::FromPyObject; +use pyo3::{FromPyObject, PyAny, PyErr}; use crate::simfrastructure::models::{ModelBase, SimModelTrait, ModelFromInput}; -use crate::simfrastructure::{PyAny, PyErr}; use crate::simfrastructure::{ModelPtr}; #[derive(std::fmt::Debug)] diff --git a/sim/src/simfrastructure/mod.rs b/sim/src/simfrastructure/mod.rs index e32b6d5..8c8c51c 100644 --- a/sim/src/simfrastructure/mod.rs +++ b/sim/src/simfrastructure/mod.rs @@ -2,6 +2,9 @@ /// Simfrastructure is the infrastructure of the simulation pub mod models; +pub mod references; + +pub use references::*; // The simulation infrastructure that powers everything pub mod runtime; @@ -9,7 +12,4 @@ pub use runtime::*; // Short hand datatypes for sim infrastructure pub mod simtypes; -pub use simtypes::*; - -pub use pyo3::PyAny; -pub use pyo3::PyErr; +pub use simtypes::*; \ No newline at end of file diff --git a/sim/src/simfrastructure/models.rs b/sim/src/simfrastructure/models.rs index f896b9f..6225862 100644 --- a/sim/src/simfrastructure/models.rs +++ b/sim/src/simfrastructure/models.rs @@ -2,20 +2,48 @@ use std::{fmt::Debug, collections::HashMap, rc::Rc, any::Any}; use pyo3::prelude::*; use super::*; +use super::references::ReferenceList; -#[derive(Debug)] -#[derive(FromPyObject)] +#[derive(Debug, FromPyObject)] pub struct ModelBase { pub order: i8, pub id: ModelID, pub local_refs: ReferenceList, + // pub global_model_list: HashMap +} + +// impl FromPyObject<'_> for ModelBase { +// fn extract(ob: &PyAny) -> PyResult { +// Ok( +// Self { +// order: ob.getattr( "order" )?.extract()?, +// id: ob.getattr( "id" )?.extract()?, +// local_refs: ob.getattr( "local_refs" )?.extract()?, +// // runtime: Runtime { run: -1, model_list: HashMap::new() } +// } +// ) +// } +// } + +pub trait ModelFromInput { + fn new( input: &PyAny ) -> Result; } pub trait SimModelTrait: Debug { + + /// A model should provide a custom implementation of this function if + /// it needs to fill its reference lists fn resolve_references( &mut self, global_model_list: &HashMap ) { + let _temp = global_model_list; + } + + /// ## ***This function should never be modified!!!*** + /// It populates a model's local_refs, which is an operation that + /// shouldn't ever need to be changed. If you need to connect references + /// manually, consider using ``` resolve_references ``` instead! + fn resolve_global_refs( &mut self, global_model_list: &HashMap ) { let locals = &mut self.get_details().local_refs; for id in &locals.reference_ids { - // println!( "{}", id ); locals.reference_list.push( Rc::downgrade( &global_model_list.get( id ).unwrap() ) ) @@ -32,26 +60,7 @@ pub trait SimModelTrait: Debug { fn get_details( &mut self ) -> &mut ModelBase; } -pub trait ModelFromInput { - fn new( input: &PyAny ) -> Result; -} - -#[derive(Debug)] -pub struct ReferenceList { - pub reference_ids: Vec, - pub reference_list: Vec>, -} - -impl FromPyObject<'_> for ReferenceList { - fn extract(ob: &PyAny) -> PyResult< Self > { - Ok( - Self { - reference_ids: ob.getattr( "reference_ids" )?.extract()?, - reference_list: vec![], - } - ) - } -} +pub trait Model: ModelFromInput + SimModelTrait {} // #[derive(Debug, FromPyObject)] // pub enum ModelRef { diff --git a/sim/src/simfrastructure/python/runconfig.py b/sim/src/simfrastructure/python/runconfig.py index 681d03d..e8545c4 100644 --- a/sim/src/simfrastructure/python/runconfig.py +++ b/sim/src/simfrastructure/python/runconfig.py @@ -40,7 +40,7 @@ def __post_init__( self ): for model in self.references: self.reference_ids.append( model.base.id ) else: - self.reference_ids.append( self.references.model_details.id ) + self.reference_ids.append( self.references.base.id ) @dataclass class ModelBase: diff --git a/sim/src/simfrastructure/references.rs b/sim/src/simfrastructure/references.rs new file mode 100644 index 0000000..63857c4 --- /dev/null +++ b/sim/src/simfrastructure/references.rs @@ -0,0 +1,155 @@ +use std::{fmt::Debug, collections::HashMap, rc::{Rc, Weak}, cell::{RefCell, Ref}, borrow::BorrowMut, ops::Deref}; + +use pyo3::{FromPyObject, PyResult, PyAny}; + +use super::{models::SimModelTrait, ModelID, WeakModelPtr, ModelPtr, ModelIdMap}; + +#[derive(Debug)] +pub struct ReferenceList { + pub reference_ids: Vec, + pub reference_list: Vec>, +} + +impl FromPyObject<'_> for ReferenceList { + fn extract(ob: &PyAny) -> PyResult< Self > { + Ok( + Self { + reference_ids: ob.getattr( "reference_ids" )?.extract()?, + reference_list: vec![], + } + ) + } +} + +impl ReferenceList { + pub fn populate_refs( &mut self, global_model_list: &HashMap>> ) -> Result<(), ()> { + for id in &self.reference_ids { + // let temp_rc = global_model_list.get( id ).unwrap().clone(); + // let object = temp_rc.borrow().as_any().downcast_mut::().unwrap(); + // let mut contents = temp_rc.borrow_mut(); + // if let Some( concrete ) = contents.as_any().downcast_mut::() { + // self.reference_list.push( + // Rc::downgrade( concrete ) + // ) + // } + self.reference_list.push( + Rc::downgrade( global_model_list.get( id ).unwrap() ) + ) + // let conc_rc = Rc::downcast::>( temp_rc ).unwrap(); + // let concrete: T = temp_rc.borrow().as_any().downcast_ref().unwrap(); + // self.reference_list.push( + // Rc::downgrade( conc_rc) + // // Rc::downcast( global_model_list.get( id ).unwrap() ) + // ) + } + + Ok(()) + } +} + +pub struct ReferenceListItem { + pub id: ModelID, + obj: Option> +} + +impl ReferenceListItem { + fn get_mut( self ) -> Option<&'static mut T> { + let option = self.obj.unwrap().upgrade().unwrap(); + let mut ting = option.deref().borrow_mut(); + let model = ting.as_any().downcast_mut::(); + model + // let mut upgraded_pointer = option.unwrap(); + // let model = upgraded_pointer.borrow_mut(); + // let model = upgraded_pointer.borrow().as_any(); + // let output = model.downcast_mut::().unwrap(); + // output + } + + fn pop_obj( &mut self, obj: WeakModelPtr ) { + self.obj = Some( obj ); + } + + fn new( id: ModelID ) -> Self { + ReferenceListItem { + id: id, + obj: None, + } + } +} + +pub struct ReferenceList2 { + list: Vec +} + +impl FromPyObject<'_> for ReferenceList2 { + fn extract(ob: &PyAny) -> PyResult { + let refs: Vec = ob.getattr( "reference_ids" )?.extract()?; + let mut new_list = Self { list: vec![] }; + + for id in refs { + new_list.list.push( + ReferenceListItem::new( id ) + ) + } + + Ok( new_list ) + } +} + +impl ReferenceList2 { + pub fn populate_refs( &mut self, global_model_list: &ModelIdMap) { + for obj in &mut self.list { + let weak = Rc::downgrade(global_model_list.get( &obj.id ).unwrap()); + obj.pop_obj( weak ); + } + } +} + +// #[derive(Debug)] +// pub struct ReferenceList2 { +// pub reference_ids: Vec, +// pub reference_list: Vec<&ref> +// pub reference_list: Vec>, +// } + +// impl FromPyObject<'_> for ReferenceList2 { +// fn extract(ob: &PyAny) -> PyResult< Self > { +// Ok( +// Self { +// reference_ids: ob.getattr( "reference_ids" )?.extract()?, +// reference_list: vec![], +// } +// ) +// } +// } + +// impl ReferenceList2 { +// pub fn populate_refs( &mut self, global_model_list: &HashMap>> ) -> Result<(), ()> { +// for id in &self.reference_ids { +// let x = global_model_list.get( id ).unwrap(); +// let ptr = x.borrow(); +// Ref::map(ptr, |t| { +// Some( t.as_any().downcast_mut::() ) +// }); +// // let temp_rc = global_model_list.get( id ).unwrap().clone(); +// // let object = temp_rc.borrow().as_any().downcast_mut::().unwrap(); +// // let mut contents = temp_rc.borrow_mut(); +// // if let Some( concrete ) = contents.as_any().downcast_mut::() { +// // self.reference_list.push( +// // Rc::downgrade( concrete ) +// // ) +// // } +// self.reference_list.push( +// Rc::downgrade( global_model_list.get( id ).unwrap() ) +// ) +// // let conc_rc = Rc::downcast::>( temp_rc ).unwrap(); +// // let concrete: T = temp_rc.borrow().as_any().downcast_ref().unwrap(); +// // self.reference_list.push( +// // Rc::downgrade( conc_rc) +// // // Rc::downcast( global_model_list.get( id ).unwrap() ) +// // ) +// } + +// Ok(()) +// } +// } \ No newline at end of file diff --git a/sim/src/simfrastructure/runtime.rs b/sim/src/simfrastructure/runtime.rs index 0f50522..0af7e48 100644 --- a/sim/src/simfrastructure/runtime.rs +++ b/sim/src/simfrastructure/runtime.rs @@ -6,6 +6,7 @@ use pyo3::{prelude::*, types::PyList}; // Local crate imports use crate::simfrastructure::{ModelCreatorMap, ModelPtr, ModelID}; +#[derive(Debug)] pub struct Runtime { pub run: i8, pub model_list: HashMap @@ -68,7 +69,13 @@ impl Runtime { // Connect references for ( _id, model ) in &runtime.model_list { - model.borrow_mut().resolve_references( &runtime.model_list ) + let mut model = model.borrow_mut(); + + // Connect the global refs + model.resolve_global_refs( &runtime.model_list ); + + // Specialized reference connection + model.resolve_references( &runtime.model_list ); } // Initialize models diff --git a/sim/src/simfrastructure/simtypes.rs b/sim/src/simfrastructure/simtypes.rs index 477f901..17b082c 100644 --- a/sim/src/simfrastructure/simtypes.rs +++ b/sim/src/simfrastructure/simtypes.rs @@ -27,4 +27,5 @@ pub type ModelCreatorFn = fn( &PyAny )->Result; /// A map of model creation functions that sorts them by the type name /// string. This can easily be called on a Python object to get its type /// and then delegate it to the proper Rust model. -pub type ModelCreatorMap = HashMap; \ No newline at end of file +pub type ModelCreatorMap = HashMap; +pub type ModelIdMap = HashMap; \ No newline at end of file