X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=org.simantics.fmil.core%2Fnative%2FFMUSimulator%2Fsrc%2Fsim_support.c;h=5725147ad04c49315437a748e695fd285573564a;hb=HEAD;hp=71915f02785ee474d3226183615fcf627f7d7bb9;hpb=a19f5279b32973dfc28ae084a8b7d20ae6aaac97;p=simantics%2Ffmil.git diff --git a/org.simantics.fmil.core/native/FMUSimulator/src/sim_support.c b/org.simantics.fmil.core/native/FMUSimulator/src/sim_support.c index 71915f0..5725147 100644 --- a/org.simantics.fmil.core/native/FMUSimulator/src/sim_support.c +++ b/org.simantics.fmil.core/native/FMUSimulator/src/sim_support.c @@ -1,477 +1,479 @@ -/* ------------------------------------------------------------------------- - * sim_support.c - * Functions used by both FMU simulators fmusim_me and fmusim_cs - * to parse command-line arguments, to unzip and load an fmu, - * to write CSV file, and more. - * Copyright 2011 QTronic GmbH. All rights reserved. - * -------------------------------------------------------------------------*/ - -#include -#include -#include -#include -#include -#include - -#ifdef FMI_COSIMULATION -#include "fmi_cs.h" -#else -#include "fmi_me.h" -#endif - -// linux/unix DSO loading -#ifndef _MSC_VER -#include -#endif - -#include "sim_support.h" -//#include "fmuExtract.h" - -#define MAX_PATH_SIZE 4096 - -#ifdef _MSC_VER -// fileName is an absolute path, e.g. C:\test\a.fmu -// or relative to the current dir, e.g. ..\test\a.fmu -// Does not check for existence of the file -static char* getFmuPath(const char* fileName){ - char pathName[MAX_PATH_SIZE]; - int n = GetFullPathName(fileName, MAX_PATH, pathName, NULL); - return n ? strdup(pathName) : NULL; -} - -int tmpPathRequests = 0; -static char* getTmpPath() { - char tmpPath[BUFSIZE]; - if(! GetTempPath(BUFSIZE, tmpPath)) { - printf ("error: Could not find temporary disk space\n"); - return NULL; - } - if(tmpPathRequests % 2 == 0) { - strcat(tmpPath, "fmu\\"); - tmpPathRequests++; - } else { - strcat(tmpPath, "fmu2\\"); - tmpPathRequests = 0; - } - - makedir(tmpPath); - - return strdup(tmpPath); -} -#endif - -static void* getAdr(int* s, FMU *fmu, const char* functionName){ - char name[BUFSIZE]; - void* fp; - sprintf(name, "%s_%s", getModelIdentifier(fmu->modelDescription), functionName); -#ifdef _MSC_VER - fp = GetProcAddress(fmu->dllHandle, name); -#else - fp = dlsym(fmu->dllHandle, name); -#endif - if (!fp) { - printf ("warning: Function %s not found in dll\n", name); - *s = 0; // mark dll load as 'failed' - } - return fp; -} - -// Load the given dll and set function pointers in fmu -// Return 0 to indicate failure -static int loadDll(const char* dllPath, FMU *fmu) { - int s = 1; -#ifdef _MSC_VER - HANDLE h = LoadLibraryEx(dllPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); - if (!h) { - int error = GetLastError(); - printf("error %d: Could not load %s\n", error, dllPath); - return 0; // failure - } - fmu->dllHandle = h; -#else - void* h = dlopen(dllPath, RTLD_LAZY); - if (!h) { - char* error = dlerror(); - printf("error %s: Could not load %s\n", error, dllPath); - return 0; // failure - } - fmu->dllHandle = h; -#endif - -#ifdef FMI_COSIMULATION - fmu->getTypesPlatform = (fGetTypesPlatform) getAdr(&s, fmu, "fmiGetTypesPlatform"); - if (s==0) { - s = 1; // work around bug for FMUs exported using Dymola 2012 and SimulationX 3.x - fmu->getTypesPlatform = (fGetTypesPlatform) getAdr(&s, fmu, "fmiGetModelTypesPlatform"); - if (s==1) printf(" using fmiGetModelTypesPlatform instead\n", dllPath); - } - fmu->instantiateSlave = (fInstantiateSlave) getAdr(&s, fmu, "fmiInstantiateSlave"); - fmu->initializeSlave = (fInitializeSlave) getAdr(&s, fmu, "fmiInitializeSlave"); - fmu->terminateSlave = (fTerminateSlave) getAdr(&s, fmu, "fmiTerminateSlave"); - fmu->resetSlave = (fResetSlave) getAdr(&s, fmu, "fmiResetSlave"); - fmu->freeSlaveInstance = (fFreeSlaveInstance) getAdr(&s, fmu, "fmiFreeSlaveInstance"); - fmu->setRealInputDerivatives = (fSetRealInputDerivatives) getAdr(&s, fmu, "fmiSetRealInputDerivatives"); - fmu->getRealOutputDerivatives = (fGetRealOutputDerivatives) getAdr(&s, fmu, "fmiGetRealOutputDerivatives"); - fmu->cancelStep = (fCancelStep) getAdr(&s, fmu, "fmiCancelStep"); - fmu->doStep = (fDoStep) getAdr(&s, fmu, "fmiDoStep"); - // SimulationX 3.4 and 3.5 do not yet export getStatus and getXStatus: do not count this as failure here - fmu->getStatus = (fGetStatus) getAdr(&x, fmu, "fmiGetStatus"); - fmu->getRealStatus = (fGetRealStatus) getAdr(&x, fmu, "fmiGetRealStatus"); - fmu->getIntegerStatus = (fGetIntegerStatus) getAdr(&x, fmu, "fmiGetIntegerStatus"); - fmu->getBooleanStatus = (fGetBooleanStatus) getAdr(&x, fmu, "fmiGetBooleanStatus"); - fmu->getStringStatus = (fGetStringStatus) getAdr(&x, fmu, "fmiGetStringStatus"); - -#else // FMI for Model Exchange 1.0 - fmu->getModelTypesPlatform = (fGetModelTypesPlatform) getAdr(&s, fmu, "fmiGetModelTypesPlatform"); - fmu->instantiateModel = (fInstantiateModel) getAdr(&s, fmu, "fmiInstantiateModel"); - fmu->freeModelInstance = (fFreeModelInstance) getAdr(&s, fmu, "fmiFreeModelInstance"); - fmu->setTime = (fSetTime) getAdr(&s, fmu, "fmiSetTime"); - fmu->setContinuousStates = (fSetContinuousStates)getAdr(&s, fmu, "fmiSetContinuousStates"); - fmu->completedIntegratorStep = (fCompletedIntegratorStep)getAdr(&s, fmu, "fmiCompletedIntegratorStep"); - fmu->initialize = (fInitialize) getAdr(&s, fmu, "fmiInitialize"); - fmu->getDerivatives = (fGetDerivatives) getAdr(&s, fmu, "fmiGetDerivatives"); - fmu->getEventIndicators = (fGetEventIndicators) getAdr(&s, fmu, "fmiGetEventIndicators"); - fmu->eventUpdate = (fEventUpdate) getAdr(&s, fmu, "fmiEventUpdate"); - fmu->getContinuousStates = (fGetContinuousStates)getAdr(&s, fmu, "fmiGetContinuousStates"); - fmu->getNominalContinuousStates = (fGetNominalContinuousStates)getAdr(&s, fmu, "fmiGetNominalContinuousStates"); - fmu->getStateValueReferences = (fGetStateValueReferences)getAdr(&s, fmu, "fmiGetStateValueReferences"); - fmu->terminate = (fTerminate) getAdr(&s, fmu, "fmiTerminate"); -#endif - fmu->getVersion = (fGetVersion) getAdr(&s, fmu, "fmiGetVersion"); - fmu->setDebugLogging = (fSetDebugLogging) getAdr(&s, fmu, "fmiSetDebugLogging"); - fmu->setReal = (fSetReal) getAdr(&s, fmu, "fmiSetReal"); - fmu->setInteger = (fSetInteger) getAdr(&s, fmu, "fmiSetInteger"); - fmu->setBoolean = (fSetBoolean) getAdr(&s, fmu, "fmiSetBoolean"); - fmu->setString = (fSetString) getAdr(&s, fmu, "fmiSetString"); - fmu->getReal = (fGetReal) getAdr(&s, fmu, "fmiGetReal"); - fmu->getInteger = (fGetInteger) getAdr(&s, fmu, "fmiGetInteger"); - fmu->getBoolean = (fGetBoolean) getAdr(&s, fmu, "fmiGetBoolean"); - fmu->getString = (fGetString) getAdr(&s, fmu, "fmiGetString"); - return s; -} - -/*static void printModelDescription(ModelDescription* md){ - Element* e = (Element*)md; - int i; - printf("%s\n", elmNames[e->type]); - for (i=0; in; i+=2) - printf(" %s=%s\n", e->attributes[i], e->attributes[i+1]); -#ifdef FMI_COSIMULATION - if (!md->cosimulation) { - printf("error: No Implementation element found in model description. This FMU is not for Co-Simulation.\n"); - exit(EXIT_FAILURE); - } - e = md->cosimulation->capabilities; - printf("%s\n", elmNames[e->type]); - for (i=0; in; i+=2) - printf(" %s=%s\n", e->attributes[i], e->attributes[i+1]); -#endif // FMI_COSIMULATION -} -*/ - -/* - * return: 1 for successful laod or number for error. - * -1. FMU path not found - * -2. Unzip failed - * -3. Loading model description failed - * -4. FMU dll load failed - */ -/* -int loadFMU(FMU *fmu, const char* fmuFileName, const char* tmpPath) { - char* fmuPath; - char* xmlPath; - char* dllPath; - unsigned old_clock = clock(); - unsigned current_clock = 0;//will be assigned later - - // get absolute path to FMU, NULL if not found - fmuPath = getFmuPath(fmuFileName); - if (!fmuPath) return -1; // path not found - - // unzip the FMU to the tmpPath directory - if (unzip(fmuPath, tmpPath)) return -2; // unzip failed - - // parse tmpPath\modelDescription.xml - xmlPath = calloc(sizeof(char), strlen(tmpPath) + strlen(XML_FILE) + 1); - sprintf(xmlPath, "%s%s", tmpPath, XML_FILE); - fmu->modelDescription = parse(xmlPath); - free(xmlPath); - if (!fmu->modelDescription) return -3; // loading model description failed - - // printModelDescription(fmu.modelDescription); - // fflush(stdout); - - // load the FMU dll - dllPath = calloc(sizeof(char), strlen(tmpPath) + strlen(DLL_DIR) - + strlen( getModelIdentifier(fmu->modelDescription)) + strlen(".dll") + 1); - sprintf(dllPath,"%s%s%s.dll", tmpPath, DLL_DIR, getModelIdentifier(fmu->modelDescription)); - if (!loadDll(dllPath, fmu)) return -4; // loading dll failed - - free(dllPath); - free(fmuPath); - - return 1; -} -*/ - -static void doubleToCommaString(char* buffer, double r){ - char* comma; - sprintf(buffer, "%.16g", r); - comma = strchr(buffer, '.'); - if (comma) *comma = ','; -} - -// output time and all non-alias variables in CSV format -// if separator is ',', columns are separated by ',' and '.' is used for floating-point numbers. -// otherwise, the given separator (e.g. ';' or '\t') is to separate columns, and ',' is used -// as decimal dot in floating-point numbers. -/* -void outputRow(FMU *fmu, fmiComponent c, double time, FILE* file, char separator, boolean header) { - int k; - fmiReal r; - fmiInteger i; - fmiBoolean b; - fmiString s; - fmiValueReference vr; - ScalarVariable** vars = fmu->modelDescription->modelVariables; - char buffer[32]; - - // print first column - if (header) - fprintf(file, "time"); - else { - if (separator==',') - fprintf(file, "%.16g", time); - else { - // separator is e.g. ';' or '\t' - doubleToCommaString(buffer, time); - fprintf(file, "%s", buffer); - } - } - - // print all other columns - for (k=0; vars[k]; k++) { - ScalarVariable* sv = vars[k]; - if (getAlias(sv)!=enu_noAlias) continue; - if (header) { - // output names only - if (separator==',') { - // treat array element, e.g. print a[1, 2] as a[1.2] - char* s = getName(sv); - fprintf(file, "%c", separator); - while (*s) { - if (*s!=' ') fprintf(file, "%c", *s==',' ? '.' : *s); - s++; - } - } - else - fprintf(file, "%c%s", separator, getName(sv)); - } - else { - // output values - vr = getValueReference(sv); - switch (sv->typeSpec->type){ - case elm_Real: - fmu->getReal(c, &vr, 1, &r); - if (separator==',') - fprintf(file, ",%.16g", r); - else { - // separator is e.g. ';' or '\t' - doubleToCommaString(buffer, r); - fprintf(file, "%c%s", separator, buffer); - } - break; - case elm_Integer: - case elm_Enumeration: - fmu->getInteger(c, &vr, 1, &i); - fprintf(file, "%c%d", separator, i); - break; - case elm_Boolean: - fmu->getBoolean(c, &vr, 1, &b); - fprintf(file, "%c%d", separator, b); - break; - case elm_String: - fmu->getString(c, &vr, 1, &s); - fprintf(file, "%c%s", separator, s); - break; - default: - fprintf(file, "%cNoValueForType=%d", separator,sv->typeSpec->type); - } - } - } // for - - // terminate this row - fprintf(file, "\n"); -} -*/ - -static const char* fmiStatusToString(fmiStatus status){ - switch (status){ - case fmiOK: return "ok"; - case fmiWarning: return "warning"; - case fmiDiscard: return "discard"; - case fmiError: return "error"; - case fmiFatal: return "fatal"; -#ifdef FMI_COSIMULATION - case fmiPending: return "fmiPending"; -#endif - default: return "?"; - } -} - -// search a fmu for the given variable -// return NULL if not found or vr = fmiUndefinedValueReference -static ScalarVariable* getSV(FMU* fmu, char type, fmiValueReference vr) { - int i; - Elm tp; - ScalarVariable** vars = fmu->modelDescription->modelVariables; - if (vr==fmiUndefinedValueReference) return NULL; - switch (type) { - case 'r': tp = elm_Real; break; - case 'i': tp = elm_Integer; break; - case 'b': tp = elm_Boolean; break; - case 's': tp = elm_String; break; - } - for (i=0; vars[i]; i++) { - ScalarVariable* sv = vars[i]; - if (vr==getValueReference(sv) && tp==sv->typeSpec->type) - return sv; - } - return NULL; -} - -// replace e.g. #r1365# by variable name and ## by # in message -// copies the result to buffer -static void replaceRefsInMessage(const char* msg, char* buffer, int nBuffer, FMU* fmu){ - int i=0; // position in msg - int k=0; // position in buffer - int n; - char c = msg[i]; - while (c!='\0' && k < nBuffer) { - if (c!='#') { - buffer[k++]=c; - i++; - c = msg[i]; - } - else { - char* end = strchr(const_cast(msg+i+1), '#'); - if (!end) { - printf("unmatched '#' in '%s'\n", msg); - buffer[k++]='#'; - break; - } - n = end - (msg+i); - if (n==1) { - // ## detected, output # - buffer[k++]='#'; - i += 2; - c = msg[i]; - } - else { - char type = msg[i+1]; // one of ribs - fmiValueReference vr; - int nvr = sscanf(msg+i+2, "%u", &vr); - if (nvr==1) { - // vr of type detected, e.g. #r12# - ScalarVariable* sv = getSV(fmu, type, vr); - const char* name = sv ? getName(sv) : "?"; - sprintf(buffer+k, "%s", name); - k += strlen(name); - i += (n+1); - c = msg[i]; - } - else { - // could not parse the number - printf("illegal value reference at position %d in '%s'\n", i+2, msg); - buffer[k++]='#'; - break; - } - } - } - } // while - buffer[k] = '\0'; -} - -#define MAX_MSG_SIZE 1000 -void fmuLogger(FMU *fmu, fmiComponent c, fmiString instanceName, fmiStatus status, - fmiString category, fmiString message, ...) { - char msg[MAX_MSG_SIZE]; -// char* copy; - va_list argp; - - // replace C format strings - va_start(argp, message); - vsprintf(msg, message, argp); - va_end(argp); - - // replace e.g. ## and #r12# -// copy = strdup(msg); -// replaceRefsInMessage(copy, msg, MAX_MSG_SIZE, fmu); -// free(copy); - - // print the final message - if (!instanceName) instanceName = "?"; - if (!category) category = "?"; - //printf("%s %s (%s): %s\n", fmiStatusToString(status), instanceName, category, msg); - printf("%s\n", message); -} - -int error(const char* message){ - printf("%s\n", message); - return 0; -} - -void parseArguments(int argc, char *argv[], char** fmuFileName, double* tEnd, double* h, int* loggingOn, char* csv_separator) { - // parse command line arguments - if (argc>1) { - *fmuFileName = argv[1]; - } - else { - printf("error: no fmu file\n"); - printHelp(argv[0]); - exit(EXIT_FAILURE); - } - if (argc>2) { - if (sscanf(argv[2],"%lf", tEnd) != 1) { - printf("error: The given end time (%s) is not a number\n", argv[2]); - exit(EXIT_FAILURE); - } - } - if (argc>3) { - if (sscanf(argv[3],"%lf", h) != 1) { - printf("error: The given stepsize (%s) is not a number\n", argv[3]); - exit(EXIT_FAILURE); - } - } - if (argc>4) { - if (sscanf(argv[4],"%d", loggingOn) != 1 || *loggingOn<0 || *loggingOn>1) { - printf("error: The given logging flag (%s) is not boolean\n", argv[4]); - exit(EXIT_FAILURE); - } - } - if (argc>5) { - if (strlen(argv[5]) != 1) { - printf("error: The given CSV separator char (%s) is not valid\n", argv[5]); - exit(EXIT_FAILURE); - } - switch (argv[5][0]) { - case 'c': *csv_separator = ','; break; // comma - case 's': *csv_separator = ';'; break; // semicolon - default: *csv_separator = argv[5][0]; break; // any other char - } - } - if (argc>6) { - printf("warning: Ignoring %d additional arguments: %s ...\n", argc-6, argv[6]); - printHelp(argv[0]); - } -} - -void printHelp(const char* fmusim) { - printf("command syntax: %s \n", fmusim); - printf(" .... path to FMU, relative to current dir or absolute, required\n"); - printf(" ......... end time of simulation, optional, defaults to 1.0 sec\n"); - printf(" ............ step size of simulation, optional, defaults to 0.1 sec\n"); - printf(" .... 1 to activate logging, optional, defaults to 0\n"); - printf(" . separator in csv file, optional, c for ';', s for';', defaults to c\n"); -} - +/* ------------------------------------------------------------------------- + * sim_support.c + * Functions used by both FMU simulators fmusim_me and fmusim_cs + * to parse command-line arguments, to unzip and load an fmu, + * to write CSV file, and more. + * Copyright 2011 QTronic GmbH. All rights reserved. + * -------------------------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include + +#ifdef FMI_COSIMULATION +#include "fmi_cs.h" +#else +#include "fmi_me.h" +#endif + +// linux/unix DSO loading +#ifndef _MSC_VER +#include +#endif + +#include "sim_support.h" +//#include "fmuExtract.h" + +#define MAX_PATH_SIZE 4096 + +#ifdef _MSC_VER + +#include +// fileName is an absolute path, e.g. C:\test\a.fmu +// or relative to the current dir, e.g. ..\test\a.fmu +// Does not check for existence of the file +static char* getFmuPath(const char* fileName){ + char pathName[MAX_PATH_SIZE]; + int n = GetFullPathName(fileName, MAX_PATH_SIZE, pathName, NULL); + return n ? strdup(pathName) : NULL; +} + +int tmpPathRequests = 0; +static char* getTmpPath() { + char tmpPath[BUFSIZE]; + if(! GetTempPath(BUFSIZE, tmpPath)) { + printf ("error: Could not find temporary disk space\n"); + return NULL; + } + if(tmpPathRequests % 2 == 0) { + strcat(tmpPath, "fmu\\"); + tmpPathRequests++; + } else { + strcat(tmpPath, "fmu2\\"); + tmpPathRequests = 0; + } + + makedir(tmpPath); + + return strdup(tmpPath); +} +#endif + +static void* getAdr(int* s, FMU *fmu, const char* functionName){ + char name[BUFSIZE]; + void* fp; + sprintf(name, "%s_%s", getModelIdentifier(fmu->modelDescription), functionName); +#ifdef _MSC_VER + fp = GetProcAddress(fmu->dllHandle, name); +#else + fp = dlsym(fmu->dllHandle, name); +#endif + if (!fp) { + printf ("warning: Function %s not found in dll\n", name); + *s = 0; // mark dll load as 'failed' + } + return fp; +} + +// Load the given dll and set function pointers in fmu +// Return 0 to indicate failure +static int loadDll(const char* dllPath, FMU *fmu) { + int s = 1; +#ifdef _MSC_VER + HANDLE h = LoadLibraryEx(dllPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); + if (!h) { + int error = GetLastError(); + printf("error %d: Could not load %s\n", error, dllPath); + return 0; // failure + } + fmu->dllHandle = h; +#else + void* h = dlopen(dllPath, RTLD_LAZY); + if (!h) { + char* error = dlerror(); + printf("error %s: Could not load %s\n", error, dllPath); + return 0; // failure + } + fmu->dllHandle = h; +#endif + +#ifdef FMI_COSIMULATION + fmu->getTypesPlatform = (fGetTypesPlatform) getAdr(&s, fmu, "fmiGetTypesPlatform"); + if (s==0) { + s = 1; // work around bug for FMUs exported using Dymola 2012 and SimulationX 3.x + fmu->getTypesPlatform = (fGetTypesPlatform) getAdr(&s, fmu, "fmiGetModelTypesPlatform"); + if (s==1) printf(" using fmiGetModelTypesPlatform instead\n", dllPath); + } + fmu->instantiateSlave = (fInstantiateSlave) getAdr(&s, fmu, "fmiInstantiateSlave"); + fmu->initializeSlave = (fInitializeSlave) getAdr(&s, fmu, "fmiInitializeSlave"); + fmu->terminateSlave = (fTerminateSlave) getAdr(&s, fmu, "fmiTerminateSlave"); + fmu->resetSlave = (fResetSlave) getAdr(&s, fmu, "fmiResetSlave"); + fmu->freeSlaveInstance = (fFreeSlaveInstance) getAdr(&s, fmu, "fmiFreeSlaveInstance"); + fmu->setRealInputDerivatives = (fSetRealInputDerivatives) getAdr(&s, fmu, "fmiSetRealInputDerivatives"); + fmu->getRealOutputDerivatives = (fGetRealOutputDerivatives) getAdr(&s, fmu, "fmiGetRealOutputDerivatives"); + fmu->cancelStep = (fCancelStep) getAdr(&s, fmu, "fmiCancelStep"); + fmu->doStep = (fDoStep) getAdr(&s, fmu, "fmiDoStep"); + // SimulationX 3.4 and 3.5 do not yet export getStatus and getXStatus: do not count this as failure here + fmu->getStatus = (fGetStatus) getAdr(&x, fmu, "fmiGetStatus"); + fmu->getRealStatus = (fGetRealStatus) getAdr(&x, fmu, "fmiGetRealStatus"); + fmu->getIntegerStatus = (fGetIntegerStatus) getAdr(&x, fmu, "fmiGetIntegerStatus"); + fmu->getBooleanStatus = (fGetBooleanStatus) getAdr(&x, fmu, "fmiGetBooleanStatus"); + fmu->getStringStatus = (fGetStringStatus) getAdr(&x, fmu, "fmiGetStringStatus"); + +#else // FMI for Model Exchange 1.0 + fmu->getModelTypesPlatform = (fGetModelTypesPlatform) getAdr(&s, fmu, "fmiGetModelTypesPlatform"); + fmu->instantiateModel = (fInstantiateModel) getAdr(&s, fmu, "fmiInstantiateModel"); + fmu->freeModelInstance = (fFreeModelInstance) getAdr(&s, fmu, "fmiFreeModelInstance"); + fmu->setTime = (fSetTime) getAdr(&s, fmu, "fmiSetTime"); + fmu->setContinuousStates = (fSetContinuousStates)getAdr(&s, fmu, "fmiSetContinuousStates"); + fmu->completedIntegratorStep = (fCompletedIntegratorStep)getAdr(&s, fmu, "fmiCompletedIntegratorStep"); + fmu->initialize = (fInitialize) getAdr(&s, fmu, "fmiInitialize"); + fmu->getDerivatives = (fGetDerivatives) getAdr(&s, fmu, "fmiGetDerivatives"); + fmu->getEventIndicators = (fGetEventIndicators) getAdr(&s, fmu, "fmiGetEventIndicators"); + fmu->eventUpdate = (fEventUpdate) getAdr(&s, fmu, "fmiEventUpdate"); + fmu->getContinuousStates = (fGetContinuousStates)getAdr(&s, fmu, "fmiGetContinuousStates"); + fmu->getNominalContinuousStates = (fGetNominalContinuousStates)getAdr(&s, fmu, "fmiGetNominalContinuousStates"); + fmu->getStateValueReferences = (fGetStateValueReferences)getAdr(&s, fmu, "fmiGetStateValueReferences"); + fmu->terminate = (fTerminate) getAdr(&s, fmu, "fmiTerminate"); +#endif + fmu->getVersion = (fGetVersion) getAdr(&s, fmu, "fmiGetVersion"); + fmu->setDebugLogging = (fSetDebugLogging) getAdr(&s, fmu, "fmiSetDebugLogging"); + fmu->setReal = (fSetReal) getAdr(&s, fmu, "fmiSetReal"); + fmu->setInteger = (fSetInteger) getAdr(&s, fmu, "fmiSetInteger"); + fmu->setBoolean = (fSetBoolean) getAdr(&s, fmu, "fmiSetBoolean"); + fmu->setString = (fSetString) getAdr(&s, fmu, "fmiSetString"); + fmu->getReal = (fGetReal) getAdr(&s, fmu, "fmiGetReal"); + fmu->getInteger = (fGetInteger) getAdr(&s, fmu, "fmiGetInteger"); + fmu->getBoolean = (fGetBoolean) getAdr(&s, fmu, "fmiGetBoolean"); + fmu->getString = (fGetString) getAdr(&s, fmu, "fmiGetString"); + return s; +} + +/*static void printModelDescription(ModelDescription* md){ + Element* e = (Element*)md; + int i; + printf("%s\n", elmNames[e->type]); + for (i=0; in; i+=2) + printf(" %s=%s\n", e->attributes[i], e->attributes[i+1]); +#ifdef FMI_COSIMULATION + if (!md->cosimulation) { + printf("error: No Implementation element found in model description. This FMU is not for Co-Simulation.\n"); + exit(EXIT_FAILURE); + } + e = md->cosimulation->capabilities; + printf("%s\n", elmNames[e->type]); + for (i=0; in; i+=2) + printf(" %s=%s\n", e->attributes[i], e->attributes[i+1]); +#endif // FMI_COSIMULATION +} +*/ + +/* + * return: 1 for successful laod or number for error. + * -1. FMU path not found + * -2. Unzip failed + * -3. Loading model description failed + * -4. FMU dll load failed + */ +/* +int loadFMU(FMU *fmu, const char* fmuFileName, const char* tmpPath) { + char* fmuPath; + char* xmlPath; + char* dllPath; + unsigned old_clock = clock(); + unsigned current_clock = 0;//will be assigned later + + // get absolute path to FMU, NULL if not found + fmuPath = getFmuPath(fmuFileName); + if (!fmuPath) return -1; // path not found + + // unzip the FMU to the tmpPath directory + if (unzip(fmuPath, tmpPath)) return -2; // unzip failed + + // parse tmpPath\modelDescription.xml + xmlPath = calloc(sizeof(char), strlen(tmpPath) + strlen(XML_FILE) + 1); + sprintf(xmlPath, "%s%s", tmpPath, XML_FILE); + fmu->modelDescription = parse(xmlPath); + free(xmlPath); + if (!fmu->modelDescription) return -3; // loading model description failed + + // printModelDescription(fmu.modelDescription); + // fflush(stdout); + + // load the FMU dll + dllPath = calloc(sizeof(char), strlen(tmpPath) + strlen(DLL_DIR) + + strlen( getModelIdentifier(fmu->modelDescription)) + strlen(".dll") + 1); + sprintf(dllPath,"%s%s%s.dll", tmpPath, DLL_DIR, getModelIdentifier(fmu->modelDescription)); + if (!loadDll(dllPath, fmu)) return -4; // loading dll failed + + free(dllPath); + free(fmuPath); + + return 1; +} +*/ + +static void doubleToCommaString(char* buffer, double r){ + char* comma; + sprintf(buffer, "%.16g", r); + comma = strchr(buffer, '.'); + if (comma) *comma = ','; +} + +// output time and all non-alias variables in CSV format +// if separator is ',', columns are separated by ',' and '.' is used for floating-point numbers. +// otherwise, the given separator (e.g. ';' or '\t') is to separate columns, and ',' is used +// as decimal dot in floating-point numbers. +/* +void outputRow(FMU *fmu, fmiComponent c, double time, FILE* file, char separator, boolean header) { + int k; + fmiReal r; + fmiInteger i; + fmiBoolean b; + fmiString s; + fmiValueReference vr; + ScalarVariable** vars = fmu->modelDescription->modelVariables; + char buffer[32]; + + // print first column + if (header) + fprintf(file, "time"); + else { + if (separator==',') + fprintf(file, "%.16g", time); + else { + // separator is e.g. ';' or '\t' + doubleToCommaString(buffer, time); + fprintf(file, "%s", buffer); + } + } + + // print all other columns + for (k=0; vars[k]; k++) { + ScalarVariable* sv = vars[k]; + if (getAlias(sv)!=enu_noAlias) continue; + if (header) { + // output names only + if (separator==',') { + // treat array element, e.g. print a[1, 2] as a[1.2] + char* s = getName(sv); + fprintf(file, "%c", separator); + while (*s) { + if (*s!=' ') fprintf(file, "%c", *s==',' ? '.' : *s); + s++; + } + } + else + fprintf(file, "%c%s", separator, getName(sv)); + } + else { + // output values + vr = getValueReference(sv); + switch (sv->typeSpec->type){ + case elm_Real: + fmu->getReal(c, &vr, 1, &r); + if (separator==',') + fprintf(file, ",%.16g", r); + else { + // separator is e.g. ';' or '\t' + doubleToCommaString(buffer, r); + fprintf(file, "%c%s", separator, buffer); + } + break; + case elm_Integer: + case elm_Enumeration: + fmu->getInteger(c, &vr, 1, &i); + fprintf(file, "%c%d", separator, i); + break; + case elm_Boolean: + fmu->getBoolean(c, &vr, 1, &b); + fprintf(file, "%c%d", separator, b); + break; + case elm_String: + fmu->getString(c, &vr, 1, &s); + fprintf(file, "%c%s", separator, s); + break; + default: + fprintf(file, "%cNoValueForType=%d", separator,sv->typeSpec->type); + } + } + } // for + + // terminate this row + fprintf(file, "\n"); +} +*/ + +static const char* fmiStatusToString(fmiStatus status){ + switch (status){ + case fmiOK: return "ok"; + case fmiWarning: return "warning"; + case fmiDiscard: return "discard"; + case fmiError: return "error"; + case fmiFatal: return "fatal"; +#ifdef FMI_COSIMULATION + case fmiPending: return "fmiPending"; +#endif + default: return "?"; + } +} + +// search a fmu for the given variable +// return NULL if not found or vr = fmiUndefinedValueReference +static ScalarVariable* getSV(FMU* fmu, char type, fmiValueReference vr) { + int i; + Elm tp; + ScalarVariable** vars = fmu->modelDescription->modelVariables; + if (vr==fmiUndefinedValueReference) return NULL; + switch (type) { + case 'r': tp = elm_Real; break; + case 'i': tp = elm_Integer; break; + case 'b': tp = elm_Boolean; break; + case 's': tp = elm_String; break; + } + for (i=0; vars[i]; i++) { + ScalarVariable* sv = vars[i]; + if (vr==getValueReference(sv) && tp==sv->typeSpec->type) + return sv; + } + return NULL; +} + +// replace e.g. #r1365# by variable name and ## by # in message +// copies the result to buffer +static void replaceRefsInMessage(const char* msg, char* buffer, int nBuffer, FMU* fmu){ + int i=0; // position in msg + int k=0; // position in buffer + int n; + char c = msg[i]; + while (c!='\0' && k < nBuffer) { + if (c!='#') { + buffer[k++]=c; + i++; + c = msg[i]; + } + else { + char* end = strchr((char*)(msg+i+1), '#'); + if (!end) { + printf("unmatched '#' in '%s'\n", msg); + buffer[k++]='#'; + break; + } + n = end - (msg+i); + if (n==1) { + // ## detected, output # + buffer[k++]='#'; + i += 2; + c = msg[i]; + } + else { + char type = msg[i+1]; // one of ribs + fmiValueReference vr; + int nvr = sscanf(msg+i+2, "%u", &vr); + if (nvr==1) { + // vr of type detected, e.g. #r12# + ScalarVariable* sv = getSV(fmu, type, vr); + const char* name = sv ? getName(sv) : "?"; + sprintf(buffer+k, "%s", name); + k += strlen(name); + i += (n+1); + c = msg[i]; + } + else { + // could not parse the number + printf("illegal value reference at position %d in '%s'\n", i+2, msg); + buffer[k++]='#'; + break; + } + } + } + } // while + buffer[k] = '\0'; +} + +#define MAX_MSG_SIZE 1000 +void fmuLogger(FMU *fmu, fmiComponent c, fmiString instanceName, fmiStatus status, + fmiString category, fmiString message, ...) { + char msg[MAX_MSG_SIZE]; +// char* copy; + va_list argp; + + // replace C format strings + va_start(argp, message); + vsprintf(msg, message, argp); + va_end(argp); + + // replace e.g. ## and #r12# +// copy = strdup(msg); +// replaceRefsInMessage(copy, msg, MAX_MSG_SIZE, fmu); +// free(copy); + + // print the final message + if (!instanceName) instanceName = "?"; + if (!category) category = "?"; + //printf("%s %s (%s): %s\n", fmiStatusToString(status), instanceName, category, msg); + printf("%s\n", message); +} + +int error(const char* message){ + printf("%s\n", message); + return 0; +} + +void parseArguments(int argc, char *argv[], char** fmuFileName, double* tEnd, double* h, int* loggingOn, char* csv_separator) { + // parse command line arguments + if (argc>1) { + *fmuFileName = argv[1]; + } + else { + printf("error: no fmu file\n"); + printHelp(argv[0]); + exit(EXIT_FAILURE); + } + if (argc>2) { + if (sscanf(argv[2],"%lf", tEnd) != 1) { + printf("error: The given end time (%s) is not a number\n", argv[2]); + exit(EXIT_FAILURE); + } + } + if (argc>3) { + if (sscanf(argv[3],"%lf", h) != 1) { + printf("error: The given stepsize (%s) is not a number\n", argv[3]); + exit(EXIT_FAILURE); + } + } + if (argc>4) { + if (sscanf(argv[4],"%d", loggingOn) != 1 || *loggingOn<0 || *loggingOn>1) { + printf("error: The given logging flag (%s) is not boolean\n", argv[4]); + exit(EXIT_FAILURE); + } + } + if (argc>5) { + if (strlen(argv[5]) != 1) { + printf("error: The given CSV separator char (%s) is not valid\n", argv[5]); + exit(EXIT_FAILURE); + } + switch (argv[5][0]) { + case 'c': *csv_separator = ','; break; // comma + case 's': *csv_separator = ';'; break; // semicolon + default: *csv_separator = argv[5][0]; break; // any other char + } + } + if (argc>6) { + printf("warning: Ignoring %d additional arguments: %s ...\n", argc-6, argv[6]); + printHelp(argv[0]); + } +} + +void printHelp(const char* fmusim) { + printf("command syntax: %s \n", fmusim); + printf(" .... path to FMU, relative to current dir or absolute, required\n"); + printf(" ......... end time of simulation, optional, defaults to 1.0 sec\n"); + printf(" ............ step size of simulation, optional, defaults to 0.1 sec\n"); + printf(" .... 1 to activate logging, optional, defaults to 0\n"); + printf(" . separator in csv file, optional, c for ';', s for';', defaults to c\n"); +} +