1 package org.simantics.fmil.core;
4 import java.io.IOException;
5 import java.io.RandomAccessFile;
6 import java.nio.channels.FileChannel;
7 import java.nio.channels.FileLock;
8 import java.nio.file.Paths;
9 import java.util.ArrayList;
10 import java.util.HashSet;
11 import java.util.List;
13 import java.util.UUID;
15 import org.eclipse.core.runtime.FileLocator;
16 import org.eclipse.core.runtime.Platform;
17 import org.osgi.framework.Bundle;
18 import org.simantics.fmil.core.ExecEnvironment.ARCHType;
19 import org.simantics.fmil.core.ExecEnvironment.OSType;
20 import org.simantics.utils.FileUtils;
21 import org.slf4j.Logger;
22 import org.slf4j.LoggerFactory;
24 import gnu.trove.list.array.TIntArrayList;
25 import gnu.trove.map.hash.TObjectIntHashMap;
30 private static final Logger LOGGER = LoggerFactory.getLogger(FMIL.class);
35 private static int OK = 0;
36 private static int ERROR = 1;
37 private static String UNSATISFIED_LINK = "Method not found. DLL might not be loaded properly.";
38 public static final String TEMP_FMU_DIRECTORY_NAME = "fmil";
39 public static String TEMP_FMU_COMMON_DIRECTORY;
40 public static String LOCK_FILE_NAME = "fmil.lock";
42 public static Object syncObject = new Object();
45 * Static: load native libraries required for the FMU simulation to work.
49 File[] libraries = new File[2];
53 ExecEnvironment env = ExecEnvironment.calculate();
54 if (env.os == OSType.WINDOWS) {
55 if (env.arch == ARCHType.X86) {
56 bundle = Platform.getBundle("org.simantics.fmil.win32");
57 } else if (env.arch == ARCHType.X86_64) {
58 bundle = Platform.getBundle("org.simantics.fmil.win64");
64 String root = FileLocator.getBundleFile(bundle).getAbsolutePath();
65 // if (env.arch == ARCHType.X86_64) {
66 // File newFIle = new File(root, "libraries/libexpat.dll");
67 // System.load(newFIle.getAbsolutePath());
69 // libraries[0] = new File(root, "libraries/zlibwapi.dll");
70 // libraries[1] = new File(root, "libraries/miniunz.dll");
71 libraries[0] = new File(root, "libraries/fmilib_shared.dll");
72 libraries[1] = new File(root, "libraries/FMUSimulator.dll");
79 for(File library : libraries) {
81 System.err.println("FMU library not loaded. FMU simulation not working.");
83 } else if(!library.isFile()) {
84 System.err.println(library.getAbsolutePath() + " not found");
87 System.load(library.getAbsolutePath());
88 } catch (Throwable t) {
89 System.err.println(t.getMessage());
96 * Static: initialize fmu temp folder from current working directory
99 TEMP_FMU_COMMON_DIRECTORY = Paths.get(".").toAbsolutePath().normalize().toString();
102 public static void setTempFMUCommonDir(File dir) {
103 TEMP_FMU_COMMON_DIRECTORY = dir.getAbsolutePath();
106 private String fmuDir;
109 public String TEMP_FOLDER_1;
110 public String TEMP_FOLDER_2;
111 public String TEMP_FMU_DIRECTORY;
112 private String dirName;
114 private String[] variableNames;
115 private String[] variableDescriptions;
116 private String[] variableDeclaredTypes;
117 private int[] variableReferences;
118 private int[] variableTypes;
119 private int[] variableCausalities;
120 private int[] variableVariabilities;
122 private String[] declaredTypes;
123 private String[] declaredTypeDescriptions;
124 private String[] declaredTypeQuantities;
125 private String[] declaredTypeUnits;
127 private TObjectIntHashMap<String> variableMap = new TObjectIntHashMap<String>();
129 private Set<String> subscriptionSet = new HashSet<String>();
130 private TIntArrayList subscription = new TIntArrayList();
131 private ArrayList<String> subscribedNames = new ArrayList<String>();
133 public List<String> getSubscribedNames() {
134 return subscribedNames;
137 public boolean subscribe(String name) throws FMILException {
139 int vr = variableMap.get(name);
140 if(vr == 0) return false;
141 if(!subscriptionSet.add(name)) return false;
142 subscribedNames.add(name);
143 System.err.println("subscribed : " + name + " => " + subscribedNames.size());
144 subscription.add(vr);
145 subscribe(new int[] { vr });
150 // Create a directory for this control
151 File tempDir = new File(TEMP_FMU_COMMON_DIRECTORY, UUID.randomUUID().toString());
153 TEMP_FMU_DIRECTORY = tempDir.getAbsolutePath();
155 // Create two directories inside the temp directory for this control
156 dirName = UUID.randomUUID().toString();
157 File fmuDir = new File(TEMP_FMU_DIRECTORY, dirName);
160 TEMP_FOLDER_1 = fmuDir.toString();
161 TEMP_FOLDER_2 = fmuDir.toString() + "_2";
163 // Lock fmu directory in temp directory
167 public int getModelIDNew() {
171 public String getModelID() {
175 public String getFmuDir() {
180 * Load fmu from a given file path. Releases the (possible) previously
183 * @param path absolute file path for fmu file
184 * @throws FMILException
186 private int fmuN = 0;
187 private boolean fmuLoaded = false;
188 public void loadFMUFile(String path) throws FMILException {
190 synchronized(syncObject) {
193 fmuDir = TEMP_FOLDER_1;
196 fmuDir = TEMP_FOLDER_2;
200 File tempDir = new File(fmuDir);
201 if(tempDir.isDirectory()) {
203 FileUtils.deleteAll(tempDir);
204 } catch (IOException e) {
205 throw new FMILException("Could not create temp folder for fmu");
214 String tmpPath = tempDir.getAbsolutePath();
215 if(!tmpPath.endsWith("\\"))
216 tmpPath = tmpPath + "\\";
217 id = loadFMUFile_(path, tmpPath);
220 getAllVariableReferences();
222 for(int i=0;i<variableNames.length;i++) {
223 variableMap.put(variableNames[i], variableReferences[i]);
227 } catch (UnsatisfiedLinkError err) {
228 throw new FMILException(UNSATISFIED_LINK, err);
229 } catch (Exception e) {
230 throw new FMILException(e.getMessage());
235 private native int loadFMUFile_(String path, String toDir);
238 * Set a step length for simulation
240 * @param step Step length for simulation
241 * @throws FMILException
243 public void setStepLength(double step) throws FMILException {
244 synchronized(syncObject) {
247 int ret = setStepLength_(getModelIDNew(), step);
249 LOGGER.warn("Function return value != OK, an exception should have been thrown from native code!");
251 } catch (UnsatisfiedLinkError err) {
252 throw new FMILException(UNSATISFIED_LINK);
253 } catch (Exception e) {
254 throw new FMILException(e.getMessage());
259 private native int setStepLength_(int id, double step);
262 * Instantiates a simulation.
264 * Make sure that an FMU is loaded first.
265 * @throws FMILException
267 public void instantiateSimulation() throws FMILException {
268 synchronized(syncObject) {
272 int ret = instantiateSimulation_(getModelIDNew());
274 LOGGER.warn("Function return value != OK, an exception should have been thrown from native code!");
276 } catch (FMILException e) {
278 } catch (UnsatisfiedLinkError err) {
279 throw new FMILException(UNSATISFIED_LINK);
280 } catch (Exception e) {
281 throw new FMILException(e.getMessage());
286 private native int instantiateSimulation_(int id) throws FMILException;
290 * Initializes a simulation.
292 * Make sure that simulation is instantiated first!
293 * @throws FMILException
295 public void initializeSimulation() throws FMILException {
296 synchronized(syncObject) {
300 int ret = initializeSimulation_(getModelIDNew());
302 LOGGER.warn("Function return value != OK, an exception should have been thrown from native code!");
304 } catch (FMILException e) {
306 } catch (UnsatisfiedLinkError err) {
307 throw new FMILException(UNSATISFIED_LINK);
308 } catch (Exception e) {
309 throw new FMILException(e.getMessage());
314 private native int initializeSimulation_(int id) throws FMILException;
317 * Subscribe a set of variables from a loaded simulation.
319 * Make sure that an FMU is loaded first.
320 * @param variables Array of variables
321 * @throws FMILException
323 public void subscribe(int[] variables) throws FMILException {
324 synchronized(syncObject) {
328 int ret = subscribe_(getModelIDNew(), variables);
330 LOGGER.warn("Function return value != OK, an exception should have been thrown from native code!");
332 } catch (UnsatisfiedLinkError err) {
333 throw new FMILException(UNSATISFIED_LINK);
334 } catch (Exception e) {
335 throw new FMILException(e.getMessage());
340 private native int subscribe_(int id, int[] variables);
343 * Set a new (Real, double) value for a variable. If the variable is a
344 * parameter, the change is effective immediately.
346 * @param name Variable
347 * @param value New (Real, double) value
348 * @throws FMILException
350 public void setRealValue(String name, double value) throws FMILException {
352 synchronized(syncObject) {
356 int ret = setRealValue_(getModelIDNew(), variableMap.get(name), value);
358 LOGGER.warn("Function return value != OK, an exception should have been thrown from native code!");
360 } catch (FMILException e) {
362 } catch (UnsatisfiedLinkError err) {
363 throw new FMILException(UNSATISFIED_LINK);
364 } catch (Exception e) {
365 throw new FMILException(e.getMessage());
372 public void setRealValue(int variableReference, double value) throws FMILException {
374 synchronized(syncObject) {
378 int ret = setRealValue_(getModelIDNew(), variableReference, value);
380 LOGGER.warn("Function return value != OK, an exception should have been thrown from native code!");
382 } catch (FMILException e) {
384 } catch (UnsatisfiedLinkError err) {
385 throw new FMILException(UNSATISFIED_LINK);
386 } catch (Exception e) {
387 throw new FMILException(e.getMessage());
394 private native int setRealValue_(int id, int variableReference, double value) throws FMILException;
397 // * Set a new (integer) value for a variable. If the variable is a
398 // * parameter, the change is effective immediately.
400 // * @param name Variable
401 // * @param value New (integer) value
402 // * @throws FMILException
404 // public void setIntegerValue(String name, int value) throws FMILException {
405 // synchronized(syncObject) {
409 // int ret = setIntegerValue_(getModelID(), name, value);
411 // throw new FMILException(getLastErrorMessage());
413 // } catch (UnsatisfiedLinkError err) {
414 // throw new FMILException(UNSATISFIED_LINK);
415 // } catch (Exception e) {
416 // throw new FMILException(e.getMessage());
420 // private native int setIntegerValue_(String id, String name, int value);
423 // * Set a new (boolean) value for a variable. If the variable is a
424 // * parameter, the change is effective immediately.
426 // * @param name Variable
427 // * @param value New (boolean) value
428 // * @throws FMILException
430 // public void setBooleanValue(String name, boolean value) throws FMILException {
431 // synchronized(syncObject) {
435 // int ret = setBooleanValue_(getModelID(), name, value);
437 // throw new FMILException(getLastErrorMessage());
439 // } catch (UnsatisfiedLinkError err) {
440 // throw new FMILException(UNSATISFIED_LINK);
441 // } catch (Exception e) {
442 // throw new FMILException(e.getMessage());
446 // private native int setBooleanValue_(String id, String name, boolean value);
448 // public void setTime(double time) throws FMILException {
449 // synchronized(syncObject) {
453 // int ret = setTime_(getModelID(), time);
455 // throw new FMILException(getLastErrorMessage());
457 // } catch (UnsatisfiedLinkError err) {
458 // throw new FMILException(UNSATISFIED_LINK);
459 // } catch (Exception e) {
460 // throw new FMILException(e.getMessage());
464 // private native int setTime_(String id, double time);
467 * Simulate one step forward. The step length can be set with
470 * @throws FMILException
472 public void simulateStep() throws FMILException {
473 synchronized(syncObject) {
477 int ret = simulateStep_(getModelIDNew());
479 LOGGER.warn("Function return value != OK, an exception should have been thrown from native code!");
481 } catch (FMILException e) {
483 } catch (UnsatisfiedLinkError err) {
484 throw new FMILException(UNSATISFIED_LINK);
485 } catch (Exception e) {
486 throw new FMILException(e.getMessage());
490 private native int simulateStep_(int id) throws FMILException;
493 * Get an array containing the current values for subscribed variables. The
494 * values are in the same order as in the subscription.
496 * @param results An array the size of subscribed results
499 public double[] getSubscribedResults() throws FMILException {
500 synchronized(syncObject) {
504 double[] results = new double[subscription.size()];
505 return getSubscribedResults_(getModelIDNew(), results);
507 } catch (UnsatisfiedLinkError err) {
508 throw new FMILException(UNSATISFIED_LINK);
509 } catch (Exception e) {
510 throw new FMILException(e.getMessage());
515 private native double[] getSubscribedResults_(int id, double[] results);
519 * Unload FMU and the dll:s that it requires.
521 * To be called after all FMU simulations are ended.
522 * If the fmu is loaded again / changed, call to loadFMUFile is sufficient. loadFMUFile
523 * releases the previous fmu.dll
525 * @throws FMILException
527 public void unloadFMU() throws FMILException {
528 synchronized(syncObject) {
532 unlockFMUDirectory();
534 int ret = unloadFMU_(getModelIDNew());
536 LOGGER.warn("Function return value != OK, an exception should have been thrown from native code!");
539 removeFMUDirectoryContents();
541 } catch (FMILException e) {
543 } catch (UnsatisfiedLinkError err) {
544 throw new FMILException(UNSATISFIED_LINK);
545 } catch (Exception e) {
546 throw new FMILException(e.getMessage());
550 private native int unloadFMU_(int id) throws FMILException;
553 // * Checks if fmu has been initialized
554 // * @return current simulation time
556 // public boolean isInitialized() throws FMILException {
557 // synchronized(syncObject) {
559 // return isInitialized_(getModelID());
560 // } catch (UnsatisfiedLinkError err) {
561 // throw new FMILException(UNSATISFIED_LINK);
562 // } catch (Exception e) {
563 // throw new FMILException(e.getMessage());
568 // private native boolean isInitialized_(String id);
571 * Get the current simulation time
572 * @return current simulation time
574 public double getTime() throws FMILException {
575 synchronized(syncObject) {
579 return getTime_(getModelIDNew());
581 } catch (UnsatisfiedLinkError err) {
582 throw new FMILException(UNSATISFIED_LINK);
583 } catch (Exception e) {
584 throw new FMILException(e.getMessage());
589 private native double getTime_(int id);
592 * Get all variables in a loaded model
593 * @return all variables in a loaded model
595 public String[] getAllVariables() throws FMILException {
596 synchronized(syncObject) {
600 if(variableNames == null) {
601 variableNames = getAllVariables_(getModelIDNew());
603 return variableNames;
605 } catch (UnsatisfiedLinkError err) {
606 throw new FMILException(UNSATISFIED_LINK);
607 } catch (Exception e) {
608 throw new FMILException(e.getMessage());
613 private native String[] getAllVariables_(int id);
615 public String[] getAllVariableDescriptions() throws FMILException {
616 synchronized(syncObject) {
620 if(variableDescriptions == null) {
621 variableDescriptions = getAllVariableDescriptions_(getModelIDNew());
623 return variableDescriptions;
625 } catch (UnsatisfiedLinkError err) {
626 throw new FMILException(UNSATISFIED_LINK);
627 } catch (Exception e) {
628 throw new FMILException(e.getMessage());
633 private native String[] getAllVariableDescriptions_(int id);
635 public String[] getAllVariableDeclaredTypes() throws FMILException {
636 synchronized(syncObject) {
640 if(variableDeclaredTypes == null) {
641 variableDeclaredTypes = getAllVariableDeclaredTypes_(getModelIDNew());
643 return variableDeclaredTypes;
645 } catch (UnsatisfiedLinkError err) {
646 throw new FMILException(UNSATISFIED_LINK);
647 } catch (Exception e) {
648 throw new FMILException(e.getMessage());
653 private native String[] getAllVariableDeclaredTypes_(int id);
655 public int[] getAllVariableReferences() throws FMILException {
656 synchronized(syncObject) {
660 if(variableReferences == null) {
661 variableReferences = getAllVariableReferences_(getModelIDNew(), new int[variableNames.length]);
663 return variableReferences;
665 } catch (UnsatisfiedLinkError err) {
666 throw new FMILException(UNSATISFIED_LINK);
667 } catch (Exception e) {
668 throw new FMILException(e.getMessage());
673 private native int[] getAllVariableReferences_(int id, int[] array);
675 public int[] getAllVariableTypes() throws FMILException {
676 synchronized(syncObject) {
680 if(variableTypes == null) {
681 variableTypes = getAllVariableTypes_(getModelIDNew(), new int[variableNames.length]);
683 return variableTypes;
685 } catch (UnsatisfiedLinkError err) {
686 throw new FMILException(UNSATISFIED_LINK);
687 } catch (Exception e) {
688 throw new FMILException(e.getMessage());
693 private native int[] getAllVariableTypes_(int id, int[] array);
695 public int[] getAllVariableCausalities() throws FMILException {
696 synchronized(syncObject) {
700 if(variableCausalities == null) {
701 variableCausalities = getAllVariableCausalities_(getModelIDNew(), new int[variableNames.length]);
703 return variableCausalities;
705 } catch (UnsatisfiedLinkError err) {
706 throw new FMILException(UNSATISFIED_LINK);
707 } catch (Exception e) {
708 throw new FMILException(e.getMessage());
713 private native int[] getAllVariableCausalities_(int id, int[] array);
715 public int[] getAllVariableVariabilities() throws FMILException {
716 synchronized(syncObject) {
720 if(variableVariabilities == null) {
721 variableVariabilities = getAllVariableVariabilities_(getModelIDNew(), new int[variableNames.length]);
723 return variableVariabilities;
725 } catch (UnsatisfiedLinkError err) {
726 throw new FMILException(UNSATISFIED_LINK);
727 } catch (Exception e) {
728 throw new FMILException(e.getMessage());
733 private native int[] getAllVariableVariabilities_(int id, int[] array);
736 * Get all variables in a loaded model
737 * @return all variables in a loaded model
739 public String[] getAllDeclaredTypes() throws FMILException {
740 synchronized(syncObject) {
744 if(declaredTypes == null) {
745 declaredTypes = getAllDeclaredTypes_(getModelIDNew());
747 return declaredTypes;
749 } catch (UnsatisfiedLinkError err) {
750 throw new FMILException(UNSATISFIED_LINK);
751 } catch (Exception e) {
752 throw new FMILException(e.getMessage());
757 private native String[] getAllDeclaredTypes_(int id);
759 public String[] getAllDeclaredTypeDescriptions() throws FMILException {
760 synchronized(syncObject) {
764 if(declaredTypeDescriptions == null) {
765 declaredTypeDescriptions = getAllDeclaredTypeDescriptions_(getModelIDNew());
767 return declaredTypeDescriptions;
769 } catch (UnsatisfiedLinkError err) {
770 throw new FMILException(UNSATISFIED_LINK);
771 } catch (Exception e) {
772 throw new FMILException(e.getMessage());
777 private native String[] getAllDeclaredTypeDescriptions_(int id);
779 public String[] getAllDeclaredTypeQuantities() throws FMILException {
780 synchronized(syncObject) {
784 if(declaredTypeQuantities == null) {
785 declaredTypeQuantities = getAllDeclaredTypeQuantities_(getModelIDNew());
787 return declaredTypeQuantities;
789 } catch (UnsatisfiedLinkError err) {
790 throw new FMILException(UNSATISFIED_LINK);
791 } catch (Exception e) {
792 throw new FMILException(e.getMessage());
797 private native String[] getAllDeclaredTypeQuantities_(int id);
799 public String[] getAllDeclaredTypeUnits() throws FMILException {
800 synchronized(syncObject) {
804 if(declaredTypeUnits == null) {
805 declaredTypeUnits = getAllDeclaredTypeUnits_(getModelIDNew());
807 return declaredTypeUnits;
809 } catch (UnsatisfiedLinkError err) {
810 throw new FMILException(UNSATISFIED_LINK);
811 } catch (Exception e) {
812 throw new FMILException(e.getMessage());
817 private native String[] getAllDeclaredTypeUnits_(int id);
822 // * Get all variables from model that match the filter (and time variable)
824 // * @param regexp Regular expression filter
825 // * @return An array of variable names that match regexp filter (and time-variable)
826 // * @throws FMILException
828 // public String[] filterVariables(String regexp) throws FMILException {
829 // synchronized(syncObject) {
832 // return filterVariables_(getModelID(), regexp + "|time");
834 // } catch (UnsatisfiedLinkError err) {
835 // throw new FMILException(UNSATISFIED_LINK);
836 // } catch (Exception e) {
837 // throw new FMILException(e.getMessage());
842 // private native String[] filterVariables_(String id, String regexp);
845 // * Get the last error message
846 // * @return Last error message
848 // public String getLastErrorMessage() throws FMILException {
849 // synchronized(syncObject) {
854 // //return getLastErrorMessage_(getModelID());
856 // } catch (UnsatisfiedLinkError err) {
857 // throw new FMILException(UNSATISFIED_LINK);
858 // } catch (Exception e) {
859 // throw new FMILException(e.getMessage());
864 // private native String getLastErrorMessage_(String id);
867 * Get a real (double) value for variable
868 * @param name Name of the variable
870 * @throws FMILException
872 public double getRealValue(String name) throws FMILException {
873 synchronized(syncObject) {
876 // TODO: printtaa id ja name, jotta saadaan virheessä kiinni
877 double result = getRealValue_(getModelIDNew(), variableMap.get(name));
878 System.err.println("getRealValue " + name + " = " + result);
880 } catch (UnsatisfiedLinkError err) {
881 throw new FMILException(UNSATISFIED_LINK);
882 } catch (Exception e) {
883 throw new FMILException(e.getMessage());
888 private native double getRealValue_(int id, int variableReference);
891 // * Get a string value for variable
892 // * @param name Name of the variable
894 // * @throws FMILException
896 // public String getStringValue(String name) throws FMILException {
897 // synchronized(syncObject) {
900 // return getStringValue_(getModelID(), name);
901 // } catch (UnsatisfiedLinkError err) {
902 // throw new FMILException(UNSATISFIED_LINK);
903 // } catch (Exception e) {
904 // throw new FMILException(e.getMessage());
909 // private native String getStringValue_(String id, String name);
912 // * Get an integer value for variable
913 // * @param name Name of the variable
915 // * @throws FMILException
917 // public int getIntegerValue(String name) throws FMILException {
918 // synchronized(syncObject) {
921 // return getIntegerValue_(getModelID(), name);
922 // } catch (UnsatisfiedLinkError err) {
923 // throw new FMILException(UNSATISFIED_LINK);
924 // } catch (Exception e) {
925 // throw new FMILException(e.getMessage());
930 // private native int getIntegerValue_(String id, String name);
933 // * Get a real (double) value for variable
934 // * @param name Name of the variable
936 // * @throws FMILException
938 // public boolean getBooleanValue(String name) throws FMILException {
939 // synchronized(syncObject) {
942 // return getBooleanValue_(getModelID(), name);
943 // } catch (UnsatisfiedLinkError err) {
944 // throw new FMILException(UNSATISFIED_LINK);
945 // } catch (Exception e) {
946 // throw new FMILException(e.getMessage());
951 // private native boolean getBooleanValue_(String id, String name);
953 private FileChannel channel;
954 private FileLock lock;
956 @SuppressWarnings("resource")
957 private boolean lockFMUDirectory() {
960 // Get a file channel for the lock file
961 File lockFile = new File(TEMP_FMU_DIRECTORY, LOCK_FILE_NAME);
962 if(!lockFile.isFile())
963 lockFile.createNewFile();
965 channel = new RandomAccessFile(lockFile, "rw").getChannel();
967 // Use the file channel to create a lock on the file.
968 // This method blocks until it can retrieve the lock.
969 lock = channel.lock();
971 // // Try acquiring the lock without blocking. This method returns
972 // // null or throws an exception if the file is already locked.
974 // lock = channel.tryLock();
975 // } catch (OverlappingFileLockException e) {
976 // // File is already locked in this thread or virtual machine
978 } catch (IOException e) {
985 private boolean unlockFMUDirectory() {
994 } catch (IOException e) {
1000 private boolean removeFMUDirectoryContents() {
1003 File tempDir = new File(TEMP_FMU_DIRECTORY);
1004 FileUtils.deleteAll(tempDir);
1006 } catch (IOException e) {
1013 protected void finalize() throws Throwable {
1016 } catch (Throwable t) {
1017 LOGGER.error("Could not unload native FMU!", t);