1 package org.simantics.fmil;
\r
4 import java.io.IOException;
\r
5 import java.io.RandomAccessFile;
\r
6 import java.nio.channels.FileChannel;
\r
7 import java.nio.channels.FileLock;
\r
8 import java.util.ArrayList;
\r
9 import java.util.HashSet;
\r
10 import java.util.List;
\r
11 import java.util.Set;
\r
12 import java.util.UUID;
\r
14 import org.eclipse.core.runtime.FileLocator;
\r
15 import org.eclipse.core.runtime.Platform;
\r
16 import org.osgi.framework.Bundle;
\r
17 import org.simantics.Simantics;
\r
18 import org.simantics.fmil.ExecEnvironment.ARCHType;
\r
19 import org.simantics.fmil.ExecEnvironment.OSType;
\r
20 import org.simantics.utils.FileUtils;
\r
22 import gnu.trove.list.array.TIntArrayList;
\r
23 import gnu.trove.map.hash.TObjectIntHashMap;
\r
31 private static int ERROR = 0;
\r
32 private static int OK = 1;
\r
33 private static String UNSATISFIED_LINK = "Method not found. DLL might not be loaded properly.";
\r
34 private static String TEMP_FMU_DIRECTORY_NAME = "fmil";
\r
35 public static String TEMP_FMU_COMMON_DIRECTORY;
\r
36 public static String LOCK_FILE_NAME = "fmil.lock";
\r
38 public static Object syncObject = new Object();
\r
41 * Static: load native libraries required for the FMU simulation to work.
\r
45 File[] libraries = new File[2];
\r
47 Bundle bundle = null;
\r
49 ExecEnvironment env = ExecEnvironment.calculate();
\r
50 if (env.os == OSType.WINDOWS) {
\r
51 if (env.arch == ARCHType.X86) {
\r
52 bundle = Platform.getBundle("org.simantics.fmil.win32");
\r
53 } else if (env.arch == ARCHType.X86_64) {
\r
54 bundle = Platform.getBundle("org.simantics.fmil.win64");
\r
58 if (bundle != null) {
\r
60 String root = FileLocator.getBundleFile(bundle).getAbsolutePath();
\r
61 // if (env.arch == ARCHType.X86_64) {
\r
62 // File newFIle = new File(root, "libraries/libexpat.dll");
\r
63 // System.load(newFIle.getAbsolutePath());
\r
65 // libraries[0] = new File(root, "libraries/zlibwapi.dll");
\r
66 // libraries[1] = new File(root, "libraries/miniunz.dll");
\r
67 libraries[0] = new File(root, "libraries/fmilib_shared.dll");
\r
68 libraries[1] = new File(root, "libraries/FMUSimulator.dll");
\r
70 catch (Exception e) {
\r
71 e.printStackTrace();
\r
75 for(File library : libraries) {
\r
76 if(library == null) {
\r
77 System.err.println("FMU library not loaded. FMU simulation not working.");
\r
79 } else if(!library.isFile()) {
\r
80 System.err.println(library.getAbsolutePath() + " not found");
\r
83 System.load(library.getAbsolutePath());
\r
84 } catch (Throwable t) {
\r
85 System.err.println(t.getMessage());
\r
92 * Static: initialize fmu temp folder
\r
95 File dir = Simantics.getTemporaryDirectory(TEMP_FMU_DIRECTORY_NAME);
\r
96 TEMP_FMU_COMMON_DIRECTORY = dir.getAbsolutePath();
\r
100 private String fmuDir;
\r
103 public String TEMP_FOLDER_1;
\r
104 public String TEMP_FOLDER_2;
\r
105 public String TEMP_FMU_DIRECTORY;
\r
106 private String dirName;
\r
108 private String[] variableNames;
\r
109 private String[] variableDescriptions;
\r
110 private int[] variableReferences;
\r
111 private int[] variableTypes;
\r
112 private int[] variableCausalities;
\r
113 private int[] variableVariabilities;
\r
114 private TObjectIntHashMap<String> variableMap = new TObjectIntHashMap<String>();
\r
116 private Set<String> subscriptionSet = new HashSet<String>();
\r
117 private TIntArrayList subscription = new TIntArrayList();
\r
118 private ArrayList<String> subscribedNames = new ArrayList<String>();
\r
120 public List<String> getSubscribedNames() {
\r
121 return subscribedNames;
\r
124 public boolean subscribe(String name) throws FMILException {
\r
126 int vr = variableMap.get(name);
\r
127 if(vr == 0) return false;
\r
128 if(!subscriptionSet.add(name)) return false;
\r
129 subscribedNames.add(name);
\r
130 System.err.println("subscribed : " + name + " => " + subscribedNames.size());
\r
131 subscription.add(vr);
\r
132 subscribe(new int[] { vr });
\r
137 // Create a directory for this control
\r
138 File tempDir = new File(TEMP_FMU_COMMON_DIRECTORY, UUID.randomUUID().toString());
\r
140 TEMP_FMU_DIRECTORY = tempDir.getAbsolutePath();
\r
142 // Create two directories inside the temp directory for this control
\r
143 dirName = UUID.randomUUID().toString();
\r
144 File fmuDir = new File(TEMP_FMU_DIRECTORY, dirName);
\r
147 TEMP_FOLDER_1 = fmuDir.toString();
\r
148 TEMP_FOLDER_2 = fmuDir.toString() + "_2";
\r
150 // Lock fmu directory in temp directory
\r
151 lockFMUDirectory();
\r
154 public int getModelIDNew() {
\r
158 public String getModelID() {
\r
162 public String getFmuDir() {
\r
167 * Load fmu from a given file path. Releases the (possible) previously
\r
170 * @param path absolute file path for fmu file
\r
171 * @throws FMILException
\r
173 private int fmuN = 0;
\r
174 private boolean fmuLoaded = false;
\r
175 public void loadFMUFile(String path) throws FMILException {
\r
177 synchronized(syncObject) {
\r
179 if(fmuN % 2 == 0) {
\r
180 fmuDir = TEMP_FOLDER_1;
\r
183 fmuDir = TEMP_FOLDER_2;
\r
187 File tempDir = new File(fmuDir);
\r
188 if(tempDir.isDirectory()) {
\r
190 FileUtils.deleteAll(tempDir);
\r
191 } catch (IOException e) {
\r
192 throw new FMILException("Could not create temp folder for fmu");
\r
201 String tmpPath = tempDir.getAbsolutePath();
\r
202 if(!tmpPath.endsWith("\\"))
\r
203 tmpPath = tmpPath + "\\";
\r
204 id = loadFMUFile_(path, tmpPath);
\r
207 getAllVariableReferences();
\r
209 for(int i=0;i<variableNames.length;i++) {
\r
210 variableMap.put(variableNames[i], variableReferences[i]);
\r
214 } catch (UnsatisfiedLinkError err) {
\r
215 throw new FMILException(UNSATISFIED_LINK);
\r
216 } catch (Exception e) {
\r
217 throw new FMILException(e.getMessage());
\r
222 private native int loadFMUFile_(String path, String toDir);
\r
225 * Set a step length for simulation
\r
227 * @param step Step length for simulation
\r
228 * @throws FMILException
\r
230 public void setStepLength(double step) throws FMILException {
\r
231 synchronized(syncObject) {
\r
235 int ret = setStepLength_(getModelIDNew(), step);
\r
237 throw new FMILException(getLastErrorMessage());
\r
239 } catch (UnsatisfiedLinkError err) {
\r
240 throw new FMILException(UNSATISFIED_LINK);
\r
241 } catch (Exception e) {
\r
242 throw new FMILException(e.getMessage());
\r
247 private native int setStepLength_(int id, double step);
\r
250 * Instantiates a simulation.
\r
252 * Make sure that an FMU is loaded first.
\r
253 * @throws FMILException
\r
255 public void instantiateSimulation() throws FMILException {
\r
256 synchronized(syncObject) {
\r
260 int ret = instantiateSimulation_(getModelIDNew());
\r
262 throw new FMILException(getLastErrorMessage());
\r
264 } catch (UnsatisfiedLinkError err) {
\r
265 throw new FMILException(UNSATISFIED_LINK);
\r
266 } catch (Exception e) {
\r
267 throw new FMILException(e.getMessage());
\r
272 private native int instantiateSimulation_(int id);
\r
276 * Initializes a simulation.
\r
278 * Make sure that simulation is instantiated first!
\r
279 * @throws FMILException
\r
281 public void initializeSimulation() throws FMILException {
\r
282 synchronized(syncObject) {
\r
286 int ret = initializeSimulation_(getModelIDNew());
\r
288 throw new FMILException(getLastErrorMessage());
\r
290 } catch (UnsatisfiedLinkError err) {
\r
291 throw new FMILException(UNSATISFIED_LINK);
\r
292 } catch (Exception e) {
\r
293 throw new FMILException(e.getMessage());
\r
298 private native int initializeSimulation_(int id);
\r
301 * Subscribe a set of variables from a loaded simulation.
\r
303 * Make sure that an FMU is loaded first.
\r
304 * @param variables Array of variables
\r
305 * @throws FMILException
\r
307 public void subscribe(int[] variables) throws FMILException {
\r
308 synchronized(syncObject) {
\r
312 int ret = subscribe_(getModelIDNew(), variables);
\r
314 throw new FMILException(getLastErrorMessage());
\r
316 } catch (UnsatisfiedLinkError err) {
\r
317 throw new FMILException(UNSATISFIED_LINK);
\r
318 } catch (Exception e) {
\r
319 throw new FMILException(e.getMessage());
\r
324 private native int subscribe_(int id, int[] variables);
\r
327 * Set a new (Real, double) value for a variable. If the variable is a
\r
328 * parameter, the change is effective immediately.
\r
330 * @param name Variable
\r
331 * @param value New (Real, double) value
\r
332 * @throws FMILException
\r
334 public void setRealValue(String name, double value) throws FMILException {
\r
336 synchronized(syncObject) {
\r
340 int ret = setRealValue_(getModelIDNew(), variableMap.get(name), value);
\r
342 throw new FMILException(getLastErrorMessage());
\r
344 } catch (UnsatisfiedLinkError err) {
\r
345 throw new FMILException(UNSATISFIED_LINK);
\r
346 } catch (Exception e) {
\r
347 throw new FMILException(e.getMessage());
\r
354 public void setRealValue(int variableReference, double value) throws FMILException {
\r
356 synchronized(syncObject) {
\r
360 int ret = setRealValue_(getModelIDNew(), variableReference, value);
\r
362 throw new FMILException(getLastErrorMessage());
\r
364 } catch (UnsatisfiedLinkError err) {
\r
365 throw new FMILException(UNSATISFIED_LINK);
\r
366 } catch (Exception e) {
\r
367 throw new FMILException(e.getMessage());
\r
374 private native int setRealValue_(int id, int variableReference, double value);
\r
377 // * Set a new (integer) value for a variable. If the variable is a
\r
378 // * parameter, the change is effective immediately.
\r
380 // * @param name Variable
\r
381 // * @param value New (integer) value
\r
382 // * @throws FMILException
\r
384 // public void setIntegerValue(String name, int value) throws FMILException {
\r
385 // synchronized(syncObject) {
\r
389 // int ret = setIntegerValue_(getModelID(), name, value);
\r
390 // if(ret == ERROR)
\r
391 // throw new FMILException(getLastErrorMessage());
\r
393 // } catch (UnsatisfiedLinkError err) {
\r
394 // throw new FMILException(UNSATISFIED_LINK);
\r
395 // } catch (Exception e) {
\r
396 // throw new FMILException(e.getMessage());
\r
400 // private native int setIntegerValue_(String id, String name, int value);
\r
403 // * Set a new (boolean) value for a variable. If the variable is a
\r
404 // * parameter, the change is effective immediately.
\r
406 // * @param name Variable
\r
407 // * @param value New (boolean) value
\r
408 // * @throws FMILException
\r
410 // public void setBooleanValue(String name, boolean value) throws FMILException {
\r
411 // synchronized(syncObject) {
\r
415 // int ret = setBooleanValue_(getModelID(), name, value);
\r
416 // if(ret == ERROR)
\r
417 // throw new FMILException(getLastErrorMessage());
\r
419 // } catch (UnsatisfiedLinkError err) {
\r
420 // throw new FMILException(UNSATISFIED_LINK);
\r
421 // } catch (Exception e) {
\r
422 // throw new FMILException(e.getMessage());
\r
426 // private native int setBooleanValue_(String id, String name, boolean value);
\r
428 // public void setTime(double time) throws FMILException {
\r
429 // synchronized(syncObject) {
\r
433 // int ret = setTime_(getModelID(), time);
\r
434 // if(ret == ERROR)
\r
435 // throw new FMILException(getLastErrorMessage());
\r
437 // } catch (UnsatisfiedLinkError err) {
\r
438 // throw new FMILException(UNSATISFIED_LINK);
\r
439 // } catch (Exception e) {
\r
440 // throw new FMILException(e.getMessage());
\r
444 // private native int setTime_(String id, double time);
\r
447 * Simulate one step forward. The step length can be set with
\r
450 * @throws FMILException
\r
452 public void simulateStep() throws FMILException {
\r
453 synchronized(syncObject) {
\r
457 int ret = simulateStep_(getModelIDNew());
\r
459 throw new FMILException(getLastErrorMessage());
\r
461 } catch (UnsatisfiedLinkError err) {
\r
462 throw new FMILException(UNSATISFIED_LINK);
\r
463 } catch (Exception e) {
\r
464 throw new FMILException(e.getMessage());
\r
468 private native int simulateStep_(int id);
\r
471 * Get an array containing the current values for subscribed variables. The
\r
472 * values are in the same order as in the subscription.
\r
474 * @param results An array the size of subscribed results
\r
477 public double[] getSubscribedResults() throws FMILException {
\r
478 synchronized(syncObject) {
\r
482 double[] results = new double[subscription.size()];
\r
483 return getSubscribedResults_(getModelIDNew(), results);
\r
485 } catch (UnsatisfiedLinkError err) {
\r
486 throw new FMILException(UNSATISFIED_LINK);
\r
487 } catch (Exception e) {
\r
488 throw new FMILException(e.getMessage());
\r
493 private native double[] getSubscribedResults_(int id, double[] results);
\r
497 * Unload FMU and the dll:s that it requires.
\r
499 * To be called after all FMU simulations are ended.
\r
500 * If the fmu is loaded again / changed, call to loadFMUFile is sufficient. loadFMUFile
\r
501 * releases the previous fmu.dll
\r
503 * @throws FMILException
\r
505 public void unloadFMU() throws FMILException {
\r
506 synchronized(syncObject) {
\r
510 unlockFMUDirectory();
\r
512 int ret = unloadFMU_(getModelIDNew());
\r
514 throw new FMILException(getLastErrorMessage());
\r
516 removeFMUDirectoryContents();
\r
518 } catch (UnsatisfiedLinkError err) {
\r
519 throw new FMILException(UNSATISFIED_LINK);
\r
520 } catch (Exception e) {
\r
521 throw new FMILException(e.getMessage());
\r
525 private native int unloadFMU_(int id);
\r
528 // * Checks if fmu has been initialized
\r
529 // * @return current simulation time
\r
531 // public boolean isInitialized() throws FMILException {
\r
532 // synchronized(syncObject) {
\r
534 // return isInitialized_(getModelID());
\r
535 // } catch (UnsatisfiedLinkError err) {
\r
536 // throw new FMILException(UNSATISFIED_LINK);
\r
537 // } catch (Exception e) {
\r
538 // throw new FMILException(e.getMessage());
\r
543 // private native boolean isInitialized_(String id);
\r
546 * Get the current simulation time
\r
547 * @return current simulation time
\r
549 public double getTime() throws FMILException {
\r
550 synchronized(syncObject) {
\r
554 return getTime_(getModelIDNew());
\r
556 } catch (UnsatisfiedLinkError err) {
\r
557 throw new FMILException(UNSATISFIED_LINK);
\r
558 } catch (Exception e) {
\r
559 throw new FMILException(e.getMessage());
\r
564 private native double getTime_(int id);
\r
567 * Get all variables in a loaded model
\r
568 * @return all variables in a loaded model
\r
570 public String[] getAllVariables() throws FMILException {
\r
571 synchronized(syncObject) {
\r
575 if(variableNames == null) {
\r
576 variableNames = getAllVariables_(getModelIDNew());
\r
578 return variableNames;
\r
580 } catch (UnsatisfiedLinkError err) {
\r
581 throw new FMILException(UNSATISFIED_LINK);
\r
582 } catch (Exception e) {
\r
583 throw new FMILException(e.getMessage());
\r
588 private native String[] getAllVariables_(int id);
\r
590 public String[] getAllVariableDescriptions() throws FMILException {
\r
591 synchronized(syncObject) {
\r
595 if(variableDescriptions == null) {
\r
596 variableDescriptions = getAllVariableDescriptions_(getModelIDNew());
\r
598 return variableDescriptions;
\r
600 } catch (UnsatisfiedLinkError err) {
\r
601 throw new FMILException(UNSATISFIED_LINK);
\r
602 } catch (Exception e) {
\r
603 throw new FMILException(e.getMessage());
\r
608 private native String[] getAllVariableDescriptions_(int id);
\r
610 public int[] getAllVariableReferences() throws FMILException {
\r
611 synchronized(syncObject) {
\r
615 if(variableReferences == null) {
\r
616 variableReferences = getAllVariableReferences_(getModelIDNew(), new int[variableNames.length]);
\r
618 return variableReferences;
\r
620 } catch (UnsatisfiedLinkError err) {
\r
621 throw new FMILException(UNSATISFIED_LINK);
\r
622 } catch (Exception e) {
\r
623 throw new FMILException(e.getMessage());
\r
628 private native int[] getAllVariableReferences_(int id, int[] array);
\r
630 public int[] getAllVariableTypes() throws FMILException {
\r
631 synchronized(syncObject) {
\r
635 if(variableTypes == null) {
\r
636 variableTypes = getAllVariableTypes_(getModelIDNew(), new int[variableNames.length]);
\r
638 return variableTypes;
\r
640 } catch (UnsatisfiedLinkError err) {
\r
641 throw new FMILException(UNSATISFIED_LINK);
\r
642 } catch (Exception e) {
\r
643 throw new FMILException(e.getMessage());
\r
648 private native int[] getAllVariableTypes_(int id, int[] array);
\r
650 public int[] getAllVariableCausalities() throws FMILException {
\r
651 synchronized(syncObject) {
\r
655 if(variableCausalities == null) {
\r
656 variableCausalities = getAllVariableCausalities_(getModelIDNew(), new int[variableNames.length]);
\r
658 return variableCausalities;
\r
660 } catch (UnsatisfiedLinkError err) {
\r
661 throw new FMILException(UNSATISFIED_LINK);
\r
662 } catch (Exception e) {
\r
663 throw new FMILException(e.getMessage());
\r
668 private native int[] getAllVariableCausalities_(int id, int[] array);
\r
670 public int[] getAllVariableVariabilities() throws FMILException {
\r
671 synchronized(syncObject) {
\r
675 if(variableVariabilities == null) {
\r
676 variableVariabilities = getAllVariableVariabilities_(getModelIDNew(), new int[variableNames.length]);
\r
678 return variableVariabilities;
\r
680 } catch (UnsatisfiedLinkError err) {
\r
681 throw new FMILException(UNSATISFIED_LINK);
\r
682 } catch (Exception e) {
\r
683 throw new FMILException(e.getMessage());
\r
688 private native int[] getAllVariableVariabilities_(int id, int[] array);
\r
692 // * Get all variables from model that match the filter (and time variable)
\r
694 // * @param regexp Regular expression filter
\r
695 // * @return An array of variable names that match regexp filter (and time-variable)
\r
696 // * @throws FMILException
\r
698 // public String[] filterVariables(String regexp) throws FMILException {
\r
699 // synchronized(syncObject) {
\r
702 // return filterVariables_(getModelID(), regexp + "|time");
\r
704 // } catch (UnsatisfiedLinkError err) {
\r
705 // throw new FMILException(UNSATISFIED_LINK);
\r
706 // } catch (Exception e) {
\r
707 // throw new FMILException(e.getMessage());
\r
712 // private native String[] filterVariables_(String id, String regexp);
\r
715 * Get the last error message
\r
716 * @return Last error message
\r
718 public String getLastErrorMessage() throws FMILException {
\r
719 synchronized(syncObject) {
\r
724 //return getLastErrorMessage_(getModelID());
\r
726 } catch (UnsatisfiedLinkError err) {
\r
727 throw new FMILException(UNSATISFIED_LINK);
\r
728 } catch (Exception e) {
\r
729 throw new FMILException(e.getMessage());
\r
734 // private native String getLastErrorMessage_(String id);
\r
737 * Get a real (double) value for variable
\r
738 * @param name Name of the variable
\r
740 * @throws FMILException
\r
742 public double getRealValue(String name) throws FMILException {
\r
743 synchronized(syncObject) {
\r
746 // TODO: printtaa id ja name, jotta saadaan virheessä kiinni
\r
747 double result = getRealValue_(getModelIDNew(), variableMap.get(name));
\r
748 System.err.println("getRealValue " + name + " = " + result);
\r
750 } catch (UnsatisfiedLinkError err) {
\r
751 throw new FMILException(UNSATISFIED_LINK);
\r
752 } catch (Exception e) {
\r
753 throw new FMILException(e.getMessage());
\r
758 private native double getRealValue_(int id, int variableReference);
\r
761 // * Get a string value for variable
\r
762 // * @param name Name of the variable
\r
764 // * @throws FMILException
\r
766 // public String getStringValue(String name) throws FMILException {
\r
767 // synchronized(syncObject) {
\r
770 // return getStringValue_(getModelID(), name);
\r
771 // } catch (UnsatisfiedLinkError err) {
\r
772 // throw new FMILException(UNSATISFIED_LINK);
\r
773 // } catch (Exception e) {
\r
774 // throw new FMILException(e.getMessage());
\r
779 // private native String getStringValue_(String id, String name);
\r
782 // * Get an integer value for variable
\r
783 // * @param name Name of the variable
\r
785 // * @throws FMILException
\r
787 // public int getIntegerValue(String name) throws FMILException {
\r
788 // synchronized(syncObject) {
\r
791 // return getIntegerValue_(getModelID(), name);
\r
792 // } catch (UnsatisfiedLinkError err) {
\r
793 // throw new FMILException(UNSATISFIED_LINK);
\r
794 // } catch (Exception e) {
\r
795 // throw new FMILException(e.getMessage());
\r
800 // private native int getIntegerValue_(String id, String name);
\r
803 // * Get a real (double) value for variable
\r
804 // * @param name Name of the variable
\r
806 // * @throws FMILException
\r
808 // public boolean getBooleanValue(String name) throws FMILException {
\r
809 // synchronized(syncObject) {
\r
812 // return getBooleanValue_(getModelID(), name);
\r
813 // } catch (UnsatisfiedLinkError err) {
\r
814 // throw new FMILException(UNSATISFIED_LINK);
\r
815 // } catch (Exception e) {
\r
816 // throw new FMILException(e.getMessage());
\r
821 // private native boolean getBooleanValue_(String id, String name);
\r
823 private FileChannel channel;
\r
824 private FileLock lock;
\r
826 @SuppressWarnings("resource")
\r
827 private boolean lockFMUDirectory() {
\r
830 // Get a file channel for the lock file
\r
831 File lockFile = new File(TEMP_FMU_DIRECTORY, LOCK_FILE_NAME);
\r
832 if(!lockFile.isFile())
\r
833 lockFile.createNewFile();
\r
835 channel = new RandomAccessFile(lockFile, "rw").getChannel();
\r
837 // Use the file channel to create a lock on the file.
\r
838 // This method blocks until it can retrieve the lock.
\r
839 lock = channel.lock();
\r
841 // // Try acquiring the lock without blocking. This method returns
\r
842 // // null or throws an exception if the file is already locked.
\r
844 // lock = channel.tryLock();
\r
845 // } catch (OverlappingFileLockException e) {
\r
846 // // File is already locked in this thread or virtual machine
\r
848 } catch (IOException e) {
\r
855 private boolean unlockFMUDirectory() {
\r
857 // Release the lock
\r
862 if(channel != null)
\r
864 } catch (IOException e) {
\r
870 private boolean removeFMUDirectoryContents() {
\r
873 File tempDir = new File(TEMP_FMU_DIRECTORY);
\r
874 FileUtils.deleteAll(tempDir);
\r
876 } catch (IOException e) {
\r