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.util.ArrayList;
9 import java.util.HashSet;
10 import java.util.List;
12 import java.util.UUID;
14 import org.eclipse.core.runtime.FileLocator;
15 import org.eclipse.core.runtime.Platform;
16 import org.osgi.framework.Bundle;
17 import org.simantics.Simantics;
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 private static 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
99 File dir = Simantics.getTemporaryDirectory(TEMP_FMU_DIRECTORY_NAME);
100 TEMP_FMU_COMMON_DIRECTORY = dir.getAbsolutePath();
104 private String fmuDir;
107 public String TEMP_FOLDER_1;
108 public String TEMP_FOLDER_2;
109 public String TEMP_FMU_DIRECTORY;
110 private String dirName;
112 private String[] variableNames;
113 private String[] variableDescriptions;
114 private String[] variableDeclaredTypes;
115 private int[] variableReferences;
116 private int[] variableTypes;
117 private int[] variableCausalities;
118 private int[] variableVariabilities;
120 private String[] declaredTypes;
121 private String[] declaredTypeDescriptions;
122 private String[] declaredTypeQuantities;
123 private String[] declaredTypeUnits;
125 private TObjectIntHashMap<String> variableMap = new TObjectIntHashMap<String>();
127 private Set<String> subscriptionSet = new HashSet<String>();
128 private TIntArrayList subscription = new TIntArrayList();
129 private ArrayList<String> subscribedNames = new ArrayList<String>();
131 public List<String> getSubscribedNames() {
132 return subscribedNames;
135 public boolean subscribe(String name) throws FMILException {
137 int vr = variableMap.get(name);
138 if(vr == 0) return false;
139 if(!subscriptionSet.add(name)) return false;
140 subscribedNames.add(name);
141 System.err.println("subscribed : " + name + " => " + subscribedNames.size());
142 subscription.add(vr);
143 subscribe(new int[] { vr });
148 // Create a directory for this control
149 File tempDir = new File(TEMP_FMU_COMMON_DIRECTORY, UUID.randomUUID().toString());
151 TEMP_FMU_DIRECTORY = tempDir.getAbsolutePath();
153 // Create two directories inside the temp directory for this control
154 dirName = UUID.randomUUID().toString();
155 File fmuDir = new File(TEMP_FMU_DIRECTORY, dirName);
158 TEMP_FOLDER_1 = fmuDir.toString();
159 TEMP_FOLDER_2 = fmuDir.toString() + "_2";
161 // Lock fmu directory in temp directory
165 public int getModelIDNew() {
169 public String getModelID() {
173 public String getFmuDir() {
178 * Load fmu from a given file path. Releases the (possible) previously
181 * @param path absolute file path for fmu file
182 * @throws FMILException
184 private int fmuN = 0;
185 private boolean fmuLoaded = false;
186 public void loadFMUFile(String path) throws FMILException {
188 synchronized(syncObject) {
191 fmuDir = TEMP_FOLDER_1;
194 fmuDir = TEMP_FOLDER_2;
198 File tempDir = new File(fmuDir);
199 if(tempDir.isDirectory()) {
201 FileUtils.deleteAll(tempDir);
202 } catch (IOException e) {
203 throw new FMILException("Could not create temp folder for fmu");
212 String tmpPath = tempDir.getAbsolutePath();
213 if(!tmpPath.endsWith("\\"))
214 tmpPath = tmpPath + "\\";
215 id = loadFMUFile_(path, tmpPath);
218 getAllVariableReferences();
220 for(int i=0;i<variableNames.length;i++) {
221 variableMap.put(variableNames[i], variableReferences[i]);
225 } catch (UnsatisfiedLinkError err) {
226 throw new FMILException(UNSATISFIED_LINK, err);
227 } catch (Exception e) {
228 throw new FMILException(e.getMessage());
233 private native int loadFMUFile_(String path, String toDir);
236 * Set a step length for simulation
238 * @param step Step length for simulation
239 * @throws FMILException
241 public void setStepLength(double step) throws FMILException {
242 synchronized(syncObject) {
245 int ret = setStepLength_(getModelIDNew(), step);
247 LOGGER.warn("Function return value != OK, an exception should have been thrown from native code!");
249 } catch (UnsatisfiedLinkError err) {
250 throw new FMILException(UNSATISFIED_LINK);
251 } catch (Exception e) {
252 throw new FMILException(e.getMessage());
257 private native int setStepLength_(int id, double step);
260 * Instantiates a simulation.
262 * Make sure that an FMU is loaded first.
263 * @throws FMILException
265 public void instantiateSimulation() throws FMILException {
266 synchronized(syncObject) {
270 int ret = instantiateSimulation_(getModelIDNew());
272 LOGGER.warn("Function return value != OK, an exception should have been thrown from native code!");
274 } catch (FMILException e) {
276 } catch (UnsatisfiedLinkError err) {
277 throw new FMILException(UNSATISFIED_LINK);
278 } catch (Exception e) {
279 throw new FMILException(e.getMessage());
284 private native int instantiateSimulation_(int id) throws FMILException;
288 * Initializes a simulation.
290 * Make sure that simulation is instantiated first!
291 * @throws FMILException
293 public void initializeSimulation() throws FMILException {
294 synchronized(syncObject) {
298 int ret = initializeSimulation_(getModelIDNew());
300 LOGGER.warn("Function return value != OK, an exception should have been thrown from native code!");
302 } catch (FMILException e) {
304 } catch (UnsatisfiedLinkError err) {
305 throw new FMILException(UNSATISFIED_LINK);
306 } catch (Exception e) {
307 throw new FMILException(e.getMessage());
312 private native int initializeSimulation_(int id) throws FMILException;
315 * Subscribe a set of variables from a loaded simulation.
317 * Make sure that an FMU is loaded first.
318 * @param variables Array of variables
319 * @throws FMILException
321 public void subscribe(int[] variables) throws FMILException {
322 synchronized(syncObject) {
326 int ret = subscribe_(getModelIDNew(), variables);
328 LOGGER.warn("Function return value != OK, an exception should have been thrown from native code!");
330 } catch (UnsatisfiedLinkError err) {
331 throw new FMILException(UNSATISFIED_LINK);
332 } catch (Exception e) {
333 throw new FMILException(e.getMessage());
338 private native int subscribe_(int id, int[] variables);
341 * Set a new (Real, double) value for a variable. If the variable is a
342 * parameter, the change is effective immediately.
344 * @param name Variable
345 * @param value New (Real, double) value
346 * @throws FMILException
348 public void setRealValue(String name, double value) throws FMILException {
350 synchronized(syncObject) {
354 int ret = setRealValue_(getModelIDNew(), variableMap.get(name), value);
356 LOGGER.warn("Function return value != OK, an exception should have been thrown from native code!");
358 } catch (FMILException e) {
360 } catch (UnsatisfiedLinkError err) {
361 throw new FMILException(UNSATISFIED_LINK);
362 } catch (Exception e) {
363 throw new FMILException(e.getMessage());
370 public void setRealValue(int variableReference, double value) throws FMILException {
372 synchronized(syncObject) {
376 int ret = setRealValue_(getModelIDNew(), variableReference, value);
378 LOGGER.warn("Function return value != OK, an exception should have been thrown from native code!");
380 } catch (FMILException e) {
382 } catch (UnsatisfiedLinkError err) {
383 throw new FMILException(UNSATISFIED_LINK);
384 } catch (Exception e) {
385 throw new FMILException(e.getMessage());
392 private native int setRealValue_(int id, int variableReference, double value) throws FMILException;
395 // * Set a new (integer) value for a variable. If the variable is a
396 // * parameter, the change is effective immediately.
398 // * @param name Variable
399 // * @param value New (integer) value
400 // * @throws FMILException
402 // public void setIntegerValue(String name, int value) throws FMILException {
403 // synchronized(syncObject) {
407 // int ret = setIntegerValue_(getModelID(), name, value);
409 // throw new FMILException(getLastErrorMessage());
411 // } catch (UnsatisfiedLinkError err) {
412 // throw new FMILException(UNSATISFIED_LINK);
413 // } catch (Exception e) {
414 // throw new FMILException(e.getMessage());
418 // private native int setIntegerValue_(String id, String name, int value);
421 // * Set a new (boolean) value for a variable. If the variable is a
422 // * parameter, the change is effective immediately.
424 // * @param name Variable
425 // * @param value New (boolean) value
426 // * @throws FMILException
428 // public void setBooleanValue(String name, boolean value) throws FMILException {
429 // synchronized(syncObject) {
433 // int ret = setBooleanValue_(getModelID(), name, value);
435 // throw new FMILException(getLastErrorMessage());
437 // } catch (UnsatisfiedLinkError err) {
438 // throw new FMILException(UNSATISFIED_LINK);
439 // } catch (Exception e) {
440 // throw new FMILException(e.getMessage());
444 // private native int setBooleanValue_(String id, String name, boolean value);
446 // public void setTime(double time) throws FMILException {
447 // synchronized(syncObject) {
451 // int ret = setTime_(getModelID(), time);
453 // throw new FMILException(getLastErrorMessage());
455 // } catch (UnsatisfiedLinkError err) {
456 // throw new FMILException(UNSATISFIED_LINK);
457 // } catch (Exception e) {
458 // throw new FMILException(e.getMessage());
462 // private native int setTime_(String id, double time);
465 * Simulate one step forward. The step length can be set with
468 * @throws FMILException
470 public void simulateStep() throws FMILException {
471 synchronized(syncObject) {
475 int ret = simulateStep_(getModelIDNew());
477 LOGGER.warn("Function return value != OK, an exception should have been thrown from native code!");
479 } catch (FMILException e) {
481 } catch (UnsatisfiedLinkError err) {
482 throw new FMILException(UNSATISFIED_LINK);
483 } catch (Exception e) {
484 throw new FMILException(e.getMessage());
488 private native int simulateStep_(int id) throws FMILException;
491 * Get an array containing the current values for subscribed variables. The
492 * values are in the same order as in the subscription.
494 * @param results An array the size of subscribed results
497 public double[] getSubscribedResults() throws FMILException {
498 synchronized(syncObject) {
502 double[] results = new double[subscription.size()];
503 return getSubscribedResults_(getModelIDNew(), results);
505 } catch (UnsatisfiedLinkError err) {
506 throw new FMILException(UNSATISFIED_LINK);
507 } catch (Exception e) {
508 throw new FMILException(e.getMessage());
513 private native double[] getSubscribedResults_(int id, double[] results);
517 * Unload FMU and the dll:s that it requires.
519 * To be called after all FMU simulations are ended.
520 * If the fmu is loaded again / changed, call to loadFMUFile is sufficient. loadFMUFile
521 * releases the previous fmu.dll
523 * @throws FMILException
525 public void unloadFMU() throws FMILException {
526 synchronized(syncObject) {
530 unlockFMUDirectory();
532 int ret = unloadFMU_(getModelIDNew());
534 LOGGER.warn("Function return value != OK, an exception should have been thrown from native code!");
537 removeFMUDirectoryContents();
539 } catch (FMILException e) {
541 } catch (UnsatisfiedLinkError err) {
542 throw new FMILException(UNSATISFIED_LINK);
543 } catch (Exception e) {
544 throw new FMILException(e.getMessage());
548 private native int unloadFMU_(int id) throws FMILException;
551 // * Checks if fmu has been initialized
552 // * @return current simulation time
554 // public boolean isInitialized() throws FMILException {
555 // synchronized(syncObject) {
557 // return isInitialized_(getModelID());
558 // } catch (UnsatisfiedLinkError err) {
559 // throw new FMILException(UNSATISFIED_LINK);
560 // } catch (Exception e) {
561 // throw new FMILException(e.getMessage());
566 // private native boolean isInitialized_(String id);
569 * Get the current simulation time
570 * @return current simulation time
572 public double getTime() throws FMILException {
573 synchronized(syncObject) {
577 return getTime_(getModelIDNew());
579 } catch (UnsatisfiedLinkError err) {
580 throw new FMILException(UNSATISFIED_LINK);
581 } catch (Exception e) {
582 throw new FMILException(e.getMessage());
587 private native double getTime_(int id);
590 * Get all variables in a loaded model
591 * @return all variables in a loaded model
593 public String[] getAllVariables() throws FMILException {
594 synchronized(syncObject) {
598 if(variableNames == null) {
599 variableNames = getAllVariables_(getModelIDNew());
601 return variableNames;
603 } catch (UnsatisfiedLinkError err) {
604 throw new FMILException(UNSATISFIED_LINK);
605 } catch (Exception e) {
606 throw new FMILException(e.getMessage());
611 private native String[] getAllVariables_(int id);
613 public String[] getAllVariableDescriptions() throws FMILException {
614 synchronized(syncObject) {
618 if(variableDescriptions == null) {
619 variableDescriptions = getAllVariableDescriptions_(getModelIDNew());
621 return variableDescriptions;
623 } catch (UnsatisfiedLinkError err) {
624 throw new FMILException(UNSATISFIED_LINK);
625 } catch (Exception e) {
626 throw new FMILException(e.getMessage());
631 private native String[] getAllVariableDescriptions_(int id);
633 public String[] getAllVariableDeclaredTypes() throws FMILException {
634 synchronized(syncObject) {
638 if(variableDeclaredTypes == null) {
639 variableDeclaredTypes = getAllVariableDeclaredTypes_(getModelIDNew());
641 return variableDeclaredTypes;
643 } catch (UnsatisfiedLinkError err) {
644 throw new FMILException(UNSATISFIED_LINK);
645 } catch (Exception e) {
646 throw new FMILException(e.getMessage());
651 private native String[] getAllVariableDeclaredTypes_(int id);
653 public int[] getAllVariableReferences() throws FMILException {
654 synchronized(syncObject) {
658 if(variableReferences == null) {
659 variableReferences = getAllVariableReferences_(getModelIDNew(), new int[variableNames.length]);
661 return variableReferences;
663 } catch (UnsatisfiedLinkError err) {
664 throw new FMILException(UNSATISFIED_LINK);
665 } catch (Exception e) {
666 throw new FMILException(e.getMessage());
671 private native int[] getAllVariableReferences_(int id, int[] array);
673 public int[] getAllVariableTypes() throws FMILException {
674 synchronized(syncObject) {
678 if(variableTypes == null) {
679 variableTypes = getAllVariableTypes_(getModelIDNew(), new int[variableNames.length]);
681 return variableTypes;
683 } catch (UnsatisfiedLinkError err) {
684 throw new FMILException(UNSATISFIED_LINK);
685 } catch (Exception e) {
686 throw new FMILException(e.getMessage());
691 private native int[] getAllVariableTypes_(int id, int[] array);
693 public int[] getAllVariableCausalities() throws FMILException {
694 synchronized(syncObject) {
698 if(variableCausalities == null) {
699 variableCausalities = getAllVariableCausalities_(getModelIDNew(), new int[variableNames.length]);
701 return variableCausalities;
703 } catch (UnsatisfiedLinkError err) {
704 throw new FMILException(UNSATISFIED_LINK);
705 } catch (Exception e) {
706 throw new FMILException(e.getMessage());
711 private native int[] getAllVariableCausalities_(int id, int[] array);
713 public int[] getAllVariableVariabilities() throws FMILException {
714 synchronized(syncObject) {
718 if(variableVariabilities == null) {
719 variableVariabilities = getAllVariableVariabilities_(getModelIDNew(), new int[variableNames.length]);
721 return variableVariabilities;
723 } catch (UnsatisfiedLinkError err) {
724 throw new FMILException(UNSATISFIED_LINK);
725 } catch (Exception e) {
726 throw new FMILException(e.getMessage());
731 private native int[] getAllVariableVariabilities_(int id, int[] array);
734 * Get all variables in a loaded model
735 * @return all variables in a loaded model
737 public String[] getAllDeclaredTypes() throws FMILException {
738 synchronized(syncObject) {
742 if(declaredTypes == null) {
743 declaredTypes = getAllDeclaredTypes_(getModelIDNew());
745 return declaredTypes;
747 } catch (UnsatisfiedLinkError err) {
748 throw new FMILException(UNSATISFIED_LINK);
749 } catch (Exception e) {
750 throw new FMILException(e.getMessage());
755 private native String[] getAllDeclaredTypes_(int id);
757 public String[] getAllDeclaredTypeDescriptions() throws FMILException {
758 synchronized(syncObject) {
762 if(declaredTypeDescriptions == null) {
763 declaredTypeDescriptions = getAllDeclaredTypeDescriptions_(getModelIDNew());
765 return declaredTypeDescriptions;
767 } catch (UnsatisfiedLinkError err) {
768 throw new FMILException(UNSATISFIED_LINK);
769 } catch (Exception e) {
770 throw new FMILException(e.getMessage());
775 private native String[] getAllDeclaredTypeDescriptions_(int id);
777 public String[] getAllDeclaredTypeQuantities() throws FMILException {
778 synchronized(syncObject) {
782 if(declaredTypeQuantities == null) {
783 declaredTypeQuantities = getAllDeclaredTypeQuantities_(getModelIDNew());
785 return declaredTypeQuantities;
787 } catch (UnsatisfiedLinkError err) {
788 throw new FMILException(UNSATISFIED_LINK);
789 } catch (Exception e) {
790 throw new FMILException(e.getMessage());
795 private native String[] getAllDeclaredTypeQuantities_(int id);
797 public String[] getAllDeclaredTypeUnits() throws FMILException {
798 synchronized(syncObject) {
802 if(declaredTypeUnits == null) {
803 declaredTypeUnits = getAllDeclaredTypeUnits_(getModelIDNew());
805 return declaredTypeUnits;
807 } catch (UnsatisfiedLinkError err) {
808 throw new FMILException(UNSATISFIED_LINK);
809 } catch (Exception e) {
810 throw new FMILException(e.getMessage());
815 private native String[] getAllDeclaredTypeUnits_(int id);
820 // * Get all variables from model that match the filter (and time variable)
822 // * @param regexp Regular expression filter
823 // * @return An array of variable names that match regexp filter (and time-variable)
824 // * @throws FMILException
826 // public String[] filterVariables(String regexp) throws FMILException {
827 // synchronized(syncObject) {
830 // return filterVariables_(getModelID(), regexp + "|time");
832 // } catch (UnsatisfiedLinkError err) {
833 // throw new FMILException(UNSATISFIED_LINK);
834 // } catch (Exception e) {
835 // throw new FMILException(e.getMessage());
840 // private native String[] filterVariables_(String id, String regexp);
843 // * Get the last error message
844 // * @return Last error message
846 // public String getLastErrorMessage() throws FMILException {
847 // synchronized(syncObject) {
852 // //return getLastErrorMessage_(getModelID());
854 // } catch (UnsatisfiedLinkError err) {
855 // throw new FMILException(UNSATISFIED_LINK);
856 // } catch (Exception e) {
857 // throw new FMILException(e.getMessage());
862 // private native String getLastErrorMessage_(String id);
865 * Get a real (double) value for variable
866 * @param name Name of the variable
868 * @throws FMILException
870 public double getRealValue(String name) throws FMILException {
871 synchronized(syncObject) {
874 // TODO: printtaa id ja name, jotta saadaan virheessä kiinni
875 double result = getRealValue_(getModelIDNew(), variableMap.get(name));
876 System.err.println("getRealValue " + name + " = " + result);
878 } catch (UnsatisfiedLinkError err) {
879 throw new FMILException(UNSATISFIED_LINK);
880 } catch (Exception e) {
881 throw new FMILException(e.getMessage());
886 private native double getRealValue_(int id, int variableReference);
889 // * Get a string value for variable
890 // * @param name Name of the variable
892 // * @throws FMILException
894 // public String getStringValue(String name) throws FMILException {
895 // synchronized(syncObject) {
898 // return getStringValue_(getModelID(), name);
899 // } catch (UnsatisfiedLinkError err) {
900 // throw new FMILException(UNSATISFIED_LINK);
901 // } catch (Exception e) {
902 // throw new FMILException(e.getMessage());
907 // private native String getStringValue_(String id, String name);
910 // * Get an integer value for variable
911 // * @param name Name of the variable
913 // * @throws FMILException
915 // public int getIntegerValue(String name) throws FMILException {
916 // synchronized(syncObject) {
919 // return getIntegerValue_(getModelID(), name);
920 // } catch (UnsatisfiedLinkError err) {
921 // throw new FMILException(UNSATISFIED_LINK);
922 // } catch (Exception e) {
923 // throw new FMILException(e.getMessage());
928 // private native int getIntegerValue_(String id, String name);
931 // * Get a real (double) value for variable
932 // * @param name Name of the variable
934 // * @throws FMILException
936 // public boolean getBooleanValue(String name) throws FMILException {
937 // synchronized(syncObject) {
940 // return getBooleanValue_(getModelID(), name);
941 // } catch (UnsatisfiedLinkError err) {
942 // throw new FMILException(UNSATISFIED_LINK);
943 // } catch (Exception e) {
944 // throw new FMILException(e.getMessage());
949 // private native boolean getBooleanValue_(String id, String name);
951 private FileChannel channel;
952 private FileLock lock;
954 @SuppressWarnings("resource")
955 private boolean lockFMUDirectory() {
958 // Get a file channel for the lock file
959 File lockFile = new File(TEMP_FMU_DIRECTORY, LOCK_FILE_NAME);
960 if(!lockFile.isFile())
961 lockFile.createNewFile();
963 channel = new RandomAccessFile(lockFile, "rw").getChannel();
965 // Use the file channel to create a lock on the file.
966 // This method blocks until it can retrieve the lock.
967 lock = channel.lock();
969 // // Try acquiring the lock without blocking. This method returns
970 // // null or throws an exception if the file is already locked.
972 // lock = channel.tryLock();
973 // } catch (OverlappingFileLockException e) {
974 // // File is already locked in this thread or virtual machine
976 } catch (IOException e) {
983 private boolean unlockFMUDirectory() {
992 } catch (IOException e) {
998 private boolean removeFMUDirectoryContents() {
1001 File tempDir = new File(TEMP_FMU_DIRECTORY);
1002 FileUtils.deleteAll(tempDir);
1004 } catch (IOException e) {
1011 protected void finalize() throws Throwable {
1014 } catch (Throwable t) {
1015 LOGGER.error("Could not unload native FMU!", t);