1 /* -------------------------------------------------------------------------
\r
3 * Simulation controls for fmus
\r
5 * Free libraries and tools used to implement this simulator:
\r
6 * - header files from the FMU specification
\r
7 * - eXpat 2.0.1 XML parser, see http://expat.sourceforge.net
\r
8 * - 7z.exe 4.57 zip and unzip tool, see http://www.7-zip.org <---------- Replace with zlib
\r
9 * Author: Teemu Lempinen
\r
10 * Copyright 2012 Semantum Oy
\r
11 * -------------------------------------------------------------------------
\r
23 #include <org_simantics_fmil_FMILJNI.h>
\r
27 #include "sim_support.h"
\r
30 #include "fmi1_cs.h"
\r
32 #define PRINT(fmt,args) { FILE *fp = fopen("R:\\Simantics\\Sysdyn\\log.txt", "ab"); fprintf(fp, fmt, args); fclose(fp); }
\r
35 #define GetCurrentDir _getcwd
\r
37 using namespace std;
\r
42 vector<string> variables; // all variables in an initialized model
\r
43 vector<int> valueReferences; // all value references
\r
44 vector<int> subscription; // subscribed value references
\r
50 struct FMUControlStruct {
\r
51 double step; // simulation step length
\r
52 fmiReal currentTime; // current simulation time
\r
54 fmiComponent c; // instance of the fmu
\r
55 ScalarVariable** vars; // model variables
\r
57 fmiEventInfo eventInfo; // updated by calls to initialize and eventUpdate
\r
58 const char* guid; // global unique id of the fmu
\r
59 fmiCallbackFunctions callbacks; // called by the model during simulation
\r
60 fmiStatus fmiFlag; // return code of the fmu functions
\r
62 map<string,int> indexes; // indexes for variable names in vars-table
\r
63 map<string,int>::iterator it;
\r
65 int nx; // number of state variables
\r
66 double *x; // continuous states
\r
67 double *xdot; // the crresponding derivatives in same order
\r
68 int nz; // number of state event indicators
\r
69 double *z; // state event indicators
\r
70 double *prez; // previous values of state event indicators
\r
72 bool initialized; // has the fmu been initialized
\r
74 vector<fmiValueReference> subscription; // result subscriptions
\r
75 vector<string> allVariables; // all variables in an initialized model
\r
76 vector<fmiValueReference> fmiValueReferences; // all value references
\r
78 string lastErrorMessage;
\r
85 //map<string,FMUControlStruct> fmus; // indexes for variable names in vars-table
\r
87 int throwException(JNIEnv *env, string message) {
\r
89 newExcCls = env->FindClass("java/lang/Exception");
\r
90 if (newExcCls == NULL) {
\r
91 /* Unable to find the exception class, give up. */
\r
94 env->ThrowNew(newExcCls, message.c_str());
\r
99 bool exists(string id) {
\r
100 map<string,FMUControlStruct>::iterator it = fmus.find(id);
\r
101 if(it != fmus.end()) {
\r
111 JNIEXPORT jint JNICALL Java_org_simantics_fmil_FMIL_loadFMUFile_1
\r
112 (JNIEnv *env, jobject obj, jstring path, jstring tempDir) {
\r
114 HMODULE module = NULL;
\r
116 FMIL_Variable *vars;
\r
117 int variableCount = 0;
\r
119 const char *fmuPath = env->GetStringUTFChars(path, 0);
\r
120 const char *fmuTempDir = env->GetStringUTFChars(tempDir, 0);
\r
122 fmi1.currentTime = 0;
\r
123 fmi1.timeStep = 0.1;
\r
124 fmi1.fmu = FMI1_CS_LOAD(fmuPath, fmuTempDir);
\r
126 return throwException(env, "No FMU loaded");
\r
128 vars = FMI1_CS_GET_VARIABLES(fmi1.fmu, &variableCount);
\r
129 for(int i=0;i<variableCount;i++) {
\r
130 fmi1.variables.push_back(string(vars[i].name));
\r
131 fmi1.valueReferences.push_back(vars[i].vr);
\r
134 fmus.push_back(fmi1);
\r
136 env->ReleaseStringUTFChars(path, fmuPath);
\r
137 env->ReleaseStringUTFChars(tempDir, fmuTempDir);
\r
139 return fmus.size() - 1;
\r
143 JNIEXPORT jint JNICALL Java_org_simantics_fmil_FMIL_setStepLength_1
\r
144 (JNIEnv *env, jobject obj, jint id, jdouble stepLength) {
\r
145 fmus[id].timeStep = stepLength;
\r
149 JNIEXPORT jint JNICALL Java_org_simantics_fmil_FMIL_subscribe_1
\r
150 (JNIEnv *env, jobject obj, jint id, jintArray vrs) {
\r
153 jint* elements = env -> GetIntArrayElements(vrs, &isCopy);
\r
154 jsize n = env -> GetArrayLength(vrs);
\r
157 for (i = 0; i < n; i++) {
\r
158 fmus[id].subscription.push_back(elements[i]);
\r
161 if (isCopy == JNI_TRUE) {
\r
162 env -> ReleaseIntArrayElements(vrs, elements, 0);
\r
169 bool referenceExists(FMUControlStruct fmuStruct, string variable) {
\r
170 map<string,int>::iterator it = fmuStruct.indexes.find(variable);
\r
171 if(it != fmuStruct.indexes.end()) {
\r
178 // Remember to check if reference exists
\r
179 fmiValueReference getReference(FMUControlStruct fmuStruct, string variable) {
\r
180 return fmuStruct.fmiValueReferences[fmuStruct.indexes[variable]];
\r
183 // Get string representation of a scalar variable type
\r
184 string getTypeString(ScalarVariable* sv) {
\r
185 switch (sv->typeSpec->type){
\r
188 case elm_Enumeration:
\r
189 return "Enumeration";
\r
199 JNIEXPORT jint JNICALL Java_org_simantics_fmil_FMIL_setRealValue_1
\r
200 (JNIEnv *env, jobject obj, jint id, jint vr, jdouble value) {
\r
202 FMI1_CS_SET_REAL(fmus[id].fmu, vr, value);
\r
207 JNIEXPORT jint JNICALL Java_org_simantics_fmil_FMIL_setIntegerValue_1
\r
208 (JNIEnv *env, jobject obj, jstring id, jstring parameter, jint value) {
\r
210 const char *fmuId = env->GetStringUTFChars(id, 0);
\r
211 if(exists(fmuId)) {
\r
212 FMUControlStruct& fmuStruct = fmus[fmuId];
\r
213 const char *name = env->GetStringUTFChars(parameter, 0);
\r
214 string nameString = name;
\r
215 string modelId = fmuId;
\r
216 if(!referenceExists(fmuStruct, name)) {
\r
217 string errorMessage = "setIntegerValue: Model (id " + modelId + ") does not contain variable: " + nameString;
\r
218 env->ReleaseStringUTFChars(parameter, name);
\r
219 env->ReleaseStringUTFChars(id, fmuId);
\r
220 return throwException(env, errorMessage);
\r
222 // Check variable type
\r
223 ScalarVariable* sv = fmuStruct.vars[fmuStruct.indexes[name]];
\r
224 switch (sv->typeSpec->type){
\r
228 string errorMessage = "setIntegerValue: " + nameString + " is not of type Integer. (type: + " + getTypeString(sv) + ")";
\r
229 env->ReleaseStringUTFChars(parameter, name);
\r
230 env->ReleaseStringUTFChars(id, fmuId);
\r
231 return throwException(env, errorMessage);
\r
236 fmiValueReference vr = getReference(fmuStruct, name);
\r
237 const int intValue = (int) value;
\r
238 fmuStruct.fmu.setInteger(fmuStruct.c, &vr, 1, &intValue);
\r
239 env->ReleaseStringUTFChars(parameter, name);
\r
240 env->ReleaseStringUTFChars(id, fmuId);
\r
244 string message = fmuId;
\r
245 env->ReleaseStringUTFChars(id, fmuId);
\r
246 return throwException(env, "setIntegerValue: Model id " + message + " not found");
\r
252 JNIEXPORT jint JNICALL Java_org_simantics_fmil_FMIL_setBooleanValue_1
\r
253 (JNIEnv *env, jobject obj, jstring id, jstring parameter, jboolean value) {
\r
255 const char *fmuId = env->GetStringUTFChars(id, 0);
\r
256 if(exists(fmuId)) {
\r
257 FMUControlStruct& fmuStruct = fmus[fmuId];
\r
258 const char *name = env->GetStringUTFChars(parameter, 0);
\r
259 string nameString = name;
\r
260 string modelId = fmuId;
\r
261 if(!referenceExists(fmuStruct, name)) {
\r
262 string errorMessage = "setBooleanValue: Model (id " + modelId + ") does not contain variable: " + nameString;
\r
263 env->ReleaseStringUTFChars(parameter, name);
\r
264 env->ReleaseStringUTFChars(id, fmuId);
\r
265 return throwException(env, errorMessage);
\r
267 // Check variable type
\r
268 ScalarVariable* sv = fmuStruct.vars[fmuStruct.indexes[name]];
\r
269 switch (sv->typeSpec->type){
\r
273 string errorMessage = "setBooleanValue: " + nameString + " is not of type Boolean. (type: + " + getTypeString(sv) + ")";
\r
274 env->ReleaseStringUTFChars(parameter, name);
\r
275 env->ReleaseStringUTFChars(id, fmuId);
\r
276 return throwException(env, errorMessage);
\r
281 fmiValueReference vr = getReference(fmuStruct, name);
\r
282 fmiBoolean result = 1;
\r
285 fmuStruct.fmu.setBoolean(fmuStruct.c, &vr, 1, &result);
\r
286 env->ReleaseStringUTFChars(parameter, name);
\r
287 env->ReleaseStringUTFChars(id, fmuId);
\r
291 string message = fmuId;
\r
292 env->ReleaseStringUTFChars(id, fmuId);
\r
293 return throwException(env, "setBooleanValue: Model id " + message + " not found");
\r
298 JNIEXPORT jboolean JNICALL Java_org_simantics_fmil_FMIL_isInitialized_1
\r
299 (JNIEnv *env, jobject obj, jstring id) {
\r
301 const char *fmuId = env->GetStringUTFChars(id, 0);
\r
302 if(exists(fmuId)) {
\r
303 FMUControlStruct& fmuStruct = fmus[fmuId];
\r
304 env->ReleaseStringUTFChars(id, fmuId);
\r
305 return fmuStruct.initialized;
\r
307 env->ReleaseStringUTFChars(id, fmuId);
\r
315 JNIEXPORT jdouble JNICALL Java_org_simantics_fmil_FMIL_getTime_1
\r
316 (JNIEnv *env, jobject obj, jint id) {
\r
317 return fmus[id].currentTime;
\r
320 double getRealValue(FMUControlStruct fmuStruct, int index) {
\r
321 ScalarVariable *sv = fmuStruct.vars[index];
\r
322 fmiValueReference vr = fmuStruct.fmiValueReferences[index];
\r
324 fmiInteger integer;
\r
325 fmiBoolean fmibool;
\r
327 switch (sv->typeSpec->type){
\r
329 fmuStruct.fmu.getReal(fmuStruct.c, &vr, 1, &real);
\r
332 case elm_Enumeration:
\r
333 fmuStruct.fmu.getInteger(fmuStruct.c, &vr, 1, &integer);
\r
334 real = (double)integer;
\r
337 fmuStruct.fmu.getBoolean(fmuStruct.c, &vr, 1, &fmibool);
\r
338 if(fmibool == fmiTrue)
\r
347 JNIEXPORT jdoubleArray JNICALL Java_org_simantics_fmil_FMIL_getSubscribedResults_1
\r
348 (JNIEnv *env, jobject obj, jint id, jdoubleArray result) {
\r
351 jdouble* resultElements = env -> GetDoubleArrayElements(result, &isCopy);
\r
352 jsize n = env -> GetArrayLength(result);
\r
355 vrs = &(fmus[id].subscription[0]);
\r
356 FMI1_CS_GET_REALS(fmus[id].fmu, vrs, resultElements, n);
\r
358 if (isCopy == JNI_TRUE) {
\r
359 env -> ReleaseDoubleArrayElements(result, resultElements, 0);
\r
366 JNIEXPORT jint JNICALL Java_org_simantics_fmil_FMIL_instantiateSimulation_1
\r
367 (JNIEnv *env, jobject obj, jint id) {
\r
369 int asd = FMI1_CS_INSTANTIATE(fmus[id].fmu);
\r
371 return throwException(env, "No FMU loaded");
\r
377 JNIEXPORT jint JNICALL Java_org_simantics_fmil_FMIL_initializeSimulation_1
\r
378 (JNIEnv *env, jobject obj, jint id) {
\r
380 int asd = FMI1_CS_INITIALIZE(fmus[id].fmu);
\r
382 return throwException(env, "No FMU loaded");
\r
388 JNIEXPORT jint JNICALL Java_org_simantics_fmil_FMIL_setTime_1
\r
389 (JNIEnv *env, jobject obj, jstring id, jdouble time) {
\r
393 JNIEXPORT jobjectArray JNICALL Java_org_simantics_fmil_FMIL_getAllVariables_1
\r
394 (JNIEnv *env, jobject obj, jint id) {
\r
396 jobjectArray ret= (jobjectArray)env->NewObjectArray(fmus[id].variables.size(),
\r
397 env->FindClass("java/lang/String"),
\r
398 env->NewStringUTF(""));
\r
400 for(int i=0;i<fmus[id].variables.size();i++) {
\r
401 env->SetObjectArrayElement(ret,i,env->NewStringUTF(fmus[id].variables[i].c_str()));
\r
408 JNIEXPORT jintArray JNICALL Java_org_simantics_fmil_FMIL_getAllVariableReferences_1
\r
409 (JNIEnv *env, jobject obj, jint id, jintArray result) {
\r
412 jint* resultElements = env -> GetIntArrayElements(result, &isCopy);
\r
413 jsize n = env -> GetArrayLength(result);
\r
416 for (i = 0; i < n; i++) {
\r
417 resultElements[i] = fmus[id].valueReferences[i];
\r
420 if (isCopy == JNI_TRUE) {
\r
421 env -> ReleaseIntArrayElements(result, resultElements, 0);
\r
429 JNIEXPORT jobjectArray JNICALL Java_org_simantics_fmil_FMIL_filterVariables_1
\r
430 (JNIEnv *env, jobject obj, jstring id, jstring regexp) {
\r
431 const char *rx = env->GetStringUTFChars(regexp, 0);
\r
432 jobjectArray result = filterVariables(env, obj, id, rx);
\r
433 env->ReleaseStringUTFChars(regexp, rx);
\r
438 JNIEXPORT jint JNICALL Java_org_simantics_fmil_FMIL_simulateStep_1
\r
439 (JNIEnv *env, jobject obj, jint id) {
\r
441 int asd = FMI1_CS_STEP(fmus[id].fmu, fmus[id].currentTime, fmus[id].timeStep);
\r
443 return throwException(env, "No FMU loaded");
\r
445 fmus[id].currentTime += fmus[id].timeStep;
\r
450 const char *fmuId = env->GetStringUTFChars(id, 0);
\r
451 if(exists(fmuId)) {
\r
452 FMUControlStruct& fmuStruct = fmus[fmuId];
\r
453 env->ReleaseStringUTFChars(id, fmuId);
\r
455 if(&fmuStruct.fmu == NULL || fmuStruct.fmu.modelDescription == NULL || &fmuStruct.vars == NULL) {
\r
456 return throwException(env, "Simulate step failed - fmu not loaded");
\r
459 if(fmuStruct.x == NULL) {
\r
460 return throwException(env, "Simulate step failed - fmu not instantiated");
\r
463 if(fmuStruct.initialized == false) {
\r
464 fmiBoolean toleranceControlled = fmiFalse;
\r
465 fmuStruct.fmiFlag = fmuStruct.fmu.initialize(fmuStruct.c, toleranceControlled, fmuStruct.currentTime, &(fmuStruct.eventInfo));
\r
466 if (fmuStruct.fmiFlag > fmiWarning) return throwException(env, "could not initialize model");
\r
467 fmuStruct.initialized = true;
\r
470 FMU& fmu = fmuStruct.fmu;
\r
471 int debug = 0; // DEBUG ON = 1, OFF = 0
\r
474 double dt, tPre, tEnd = fmuStruct.currentTime + fmuStruct.step;
\r
476 fmiBoolean timeEvent, stateEvent, stepEvent;
\r
477 fmiStatus fmiFlag; // return code of the fmu functions
\r
478 fmiValueReference vr;
\r
482 /* Simulate the duration of one step. The simulation may be done in
\r
483 * multiple parts if events occur
\r
485 while (fmuStruct.currentTime < tEnd) {
\r
486 // get current state and derivatives
\r
487 fmiFlag = fmu.getContinuousStates(fmuStruct.c, fmuStruct.x, fmuStruct.nx);
\r
488 if (fmiFlag > fmiWarning)
\r
489 return throwException(env, "could not retrieve states");
\r
491 fmiFlag = fmu.getDerivatives(fmuStruct.c, fmuStruct.xdot, fmuStruct.nx);
\r
492 if (fmiFlag > fmiWarning)
\r
493 return throwException(env, "could not retrieve derivatives");
\r
496 tPre = fmuStruct.currentTime;
\r
497 fmuStruct.currentTime = min(fmuStruct.currentTime+fmuStruct.step, tEnd);
\r
498 timeEvent = fmuStruct.eventInfo.upcomingTimeEvent && fmuStruct.eventInfo.nextEventTime < fmuStruct.currentTime;
\r
500 if (timeEvent) fmuStruct.currentTime = fmuStruct.eventInfo.nextEventTime;
\r
501 dt = fmuStruct.currentTime - tPre;
\r
502 fmiFlag = fmu.setTime(fmuStruct.c, fmuStruct.currentTime);
\r
503 if (fmiFlag > fmiWarning) throwException(env, "could not set time");
\r
505 if(referenceExists(fmuStruct, "time")) {
\r
506 vr = getReference(fmuStruct, "time");
\r
508 fmu.setReal(fmuStruct.c, &vr, 1, &(fmuStruct.currentTime));
\r
513 printf("Actual time: %lf\n", fmuStruct.currentTime);
\r
515 if (fmiFlag > fmiWarning)
\r
516 return throwException(env, "could not set time");
\r
518 // perform one step
\r
519 for (i=0; i<fmuStruct.nx; i++)
\r
520 fmuStruct.x[i] += dt*fmuStruct.xdot[i]; // forward Euler method
\r
522 fmiFlag = fmu.setContinuousStates(fmuStruct.c, fmuStruct.x, fmuStruct.nx);
\r
523 if (fmiFlag > fmiWarning)
\r
524 return throwException(env, "could not set states");
\r
526 // Check for step event, e.g. dynamic state selection
\r
527 fmiFlag = fmu.completedIntegratorStep(fmuStruct.c, &stepEvent);
\r
528 if (fmiFlag > fmiWarning) return throwException(env, "could not complete intgrator step");
\r
530 /* for (i=0; i<fmuStruct.nz; i++) fmuStruct.prez[i] = fmuStruct.z[i];
\r
531 fmiFlag = fmu.getEventIndicators(fmuStruct.c, fmuStruct.z, fmuStruct.nz);
\r
532 if (fmiFlag > fmiWarning) return throwException(env, "could not retrieve event indicators");
\r
533 stateEvent = FALSE;
\r
534 for (i=0; i<fmuStruct.nz; i++)
\r
535 stateEvent = stateEvent || (fmuStruct.prez[i] * fmuStruct.z[i] < 0);
\r
538 stepEvent = fmiTrue;
\r
540 if (timeEvent || stateEvent || stepEvent) {
\r
542 // event iteration in one step, ignoring intermediate results
\r
543 fmiFlag = fmu.eventUpdate(fmuStruct.c, fmiFalse, &(fmuStruct.eventInfo));
\r
544 if (fmiFlag > fmiWarning) return throwException(env, "could not perform event update");
\r
555 string message = fmuId;
\r
556 env->ReleaseStringUTFChars(id, fmuId);
\r
557 return throwException(env, "simulateStep: Model id " + message + " not found");
\r
562 JNIEXPORT jint JNICALL Java_org_simantics_fmil_FMIL_unloadFMU_1
\r
563 (JNIEnv *env, jobject obj, jint id) {
\r
565 int asd = FMI1_CS_UNLOAD(fmus[id].fmu);
\r
570 JNIEXPORT jstring JNICALL Java_org_simantics_fmil_FMIL_getLastErrorMessage_1
\r
571 (JNIEnv *env, jobject obj, jstring id) {
\r
572 return env->NewStringUTF("No errors");
\r
575 JNIEXPORT jdouble JNICALL Java_org_simantics_fmil_FMIL_getRealValue_1
\r
576 (JNIEnv *env, jobject obj, jint id, jint vr) {
\r
577 return FMI1_CS_GET_REAL(fmus[id].fmu, vr);
\r
580 JNIEXPORT jint JNICALL Java_org_simantics_fmil_FMIL_getIntegerValue_1
\r
581 (JNIEnv *env, jobject obj, jstring id, jstring variable) {
\r
583 const char *fmuId = env->GetStringUTFChars(id, 0);
\r
584 if(exists(fmuId)) {
\r
585 FMUControlStruct fmuStruct = fmus[fmuId];
\r
586 env->ReleaseStringUTFChars(id, fmuId);
\r
587 const char *name = env->GetStringUTFChars(variable, 0);
\r
589 if(referenceExists(fmuStruct, name)) {
\r
590 fmiValueReference vr = getReference(fmuStruct, name);
\r
592 fmuStruct.fmu.getInteger(fmuStruct.c, &vr, 1, &result);
\r
593 env->ReleaseStringUTFChars(variable, name);
\r
597 string nameString = name;
\r
598 string message = "Variable " + nameString + " not found";
\r
599 env->ReleaseStringUTFChars(variable, name);
\r
600 return throwException(env, message);
\r
604 string message = fmuId;
\r
605 env->ReleaseStringUTFChars(id, fmuId);
\r
606 return throwException(env, "unloadFMU: Model id " + message + " not found");
\r
613 JNIEXPORT jboolean JNICALL Java_org_simantics_fmil_FMIL_getBooleanValue_1
\r
614 (JNIEnv *env, jobject obj, jstring id, jstring variable) {
\r
616 const char *fmuId = env->GetStringUTFChars(id, 0);
\r
617 if(exists(fmuId)) {
\r
618 FMUControlStruct fmuStruct = fmus[fmuId];
\r
619 env->ReleaseStringUTFChars(id, fmuId);
\r
620 const char *name = env->GetStringUTFChars(variable, 0);
\r
622 if(referenceExists(fmuStruct, name)) {
\r
623 fmiValueReference vr = getReference(fmuStruct, name);
\r
625 fmuStruct.fmu.getBoolean(fmuStruct.c, &vr, 1, &result);
\r
626 env->ReleaseStringUTFChars(variable, name);
\r
630 string nameString = name;
\r
631 string message = "Variable " + nameString + " not found";
\r
632 env->ReleaseStringUTFChars(variable, name);
\r
633 return throwException(env, message);
\r
637 string message = fmuId;
\r
638 env->ReleaseStringUTFChars(id, fmuId);
\r
639 return throwException(env, "unloadFMU: Model id " + message + " not found");
\r
645 JNIEXPORT jstring JNICALL Java_org_simantics_fmil_FMIL_getStringValue_1
\r
646 (JNIEnv *env, jobject obj, jstring id, jstring variable) {
\r
648 const char *fmuId = env->GetStringUTFChars(id, 0);
\r
649 if(exists(fmuId)) {
\r
650 FMUControlStruct fmuStruct = fmus[fmuId];
\r
651 env->ReleaseStringUTFChars(id, fmuId);
\r
652 const char *name = env->GetStringUTFChars(variable, 0);
\r
654 if(referenceExists(fmuStruct, name)) {
\r
655 fmiValueReference vr = getReference(fmuStruct, name);
\r
657 fmuStruct.fmu.getString(fmuStruct.c, &vr, 1, &result);
\r
658 env->ReleaseStringUTFChars(variable, name);
\r
659 return env->NewStringUTF(result);
\r
662 string nameString = name;
\r
663 string message = "Variable " + nameString + " not found";
\r
664 env->ReleaseStringUTFChars(variable, name);
\r
665 return 0; //throwException(env, message);
\r
669 string message = fmuId;
\r
670 env->ReleaseStringUTFChars(id, fmuId);
\r
671 return 0; //throwException(env, "unloadFMU: Model id " + message + " not found");
\r