2 Copyright (C) 2012 Modelon AB
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the BSD style license.
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 FMILIB_License.txt file for more details.
12 You should have received a copy of the FMILIB_License.txt file
13 along with this program. If not, contact Modelon AB <http://www.modelon.com>.
19 #include <JM/jm_vector.h>
21 #include "fmi2_xml_parser.h"
22 #include "fmi2_xml_type_impl.h"
23 #include "fmi2_xml_model_description_impl.h"
25 #include "fmi2_xml_variable_impl.h"
27 static const char* module = "FMI2XML";
29 const char* fmi2_xml_get_variable_name(fmi2_xml_variable_t* v) {
33 const char* fmi2_xml_get_variable_description(fmi2_xml_variable_t* v) {
34 return v->description;
37 size_t fmi2_xml_get_variable_original_order(fmi2_xml_variable_t* v) {
38 return v->originalIndex;
41 fmi2_value_reference_t fmi2_xml_get_variable_vr(fmi2_xml_variable_t* v) {
45 fmi2_variable_alias_kind_enu_t fmi2_xml_get_variable_alias_kind(fmi2_xml_variable_t* v) {
46 return (fmi2_variable_alias_kind_enu_t)v->aliasKind;
49 fmi2_xml_variable_t* fmi2_xml_get_variable_alias_base(fmi2_xml_model_description_t* md, fmi2_xml_variable_t* v) {
50 fmi2_xml_variable_t key;
51 fmi2_xml_variable_t *pkey = &key, *base;
53 if(!md->variablesByVR) return 0;
54 if(v->aliasKind == fmi2_variable_is_not_alias) return v;
56 key.aliasKind = fmi2_variable_is_not_alias;
58 found = jm_vector_bsearch(jm_voidp)(md->variablesByVR,(void**)&pkey, fmi2_xml_compare_vr);
65 Return the list of all the variables aliased to the given one (including the base one.
66 The list is ordered: base variable, aliases.
68 jm_status_enu_t fmi2_xml_get_variable_aliases(fmi2_xml_model_description_t* md,fmi2_xml_variable_t* v, jm_vector(jm_voidp)* list) {
69 fmi2_xml_variable_t key, *cur;
70 fmi2_value_reference_t vr = fmi2_xml_get_variable_vr(v);
71 size_t baseIndex, i, num = jm_vector_get_size(jm_voidp)(md->variablesByVR);
75 baseIndex = jm_vector_bsearch_index(jm_voidp)(md->variablesByVR,(void**)&cur, fmi2_xml_compare_vr);
76 cur = (fmi2_xml_variable_t*)jm_vector_get_item(jm_voidp)(md->variablesByVR, baseIndex);
79 while(fmi2_xml_get_variable_vr(cur) == vr) {
80 if(!jm_vector_push_back(jm_voidp)(list, cur)) {
81 jm_log_fatal(md->callbacks,module,"Could not allocate memory");
82 return jm_status_error;
85 cur = (fmi2_xml_variable_t*)jm_vector_get_item(jm_voidp)(md->variablesByVR, i);
91 cur = (fmi2_xml_variable_t*)jm_vector_get_item(jm_voidp)(md->variablesByVR, i);
92 while(fmi2_xml_get_variable_vr(cur) == vr) {
93 if(!jm_vector_push_back(jm_voidp)(list, cur)) {
94 jm_log_fatal(md->callbacks,module,"Could not allocate memory");
95 return jm_status_error;
99 cur = (fmi2_xml_variable_t*)jm_vector_get_item(jm_voidp)(md->variablesByVR, i - 1);
103 return jm_status_success;
107 fmi2_xml_variable_typedef_t* fmi2_xml_get_variable_declared_type(fmi2_xml_variable_t* v) {
108 return (fmi2_xml_variable_typedef_t*)(fmi2_xml_find_type_struct(v->typeBase, fmi2_xml_type_struct_enu_typedef));
111 fmi2_base_type_enu_t fmi2_xml_get_variable_base_type(fmi2_xml_variable_t* v) {
112 fmi2_xml_variable_type_base_t* type = v->typeBase;
113 return (type->baseType);
116 int fmi2_xml_get_variable_has_start(fmi2_xml_variable_t* v) {
117 return (v->typeBase->structKind == fmi2_xml_type_struct_enu_start);
120 fmi2_variability_enu_t fmi2_xml_get_variability(fmi2_xml_variable_t* v) {
121 return (fmi2_variability_enu_t)v->variability;
124 fmi2_causality_enu_t fmi2_xml_get_causality(fmi2_xml_variable_t* v) {
125 return (fmi2_causality_enu_t)v->causality;
128 fmi2_initial_enu_t fmi2_xml_get_initial(fmi2_xml_variable_t* v) {
129 return (fmi2_initial_enu_t)v->initial;
132 fmi2_xml_variable_t* fmi2_xml_get_previous(fmi2_xml_variable_t* v) {
136 fmi2_boolean_t fmi2_xml_get_canHandleMultipleSetPerTimeInstant(fmi2_xml_variable_t* v) {
137 return (fmi2_boolean_t)v->canHandleMultipleSetPerTimeInstant;
141 double fmi2_xml_get_real_variable_start(fmi2_xml_real_variable_t* v) {
142 fmi2_xml_variable_t* vv = (fmi2_xml_variable_t*)v;
143 if(fmi2_xml_get_variable_has_start(vv)) {
144 fmi2_xml_variable_start_real_t* start = (fmi2_xml_variable_start_real_t*)(vv->typeBase);
147 return fmi2_xml_get_real_variable_nominal(v);
150 fmi2_xml_real_variable_t* fmi2_xml_get_real_variable_derivative_of(fmi2_xml_real_variable_t* v) {
151 fmi2_xml_variable_t *vv = (fmi2_xml_variable_t *)v;
153 return (fmi2_xml_real_variable_t *)vv->derivativeOf;
156 fmi2_boolean_t fmi2_xml_get_real_variable_reinit(fmi2_xml_real_variable_t* v) {
157 fmi2_xml_variable_t *vv = (fmi2_xml_variable_t *)v;
158 return (fmi2_boolean_t)vv->reinit;
161 fmi2_xml_unit_t* fmi2_xml_get_real_variable_unit(fmi2_xml_real_variable_t* v) {
162 fmi2_xml_variable_t* vv = (fmi2_xml_variable_t*)v;
163 fmi2_xml_real_type_props_t* props = (fmi2_xml_real_type_props_t*)(fmi2_xml_find_type_struct(vv->typeBase, fmi2_xml_type_struct_enu_props));
164 if(!props || !props->displayUnit) return 0;
165 return props->displayUnit->baseUnit;
168 fmi2_xml_display_unit_t* fmi2_xml_get_real_variable_display_unit(fmi2_xml_real_variable_t* v) {
169 fmi2_xml_variable_t* vv = (fmi2_xml_variable_t*)v;
170 fmi2_xml_real_type_props_t* props = (fmi2_xml_real_type_props_t*)(fmi2_xml_find_type_struct(vv->typeBase, fmi2_xml_type_struct_enu_props));
171 if(!props || !props->displayUnit || !props->displayUnit->displayUnit[0]) return 0;
172 return props->displayUnit;
175 double fmi2_xml_get_real_variable_max(fmi2_xml_real_variable_t* v) {
176 fmi2_xml_variable_t* vv = (fmi2_xml_variable_t*)v;
177 fmi2_xml_real_type_props_t* props = (fmi2_xml_real_type_props_t*)(fmi2_xml_find_type_props(vv->typeBase));
179 return props->typeMax;
182 double fmi2_xml_get_real_variable_min(fmi2_xml_real_variable_t* v) {
183 fmi2_xml_variable_t* vv = (fmi2_xml_variable_t*)v;
184 fmi2_xml_real_type_props_t* props = (fmi2_xml_real_type_props_t*)(fmi2_xml_find_type_props(vv->typeBase));
186 return props->typeMin;
189 double fmi2_xml_get_real_variable_nominal(fmi2_xml_real_variable_t* v){
190 fmi2_xml_variable_t* vv = (fmi2_xml_variable_t*)v;
191 fmi2_xml_real_type_props_t* props = (fmi2_xml_real_type_props_t*)(fmi2_xml_find_type_props(vv->typeBase));
193 return props->typeNominal;
196 int fmi2_xml_get_integer_variable_start(fmi2_xml_integer_variable_t* v){
197 fmi2_xml_variable_t* vv = (fmi2_xml_variable_t*)v;
198 if(fmi2_xml_get_variable_has_start(vv)) {
199 fmi2_xml_variable_start_integer_t* start = (fmi2_xml_variable_start_integer_t*)(vv->typeBase);
205 int fmi2_xml_get_integer_variable_min(fmi2_xml_integer_variable_t* v){
206 fmi2_xml_variable_t* vv = (fmi2_xml_variable_t*)v;
207 fmi2_xml_integer_type_props_t* props = (fmi2_xml_integer_type_props_t*)(fmi2_xml_find_type_props(vv->typeBase));
209 return props->typeMin;
212 int fmi2_xml_get_integer_variable_max(fmi2_xml_integer_variable_t* v){
213 fmi2_xml_variable_t* vv = (fmi2_xml_variable_t*)v;
214 fmi2_xml_integer_type_props_t* props = (fmi2_xml_integer_type_props_t*)(fmi2_xml_find_type_props(vv->typeBase));
216 return props->typeMax;
219 int fmi2_xml_get_enum_variable_min(fmi2_xml_enum_variable_t* v){
220 fmi2_xml_variable_t* vv = (fmi2_xml_variable_t*)v;
221 fmi2_xml_variable_type_base_t* props = fmi2_xml_find_type_props(vv->typeBase);
222 return ((fmi2_xml_enum_variable_props_t*)props)->typeMin;
225 int fmi2_xml_get_enum_variable_max(fmi2_xml_enum_variable_t* v){
226 fmi2_xml_variable_t* vv = (fmi2_xml_variable_t*)v;
227 fmi2_xml_enum_variable_props_t* props =
228 (fmi2_xml_enum_variable_props_t*)(fmi2_xml_find_type_props(vv->typeBase));
230 return props->typeMax;
233 const char* fmi2_xml_get_string_variable_start(fmi2_xml_string_variable_t* v){
234 fmi2_xml_variable_t* vv = (fmi2_xml_variable_t*)v;
235 if(fmi2_xml_get_variable_has_start(vv)) {
236 fmi2_xml_variable_start_string_t* start = (fmi2_xml_variable_start_string_t*)(vv->typeBase);
242 int fmi2_xml_get_enum_variable_start(fmi2_xml_enum_variable_t* v) {
243 fmi2_xml_variable_t* vv = (fmi2_xml_variable_t*)v;
244 if(fmi2_xml_get_variable_has_start(vv)) {
245 fmi2_xml_variable_start_integer_t* start = (fmi2_xml_variable_start_integer_t*)(vv->typeBase);
251 fmi2_boolean_t fmi2_xml_get_boolean_variable_start(fmi2_xml_bool_variable_t* v) {
252 fmi2_xml_variable_t* vv = (fmi2_xml_variable_t*)v;
253 if(fmi2_xml_get_variable_has_start(vv)) {
254 fmi2_xml_variable_start_integer_t* start = (fmi2_xml_variable_start_integer_t*)(vv->typeBase);
260 fmi2_xml_real_variable_t* fmi2_xml_get_variable_as_real(fmi2_xml_variable_t* v) {
261 if(fmi2_xml_get_variable_base_type(v) == fmi2_base_type_real) return (void*)v;
265 fmi2_xml_integer_variable_t* fmi2_xml_get_variable_as_integer(fmi2_xml_variable_t*v){
266 if(fmi2_xml_get_variable_base_type(v) == fmi2_base_type_int) return (void*)v;
269 fmi2_xml_enum_variable_t* fmi2_xml_get_variable_as_enum(fmi2_xml_variable_t* v){
270 if(fmi2_xml_get_variable_base_type(v) == fmi2_base_type_enum) return (void*)v;
273 fmi2_xml_string_variable_t* fmi2_xml_get_variable_as_string(fmi2_xml_variable_t* v){
274 if(fmi2_xml_get_variable_base_type(v) == fmi2_base_type_str) return (void*)v;
277 fmi2_xml_bool_variable_t* fmi2_xml_get_variable_as_boolean(fmi2_xml_variable_t* v){
278 if(fmi2_xml_get_variable_base_type(v) == fmi2_base_type_bool) return (void*)v;
282 int fmi2_xml_handle_ScalarVariable(fmi2_xml_parser_context_t *context, const char* data) {
284 fmi2_xml_model_description_t* md = context->modelDescription;
285 fmi2_xml_variable_t* variable;
286 fmi2_xml_variable_t dummyV;
287 const char* description = 0;
288 jm_named_ptr named, *pnamed;
289 jm_vector(char)* bufName = fmi2_xml_reserve_parse_buffer(context,1,100);
290 jm_vector(char)* bufDescr = fmi2_xml_reserve_parse_buffer(context,2,100);
293 if(!bufName || !bufDescr) return -1;
295 /* <xs:attribute name="valueReference" type="xs:unsignedInt" use="optional but required for FMI"> */
296 if(fmi2_xml_set_attr_uint(context, fmi2_xml_elmID_ScalarVariable, fmi_attr_id_valueReference, 1, &vr, 0)) return -1;
299 /* <xs:attribute name="name" type="xs:normalizedString" use="required"/> */
300 fmi2_xml_set_attr_string(context, fmi2_xml_elmID_ScalarVariable, fmi_attr_id_name, 1, bufName) ||
301 /* <xs:attribute name="description" type="xs:string"/> */
302 fmi2_xml_set_attr_string(context, fmi2_xml_elmID_ScalarVariable, fmi_attr_id_description, 0, bufDescr)
305 if(context->skipOneVariableFlag) {
306 jm_log_error(context->callbacks,module, "Ignoring variable with undefined vr '%s'", jm_vector_get_itemp(char)(bufName,0));
309 if(jm_vector_get_size(char)(bufDescr)) {
310 description = jm_string_set_put(&md->descriptions, jm_vector_get_itemp(char)(bufDescr,0));
315 pnamed = jm_vector_push_back(jm_named_ptr)(&md->variablesByName, named);
317 if(pnamed) *pnamed = named = jm_named_alloc_v(bufName,sizeof(fmi2_xml_variable_t), dummyV.name - (char*)&dummyV, context->callbacks);
318 variable = named.ptr;
319 if( !pnamed || !variable ) {
320 fmi2_xml_parse_fatal(context, "Could not allocate memory");
324 variable->description = description;
325 variable->typeBase = 0;
326 variable->originalIndex = jm_vector_get_size(jm_named_ptr)(&md->variablesByName) - 1;
327 variable->derivativeOf = 0;
328 variable->previous = 0;
329 variable->aliasKind = fmi2_variable_is_not_alias;
330 variable->reinit = 0;
331 variable->canHandleMultipleSetPerTimeInstant = 1;
334 jm_name_ID_map_t causalityConventionMap[] = {{"local",fmi2_causality_enu_local},
335 {"input",fmi2_causality_enu_input},
336 {"output",fmi2_causality_enu_output},
337 {"parameter",fmi2_causality_enu_parameter},
338 {"calculatedParameter",fmi2_causality_enu_calculated_parameter},
339 {"independent",fmi2_causality_enu_independent},
341 jm_name_ID_map_t variabilityConventionMap[] = {{"continuous",fmi2_variability_enu_continuous},
342 {"constant", fmi2_variability_enu_constant},
343 {"fixed", fmi2_variability_enu_fixed},
344 {"tunable", fmi2_variability_enu_tunable},
345 {"discrete", fmi2_variability_enu_discrete},
347 jm_name_ID_map_t initialConventionMap[] = {{"approx",fmi2_initial_enu_approx},
348 {"calculated",fmi2_initial_enu_calculated},
349 {"exact",fmi2_initial_enu_exact},
351 unsigned int causality, variability, initial;
352 fmi2_initial_enu_t defaultInitial;
353 /* <xs:attribute name="causality" default="local"> */
354 if(fmi2_xml_set_attr_enum(context, fmi2_xml_elmID_ScalarVariable, fmi_attr_id_causality,0,&causality,fmi2_causality_enu_local,causalityConventionMap))
355 causality = fmi2_causality_enu_local;
356 variable->causality = causality;
357 /* <xs:attribute name="variability" default="continuous"> */
358 if(fmi2_xml_set_attr_enum(context, fmi2_xml_elmID_ScalarVariable, fmi_attr_id_variability,0,&variability,fmi2_variability_enu_continuous,variabilityConventionMap))
359 variability = fmi2_variability_enu_continuous;
361 defaultInitial = fmi2_get_default_initial((fmi2_variability_enu_t)variability, (fmi2_causality_enu_t)causality);
362 if(defaultInitial == fmi2_initial_enu_unknown) {
363 fmi2_xml_parse_error(context,"Invalid combination of variability %s and causality %s. Setting variability to 'fixed'",
364 fmi2_variability_to_string((fmi2_variability_enu_t)variability),
365 fmi2_causality_to_string((fmi2_causality_enu_t)causality));
366 variability = fmi2_variability_enu_fixed;
367 defaultInitial = fmi2_get_default_initial((fmi2_variability_enu_t)variability, (fmi2_causality_enu_t)causality);
369 variable->variability = variability;
371 /* <xs:attribute name="initial"> */
372 if(fmi2_xml_set_attr_enum(context, fmi2_xml_elmID_ScalarVariable, fmi_attr_id_initial,0,&initial,defaultInitial,initialConventionMap))
373 initial = defaultInitial;
374 defaultInitial = fmi2_get_valid_initial((fmi2_variability_enu_t)variability, (fmi2_causality_enu_t)causality, (fmi2_initial_enu_t) initial);
375 if(defaultInitial != initial) {
376 fmi2_xml_parse_error(context,"Initial '%s' is not allowed for variability '%s' and causality '%s'. Setting initial to '%s'",
377 fmi2_initial_to_string((fmi2_initial_enu_t)initial),
378 fmi2_variability_to_string((fmi2_variability_enu_t)variability),
379 fmi2_causality_to_string((fmi2_causality_enu_t)causality),
380 fmi2_initial_to_string(defaultInitial));
381 initial = defaultInitial;
383 variable->initial = initial;
386 unsigned int previous, multipleSet;
388 /* <xs:attribute name="previous" type="xs:unsignedInt"> */
389 fmi2_xml_set_attr_uint(context, fmi2_xml_elmID_ScalarVariable, fmi_attr_id_previous, 0, &previous, 0) ||
390 /* <xs:attribute name="canHandleMultipleSetPerTimeInstant" type="xs:boolean"> */
391 fmi2_xml_set_attr_boolean(context, fmi2_xml_elmID_ScalarVariable, fmi_attr_id_canHandleMultipleSetPerTimeInstant, 0, &multipleSet, 1)
394 /* Store the index as a pointer since we cannot access the variables list yet (we are constructing it). */
395 variable->previous = (void*)((char *)NULL + previous);
396 variable->canHandleMultipleSetPerTimeInstant = (char)multipleSet;
398 if (variable->variability != fmi2_causality_enu_input && !multipleSet) {
399 fmi2_xml_parse_error(context, "Only variables with causality='input' can have canHandleMultipleSetPerTimeInstant=false");
405 if(context->skipOneVariableFlag) {
406 context->skipOneVariableFlag = 0;
409 /* check that the type for the variable is set */
410 fmi2_xml_model_description_t* md = context->modelDescription;
411 fmi2_xml_variable_t* variable = jm_vector_get_last(jm_named_ptr)(&md->variablesByName).ptr;
412 if(!variable->typeBase) {
413 jm_log_error(context->callbacks, module, "No variable type element for variable %s. Assuming Real.", variable->name);
415 return fmi2_xml_handle_RealVariable(context, data);
418 /* might give out a warning if(data[0] != 0) */
423 int fmi2_xml_get_has_start(fmi2_xml_parser_context_t *context, fmi2_xml_variable_t* variable) {
424 int hasStart = fmi2_xml_is_attr_defined(context, fmi_attr_id_start);
427 Variables with causality = "parameter" or "input", as well as variables with variability = "constant", must have a "start" value.
428 If initial = exact or approx, a start value must be provided.
429 The second condition is actually enough since parameters and inputs and constants must be "initial=exact"
431 if( (variable->causality == (char)fmi2_causality_enu_parameter)
432 || (variable->causality == (char)fmi2_causality_enu_input)
433 || (variable->variability == (char)fmi2_variability_enu_constant)) {
434 assert(variable->initial != (char)fmi2_initial_enu_exact);
437 if (variable->initial != (char)fmi2_initial_enu_calculated)
439 fmi2_xml_parse_error(context, "Start attribute is required for this causality, variability and initial combination");
444 /* If initial = calculated, it is not allowed to provide a start value. */
445 if(variable->initial == (char)fmi2_initial_enu_calculated) {
446 fmi2_xml_parse_error(context, "Start attribute is not allowed for variables with initial='calculated'");
453 static void fmi2_log_error_if_start_required(
454 fmi2_xml_parser_context_t *context,
455 fmi2_xml_variable_t *variable)
457 if (variable->causality == fmi2_causality_enu_input) {
458 jm_log_error(context->callbacks,
459 "Error: variable %s: start value required for input variables",
461 } else if (variable->causality == fmi2_causality_enu_parameter) {
462 jm_log_error(context->callbacks,
463 "Error: variable %s: start value required for parameter variables",
465 } else if (variable->variability == fmi2_variability_enu_constant) {
466 jm_log_error(context->callbacks,
467 "Error: variable %s: start value required for variables with constant variability",
469 } else if (variable->initial == fmi2_initial_enu_exact) {
470 jm_log_error(context->callbacks,
471 "Error: variable %s: start value required for variables with initial == \"exact\"",
473 } else if (variable->initial == fmi2_initial_enu_approx) {
474 jm_log_error(context->callbacks,
475 "Error: variable %s: start value required for variables with initial == \"approx\"",
480 int fmi2_xml_handle_RealVariable(fmi2_xml_parser_context_t *context, const char* data) {
481 if(context->skipOneVariableFlag) return 0;
484 fmi2_xml_model_description_t* md = context->modelDescription;
485 fmi2_xml_variable_t* variable = jm_vector_get_last(jm_named_ptr)(&md->variablesByName).ptr;
486 fmi2_xml_type_definitions_t* td = &md->typeDefinitions;
487 fmi2_xml_variable_type_base_t * declaredType = 0;
488 fmi2_xml_real_type_props_t * type = 0;
491 assert(!variable->typeBase);
493 declaredType = fmi2_get_declared_type(context, fmi2_xml_elmID_Real, &td->defaultRealType.typeBase);
495 if(!declaredType) return -1;
498 int hasUnit = fmi2_xml_is_attr_defined(context, fmi_attr_id_unit) ||
499 fmi2_xml_is_attr_defined(context, fmi_attr_id_displayUnit);
500 int hasMin = fmi2_xml_is_attr_defined(context, fmi_attr_id_min);
501 int hasMax = fmi2_xml_is_attr_defined(context, fmi_attr_id_max);
502 int hasNom = fmi2_xml_is_attr_defined(context, fmi_attr_id_nominal);
503 int hasQuan = fmi2_xml_is_attr_defined(context, fmi_attr_id_quantity);
504 int hasRelQ = fmi2_xml_is_attr_defined(context, fmi_attr_id_relativeQuantity);
505 int hasUnb = fmi2_xml_is_attr_defined(context, fmi_attr_id_unbounded);
508 if(hasUnit || hasMin || hasMax || hasNom || hasQuan || hasRelQ ||hasUnb) {
509 fmi2_xml_real_type_props_t* props = 0;
511 if(declaredType->structKind == fmi2_xml_type_struct_enu_typedef)
512 props = (fmi2_xml_real_type_props_t*)(declaredType->baseTypeStruct);
514 props = (fmi2_xml_real_type_props_t* )declaredType;
516 fmi2_xml_reserve_parse_buffer(context, 1, 0);
517 fmi2_xml_reserve_parse_buffer(context, 2, 0);
519 type = fmi2_xml_parse_real_type_properties(context, fmi2_xml_elmID_Real);
522 type->typeBase.baseTypeStruct = declaredType;
523 if( !hasUnit) type->displayUnit = props->displayUnit;
524 if( !hasMin) type->typeMin = props->typeMin;
525 if( !hasMax) type->typeMax = props->typeMax;
526 if( !hasNom) type->typeNominal = props->typeNominal;
527 if( !hasQuan) type->quantity = props->quantity;
528 if( !hasRelQ) type->typeBase.isRelativeQuantity = type->typeBase.isRelativeQuantity;
529 if( !hasUnb) type->typeBase.isUnbounded = type->typeBase.isUnbounded;
532 type = (fmi2_xml_real_type_props_t*)declaredType;
534 variable->typeBase = &type->typeBase;
536 hasStart = fmi2_xml_get_has_start(context, variable);
539 fmi2_xml_variable_start_real_t * start = (fmi2_xml_variable_start_real_t*)fmi2_xml_alloc_variable_type_start(td, &type->typeBase, sizeof(fmi2_xml_variable_start_real_t));
541 fmi2_xml_parse_fatal(context, "Could not allocate memory");
545 /* <xs:attribute name="start" type="xs:double"/> */
546 fmi2_xml_set_attr_double(context, fmi2_xml_elmID_Real, fmi_attr_id_start, 0, &start->start, 0)
549 variable->typeBase = &start->typeBase;
551 fmi2_log_error_if_start_required(context, variable);
555 /* <xs:attribute name="derivative" type="xs:unsignedInt"> */
556 unsigned int derivativeOf;
559 if(fmi2_xml_set_attr_uint(context, fmi2_xml_elmID_Real,
560 fmi_attr_id_derivative, 0, &derivativeOf, 0)) return -1;
561 /* TODO: consider: is it ok to read in an unsigned int to store in a size_t? */
562 /* Store the index as a pointer since we cannot access the variables list yet (we are constructing it). */
563 variable->derivativeOf = (void *)((char *)NULL + derivativeOf);
565 /* <xs:attribute name="reinit" type="xs:boolean" use="optional" default="false"> */
566 if(fmi2_xml_set_attr_boolean(context, fmi2_xml_elmID_Real,
567 fmi_attr_id_reinit, 0, &reinit, 0)) return -1;
568 variable->reinit = (char)reinit;
570 if (variable->variability != fmi2_variability_enu_continuous && reinit) {
571 /* If reinit is true, this variable must be continuous. */
572 fmi2_xml_parse_error(context, "The reinit attribute may only be set on continuous-time states.");
578 /* don't do anything. might give out a warning if(data[0] != 0) */
584 int fmi2_xml_handle_IntegerVariable(fmi2_xml_parser_context_t *context, const char* data) {
585 if(context->skipOneVariableFlag) return 0;
588 fmi2_xml_model_description_t* md = context->modelDescription;
589 fmi2_xml_type_definitions_t* td = &md->typeDefinitions;
590 fmi2_xml_variable_t* variable = jm_vector_get_last(jm_named_ptr)(&md->variablesByName).ptr;
591 fmi2_xml_variable_type_base_t * declaredType = 0;
592 fmi2_xml_integer_type_props_t * type = 0;
595 declaredType = fmi2_get_declared_type(context, fmi2_xml_elmID_Integer,&td->defaultIntegerType.typeBase) ;
597 if(!declaredType) return -1;
599 int hasMin = fmi2_xml_is_attr_defined(context,fmi_attr_id_min);
600 int hasMax = fmi2_xml_is_attr_defined(context,fmi_attr_id_max);
601 int hasQuan = fmi2_xml_is_attr_defined(context,fmi_attr_id_quantity);
602 if( hasMin || hasMax || hasQuan) {
603 fmi2_xml_integer_type_props_t* props = 0;
605 if(declaredType->structKind != fmi2_xml_type_struct_enu_typedef)
606 props = (fmi2_xml_integer_type_props_t*)declaredType;
608 props = (fmi2_xml_integer_type_props_t*)(declaredType->baseTypeStruct);
609 assert(props->typeBase.structKind == fmi2_xml_type_struct_enu_props);
610 fmi2_xml_reserve_parse_buffer(context, 1, 0);
611 fmi2_xml_reserve_parse_buffer(context, 2, 0);
612 type = fmi2_xml_parse_integer_type_properties(context, fmi2_xml_elmID_Integer);
614 type->typeBase.baseTypeStruct = declaredType;
615 if(!hasMin) type->typeMin = props->typeMin;
616 if(!hasMax) type->typeMax = props->typeMax;
617 if(!hasQuan) type->quantity = props->quantity;
620 type = (fmi2_xml_integer_type_props_t*)declaredType;
622 variable->typeBase = &type->typeBase;
624 hasStart = fmi2_xml_get_has_start(context, variable);
626 fmi2_xml_variable_start_integer_t * start = (fmi2_xml_variable_start_integer_t*)fmi2_xml_alloc_variable_type_start(td, &type->typeBase, sizeof(fmi2_xml_variable_start_integer_t));
628 fmi2_xml_parse_fatal(context, "Could not allocate memory");
632 /* <xs:attribute name="start" type="xs:integer"/> */
633 fmi2_xml_set_attr_int(context, fmi2_xml_elmID_Integer, fmi_attr_id_start, 0, &start->start, 0)
635 /* not sure how to peek a default here (and start is probably required attriute)*/
636 jm_log_error(context->callbacks, module, "Start value zero will be assumed.");
639 variable->typeBase = &start->typeBase;
641 fmi2_log_error_if_start_required(context, variable);
645 /* don't do anything. might give out a warning if(data[0] != 0) */
651 int fmi2_xml_handle_BooleanVariable(fmi2_xml_parser_context_t *context, const char* data) {
652 if(context->skipOneVariableFlag) return 0;
655 fmi2_xml_model_description_t* md = context->modelDescription;
656 fmi2_xml_type_definitions_t* td = &md->typeDefinitions;
657 fmi2_xml_variable_t* variable = jm_vector_get_last(jm_named_ptr)(&md->variablesByName).ptr;
660 assert(!variable->typeBase);
662 variable->typeBase = fmi2_get_declared_type(context, fmi2_xml_elmID_Boolean, &td->defaultBooleanType) ;
664 if(!variable->typeBase) return -1;
666 hasStart = fmi2_xml_get_has_start(context, variable);
668 fmi2_xml_variable_start_integer_t * start = (fmi2_xml_variable_start_integer_t*)fmi2_xml_alloc_variable_type_start(td, variable->typeBase, sizeof(fmi2_xml_variable_start_integer_t ));
670 fmi2_xml_parse_fatal(context, "Could not allocate memory");
674 /* <xs:attribute name="start" type="xs:boolean"/> */
675 fmi2_xml_set_attr_boolean(context, fmi2_xml_elmID_Boolean, fmi_attr_id_start, 0, (unsigned*)&start->start, 0)
678 variable->typeBase = &start->typeBase;
680 fmi2_log_error_if_start_required(context, variable);
684 /* don't do anything. might give out a warning if(data[0] != 0) */
690 int fmi2_xml_handle_StringVariable(fmi2_xml_parser_context_t *context, const char* data) {
691 if(context->skipOneVariableFlag) return 0;
694 fmi2_xml_model_description_t* md = context->modelDescription;
695 fmi2_xml_type_definitions_t* td = &md->typeDefinitions;
696 fmi2_xml_variable_t* variable = jm_vector_get_last(jm_named_ptr)(&md->variablesByName).ptr;
699 assert(!variable->typeBase);
701 variable->typeBase = fmi2_get_declared_type(context, fmi2_xml_elmID_String,&td->defaultStringType) ;
703 if(!variable->typeBase) return -1;
705 hasStart = fmi2_xml_get_has_start(context, variable);
707 jm_vector(char)* bufStartStr = fmi2_xml_reserve_parse_buffer(context,1, 100);
709 fmi2_xml_variable_start_string_t * start;
711 /* <xs:attribute name="start" type="xs:string"/> */
712 fmi2_xml_set_attr_string(context, fmi2_xml_elmID_String, fmi_attr_id_start, 0, bufStartStr)
715 strlen = jm_vector_get_size_char(bufStartStr);
717 start = (fmi2_xml_variable_start_string_t*)fmi2_xml_alloc_variable_type_start(td, variable->typeBase, sizeof(fmi2_xml_variable_start_string_t) + strlen);
720 fmi2_xml_parse_fatal(context, "Could not allocate memory");
723 if (strlen != 0) { /* No need to memcpy empty strings (gives assetion error) */
724 memcpy(start->start, jm_vector_get_itemp_char(bufStartStr,0), strlen);
726 start->start[strlen] = 0;
727 variable->typeBase = &start->typeBase;
729 fmi2_log_error_if_start_required(context, variable);
733 /* don't do anything. might give out a warning if(data[0] != 0) */
739 fmi2_xml_enum_variable_props_t * fmi2_xml_parse_enum_properties(fmi2_xml_parser_context_t* context, fmi2_xml_enum_variable_props_t* declaredType) {
741 fmi2_xml_model_description_t* md = context->modelDescription;
742 fmi2_xml_enum_variable_props_t * props = 0;
743 fmi2_xml_elm_enu_t elmID = fmi2_xml_elmID_Enumeration;
744 const char* quantity = 0;
746 /* jm_vector(char)* bufName = fmi_get_parse_buffer(context,1);
747 jm_vector(char)* bufDescr = fmi_get_parse_buffer(context,2); */
748 jm_vector(char)* bufQuantity = fmi2_xml_reserve_parse_buffer(context,3,100);
750 props = (fmi2_xml_enum_variable_props_t*)fmi2_xml_alloc_variable_type_props(&md->typeDefinitions,
751 &md->typeDefinitions.defaultEnumType.base.typeBase, sizeof(fmi2_xml_enum_variable_props_t));
753 if(!bufQuantity || !props ||
754 /* <xs:attribute name="quantity" type="xs:normalizedString"/> */
755 fmi2_xml_set_attr_string(context, elmID, fmi_attr_id_quantity, 0, bufQuantity)
758 if(jm_vector_get_size(char)(bufQuantity))
759 quantity = jm_string_set_put(&md->typeDefinitions.quantities, jm_vector_get_itemp(char)(bufQuantity, 0));
761 props->quantity = (quantity == 0) ? declaredType->quantity: quantity;
763 if( /* <xs:attribute name="min" type="xs:int"/> */
764 fmi2_xml_set_attr_int(context, elmID, fmi_attr_id_min, 0, &props->typeMin, declaredType->typeMin) ||
765 /* <xs:attribute name="max" type="xs:int"/> */
766 fmi2_xml_set_attr_int(context, elmID, fmi_attr_id_max, 0, &props->typeMax, declaredType->typeMax)
771 int fmi2_xml_handle_EnumerationVariable(fmi2_xml_parser_context_t *context, const char* data) {
772 if(context->skipOneVariableFlag) return 0;
775 fmi2_xml_model_description_t* md = context->modelDescription;
776 fmi2_xml_type_definitions_t* td = &md->typeDefinitions;
777 fmi2_xml_variable_t* variable = jm_vector_get_last(jm_named_ptr)(&md->variablesByName).ptr;
778 fmi2_xml_variable_type_base_t * declaredType = 0;
779 fmi2_xml_enum_variable_props_t * type = 0;
782 assert(!variable->typeBase);
784 declaredType = fmi2_get_declared_type(context, fmi2_xml_elmID_Enumeration,&td->defaultEnumType.base.typeBase);
786 if(!declaredType) return -1;
789 fmi2_xml_is_attr_defined(context,fmi_attr_id_min) ||
790 fmi2_xml_is_attr_defined(context,fmi_attr_id_max) ||
791 fmi2_xml_is_attr_defined(context,fmi_attr_id_quantity)
793 fmi2_xml_enum_variable_props_t* props = 0;
795 if(declaredType->structKind != fmi2_xml_type_struct_enu_typedef)
796 props = (fmi2_xml_enum_variable_props_t*)declaredType;
798 props = (fmi2_xml_enum_variable_props_t*)declaredType->baseTypeStruct;
799 assert(props->typeBase.structKind == fmi2_xml_type_struct_enu_props);
800 fmi2_xml_reserve_parse_buffer(context, 1, 0);
801 fmi2_xml_reserve_parse_buffer(context, 2, 0);
802 type = fmi2_xml_parse_enum_properties(context, props);
804 type->typeBase.baseTypeStruct = declaredType;
807 type = (fmi2_xml_enum_variable_props_t*)declaredType;
809 variable->typeBase = &type->typeBase;
811 hasStart = fmi2_xml_get_has_start(context, variable);
813 fmi2_xml_variable_start_integer_t * start = (fmi2_xml_variable_start_integer_t*)fmi2_xml_alloc_variable_type_start(td, &type->typeBase, sizeof(fmi2_xml_variable_start_integer_t ));
815 fmi2_xml_parse_fatal(context, "Could not allocate memory");
819 /* <xs:attribute name="start" type="xs:integer"/> */
820 fmi2_xml_set_attr_int(context, fmi2_xml_elmID_Enumeration, fmi_attr_id_start, 0, &start->start, 0)
822 start->start = type->typeMin;
823 variable->typeBase = &start->typeBase;
825 fmi2_log_error_if_start_required(context, variable);
829 /* don't do anything. might give out a warning if(data[0] != 0) */
835 static int fmi2_xml_compare_variable_original_index (const void* first, const void* second) {
836 size_t a = (*(fmi2_xml_variable_t**)first)->originalIndex;
837 size_t b = (*(fmi2_xml_variable_t**)second)->originalIndex;
843 void fmi2_xml_eliminate_bad_alias(fmi2_xml_parser_context_t *context, size_t indexVR) {
844 fmi2_xml_model_description_t* md = context->modelDescription;
845 jm_vector(jm_voidp)* varByVR = md->variablesByVR;
846 fmi2_xml_variable_t* v = (fmi2_xml_variable_t*)jm_vector_get_item(jm_voidp)(varByVR, indexVR);
847 fmi2_value_reference_t vr = v->vr;
848 fmi2_base_type_enu_t vt = fmi2_xml_get_variable_base_type(v);
849 size_t i, n = jm_vector_get_size(jm_voidp)(varByVR);
850 for(i = 0; i< n; i++) {
853 v = (fmi2_xml_variable_t*)jm_vector_get_item(jm_voidp)(varByVR, i);
854 if((v->vr != vr)||(vt != fmi2_xml_get_variable_base_type(v))) continue;
855 jm_vector_remove_item_jm_voidp(varByVR,i);
858 index = jm_vector_bsearch_index(jm_named_ptr)(&md->variablesByName, &key, jm_compare_named);
860 jm_vector_remove_item(jm_named_ptr)(&md->variablesByName,index);
862 index = jm_vector_bsearch_index(jm_voidp)(md->variablesOrigOrder, (jm_voidp*)&v, fmi2_xml_compare_variable_original_index);
865 jm_vector_remove_item(jm_voidp)(md->variablesOrigOrder,index);
867 jm_log_error(context->callbacks, module,"Removing incorrect alias variable '%s'", v->name);
868 md->callbacks->free(v);
872 static int fmi2_xml_compare_vr_and_original_index (const void* first, const void* second) {
873 int ret = fmi2_xml_compare_vr(first, second);
874 if(ret != 0) return ret;
877 fmi2_xml_variable_t* a = *(fmi2_xml_variable_t**)first;
878 fmi2_xml_variable_t* b = *(fmi2_xml_variable_t**)second;
879 ret = a->causality - b->causality;
880 if(ret != 0 ) return ret;
881 ret = a->variability - b->variability;
882 if(ret != 0) return ret;
884 size_t ai = a->originalIndex;
885 size_t bi = b->originalIndex;
886 if(ai > bi) return 1;
887 if(ai < bi) return -1;
894 int fmi2_xml_handle_ModelVariables(fmi2_xml_parser_context_t *context, const char* data) {
896 jm_log_verbose(context->callbacks, module,"Parsing XML element ModelVariables");
897 /* reset handles for the elements that are specific under ModelVariables */
898 fmi2_xml_set_element_handle(context, "Real", FMI2_XML_ELM_ID(RealVariable));
899 fmi2_xml_set_element_handle(context, "Integer", FMI2_XML_ELM_ID(IntegerVariable));
900 fmi2_xml_set_element_handle(context, "Enumeration", FMI2_XML_ELM_ID(EnumerationVariable));
901 fmi2_xml_set_element_handle(context, "String", FMI2_XML_ELM_ID(StringVariable));
902 fmi2_xml_set_element_handle(context, "Boolean", FMI2_XML_ELM_ID(BooleanVariable));
903 fmi2_xml_set_element_handle(context, "Tool", FMI2_XML_ELM_ID(VariableTool));
906 /* postprocess variable list */
908 fmi2_xml_model_description_t* md = context->modelDescription;
909 jm_vector(jm_voidp)* varByVR;
912 numvar = jm_vector_get_size(jm_named_ptr)(&md->variablesByName);
914 /* store the list of vars in original order */
916 size_t size = jm_vector_get_size(jm_named_ptr)(&md->variablesByName);
917 md->variablesOrigOrder = jm_vector_alloc(jm_voidp)(size,size,md->callbacks);
918 if(md->variablesOrigOrder) {
920 for(i= 0; i < size; ++i) {
921 jm_vector_set_item(jm_voidp)(md->variablesOrigOrder, i, jm_vector_get_item(jm_named_ptr)(&md->variablesByName,i).ptr);
926 /* look up actual pointers for the derivativeOf and previous fields in variablesOrigOrder */
928 size_t size = jm_vector_get_size(jm_voidp)(md->variablesOrigOrder);
930 for (k=0; k < size; k++) {
931 fmi2_xml_variable_t *variable = jm_vector_get_item(jm_voidp)(md->variablesOrigOrder, k);
933 if (variable->derivativeOf) {
934 /* Retrieve index that was stored as a pointer */
935 size_t index = (char*)variable->derivativeOf - (char *)NULL;
936 /* Convert from one- to zero-based indexing */
938 /* Ok to just check upper bound since index is unsigned. */
940 fmi2_xml_parse_error(context, "The 'derivative' attribute must have a value between 1 and the number of model variables.");
941 /* todo: free allocated memory? */
944 variable->derivativeOf = (fmi2_xml_variable_t*)jm_vector_get_item(jm_voidp)(md->variablesOrigOrder, index);
946 if (variable->previous) {
947 /* retrieve index that was stored as a pointer */
948 size_t index = (char*)variable->previous - (char *)NULL;
949 /* Convert from one- to zero-based indexing */
951 /* Ok to just check upper bound since index is unsigned. */
953 fmi2_xml_parse_error(context, "The 'previous' attribute must have a value between 1 and the number of model variables.");
954 /* todo: free allocated memory? */
957 variable->previous = (fmi2_xml_variable_t*)jm_vector_get_item(jm_voidp)(md->variablesOrigOrder, index);
962 /* sort the variables by names */
963 jm_vector_qsort(jm_named_ptr)(&md->variablesByName,jm_compare_named);
965 /* create VR index */
966 md->status = fmi2_xml_model_description_enu_ok;
968 size_t size = jm_vector_get_size(jm_named_ptr)(&md->variablesByName);
969 md->variablesByVR = jm_vector_alloc(jm_voidp)(size,size,md->callbacks);
970 if(md->variablesByVR) {
972 for(i= 0; i < size; ++i) {
973 jm_vector_set_item(jm_voidp)(md->variablesByVR, i, jm_vector_get_item(jm_named_ptr)(&md->variablesByName,i).ptr);
978 md->status = fmi2_xml_model_description_enu_empty;
979 if(!md->variablesByVR || !md->variablesOrigOrder) {
980 fmi2_xml_parse_fatal(context, "Could not allocate memory");
983 varByVR = md->variablesByVR;
984 jm_vector_qsort(jm_voidp)(varByVR, fmi2_xml_compare_vr_and_original_index);
986 numvar = jm_vector_get_size(jm_voidp)(varByVR);
991 jm_log_verbose(context->callbacks, module,"Building alias index");
993 fmi2_xml_variable_t* a = (fmi2_xml_variable_t*)jm_vector_get_item(jm_voidp)(varByVR, 0);
994 int startPresent = fmi2_xml_get_variable_has_start(a);
995 int isConstant = (fmi2_xml_get_variability(a) == fmi2_variability_enu_constant);
996 a->aliasKind = fmi2_variable_is_not_alias;
1000 for(i = 1; i< numvar; i++) {
1001 fmi2_xml_variable_t* b = (fmi2_xml_variable_t*)jm_vector_get_item(jm_voidp)(varByVR, i);
1002 int b_startPresent = fmi2_xml_get_variable_has_start(b);
1003 int b_isConstant = (fmi2_xml_get_variability(b) == fmi2_variability_enu_constant);
1004 if((fmi2_xml_get_variable_base_type(a) == fmi2_xml_get_variable_base_type(b))
1005 && (a->vr == b->vr)) {
1007 jm_log_verbose(context->callbacks,module,"Variables %s and %s reference the same vr %u. Marking '%s' as alias.",
1008 a->name, b->name, b->vr, b->name);
1009 b->aliasKind = fmi2_variable_is_alias;
1011 if(!isConstant != !b_isConstant) {
1012 jm_log_error(context->callbacks,module,
1013 "Only constants can be aliases with constants (variables: %s and %s)",
1015 fmi2_xml_eliminate_bad_alias(context,i);
1016 numvar = jm_vector_get_size(jm_voidp)(varByVR);
1019 } else if (isConstant) {
1020 if (!startPresent || !b_startPresent) {
1021 jm_log_error(context->callbacks,module,
1022 "Constants in alias set must all have start attributes (variables: %s and %s)",
1024 fmi2_xml_eliminate_bad_alias(context,i);
1025 numvar = jm_vector_get_size(jm_voidp)(varByVR);
1029 /* TODO: Check that both start values are the same */
1030 } else if(startPresent && b_startPresent) {
1031 jm_log_error(context->callbacks,module,
1032 "Only one variable among non constant aliases is allowed to have start attribute (variables: %s and %s) %d, %d, const enum value: %d",
1033 a->name, b->name, fmi2_xml_get_variability(a), fmi2_xml_get_variability(b), fmi2_variability_enu_constant);
1034 fmi2_xml_eliminate_bad_alias(context,i);
1035 numvar = jm_vector_get_size(jm_voidp)(varByVR);
1039 if(b_startPresent) {
1045 b->aliasKind = fmi2_variable_is_not_alias;
1046 startPresent = b_startPresent;
1047 isConstant = b_isConstant;
1051 } while(foundBadAlias);
1054 numvar = jm_vector_get_size(jm_named_ptr)(&md->variablesByName);
1056 /* might give out a warning if(data[0] != 0) */