/* 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 . */ #include #include #include #include #include #include "fmi2_import_impl.h" /** \brief Collect model information by counting the number of variables with specific properties and fillinf in fmi2_import_model_counts_t struct. \param fmu - An fmu object as returned by fmi2_import_parse_xml(). \param counts - a pointer to a preallocated struct. */ void fmi2_import_collect_model_counts(fmi2_import_t* fmu, fmi2_import_model_counts_t* counts) { jm_vector(jm_voidp)* vars = fmi2_xml_get_variables_original_order(fmu->md); size_t nv, i; memset(counts,0,sizeof(fmi2_import_model_counts_t)); if(!vars) return; nv = jm_vector_get_size(jm_voidp)(vars); for(i = 0; i< nv; i++) { fmi2_xml_variable_t* var = (fmi2_xml_variable_t*)jm_vector_get_item(jm_voidp)(vars, i); switch (fmi2_xml_get_variability(var)) { case fmi2_variability_enu_constant: counts->num_constants++; break; case fmi2_variability_enu_fixed: counts->num_fixed++; break; case fmi2_variability_enu_tunable: counts->num_tunable++; break; case fmi2_variability_enu_discrete: counts->num_discrete++; break; case fmi2_variability_enu_continuous: counts->num_continuous++; break; default: assert(0); } switch(fmi2_xml_get_causality(var)) { case fmi2_causality_enu_parameter: counts->num_parameters++; break; case fmi2_causality_enu_calculated_parameter: counts->num_calculated_parameters++; break; case fmi2_causality_enu_input: counts->num_inputs++; break; case fmi2_causality_enu_output: counts->num_outputs++; break; case fmi2_causality_enu_local: counts->num_local++; break; case fmi2_causality_enu_independent: counts->num_local++; break; default: assert(0); } switch(fmi2_xml_get_variable_base_type(var)) { case fmi2_base_type_real: counts->num_real_vars++; break; case fmi2_base_type_int: counts->num_integer_vars++; break; case fmi2_base_type_bool: counts->num_bool_vars++; break; case fmi2_base_type_str: counts->num_string_vars++; break; case fmi2_base_type_enum: counts->num_enum_vars++; break; default: assert(0); } } return; } void fmi2_import_expand_variable_references_impl(fmi2_import_t* fmu, const char* msgIn); void fmi2_import_expand_variable_references(fmi2_import_t* fmu, const char* msgIn, char* msgOut, size_t maxMsgSize) { fmi2_import_expand_variable_references_impl(fmu, msgIn); strncpy(msgOut, jm_vector_get_itemp(char)(&fmu->logMessageBufferExpanded,0),maxMsgSize); msgOut[maxMsgSize - 1] = '\0'; } /* Print msgIn into msgOut by expanding variable references of the form ## into variable names and replacing '##' with a single # */ void fmi2_import_expand_variable_references_impl(fmi2_import_t* fmu, const char* msgIn){ jm_vector(char)* msgOut = &fmu->logMessageBufferExpanded; fmi2_xml_model_description_t* md = fmu->md; jm_callbacks* callbacks = fmu->callbacks; char curCh; const char* firstRef; size_t i; /* next char index after curCh in msgIn*/ size_t msgLen = strlen(msgIn)+1; /* original message length including terminating 0 */ if(jm_vector_reserve(char)(msgOut, msgLen + 100) < msgLen + 100) { jm_log(fmu->callbacks,"LOGGER", jm_log_level_warning, "Could not allocate memory for the log message"); jm_vector_resize(char)(msgOut, 6); memcpy(jm_vector_get_itemp(char)(msgOut,0),"ERROR",6); /* at least 16 chars are always there */ return; } /* check if there are any refs at all and copy the head of the string without references */ firstRef = strchr(msgIn, '#'); if(firstRef) { i = firstRef - msgIn; jm_vector_resize(char)(msgOut, i); if(i) { memcpy(jm_vector_get_itemp(char)(msgOut, 0), msgIn, i); } curCh = msgIn[i++]; } else { jm_vector_resize(char)(msgOut, msgLen); memcpy(jm_vector_get_itemp(char)(msgOut, 0), msgIn, msgLen); return; } do { if (curCh!='#') { jm_vector_push_back(char)(msgOut, curCh); /* copy in to out */ } else if(msgIn[i] == '#') { jm_vector_push_back(char)(msgOut, '#'); i++; /* skip the second # */ } else { unsigned int bufVR; fmi2_value_reference_t vr; char typeChar = msgIn[i++]; size_t pastePos = jm_vector_get_size(char)(msgOut); fmi2_base_type_enu_t baseType; size_t num_digits; fmi2_xml_variable_t* var; const char* name; size_t nameLen; switch(typeChar) { case 'r': baseType = fmi2_base_type_real; break; case 'i': baseType = fmi2_base_type_int; break; case 'b': baseType = fmi2_base_type_bool; break; case 's': baseType = fmi2_base_type_str; break; default: jm_vector_push_back(char)(msgOut, 0); jm_log(callbacks,"LOGGER", jm_log_level_warning, "Expected type specification character 'r', 'i', 'b' or 's' in log message here: '%s'", jm_vector_get_itemp(char)(msgOut,0)); jm_vector_resize(char)(msgOut, msgLen); memcpy(jm_vector_get_itemp(char)(msgOut,0),msgIn,msgLen); return; } curCh = msgIn[i++]; while( isdigit(curCh) ) { jm_vector_push_back(char)(msgOut, curCh); curCh = msgIn[i++]; } num_digits = jm_vector_get_size(char)(msgOut) - pastePos; jm_vector_push_back(char)(msgOut, 0); if(num_digits == 0) { jm_log(callbacks,"LOGGER", jm_log_level_warning, "Expected value reference in log message here: '%s'", jm_vector_get_itemp(char)(msgOut,0)); jm_vector_resize(char)(msgOut, msgLen); jm_vector_resize(char)(msgOut, msgLen); memcpy(jm_vector_get_itemp(char)(msgOut,0),msgIn,msgLen); return; } else if(curCh != '#') { jm_log(callbacks,"LOGGER", jm_log_level_warning, "Expected terminating '#' in log message here: '%s'", jm_vector_get_itemp(char)(msgOut,0)); jm_vector_resize(char)(msgOut, msgLen); jm_vector_resize(char)(msgOut, msgLen); memcpy(jm_vector_get_itemp(char)(msgOut,0),msgIn,msgLen); return; } if(sscanf(jm_vector_get_itemp(char)(msgOut, pastePos), "%u",&bufVR) != 1) { jm_log(callbacks,"LOGGER", jm_log_level_warning, "Could not decode value reference in log message here: '%s'", jm_vector_get_itemp(char)(msgOut,0)); jm_vector_resize(char)(msgOut, msgLen); jm_vector_resize(char)(msgOut, msgLen); memcpy(jm_vector_get_itemp(char)(msgOut,0),msgIn,msgLen); return; } vr = bufVR; var = fmi2_xml_get_variable_by_vr(md,baseType,vr); if(!var) { jm_log(callbacks,"LOGGER", jm_log_level_warning, "Could not find variable referenced in log message here: '%s'", jm_vector_get_itemp(char)(msgOut,0)); jm_vector_resize(char)(msgOut, msgLen); jm_vector_resize(char)(msgOut, msgLen); memcpy(jm_vector_get_itemp(char)(msgOut,0),msgIn,msgLen); return; } name = fmi2_xml_get_variable_name(var); nameLen = strlen(name); if(jm_vector_resize(char)(msgOut, pastePos + nameLen) != pastePos + nameLen) { jm_log(callbacks,"LOGGER", jm_log_level_warning, "Could not allocate memory for the log message"); jm_vector_resize(char)(msgOut, msgLen); jm_vector_resize(char)(msgOut, msgLen); memcpy(jm_vector_get_itemp(char)(msgOut,0),msgIn,msgLen); return; }; memcpy(jm_vector_get_itemp(char)(msgOut, pastePos), name, nameLen); } curCh = msgIn[i++]; } while (curCh); jm_vector_push_back(char)(msgOut, 0); } void fmi2_log_forwarding(fmi2_component_environment_t c, fmi2_string_t instanceName, fmi2_status_t status, fmi2_string_t category, fmi2_string_t message, ...) { va_list args; va_start (args, message); fmi2_log_forwarding_v(c, instanceName, status, category, message, args); va_end (args); } void fmi2_log_forwarding_v(fmi2_component_environment_t c, fmi2_string_t instanceName, fmi2_status_t status, fmi2_string_t category, fmi2_string_t message, va_list args) { #define BUFSIZE JM_MAX_ERROR_MESSAGE_SIZE char buffer[BUFSIZE], *buf, *curp, *msg; const char* statusStr; fmi2_import_t* fmu = (fmi2_import_t*)c; jm_callbacks* cb; jm_log_level_enu_t logLevel; if(fmu) { cb = fmu->callbacks; buf = jm_vector_get_itemp(char)(&fmu->logMessageBufferCoded,0); } else { cb = jm_get_default_callbacks(); buf = buffer; } logLevel = cb->log_level; switch(status) { case fmi2_status_discard: case fmi2_status_pending: case fmi2_status_ok: logLevel = jm_log_level_info; break; case fmi2_status_warning: logLevel = jm_log_level_warning; break; case fmi2_status_error: logLevel = jm_log_level_error; break; case fmi2_status_fatal: default: logLevel = jm_log_level_fatal; } if(logLevel > cb->log_level) return; curp = buf; *curp = 0; if(category) { curp += jm_snprintf(curp, 100, "[%s]", category); } statusStr = fmi2_status_to_string(status); curp += jm_snprintf(curp, 200, "[FMU status:%s] ", statusStr); if(fmu) { int bufsize = jm_vector_get_size(char)(&fmu->logMessageBufferCoded); int len; #ifdef JM_VA_COPY va_list argscp; JM_VA_COPY(argscp, args); #endif len = jm_vsnprintf(curp, bufsize -(curp-buf), message, args); if(len > (bufsize -(curp-buf+1))) { int offset = (curp-buf); len = jm_vector_resize(char)(&fmu->logMessageBufferCoded, len + offset + 1) - offset; buf = jm_vector_get_itemp(char)(&fmu->logMessageBufferCoded,0); curp = buf + offset; #ifdef JM_VA_COPY jm_vsnprintf(curp, len, message, argscp); #endif } #ifdef JM_VA_COPY va_end(argscp); #endif fmi2_import_expand_variable_references(fmu, buf, cb->errMessageBuffer,JM_MAX_ERROR_MESSAGE_SIZE); msg = jm_vector_get_itemp(char)(&fmu->logMessageBufferExpanded,0); } else { jm_vsnprintf(curp, BUFSIZE -(curp-buf), message, args); strncpy(cb->errMessageBuffer, buf, JM_MAX_ERROR_MESSAGE_SIZE); cb->errMessageBuffer[JM_MAX_ERROR_MESSAGE_SIZE - 1] = '\0'; msg = cb->errMessageBuffer; } if(cb->logger) { cb->logger(cb, instanceName, logLevel, msg); } } void fmi2_default_callback_logger(fmi2_component_environment_t c, fmi2_string_t instanceName, fmi2_status_t status, fmi2_string_t category, fmi2_string_t message, ...) { va_list args; char buf[BUFSIZE], *curp; va_start (args, message); curp = buf; *curp = 0; if(instanceName) { curp += jm_snprintf(curp, 200, "[%s]", instanceName); } if(category) { curp += jm_snprintf(curp, 200, "[%s]", category); } fprintf(stdout, "%s[status=%s]", buf, fmi2_status_to_string(status)); vfprintf (stdout, message, args); fprintf(stdout, "\n"); va_end (args); } void fmi2_logger(jm_callbacks* cb, jm_string module, jm_log_level_enu_t log_level, jm_string message) { fmi2_callback_functions_t* c = (fmi2_callback_functions_t*)cb->context; fmi2_status_t status; if(!c ||!c->logger) return; if(log_level > jm_log_level_all) { assert(0); status = fmi2_status_error; } else if(log_level >= jm_log_level_info) status = fmi2_status_ok; else if(log_level >= jm_log_level_warning) status = fmi2_status_warning; else if(log_level >= jm_log_level_error) status = fmi2_status_error; else if(log_level >= jm_log_level_fatal) status = fmi2_status_fatal; else { status = fmi2_status_ok; } c->logger( c, module, status, jm_log_level_to_string(log_level), message); } void fmi2_import_init_logger(jm_callbacks* cb, fmi2_callback_functions_t* fmiCallbacks) { cb->logger = fmi2_logger; cb->context = fmiCallbacks; }