use std::{fmt::Display, str::FromStr};
#[derive(Clone, Default, PartialEq, Debug)]
pub enum Causality {
Parameter,
CalculatedParameter,
Input,
Output,
#[default]
Local,
Independent,
}
impl FromStr for Causality {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"parameter" => Ok(Causality::Parameter),
"calculatedParameter" => Ok(Causality::CalculatedParameter),
"input" => Ok(Causality::Input),
"output" => Ok(Causality::Output),
"local" => Ok(Causality::Local),
"independent" => Ok(Causality::Independent),
_ => Err(format!("Invalid Causality: {}", s)),
}
}
}
impl Display for Causality {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = match self {
Causality::Parameter => "parameter",
Causality::CalculatedParameter => "calculatedParameter",
Causality::Input => "input",
Causality::Output => "output",
Causality::Local => "local",
Causality::Independent => "independent",
};
write!(f, "{}", s)
}
}
#[derive(Clone, Copy, Default, PartialEq, Debug)]
pub enum Variability {
Constant,
Fixed,
Tunable,
Discrete,
#[default]
Continuous,
}
impl FromStr for Variability {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"constant" => Ok(Variability::Constant),
"fixed" => Ok(Variability::Fixed),
"tunable" => Ok(Variability::Tunable),
"discrete" => Ok(Variability::Discrete),
"continuous" => Ok(Variability::Continuous),
_ => Err(format!("Invalid Variability: {}", s)),
}
}
}
impl Display for Variability {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = match self {
Variability::Constant => "constant",
Variability::Fixed => "fixed",
Variability::Tunable => "tunable",
Variability::Discrete => "discrete",
Variability::Continuous => "continuous",
};
write!(f, "{}", s)
}
}
#[derive(Clone, Default, PartialEq, Debug)]
pub enum Initial {
#[default]
Exact,
Approx,
Calculated,
}
impl FromStr for Initial {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"exact" => Ok(Initial::Exact),
"approx" => Ok(Initial::Approx),
"calculated" => Ok(Initial::Calculated),
_ => Err(format!("Invalid Initial: {}", s)),
}
}
}
impl Display for Initial {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = match self {
Initial::Exact => "exact",
Initial::Approx => "approx",
Initial::Calculated => "calculated",
};
write!(f, "{}", s)
}
}
#[derive(Clone, Default, PartialEq, Debug, hard_xml::XmlRead, hard_xml::XmlWrite)]
#[xml(tag = "Real")]
pub struct Real {
#[xml(attr = "declaredType")]
pub declared_type: Option<String>,
#[xml(attr = "start")]
pub start: Option<f64>,
#[xml(attr = "derivative")]
pub derivative: Option<u32>,
#[xml(attr = "reinit")]
pub reinit: Option<bool>,
}
#[derive(Clone, Default, PartialEq, Debug, hard_xml::XmlRead, hard_xml::XmlWrite)]
#[xml(tag = "Integer")]
pub struct Integer {
#[xml(attr = "declaredType")]
pub declared_type: Option<String>,
#[xml(attr = "start")]
pub start: Option<i32>,
}
#[derive(Clone, Default, PartialEq, Debug, hard_xml::XmlRead, hard_xml::XmlWrite)]
#[xml(tag = "Boolean")]
pub struct Boolean {
#[xml(attr = "declaredType")]
pub declared_type: Option<String>,
#[xml(attr = "start")]
pub start: Option<bool>,
}
#[derive(Clone, PartialEq, Debug, hard_xml::XmlRead, hard_xml::XmlWrite)]
pub enum ScalarVariableElement {
#[xml(tag = "Real")]
Real(Real),
#[xml(tag = "Integer")]
Integer(Integer),
#[xml(tag = "Boolean")]
Boolean(Boolean),
#[xml(tag = "String")]
String,
#[xml(tag = "Enumeration")]
Enumeration,
}
impl Default for ScalarVariableElement {
fn default() -> Self {
Self::Real(Real::default())
}
}
#[cfg(feature = "arrow")]
impl ScalarVariableElement {
pub fn data_type(&self) -> arrow::datatypes::DataType {
match self {
ScalarVariableElement::Real(_) => arrow::datatypes::DataType::Float64,
ScalarVariableElement::Integer(_) => arrow::datatypes::DataType::Int32,
ScalarVariableElement::Boolean(_) => arrow::datatypes::DataType::Boolean,
ScalarVariableElement::String => arrow::datatypes::DataType::Utf8,
ScalarVariableElement::Enumeration => arrow::datatypes::DataType::Int32,
}
}
}
#[derive(Default, Debug, hard_xml::XmlRead, hard_xml::XmlWrite)]
#[xml(tag = "ScalarVariable", strict(unknown_attribute, unknown_element))]
pub struct ScalarVariable {
#[xml(attr = "name")]
pub name: String,
#[xml(attr = "valueReference")]
pub value_reference: u32,
#[xml(attr = "description")]
pub description: Option<String>,
#[xml(attr = "causality", default)]
pub causality: Causality,
#[xml(attr = "variability")]
pub variability: Option<Variability>,
#[xml(attr = "initial")]
pub initial: Option<Initial>,
#[xml(
child = "Real",
child = "Integer",
child = "Boolean",
child = "String",
child = "Enumeration"
)]
pub elem: ScalarVariableElement,
}
impl ScalarVariable {
pub fn is_continuous_input(&self) -> bool {
matches!(
(&self.elem, &self.causality),
(ScalarVariableElement::Real { .. }, Causality::Input)
)
}
}
#[cfg(test)]
mod tests {
use hard_xml::XmlRead;
use super::*;
#[test]
fn test_scalar_variable() {
let s = r#"
<ScalarVariable
name="inertia1.J"
valueReference="1073741824"
description="Moment of load inertia"
causality="parameter"
variability="fixed">
<Real declaredType="Modelica.SIunits.Inertia" start="1"/>
</ScalarVariable>
"#;
let sv = ScalarVariable::from_str(s).unwrap();
assert_eq!(sv.name, "inertia1.J");
assert_eq!(sv.value_reference, 1073741824);
assert_eq!(sv.description, Some("Moment of load inertia".into()));
assert_eq!(sv.causality, Causality::Parameter);
assert_eq!(sv.variability, Some(Variability::Fixed));
assert_eq!(
sv.elem,
ScalarVariableElement::Real(Real {
declared_type: Some("Modelica.SIunits.Inertia".to_string()),
start: Some(1.0),
derivative: None,
reinit: None
})
);
}
}