]> gerrit.simantics Code Review - simantics/fmil.git/blob - org.simantics.fmil.core/native/FMUSimulator/src/sim_support.c
Linux 64 support for FMU
[simantics/fmil.git] / org.simantics.fmil.core / native / FMUSimulator / src / sim_support.c
1 /* ------------------------------------------------------------------------- \r
2  * sim_support.c\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
8 \r
9 #include <stdio.h>\r
10 #include <stdlib.h>\r
11 #include <string.h>\r
12 #include <assert.h>\r
13 #include <time.h>\r
14 #include <stdarg.h>\r
15 \r
16 #ifdef FMI_COSIMULATION\r
17 #include "fmi_cs.h"\r
18 #else\r
19 #include "fmi_me.h"\r
20 #endif\r
21 \r
22 // linux/unix DSO loading\r
23 #ifndef _MSC_VER\r
24 #include <dlfcn.h>\r
25 #endif\r
26 \r
27 #include "sim_support.h"\r
28 //#include "fmuExtract.h"\r
29 \r
30 #define MAX_PATH_SIZE 4096\r
31 \r
32 #ifdef _MSC_VER\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
40 }\r
41 \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
47         return NULL;\r
48     }\r
49         if(tmpPathRequests % 2 == 0) {\r
50                 strcat(tmpPath, "fmu\\");\r
51                 tmpPathRequests++;\r
52         } else {\r
53                 strcat(tmpPath, "fmu2\\");\r
54                 tmpPathRequests = 0;\r
55         }\r
56 \r
57         makedir(tmpPath);\r
58 \r
59     return strdup(tmpPath);\r
60 }\r
61 #endif\r
62 \r
63 static void* getAdr(int* s, FMU *fmu, const char* functionName){\r
64     char name[BUFSIZE];\r
65     void* fp;\r
66     sprintf(name, "%s_%s", getModelIdentifier(fmu->modelDescription), functionName);\r
67 #ifdef _MSC_VER\r
68     fp = GetProcAddress(fmu->dllHandle, name);\r
69 #else\r
70     fp = dlsym(fmu->dllHandle, name);\r
71 #endif\r
72     if (!fp) {\r
73         printf ("warning: Function %s not found in dll\n", name);\r
74         *s = 0; // mark dll load as 'failed'        \r
75     }\r
76     return fp;\r
77 }\r
78 \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
82     int s = 1;\r
83 #ifdef _MSC_VER\r
84     HANDLE h = LoadLibraryEx(dllPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);\r
85     if (!h) {\r
86         int error = GetLastError();\r
87         printf("error %d: Could not load %s\n", error, dllPath);\r
88         return 0; // failure\r
89     }\r
90     fmu->dllHandle = h;\r
91 #else\r
92     void* h = dlopen(dllPath, RTLD_LAZY);\r
93     if (!h) {\r
94         char* error = dlerror();\r
95         printf("error %s: Could not load %s\n", error, dllPath);\r
96         return 0; // failure\r
97     }\r
98     fmu->dllHandle = h;\r
99 #endif\r
100 \r
101 #ifdef FMI_COSIMULATION   \r
102     fmu->getTypesPlatform        = (fGetTypesPlatform)   getAdr(&s, fmu, "fmiGetTypesPlatform");\r
103     if (s==0) { \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
107     }\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
123 \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
139 #endif \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
150     return s; \r
151 }\r
152 \r
153 /*static void printModelDescription(ModelDescription* md){\r
154     Element* e = (Element*)md;  \r
155     int i;\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
163     }\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
169 }\r
170 */\r
171 \r
172 /*\r
173  * return: 1 for successful laod or number for error.\r
174  * -1. FMU path not found\r
175  * -2. Unzip failed\r
176  * -3. Loading model description failed\r
177  * -4. FMU dll load failed\r
178  */\r
179 /*\r
180 int loadFMU(FMU *fmu, const char* fmuFileName, const char* tmpPath) {\r
181     char* fmuPath;\r
182     char* xmlPath;\r
183     char* dllPath;\r
184         unsigned old_clock = clock();\r
185         unsigned current_clock = 0;//will be assigned later\r
186     \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
190 \r
191     // unzip the FMU to the tmpPath directory\r
192     if (unzip(fmuPath, tmpPath)) return -2; // unzip failed\r
193 \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
198     free(xmlPath);\r
199     if (!fmu->modelDescription) return -3; // loading model description failed\r
200 \r
201         // printModelDescription(fmu.modelDescription);\r
202         // fflush(stdout);\r
203 \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
209 \r
210     free(dllPath);\r
211     free(fmuPath);\r
212 \r
213         return 1;\r
214 }\r
215 */\r
216 \r
217 static void doubleToCommaString(char* buffer, double r){\r
218     char* comma;\r
219     sprintf(buffer, "%.16g", r);\r
220     comma = strchr(buffer, '.');\r
221     if (comma) *comma = ',';\r
222 }\r
223 \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
228 /*\r
229 void outputRow(FMU *fmu, fmiComponent c, double time, FILE* file, char separator, boolean header) {\r
230     int k;\r
231     fmiReal r;\r
232     fmiInteger i;\r
233     fmiBoolean b;\r
234     fmiString s;\r
235     fmiValueReference vr;\r
236     ScalarVariable** vars = fmu->modelDescription->modelVariables;\r
237     char buffer[32];\r
238     \r
239     // print first column\r
240     if (header) \r
241         fprintf(file, "time"); \r
242     else {\r
243         if (separator==',') \r
244             fprintf(file, "%.16g", time);\r
245         else {\r
246             // separator is e.g. ';' or '\t'\r
247             doubleToCommaString(buffer, time);\r
248             fprintf(file, "%s", buffer);       \r
249         }\r
250     }\r
251     \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
256         if (header) {\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
262                 while (*s) {\r
263                    if (*s!=' ') fprintf(file, "%c", *s==',' ? '.' : *s);\r
264                    s++;\r
265                 }\r
266              }\r
267             else\r
268                 fprintf(file, "%c%s", separator, getName(sv));\r
269         }\r
270         else {\r
271             // output values\r
272             vr = getValueReference(sv);\r
273             switch (sv->typeSpec->type){\r
274                 case elm_Real:\r
275                     fmu->getReal(c, &vr, 1, &r);\r
276                     if (separator==',') \r
277                         fprintf(file, ",%.16g", r);\r
278                     else {\r
279                         // separator is e.g. ';' or '\t'\r
280                         doubleToCommaString(buffer, r);\r
281                         fprintf(file, "%c%s", separator, buffer);       \r
282                     }\r
283                     break;\r
284                 case elm_Integer:\r
285                 case elm_Enumeration:\r
286                     fmu->getInteger(c, &vr, 1, &i);\r
287                     fprintf(file, "%c%d", separator, i);\r
288                     break;\r
289                 case elm_Boolean:\r
290                     fmu->getBoolean(c, &vr, 1, &b);\r
291                     fprintf(file, "%c%d", separator, b);\r
292                     break;\r
293                 case elm_String:\r
294                     fmu->getString(c, &vr, 1, &s);\r
295                     fprintf(file, "%c%s", separator, s);\r
296                     break;\r
297                 default: \r
298                     fprintf(file, "%cNoValueForType=%d", separator,sv->typeSpec->type);\r
299             }\r
300         }\r
301     } // for\r
302     \r
303     // terminate this row\r
304     fprintf(file, "\n"); \r
305 }\r
306 */\r
307 \r
308 static const char* fmiStatusToString(fmiStatus status){\r
309     switch (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
317 #endif\r
318         default:         return "?";\r
319     }\r
320 }\r
321 \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
325     int i;\r
326     Elm tp;\r
327     ScalarVariable** vars = fmu->modelDescription->modelVariables;\r
328     if (vr==fmiUndefinedValueReference) return NULL;\r
329     switch (type) {\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
334     }\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
338             return sv;\r
339     }\r
340     return NULL;\r
341 }\r
342 \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
348     int n;\r
349     char c = msg[i];\r
350     while (c!='\0' && k < nBuffer) {\r
351         if (c!='#') {\r
352             buffer[k++]=c;\r
353             i++;\r
354             c = msg[i];\r
355         }\r
356         else {\r
357             char* end = strchr(const_cast<char*>(msg+i+1), '#');\r
358             if (!end) {\r
359                 printf("unmatched '#' in '%s'\n", msg);\r
360                 buffer[k++]='#';\r
361                 break;\r
362             }\r
363             n = end - (msg+i);\r
364             if (n==1) {\r
365                 // ## detected, output #\r
366                 buffer[k++]='#';\r
367                 i += 2;\r
368                 c = msg[i];\r
369             }\r
370             else {\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
374                 if (nvr==1) {\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
379                     k += strlen(name);\r
380                     i += (n+1);\r
381                     c = msg[i]; \r
382                 }\r
383                 else {\r
384                     // could not parse the number\r
385                     printf("illegal value reference at position %d in '%s'\n", i+2, msg);\r
386                     buffer[k++]='#';\r
387                     break;\r
388                 }\r
389             }\r
390         }\r
391     } // while\r
392     buffer[k] = '\0';\r
393 }\r
394 \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
399 //    char* copy;\r
400     va_list argp;\r
401 \r
402     // replace C format strings\r
403     va_start(argp, message);\r
404     vsprintf(msg, message, argp);\r
405     va_end(argp);\r
406 \r
407     // replace e.g. ## and #r12#  \r
408 //    copy = strdup(msg);\r
409 //    replaceRefsInMessage(copy, msg, MAX_MSG_SIZE, fmu);\r
410 //    free(copy);\r
411     \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
417 }\r
418 \r
419 int error(const char* message){\r
420     printf("%s\n", message);\r
421     return 0;\r
422 }\r
423 \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
426     if (argc>1) {\r
427         *fmuFileName = argv[1];\r
428     }\r
429     else {\r
430         printf("error: no fmu file\n");\r
431         printHelp(argv[0]);\r
432         exit(EXIT_FAILURE);\r
433     }\r
434     if (argc>2) {\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
438         }\r
439     }\r
440     if (argc>3) {\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
444         }\r
445     }\r
446     if (argc>4) {\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
450         }\r
451     }\r
452     if (argc>5) {\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
456         }\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
461         }\r
462     }\r
463     if (argc>6) {\r
464         printf("warning: Ignoring %d additional arguments: %s ...\n", argc-6, argv[6]);\r
465         printHelp(argv[0]);\r
466     }\r
467 }\r
468 \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
476 }\r
477 \r