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 int[] variableReferences;
\r
110 private TObjectIntHashMap<String> variableMap = new TObjectIntHashMap<String>();
\r
112 private Set<String> subscriptionSet = new HashSet<String>();
\r
113 private TIntArrayList subscription = new TIntArrayList();
\r
114 private ArrayList<String> subscribedNames = new ArrayList<String>();
\r
116 public List<String> getSubscribedNames() {
\r
117 return subscribedNames;
\r
120 public boolean subscribe(String name) throws FMILException {
\r
122 int vr = variableMap.get(name);
\r
123 if(vr == 0) return false;
\r
124 if(!subscriptionSet.add(name)) return false;
\r
125 subscribedNames.add(name);
\r
126 System.err.println("subscribed : " + name + " => " + subscribedNames.size());
\r
127 subscription.add(vr);
\r
128 subscribe(new int[] { vr });
\r
133 // Create a directory for this control
\r
134 File tempDir = new File(TEMP_FMU_COMMON_DIRECTORY, UUID.randomUUID().toString());
\r
136 TEMP_FMU_DIRECTORY = tempDir.getAbsolutePath();
\r
138 // Create two directories inside the temp directory for this control
\r
139 dirName = UUID.randomUUID().toString();
\r
140 File fmuDir = new File(TEMP_FMU_DIRECTORY, dirName);
\r
143 TEMP_FOLDER_1 = fmuDir.toString();
\r
144 TEMP_FOLDER_2 = fmuDir.toString() + "_2";
\r
146 // Lock fmu directory in temp directory
\r
147 lockFMUDirectory();
\r
150 public int getModelIDNew() {
\r
154 public String getModelID() {
\r
158 public String getFmuDir() {
\r
163 * Load fmu from a given file path. Releases the (possible) previously
\r
166 * @param path absolute file path for fmu file
\r
167 * @throws FMILException
\r
169 private int fmuN = 0;
\r
170 private boolean fmuLoaded = false;
\r
171 public void loadFMUFile(String path) throws FMILException {
\r
173 synchronized(syncObject) {
\r
175 if(fmuN % 2 == 0) {
\r
176 fmuDir = TEMP_FOLDER_1;
\r
179 fmuDir = TEMP_FOLDER_2;
\r
183 File tempDir = new File(fmuDir);
\r
184 if(tempDir.isDirectory()) {
\r
186 FileUtils.deleteAll(tempDir);
\r
187 } catch (IOException e) {
\r
188 throw new FMILException("Could not create temp folder for fmu");
\r
197 String tmpPath = tempDir.getAbsolutePath();
\r
198 if(!tmpPath.endsWith("\\"))
\r
199 tmpPath = tmpPath + "\\";
\r
200 id = loadFMUFile_(path, tmpPath);
\r
203 getAllVariableReferences();
\r
205 for(int i=0;i<variableNames.length;i++) {
\r
206 variableMap.put(variableNames[i], variableReferences[i]);
\r
210 } catch (UnsatisfiedLinkError err) {
\r
211 throw new FMILException(UNSATISFIED_LINK);
\r
212 } catch (Exception e) {
\r
213 throw new FMILException(e.getMessage());
\r
218 private native int loadFMUFile_(String path, String toDir);
\r
221 * Set a step length for simulation
\r
223 * @param step Step length for simulation
\r
224 * @throws FMILException
\r
226 public void setStepLength(double step) throws FMILException {
\r
227 synchronized(syncObject) {
\r
231 int ret = setStepLength_(getModelIDNew(), step);
\r
233 throw new FMILException(getLastErrorMessage());
\r
235 } catch (UnsatisfiedLinkError err) {
\r
236 throw new FMILException(UNSATISFIED_LINK);
\r
237 } catch (Exception e) {
\r
238 throw new FMILException(e.getMessage());
\r
243 private native int setStepLength_(int id, double step);
\r
246 * Instantiates a simulation.
\r
248 * Make sure that an FMU is loaded first.
\r
249 * @throws FMILException
\r
251 public void instantiateSimulation() throws FMILException {
\r
252 synchronized(syncObject) {
\r
256 int ret = instantiateSimulation_(getModelIDNew());
\r
258 throw new FMILException(getLastErrorMessage());
\r
260 } catch (UnsatisfiedLinkError err) {
\r
261 throw new FMILException(UNSATISFIED_LINK);
\r
262 } catch (Exception e) {
\r
263 throw new FMILException(e.getMessage());
\r
268 private native int instantiateSimulation_(int id);
\r
272 * Initializes a simulation.
\r
274 * Make sure that simulation is instantiated first!
\r
275 * @throws FMILException
\r
277 public void initializeSimulation() throws FMILException {
\r
278 synchronized(syncObject) {
\r
282 int ret = initializeSimulation_(getModelIDNew());
\r
284 throw new FMILException(getLastErrorMessage());
\r
286 } catch (UnsatisfiedLinkError err) {
\r
287 throw new FMILException(UNSATISFIED_LINK);
\r
288 } catch (Exception e) {
\r
289 throw new FMILException(e.getMessage());
\r
294 private native int initializeSimulation_(int id);
\r
297 * Subscribe a set of variables from a loaded simulation.
\r
299 * Make sure that an FMU is loaded first.
\r
300 * @param variables Array of variables
\r
301 * @throws FMILException
\r
303 public void subscribe(int[] variables) throws FMILException {
\r
304 synchronized(syncObject) {
\r
308 int ret = subscribe_(getModelIDNew(), variables);
\r
310 throw new FMILException(getLastErrorMessage());
\r
312 } catch (UnsatisfiedLinkError err) {
\r
313 throw new FMILException(UNSATISFIED_LINK);
\r
314 } catch (Exception e) {
\r
315 throw new FMILException(e.getMessage());
\r
320 private native int subscribe_(int id, int[] variables);
\r
323 * Set a new (Real, double) value for a variable. If the variable is a
\r
324 * parameter, the change is effective immediately.
\r
326 * @param name Variable
\r
327 * @param value New (Real, double) value
\r
328 * @throws FMILException
\r
330 public void setRealValue(String name, double value) throws FMILException {
\r
332 synchronized(syncObject) {
\r
336 int ret = setRealValue_(getModelIDNew(), variableMap.get(name), value);
\r
338 throw new FMILException(getLastErrorMessage());
\r
340 } catch (UnsatisfiedLinkError err) {
\r
341 throw new FMILException(UNSATISFIED_LINK);
\r
342 } catch (Exception e) {
\r
343 throw new FMILException(e.getMessage());
\r
350 public void setRealValue(int variableReference, double value) throws FMILException {
\r
352 synchronized(syncObject) {
\r
356 int ret = setRealValue_(getModelIDNew(), variableReference, value);
\r
358 throw new FMILException(getLastErrorMessage());
\r
360 } catch (UnsatisfiedLinkError err) {
\r
361 throw new FMILException(UNSATISFIED_LINK);
\r
362 } catch (Exception e) {
\r
363 throw new FMILException(e.getMessage());
\r
370 private native int setRealValue_(int id, int variableReference, double value);
\r
373 // * Set a new (integer) value for a variable. If the variable is a
\r
374 // * parameter, the change is effective immediately.
\r
376 // * @param name Variable
\r
377 // * @param value New (integer) value
\r
378 // * @throws FMILException
\r
380 // public void setIntegerValue(String name, int value) throws FMILException {
\r
381 // synchronized(syncObject) {
\r
385 // int ret = setIntegerValue_(getModelID(), name, value);
\r
386 // if(ret == ERROR)
\r
387 // throw new FMILException(getLastErrorMessage());
\r
389 // } catch (UnsatisfiedLinkError err) {
\r
390 // throw new FMILException(UNSATISFIED_LINK);
\r
391 // } catch (Exception e) {
\r
392 // throw new FMILException(e.getMessage());
\r
396 // private native int setIntegerValue_(String id, String name, int value);
\r
399 // * Set a new (boolean) value for a variable. If the variable is a
\r
400 // * parameter, the change is effective immediately.
\r
402 // * @param name Variable
\r
403 // * @param value New (boolean) value
\r
404 // * @throws FMILException
\r
406 // public void setBooleanValue(String name, boolean value) throws FMILException {
\r
407 // synchronized(syncObject) {
\r
411 // int ret = setBooleanValue_(getModelID(), name, value);
\r
412 // if(ret == ERROR)
\r
413 // throw new FMILException(getLastErrorMessage());
\r
415 // } catch (UnsatisfiedLinkError err) {
\r
416 // throw new FMILException(UNSATISFIED_LINK);
\r
417 // } catch (Exception e) {
\r
418 // throw new FMILException(e.getMessage());
\r
422 // private native int setBooleanValue_(String id, String name, boolean value);
\r
424 // public void setTime(double time) throws FMILException {
\r
425 // synchronized(syncObject) {
\r
429 // int ret = setTime_(getModelID(), time);
\r
430 // if(ret == ERROR)
\r
431 // throw new FMILException(getLastErrorMessage());
\r
433 // } catch (UnsatisfiedLinkError err) {
\r
434 // throw new FMILException(UNSATISFIED_LINK);
\r
435 // } catch (Exception e) {
\r
436 // throw new FMILException(e.getMessage());
\r
440 // private native int setTime_(String id, double time);
\r
443 * Simulate one step forward. The step length can be set with
\r
446 * @throws FMILException
\r
448 public void simulateStep() throws FMILException {
\r
449 synchronized(syncObject) {
\r
453 int ret = simulateStep_(getModelIDNew());
\r
455 throw new FMILException(getLastErrorMessage());
\r
457 } catch (UnsatisfiedLinkError err) {
\r
458 throw new FMILException(UNSATISFIED_LINK);
\r
459 } catch (Exception e) {
\r
460 throw new FMILException(e.getMessage());
\r
464 private native int simulateStep_(int id);
\r
467 * Get an array containing the current values for subscribed variables. The
\r
468 * values are in the same order as in the subscription.
\r
470 * @param results An array the size of subscribed results
\r
473 public double[] getSubscribedResults() throws FMILException {
\r
474 synchronized(syncObject) {
\r
478 double[] results = new double[subscription.size()];
\r
479 return getSubscribedResults_(getModelIDNew(), results);
\r
481 } catch (UnsatisfiedLinkError err) {
\r
482 throw new FMILException(UNSATISFIED_LINK);
\r
483 } catch (Exception e) {
\r
484 throw new FMILException(e.getMessage());
\r
489 private native double[] getSubscribedResults_(int id, double[] results);
\r
493 * Unload FMU and the dll:s that it requires.
\r
495 * To be called after all FMU simulations are ended.
\r
496 * If the fmu is loaded again / changed, call to loadFMUFile is sufficient. loadFMUFile
\r
497 * releases the previous fmu.dll
\r
499 * @throws FMILException
\r
501 public void unloadFMU() throws FMILException {
\r
502 synchronized(syncObject) {
\r
506 unlockFMUDirectory();
\r
508 int ret = unloadFMU_(getModelIDNew());
\r
510 throw new FMILException(getLastErrorMessage());
\r
512 removeFMUDirectoryContents();
\r
514 } catch (UnsatisfiedLinkError err) {
\r
515 throw new FMILException(UNSATISFIED_LINK);
\r
516 } catch (Exception e) {
\r
517 throw new FMILException(e.getMessage());
\r
521 private native int unloadFMU_(int id);
\r
524 // * Checks if fmu has been initialized
\r
525 // * @return current simulation time
\r
527 // public boolean isInitialized() throws FMILException {
\r
528 // synchronized(syncObject) {
\r
530 // return isInitialized_(getModelID());
\r
531 // } catch (UnsatisfiedLinkError err) {
\r
532 // throw new FMILException(UNSATISFIED_LINK);
\r
533 // } catch (Exception e) {
\r
534 // throw new FMILException(e.getMessage());
\r
539 // private native boolean isInitialized_(String id);
\r
542 * Get the current simulation time
\r
543 * @return current simulation time
\r
545 public double getTime() throws FMILException {
\r
546 synchronized(syncObject) {
\r
550 return getTime_(getModelIDNew());
\r
552 } catch (UnsatisfiedLinkError err) {
\r
553 throw new FMILException(UNSATISFIED_LINK);
\r
554 } catch (Exception e) {
\r
555 throw new FMILException(e.getMessage());
\r
560 private native double getTime_(int id);
\r
563 * Get all variables in a loaded model
\r
564 * @return all variables in a loaded model
\r
566 public String[] getAllVariables() throws FMILException {
\r
567 synchronized(syncObject) {
\r
571 if(variableNames == null) {
\r
572 variableNames = getAllVariables_(getModelIDNew());
\r
574 return variableNames;
\r
576 } catch (UnsatisfiedLinkError err) {
\r
577 throw new FMILException(UNSATISFIED_LINK);
\r
578 } catch (Exception e) {
\r
579 throw new FMILException(e.getMessage());
\r
584 private native String[] getAllVariables_(int id);
\r
587 * Get all variables in a loaded model
\r
588 * @return all variables in a loaded model
\r
590 public int[] getAllVariableReferences() throws FMILException {
\r
591 synchronized(syncObject) {
\r
595 if(variableReferences == null) {
\r
596 variableReferences = getAllVariableReferences_(getModelIDNew(), new int[variableNames.length]);
\r
598 return variableReferences;
\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 int[] getAllVariableReferences_(int id, int[] array);
\r
612 // * Get all variables from model that match the filter (and time variable)
\r
614 // * @param regexp Regular expression filter
\r
615 // * @return An array of variable names that match regexp filter (and time-variable)
\r
616 // * @throws FMILException
\r
618 // public String[] filterVariables(String regexp) throws FMILException {
\r
619 // synchronized(syncObject) {
\r
622 // return filterVariables_(getModelID(), regexp + "|time");
\r
624 // } catch (UnsatisfiedLinkError err) {
\r
625 // throw new FMILException(UNSATISFIED_LINK);
\r
626 // } catch (Exception e) {
\r
627 // throw new FMILException(e.getMessage());
\r
632 // private native String[] filterVariables_(String id, String regexp);
\r
635 * Get the last error message
\r
636 * @return Last error message
\r
638 public String getLastErrorMessage() throws FMILException {
\r
639 synchronized(syncObject) {
\r
644 //return getLastErrorMessage_(getModelID());
\r
646 } catch (UnsatisfiedLinkError err) {
\r
647 throw new FMILException(UNSATISFIED_LINK);
\r
648 } catch (Exception e) {
\r
649 throw new FMILException(e.getMessage());
\r
654 // private native String getLastErrorMessage_(String id);
\r
657 * Get a real (double) value for variable
\r
658 * @param name Name of the variable
\r
660 * @throws FMILException
\r
662 public double getRealValue(String name) throws FMILException {
\r
663 synchronized(syncObject) {
\r
666 // TODO: printtaa id ja name, jotta saadaan virheessä kiinni
\r
667 double result = getRealValue_(getModelIDNew(), variableMap.get(name));
\r
668 System.err.println("getRealValue " + name + " = " + result);
\r
670 } catch (UnsatisfiedLinkError err) {
\r
671 throw new FMILException(UNSATISFIED_LINK);
\r
672 } catch (Exception e) {
\r
673 throw new FMILException(e.getMessage());
\r
678 private native double getRealValue_(int id, int variableReference);
\r
681 // * Get a string value for variable
\r
682 // * @param name Name of the variable
\r
684 // * @throws FMILException
\r
686 // public String getStringValue(String name) throws FMILException {
\r
687 // synchronized(syncObject) {
\r
690 // return getStringValue_(getModelID(), name);
\r
691 // } catch (UnsatisfiedLinkError err) {
\r
692 // throw new FMILException(UNSATISFIED_LINK);
\r
693 // } catch (Exception e) {
\r
694 // throw new FMILException(e.getMessage());
\r
699 // private native String getStringValue_(String id, String name);
\r
702 // * Get an integer value for variable
\r
703 // * @param name Name of the variable
\r
705 // * @throws FMILException
\r
707 // public int getIntegerValue(String name) throws FMILException {
\r
708 // synchronized(syncObject) {
\r
711 // return getIntegerValue_(getModelID(), name);
\r
712 // } catch (UnsatisfiedLinkError err) {
\r
713 // throw new FMILException(UNSATISFIED_LINK);
\r
714 // } catch (Exception e) {
\r
715 // throw new FMILException(e.getMessage());
\r
720 // private native int getIntegerValue_(String id, String name);
\r
723 // * Get a real (double) value for variable
\r
724 // * @param name Name of the variable
\r
726 // * @throws FMILException
\r
728 // public boolean getBooleanValue(String name) throws FMILException {
\r
729 // synchronized(syncObject) {
\r
732 // return getBooleanValue_(getModelID(), name);
\r
733 // } catch (UnsatisfiedLinkError err) {
\r
734 // throw new FMILException(UNSATISFIED_LINK);
\r
735 // } catch (Exception e) {
\r
736 // throw new FMILException(e.getMessage());
\r
741 // private native boolean getBooleanValue_(String id, String name);
\r
743 private FileChannel channel;
\r
744 private FileLock lock;
\r
746 @SuppressWarnings("resource")
\r
747 private boolean lockFMUDirectory() {
\r
750 // Get a file channel for the lock file
\r
751 File lockFile = new File(TEMP_FMU_DIRECTORY, LOCK_FILE_NAME);
\r
752 if(!lockFile.isFile())
\r
753 lockFile.createNewFile();
\r
755 channel = new RandomAccessFile(lockFile, "rw").getChannel();
\r
757 // Use the file channel to create a lock on the file.
\r
758 // This method blocks until it can retrieve the lock.
\r
759 lock = channel.lock();
\r
761 // // Try acquiring the lock without blocking. This method returns
\r
762 // // null or throws an exception if the file is already locked.
\r
764 // lock = channel.tryLock();
\r
765 // } catch (OverlappingFileLockException e) {
\r
766 // // File is already locked in this thread or virtual machine
\r
768 } catch (IOException e) {
\r
775 private boolean unlockFMUDirectory() {
\r
777 // Release the lock
\r
782 if(channel != null)
\r
784 } catch (IOException e) {
\r
790 private boolean removeFMUDirectoryContents() {
\r
793 File tempDir = new File(TEMP_FMU_DIRECTORY);
\r
794 FileUtils.deleteAll(tempDir);
\r
796 } catch (IOException e) {
\r