use std::{fmt::Display, path::PathBuf, str::FromStr};
use fmi::{
EventFlags,
fmi3::{Fmi3Error, Fmi3Res, Fmi3Status, binding},
schema::fmi3::AppendToModelVariables,
};
use crate::fmi3::ModelState;
mod model_get_set;
mod wrappers;
pub use model_get_set::{ModelGetSet, ModelGetSetStates};
pub use wrappers::{Fmi3CoSimulation, Fmi3Common, Fmi3ModelExchange, Fmi3ScheduledExecution};
pub trait Context<M: UserModel> {
fn logging_on(&self, category: M::LoggingCategory) -> bool;
fn set_logging(&mut self, category: M::LoggingCategory, enabled: bool);
fn log(&self, status: Fmi3Status, category: M::LoggingCategory, args: std::fmt::Arguments<'_>);
fn resource_path(&self) -> &PathBuf;
fn initialize(&mut self, start_time: f64, stop_time: Option<f64>);
fn time(&self) -> f64;
fn set_time(&mut self, time: f64);
fn stop_time(&self) -> Option<f64>;
fn early_return_allowed(&self) -> bool {
false
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any;
}
pub trait Model: Default {
const MODEL_NAME: &'static str;
const INSTANTIATION_TOKEN: &'static str;
const MAX_EVENT_INDICATORS: usize;
const SUPPORTS_MODEL_EXCHANGE: bool;
const SUPPORTS_CO_SIMULATION: bool;
const SUPPORTS_SCHEDULED_EXECUTION: bool;
fn build_metadata(
variables: &mut fmi::schema::fmi3::ModelVariables,
model_structure: &mut fmi::schema::fmi3::ModelStructure,
vr_offset: u32,
prefix: Option<&str>,
) -> u32;
fn build_terminals(_terminals: &mut Vec<fmi::schema::fmi3::Terminal>, _prefix: Option<&str>) {}
fn build_toplevel_terminals() -> Option<fmi::schema::fmi3::Fmi3TerminalsAndIcons> {
let mut terminals = Vec::new();
Self::build_terminals(&mut terminals, None);
if terminals.is_empty() {
return None;
}
let fmi_version = unsafe { std::ffi::CStr::from_ptr(binding::fmi3Version.as_ptr() as _) }
.to_string_lossy();
Some(fmi::schema::fmi3::Fmi3TerminalsAndIcons {
fmi_version: fmi_version.to_string(),
terminals: Some(fmi::schema::fmi3::Terminals { terminals }),
annotations: None,
})
}
fn build_toplevel_metadata() -> ModelMetadata {
let mut variables = fmi::schema::fmi3::ModelVariables::default();
let time = fmi::schema::fmi3::FmiFloat64::new(
"time".to_string(),
0,
None,
fmi::schema::fmi3::Causality::Independent,
fmi::schema::fmi3::Variability::Continuous,
None,
None,
);
AppendToModelVariables::append_to_variables(time, &mut variables);
let mut structure = fmi::schema::fmi3::ModelStructure::default();
let _num_vars = Self::build_metadata(&mut variables, &mut structure, 1, None);
let terminals = Self::build_toplevel_terminals();
ModelMetadata {
model_variables: variables,
model_structure: structure,
terminals,
}
}
fn set_start_values(&mut self);
fn validate_variable_setting(
vr: binding::fmi3ValueReference,
state: &ModelState,
) -> Result<(), &'static str> {
let _ = (vr, state);
Ok(())
}
}
pub struct ModelMetadata {
pub model_variables: fmi::schema::fmi3::ModelVariables,
pub model_structure: fmi::schema::fmi3::ModelStructure,
pub terminals: Option<fmi::schema::fmi3::Fmi3TerminalsAndIcons>,
}
#[allow(dead_code)]
pub trait TerminalProvider {
fn terminal(name: &str, prefix: Option<&str>) -> fmi::schema::fmi3::Terminal;
}
pub trait ModelLoggingCategory: Display + FromStr + Ord + Copy + Default {
fn all_categories() -> impl Iterator<Item = Self>;
fn trace_category() -> Self;
fn error_category() -> Self;
}
#[derive(Debug, Clone, Copy, Default)]
pub struct CSDoStepResult {
pub event_handling_needed: bool,
pub terminate_simulation: bool,
pub early_return: bool,
pub last_successful_time: f64,
}
impl CSDoStepResult {
pub fn completed(last_successful_time: f64) -> Self {
Self {
event_handling_needed: false,
terminate_simulation: false,
early_return: false,
last_successful_time,
}
}
}
pub trait UserModel: Sized {
type LoggingCategory: ModelLoggingCategory + 'static;
fn configurate(&mut self, _context: &dyn Context<Self>) -> Result<(), Fmi3Error> {
Ok(())
}
fn calculate_values(&mut self, _context: &dyn Context<Self>) -> Result<Fmi3Res, Fmi3Error> {
Ok(Fmi3Res::OK)
}
fn event_update(
&mut self,
_context: &dyn Context<Self>,
event_flags: &mut EventFlags,
) -> Result<Fmi3Res, Fmi3Error> {
event_flags.reset();
Ok(Fmi3Res::OK)
}
fn get_event_indicators(
&mut self,
_context: &dyn Context<Self>,
indicators: &mut [f64],
) -> Result<bool, Fmi3Error> {
for indicator in indicators.iter_mut() {
*indicator = 0.0;
}
Ok(true)
}
fn do_step(
&mut self,
context: &mut dyn Context<Self>,
current_communication_point: f64,
communication_step_size: f64,
_no_set_fmu_state_prior_to_current_point: bool,
) -> Result<CSDoStepResult, Fmi3Error> {
let target_time = current_communication_point + communication_step_size;
context.set_time(target_time);
Ok(CSDoStepResult::completed(target_time))
}
}