1 /* -------------------------------------------------------------------------
\r
3 * Functions used by both FMU simulators fmusim_me and fmusim_cs
\r
4 * to parse command-line arguments, to unzip and load an fmu,
\r
5 * to write CSV file, and more.
\r
6 * Copyright 2011 QTronic GmbH. All rights reserved.
\r
7 * -------------------------------------------------------------------------*/
\r
16 #ifdef FMI_COSIMULATION
\r
22 // linux/unix DSO loading
\r
27 #include "sim_support.h"
\r
28 //#include "fmuExtract.h"
\r
30 #define MAX_PATH_SIZE 4096
\r
33 // fileName is an absolute path, e.g. C:\test\a.fmu
\r
34 // or relative to the current dir, e.g. ..\test\a.fmu
\r
35 // Does not check for existence of the file
\r
36 static char* getFmuPath(const char* fileName){
\r
37 char pathName[MAX_PATH_SIZE];
\r
38 int n = GetFullPathName(fileName, MAX_PATH, pathName, NULL);
\r
39 return n ? strdup(pathName) : NULL;
\r
42 int tmpPathRequests = 0;
\r
43 static char* getTmpPath() {
\r
44 char tmpPath[BUFSIZE];
\r
45 if(! GetTempPath(BUFSIZE, tmpPath)) {
\r
46 printf ("error: Could not find temporary disk space\n");
\r
49 if(tmpPathRequests % 2 == 0) {
\r
50 strcat(tmpPath, "fmu\\");
\r
53 strcat(tmpPath, "fmu2\\");
\r
54 tmpPathRequests = 0;
\r
59 return strdup(tmpPath);
\r
63 static void* getAdr(int* s, FMU *fmu, const char* functionName){
\r
66 sprintf(name, "%s_%s", getModelIdentifier(fmu->modelDescription), functionName);
\r
68 fp = GetProcAddress(fmu->dllHandle, name);
\r
70 fp = dlsym(fmu->dllHandle, name);
\r
73 printf ("warning: Function %s not found in dll\n", name);
\r
74 *s = 0; // mark dll load as 'failed'
\r
79 // Load the given dll and set function pointers in fmu
\r
80 // Return 0 to indicate failure
\r
81 static int loadDll(const char* dllPath, FMU *fmu) {
\r
84 HANDLE h = LoadLibraryEx(dllPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
\r
86 int error = GetLastError();
\r
87 printf("error %d: Could not load %s\n", error, dllPath);
\r
88 return 0; // failure
\r
92 void* h = dlopen(dllPath, RTLD_LAZY);
\r
94 char* error = dlerror();
\r
95 printf("error %s: Could not load %s\n", error, dllPath);
\r
96 return 0; // failure
\r
101 #ifdef FMI_COSIMULATION
\r
102 fmu->getTypesPlatform = (fGetTypesPlatform) getAdr(&s, fmu, "fmiGetTypesPlatform");
\r
104 s = 1; // work around bug for FMUs exported using Dymola 2012 and SimulationX 3.x
\r
105 fmu->getTypesPlatform = (fGetTypesPlatform) getAdr(&s, fmu, "fmiGetModelTypesPlatform");
\r
106 if (s==1) printf(" using fmiGetModelTypesPlatform instead\n", dllPath);
\r
108 fmu->instantiateSlave = (fInstantiateSlave) getAdr(&s, fmu, "fmiInstantiateSlave");
\r
109 fmu->initializeSlave = (fInitializeSlave) getAdr(&s, fmu, "fmiInitializeSlave");
\r
110 fmu->terminateSlave = (fTerminateSlave) getAdr(&s, fmu, "fmiTerminateSlave");
\r
111 fmu->resetSlave = (fResetSlave) getAdr(&s, fmu, "fmiResetSlave");
\r
112 fmu->freeSlaveInstance = (fFreeSlaveInstance) getAdr(&s, fmu, "fmiFreeSlaveInstance");
\r
113 fmu->setRealInputDerivatives = (fSetRealInputDerivatives) getAdr(&s, fmu, "fmiSetRealInputDerivatives");
\r
114 fmu->getRealOutputDerivatives = (fGetRealOutputDerivatives) getAdr(&s, fmu, "fmiGetRealOutputDerivatives");
\r
115 fmu->cancelStep = (fCancelStep) getAdr(&s, fmu, "fmiCancelStep");
\r
116 fmu->doStep = (fDoStep) getAdr(&s, fmu, "fmiDoStep");
\r
117 // SimulationX 3.4 and 3.5 do not yet export getStatus and getXStatus: do not count this as failure here
\r
118 fmu->getStatus = (fGetStatus) getAdr(&x, fmu, "fmiGetStatus");
\r
119 fmu->getRealStatus = (fGetRealStatus) getAdr(&x, fmu, "fmiGetRealStatus");
\r
120 fmu->getIntegerStatus = (fGetIntegerStatus) getAdr(&x, fmu, "fmiGetIntegerStatus");
\r
121 fmu->getBooleanStatus = (fGetBooleanStatus) getAdr(&x, fmu, "fmiGetBooleanStatus");
\r
122 fmu->getStringStatus = (fGetStringStatus) getAdr(&x, fmu, "fmiGetStringStatus");
\r
124 #else // FMI for Model Exchange 1.0
\r
125 fmu->getModelTypesPlatform = (fGetModelTypesPlatform) getAdr(&s, fmu, "fmiGetModelTypesPlatform");
\r
126 fmu->instantiateModel = (fInstantiateModel) getAdr(&s, fmu, "fmiInstantiateModel");
\r
127 fmu->freeModelInstance = (fFreeModelInstance) getAdr(&s, fmu, "fmiFreeModelInstance");
\r
128 fmu->setTime = (fSetTime) getAdr(&s, fmu, "fmiSetTime");
\r
129 fmu->setContinuousStates = (fSetContinuousStates)getAdr(&s, fmu, "fmiSetContinuousStates");
\r
130 fmu->completedIntegratorStep = (fCompletedIntegratorStep)getAdr(&s, fmu, "fmiCompletedIntegratorStep");
\r
131 fmu->initialize = (fInitialize) getAdr(&s, fmu, "fmiInitialize");
\r
132 fmu->getDerivatives = (fGetDerivatives) getAdr(&s, fmu, "fmiGetDerivatives");
\r
133 fmu->getEventIndicators = (fGetEventIndicators) getAdr(&s, fmu, "fmiGetEventIndicators");
\r
134 fmu->eventUpdate = (fEventUpdate) getAdr(&s, fmu, "fmiEventUpdate");
\r
135 fmu->getContinuousStates = (fGetContinuousStates)getAdr(&s, fmu, "fmiGetContinuousStates");
\r
136 fmu->getNominalContinuousStates = (fGetNominalContinuousStates)getAdr(&s, fmu, "fmiGetNominalContinuousStates");
\r
137 fmu->getStateValueReferences = (fGetStateValueReferences)getAdr(&s, fmu, "fmiGetStateValueReferences");
\r
138 fmu->terminate = (fTerminate) getAdr(&s, fmu, "fmiTerminate");
\r
140 fmu->getVersion = (fGetVersion) getAdr(&s, fmu, "fmiGetVersion");
\r
141 fmu->setDebugLogging = (fSetDebugLogging) getAdr(&s, fmu, "fmiSetDebugLogging");
\r
142 fmu->setReal = (fSetReal) getAdr(&s, fmu, "fmiSetReal");
\r
143 fmu->setInteger = (fSetInteger) getAdr(&s, fmu, "fmiSetInteger");
\r
144 fmu->setBoolean = (fSetBoolean) getAdr(&s, fmu, "fmiSetBoolean");
\r
145 fmu->setString = (fSetString) getAdr(&s, fmu, "fmiSetString");
\r
146 fmu->getReal = (fGetReal) getAdr(&s, fmu, "fmiGetReal");
\r
147 fmu->getInteger = (fGetInteger) getAdr(&s, fmu, "fmiGetInteger");
\r
148 fmu->getBoolean = (fGetBoolean) getAdr(&s, fmu, "fmiGetBoolean");
\r
149 fmu->getString = (fGetString) getAdr(&s, fmu, "fmiGetString");
\r
153 /*static void printModelDescription(ModelDescription* md){
\r
154 Element* e = (Element*)md;
\r
156 printf("%s\n", elmNames[e->type]);
\r
157 for (i=0; i<e->n; i+=2)
\r
158 printf(" %s=%s\n", e->attributes[i], e->attributes[i+1]);
\r
159 #ifdef FMI_COSIMULATION
\r
160 if (!md->cosimulation) {
\r
161 printf("error: No Implementation element found in model description. This FMU is not for Co-Simulation.\n");
\r
162 exit(EXIT_FAILURE);
\r
164 e = md->cosimulation->capabilities;
\r
165 printf("%s\n", elmNames[e->type]);
\r
166 for (i=0; i<e->n; i+=2)
\r
167 printf(" %s=%s\n", e->attributes[i], e->attributes[i+1]);
\r
168 #endif // FMI_COSIMULATION
\r
173 * return: 1 for successful laod or number for error.
\r
174 * -1. FMU path not found
\r
176 * -3. Loading model description failed
\r
177 * -4. FMU dll load failed
\r
180 int loadFMU(FMU *fmu, const char* fmuFileName, const char* tmpPath) {
\r
184 unsigned old_clock = clock();
\r
185 unsigned current_clock = 0;//will be assigned later
\r
187 // get absolute path to FMU, NULL if not found
\r
188 fmuPath = getFmuPath(fmuFileName);
\r
189 if (!fmuPath) return -1; // path not found
\r
191 // unzip the FMU to the tmpPath directory
\r
192 if (unzip(fmuPath, tmpPath)) return -2; // unzip failed
\r
194 // parse tmpPath\modelDescription.xml
\r
195 xmlPath = calloc(sizeof(char), strlen(tmpPath) + strlen(XML_FILE) + 1);
\r
196 sprintf(xmlPath, "%s%s", tmpPath, XML_FILE);
\r
197 fmu->modelDescription = parse(xmlPath);
\r
199 if (!fmu->modelDescription) return -3; // loading model description failed
\r
201 // printModelDescription(fmu.modelDescription);
\r
204 // load the FMU dll
\r
205 dllPath = calloc(sizeof(char), strlen(tmpPath) + strlen(DLL_DIR)
\r
206 + strlen( getModelIdentifier(fmu->modelDescription)) + strlen(".dll") + 1);
\r
207 sprintf(dllPath,"%s%s%s.dll", tmpPath, DLL_DIR, getModelIdentifier(fmu->modelDescription));
\r
208 if (!loadDll(dllPath, fmu)) return -4; // loading dll failed
\r
217 static void doubleToCommaString(char* buffer, double r){
\r
219 sprintf(buffer, "%.16g", r);
\r
220 comma = strchr(buffer, '.');
\r
221 if (comma) *comma = ',';
\r
224 // output time and all non-alias variables in CSV format
\r
225 // if separator is ',', columns are separated by ',' and '.' is used for floating-point numbers.
\r
226 // otherwise, the given separator (e.g. ';' or '\t') is to separate columns, and ',' is used
\r
227 // as decimal dot in floating-point numbers.
\r
229 void outputRow(FMU *fmu, fmiComponent c, double time, FILE* file, char separator, boolean header) {
\r
235 fmiValueReference vr;
\r
236 ScalarVariable** vars = fmu->modelDescription->modelVariables;
\r
239 // print first column
\r
241 fprintf(file, "time");
\r
243 if (separator==',')
\r
244 fprintf(file, "%.16g", time);
\r
246 // separator is e.g. ';' or '\t'
\r
247 doubleToCommaString(buffer, time);
\r
248 fprintf(file, "%s", buffer);
\r
252 // print all other columns
\r
253 for (k=0; vars[k]; k++) {
\r
254 ScalarVariable* sv = vars[k];
\r
255 if (getAlias(sv)!=enu_noAlias) continue;
\r
257 // output names only
\r
258 if (separator==',') {
\r
259 // treat array element, e.g. print a[1, 2] as a[1.2]
\r
260 char* s = getName(sv);
\r
261 fprintf(file, "%c", separator);
\r
263 if (*s!=' ') fprintf(file, "%c", *s==',' ? '.' : *s);
\r
268 fprintf(file, "%c%s", separator, getName(sv));
\r
272 vr = getValueReference(sv);
\r
273 switch (sv->typeSpec->type){
\r
275 fmu->getReal(c, &vr, 1, &r);
\r
276 if (separator==',')
\r
277 fprintf(file, ",%.16g", r);
\r
279 // separator is e.g. ';' or '\t'
\r
280 doubleToCommaString(buffer, r);
\r
281 fprintf(file, "%c%s", separator, buffer);
\r
285 case elm_Enumeration:
\r
286 fmu->getInteger(c, &vr, 1, &i);
\r
287 fprintf(file, "%c%d", separator, i);
\r
290 fmu->getBoolean(c, &vr, 1, &b);
\r
291 fprintf(file, "%c%d", separator, b);
\r
294 fmu->getString(c, &vr, 1, &s);
\r
295 fprintf(file, "%c%s", separator, s);
\r
298 fprintf(file, "%cNoValueForType=%d", separator,sv->typeSpec->type);
\r
303 // terminate this row
\r
304 fprintf(file, "\n");
\r
308 static const char* fmiStatusToString(fmiStatus status){
\r
310 case fmiOK: return "ok";
\r
311 case fmiWarning: return "warning";
\r
312 case fmiDiscard: return "discard";
\r
313 case fmiError: return "error";
\r
314 case fmiFatal: return "fatal";
\r
315 #ifdef FMI_COSIMULATION
\r
316 case fmiPending: return "fmiPending";
\r
318 default: return "?";
\r
322 // search a fmu for the given variable
\r
323 // return NULL if not found or vr = fmiUndefinedValueReference
\r
324 static ScalarVariable* getSV(FMU* fmu, char type, fmiValueReference vr) {
\r
327 ScalarVariable** vars = fmu->modelDescription->modelVariables;
\r
328 if (vr==fmiUndefinedValueReference) return NULL;
\r
330 case 'r': tp = elm_Real; break;
\r
331 case 'i': tp = elm_Integer; break;
\r
332 case 'b': tp = elm_Boolean; break;
\r
333 case 's': tp = elm_String; break;
\r
335 for (i=0; vars[i]; i++) {
\r
336 ScalarVariable* sv = vars[i];
\r
337 if (vr==getValueReference(sv) && tp==sv->typeSpec->type)
\r
343 // replace e.g. #r1365# by variable name and ## by # in message
\r
344 // copies the result to buffer
\r
345 static void replaceRefsInMessage(const char* msg, char* buffer, int nBuffer, FMU* fmu){
\r
346 int i=0; // position in msg
\r
347 int k=0; // position in buffer
\r
350 while (c!='\0' && k < nBuffer) {
\r
357 char* end = strchr(const_cast<char*>(msg+i+1), '#');
\r
359 printf("unmatched '#' in '%s'\n", msg);
\r
365 // ## detected, output #
\r
371 char type = msg[i+1]; // one of ribs
\r
372 fmiValueReference vr;
\r
373 int nvr = sscanf(msg+i+2, "%u", &vr);
\r
375 // vr of type detected, e.g. #r12#
\r
376 ScalarVariable* sv = getSV(fmu, type, vr);
\r
377 const char* name = sv ? getName(sv) : "?";
\r
378 sprintf(buffer+k, "%s", name);
\r
384 // could not parse the number
\r
385 printf("illegal value reference at position %d in '%s'\n", i+2, msg);
\r
395 #define MAX_MSG_SIZE 1000
\r
396 void fmuLogger(FMU *fmu, fmiComponent c, fmiString instanceName, fmiStatus status,
\r
397 fmiString category, fmiString message, ...) {
\r
398 char msg[MAX_MSG_SIZE];
\r
402 // replace C format strings
\r
403 va_start(argp, message);
\r
404 vsprintf(msg, message, argp);
\r
407 // replace e.g. ## and #r12#
\r
408 // copy = strdup(msg);
\r
409 // replaceRefsInMessage(copy, msg, MAX_MSG_SIZE, fmu);
\r
412 // print the final message
\r
413 if (!instanceName) instanceName = "?";
\r
414 if (!category) category = "?";
\r
415 //printf("%s %s (%s): %s\n", fmiStatusToString(status), instanceName, category, msg);
\r
416 printf("%s\n", message);
\r
419 int error(const char* message){
\r
420 printf("%s\n", message);
\r
424 void parseArguments(int argc, char *argv[], char** fmuFileName, double* tEnd, double* h, int* loggingOn, char* csv_separator) {
\r
425 // parse command line arguments
\r
427 *fmuFileName = argv[1];
\r
430 printf("error: no fmu file\n");
\r
431 printHelp(argv[0]);
\r
432 exit(EXIT_FAILURE);
\r
435 if (sscanf(argv[2],"%lf", tEnd) != 1) {
\r
436 printf("error: The given end time (%s) is not a number\n", argv[2]);
\r
437 exit(EXIT_FAILURE);
\r
441 if (sscanf(argv[3],"%lf", h) != 1) {
\r
442 printf("error: The given stepsize (%s) is not a number\n", argv[3]);
\r
443 exit(EXIT_FAILURE);
\r
447 if (sscanf(argv[4],"%d", loggingOn) != 1 || *loggingOn<0 || *loggingOn>1) {
\r
448 printf("error: The given logging flag (%s) is not boolean\n", argv[4]);
\r
449 exit(EXIT_FAILURE);
\r
453 if (strlen(argv[5]) != 1) {
\r
454 printf("error: The given CSV separator char (%s) is not valid\n", argv[5]);
\r
455 exit(EXIT_FAILURE);
\r
457 switch (argv[5][0]) {
\r
458 case 'c': *csv_separator = ','; break; // comma
\r
459 case 's': *csv_separator = ';'; break; // semicolon
\r
460 default: *csv_separator = argv[5][0]; break; // any other char
\r
464 printf("warning: Ignoring %d additional arguments: %s ...\n", argc-6, argv[6]);
\r
465 printHelp(argv[0]);
\r
469 void printHelp(const char* fmusim) {
\r
470 printf("command syntax: %s <model.fmu> <tEnd> <h> <loggingOn> <csv separator>\n", fmusim);
\r
471 printf(" <model.fmu> .... path to FMU, relative to current dir or absolute, required\n");
\r
472 printf(" <tEnd> ......... end time of simulation, optional, defaults to 1.0 sec\n");
\r
473 printf(" <h> ............ step size of simulation, optional, defaults to 0.1 sec\n");
\r
474 printf(" <loggingOn> .... 1 to activate logging, optional, defaults to 0\n");
\r
475 printf(" <csv separator>. separator in csv file, optional, c for ';', s for';', defaults to c\n");
\r