use crate::component::{Component, InputState, OutputState};
use crate::errors::RSCMResult;
use crate::timeseries::{FloatValue, Time};
use pyo3::prelude::*;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::ffi::CString;
use std::sync::Arc;
pub use crate::component::{RequirementDefinition, RequirementType};
#[macro_export]
macro_rules! create_component_builder {
($builder_name:ident, $rust_component:ty, $component_parameters:ty) => {
#[pyclass]
pub struct $builder_name {
parameters: $component_parameters,
}
#[pymethods]
impl $builder_name {
#[staticmethod]
pub fn from_parameters(parameters: Bound<PyAny>) -> PyResult<Self> {
use pyo3::exceptions::PyValueError;
let parameters = pythonize::depythonize::<$component_parameters>(¶meters);
match parameters {
Ok(parameters) => Ok(Self { parameters }),
Err(e) => Err(PyValueError::new_err(format!("{}", e))),
}
}
pub fn build(&self) -> PyRustComponent {
PyRustComponent(std::sync::Arc::new(<$rust_component>::from_parameters(
self.parameters.clone(),
)))
}
}
};
}
#[macro_export]
macro_rules! impl_component {
($py_component:ty) => {
#[pymethods]
impl $py_component {
fn definitions(&self) -> Vec<RequirementDefinition> {
self.0.definitions()
}
pub fn solve(
&mut self,
t_current: Time,
t_next: Time,
collection: crate::python::timeseries_collection::PyTimeseriesCollection,
) -> PyResult<HashMap<String, FloatValue>> {
let input_state =
crate::model::extract_state(&collection.0, self.0.input_names(), t_current);
let output_state = self.0.solve(t_current, t_next, &input_state)?;
Ok(output_state)
}
}
};
}
#[pymethods]
impl RequirementDefinition {
#[new]
pub fn new_python(name: String, unit: String, requirement_type: RequirementType) -> Self {
Self {
name,
unit,
requirement_type,
}
}
}
#[derive(Debug, Clone)]
#[pyclass]
#[pyo3{name = "RustComponent"}]
pub struct PyRustComponent(pub Arc<dyn Component + Send + Sync>);
impl_component!(PyRustComponent);
#[derive(Debug)]
pub struct PythonComponent {
pub component: Py<PyAny>,
}
#[typetag::serde]
impl Component for PythonComponent {
fn definitions(&self) -> Vec<RequirementDefinition> {
Python::attach(|py| {
let py_result = self
.component
.bind(py)
.call_method("definitions", (), None)
.unwrap();
let py_result: Vec<RequirementDefinition> = py_result.extract().unwrap();
py_result
})
}
fn solve(
&self,
t_current: Time,
t_next: Time,
input_state: &InputState,
) -> RSCMResult<OutputState> {
Python::attach(|py| {
let py_result = self
.component
.bind(py)
.call_method(
"solve",
(t_current, t_next, input_state.clone().to_hashmap()),
None,
)
.unwrap();
let output_state = py_result.extract().unwrap();
Ok(output_state)
})
}
}
impl Serialize for PythonComponent {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
Python::attach(|py| {
let py_result = self
.component
.bind(py)
.call_method("to_json", (), None)
.unwrap();
let py_result: String = py_result.extract().unwrap();
serializer.serialize_str(&py_result)
})
}
}
impl<'de> Deserialize<'de> for PythonComponent {
fn deserialize<D>(deserializer: D) -> Result<PythonComponent, D::Error>
where
D: serde::Deserializer<'de>,
{
let s: String = Deserialize::deserialize(deserializer)?;
Python::attach(|py| {
let code = CString::new(format!("Component.from_json('{}')", s)).unwrap();
let component = py.eval(code.as_c_str(), None, None).unwrap().unbind();
Ok(PythonComponent { component })
})
}
}
#[pyclass]
#[pyo3(name = "PythonComponent")]
pub struct PyPythonComponent(pub Arc<PythonComponent>);
#[pymethods]
impl PyPythonComponent {
#[staticmethod]
pub fn build(component: Py<PyAny>) -> Self {
Self(Arc::new(PythonComponent { component }))
}
}
impl_component!(PyPythonComponent);