-package org.simantics.fmil.core;\r
-\r
-import java.io.File;\r
-import java.io.IOException;\r
-import java.io.RandomAccessFile;\r
-import java.nio.channels.FileChannel;\r
-import java.nio.channels.FileLock;\r
-import java.util.ArrayList;\r
-import java.util.HashSet;\r
-import java.util.List;\r
-import java.util.Set;\r
-import java.util.UUID;\r
-\r
-import org.eclipse.core.runtime.FileLocator;\r
-import org.eclipse.core.runtime.Platform;\r
-import org.osgi.framework.Bundle;\r
-import org.simantics.Simantics;\r
-import org.simantics.fmil.core.ExecEnvironment.ARCHType;\r
-import org.simantics.fmil.core.ExecEnvironment.OSType;\r
-import org.simantics.utils.FileUtils;\r
-\r
-import gnu.trove.list.array.TIntArrayList;\r
-import gnu.trove.map.hash.TObjectIntHashMap;\r
-\r
-\r
-public class FMIL {\r
-\r
- /**\r
- * Static variables\r
- */\r
- private static int ERROR = 0;\r
- private static int OK = 1;\r
- private static String UNSATISFIED_LINK = "Method not found. DLL might not be loaded properly."; \r
- private static String TEMP_FMU_DIRECTORY_NAME = "fmil"; \r
- public static String TEMP_FMU_COMMON_DIRECTORY; \r
- public static String LOCK_FILE_NAME = "fmil.lock";\r
-\r
- public static Object syncObject = new Object();\r
-\r
- /**\r
- * Static: load native libraries required for the FMU simulation to work.\r
- */\r
- static {\r
- \r
- File[] libraries = new File[2];\r
-\r
- Bundle bundle = null;\r
- \r
- ExecEnvironment env = ExecEnvironment.calculate();\r
- if (env.os == OSType.WINDOWS) {\r
- if (env.arch == ARCHType.X86) {\r
- bundle = Platform.getBundle("org.simantics.fmil.win32");\r
- } else if (env.arch == ARCHType.X86_64) {\r
- bundle = Platform.getBundle("org.simantics.fmil.win64");\r
- }\r
- }\r
- \r
- if (bundle != null) {\r
- try{\r
- String root = FileLocator.getBundleFile(bundle).getAbsolutePath();\r
-// if (env.arch == ARCHType.X86_64) {\r
-// File newFIle = new File(root, "libraries/libexpat.dll");\r
-// System.load(newFIle.getAbsolutePath());\r
-// }\r
-// libraries[0] = new File(root, "libraries/zlibwapi.dll");\r
-// libraries[1] = new File(root, "libraries/miniunz.dll");\r
- libraries[0] = new File(root, "libraries/fmilib_shared.dll");\r
- libraries[1] = new File(root, "libraries/FMUSimulator.dll");\r
- }\r
- catch (Exception e) {\r
- e.printStackTrace();\r
- }\r
- }\r
-\r
- for(File library : libraries) {\r
- if(library == null) {\r
- System.err.println("FMU library not loaded. FMU simulation not working.");\r
- continue;\r
- } else if(!library.isFile()) {\r
- System.err.println(library.getAbsolutePath() + " not found");\r
- } else {\r
- try {\r
- System.load(library.getAbsolutePath());\r
- } catch (Throwable t) {\r
- System.err.println(t.getMessage());\r
- }\r
- } \r
- }\r
- }\r
-\r
- /**\r
- * Static: initialize fmu temp folder\r
- */\r
- static {\r
- File dir = Simantics.getTemporaryDirectory(TEMP_FMU_DIRECTORY_NAME);\r
- TEMP_FMU_COMMON_DIRECTORY = dir.getAbsolutePath(); \r
- }\r
-\r
-\r
- private String fmuDir;\r
- private int id;\r
-\r
- public String TEMP_FOLDER_1;\r
- public String TEMP_FOLDER_2;\r
- public String TEMP_FMU_DIRECTORY;\r
- private String dirName;\r
-\r
- private String[] variableNames;\r
- private String[] variableDescriptions;\r
- private String[] variableDeclaredTypes;\r
- private int[] variableReferences;\r
- private int[] variableTypes;\r
- private int[] variableCausalities;\r
- private int[] variableVariabilities;\r
- \r
- private String[] declaredTypes;\r
- private String[] declaredTypeDescriptions;\r
- private String[] declaredTypeQuantities;\r
- private String[] declaredTypeUnits;\r
- \r
- private TObjectIntHashMap<String> variableMap = new TObjectIntHashMap<String>();\r
- \r
- private Set<String> subscriptionSet = new HashSet<String>();\r
- private TIntArrayList subscription = new TIntArrayList();\r
- private ArrayList<String> subscribedNames = new ArrayList<String>();\r
- \r
- public List<String> getSubscribedNames() {\r
- return subscribedNames;\r
- }\r
- \r
- public boolean subscribe(String name) throws FMILException {\r
- // Safety check\r
- int vr = variableMap.get(name);\r
- if(vr == 0) return false;\r
- if(!subscriptionSet.add(name)) return false;\r
- subscribedNames.add(name);\r
- System.err.println("subscribed : " + name + " => " + subscribedNames.size());\r
- subscription.add(vr);\r
- subscribe(new int[] { vr });\r
- return true;\r
- }\r
-\r
- public FMIL() {\r
- // Create a directory for this control\r
- File tempDir = new File(TEMP_FMU_COMMON_DIRECTORY, UUID.randomUUID().toString());\r
- tempDir.mkdir();\r
- TEMP_FMU_DIRECTORY = tempDir.getAbsolutePath();\r
-\r
- // Create two directories inside the temp directory for this control\r
- dirName = UUID.randomUUID().toString();\r
- File fmuDir = new File(TEMP_FMU_DIRECTORY, dirName);\r
- fmuDir.mkdir();\r
-\r
- TEMP_FOLDER_1 = fmuDir.toString();\r
- TEMP_FOLDER_2 = fmuDir.toString() + "_2";\r
-\r
- // Lock fmu directory in temp directory\r
- lockFMUDirectory();\r
- }\r
-\r
- public int getModelIDNew() {\r
- return id;\r
- }\r
-\r
- public String getModelID() {\r
- return dirName;\r
- }\r
-\r
- public String getFmuDir() {\r
- return fmuDir;\r
- }\r
-\r
- /**\r
- * Load fmu from a given file path. Releases the (possible) previously\r
- * loaded fmu.\r
- * \r
- * @param path absolute file path for fmu file\r
- * @throws FMILException\r
- */\r
- private int fmuN = 0;\r
- private boolean fmuLoaded = false;\r
- public void loadFMUFile(String path) throws FMILException {\r
-\r
- synchronized(syncObject) {\r
-\r
- if(fmuN % 2 == 0) {\r
- fmuDir = TEMP_FOLDER_1;\r
- fmuN++;\r
- } else {\r
- fmuDir = TEMP_FOLDER_2;\r
- fmuN = 0;\r
- }\r
-\r
- File tempDir = new File(fmuDir);\r
- if(tempDir.isDirectory()) {\r
- try {\r
- FileUtils.deleteAll(tempDir);\r
- } catch (IOException e) {\r
- throw new FMILException("Could not create temp folder for fmu");\r
- }\r
- tempDir.mkdir();\r
- } else {\r
- tempDir.mkdir();\r
- }\r
-\r
-\r
- try {\r
- String tmpPath = tempDir.getAbsolutePath();\r
- if(!tmpPath.endsWith("\\"))\r
- tmpPath = tmpPath + "\\";\r
- id = loadFMUFile_(path, tmpPath);\r
- \r
- getAllVariables();\r
- getAllVariableReferences();\r
- \r
- for(int i=0;i<variableNames.length;i++) {\r
- variableMap.put(variableNames[i], variableReferences[i]);\r
- }\r
-\r
- fmuLoaded = true;\r
- } catch (UnsatisfiedLinkError err) {\r
- throw new FMILException(UNSATISFIED_LINK, err);\r
- } catch (Exception e) {\r
- throw new FMILException(e.getMessage());\r
- }\r
- }\r
- }\r
-\r
- private native int loadFMUFile_(String path, String toDir);\r
-\r
- /**\r
- * Set a step length for simulation\r
- * \r
- * @param step Step length for simulation\r
- * @throws FMILException\r
- */\r
- public void setStepLength(double step) throws FMILException {\r
- synchronized(syncObject) {\r
-\r
- try {\r
-\r
- int ret = setStepLength_(getModelIDNew(), step);\r
- if(ret == ERROR)\r
- throw new FMILException(getLastErrorMessage());\r
-\r
- } catch (UnsatisfiedLinkError err) {\r
- throw new FMILException(UNSATISFIED_LINK);\r
- } catch (Exception e) {\r
- throw new FMILException(e.getMessage());\r
- }\r
- }\r
- }\r
-\r
- private native int setStepLength_(int id, double step);\r
-\r
- /**\r
- * Instantiates a simulation. \r
- * <p>\r
- * Make sure that an FMU is loaded first.\r
- * @throws FMILException\r
- */\r
- public void instantiateSimulation() throws FMILException {\r
- synchronized(syncObject) {\r
-\r
- try {\r
-\r
- int ret = instantiateSimulation_(getModelIDNew()); \r
- if(ret == ERROR)\r
- throw new FMILException(getLastErrorMessage());\r
-\r
- } catch (UnsatisfiedLinkError err) {\r
- throw new FMILException(UNSATISFIED_LINK);\r
- } catch (Exception e) {\r
- throw new FMILException(e.getMessage());\r
- }\r
- }\r
- }\r
-\r
- private native int instantiateSimulation_(int id);\r
-\r
- \r
- /**\r
- * Initializes a simulation. \r
- * <p>\r
- * Make sure that simulation is instantiated first!\r
- * @throws FMILException\r
- */\r
- public void initializeSimulation() throws FMILException {\r
- synchronized(syncObject) {\r
-\r
- try {\r
-\r
- int ret = initializeSimulation_(getModelIDNew()); \r
- if(ret == ERROR)\r
- throw new FMILException(getLastErrorMessage());\r
-\r
- } catch (UnsatisfiedLinkError err) {\r
- throw new FMILException(UNSATISFIED_LINK);\r
- } catch (Exception e) {\r
- throw new FMILException(e.getMessage());\r
- }\r
- }\r
- }\r
-\r
- private native int initializeSimulation_(int id);\r
-\r
- /**\r
- * Subscribe a set of variables from a loaded simulation.\r
- * <p>\r
- * Make sure that an FMU is loaded first.\r
- * @param variables Array of variables\r
- * @throws FMILException\r
- */\r
- public void subscribe(int[] variables) throws FMILException {\r
- synchronized(syncObject) {\r
-\r
- try {\r
-\r
- int ret = subscribe_(getModelIDNew(), variables); \r
- if(ret == ERROR)\r
- throw new FMILException(getLastErrorMessage());\r
-\r
- } catch (UnsatisfiedLinkError err) {\r
- throw new FMILException(UNSATISFIED_LINK);\r
- } catch (Exception e) {\r
- throw new FMILException(e.getMessage());\r
- }\r
- }\r
- }\r
-\r
- private native int subscribe_(int id, int[] variables);\r
-\r
- /**\r
- * Set a new (Real, double) value for a variable. If the variable is a \r
- * parameter, the change is effective immediately.\r
- * \r
- * @param name Variable\r
- * @param value New (Real, double) value\r
- * @throws FMILException\r
- */\r
- public void setRealValue(String name, double value) throws FMILException {\r
- \r
- synchronized(syncObject) {\r
-\r
- try {\r
-\r
- int ret = setRealValue_(getModelIDNew(), variableMap.get(name), value); \r
- if(ret == ERROR)\r
- throw new FMILException(getLastErrorMessage());\r
-\r
- } catch (UnsatisfiedLinkError err) {\r
- throw new FMILException(UNSATISFIED_LINK);\r
- } catch (Exception e) {\r
- throw new FMILException(e.getMessage());\r
- }\r
- \r
- }\r
- \r
- }\r
-\r
- public void setRealValue(int variableReference, double value) throws FMILException {\r
- \r
- synchronized(syncObject) {\r
-\r
- try {\r
-\r
- int ret = setRealValue_(getModelIDNew(), variableReference, value); \r
- if(ret == ERROR)\r
- throw new FMILException(getLastErrorMessage());\r
-\r
- } catch (UnsatisfiedLinkError err) {\r
- throw new FMILException(UNSATISFIED_LINK);\r
- } catch (Exception e) {\r
- throw new FMILException(e.getMessage());\r
- }\r
- \r
- }\r
- \r
- }\r
-\r
- private native int setRealValue_(int id, int variableReference, double value);\r
-\r
-// /**\r
-// * Set a new (integer) value for a variable. If the variable is a \r
-// * parameter, the change is effective immediately.\r
-// * \r
-// * @param name Variable\r
-// * @param value New (integer) value\r
-// * @throws FMILException\r
-// */\r
-// public void setIntegerValue(String name, int value) throws FMILException {\r
-// synchronized(syncObject) {\r
-//\r
-// try {\r
-//\r
-// int ret = setIntegerValue_(getModelID(), name, value); \r
-// if(ret == ERROR)\r
-// throw new FMILException(getLastErrorMessage());\r
-//\r
-// } catch (UnsatisfiedLinkError err) {\r
-// throw new FMILException(UNSATISFIED_LINK);\r
-// } catch (Exception e) {\r
-// throw new FMILException(e.getMessage());\r
-// }\r
-// }\r
-// }\r
-// private native int setIntegerValue_(String id, String name, int value);\r
-//\r
-// /**\r
-// * Set a new (boolean) value for a variable. If the variable is a \r
-// * parameter, the change is effective immediately.\r
-// * \r
-// * @param name Variable\r
-// * @param value New (boolean) value\r
-// * @throws FMILException\r
-// */\r
-// public void setBooleanValue(String name, boolean value) throws FMILException {\r
-// synchronized(syncObject) {\r
-//\r
-// try {\r
-//\r
-// int ret = setBooleanValue_(getModelID(), name, value); \r
-// if(ret == ERROR)\r
-// throw new FMILException(getLastErrorMessage());\r
-//\r
-// } catch (UnsatisfiedLinkError err) {\r
-// throw new FMILException(UNSATISFIED_LINK);\r
-// } catch (Exception e) {\r
-// throw new FMILException(e.getMessage());\r
-// }\r
-// }\r
-// }\r
-// private native int setBooleanValue_(String id, String name, boolean value);\r
-//\r
-// public void setTime(double time) throws FMILException {\r
-// synchronized(syncObject) {\r
-//\r
-// try {\r
-//\r
-// int ret = setTime_(getModelID(), time); \r
-// if(ret == ERROR)\r
-// throw new FMILException(getLastErrorMessage());\r
-//\r
-// } catch (UnsatisfiedLinkError err) {\r
-// throw new FMILException(UNSATISFIED_LINK);\r
-// } catch (Exception e) {\r
-// throw new FMILException(e.getMessage());\r
-// }\r
-// }\r
-// }\r
-// private native int setTime_(String id, double time);\r
-\r
- /**\r
- * Simulate one step forward. The step length can be set with\r
- * setStepLength()\r
- * \r
- * @throws FMILException\r
- */\r
- public void simulateStep() throws FMILException {\r
- synchronized(syncObject) {\r
-\r
- try {\r
-\r
- int ret = simulateStep_(getModelIDNew()); \r
- if(ret == ERROR)\r
- throw new FMILException(getLastErrorMessage());\r
-\r
- } catch (UnsatisfiedLinkError err) {\r
- throw new FMILException(UNSATISFIED_LINK);\r
- } catch (Exception e) {\r
- throw new FMILException(e.getMessage());\r
- }\r
- }\r
- }\r
- private native int simulateStep_(int id);\r
-\r
- /**\r
- * Get an array containing the current values for subscribed variables. The\r
- * values are in the same order as in the subscription.\r
- * \r
- * @param results An array the size of subscribed results\r
- * @return\r
- */\r
- public double[] getSubscribedResults() throws FMILException {\r
- synchronized(syncObject) {\r
-\r
- try {\r
-\r
- double[] results = new double[subscription.size()];\r
- return getSubscribedResults_(getModelIDNew(), results);\r
-\r
- } catch (UnsatisfiedLinkError err) {\r
- throw new FMILException(UNSATISFIED_LINK);\r
- } catch (Exception e) {\r
- throw new FMILException(e.getMessage());\r
- }\r
- }\r
- }\r
-\r
- private native double[] getSubscribedResults_(int id, double[] results);\r
- \r
-\r
- /**\r
- * Unload FMU and the dll:s that it requires.\r
- * <p>\r
- * To be called after all FMU simulations are ended. \r
- * If the fmu is loaded again / changed, call to loadFMUFile is sufficient. loadFMUFile \r
- * releases the previous fmu.dll \r
- * \r
- * @throws FMILException\r
- */\r
- public void unloadFMU() throws FMILException {\r
- synchronized(syncObject) {\r
-\r
- try {\r
-\r
- unlockFMUDirectory();\r
- if(fmuLoaded) {\r
- int ret = unloadFMU_(getModelIDNew()); \r
- if(ret == ERROR)\r
- throw new FMILException(getLastErrorMessage());\r
- }\r
- removeFMUDirectoryContents();\r
-\r
- } catch (UnsatisfiedLinkError err) {\r
- throw new FMILException(UNSATISFIED_LINK);\r
- } catch (Exception e) {\r
- throw new FMILException(e.getMessage());\r
- }\r
- }\r
- }\r
- private native int unloadFMU_(int id);\r
- \r
-// /**\r
-// * Checks if fmu has been initialized\r
-// * @return current simulation time\r
-// */\r
-// public boolean isInitialized() throws FMILException {\r
-// synchronized(syncObject) {\r
-// try {\r
-// return isInitialized_(getModelID());\r
-// } catch (UnsatisfiedLinkError err) {\r
-// throw new FMILException(UNSATISFIED_LINK);\r
-// } catch (Exception e) {\r
-// throw new FMILException(e.getMessage());\r
-// }\r
-// }\r
-// }\r
-//\r
-// private native boolean isInitialized_(String id);\r
-//\r
- /**\r
- * Get the current simulation time\r
- * @return current simulation time\r
- */\r
- public double getTime() throws FMILException {\r
- synchronized(syncObject) {\r
-\r
- try {\r
-\r
- return getTime_(getModelIDNew());\r
-\r
- } catch (UnsatisfiedLinkError err) {\r
- throw new FMILException(UNSATISFIED_LINK);\r
- } catch (Exception e) {\r
- throw new FMILException(e.getMessage());\r
- }\r
- }\r
- }\r
-\r
- private native double getTime_(int id);\r
-\r
- /**\r
- * Get all variables in a loaded model\r
- * @return all variables in a loaded model\r
- */\r
- public String[] getAllVariables() throws FMILException {\r
- synchronized(syncObject) {\r
-\r
- try {\r
-\r
- if(variableNames == null) {\r
- variableNames = getAllVariables_(getModelIDNew());\r
- }\r
- return variableNames;\r
-\r
- } catch (UnsatisfiedLinkError err) {\r
- throw new FMILException(UNSATISFIED_LINK);\r
- } catch (Exception e) {\r
- throw new FMILException(e.getMessage());\r
- }\r
- }\r
- }\r
-\r
- private native String[] getAllVariables_(int id);\r
-\r
- public String[] getAllVariableDescriptions() throws FMILException {\r
- synchronized(syncObject) {\r
-\r
- try {\r
-\r
- if(variableDescriptions == null) {\r
- variableDescriptions = getAllVariableDescriptions_(getModelIDNew());\r
- }\r
- return variableDescriptions;\r
-\r
- } catch (UnsatisfiedLinkError err) {\r
- throw new FMILException(UNSATISFIED_LINK);\r
- } catch (Exception e) {\r
- throw new FMILException(e.getMessage());\r
- }\r
- }\r
- }\r
-\r
- private native String[] getAllVariableDescriptions_(int id);\r
- \r
- public String[] getAllVariableDeclaredTypes() throws FMILException {\r
- synchronized(syncObject) {\r
-\r
- try {\r
-\r
- if(variableDeclaredTypes == null) {\r
- variableDeclaredTypes = getAllVariableDeclaredTypes_(getModelIDNew());\r
- }\r
- return variableDeclaredTypes;\r
-\r
- } catch (UnsatisfiedLinkError err) {\r
- throw new FMILException(UNSATISFIED_LINK);\r
- } catch (Exception e) {\r
- throw new FMILException(e.getMessage());\r
- }\r
- }\r
- }\r
-\r
- private native String[] getAllVariableDeclaredTypes_(int id);\r
-\r
- public int[] getAllVariableReferences() throws FMILException {\r
- synchronized(syncObject) {\r
-\r
- try {\r
-\r
- if(variableReferences == null) {\r
- variableReferences = getAllVariableReferences_(getModelIDNew(), new int[variableNames.length]); \r
- }\r
- return variableReferences;\r
-\r
- } catch (UnsatisfiedLinkError err) {\r
- throw new FMILException(UNSATISFIED_LINK);\r
- } catch (Exception e) {\r
- throw new FMILException(e.getMessage());\r
- }\r
- }\r
- }\r
-\r
- private native int[] getAllVariableReferences_(int id, int[] array);\r
-\r
- public int[] getAllVariableTypes() throws FMILException {\r
- synchronized(syncObject) {\r
-\r
- try {\r
-\r
- if(variableTypes == null) {\r
- variableTypes = getAllVariableTypes_(getModelIDNew(), new int[variableNames.length]); \r
- }\r
- return variableTypes;\r
-\r
- } catch (UnsatisfiedLinkError err) {\r
- throw new FMILException(UNSATISFIED_LINK);\r
- } catch (Exception e) {\r
- throw new FMILException(e.getMessage());\r
- }\r
- }\r
- }\r
-\r
- private native int[] getAllVariableTypes_(int id, int[] array);\r
-\r
- public int[] getAllVariableCausalities() throws FMILException {\r
- synchronized(syncObject) {\r
-\r
- try {\r
-\r
- if(variableCausalities == null) {\r
- variableCausalities = getAllVariableCausalities_(getModelIDNew(), new int[variableNames.length]); \r
- }\r
- return variableCausalities;\r
-\r
- } catch (UnsatisfiedLinkError err) {\r
- throw new FMILException(UNSATISFIED_LINK);\r
- } catch (Exception e) {\r
- throw new FMILException(e.getMessage());\r
- }\r
- }\r
- }\r
-\r
- private native int[] getAllVariableCausalities_(int id, int[] array);\r
-\r
- public int[] getAllVariableVariabilities() throws FMILException {\r
- synchronized(syncObject) {\r
-\r
- try {\r
-\r
- if(variableVariabilities == null) {\r
- variableVariabilities = getAllVariableVariabilities_(getModelIDNew(), new int[variableNames.length]); \r
- }\r
- return variableVariabilities;\r
-\r
- } catch (UnsatisfiedLinkError err) {\r
- throw new FMILException(UNSATISFIED_LINK);\r
- } catch (Exception e) {\r
- throw new FMILException(e.getMessage());\r
- }\r
- }\r
- }\r
-\r
- private native int[] getAllVariableVariabilities_(int id, int[] array);\r
-\r
- /**\r
- * Get all variables in a loaded model\r
- * @return all variables in a loaded model\r
- */\r
- public String[] getAllDeclaredTypes() throws FMILException {\r
- synchronized(syncObject) {\r
-\r
- try {\r
-\r
- if(declaredTypes == null) {\r
- declaredTypes = getAllDeclaredTypes_(getModelIDNew());\r
- }\r
- return declaredTypes;\r
-\r
- } catch (UnsatisfiedLinkError err) {\r
- throw new FMILException(UNSATISFIED_LINK);\r
- } catch (Exception e) {\r
- throw new FMILException(e.getMessage());\r
- }\r
- }\r
- }\r
-\r
- private native String[] getAllDeclaredTypes_(int id);\r
- \r
- public String[] getAllDeclaredTypeDescriptions() throws FMILException {\r
- synchronized(syncObject) {\r
-\r
- try {\r
-\r
- if(declaredTypeDescriptions == null) {\r
- declaredTypeDescriptions = getAllDeclaredTypeDescriptions_(getModelIDNew());\r
- }\r
- return declaredTypeDescriptions;\r
-\r
- } catch (UnsatisfiedLinkError err) {\r
- throw new FMILException(UNSATISFIED_LINK);\r
- } catch (Exception e) {\r
- throw new FMILException(e.getMessage());\r
- }\r
- }\r
- }\r
-\r
- private native String[] getAllDeclaredTypeDescriptions_(int id);\r
- \r
- public String[] getAllDeclaredTypeQuantities() throws FMILException {\r
- synchronized(syncObject) {\r
-\r
- try {\r
-\r
- if(declaredTypeQuantities == null) {\r
- declaredTypeQuantities = getAllDeclaredTypeQuantities_(getModelIDNew());\r
- }\r
- return declaredTypeQuantities;\r
-\r
- } catch (UnsatisfiedLinkError err) {\r
- throw new FMILException(UNSATISFIED_LINK);\r
- } catch (Exception e) {\r
- throw new FMILException(e.getMessage());\r
- }\r
- }\r
- }\r
-\r
- private native String[] getAllDeclaredTypeQuantities_(int id);\r
-\r
- public String[] getAllDeclaredTypeUnits() throws FMILException {\r
- synchronized(syncObject) {\r
-\r
- try {\r
-\r
- if(declaredTypeUnits == null) {\r
- declaredTypeUnits = getAllDeclaredTypeUnits_(getModelIDNew());\r
- }\r
- return declaredTypeUnits;\r
-\r
- } catch (UnsatisfiedLinkError err) {\r
- throw new FMILException(UNSATISFIED_LINK);\r
- } catch (Exception e) {\r
- throw new FMILException(e.getMessage());\r
- }\r
- }\r
- }\r
-\r
- private native String[] getAllDeclaredTypeUnits_(int id);\r
- \r
- \r
-//\r
-// /**\r
-// * Get all variables from model that match the filter (and time variable)\r
-// * \r
-// * @param regexp Regular expression filter\r
-// * @return An array of variable names that match regexp filter (and time-variable)\r
-// * @throws FMILException\r
-// */\r
-// public String[] filterVariables(String regexp) throws FMILException { \r
-// synchronized(syncObject) {\r
-// try {\r
-//\r
-// return filterVariables_(getModelID(), regexp + "|time");\r
-//\r
-// } catch (UnsatisfiedLinkError err) {\r
-// throw new FMILException(UNSATISFIED_LINK);\r
-// } catch (Exception e) {\r
-// throw new FMILException(e.getMessage());\r
-// }\r
-// }\r
-// }\r
-//\r
-// private native String[] filterVariables_(String id, String regexp);\r
-//\r
- /**\r
- * Get the last error message\r
- * @return Last error message\r
- */\r
- public String getLastErrorMessage() throws FMILException {\r
- synchronized(syncObject) {\r
-\r
- try {\r
-\r
- return "err";\r
- //return getLastErrorMessage_(getModelID());\r
-\r
- } catch (UnsatisfiedLinkError err) {\r
- throw new FMILException(UNSATISFIED_LINK);\r
- } catch (Exception e) {\r
- throw new FMILException(e.getMessage());\r
- }\r
- }\r
- }\r
-//\r
-// private native String getLastErrorMessage_(String id);\r
-\r
- /**\r
- * Get a real (double) value for variable\r
- * @param name Name of the variable\r
- * @return value\r
- * @throws FMILException\r
- */\r
- public double getRealValue(String name) throws FMILException {\r
- synchronized(syncObject) {\r
-\r
- try {\r
- // TODO: printtaa id ja name, jotta saadaan virheessä kiinni\r
- double result = getRealValue_(getModelIDNew(), variableMap.get(name));\r
- System.err.println("getRealValue " + name + " = " + result);\r
- return result;\r
- } catch (UnsatisfiedLinkError err) {\r
- throw new FMILException(UNSATISFIED_LINK);\r
- } catch (Exception e) {\r
- throw new FMILException(e.getMessage());\r
- }\r
- }\r
- }\r
-\r
- private native double getRealValue_(int id, int variableReference);\r
-\r
-// /**\r
-// * Get a string value for variable\r
-// * @param name Name of the variable\r
-// * @return value\r
-// * @throws FMILException\r
-// */\r
-// public String getStringValue(String name) throws FMILException {\r
-// synchronized(syncObject) {\r
-//\r
-// try {\r
-// return getStringValue_(getModelID(), name); \r
-// } catch (UnsatisfiedLinkError err) {\r
-// throw new FMILException(UNSATISFIED_LINK);\r
-// } catch (Exception e) {\r
-// throw new FMILException(e.getMessage());\r
-// }\r
-// }\r
-// }\r
-//\r
-// private native String getStringValue_(String id, String name);\r
-//\r
-// /**\r
-// * Get an integer value for variable\r
-// * @param name Name of the variable\r
-// * @return value\r
-// * @throws FMILException\r
-// */\r
-// public int getIntegerValue(String name) throws FMILException {\r
-// synchronized(syncObject) {\r
-//\r
-// try {\r
-// return getIntegerValue_(getModelID(), name); \r
-// } catch (UnsatisfiedLinkError err) {\r
-// throw new FMILException(UNSATISFIED_LINK);\r
-// } catch (Exception e) {\r
-// throw new FMILException(e.getMessage());\r
-// }\r
-// }\r
-// }\r
-//\r
-// private native int getIntegerValue_(String id, String name);\r
-//\r
-// /**\r
-// * Get a real (double) value for variable\r
-// * @param name Name of the variable\r
-// * @return value\r
-// * @throws FMILException\r
-// */\r
-// public boolean getBooleanValue(String name) throws FMILException {\r
-// synchronized(syncObject) {\r
-//\r
-// try {\r
-// return getBooleanValue_(getModelID(), name); \r
-// } catch (UnsatisfiedLinkError err) {\r
-// throw new FMILException(UNSATISFIED_LINK);\r
-// } catch (Exception e) {\r
-// throw new FMILException(e.getMessage());\r
-// }\r
-// }\r
-// }\r
-//\r
-// private native boolean getBooleanValue_(String id, String name);\r
-\r
- private FileChannel channel; \r
- private FileLock lock;\r
-\r
- @SuppressWarnings("resource")\r
- private boolean lockFMUDirectory() {\r
-\r
- try {\r
- // Get a file channel for the lock file\r
- File lockFile = new File(TEMP_FMU_DIRECTORY, LOCK_FILE_NAME);\r
- if(!lockFile.isFile())\r
- lockFile.createNewFile();\r
-\r
- channel = new RandomAccessFile(lockFile, "rw").getChannel();\r
-\r
- // Use the file channel to create a lock on the file.\r
- // This method blocks until it can retrieve the lock.\r
- lock = channel.lock();\r
-\r
- // // Try acquiring the lock without blocking. This method returns\r
- // // null or throws an exception if the file is already locked.\r
- // try {\r
- // lock = channel.tryLock();\r
- // } catch (OverlappingFileLockException e) {\r
- // // File is already locked in this thread or virtual machine\r
- // }\r
- } catch (IOException e) {\r
- return false;\r
- }\r
-\r
- return true;\r
- }\r
-\r
- private boolean unlockFMUDirectory() {\r
- try {\r
- // Release the lock\r
- if(lock != null)\r
- lock.release();\r
-\r
- // Close the file\r
- if(channel != null)\r
- channel.close();\r
- } catch (IOException e) {\r
- return false;\r
- }\r
- return true;\r
- }\r
-\r
- private boolean removeFMUDirectoryContents() {\r
- // Remove contents\r
- try {\r
- File tempDir = new File(TEMP_FMU_DIRECTORY);\r
- FileUtils.deleteAll(tempDir);\r
- tempDir.delete();\r
- } catch (IOException e) {\r
- return false;\r
- }\r
- return true;\r
- }\r
-}\r
+package org.simantics.fmil.core;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.net.URL;
+import java.nio.channels.FileChannel;
+import java.nio.channels.FileLock;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+
+import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Platform;
+import org.osgi.framework.Bundle;
+import org.simantics.fmil.core.ExecEnvironment.OSType;
+import org.simantics.utils.FileUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import gnu.trove.list.array.TIntArrayList;
+import gnu.trove.map.hash.TObjectIntHashMap;
+
+
+public class FMIL {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(FMIL.class);
+
+ private static final boolean DEBUG = false;
+
+ /**
+ * Static variables
+ */
+ private static int OK = 0;
+ private static int ERROR = 1;
+ private static int PENDING = 2;
+ private static String UNSATISFIED_LINK = "Method not found. DLL might not be loaded properly.";
+ public static final String TEMP_FMU_DIRECTORY_NAME = "fmil";
+ public static String TEMP_FMU_COMMON_DIRECTORY;
+ public static String LOCK_FILE_NAME = "fmil.lock";
+
+ public static Object syncObject = new Object();
+
+ /**
+ * Static: load native libraries required for the FMU simulation to work.
+ */
+ static {
+
+ File[] libraries = new File[2];
+
+ ExecEnvironment env = ExecEnvironment.calculate();
+
+ try {
+ URL sharedFMILIBUrl = null;
+ URL simulatorFMIUrl = null;
+ Bundle b = Platform.getBundle("org.simantics.fmil.core");
+
+ if (env.os == OSType.WINDOWS) {
+ sharedFMILIBUrl = FileLocator.find(b, new Path("libraries/fmilib_shared.dll"), null);
+ simulatorFMIUrl = FileLocator.find(b, new Path("libraries/FMUSimulator.dll"), null);
+ } else if(env.os == OSType.LINUX) {
+ sharedFMILIBUrl = FileLocator.find(b, new Path("libraries/libfmilib_shared.so"), null);
+ simulatorFMIUrl = FileLocator.find(b, new Path("libraries/libFMUSimulator.so"), null);
+ }
+
+ libraries[0] = new File(FileLocator.toFileURL(sharedFMILIBUrl).getPath());
+ libraries[1] = new File(FileLocator.toFileURL(simulatorFMIUrl).getPath());
+ } catch (Exception e) {
+ LOGGER.error("Failed to resolve native FMU simulation library for execution environment {}.{}", env.os, env.arch, e);
+ }
+
+ for(File library : libraries) {
+ if(library == null) {
+ System.err.println("FMU library not loaded. FMU simulation not working.");
+ continue;
+ } else if(!library.isFile()) {
+ System.err.println(library.getAbsolutePath() + " not found");
+ } else {
+ try {
+ System.load(library.getAbsolutePath());
+ } catch (Throwable t) {
+ System.err.println(t.getMessage());
+ }
+ }
+ }
+ }
+
+ /**
+ * Static: initialize fmu temp folder from current working directory
+ */
+ static {
+ TEMP_FMU_COMMON_DIRECTORY = Paths.get(".").toAbsolutePath().normalize().toString();
+ }
+
+ public static void setTempFMUCommonDir(File dir) {
+ TEMP_FMU_COMMON_DIRECTORY = dir.getAbsolutePath();
+ }
+
+ private String fmuDir;
+ private int id;
+
+ public String TEMP_FOLDER_1;
+ public String TEMP_FOLDER_2;
+ public String TEMP_FMU_DIRECTORY;
+ private String dirName;
+
+ private String[] variableNames;
+ private String[] variableDescriptions;
+ private String[] variableDeclaredTypes;
+ private int[] variableReferences;
+ private int[] variableTypes;
+ private int[] variableCausalities;
+ private int[] variableVariabilities;
+
+ private String[] declaredTypes;
+ private String[] declaredTypeDescriptions;
+ private String[] declaredTypeQuantities;
+ private String[] declaredTypeUnits;
+
+ private static int NO_VARIABLE_KEY = -1;
+
+ private TObjectIntHashMap<String> variableMap = new TObjectIntHashMap<String>(10, 0.5f, NO_VARIABLE_KEY);
+
+ private Set<String> subscriptionSet = new HashSet<String>();
+ private TIntArrayList subscription = new TIntArrayList();
+ private ArrayList<String> subscribedNames = new ArrayList<String>();
+
+ public List<String> getSubscribedNames() {
+ return subscribedNames;
+ }
+
+ public boolean subscribe(String name) throws FMILException {
+ synchronized(syncObject) {
+ // Safety check
+ int vr = variableMap.get(name);
+ if(vr == NO_VARIABLE_KEY) return false;
+ if(!subscriptionSet.add(name)) return false;
+ subscribedNames.add(name);
+ subscription.add(vr);
+ subscribe(new int[] { vr });
+ return true;
+ }
+ }
+
+ public FMIL() {
+ // Create a directory for this control
+ File tempDir = new File(TEMP_FMU_COMMON_DIRECTORY, UUID.randomUUID().toString());
+ tempDir.mkdir();
+ TEMP_FMU_DIRECTORY = tempDir.getAbsolutePath();
+
+ // Create two directories inside the temp directory for this control
+ dirName = UUID.randomUUID().toString();
+ File fmuDir = new File(TEMP_FMU_DIRECTORY, dirName);
+ fmuDir.mkdir();
+
+ TEMP_FOLDER_1 = fmuDir.toString();
+ TEMP_FOLDER_2 = fmuDir.toString() + "_2";
+
+ // Lock fmu directory in temp directory
+ lockFMUDirectory();
+ }
+
+ public int getModelIDNew() {
+ return id;
+ }
+
+ public String getModelID() {
+ return dirName;
+ }
+
+ public String getFmuDir() {
+ return fmuDir;
+ }
+
+ /**
+ * Load fmu from a given file path. Releases the (possible) previously
+ * loaded fmu.
+ *
+ * @param path absolute file path for fmu file
+ * @throws FMILException
+ */
+ private int fmuN = 0;
+ private boolean instantiated = false;
+ public void loadFMUFile(String path) throws FMILException {
+
+ if (!Files.exists(Paths.get(path)))
+ throw new FMILException("File " + path + " does not exist");
+ if (!Files.isRegularFile(Paths.get(path)))
+ throw new FMILException("Path " + path + " is not a file");
+
+ synchronized(syncObject) {
+
+ if(fmuN % 2 == 0) {
+ fmuDir = TEMP_FOLDER_1;
+ fmuN++;
+ } else {
+ fmuDir = TEMP_FOLDER_2;
+ fmuN = 0;
+ }
+
+ java.nio.file.Path tempDir = Paths.get(fmuDir);
+ if(Files.exists(tempDir) && Files.isDirectory(tempDir)) {
+ try {
+ FileUtils.emptyDirectory(tempDir);
+ } catch (IOException e) {
+ throw new FMILException("Could not delete existing files from temp folder for fmu " + path, e);
+ }
+ } else {
+ try {
+ Files.createDirectory(tempDir);
+ } catch (IOException e) {
+ throw new FMILException("Could not create temp folder for fmu " + path, e);
+ }
+ }
+
+ try {
+ String tmpPath = tempDir.toString();
+ if(!tmpPath.endsWith("\\") && !tmpPath.endsWith("/"))
+ tmpPath = tmpPath + "/";
+ id = loadFMUFile_(path, tmpPath);
+
+ getAllVariables();
+ getAllVariableReferences();
+
+ for(int i=0;i<variableNames.length;i++) {
+ variableMap.put(variableNames[i], variableReferences[i]);
+ }
+
+ instantiated = false;
+ } catch (UnsatisfiedLinkError err) {
+ throw new FMILException(UNSATISFIED_LINK, err);
+ } catch (Exception e) {
+ LOGGER.error(e.getMessage());
+ throw new FMILException(e.getMessage());
+ }
+ }
+ }
+
+ private native int loadFMUFile_(String path, String toDir) throws FMILException;
+
+ /**
+ * Set a step length for simulation
+ *
+ * @param step Step length for simulation
+ * @throws FMILException
+ */
+ public void setStepLength(double step) throws FMILException {
+ synchronized(syncObject) {
+
+ try {
+ int ret = setStepLength_(getModelIDNew(), step);
+ if(ret != OK)
+ LOGGER.warn("Function return value != OK, an exception should have been thrown from native code!");
+
+ } catch (UnsatisfiedLinkError err) {
+ throw new FMILException(UNSATISFIED_LINK);
+ } catch (Exception e) {
+ throw new FMILException(e.getMessage());
+ }
+ }
+ }
+
+ private native int setStepLength_(int id, double step);
+
+ /**
+ * Instantiates a simulation.
+ * <p>
+ * Make sure that an FMU is loaded first.
+ * @throws FMILException
+ */
+ public void instantiateSimulation() throws FMILException {
+ synchronized(syncObject) {
+
+ try {
+
+ int ret = instantiateSimulation_(getModelIDNew());
+ if(ret != OK) {
+ LOGGER.warn("Function return value != OK, an exception should have been thrown from native code!");
+ } else {
+ instantiated = true;
+ }
+ } catch (FMILException e) {
+ throw e;
+ } catch (UnsatisfiedLinkError err) {
+ throw new FMILException(UNSATISFIED_LINK);
+ } catch (Exception e) {
+ throw new FMILException(e.getMessage());
+ }
+ }
+ }
+
+ private native int instantiateSimulation_(int id) throws FMILException;
+
+
+ /**
+ * Initializes a simulation.
+ * <p>
+ * Make sure that simulation is instantiated first!
+ * @throws FMILException
+ */
+ public void initializeSimulation() throws FMILException {
+ synchronized(syncObject) {
+
+ try {
+
+ int ret = initializeSimulation_(getModelIDNew());
+ if(ret != OK)
+ LOGGER.warn("Function return value != OK, an exception should have been thrown from native code!");
+
+ } catch (FMILException e) {
+ throw e;
+ } catch (UnsatisfiedLinkError err) {
+ throw new FMILException(UNSATISFIED_LINK);
+ } catch (Exception e) {
+ throw new FMILException(e.getMessage());
+ }
+ }
+ }
+
+ private native int initializeSimulation_(int id) throws FMILException;
+
+ /**
+ * Subscribe a set of variables from a loaded simulation.
+ * <p>
+ * Make sure that an FMU is loaded first.
+ * @param variables Array of variables
+ * @throws FMILException
+ */
+ public void subscribe(int[] variables) throws FMILException {
+ synchronized(syncObject) {
+
+ try {
+
+ int ret = subscribe_(getModelIDNew(), variables);
+ if(ret != OK)
+ LOGGER.warn("Function return value != OK, an exception should have been thrown from native code!");
+
+ } catch (UnsatisfiedLinkError err) {
+ throw new FMILException(UNSATISFIED_LINK);
+ } catch (Exception e) {
+ throw new FMILException(e.getMessage());
+ }
+ }
+ }
+
+ private native int subscribe_(int id, int[] variables);
+
+ /**
+ * Set value of a real variable. If the variable is a
+ * parameter, the change is effective immediately.
+ *
+ * @param name Variable name
+ * @param value New value
+ * @throws FMILException
+ */
+ public void setRealValue(String name, double value) throws FMILException {
+ int key = variableMap.get(name);
+ if(key == NO_VARIABLE_KEY)
+ throw new FMILException("No variable with name " + name);
+ setRealValue(key, value);
+ }
+
+ /**
+ * Set value of a real variable. If the variable is a
+ * parameter, the change is effective immediately.
+ *
+ * @param name Variable id
+ * @param value New value
+ * @throws FMILException
+ */
+ public void setRealValue(int variableReference, double value) throws FMILException {
+ synchronized(syncObject) {
+ try {
+ int ret = setRealValue_(getModelIDNew(), variableReference, value);
+ if(ret != OK)
+ LOGGER.warn("Function return value != OK, an exception should have been thrown from native code!");
+ } catch (FMILException e) {
+ throw e;
+ } catch (UnsatisfiedLinkError err) {
+ throw new FMILException(UNSATISFIED_LINK);
+ } catch (Exception e) {
+ throw new FMILException(e.getMessage());
+ }
+ }
+ }
+
+ private native int setRealValue_(int id, int variableReference, double value) throws FMILException;
+
+
+ /**
+ * Set value of an integer variable. If the variable is a
+ * parameter, the change is effective immediately.
+ *
+ * @param name Variable name
+ * @param value New value
+ * @throws FMILException
+ */
+ public void setIntegerValue(String name, int value) throws FMILException {
+ int key = variableMap.get(name);
+ if(key == NO_VARIABLE_KEY)
+ throw new FMILException("No variable with name " + name);
+ setIntegerValue(key, value);
+ }
+
+ /**
+ * Set value of an integer variable. If the variable is a
+ * parameter, the change is effective immediately.
+ *
+ * @param name Variable id
+ * @param value New value
+ * @throws FMILException
+ */
+ public void setIntegerValue(int variableReference, int value) throws FMILException {
+ synchronized(syncObject) {
+ try {
+ int ret = setIntegerValue_(getModelIDNew(), variableReference, value);
+ if(ret != OK)
+ LOGGER.warn("Function return value != OK, an exception should have been thrown from native code!");
+ } catch (FMILException e) {
+ throw e;
+ } catch (UnsatisfiedLinkError err) {
+ throw new FMILException(UNSATISFIED_LINK);
+ } catch (Exception e) {
+ throw new FMILException(e.getMessage());
+ }
+ }
+ }
+
+ private native int setIntegerValue_(int id, int variableReference, int value) throws FMILException;
+
+
+ /**
+ * Set value of a boolean variable. If the variable is a
+ * parameter, the change is effective immediately.
+ *
+ * @param name Variable name
+ * @param value New value
+ * @throws FMILException
+ */
+ public void setBooleanValue(String name, boolean value) throws FMILException {
+ int key = variableMap.get(name);
+ if(key == NO_VARIABLE_KEY)
+ throw new FMILException("No variable with name " + name);
+ setBooleanValue(key, value);
+ }
+
+ /**
+ * Set value of a boolean variable. If the variable is a
+ * parameter, the change is effective immediately.
+ *
+ * @param name Variable id
+ * @param value New value
+ * @throws FMILException
+ */
+ public void setBooleanValue(int variableReference, boolean value) throws FMILException {
+ synchronized(syncObject) {
+ try {
+ int ret = setBooleanValue_(getModelIDNew(), variableReference, value);
+ if(ret != OK)
+ LOGGER.warn("Function return value != OK, an exception should have been thrown from native code!");
+ } catch (FMILException e) {
+ throw e;
+ } catch (UnsatisfiedLinkError err) {
+ throw new FMILException(UNSATISFIED_LINK);
+ } catch (Exception e) {
+ throw new FMILException(e.getMessage());
+ }
+ }
+ }
+
+ private native int setBooleanValue_(int id, int variableReference, boolean value) throws FMILException;
+
+
+ /**
+ * Set value of a string variable. If the variable is a
+ * parameter, the change is effective immediately.
+ *
+ * @param name Variable name
+ * @param value New value
+ * @throws FMILException
+ */
+ public void setStringValue(String name, String value) throws FMILException {
+ int key = variableMap.get(name);
+ if(key == NO_VARIABLE_KEY)
+ throw new FMILException("No variable with name " + name);
+ setStringValue(key, value);
+ }
+
+ /**
+ * Set value of a string variable. If the variable is a
+ * parameter, the change is effective immediately.
+ *
+ * @param name Variable id
+ * @param value New value
+ * @throws FMILException
+ */
+ public void setStringValue(int variableReference, String value) throws FMILException {
+ synchronized(syncObject) {
+ try {
+ int ret = setStringValue_(getModelIDNew(), variableReference, value);
+ if(ret != OK)
+ LOGGER.warn("Function return value != OK, an exception should have been thrown from native code!");
+ } catch (FMILException e) {
+ throw e;
+ } catch (UnsatisfiedLinkError err) {
+ throw new FMILException(UNSATISFIED_LINK);
+ } catch (Exception e) {
+ throw new FMILException(e.getMessage());
+ }
+ }
+ }
+
+ private native int setStringValue_(int id, int variableReference, String value) throws FMILException;
+
+
+ /**
+ * Simulate one step forward. The step length can be set with
+ * setStepLength()
+ *
+ * @throws FMILException
+ */
+ public void simulateStep() throws FMILException {
+ synchronized(syncObject) {
+
+ try {
+
+ int ret = simulateStep_(getModelIDNew()); //0 is ok, 1 is error, 2 is pending
+ if(ret == PENDING)
+ LOGGER.warn("Pending status return from FMU. This is not implemented in our Simulator yet!");
+ else if(ret != OK)
+ LOGGER.warn("Function return value != OK, an exception should have been thrown from native code!");
+ } catch (FMILException e) {
+ throw e;
+ } catch (UnsatisfiedLinkError err) {
+ throw new FMILException(UNSATISFIED_LINK);
+ } catch (Exception e) {
+ throw new FMILException(e.getMessage());
+ }
+ }
+ }
+ private native int simulateStep_(int id) throws FMILException;
+
+ /**
+ * Get an array containing the current values for subscribed variables. The
+ * values are in the same order as in the subscription.
+ *
+ * @param results An array the size of subscribed results
+ * @return
+ */
+ public double[] getSubscribedResults() throws FMILException {
+ synchronized(syncObject) {
+
+ try {
+
+ double[] results = new double[subscription.size()];
+ Arrays.fill(results, Double.NaN);
+
+ return getSubscribedResults_(getModelIDNew(), results);
+ } catch (UnsatisfiedLinkError err) {
+ throw new FMILException(UNSATISFIED_LINK);
+ } catch (Exception e) {
+ throw new FMILException(e.getMessage());
+ }
+ }
+ }
+
+ private native double[] getSubscribedResults_(int id, double[] results);
+
+
+ /**
+ * Unload FMU and the dll:s that it requires.
+ * <p>
+ * To be called after all FMU simulations are ended.
+ * If the fmu is loaded again / changed, call to loadFMUFile is sufficient. loadFMUFile
+ * releases the previous fmu.dll
+ *
+ * @throws FMILException
+ */
+ public void unloadFMU() throws FMILException {
+ synchronized(syncObject) {
+
+ try {
+
+ unlockFMUDirectory();
+
+ // Many FMUs will not correctly clean-up during unload if the simulation isn't instantiated
+ // Essentially we'd likely be passing an invalid pointer to the FMU to cleanup, causing native c-level crashes.
+
+ if(instantiated) {
+ int ret = unloadFMU_(getModelIDNew());
+ if(ret != OK)
+ LOGGER.warn("Function return value != OK, an exception should have been thrown from native code!");
+ instantiated = false;
+ }
+ removeFMUDirectoryContents();
+
+ } catch (FMILException e) {
+ throw e;
+ } catch (UnsatisfiedLinkError err) {
+ throw new FMILException(UNSATISFIED_LINK);
+ } catch (Exception e) {
+ throw new FMILException(e.getMessage());
+ }
+ }
+ }
+ private native int unloadFMU_(int id) throws FMILException;
+
+// /**
+// * Checks if fmu has been initialized
+// * @return current simulation time
+// */
+// public boolean isInitialized() throws FMILException {
+// synchronized(syncObject) {
+// try {
+// return isInitialized_(getModelID());
+// } catch (UnsatisfiedLinkError err) {
+// throw new FMILException(UNSATISFIED_LINK);
+// } catch (Exception e) {
+// throw new FMILException(e.getMessage());
+// }
+// }
+// }
+//
+// private native boolean isInitialized_(String id);
+//
+ /**
+ * Get the current simulation time
+ * @return current simulation time
+ */
+ public double getTime() throws FMILException {
+ synchronized(syncObject) {
+
+ try {
+
+ return getTime_(getModelIDNew());
+
+ } catch (UnsatisfiedLinkError err) {
+ throw new FMILException(UNSATISFIED_LINK);
+ } catch (Exception e) {
+ throw new FMILException(e.getMessage());
+ }
+ }
+ }
+
+ private native double getTime_(int id);
+
+ /**
+ * Get all variables in a loaded model
+ * @return all variables in a loaded model
+ */
+ public String[] getAllVariables() throws FMILException {
+ synchronized(syncObject) {
+
+ try {
+
+ if(variableNames == null) {
+ variableNames = getAllVariables_(getModelIDNew());
+ }
+ return variableNames;
+
+ } catch (UnsatisfiedLinkError err) {
+ throw new FMILException(UNSATISFIED_LINK);
+ } catch (Exception e) {
+ throw new FMILException(e.getMessage());
+ }
+ }
+ }
+
+ private native String[] getAllVariables_(int id);
+
+ public String[] getAllVariableDescriptions() throws FMILException {
+ synchronized(syncObject) {
+
+ try {
+
+ if(variableDescriptions == null) {
+ variableDescriptions = getAllVariableDescriptions_(getModelIDNew());
+ }
+ return variableDescriptions;
+
+ } catch (UnsatisfiedLinkError err) {
+ throw new FMILException(UNSATISFIED_LINK);
+ } catch (Exception e) {
+ throw new FMILException(e.getMessage());
+ }
+ }
+ }
+
+ private native String[] getAllVariableDescriptions_(int id);
+
+ public String[] getAllVariableDeclaredTypes() throws FMILException {
+ synchronized(syncObject) {
+
+ try {
+
+ if(variableDeclaredTypes == null) {
+ variableDeclaredTypes = getAllVariableDeclaredTypes_(getModelIDNew());
+ }
+ return variableDeclaredTypes;
+
+ } catch (UnsatisfiedLinkError err) {
+ throw new FMILException(UNSATISFIED_LINK);
+ } catch (Exception e) {
+ throw new FMILException(e.getMessage());
+ }
+ }
+ }
+
+ private native String[] getAllVariableDeclaredTypes_(int id);
+
+ public int[] getAllVariableReferences() throws FMILException {
+ synchronized(syncObject) {
+
+ try {
+
+ if(variableReferences == null) {
+ variableReferences = getAllVariableReferences_(getModelIDNew(), new int[variableNames.length]);
+ }
+ return variableReferences;
+
+ } catch (UnsatisfiedLinkError err) {
+ throw new FMILException(UNSATISFIED_LINK);
+ } catch (Exception e) {
+ throw new FMILException(e.getMessage());
+ }
+ }
+ }
+
+ private native int[] getAllVariableReferences_(int id, int[] array);
+
+ public int[] getAllVariableTypes() throws FMILException {
+ synchronized(syncObject) {
+
+ try {
+
+ if(variableTypes == null) {
+ variableTypes = getAllVariableTypes_(getModelIDNew(), new int[variableNames.length]);
+ }
+ return variableTypes;
+
+ } catch (UnsatisfiedLinkError err) {
+ throw new FMILException(UNSATISFIED_LINK);
+ } catch (Exception e) {
+ throw new FMILException(e.getMessage());
+ }
+ }
+ }
+
+ private native int[] getAllVariableTypes_(int id, int[] array);
+
+ public int[] getAllVariableCausalities() throws FMILException {
+ synchronized(syncObject) {
+
+ try {
+
+ if(variableCausalities == null) {
+ variableCausalities = getAllVariableCausalities_(getModelIDNew(), new int[variableNames.length]);
+ }
+ return variableCausalities;
+
+ } catch (UnsatisfiedLinkError err) {
+ throw new FMILException(UNSATISFIED_LINK);
+ } catch (Exception e) {
+ throw new FMILException(e.getMessage());
+ }
+ }
+ }
+
+ private native int[] getAllVariableCausalities_(int id, int[] array);
+
+ public int[] getAllVariableVariabilities() throws FMILException {
+ synchronized(syncObject) {
+
+ try {
+
+ if(variableVariabilities == null) {
+ variableVariabilities = getAllVariableVariabilities_(getModelIDNew(), new int[variableNames.length]);
+ }
+ return variableVariabilities;
+
+ } catch (UnsatisfiedLinkError err) {
+ throw new FMILException(UNSATISFIED_LINK);
+ } catch (Exception e) {
+ throw new FMILException(e.getMessage());
+ }
+ }
+ }
+
+ private native int[] getAllVariableVariabilities_(int id, int[] array);
+
+ /**
+ * Get all variables in a loaded model
+ * @return all variables in a loaded model
+ */
+ public String[] getAllDeclaredTypes() throws FMILException {
+ synchronized(syncObject) {
+
+ try {
+
+ if(declaredTypes == null) {
+ declaredTypes = getAllDeclaredTypes_(getModelIDNew());
+ }
+ return declaredTypes;
+
+ } catch (UnsatisfiedLinkError err) {
+ throw new FMILException(UNSATISFIED_LINK);
+ } catch (Exception e) {
+ throw new FMILException(e.getMessage());
+ }
+ }
+ }
+
+ private native String[] getAllDeclaredTypes_(int id);
+
+ public String[] getAllDeclaredTypeDescriptions() throws FMILException {
+ synchronized(syncObject) {
+
+ try {
+
+ if(declaredTypeDescriptions == null) {
+ declaredTypeDescriptions = getAllDeclaredTypeDescriptions_(getModelIDNew());
+ }
+ return declaredTypeDescriptions;
+
+ } catch (UnsatisfiedLinkError err) {
+ throw new FMILException(UNSATISFIED_LINK);
+ } catch (Exception e) {
+ throw new FMILException(e.getMessage());
+ }
+ }
+ }
+
+ private native String[] getAllDeclaredTypeDescriptions_(int id);
+
+ public String[] getAllDeclaredTypeQuantities() throws FMILException {
+ synchronized(syncObject) {
+
+ try {
+
+ if(declaredTypeQuantities == null) {
+ declaredTypeQuantities = getAllDeclaredTypeQuantities_(getModelIDNew());
+ }
+ return declaredTypeQuantities;
+
+ } catch (UnsatisfiedLinkError err) {
+ throw new FMILException(UNSATISFIED_LINK);
+ } catch (Exception e) {
+ throw new FMILException(e.getMessage());
+ }
+ }
+ }
+
+ private native String[] getAllDeclaredTypeQuantities_(int id);
+
+ public String[] getAllDeclaredTypeUnits() throws FMILException {
+ synchronized(syncObject) {
+
+ try {
+
+ if(declaredTypeUnits == null) {
+ declaredTypeUnits = getAllDeclaredTypeUnits_(getModelIDNew());
+ }
+ return declaredTypeUnits;
+
+ } catch (UnsatisfiedLinkError err) {
+ throw new FMILException(UNSATISFIED_LINK);
+ } catch (Exception e) {
+ throw new FMILException(e.getMessage());
+ }
+ }
+ }
+
+ private native String[] getAllDeclaredTypeUnits_(int id);
+
+
+//
+// /**
+// * Get all variables from model that match the filter (and time variable)
+// *
+// * @param regexp Regular expression filter
+// * @return An array of variable names that match regexp filter (and time-variable)
+// * @throws FMILException
+// */
+// public String[] filterVariables(String regexp) throws FMILException {
+// synchronized(syncObject) {
+// try {
+//
+// return filterVariables_(getModelID(), regexp + "|time");
+//
+// } catch (UnsatisfiedLinkError err) {
+// throw new FMILException(UNSATISFIED_LINK);
+// } catch (Exception e) {
+// throw new FMILException(e.getMessage());
+// }
+// }
+// }
+//
+// private native String[] filterVariables_(String id, String regexp);
+////
+// /**
+// * Get the last error message
+// * @return Last error message
+// */
+// public String getLastErrorMessage() throws FMILException {
+// synchronized(syncObject) {
+//
+// try {
+//
+// return "err";
+// //return getLastErrorMessage_(getModelID());
+//
+// } catch (UnsatisfiedLinkError err) {
+// throw new FMILException(UNSATISFIED_LINK);
+// } catch (Exception e) {
+// throw new FMILException(e.getMessage());
+// }
+// }
+// }
+//
+// private native String getLastErrorMessage_(String id);
+
+ /**
+ * Get a value (double) for real variable
+ * @param name Name of the variable
+ * @return value
+ * @throws FMILException
+ */
+ public double getRealValue(String name) throws FMILException {
+ int key = variableMap.get(name);
+ if(key == NO_VARIABLE_KEY)
+ throw new FMILException("No variable with name " + name);
+ double result = getRealValue(key);
+ if (DEBUG) System.err.println("getRealValue " + name + " = " + result);
+ return result;
+ }
+
+ /**
+ * Get a value (double) for real variable
+ * @param variableReference Numeric id of the variable
+ * @return value
+ * @throws FMILException
+ */
+ public double getRealValue(int variableReference) throws FMILException {
+ synchronized(syncObject) {
+ try {
+ return getRealValue_(getModelIDNew(), variableReference);
+ } catch (UnsatisfiedLinkError err) {
+ throw new FMILException(UNSATISFIED_LINK);
+ } catch (Exception e) {
+ throw new FMILException(e.getMessage());
+ }
+ }
+ }
+
+ private native double getRealValue_(int id, int variableReference) throws FMILException;
+
+
+ /**
+ * Get value of integer variable
+ * @param name Name of the variable
+ * @return value
+ * @throws FMILException
+ */
+ public int getIntegerValue(String name) throws FMILException {
+ int key = variableMap.get(name);
+ if(key == NO_VARIABLE_KEY)
+ throw new FMILException("No variable with name " + name);
+ int result = getIntegerValue(key);
+ if (DEBUG) System.err.println("getIntegerValue " + name + " = " + result);
+ return result;
+ }
+
+ /**
+ * Get a real (double) value for variable
+ * @param variableReference Numeric id of the variable
+ * @return value
+ * @throws FMILException
+ */
+ public int getIntegerValue(int variableReference) throws FMILException {
+ synchronized(syncObject) {
+ try {
+ return getIntegerValue_(getModelIDNew(), variableReference);
+ } catch (UnsatisfiedLinkError err) {
+ throw new FMILException(UNSATISFIED_LINK);
+ } catch (Exception e) {
+ throw new FMILException(e.getMessage());
+ }
+ }
+ }
+
+ private native int getIntegerValue_(int id, int variableReference) throws FMILException;
+
+
+ /**
+ * Get value of boolean variable
+ * @param name Name of the variable
+ * @return value
+ * @throws FMILException
+ */
+ public boolean getBooleanValue(String name) throws FMILException {
+ int key = variableMap.get(name);
+ if(key == NO_VARIABLE_KEY)
+ throw new FMILException("No variable with name " + name);
+ boolean result = getBooleanValue(key);
+ if (DEBUG) System.err.println("getBooleanValue " + name + " = " + result);
+ return result;
+ }
+
+ /**
+ * Get value of boolean variable
+ * @param variableReference Numeric id of the variable
+ * @return value
+ * @throws FMILException
+ */
+ public boolean getBooleanValue(int variableReference) throws FMILException {
+ synchronized(syncObject) {
+ try {
+ return getBooleanValue_(getModelIDNew(), variableReference);
+ } catch (UnsatisfiedLinkError err) {
+ throw new FMILException(UNSATISFIED_LINK);
+ } catch (Exception e) {
+ throw new FMILException(e.getMessage());
+ }
+ }
+ }
+
+ private native boolean getBooleanValue_(int id, int variableReference) throws FMILException;
+
+
+ /**
+ * Get value of string variable
+ * @param name Name of the variable
+ * @return value
+ * @throws FMILException
+ */
+ public String getStringValue(String name) throws FMILException {
+ int key = variableMap.get(name);
+ if(key == NO_VARIABLE_KEY)
+ throw new FMILException("No variable with name " + name);
+ String result = getStringValue(key);
+ if (DEBUG) System.err.println("getIntegerValue " + name + " = " + result);
+ return result;
+ }
+
+ /**
+ * Get value of string variable
+ * @param variableReference Numeric id of the variable
+ * @return value
+ * @throws FMILException
+ */
+ public String getStringValue(int variableReference) throws FMILException {
+ synchronized(syncObject) {
+ try {
+ return getStringValue_(getModelIDNew(), variableReference);
+ } catch (UnsatisfiedLinkError err) {
+ throw new FMILException(UNSATISFIED_LINK);
+ } catch (Exception e) {
+ throw new FMILException(e.getMessage());
+ }
+ }
+ }
+
+ private native String getStringValue_(int id, int variableReference) throws FMILException;
+
+
+ private FileChannel channel;
+ private FileLock lock;
+
+ @SuppressWarnings("resource")
+ private boolean lockFMUDirectory() {
+
+ try {
+ // Get a file channel for the lock file
+ File lockFile = new File(TEMP_FMU_DIRECTORY, LOCK_FILE_NAME);
+ if(!lockFile.isFile())
+ lockFile.createNewFile();
+
+ channel = new RandomAccessFile(lockFile, "rw").getChannel();
+
+ // Use the file channel to create a lock on the file.
+ // This method blocks until it can retrieve the lock.
+ lock = channel.lock();
+
+ // // Try acquiring the lock without blocking. This method returns
+ // // null or throws an exception if the file is already locked.
+ // try {
+ // lock = channel.tryLock();
+ // } catch (OverlappingFileLockException e) {
+ // // File is already locked in this thread or virtual machine
+ // }
+ } catch (IOException e) {
+ return false;
+ }
+
+ return true;
+ }
+
+ private boolean unlockFMUDirectory() {
+ try {
+ // Release the lock
+ if(lock != null)
+ lock.release();
+
+ // Close the file
+ if(channel != null)
+ channel.close();
+ } catch (IOException e) {
+ return false;
+ }
+ return true;
+ }
+
+ private boolean removeFMUDirectoryContents() {
+ // Remove contents
+ try {
+ File tempDir = new File(TEMP_FMU_DIRECTORY);
+ FileUtils.deleteAll(tempDir);
+ tempDir.delete();
+ } catch (IOException e) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ unloadFMU();
+ } catch (Throwable t) {
+ LOGGER.error("Could not unload native FMU!", t);
+ } finally {
+ super.finalize();
+ }
+ }
+}