]> gerrit.simantics Code Review - simantics/fmil.git/blobdiff - org.simantics.fmil.core/native/FMILibrary/src/XML/src/FMI2/fmi2_xml_model_structure.c
Add FMILibrary-2.0.3 to org.simantics.fmil.core\native.
[simantics/fmil.git] / org.simantics.fmil.core / native / FMILibrary / src / XML / src / FMI2 / fmi2_xml_model_structure.c
diff --git a/org.simantics.fmil.core/native/FMILibrary/src/XML/src/FMI2/fmi2_xml_model_structure.c b/org.simantics.fmil.core/native/FMILibrary/src/XML/src/FMI2/fmi2_xml_model_structure.c
new file mode 100644 (file)
index 0000000..1b809cb
--- /dev/null
@@ -0,0 +1,499 @@
+/*
+    Copyright (C) 2012 Modelon AB
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the BSD style license.
+
+     This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    FMILIB_License.txt file for more details.
+
+    You should have received a copy of the FMILIB_License.txt file
+    along with this program. If not, contact Modelon AB <http://www.modelon.com>.
+*/
+
+/** \file fmi2_xml_model_structure.c
+*  \brief Implementation for the model structure interface.
+*/
+#include <string.h>
+#include <stdio.h>
+
+#include "fmi2_xml_parser.h"
+#include "fmi2_xml_model_structure_impl.h"
+#include "fmi2_xml_model_description_impl.h"
+
+static const char * module = "FMI2XML";
+
+fmi2_xml_model_structure_t* fmi2_xml_allocate_model_structure(jm_callbacks* cb) {
+       fmi2_xml_model_structure_t* ms = (fmi2_xml_model_structure_t*)(cb->calloc(1, sizeof(fmi2_xml_model_structure_t)));
+       if(!ms) return 0;
+
+       jm_vector_init(jm_voidp)(&ms->outputs,0,cb);
+       jm_vector_init(jm_voidp)(&ms->derivatives,0,cb);
+       jm_vector_init(jm_voidp)(&ms->discreteStates,0,cb);
+       jm_vector_init(jm_voidp)(&ms->initialUnknowns,0,cb);
+
+       ms->isValidFlag = 1;
+
+    ms->outputDeps = fmi2_xml_allocate_dependencies(cb);
+    ms->derivativeDeps = fmi2_xml_allocate_dependencies(cb);
+    ms->discreteStateDeps = fmi2_xml_allocate_dependencies(cb);
+    ms->initialUnknownDeps = fmi2_xml_allocate_dependencies(cb);
+
+       if(!ms->outputDeps || !ms->derivativeDeps || !ms->discreteStateDeps || !ms->initialUnknownDeps) {
+               fmi2_xml_free_model_structure(ms);
+               return 0;
+       }
+
+       return ms;
+}
+
+void fmi2_xml_free_model_structure(fmi2_xml_model_structure_t* ms) {
+       jm_callbacks* cb;
+       if(!ms) return;
+       cb = ms->outputs.callbacks;
+
+       jm_vector_free_data(jm_voidp)(&ms->outputs);
+       jm_vector_free_data(jm_voidp)(&ms->derivatives);
+       jm_vector_free_data(jm_voidp)(&ms->discreteStates);
+       jm_vector_free_data(jm_voidp)(&ms->initialUnknowns);
+
+    fmi2_xml_free_dependencies(ms->outputDeps);
+    fmi2_xml_free_dependencies(ms->derivativeDeps);
+    fmi2_xml_free_dependencies(ms->discreteStateDeps);
+    fmi2_xml_free_dependencies(ms->initialUnknownDeps);
+       cb->free(ms);
+}
+
+jm_vector(jm_voidp)* fmi2_xml_get_outputs(fmi2_xml_model_structure_t* ms) {
+       return &ms->outputs;
+}
+
+jm_vector(jm_voidp)* fmi2_xml_get_derivatives(fmi2_xml_model_structure_t* ms){
+       return &ms->derivatives;
+}
+
+jm_vector(jm_voidp)* fmi2_xml_get_discrete_states(fmi2_xml_model_structure_t* ms){
+       return &ms->discreteStates;
+}
+
+jm_vector(jm_voidp)* fmi2_xml_get_initial_unknowns(fmi2_xml_model_structure_t* ms){
+       return &ms->initialUnknowns;
+}
+
+
+void fmi2_xml_get_dependencies(fmi2_xml_dependencies_t* dep, size_t** startIndex, size_t** dependency, char** factorKind){
+    if(dep) {
+        if (jm_vector_get_size(size_t)(&dep->dependencyIndex) == 0) {
+            *startIndex = NULL;
+            *dependency = NULL;
+            *factorKind = NULL;
+        } else {
+            *startIndex = jm_vector_get_itemp(size_t)(&dep->startIndex, 0);
+            *dependency = jm_vector_get_itemp(size_t)(&dep->dependencyIndex, 0);
+            *factorKind = jm_vector_get_itemp(char)(&dep->dependencyFactorKind, 0);
+        }
+    }
+    else {
+        *startIndex = 0;
+    }
+}
+
+void fmi2_xml_get_outputs_dependencies(fmi2_xml_model_structure_t* ms,
+                                           size_t** startIndex, size_t** dependency, char** factorKind) {
+    fmi2_xml_get_dependencies(ms->outputDeps, startIndex, dependency, factorKind);
+}
+
+void fmi2_xml_get_derivatives_dependencies(fmi2_xml_model_structure_t* ms,
+                                           size_t** startIndex, size_t** dependency, char** factorKind) {
+    fmi2_xml_get_dependencies(ms->derivativeDeps, startIndex, dependency, factorKind);
+}
+
+void fmi2_xml_get_discrete_states_dependencies(fmi2_xml_model_structure_t* ms,
+                                           size_t** startIndex, size_t** dependency, char** factorKind) {
+    fmi2_xml_get_dependencies(ms->discreteStateDeps, startIndex, dependency, factorKind);
+}
+
+void fmi2_xml_get_initial_unknowns_dependencies(fmi2_xml_model_structure_t* ms,
+                                           size_t** startIndex, size_t** dependency, char** factorKind) {
+    fmi2_xml_get_dependencies(ms->initialUnknownDeps, startIndex, dependency, factorKind);
+}
+
+
+fmi2_xml_dependencies_t* fmi2_xml_allocate_dependencies(jm_callbacks* cb) {
+       fmi2_xml_dependencies_t* dep = (fmi2_xml_dependencies_t*)(cb->malloc(sizeof(fmi2_xml_dependencies_t)));
+       if(!dep) return 0;
+       jm_vector_init(size_t)(&dep->startIndex, 0, cb);
+       jm_vector_push_back(size_t)(&dep->startIndex, 0);
+
+       jm_vector_init(size_t)(&dep->dependencyIndex, 0, cb);
+       jm_vector_init(char)(&dep->dependencyFactorKind, 0, cb);
+
+       dep->isRowMajor = 1;
+
+       return dep;
+}
+
+void fmi2_xml_zero_empty_dependencies(fmi2_xml_dependencies_t** pdep) {
+       fmi2_xml_dependencies_t* dep =*pdep;
+       size_t ndep = jm_vector_get_size(size_t)(&dep->dependencyIndex);
+       size_t i;
+       if(!dep) return;
+       for(i = 0; i<ndep;i++) {
+               if(jm_vector_get_item(size_t)(&dep->dependencyIndex, i)) break;
+       }
+       if(i == ndep) {
+               fmi2_xml_free_dependencies(dep);
+               *pdep = 0;
+       }
+}
+
+
+void fmi2_xml_free_dependencies(fmi2_xml_dependencies_t* dep) {
+    jm_callbacks* cb;
+       if(!dep) return;
+    cb = dep->startIndex.callbacks;
+       jm_vector_free_data(size_t)(&dep->startIndex);
+
+       jm_vector_free_data(size_t)(&dep->dependencyIndex);
+       jm_vector_free_data(char)(&dep->dependencyFactorKind);
+    cb->free(dep);
+}
+
+
+int fmi2_xml_check_model_structure(fmi2_xml_model_description_t* md) {
+       fmi2_xml_model_structure_t* ms = md->modelStructure;
+
+       if(!ms || !ms->isValidFlag) return 0;
+
+       return ms->isValidFlag;
+}
+
+int fmi2_xml_handle_ModelStructure(fmi2_xml_parser_context_t *context, const char* data) {
+    fmi2_xml_model_description_t* md = context->modelDescription;
+    if(!data) {
+               jm_log_verbose(context->callbacks, module,"Parsing XML element ModelStructure");
+               /** allocate model structure */
+               md->modelStructure = fmi2_xml_allocate_model_structure(md->callbacks);
+               if(!md->modelStructure) {
+                               fmi2_xml_parse_fatal(context, module, "Could not allocate memory");
+                               return -1;
+               }
+    }
+    else {
+               /** make sure model structure information is consistent */
+
+               if(!fmi2_xml_check_model_structure(md)) {
+                       fmi2_xml_parse_fatal(context, "Model structure is not valid due to detected errors. Cannot continue.");
+                       return -1;
+               }
+/*             md->numberOfContinuousStates = jm_vector_get_size(jm_voidp)(&md->modelStructure->states); */
+
+    }
+    return 0;
+}
+
+
+int fmi2_xml_handle_Outputs(fmi2_xml_parser_context_t *context, const char* data) {
+    if (!data) {
+        jm_log_verbose(context->callbacks, module, "Parsing XML element Outputs");
+        /*  reset handles for the elements that are specific under Outputs */
+/*        fmi2_xml_set_element_handle(context, "Unknown", FMI2_XML_ELM_ID(OutputUnknown));*/
+        fmi2_xml_set_element_handle(context, "Unknown", FMI2_XML_ELM_ID(Unknown));
+    }
+    return 0;
+}
+int fmi2_xml_handle_Derivatives(fmi2_xml_parser_context_t *context, const char* data) {
+    if (!data) {
+        jm_log_verbose(context->callbacks, module, "Parsing XML element Derivatives");
+        /*  reset handles for the elements that are specific under Derivatives */
+        fmi2_xml_set_element_handle(context, "Unknown", FMI2_XML_ELM_ID(DerivativeUnknown));
+    }
+    else {
+        fmi2_xml_model_description_t* md = context->modelDescription;
+        fmi2_xml_model_structure_t* ms = md->modelStructure;
+        /* count the number of continuous states as the number of <Unknown> elements under <Derivatives> */
+        md->numberOfContinuousStates = jm_vector_get_size(jm_voidp)(&ms->derivatives);
+    }
+    return 0;
+}
+int fmi2_xml_handle_DiscreteStates(fmi2_xml_parser_context_t *context, const char* data) {
+    if (!data) {
+        jm_log_verbose(context->callbacks, module, "Parsing XML element DiscreteStates");
+        /*  reset handles for the elements that are specific under DiscreteStates */
+        fmi2_xml_set_element_handle(context, "Unknown", FMI2_XML_ELM_ID(DiscreteStateUnknown));
+    }
+    return 0;
+}
+int fmi2_xml_handle_InitialUnknowns(fmi2_xml_parser_context_t *context, const char* data) {
+    if (!data) {
+        jm_log_verbose(context->callbacks, module, "Parsing XML element InitialUnknowns");
+        /*  reset handles for the elements that are specific under InitialUnknowns */
+        fmi2_xml_set_element_handle(context, "Unknown", FMI2_XML_ELM_ID(InitialUnknown));
+    }
+    return 0;
+}
+
+
+int fmi2_xml_parse_dependencies(fmi2_xml_parser_context_t *context,
+                                fmi2_xml_elm_enu_t parentElmID,
+                                                               fmi2_xml_dependencies_t* deps)
+{
+    fmi2_xml_model_description_t* md = context->modelDescription;
+    fmi2_xml_model_structure_t* ms = md->modelStructure;
+
+    const char* listInd;
+    const char* listKind;
+    size_t numDepInd = 0;
+    size_t numDepKind = 0;
+    size_t totNumDep = jm_vector_get_size(size_t)(&deps->dependencyIndex);
+
+    /*  <xs:attribute name="dependencies">
+            <xs:simpleType>
+                <xs:list itemType="xs:unsignedInt"/>
+            </xs:simpleType>
+        </xs:attribute> */
+    if(fmi2_xml_get_attr_str(context, fmi2_xml_elmID_Unknown, fmi_attr_id_dependencies, 0, &listInd)) {
+        ms->isValidFlag = 0;
+        return 0;
+    }
+    if(listInd) {
+         const char* cur = listInd;
+         int ind;
+         while(*cur) {
+             char ch = *cur;
+             while((ch ==' ') || (ch == '\t') || (ch =='\n') || (ch == '\r')) {
+                 cur++; ch = *cur;
+                 if(!ch) break;
+             }
+             if(!ch) break;
+             if(sscanf(cur, "%d", &ind) != 1) {
+                 fmi2_xml_parse_error(context, "XML element 'Unknown': could not parse item %d in the list for attribute 'dependencies'",
+                     numDepInd);
+                ms->isValidFlag = 0;
+                return 0;
+             }
+             if(ind < 1) {
+                 fmi2_xml_parse_error(context, "XML element 'Unknown': item %d=%d is less than one in the list for attribute 'dependencies'",
+                     numDepInd, ind);
+                ms->isValidFlag = 0;
+                return 0;
+             }
+             if(!jm_vector_push_back(size_t)(&deps->dependencyIndex, (size_t)ind)) {
+                fmi2_xml_parse_fatal(context, "Could not allocate memory");
+                return -1;
+            }
+             while((*cur >= '0') && (*cur <= '9')) cur++;
+             numDepInd++;
+         }
+    }
+
+    /*
+        <xs:attribute name="dependenciesKind">
+            <xs:simpleType>
+                <xs:list>
+                    <xs:simpleType>
+                        <xs:restriction base="xs:normalizedString">
+                            <xs:enumeration value="dependent"/>
+                            <xs:enumeration value="constant"/>
+                            <xs:enumeration value="fixed"/>
+                            <xs:enumeration value="tunable"/>
+                            <xs:enumeration value="discrete"/>
+                        </xs:restriction>
+                    </xs:simpleType>
+                </xs:list>
+            </xs:simpleType>
+        </xs:attribute>
+        */
+    if(fmi2_xml_get_attr_str(context, fmi2_xml_elmID_Unknown, fmi_attr_id_dependenciesKind, 0, &listKind)) {
+        ms->isValidFlag = 0;
+        return 0;
+    }
+    if(listKind) {
+         const char* cur = listKind;
+         char kind;
+         while(*cur) {
+             char ch = *cur;
+             while(ch && ((ch ==' ') || (ch == '\t') || (ch =='\n') || (ch == '\r'))) {
+                 cur++; ch = *cur;
+             }
+             if(!ch) break;
+             if(strncmp("dependent", cur, 9) == 0) {
+                 kind = fmi2_dependency_factor_kind_dependent;
+                 cur+=9;
+             }
+             else if(strncmp("constant", cur, 8) == 0) {
+                 kind = fmi2_dependency_factor_kind_constant;
+                 cur+=8;
+             }
+             else if(strncmp("fixed", cur, 5) == 0) {
+                 kind = fmi2_dependency_factor_kind_fixed;
+                 cur+=5;
+             }
+             else if(strncmp("tunable", cur, 7) == 0) {
+                 kind = fmi2_dependency_factor_kind_tunable;
+                 cur+=7;
+             }
+             else if(strncmp("discrete", cur, 8) == 0) {
+                  kind = fmi2_dependency_factor_kind_discrete;
+                 cur+=8;
+             }
+             else {
+                 fmi2_xml_parse_error(context, "XML element 'Unknown': could not parse item %d in the list for attribute 'dependenciesKind'",
+                     numDepKind);
+                 ms->isValidFlag = 0;
+                 return 0;
+             }
+             if (parentElmID == fmi2_xml_elmID_InitialUnknowns) {
+                if (kind == fmi2_dependency_factor_kind_fixed) {
+                    fmi2_xml_parse_error(context, "XML element 'Unknown' within 'InitialUnknowns': 'fixed' is not allowed in list for attribute 'dependenciesKind'; setting to 'dependent'");
+                    kind = fmi2_dependency_factor_kind_dependent;
+                }
+                else if (!(kind == fmi2_dependency_factor_kind_dependent || kind == fmi2_dependency_factor_kind_constant)) {
+                    fmi2_xml_parse_error(context, "XML element 'Unknown' within 'InitialUnknowns': only 'dependent' and 'constant' allowed in list for attribute 'dependenciesKind'");
+                    ms->isValidFlag = 0;
+                    return 0;
+                }
+             }
+             if(!jm_vector_push_back(char)(&deps->dependencyFactorKind, kind)) {
+                fmi2_xml_parse_fatal(context, "Could not allocate memory");
+                return -1;
+            }
+             numDepKind++;
+         }
+    }
+    if(listInd && listKind) {
+        /* both lists are present - the number of items must match */
+        if(numDepInd != numDepKind) {
+            fmi2_xml_parse_error(context, "XML element 'Unknown': different number of items (%u and %u) in the lists for 'dependencies' and 'dependenciesKind'",
+                                 numDepInd, numDepKind);
+            ms->isValidFlag = 0;
+            return 0;
+        }
+    }
+    else if(listInd) {
+        /* only Dependencies are present, set all kinds to dependent */
+        char kind = fmi2_dependency_factor_kind_dependent;
+        if(jm_vector_reserve(char)(&deps->dependencyFactorKind,totNumDep + numDepInd) < totNumDep + numDepInd) {
+            fmi2_xml_parse_fatal(context, "Could not allocate memory");
+            return -1;
+        }
+        for(;numDepKind < numDepInd; numDepKind++)
+            jm_vector_push_back(char)(&deps->dependencyFactorKind, kind);
+    }
+    else if(listKind) {
+        fmi2_xml_parse_error(context, "XML element 'Unknown': if `dependenciesKind` attribute is present then the `dependencies` attribute must be present also.");
+        ms->isValidFlag = 0;
+        return 0;
+    }
+    else {
+        /* Dependencies are not provided. Put zero index/dependent to indicate that full row must be considered. */
+        numDepInd = numDepKind = 1;
+        if(!jm_vector_push_back(char)(&deps->dependencyFactorKind, fmi2_dependency_factor_kind_dependent) ||
+         !jm_vector_push_back(size_t)(&deps->dependencyIndex, 0)
+          ) {
+            fmi2_xml_parse_fatal(context, "Could not allocate memory");
+            return -1;
+        }
+    }
+    if(!jm_vector_push_back(size_t)(&deps->startIndex, totNumDep + numDepInd)) {
+        fmi2_xml_parse_fatal(context, "Could not allocate memory");
+        return -1;
+    }
+
+    return 0;
+}
+
+
+int fmi2_xml_parse_unknown(fmi2_xml_parser_context_t *context,
+                           fmi2_xml_elm_enu_t parentElmID,
+                           jm_vector(jm_voidp) *destVarList,
+                           fmi2_xml_dependencies_t* deps)
+{
+    fmi2_xml_model_description_t* md = context->modelDescription;
+    fmi2_xml_model_structure_t* ms = md->modelStructure;
+
+    unsigned int index;
+    fmi2_xml_variable_t* variable;
+
+    /* <xs:attribute name="index" type="xs:unsignedInt" use="required"> */
+    if (fmi2_xml_set_attr_uint(context, fmi2_xml_elmID_Unknown, fmi_attr_id_index, 1, &index, 0)) return -1;
+    index--; /* Convert from one- to zero-based indexing */
+
+    /* Ok to just check upper bound since index is unsigned. */
+    if (index >= jm_vector_get_size(jm_voidp)(md->variablesOrigOrder)) {
+        fmi2_xml_parse_error(context, "The index attribute must have a value between 1 and the number of model variables.");
+        ms->isValidFlag = 0;
+        return -1;
+    }
+    variable = (fmi2_xml_variable_t*)jm_vector_get_item(jm_voidp)(md->variablesOrigOrder, index);
+
+    if (!jm_vector_push_back(jm_voidp)(destVarList, variable)) {
+        fmi2_xml_parse_fatal(context, "Could not allocate memory");
+        ms->isValidFlag = 0;
+        return -1;
+    }
+
+    return fmi2_xml_parse_dependencies(context, parentElmID, deps);
+}
+
+
+/*int fmi2_xml_handle_OutputUnknown(fmi2_xml_parser_context_t *context, const char* data) {*/
+int fmi2_xml_handle_Unknown(fmi2_xml_parser_context_t *context, const char* data) {
+    if(!data) {
+        fmi2_xml_model_description_t* md = context->modelDescription;
+        fmi2_xml_model_structure_t* ms = md->modelStructure;
+
+        return fmi2_xml_parse_unknown(context, fmi2_xml_elmID_Outputs, &ms->outputs, ms->outputDeps);
+    }
+    else {
+    }
+    return 0;
+}
+
+int fmi2_xml_handle_DerivativeUnknown(fmi2_xml_parser_context_t *context, const char* data) {
+    if(!data) {
+        fmi2_xml_model_description_t* md = context->modelDescription;
+        fmi2_xml_model_structure_t* ms = md->modelStructure;
+        int status = fmi2_xml_parse_unknown(context, fmi2_xml_elmID_Derivatives, &ms->derivatives, ms->derivativeDeps);
+
+        if (status) {
+            return status;
+        } else {
+            fmi2_xml_real_variable_t *der = (fmi2_xml_real_variable_t*) jm_vector_get_last(jm_voidp)(&ms->derivatives);
+            if (!fmi2_xml_get_real_variable_derivative_of(der)) {
+                ms->isValidFlag = 0;
+                fmi2_xml_parse_error(context,
+                        "The state derivative '%s' does not specify the state variable that it is a derivative of.",
+                        fmi2_xml_get_variable_name((fmi2_xml_variable_t *) der));
+                return -1;
+            }
+        }
+    }
+    return 0;
+}
+
+int fmi2_xml_handle_DiscreteStateUnknown(fmi2_xml_parser_context_t *context, const char* data) {
+    if(!data) {
+        fmi2_xml_model_description_t* md = context->modelDescription;
+        fmi2_xml_model_structure_t* ms = md->modelStructure;
+
+        return fmi2_xml_parse_unknown(context, fmi2_xml_elmID_DiscreteStates, &ms->discreteStates, ms->discreteStateDeps);
+    }
+    else {
+    }
+    return 0;
+}
+
+int fmi2_xml_handle_InitialUnknown(fmi2_xml_parser_context_t *context, const char* data) {
+    if(!data) {
+        fmi2_xml_model_description_t* md = context->modelDescription;
+        fmi2_xml_model_structure_t* ms = md->modelStructure;
+
+        return fmi2_xml_parse_unknown(context, fmi2_xml_elmID_InitialUnknowns, &ms->initialUnknowns, ms->initialUnknownDeps);
+    }
+    else {
+    }
+    return 0;
+}