]> gerrit.simantics Code Review - simantics/fmil.git/blob - org.simantics.fmil.core/src/org/simantics/fmil/core/FMIL.java
ec63279c89feb1e982b3e79e254f77713afccac6
[simantics/fmil.git] / org.simantics.fmil.core / src / org / simantics / fmil / core / FMIL.java
1 package org.simantics.fmil.core;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.io.RandomAccessFile;
6 import java.net.URL;
7 import java.nio.channels.FileChannel;
8 import java.nio.channels.FileLock;
9 import java.nio.file.Files;
10 import java.nio.file.Paths;
11 import java.util.ArrayList;
12 import java.util.Arrays;
13 import java.util.HashSet;
14 import java.util.List;
15 import java.util.Set;
16 import java.util.UUID;
17
18 import org.eclipse.core.runtime.FileLocator;
19 import org.eclipse.core.runtime.Path;
20 import org.eclipse.core.runtime.Platform;
21 import org.osgi.framework.Bundle;
22 import org.simantics.fmil.core.ExecEnvironment.OSType;
23 import org.simantics.utils.FileUtils;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
26
27 import gnu.trove.list.array.TIntArrayList;
28 import gnu.trove.map.hash.TObjectIntHashMap;
29
30
31 public class FMIL {
32
33     private static final Logger LOGGER = LoggerFactory.getLogger(FMIL.class);
34
35         private static final boolean DEBUG = false;
36     
37     /**
38      * Static variables
39      */
40     private static int      OK                  = 0;
41     private static int      ERROR               = 1;
42     private static int      PENDING                     = 2;
43     private static String   UNSATISFIED_LINK    = "Method not found. DLL might not be loaded properly.";
44     public static final String TEMP_FMU_DIRECTORY_NAME = "fmil";
45     public static String    TEMP_FMU_COMMON_DIRECTORY;
46     public static String    LOCK_FILE_NAME      = "fmil.lock";
47
48     public static Object syncObject = new Object();
49
50     /**
51      * Static: load native libraries required for the FMU simulation to work.
52      */
53     static {
54
55         File[] libraries = new File[2];
56
57         ExecEnvironment env = ExecEnvironment.calculate();
58
59         try {
60             URL sharedFMILIBUrl = null;
61             URL simulatorFMIUrl = null;
62             Bundle b = Platform.getBundle("org.simantics.fmil.core");
63
64             if (env.os == OSType.WINDOWS) {
65                 sharedFMILIBUrl = FileLocator.find(b, new Path("libraries/fmilib_shared.dll"), null);
66                 simulatorFMIUrl = FileLocator.find(b, new Path("libraries/FMUSimulator.dll"), null);
67             } else if(env.os == OSType.LINUX) {
68                 sharedFMILIBUrl = FileLocator.find(b, new Path("libraries/libfmilib_shared.so"), null);
69                 simulatorFMIUrl = FileLocator.find(b, new Path("libraries/libFMUSimulator.so"), null);
70             }
71
72             libraries[0] = new File(FileLocator.toFileURL(sharedFMILIBUrl).getPath());
73             libraries[1] = new File(FileLocator.toFileURL(simulatorFMIUrl).getPath());
74         } catch (Exception e) {
75             LOGGER.error("Failed to resolve native FMU simulation library for execution environment {}.{}", env.os, env.arch, e);
76         }
77
78         for(File library : libraries) {
79             if(library == null) {
80                 System.err.println("FMU library not loaded. FMU simulation not working.");
81                 continue;
82             } else if(!library.isFile()) {
83                 System.err.println(library.getAbsolutePath() + " not found");
84             } else {
85                 try {
86                     System.load(library.getAbsolutePath());
87                 } catch (Throwable t) {
88                     System.err.println(t.getMessage());
89                 }
90             } 
91         }
92     }
93
94     /**
95      * Static: initialize fmu temp folder from current working directory
96      */
97     static {
98         TEMP_FMU_COMMON_DIRECTORY = Paths.get(".").toAbsolutePath().normalize().toString();
99     }
100     
101     public static void setTempFMUCommonDir(File dir) {
102         TEMP_FMU_COMMON_DIRECTORY = dir.getAbsolutePath(); 
103     }
104
105     private String fmuDir;
106     private int id;
107
108     public String TEMP_FOLDER_1;
109     public String TEMP_FOLDER_2;
110     public String TEMP_FMU_DIRECTORY;
111     private String dirName;
112
113         private String[] variableNames;
114         private String[] variableDescriptions;
115         private String[] variableDeclaredTypes;
116         private int[] variableReferences;
117         private int[] variableTypes;
118         private int[] variableCausalities;
119         private int[] variableVariabilities;
120         
121         private String[] declaredTypes;
122         private String[] declaredTypeDescriptions;
123         private String[] declaredTypeQuantities;
124         private String[] declaredTypeUnits;
125         
126         private TObjectIntHashMap<String> variableMap = new TObjectIntHashMap<String>();
127         
128         private Set<String> subscriptionSet = new HashSet<String>();
129         private TIntArrayList subscription = new TIntArrayList();
130         private ArrayList<String> subscribedNames = new ArrayList<String>();
131         
132         public List<String> getSubscribedNames() {
133                 return subscribedNames;
134         }
135         
136         public boolean subscribe(String name) throws FMILException {
137                 synchronized(syncObject) {
138                         // Safety check
139                         int vr = variableMap.get(name);
140                         if(vr == 0) return false;
141                         if(!subscriptionSet.add(name)) return false;
142                         subscribedNames.add(name);
143                         subscription.add(vr);
144                         subscribe(new int[] { vr });
145                         return true;
146                 }
147         }
148
149     public FMIL() {
150         // Create a directory for this control
151         File tempDir = new File(TEMP_FMU_COMMON_DIRECTORY, UUID.randomUUID().toString());
152         tempDir.mkdir();
153         TEMP_FMU_DIRECTORY = tempDir.getAbsolutePath();
154
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);
158         fmuDir.mkdir();
159
160         TEMP_FOLDER_1 = fmuDir.toString();
161         TEMP_FOLDER_2 = fmuDir.toString() + "_2";
162
163         // Lock fmu directory in temp directory
164         lockFMUDirectory();
165     }
166
167     public int getModelIDNew() {
168         return id;
169     }
170
171     public String getModelID() {
172         return dirName;
173     }
174
175     public String getFmuDir() {
176         return fmuDir;
177     }
178
179     /**
180      * Load fmu from a given file path. Releases the (possible) previously
181      * loaded fmu.
182      * 
183      * @param path absolute file path for fmu file
184      * @throws FMILException
185      */
186     private int fmuN = 0;
187     private boolean instantiated = false;
188     public void loadFMUFile(String path) throws FMILException {
189
190         if (!Files.exists(Paths.get(path)))
191             throw new FMILException("File " + path + " does not exist");
192         if (!Files.isRegularFile(Paths.get(path)))
193             throw new FMILException("Path " + path + " is not a file");
194
195         synchronized(syncObject) {
196
197             if(fmuN % 2 == 0) {
198                 fmuDir = TEMP_FOLDER_1;
199                 fmuN++;
200             } else {
201                 fmuDir = TEMP_FOLDER_2;
202                 fmuN = 0;
203             }
204
205             java.nio.file.Path tempDir = Paths.get(fmuDir);
206             if(Files.exists(tempDir) && Files.isDirectory(tempDir)) {
207                 try {
208                         FileUtils.emptyDirectory(tempDir);
209                 } catch (IOException e) {
210                     throw new FMILException("Could not delete existing files from temp folder for fmu " + path, e);
211                 }
212             } else {
213                 try {
214                         Files.createDirectory(tempDir);
215                 } catch (IOException e) {
216                     throw new FMILException("Could not create temp folder for fmu " + path, e);
217                 }
218             }
219
220             try {
221                 String tmpPath = tempDir.toString();
222                 if(!tmpPath.endsWith("\\") && !tmpPath.endsWith("/"))
223                     tmpPath = tmpPath + "/";
224                 id = loadFMUFile_(path, tmpPath);
225                 
226                 getAllVariables();
227                 getAllVariableReferences();
228                 
229                 for(int i=0;i<variableNames.length;i++) {
230                         variableMap.put(variableNames[i], variableReferences[i]);
231                 }
232                 
233                 instantiated = false;
234             } catch (UnsatisfiedLinkError err) {
235                 throw new FMILException(UNSATISFIED_LINK, err);
236             } catch (Exception e) {
237                 LOGGER.error(e.getMessage());
238                 throw new FMILException(e.getMessage());
239             }
240         }
241     }
242
243     private native int loadFMUFile_(String path, String toDir) throws FMILException;
244
245     /**
246      * Set a step length for simulation
247      * 
248      * @param step Step length for simulation
249      * @throws FMILException
250      */
251     public void setStepLength(double step) throws FMILException {
252         synchronized(syncObject) {
253
254             try {
255                 int ret = setStepLength_(getModelIDNew(), step);
256                 if(ret != OK)
257                     LOGGER.warn("Function return value != OK, an exception should have been thrown from native code!");
258
259             } catch (UnsatisfiedLinkError err) {
260                 throw new FMILException(UNSATISFIED_LINK);
261             } catch (Exception e) {
262                 throw new FMILException(e.getMessage());
263             }
264         }
265     }
266
267     private native int setStepLength_(int id, double step);
268
269     /**
270      * Instantiates a simulation. 
271      * <p>
272      * Make sure that an FMU is loaded first.
273      * @throws FMILException
274      */
275     public void instantiateSimulation() throws FMILException {
276         synchronized(syncObject) {
277
278             try {
279
280                 int ret = instantiateSimulation_(getModelIDNew()); 
281                 if(ret != OK) {
282                     LOGGER.warn("Function return value != OK, an exception should have been thrown from native code!");
283                 } else {
284                         instantiated = true;
285                 }
286             } catch (FMILException e) {
287                 throw e;
288             } catch (UnsatisfiedLinkError err) {
289                 throw new FMILException(UNSATISFIED_LINK);
290             } catch (Exception e) {
291                 throw new FMILException(e.getMessage());
292             }
293         }
294     }
295
296     private native int instantiateSimulation_(int id) throws FMILException;
297
298     
299     /**
300      * Initializes a simulation. 
301      * <p>
302      * Make sure that simulation is instantiated first!
303      * @throws FMILException
304      */
305     public void initializeSimulation() throws FMILException {
306         synchronized(syncObject) {
307
308             try {
309
310                 int ret = initializeSimulation_(getModelIDNew()); 
311                 if(ret != OK)
312                     LOGGER.warn("Function return value != OK, an exception should have been thrown from native code!");
313
314             } catch (FMILException e) {
315                 throw e;
316             } catch (UnsatisfiedLinkError err) {
317                 throw new FMILException(UNSATISFIED_LINK);
318             } catch (Exception e) {
319                 throw new FMILException(e.getMessage());
320             }
321         }
322     }
323
324     private native int initializeSimulation_(int id) throws FMILException;
325
326     /**
327      * Subscribe a set of variables from a loaded simulation.
328      * <p>
329      * Make sure that an FMU is loaded first.
330      * @param variables Array of variables
331      * @throws FMILException
332      */
333     public void subscribe(int[] variables) throws FMILException {
334         synchronized(syncObject) {
335
336             try {
337
338                 int ret = subscribe_(getModelIDNew(), variables); 
339                 if(ret != OK)
340                     LOGGER.warn("Function return value != OK, an exception should have been thrown from native code!");
341
342             } catch (UnsatisfiedLinkError err) {
343                 throw new FMILException(UNSATISFIED_LINK);
344             } catch (Exception e) {
345                 throw new FMILException(e.getMessage());
346             }
347         }
348     }
349
350     private native int subscribe_(int id, int[] variables);
351
352     /**
353      * Set value of a real variable. If the variable is a 
354      * parameter, the change is effective immediately.
355      *  
356      * @param name  Variable name
357      * @param value  New value
358      * @throws FMILException
359      */
360     public void setRealValue(String name, double value) throws FMILException {
361         setRealValue(variableMap.get(name), value);
362     }
363
364     /**
365      * Set value of a real variable. If the variable is a 
366      * parameter, the change is effective immediately.
367      *  
368      * @param name  Variable id
369      * @param value  New value
370      * @throws FMILException
371      */
372     public void setRealValue(int variableReference, double value) throws FMILException {
373         synchronized(syncObject) {
374             try {
375                 int ret = setRealValue_(getModelIDNew(), variableReference, value); 
376                 if(ret != OK)
377                     LOGGER.warn("Function return value != OK, an exception should have been thrown from native code!");
378             } catch (FMILException e) {
379                 throw e;
380             } catch (UnsatisfiedLinkError err) {
381                 throw new FMILException(UNSATISFIED_LINK);
382             } catch (Exception e) {
383                 throw new FMILException(e.getMessage());
384             }
385         }
386     }
387
388     private native int setRealValue_(int id, int variableReference, double value) throws FMILException;
389
390     
391     /**
392      * Set value of an integer variable. If the variable is a 
393      * parameter, the change is effective immediately.
394      *  
395      * @param name  Variable name
396      * @param value  New value
397      * @throws FMILException
398      */
399     public void setIntegerValue(String name, int value) throws FMILException {
400         setIntegerValue(variableMap.get(name), value);
401     }
402
403     /**
404      * Set value of an integer variable. If the variable is a 
405      * parameter, the change is effective immediately.
406      *  
407      * @param name  Variable id
408      * @param value  New value
409      * @throws FMILException
410      */
411     public void setIntegerValue(int variableReference, int value) throws FMILException {
412         synchronized(syncObject) {
413             try {
414                 int ret = setIntegerValue_(getModelIDNew(), variableReference, value); 
415                 if(ret != OK)
416                     LOGGER.warn("Function return value != OK, an exception should have been thrown from native code!");
417             } catch (FMILException e) {
418                 throw e;
419             } catch (UnsatisfiedLinkError err) {
420                 throw new FMILException(UNSATISFIED_LINK);
421             } catch (Exception e) {
422                 throw new FMILException(e.getMessage());
423             }
424         }
425     }
426
427     private native int setIntegerValue_(int id, int variableReference, int value) throws FMILException;
428
429     
430     /**
431      * Set value of a boolean variable. If the variable is a 
432      * parameter, the change is effective immediately.
433      *  
434      * @param name  Variable name
435      * @param value  New value
436      * @throws FMILException
437      */
438     public void setBooleanValue(String name, boolean value) throws FMILException {
439         setBooleanValue(variableMap.get(name), value);
440     }
441
442     /**
443      * Set value of a boolean variable. If the variable is a 
444      * parameter, the change is effective immediately.
445      *  
446      * @param name  Variable id
447      * @param value  New value
448      * @throws FMILException
449      */
450     public void setBooleanValue(int variableReference, boolean value) throws FMILException {
451         synchronized(syncObject) {
452             try {
453                 int ret = setBooleanValue_(getModelIDNew(), variableReference, value); 
454                 if(ret != OK)
455                     LOGGER.warn("Function return value != OK, an exception should have been thrown from native code!");
456             } catch (FMILException e) {
457                 throw e;
458             } catch (UnsatisfiedLinkError err) {
459                 throw new FMILException(UNSATISFIED_LINK);
460             } catch (Exception e) {
461                 throw new FMILException(e.getMessage());
462             }
463         }
464     }
465
466     private native int setBooleanValue_(int id, int variableReference, boolean value) throws FMILException;
467
468     
469     /**
470      * Set value of a string variable. If the variable is a 
471      * parameter, the change is effective immediately.
472      *  
473      * @param name  Variable name
474      * @param value  New value
475      * @throws FMILException
476      */
477     public void setStringValue(String name, String value) throws FMILException {
478         setStringValue(variableMap.get(name), value);
479     }
480
481     /**
482      * Set value of a string variable. If the variable is a 
483      * parameter, the change is effective immediately.
484      *  
485      * @param name  Variable id
486      * @param value  New value
487      * @throws FMILException
488      */
489     public void setStringValue(int variableReference, String value) throws FMILException {
490         synchronized(syncObject) {
491             try {
492                 int ret = setStringValue_(getModelIDNew(), variableReference, value); 
493                 if(ret != OK)
494                     LOGGER.warn("Function return value != OK, an exception should have been thrown from native code!");
495             } catch (FMILException e) {
496                 throw e;
497             } catch (UnsatisfiedLinkError err) {
498                 throw new FMILException(UNSATISFIED_LINK);
499             } catch (Exception e) {
500                 throw new FMILException(e.getMessage());
501             }
502         }
503     }
504
505     private native int setStringValue_(int id, int variableReference, String value) throws FMILException;
506     
507
508     /**
509      * Simulate one step forward. The step length can be set with
510      * setStepLength()
511      * 
512      * @throws FMILException
513      */
514     public void simulateStep() throws FMILException {
515         synchronized(syncObject) {
516
517             try {
518
519                 int ret = simulateStep_(getModelIDNew()); //0 is ok, 1 is error, 2 is pending
520                 if(ret == PENDING)
521                     LOGGER.warn("Pending status return from FMU. This is not implemented in our Simulator yet!");
522                 else if(ret != OK)
523                     LOGGER.warn("Function return value != OK, an exception should have been thrown from native code!");
524             } catch (FMILException e) {
525                 throw e;
526             } catch (UnsatisfiedLinkError err) {
527                 throw new FMILException(UNSATISFIED_LINK);
528             } catch (Exception e) {
529                 throw new FMILException(e.getMessage());
530             }
531         }
532     }
533     private native int simulateStep_(int id) throws FMILException;
534
535     /**
536      * Get an array containing the current values for subscribed variables. The
537      * values are in the same order as in the subscription.
538      * 
539      * @param results An array the size of subscribed results
540      * @return
541      */
542     public double[] getSubscribedResults() throws FMILException {
543         synchronized(syncObject) {
544
545             try {
546                 
547                 double[] results = new double[subscription.size()];
548                 Arrays.fill(results, Double.NaN);
549                 
550                 return getSubscribedResults_(getModelIDNew(), results);
551             } catch (UnsatisfiedLinkError err) {
552                 throw new FMILException(UNSATISFIED_LINK);
553             } catch (Exception e) {
554                 throw new FMILException(e.getMessage());
555             }
556         }
557     }
558
559     private native double[] getSubscribedResults_(int id, double[] results);
560     
561
562     /**
563      * Unload FMU and the dll:s that it requires.
564      * <p>
565      * To be called after all FMU simulations are ended. 
566      * If the fmu is loaded again / changed, call to loadFMUFile is sufficient. loadFMUFile 
567      * releases the previous fmu.dll  
568      * 
569      * @throws FMILException
570      */
571     public void unloadFMU() throws FMILException {
572         synchronized(syncObject) {
573
574             try {
575
576                 unlockFMUDirectory();
577                 
578                 // Many FMUs will not correctly clean-up during unload if the simulation isn't instantiated
579                 // Essentially we'd likely be passing an invalid pointer to the FMU to cleanup, causing native c-level crashes.
580                 
581                 if(instantiated) {
582                     int ret = unloadFMU_(getModelIDNew()); 
583                     if(ret != OK)
584                         LOGGER.warn("Function return value != OK, an exception should have been thrown from native code!");
585                     instantiated = false;
586                 }
587                 removeFMUDirectoryContents();
588
589             } catch (FMILException e) {
590                 throw e;
591             } catch (UnsatisfiedLinkError err) {
592                 throw new FMILException(UNSATISFIED_LINK);
593             } catch (Exception e) {
594                 throw new FMILException(e.getMessage());
595             }
596         }
597     }
598     private native int unloadFMU_(int id) throws FMILException;
599     
600 //    /**
601 //     * Checks if fmu has been initialized
602 //     * @return current simulation time
603 //     */
604 //    public boolean isInitialized() throws FMILException {
605 //        synchronized(syncObject) {
606 //            try {
607 //                return isInitialized_(getModelID());
608 //            } catch (UnsatisfiedLinkError err) {
609 //                throw new FMILException(UNSATISFIED_LINK);
610 //            } catch (Exception e) {
611 //                throw new FMILException(e.getMessage());
612 //            }
613 //        }
614 //    }
615 //
616 //    private native boolean isInitialized_(String id);
617 //
618     /**
619      * Get the current simulation time
620      * @return current simulation time
621      */
622     public double getTime() throws FMILException {
623         synchronized(syncObject) {
624
625             try {
626
627                 return getTime_(getModelIDNew());
628
629             } catch (UnsatisfiedLinkError err) {
630                 throw new FMILException(UNSATISFIED_LINK);
631             } catch (Exception e) {
632                 throw new FMILException(e.getMessage());
633             }
634         }
635     }
636
637     private native double getTime_(int id);
638
639     /**
640      * Get all variables in a loaded model
641      * @return all variables in a loaded model
642      */
643     public String[] getAllVariables() throws FMILException {
644         synchronized(syncObject) {
645
646             try {
647
648                 if(variableNames == null) {
649                         variableNames = getAllVariables_(getModelIDNew());
650                 }
651                 return variableNames;
652
653             } catch (UnsatisfiedLinkError err) {
654                 throw new FMILException(UNSATISFIED_LINK);
655             } catch (Exception e) {
656                 throw new FMILException(e.getMessage());
657             }
658         }
659     }
660
661     private native String[] getAllVariables_(int id);
662
663     public String[] getAllVariableDescriptions() throws FMILException {
664         synchronized(syncObject) {
665
666             try {
667
668                 if(variableDescriptions == null) {
669                         variableDescriptions = getAllVariableDescriptions_(getModelIDNew());
670                 }
671                 return variableDescriptions;
672
673             } catch (UnsatisfiedLinkError err) {
674                 throw new FMILException(UNSATISFIED_LINK);
675             } catch (Exception e) {
676                 throw new FMILException(e.getMessage());
677             }
678         }
679     }
680
681     private native String[] getAllVariableDescriptions_(int id);
682     
683     public String[] getAllVariableDeclaredTypes() throws FMILException {
684         synchronized(syncObject) {
685
686             try {
687
688                 if(variableDeclaredTypes == null) {
689                         variableDeclaredTypes = getAllVariableDeclaredTypes_(getModelIDNew());
690                 }
691                 return variableDeclaredTypes;
692
693             } catch (UnsatisfiedLinkError err) {
694                 throw new FMILException(UNSATISFIED_LINK);
695             } catch (Exception e) {
696                 throw new FMILException(e.getMessage());
697             }
698         }
699     }
700
701     private native String[] getAllVariableDeclaredTypes_(int id);
702
703     public int[] getAllVariableReferences() throws FMILException {
704         synchronized(syncObject) {
705
706             try {
707
708                 if(variableReferences == null) {
709                         variableReferences = getAllVariableReferences_(getModelIDNew(), new int[variableNames.length]); 
710                 }
711                 return variableReferences;
712
713             } catch (UnsatisfiedLinkError err) {
714                 throw new FMILException(UNSATISFIED_LINK);
715             } catch (Exception e) {
716                 throw new FMILException(e.getMessage());
717             }
718         }
719     }
720
721     private native int[] getAllVariableReferences_(int id, int[] array);
722
723     public int[] getAllVariableTypes() throws FMILException {
724         synchronized(syncObject) {
725
726             try {
727
728                 if(variableTypes == null) {
729                         variableTypes = getAllVariableTypes_(getModelIDNew(), new int[variableNames.length]); 
730                 }
731                 return variableTypes;
732
733             } catch (UnsatisfiedLinkError err) {
734                 throw new FMILException(UNSATISFIED_LINK);
735             } catch (Exception e) {
736                 throw new FMILException(e.getMessage());
737             }
738         }
739     }
740
741     private native int[] getAllVariableTypes_(int id, int[] array);
742
743     public int[] getAllVariableCausalities() throws FMILException {
744         synchronized(syncObject) {
745
746             try {
747
748                 if(variableCausalities == null) {
749                         variableCausalities = getAllVariableCausalities_(getModelIDNew(), new int[variableNames.length]); 
750                 }
751                 return variableCausalities;
752
753             } catch (UnsatisfiedLinkError err) {
754                 throw new FMILException(UNSATISFIED_LINK);
755             } catch (Exception e) {
756                 throw new FMILException(e.getMessage());
757             }
758         }
759     }
760
761     private native int[] getAllVariableCausalities_(int id, int[] array);
762
763     public int[] getAllVariableVariabilities() throws FMILException {
764         synchronized(syncObject) {
765
766             try {
767
768                 if(variableVariabilities == null) {
769                         variableVariabilities = getAllVariableVariabilities_(getModelIDNew(), new int[variableNames.length]); 
770                 }
771                 return variableVariabilities;
772
773             } catch (UnsatisfiedLinkError err) {
774                 throw new FMILException(UNSATISFIED_LINK);
775             } catch (Exception e) {
776                 throw new FMILException(e.getMessage());
777             }
778         }
779     }
780
781     private native int[] getAllVariableVariabilities_(int id, int[] array);
782
783     /**
784      * Get all variables in a loaded model
785      * @return all variables in a loaded model
786      */
787     public String[] getAllDeclaredTypes() throws FMILException {
788         synchronized(syncObject) {
789
790             try {
791
792                 if(declaredTypes == null) {
793                         declaredTypes = getAllDeclaredTypes_(getModelIDNew());
794                 }
795                 return declaredTypes;
796
797             } catch (UnsatisfiedLinkError err) {
798                 throw new FMILException(UNSATISFIED_LINK);
799             } catch (Exception e) {
800                 throw new FMILException(e.getMessage());
801             }
802         }
803     }
804
805     private native String[] getAllDeclaredTypes_(int id);
806     
807     public String[] getAllDeclaredTypeDescriptions() throws FMILException {
808         synchronized(syncObject) {
809
810             try {
811
812                 if(declaredTypeDescriptions == null) {
813                         declaredTypeDescriptions = getAllDeclaredTypeDescriptions_(getModelIDNew());
814                 }
815                 return declaredTypeDescriptions;
816
817             } catch (UnsatisfiedLinkError err) {
818                 throw new FMILException(UNSATISFIED_LINK);
819             } catch (Exception e) {
820                 throw new FMILException(e.getMessage());
821             }
822         }
823     }
824
825     private native String[] getAllDeclaredTypeDescriptions_(int id);
826     
827     public String[] getAllDeclaredTypeQuantities() throws FMILException {
828         synchronized(syncObject) {
829
830             try {
831
832                 if(declaredTypeQuantities == null) {
833                         declaredTypeQuantities = getAllDeclaredTypeQuantities_(getModelIDNew());
834                 }
835                 return declaredTypeQuantities;
836
837             } catch (UnsatisfiedLinkError err) {
838                 throw new FMILException(UNSATISFIED_LINK);
839             } catch (Exception e) {
840                 throw new FMILException(e.getMessage());
841             }
842         }
843     }
844
845     private native String[] getAllDeclaredTypeQuantities_(int id);
846
847     public String[] getAllDeclaredTypeUnits() throws FMILException {
848         synchronized(syncObject) {
849
850             try {
851
852                 if(declaredTypeUnits == null) {
853                         declaredTypeUnits = getAllDeclaredTypeUnits_(getModelIDNew());
854                 }
855                 return declaredTypeUnits;
856
857             } catch (UnsatisfiedLinkError err) {
858                 throw new FMILException(UNSATISFIED_LINK);
859             } catch (Exception e) {
860                 throw new FMILException(e.getMessage());
861             }
862         }
863     }
864
865     private native String[] getAllDeclaredTypeUnits_(int id);
866     
867     
868 //
869 //    /**
870 //     * Get all variables from model that match the filter (and time variable)
871 //     * 
872 //     * @param regexp Regular expression filter
873 //     * @return An array of variable names that match regexp filter (and time-variable)
874 //     * @throws FMILException
875 //     */
876 //    public String[] filterVariables(String regexp) throws FMILException {       
877 //        synchronized(syncObject) {
878 //            try {
879 //
880 //                return filterVariables_(getModelID(), regexp + "|time");
881 //
882 //            } catch (UnsatisfiedLinkError err) {
883 //                throw new FMILException(UNSATISFIED_LINK);
884 //            } catch (Exception e) {
885 //                throw new FMILException(e.getMessage());
886 //            }
887 //        }
888 //    }
889 //
890 //    private native String[] filterVariables_(String id, String regexp);
891 ////
892 //    /**
893 //     * Get the last error message
894 //     * @return Last error message
895 //     */
896 //    public String getLastErrorMessage() throws FMILException {
897 //        synchronized(syncObject) {
898 //
899 //            try {
900 //
901 //              return "err";
902 //                //return getLastErrorMessage_(getModelID());
903 //
904 //            } catch (UnsatisfiedLinkError err) {
905 //                throw new FMILException(UNSATISFIED_LINK);
906 //            } catch (Exception e) {
907 //                throw new FMILException(e.getMessage());
908 //            }
909 //        }
910 //    }
911 //
912 //    private native String getLastErrorMessage_(String id);
913
914     /**
915      * Get a value (double) for real variable
916      * @param name Name of the variable
917      * @return value
918      * @throws FMILException
919      */
920     public double getRealValue(String name) throws FMILException {
921         double result = getRealValue(variableMap.get(name)); 
922         if (DEBUG) System.err.println("getRealValue " + name + " = " + result);
923         return result;
924     }
925
926     /**
927      * Get a value (double) for real variable
928      * @param variableReference  Numeric id of the variable
929      * @return value
930      * @throws FMILException
931      */
932     public double getRealValue(int variableReference) throws FMILException {
933         synchronized(syncObject) {
934             try {
935                 return getRealValue_(getModelIDNew(), variableReference);
936             } catch (UnsatisfiedLinkError err) {
937                 throw new FMILException(UNSATISFIED_LINK);
938             } catch (Exception e) {
939                 throw new FMILException(e.getMessage());
940             }
941         }
942     }
943     
944     private native double getRealValue_(int id, int variableReference) throws FMILException;
945
946
947     /**
948      * Get value of integer variable
949      * @param name Name of the variable
950      * @return value
951      * @throws FMILException
952      */
953     public int getIntegerValue(String name) throws FMILException {
954         int result = getIntegerValue(variableMap.get(name));
955         if (DEBUG) System.err.println("getIntegerValue " + name + " = " + result);
956         return result;
957     }
958
959     /**
960      * Get a real (double) value for variable
961      * @param variableReference  Numeric id of the variable
962      * @return value
963      * @throws FMILException
964      */
965     public int getIntegerValue(int variableReference) throws FMILException {
966         synchronized(syncObject) {
967             try {
968                 return getIntegerValue_(getModelIDNew(), variableReference);
969             } catch (UnsatisfiedLinkError err) {
970                 throw new FMILException(UNSATISFIED_LINK);
971             } catch (Exception e) {
972                 throw new FMILException(e.getMessage());
973             }
974         }
975     }
976     
977     private native int getIntegerValue_(int id, int variableReference) throws FMILException;
978     
979     
980     /**
981      * Get value of boolean variable
982      * @param name Name of the variable
983      * @return value
984      * @throws FMILException
985      */
986     public boolean getBooleanValue(String name) throws FMILException {
987         boolean result = getBooleanValue(variableMap.get(name));
988         if (DEBUG) System.err.println("getBooleanValue " + name + " = " + result);
989         return result;
990     }
991
992     /**
993      * Get value of boolean variable
994      * @param variableReference  Numeric id of the variable
995      * @return value
996      * @throws FMILException
997      */
998     public boolean getBooleanValue(int variableReference) throws FMILException {
999         synchronized(syncObject) {
1000             try {
1001                 return getBooleanValue_(getModelIDNew(), variableReference);
1002             } catch (UnsatisfiedLinkError err) {
1003                 throw new FMILException(UNSATISFIED_LINK);
1004             } catch (Exception e) {
1005                 throw new FMILException(e.getMessage());
1006             }
1007         }
1008     }
1009     
1010     private native boolean getBooleanValue_(int id, int variableReference) throws FMILException;
1011     
1012     
1013     /**
1014      * Get value of string variable
1015      * @param name Name of the variable
1016      * @return value
1017      * @throws FMILException
1018      */
1019     public String getStringValue(String name) throws FMILException {
1020         String result = getStringValue(variableMap.get(name));
1021         if (DEBUG) System.err.println("getIntegerValue " + name + " = " + result);
1022         return result;
1023     }
1024
1025     /**
1026      * Get value of string variable
1027      * @param variableReference  Numeric id of the variable
1028      * @return value
1029      * @throws FMILException
1030      */
1031     public String getStringValue(int variableReference) throws FMILException {
1032         synchronized(syncObject) {
1033             try {
1034                 return getStringValue_(getModelIDNew(), variableReference);
1035             } catch (UnsatisfiedLinkError err) {
1036                 throw new FMILException(UNSATISFIED_LINK);
1037             } catch (Exception e) {
1038                 throw new FMILException(e.getMessage());
1039             }
1040         }
1041     }
1042     
1043     private native String getStringValue_(int id, int variableReference) throws FMILException;
1044     
1045
1046     private FileChannel channel; 
1047     private FileLock lock;
1048
1049     @SuppressWarnings("resource")
1050     private boolean lockFMUDirectory() {
1051
1052         try {
1053             // Get a file channel for the lock file
1054             File lockFile = new File(TEMP_FMU_DIRECTORY, LOCK_FILE_NAME);
1055             if(!lockFile.isFile())
1056                 lockFile.createNewFile();
1057
1058             channel = new RandomAccessFile(lockFile, "rw").getChannel();
1059
1060             // Use the file channel to create a lock on the file.
1061             // This method blocks until it can retrieve the lock.
1062             lock = channel.lock();
1063
1064             //          // Try acquiring the lock without blocking. This method returns
1065             //          // null or throws an exception if the file is already locked.
1066             //          try {
1067             //              lock = channel.tryLock();
1068             //          } catch (OverlappingFileLockException e) {
1069             //              // File is already locked in this thread or virtual machine
1070             //          }
1071         } catch (IOException e) {
1072             return false;
1073         }
1074
1075         return true;
1076     }
1077
1078     private boolean unlockFMUDirectory() {
1079         try {
1080             // Release the lock
1081             if(lock != null)
1082                 lock.release();
1083
1084             // Close the file
1085             if(channel != null)
1086                 channel.close();
1087         } catch (IOException e) {
1088             return false;
1089         }
1090         return true;
1091     }
1092
1093     private boolean removeFMUDirectoryContents() {
1094         // Remove contents
1095         try {
1096             File tempDir = new File(TEMP_FMU_DIRECTORY);
1097             FileUtils.deleteAll(tempDir);
1098             tempDir.delete();
1099         } catch (IOException e) {
1100             return false;
1101         }
1102         return true;
1103     }
1104     
1105     @Override
1106     protected void finalize() throws Throwable {
1107         try {
1108             unloadFMU();
1109         } catch (Throwable t) {
1110             LOGGER.error("Could not unload native FMU!", t);
1111         } finally {
1112             super.finalize();
1113         }
1114     }
1115 }