1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
use std::ffi::CString;
use crate::schema::traits::FmiInterfaceType;
use crate::{
Error, EventFlags,
fmi3::{CoSimulation, Common, Fmi3Error, Fmi3Res, Fmi3Status, binding, import, logger},
traits::{FmiEventHandler, FmiImport, FmiStatus},
};
use super::{CS, Instance};
impl Instance<CS> {
/// Returns a new CoSimulation instance.
///
/// An FMU can be instantiated many times (provided capability flag
/// `can_be_instantiated_only_once_per_process = false`).
///
/// Arguments:
/// * `instance_name`: a unique identifier for the FMU instance. It is used to name the
/// instance, for example, in error or information messages generated by one of the fmi3XXX
/// functions. The argument `instance_name` must be a non empty string (in other words, must
/// have at least one character that is not a white space). If only one FMU is simulated,
/// either the attribute [`crate::fmi3::schema::Fmi3ModelDescription::model_name`] or one of
/// - [`crate::fmi3::schema::Fmi3ModelExchange::model_identifier`],
/// - [`crate::fmi3::schema::Fmi3CoSimulation::model_identifier`], or
/// - [`crate::fmi3::schema::Fmi3ScheduledExecution::model_identifier`]
/// can be used as `instance_name`.
///
/// * `visible`: Defines that the interaction with the user should be reduced to a minimum (no
/// application window, no plotting, no animation, etc.). In other words, the FMU is executed
/// in batch mode. If `visible = true`, the FMU is executed in interactive mode, and the FMU
/// might require to explicitly acknowledge start of simulation / instantiation /
/// initialization (acknowledgment is non-blocking).
///
/// * `logging_on`: If `= false`, then any logging is disabled and the logMessage callback
/// function must not be called by the FMU. If `logging_on = true`, then all `LogCategories`
/// are enabled. The function [`crate::fmi3::traits::Common::set_debug_logging`] gives more detailed
/// control about enabling specific `LogCategories`.
///
/// * `event_mode_used`: If `= true` the importer can handle events. The flag may only be
/// `true`, if `has_event_mode = true`, otherwise the FMU must raise an error. For FMUs that
/// have clocks, `event_mode_used = true` is required.
///
/// * `early_return_allowed`: If `= true` the importer can handle early return. Only in this
/// case, [`crate::fmi3::traits::CoSimulation::do_step()`] may return with `early_return = true`.
///
/// * `required_intermediate_variables`: An array of the value references of all input variables
/// that the simulation algorithm intends to set and all output variables it intends to get
/// during intermediate updates. This set may be empty (nRequiredIntermediateVariables = 0)
/// when the simulation algorithm does not intend to use intermediate update. Only the
/// variables in requiredIntermediateVariables may be accessed by the simulation algorithm
/// using fmi3Set{VariableType} and fmi3Get{VariableType} during Intermediate Update Mode. All
/// variables referenced in this set must be marked with the attribute intermediateUpdate =
/// "true" in modelDescription.xml.
///
/// See: <https://fmi-standard.org/docs/3.0.1/#fmi3InstantiateCoSimulation>
pub fn new(
import: &import::Fmi3Import,
instance_name: &str,
visible: bool,
logging_on: bool,
event_mode_used: bool,
early_return_allowed: bool,
required_intermediate_variables: &[binding::fmi3ValueReference],
) -> Result<Self, Error> {
let model_description = import.model_description();
let name = instance_name.to_owned();
let co_simulation = model_description
.co_simulation
.as_ref()
.ok_or(Error::UnsupportedFmuType("CoSimulation".to_owned()))?;
log::debug!(
"Instantiating CS: {} '{name}'",
co_simulation.model_identifier()
);
let binding = import.binding(co_simulation.model_identifier())?;
let instance_name = CString::new(instance_name).expect("Invalid instance name");
let instantiation_token = CString::new(model_description.instantiation_token.as_bytes())
.expect("Invalid instantiation token");
let resource_path =
CString::new(import.canonical_resource_path_string()).expect("Invalid resource path");
// instanceEnvironment is a pointer that must be passed to fmi3IntermediateUpdateCallback,
// fmi3ClockUpdateCallback, and fmi3LogMessageCallback to allow the simulation environment
// an efficient way to identify the calling FMU.
let instance = unsafe {
binding.fmi3InstantiateCoSimulation(
instance_name.as_ptr() as binding::fmi3String,
instantiation_token.as_ptr() as binding::fmi3String,
resource_path.as_ptr() as binding::fmi3String,
visible as binding::fmi3Boolean,
logging_on as binding::fmi3Boolean,
event_mode_used as binding::fmi3Boolean,
early_return_allowed as binding::fmi3Boolean,
required_intermediate_variables.as_ptr(),
required_intermediate_variables.len() as _,
std::ptr::null_mut() as binding::fmi3InstanceEnvironment,
Some(logger::callback_log),
// intermediateUpdate: fmi3IntermediateUpdateCallback,
None,
)
};
if instance.is_null() {
return Err(Error::Instantiation);
}
Ok(Self {
binding,
ptr: instance,
name,
_tag: std::marker::PhantomData,
})
}
}
impl CoSimulation for Instance<CS> {
fn enter_step_mode(&mut self) -> Result<Fmi3Res, Fmi3Error> {
Fmi3Status::from(unsafe { self.binding.fmi3EnterStepMode(self.ptr) }).ok()
}
fn do_step(
&mut self,
current_communication_point: f64,
communication_step_size: f64,
no_set_fmustate_prior_to_current_point: bool,
event_handling_needed: &mut bool,
terminate_simulation: &mut bool,
early_return: &mut bool,
last_successful_time: &mut f64,
) -> Result<Fmi3Res, Fmi3Error> {
Fmi3Status::from(unsafe {
self.binding.fmi3DoStep(
self.ptr,
current_communication_point,
communication_step_size,
no_set_fmustate_prior_to_current_point,
event_handling_needed,
terminate_simulation,
early_return,
last_successful_time,
)
})
.ok()
}
fn get_output_derivatives(
&mut self,
vrs: &[binding::fmi3ValueReference],
orders: &[i32],
values: &mut [f64],
) -> Result<Fmi3Res, Fmi3Error> {
Fmi3Status::from(unsafe {
self.binding.fmi3GetOutputDerivatives(
self.ptr,
vrs.as_ptr(),
vrs.len() as _,
orders.as_ptr(),
values.as_mut_ptr(),
values.len() as _,
)
})
.ok()
}
}
impl FmiEventHandler for Instance<CS> {
#[inline]
fn enter_event_mode(&mut self) -> Result<Fmi3Res, Fmi3Error> {
Common::enter_event_mode(self)
}
#[inline]
fn update_discrete_states(
&mut self,
event_flags: &mut EventFlags,
) -> Result<Fmi3Res, Fmi3Error> {
Common::update_discrete_states(self, event_flags)
}
}