1 /* -------------------------------------------------------------------------
3 * Functions used by both FMU simulators fmusim_me and fmusim_cs
4 * to parse command-line arguments, to unzip and load an fmu,
5 * to write CSV file, and more.
6 * Copyright 2011 QTronic GmbH. All rights reserved.
7 * -------------------------------------------------------------------------*/
16 #ifdef FMI_COSIMULATION
22 // linux/unix DSO loading
27 #include "sim_support.h"
28 //#include "fmuExtract.h"
30 #define MAX_PATH_SIZE 4096
35 // fileName is an absolute path, e.g. C:\test\a.fmu
36 // or relative to the current dir, e.g. ..\test\a.fmu
37 // Does not check for existence of the file
38 static char* getFmuPath(const char* fileName){
39 char pathName[MAX_PATH_SIZE];
40 int n = GetFullPathName(fileName, MAX_PATH_SIZE, pathName, NULL);
41 return n ? strdup(pathName) : NULL;
44 int tmpPathRequests = 0;
45 static char* getTmpPath() {
46 char tmpPath[BUFSIZE];
47 if(! GetTempPath(BUFSIZE, tmpPath)) {
48 printf ("error: Could not find temporary disk space\n");
51 if(tmpPathRequests % 2 == 0) {
52 strcat(tmpPath, "fmu\\");
55 strcat(tmpPath, "fmu2\\");
61 return strdup(tmpPath);
65 static void* getAdr(int* s, FMU *fmu, const char* functionName){
68 sprintf(name, "%s_%s", getModelIdentifier(fmu->modelDescription), functionName);
70 fp = GetProcAddress(fmu->dllHandle, name);
72 fp = dlsym(fmu->dllHandle, name);
75 printf ("warning: Function %s not found in dll\n", name);
76 *s = 0; // mark dll load as 'failed'
81 // Load the given dll and set function pointers in fmu
82 // Return 0 to indicate failure
83 static int loadDll(const char* dllPath, FMU *fmu) {
86 HANDLE h = LoadLibraryEx(dllPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
88 int error = GetLastError();
89 printf("error %d: Could not load %s\n", error, dllPath);
94 void* h = dlopen(dllPath, RTLD_LAZY);
96 char* error = dlerror();
97 printf("error %s: Could not load %s\n", error, dllPath);
103 #ifdef FMI_COSIMULATION
104 fmu->getTypesPlatform = (fGetTypesPlatform) getAdr(&s, fmu, "fmiGetTypesPlatform");
106 s = 1; // work around bug for FMUs exported using Dymola 2012 and SimulationX 3.x
107 fmu->getTypesPlatform = (fGetTypesPlatform) getAdr(&s, fmu, "fmiGetModelTypesPlatform");
108 if (s==1) printf(" using fmiGetModelTypesPlatform instead\n", dllPath);
110 fmu->instantiateSlave = (fInstantiateSlave) getAdr(&s, fmu, "fmiInstantiateSlave");
111 fmu->initializeSlave = (fInitializeSlave) getAdr(&s, fmu, "fmiInitializeSlave");
112 fmu->terminateSlave = (fTerminateSlave) getAdr(&s, fmu, "fmiTerminateSlave");
113 fmu->resetSlave = (fResetSlave) getAdr(&s, fmu, "fmiResetSlave");
114 fmu->freeSlaveInstance = (fFreeSlaveInstance) getAdr(&s, fmu, "fmiFreeSlaveInstance");
115 fmu->setRealInputDerivatives = (fSetRealInputDerivatives) getAdr(&s, fmu, "fmiSetRealInputDerivatives");
116 fmu->getRealOutputDerivatives = (fGetRealOutputDerivatives) getAdr(&s, fmu, "fmiGetRealOutputDerivatives");
117 fmu->cancelStep = (fCancelStep) getAdr(&s, fmu, "fmiCancelStep");
118 fmu->doStep = (fDoStep) getAdr(&s, fmu, "fmiDoStep");
119 // SimulationX 3.4 and 3.5 do not yet export getStatus and getXStatus: do not count this as failure here
120 fmu->getStatus = (fGetStatus) getAdr(&x, fmu, "fmiGetStatus");
121 fmu->getRealStatus = (fGetRealStatus) getAdr(&x, fmu, "fmiGetRealStatus");
122 fmu->getIntegerStatus = (fGetIntegerStatus) getAdr(&x, fmu, "fmiGetIntegerStatus");
123 fmu->getBooleanStatus = (fGetBooleanStatus) getAdr(&x, fmu, "fmiGetBooleanStatus");
124 fmu->getStringStatus = (fGetStringStatus) getAdr(&x, fmu, "fmiGetStringStatus");
126 #else // FMI for Model Exchange 1.0
127 fmu->getModelTypesPlatform = (fGetModelTypesPlatform) getAdr(&s, fmu, "fmiGetModelTypesPlatform");
128 fmu->instantiateModel = (fInstantiateModel) getAdr(&s, fmu, "fmiInstantiateModel");
129 fmu->freeModelInstance = (fFreeModelInstance) getAdr(&s, fmu, "fmiFreeModelInstance");
130 fmu->setTime = (fSetTime) getAdr(&s, fmu, "fmiSetTime");
131 fmu->setContinuousStates = (fSetContinuousStates)getAdr(&s, fmu, "fmiSetContinuousStates");
132 fmu->completedIntegratorStep = (fCompletedIntegratorStep)getAdr(&s, fmu, "fmiCompletedIntegratorStep");
133 fmu->initialize = (fInitialize) getAdr(&s, fmu, "fmiInitialize");
134 fmu->getDerivatives = (fGetDerivatives) getAdr(&s, fmu, "fmiGetDerivatives");
135 fmu->getEventIndicators = (fGetEventIndicators) getAdr(&s, fmu, "fmiGetEventIndicators");
136 fmu->eventUpdate = (fEventUpdate) getAdr(&s, fmu, "fmiEventUpdate");
137 fmu->getContinuousStates = (fGetContinuousStates)getAdr(&s, fmu, "fmiGetContinuousStates");
138 fmu->getNominalContinuousStates = (fGetNominalContinuousStates)getAdr(&s, fmu, "fmiGetNominalContinuousStates");
139 fmu->getStateValueReferences = (fGetStateValueReferences)getAdr(&s, fmu, "fmiGetStateValueReferences");
140 fmu->terminate = (fTerminate) getAdr(&s, fmu, "fmiTerminate");
142 fmu->getVersion = (fGetVersion) getAdr(&s, fmu, "fmiGetVersion");
143 fmu->setDebugLogging = (fSetDebugLogging) getAdr(&s, fmu, "fmiSetDebugLogging");
144 fmu->setReal = (fSetReal) getAdr(&s, fmu, "fmiSetReal");
145 fmu->setInteger = (fSetInteger) getAdr(&s, fmu, "fmiSetInteger");
146 fmu->setBoolean = (fSetBoolean) getAdr(&s, fmu, "fmiSetBoolean");
147 fmu->setString = (fSetString) getAdr(&s, fmu, "fmiSetString");
148 fmu->getReal = (fGetReal) getAdr(&s, fmu, "fmiGetReal");
149 fmu->getInteger = (fGetInteger) getAdr(&s, fmu, "fmiGetInteger");
150 fmu->getBoolean = (fGetBoolean) getAdr(&s, fmu, "fmiGetBoolean");
151 fmu->getString = (fGetString) getAdr(&s, fmu, "fmiGetString");
155 /*static void printModelDescription(ModelDescription* md){
156 Element* e = (Element*)md;
158 printf("%s\n", elmNames[e->type]);
159 for (i=0; i<e->n; i+=2)
160 printf(" %s=%s\n", e->attributes[i], e->attributes[i+1]);
161 #ifdef FMI_COSIMULATION
162 if (!md->cosimulation) {
163 printf("error: No Implementation element found in model description. This FMU is not for Co-Simulation.\n");
166 e = md->cosimulation->capabilities;
167 printf("%s\n", elmNames[e->type]);
168 for (i=0; i<e->n; i+=2)
169 printf(" %s=%s\n", e->attributes[i], e->attributes[i+1]);
170 #endif // FMI_COSIMULATION
175 * return: 1 for successful laod or number for error.
176 * -1. FMU path not found
178 * -3. Loading model description failed
179 * -4. FMU dll load failed
182 int loadFMU(FMU *fmu, const char* fmuFileName, const char* tmpPath) {
186 unsigned old_clock = clock();
187 unsigned current_clock = 0;//will be assigned later
189 // get absolute path to FMU, NULL if not found
190 fmuPath = getFmuPath(fmuFileName);
191 if (!fmuPath) return -1; // path not found
193 // unzip the FMU to the tmpPath directory
194 if (unzip(fmuPath, tmpPath)) return -2; // unzip failed
196 // parse tmpPath\modelDescription.xml
197 xmlPath = calloc(sizeof(char), strlen(tmpPath) + strlen(XML_FILE) + 1);
198 sprintf(xmlPath, "%s%s", tmpPath, XML_FILE);
199 fmu->modelDescription = parse(xmlPath);
201 if (!fmu->modelDescription) return -3; // loading model description failed
203 // printModelDescription(fmu.modelDescription);
207 dllPath = calloc(sizeof(char), strlen(tmpPath) + strlen(DLL_DIR)
208 + strlen( getModelIdentifier(fmu->modelDescription)) + strlen(".dll") + 1);
209 sprintf(dllPath,"%s%s%s.dll", tmpPath, DLL_DIR, getModelIdentifier(fmu->modelDescription));
210 if (!loadDll(dllPath, fmu)) return -4; // loading dll failed
219 static void doubleToCommaString(char* buffer, double r){
221 sprintf(buffer, "%.16g", r);
222 comma = strchr(buffer, '.');
223 if (comma) *comma = ',';
226 // output time and all non-alias variables in CSV format
227 // if separator is ',', columns are separated by ',' and '.' is used for floating-point numbers.
228 // otherwise, the given separator (e.g. ';' or '\t') is to separate columns, and ',' is used
229 // as decimal dot in floating-point numbers.
231 void outputRow(FMU *fmu, fmiComponent c, double time, FILE* file, char separator, boolean header) {
237 fmiValueReference vr;
238 ScalarVariable** vars = fmu->modelDescription->modelVariables;
241 // print first column
243 fprintf(file, "time");
246 fprintf(file, "%.16g", time);
248 // separator is e.g. ';' or '\t'
249 doubleToCommaString(buffer, time);
250 fprintf(file, "%s", buffer);
254 // print all other columns
255 for (k=0; vars[k]; k++) {
256 ScalarVariable* sv = vars[k];
257 if (getAlias(sv)!=enu_noAlias) continue;
260 if (separator==',') {
261 // treat array element, e.g. print a[1, 2] as a[1.2]
262 char* s = getName(sv);
263 fprintf(file, "%c", separator);
265 if (*s!=' ') fprintf(file, "%c", *s==',' ? '.' : *s);
270 fprintf(file, "%c%s", separator, getName(sv));
274 vr = getValueReference(sv);
275 switch (sv->typeSpec->type){
277 fmu->getReal(c, &vr, 1, &r);
279 fprintf(file, ",%.16g", r);
281 // separator is e.g. ';' or '\t'
282 doubleToCommaString(buffer, r);
283 fprintf(file, "%c%s", separator, buffer);
287 case elm_Enumeration:
288 fmu->getInteger(c, &vr, 1, &i);
289 fprintf(file, "%c%d", separator, i);
292 fmu->getBoolean(c, &vr, 1, &b);
293 fprintf(file, "%c%d", separator, b);
296 fmu->getString(c, &vr, 1, &s);
297 fprintf(file, "%c%s", separator, s);
300 fprintf(file, "%cNoValueForType=%d", separator,sv->typeSpec->type);
305 // terminate this row
310 static const char* fmiStatusToString(fmiStatus status){
312 case fmiOK: return "ok";
313 case fmiWarning: return "warning";
314 case fmiDiscard: return "discard";
315 case fmiError: return "error";
316 case fmiFatal: return "fatal";
317 #ifdef FMI_COSIMULATION
318 case fmiPending: return "fmiPending";
324 // search a fmu for the given variable
325 // return NULL if not found or vr = fmiUndefinedValueReference
326 static ScalarVariable* getSV(FMU* fmu, char type, fmiValueReference vr) {
329 ScalarVariable** vars = fmu->modelDescription->modelVariables;
330 if (vr==fmiUndefinedValueReference) return NULL;
332 case 'r': tp = elm_Real; break;
333 case 'i': tp = elm_Integer; break;
334 case 'b': tp = elm_Boolean; break;
335 case 's': tp = elm_String; break;
337 for (i=0; vars[i]; i++) {
338 ScalarVariable* sv = vars[i];
339 if (vr==getValueReference(sv) && tp==sv->typeSpec->type)
345 // replace e.g. #r1365# by variable name and ## by # in message
346 // copies the result to buffer
347 static void replaceRefsInMessage(const char* msg, char* buffer, int nBuffer, FMU* fmu){
348 int i=0; // position in msg
349 int k=0; // position in buffer
352 while (c!='\0' && k < nBuffer) {
359 char* end = strchr((char*)(msg+i+1), '#');
361 printf("unmatched '#' in '%s'\n", msg);
367 // ## detected, output #
373 char type = msg[i+1]; // one of ribs
374 fmiValueReference vr;
375 int nvr = sscanf(msg+i+2, "%u", &vr);
377 // vr of type detected, e.g. #r12#
378 ScalarVariable* sv = getSV(fmu, type, vr);
379 const char* name = sv ? getName(sv) : "?";
380 sprintf(buffer+k, "%s", name);
386 // could not parse the number
387 printf("illegal value reference at position %d in '%s'\n", i+2, msg);
397 #define MAX_MSG_SIZE 1000
398 void fmuLogger(FMU *fmu, fmiComponent c, fmiString instanceName, fmiStatus status,
399 fmiString category, fmiString message, ...) {
400 char msg[MAX_MSG_SIZE];
404 // replace C format strings
405 va_start(argp, message);
406 vsprintf(msg, message, argp);
409 // replace e.g. ## and #r12#
410 // copy = strdup(msg);
411 // replaceRefsInMessage(copy, msg, MAX_MSG_SIZE, fmu);
414 // print the final message
415 if (!instanceName) instanceName = "?";
416 if (!category) category = "?";
417 //printf("%s %s (%s): %s\n", fmiStatusToString(status), instanceName, category, msg);
418 printf("%s\n", message);
421 int error(const char* message){
422 printf("%s\n", message);
426 void parseArguments(int argc, char *argv[], char** fmuFileName, double* tEnd, double* h, int* loggingOn, char* csv_separator) {
427 // parse command line arguments
429 *fmuFileName = argv[1];
432 printf("error: no fmu file\n");
437 if (sscanf(argv[2],"%lf", tEnd) != 1) {
438 printf("error: The given end time (%s) is not a number\n", argv[2]);
443 if (sscanf(argv[3],"%lf", h) != 1) {
444 printf("error: The given stepsize (%s) is not a number\n", argv[3]);
449 if (sscanf(argv[4],"%d", loggingOn) != 1 || *loggingOn<0 || *loggingOn>1) {
450 printf("error: The given logging flag (%s) is not boolean\n", argv[4]);
455 if (strlen(argv[5]) != 1) {
456 printf("error: The given CSV separator char (%s) is not valid\n", argv[5]);
459 switch (argv[5][0]) {
460 case 'c': *csv_separator = ','; break; // comma
461 case 's': *csv_separator = ';'; break; // semicolon
462 default: *csv_separator = argv[5][0]; break; // any other char
466 printf("warning: Ignoring %d additional arguments: %s ...\n", argc-6, argv[6]);
471 void printHelp(const char* fmusim) {
472 printf("command syntax: %s <model.fmu> <tEnd> <h> <loggingOn> <csv separator>\n", fmusim);
473 printf(" <model.fmu> .... path to FMU, relative to current dir or absolute, required\n");
474 printf(" <tEnd> ......... end time of simulation, optional, defaults to 1.0 sec\n");
475 printf(" <h> ............ step size of simulation, optional, defaults to 0.1 sec\n");
476 printf(" <loggingOn> .... 1 to activate logging, optional, defaults to 0\n");
477 printf(" <csv separator>. separator in csv file, optional, c for ';', s for';', defaults to c\n");