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
15 #ifdef FMI_COSIMULATION
\r
21 #include "sim_support.h"
\r
22 //#include "fmuExtract.h"
\r
24 // fileName is an absolute path, e.g. C:\test\a.fmu
\r
25 // or relative to the current dir, e.g. ..\test\a.fmu
\r
26 // Does not check for existence of the file
\r
27 static char* getFmuPath(const char* fileName){
\r
28 char pathName[MAX_PATH];
\r
29 int n = GetFullPathName(fileName, MAX_PATH, pathName, NULL);
\r
30 return n ? strdup(pathName) : NULL;
\r
33 int tmpPathRequests = 0;
\r
34 static char* getTmpPath() {
\r
35 char tmpPath[BUFSIZE];
\r
36 if(! GetTempPath(BUFSIZE, tmpPath)) {
\r
37 printf ("error: Could not find temporary disk space\n");
\r
40 if(tmpPathRequests % 2 == 0) {
\r
41 strcat(tmpPath, "fmu\\");
\r
44 strcat(tmpPath, "fmu2\\");
\r
45 tmpPathRequests = 0;
\r
50 return strdup(tmpPath);
\r
53 static void* getAdr(int* s, FMU *fmu, const char* functionName){
\r
56 sprintf(name, "%s_%s", getModelIdentifier(fmu->modelDescription), functionName);
\r
57 fp = GetProcAddress(fmu->dllHandle, name);
\r
59 printf ("warning: Function %s not found in dll\n", name);
\r
60 *s = 0; // mark dll load as 'failed'
\r
65 // Load the given dll and set function pointers in fmu
\r
66 // Return 0 to indicate failure
\r
67 static int loadDll(const char* dllPath, FMU *fmu) {
\r
69 HANDLE h = LoadLibraryEx(dllPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
\r
71 int error = GetLastError();
\r
72 printf("error %d: Could not load %s\n", error, dllPath);
\r
73 return 0; // failure
\r
77 #ifdef FMI_COSIMULATION
\r
78 fmu->getTypesPlatform = (fGetTypesPlatform) getAdr(&s, fmu, "fmiGetTypesPlatform");
\r
80 s = 1; // work around bug for FMUs exported using Dymola 2012 and SimulationX 3.x
\r
81 fmu->getTypesPlatform = (fGetTypesPlatform) getAdr(&s, fmu, "fmiGetModelTypesPlatform");
\r
82 if (s==1) printf(" using fmiGetModelTypesPlatform instead\n", dllPath);
\r
84 fmu->instantiateSlave = (fInstantiateSlave) getAdr(&s, fmu, "fmiInstantiateSlave");
\r
85 fmu->initializeSlave = (fInitializeSlave) getAdr(&s, fmu, "fmiInitializeSlave");
\r
86 fmu->terminateSlave = (fTerminateSlave) getAdr(&s, fmu, "fmiTerminateSlave");
\r
87 fmu->resetSlave = (fResetSlave) getAdr(&s, fmu, "fmiResetSlave");
\r
88 fmu->freeSlaveInstance = (fFreeSlaveInstance) getAdr(&s, fmu, "fmiFreeSlaveInstance");
\r
89 fmu->setRealInputDerivatives = (fSetRealInputDerivatives) getAdr(&s, fmu, "fmiSetRealInputDerivatives");
\r
90 fmu->getRealOutputDerivatives = (fGetRealOutputDerivatives) getAdr(&s, fmu, "fmiGetRealOutputDerivatives");
\r
91 fmu->cancelStep = (fCancelStep) getAdr(&s, fmu, "fmiCancelStep");
\r
92 fmu->doStep = (fDoStep) getAdr(&s, fmu, "fmiDoStep");
\r
93 // SimulationX 3.4 and 3.5 do not yet export getStatus and getXStatus: do not count this as failure here
\r
94 fmu->getStatus = (fGetStatus) getAdr(&x, fmu, "fmiGetStatus");
\r
95 fmu->getRealStatus = (fGetRealStatus) getAdr(&x, fmu, "fmiGetRealStatus");
\r
96 fmu->getIntegerStatus = (fGetIntegerStatus) getAdr(&x, fmu, "fmiGetIntegerStatus");
\r
97 fmu->getBooleanStatus = (fGetBooleanStatus) getAdr(&x, fmu, "fmiGetBooleanStatus");
\r
98 fmu->getStringStatus = (fGetStringStatus) getAdr(&x, fmu, "fmiGetStringStatus");
\r
100 #else // FMI for Model Exchange 1.0
\r
101 fmu->getModelTypesPlatform = (fGetModelTypesPlatform) getAdr(&s, fmu, "fmiGetModelTypesPlatform");
\r
102 fmu->instantiateModel = (fInstantiateModel) getAdr(&s, fmu, "fmiInstantiateModel");
\r
103 fmu->freeModelInstance = (fFreeModelInstance) getAdr(&s, fmu, "fmiFreeModelInstance");
\r
104 fmu->setTime = (fSetTime) getAdr(&s, fmu, "fmiSetTime");
\r
105 fmu->setContinuousStates = (fSetContinuousStates)getAdr(&s, fmu, "fmiSetContinuousStates");
\r
106 fmu->completedIntegratorStep = (fCompletedIntegratorStep)getAdr(&s, fmu, "fmiCompletedIntegratorStep");
\r
107 fmu->initialize = (fInitialize) getAdr(&s, fmu, "fmiInitialize");
\r
108 fmu->getDerivatives = (fGetDerivatives) getAdr(&s, fmu, "fmiGetDerivatives");
\r
109 fmu->getEventIndicators = (fGetEventIndicators) getAdr(&s, fmu, "fmiGetEventIndicators");
\r
110 fmu->eventUpdate = (fEventUpdate) getAdr(&s, fmu, "fmiEventUpdate");
\r
111 fmu->getContinuousStates = (fGetContinuousStates)getAdr(&s, fmu, "fmiGetContinuousStates");
\r
112 fmu->getNominalContinuousStates = (fGetNominalContinuousStates)getAdr(&s, fmu, "fmiGetNominalContinuousStates");
\r
113 fmu->getStateValueReferences = (fGetStateValueReferences)getAdr(&s, fmu, "fmiGetStateValueReferences");
\r
114 fmu->terminate = (fTerminate) getAdr(&s, fmu, "fmiTerminate");
\r
116 fmu->getVersion = (fGetVersion) getAdr(&s, fmu, "fmiGetVersion");
\r
117 fmu->setDebugLogging = (fSetDebugLogging) getAdr(&s, fmu, "fmiSetDebugLogging");
\r
118 fmu->setReal = (fSetReal) getAdr(&s, fmu, "fmiSetReal");
\r
119 fmu->setInteger = (fSetInteger) getAdr(&s, fmu, "fmiSetInteger");
\r
120 fmu->setBoolean = (fSetBoolean) getAdr(&s, fmu, "fmiSetBoolean");
\r
121 fmu->setString = (fSetString) getAdr(&s, fmu, "fmiSetString");
\r
122 fmu->getReal = (fGetReal) getAdr(&s, fmu, "fmiGetReal");
\r
123 fmu->getInteger = (fGetInteger) getAdr(&s, fmu, "fmiGetInteger");
\r
124 fmu->getBoolean = (fGetBoolean) getAdr(&s, fmu, "fmiGetBoolean");
\r
125 fmu->getString = (fGetString) getAdr(&s, fmu, "fmiGetString");
\r
129 static void printModelDescription(ModelDescription* md){
\r
130 Element* e = (Element*)md;
\r
132 printf("%s\n", elmNames[e->type]);
\r
133 for (i=0; i<e->n; i+=2)
\r
134 printf(" %s=%s\n", e->attributes[i], e->attributes[i+1]);
\r
135 #ifdef FMI_COSIMULATION
\r
136 if (!md->cosimulation) {
\r
137 printf("error: No Implementation element found in model description. This FMU is not for Co-Simulation.\n");
\r
138 exit(EXIT_FAILURE);
\r
140 e = md->cosimulation->capabilities;
\r
141 printf("%s\n", elmNames[e->type]);
\r
142 for (i=0; i<e->n; i+=2)
\r
143 printf(" %s=%s\n", e->attributes[i], e->attributes[i+1]);
\r
144 #endif // FMI_COSIMULATION
\r
148 * return: 1 for successful laod or number for error.
\r
149 * -1. FMU path not found
\r
151 * -3. Loading model description failed
\r
152 * -4. FMU dll load failed
\r
155 int loadFMU(FMU *fmu, const char* fmuFileName, const char* tmpPath) {
\r
159 unsigned old_clock = clock();
\r
160 unsigned current_clock = 0;//will be assigned later
\r
162 // get absolute path to FMU, NULL if not found
\r
163 fmuPath = getFmuPath(fmuFileName);
\r
164 if (!fmuPath) return -1; // path not found
\r
166 // unzip the FMU to the tmpPath directory
\r
167 if (unzip(fmuPath, tmpPath)) return -2; // unzip failed
\r
169 // parse tmpPath\modelDescription.xml
\r
170 xmlPath = calloc(sizeof(char), strlen(tmpPath) + strlen(XML_FILE) + 1);
\r
171 sprintf(xmlPath, "%s%s", tmpPath, XML_FILE);
\r
172 fmu->modelDescription = parse(xmlPath);
\r
174 if (!fmu->modelDescription) return -3; // loading model description failed
\r
176 // printModelDescription(fmu.modelDescription);
\r
179 // load the FMU dll
\r
180 dllPath = calloc(sizeof(char), strlen(tmpPath) + strlen(DLL_DIR)
\r
181 + strlen( getModelIdentifier(fmu->modelDescription)) + strlen(".dll") + 1);
\r
182 sprintf(dllPath,"%s%s%s.dll", tmpPath, DLL_DIR, getModelIdentifier(fmu->modelDescription));
\r
183 if (!loadDll(dllPath, fmu)) return -4; // loading dll failed
\r
192 static void doubleToCommaString(char* buffer, double r){
\r
194 sprintf(buffer, "%.16g", r);
\r
195 comma = strchr(buffer, '.');
\r
196 if (comma) *comma = ',';
\r
199 // output time and all non-alias variables in CSV format
\r
200 // if separator is ',', columns are separated by ',' and '.' is used for floating-point numbers.
\r
201 // otherwise, the given separator (e.g. ';' or '\t') is to separate columns, and ',' is used
\r
202 // as decimal dot in floating-point numbers.
\r
204 void outputRow(FMU *fmu, fmiComponent c, double time, FILE* file, char separator, boolean header) {
\r
210 fmiValueReference vr;
\r
211 ScalarVariable** vars = fmu->modelDescription->modelVariables;
\r
214 // print first column
\r
216 fprintf(file, "time");
\r
218 if (separator==',')
\r
219 fprintf(file, "%.16g", time);
\r
221 // separator is e.g. ';' or '\t'
\r
222 doubleToCommaString(buffer, time);
\r
223 fprintf(file, "%s", buffer);
\r
227 // print all other columns
\r
228 for (k=0; vars[k]; k++) {
\r
229 ScalarVariable* sv = vars[k];
\r
230 if (getAlias(sv)!=enu_noAlias) continue;
\r
232 // output names only
\r
233 if (separator==',') {
\r
234 // treat array element, e.g. print a[1, 2] as a[1.2]
\r
235 char* s = getName(sv);
\r
236 fprintf(file, "%c", separator);
\r
238 if (*s!=' ') fprintf(file, "%c", *s==',' ? '.' : *s);
\r
243 fprintf(file, "%c%s", separator, getName(sv));
\r
247 vr = getValueReference(sv);
\r
248 switch (sv->typeSpec->type){
\r
250 fmu->getReal(c, &vr, 1, &r);
\r
251 if (separator==',')
\r
252 fprintf(file, ",%.16g", r);
\r
254 // separator is e.g. ';' or '\t'
\r
255 doubleToCommaString(buffer, r);
\r
256 fprintf(file, "%c%s", separator, buffer);
\r
260 case elm_Enumeration:
\r
261 fmu->getInteger(c, &vr, 1, &i);
\r
262 fprintf(file, "%c%d", separator, i);
\r
265 fmu->getBoolean(c, &vr, 1, &b);
\r
266 fprintf(file, "%c%d", separator, b);
\r
269 fmu->getString(c, &vr, 1, &s);
\r
270 fprintf(file, "%c%s", separator, s);
\r
273 fprintf(file, "%cNoValueForType=%d", separator,sv->typeSpec->type);
\r
278 // terminate this row
\r
279 fprintf(file, "\n");
\r
283 static const char* fmiStatusToString(fmiStatus status){
\r
285 case fmiOK: return "ok";
\r
286 case fmiWarning: return "warning";
\r
287 case fmiDiscard: return "discard";
\r
288 case fmiError: return "error";
\r
289 case fmiFatal: return "fatal";
\r
290 #ifdef FMI_COSIMULATION
\r
291 case fmiPending: return "fmiPending";
\r
293 default: return "?";
\r
297 // search a fmu for the given variable
\r
298 // return NULL if not found or vr = fmiUndefinedValueReference
\r
299 static ScalarVariable* getSV(FMU* fmu, char type, fmiValueReference vr) {
\r
302 ScalarVariable** vars = fmu->modelDescription->modelVariables;
\r
303 if (vr==fmiUndefinedValueReference) return NULL;
\r
305 case 'r': tp = elm_Real; break;
\r
306 case 'i': tp = elm_Integer; break;
\r
307 case 'b': tp = elm_Boolean; break;
\r
308 case 's': tp = elm_String; break;
\r
310 for (i=0; vars[i]; i++) {
\r
311 ScalarVariable* sv = vars[i];
\r
312 if (vr==getValueReference(sv) && tp==sv->typeSpec->type)
\r
318 // replace e.g. #r1365# by variable name and ## by # in message
\r
319 // copies the result to buffer
\r
320 static void replaceRefsInMessage(const char* msg, char* buffer, int nBuffer, FMU* fmu){
\r
321 int i=0; // position in msg
\r
322 int k=0; // position in buffer
\r
325 while (c!='\0' && k < nBuffer) {
\r
332 char* end = strchr(msg+i+1, '#');
\r
334 printf("unmatched '#' in '%s'\n", msg);
\r
340 // ## detected, output #
\r
346 char type = msg[i+1]; // one of ribs
\r
347 fmiValueReference vr;
\r
348 int nvr = sscanf(msg+i+2, "%u", &vr);
\r
350 // vr of type detected, e.g. #r12#
\r
351 ScalarVariable* sv = getSV(fmu, type, vr);
\r
352 const char* name = sv ? getName(sv) : "?";
\r
353 sprintf(buffer+k, "%s", name);
\r
359 // could not parse the number
\r
360 printf("illegal value reference at position %d in '%s'\n", i+2, msg);
\r
370 #define MAX_MSG_SIZE 1000
\r
371 void fmuLogger(FMU *fmu, fmiComponent c, fmiString instanceName, fmiStatus status,
\r
372 fmiString category, fmiString message, ...) {
\r
373 char msg[MAX_MSG_SIZE];
\r
377 // replace C format strings
\r
378 va_start(argp, message);
\r
379 vsprintf(msg, message, argp);
\r
381 // replace e.g. ## and #r12#
\r
382 // copy = strdup(msg);
\r
383 // replaceRefsInMessage(copy, msg, MAX_MSG_SIZE, fmu);
\r
386 // print the final message
\r
387 if (!instanceName) instanceName = "?";
\r
388 if (!category) category = "?";
\r
389 //printf("%s %s (%s): %s\n", fmiStatusToString(status), instanceName, category, msg);
\r
390 printf("%s\n", message);
\r
393 int error(const char* message){
\r
394 printf("%s\n", message);
\r
398 void parseArguments(int argc, char *argv[], char** fmuFileName, double* tEnd, double* h, int* loggingOn, char* csv_separator) {
\r
399 // parse command line arguments
\r
401 *fmuFileName = argv[1];
\r
404 printf("error: no fmu file\n");
\r
405 printHelp(argv[0]);
\r
406 exit(EXIT_FAILURE);
\r
409 if (sscanf(argv[2],"%lf", tEnd) != 1) {
\r
410 printf("error: The given end time (%s) is not a number\n", argv[2]);
\r
411 exit(EXIT_FAILURE);
\r
415 if (sscanf(argv[3],"%lf", h) != 1) {
\r
416 printf("error: The given stepsize (%s) is not a number\n", argv[3]);
\r
417 exit(EXIT_FAILURE);
\r
421 if (sscanf(argv[4],"%d", loggingOn) != 1 || *loggingOn<0 || *loggingOn>1) {
\r
422 printf("error: The given logging flag (%s) is not boolean\n", argv[4]);
\r
423 exit(EXIT_FAILURE);
\r
427 if (strlen(argv[5]) != 1) {
\r
428 printf("error: The given CSV separator char (%s) is not valid\n", argv[5]);
\r
429 exit(EXIT_FAILURE);
\r
431 switch (argv[5][0]) {
\r
432 case 'c': *csv_separator = ','; break; // comma
\r
433 case 's': *csv_separator = ';'; break; // semicolon
\r
434 default: *csv_separator = argv[5][0]; break; // any other char
\r
438 printf("warning: Ignoring %d additional arguments: %s ...\n", argc-6, argv[6]);
\r
439 printHelp(argv[0]);
\r
443 void printHelp(const char* fmusim) {
\r
444 printf("command syntax: %s <model.fmu> <tEnd> <h> <loggingOn> <csv separator>\n", fmusim);
\r
445 printf(" <model.fmu> .... path to FMU, relative to current dir or absolute, required\n");
\r
446 printf(" <tEnd> ......... end time of simulation, optional, defaults to 1.0 sec\n");
\r
447 printf(" <h> ............ step size of simulation, optional, defaults to 0.1 sec\n");
\r
448 printf(" <loggingOn> .... 1 to activate logging, optional, defaults to 0\n");
\r
449 printf(" <csv separator>. separator in csv file, optional, c for ';', s for';', defaults to c\n");
\r