package org.simantics.fmil.core; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; import java.util.ArrayList; 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.Platform; import org.osgi.framework.Bundle; import org.simantics.Simantics; import org.simantics.fmil.core.ExecEnvironment.ARCHType; import org.simantics.fmil.core.ExecEnvironment.OSType; import org.simantics.utils.FileUtils; import gnu.trove.list.array.TIntArrayList; import gnu.trove.map.hash.TObjectIntHashMap; public class FMIL { /** * Static variables */ private static int ERROR = 0; private static int OK = 1; private static String UNSATISFIED_LINK = "Method not found. DLL might not be loaded properly."; private static 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]; Bundle bundle = null; ExecEnvironment env = ExecEnvironment.calculate(); if (env.os == OSType.WINDOWS) { if (env.arch == ARCHType.X86) { bundle = Platform.getBundle("org.simantics.fmil.win32"); } else if (env.arch == ARCHType.X86_64) { bundle = Platform.getBundle("org.simantics.fmil.win64"); } } if (bundle != null) { try{ String root = FileLocator.getBundleFile(bundle).getAbsolutePath(); // if (env.arch == ARCHType.X86_64) { // File newFIle = new File(root, "libraries/libexpat.dll"); // System.load(newFIle.getAbsolutePath()); // } // libraries[0] = new File(root, "libraries/zlibwapi.dll"); // libraries[1] = new File(root, "libraries/miniunz.dll"); libraries[0] = new File(root, "libraries/fmilib_shared.dll"); libraries[1] = new File(root, "libraries/FMUSimulator.dll"); } catch (Exception e) { e.printStackTrace(); } } 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 */ static { File dir = Simantics.getTemporaryDirectory(TEMP_FMU_DIRECTORY_NAME); 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 TObjectIntHashMap variableMap = new TObjectIntHashMap(); private Set subscriptionSet = new HashSet(); private TIntArrayList subscription = new TIntArrayList(); private ArrayList subscribedNames = new ArrayList(); public List getSubscribedNames() { return subscribedNames; } public boolean subscribe(String name) throws FMILException { // Safety check int vr = variableMap.get(name); if(vr == 0) return false; if(!subscriptionSet.add(name)) return false; subscribedNames.add(name); System.err.println("subscribed : " + name + " => " + subscribedNames.size()); 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 fmuLoaded = false; public void loadFMUFile(String path) throws FMILException { synchronized(syncObject) { if(fmuN % 2 == 0) { fmuDir = TEMP_FOLDER_1; fmuN++; } else { fmuDir = TEMP_FOLDER_2; fmuN = 0; } File tempDir = new File(fmuDir); if(tempDir.isDirectory()) { try { FileUtils.deleteAll(tempDir); } catch (IOException e) { throw new FMILException("Could not create temp folder for fmu"); } tempDir.mkdir(); } else { tempDir.mkdir(); } try { String tmpPath = tempDir.getAbsolutePath(); if(!tmpPath.endsWith("\\")) tmpPath = tmpPath + "\\"; id = loadFMUFile_(path, tmpPath); getAllVariables(); getAllVariableReferences(); for(int i=0;i * Make sure that an FMU is loaded first. * @throws FMILException */ public void instantiateSimulation() throws FMILException { synchronized(syncObject) { try { int ret = instantiateSimulation_(getModelIDNew()); if(ret == ERROR) throw new FMILException(getLastErrorMessage()); } catch (UnsatisfiedLinkError err) { throw new FMILException(UNSATISFIED_LINK); } catch (Exception e) { throw new FMILException(e.getMessage()); } } } private native int instantiateSimulation_(int id); /** * Initializes a simulation. *

* Make sure that simulation is instantiated first! * @throws FMILException */ public void initializeSimulation() throws FMILException { synchronized(syncObject) { try { int ret = initializeSimulation_(getModelIDNew()); if(ret == ERROR) throw new FMILException(getLastErrorMessage()); } catch (UnsatisfiedLinkError err) { throw new FMILException(UNSATISFIED_LINK); } catch (Exception e) { throw new FMILException(e.getMessage()); } } } private native int initializeSimulation_(int id); /** * Subscribe a set of variables from a loaded simulation. *

* 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 == ERROR) throw new FMILException(getLastErrorMessage()); } 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 a new (Real, double) value for a variable. If the variable is a * parameter, the change is effective immediately. * * @param name Variable * @param value New (Real, double) value * @throws FMILException */ public void setRealValue(String name, double value) throws FMILException { synchronized(syncObject) { try { int ret = setRealValue_(getModelIDNew(), variableMap.get(name), value); if(ret == ERROR) throw new FMILException(getLastErrorMessage()); } catch (UnsatisfiedLinkError err) { throw new FMILException(UNSATISFIED_LINK); } catch (Exception e) { throw new FMILException(e.getMessage()); } } } public void setRealValue(int variableReference, double value) throws FMILException { synchronized(syncObject) { try { int ret = setRealValue_(getModelIDNew(), variableReference, value); if(ret == ERROR) throw new FMILException(getLastErrorMessage()); } 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); // /** // * Set a new (integer) value for a variable. If the variable is a // * parameter, the change is effective immediately. // * // * @param name Variable // * @param value New (integer) value // * @throws FMILException // */ // public void setIntegerValue(String name, int value) throws FMILException { // synchronized(syncObject) { // // try { // // int ret = setIntegerValue_(getModelID(), name, value); // if(ret == ERROR) // throw new FMILException(getLastErrorMessage()); // // } catch (UnsatisfiedLinkError err) { // throw new FMILException(UNSATISFIED_LINK); // } catch (Exception e) { // throw new FMILException(e.getMessage()); // } // } // } // private native int setIntegerValue_(String id, String name, int value); // // /** // * Set a new (boolean) value for a variable. If the variable is a // * parameter, the change is effective immediately. // * // * @param name Variable // * @param value New (boolean) value // * @throws FMILException // */ // public void setBooleanValue(String name, boolean value) throws FMILException { // synchronized(syncObject) { // // try { // // int ret = setBooleanValue_(getModelID(), name, value); // if(ret == ERROR) // throw new FMILException(getLastErrorMessage()); // // } catch (UnsatisfiedLinkError err) { // throw new FMILException(UNSATISFIED_LINK); // } catch (Exception e) { // throw new FMILException(e.getMessage()); // } // } // } // private native int setBooleanValue_(String id, String name, boolean value); // // public void setTime(double time) throws FMILException { // synchronized(syncObject) { // // try { // // int ret = setTime_(getModelID(), time); // if(ret == ERROR) // throw new FMILException(getLastErrorMessage()); // // } catch (UnsatisfiedLinkError err) { // throw new FMILException(UNSATISFIED_LINK); // } catch (Exception e) { // throw new FMILException(e.getMessage()); // } // } // } // private native int setTime_(String id, double time); /** * 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()); if(ret == ERROR) throw new FMILException(getLastErrorMessage()); } catch (UnsatisfiedLinkError err) { throw new FMILException(UNSATISFIED_LINK); } catch (Exception e) { throw new FMILException(e.getMessage()); } } } private native int simulateStep_(int id); /** * 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()]; 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. *

* 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(); if(fmuLoaded) { int ret = unloadFMU_(getModelIDNew()); if(ret == ERROR) throw new FMILException(getLastErrorMessage()); } removeFMUDirectoryContents(); } catch (UnsatisfiedLinkError err) { throw new FMILException(UNSATISFIED_LINK); } catch (Exception e) { throw new FMILException(e.getMessage()); } } } private native int unloadFMU_(int id); // /** // * 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 real (double) value for variable * @param name Name of the variable * @return value * @throws FMILException */ public double getRealValue(String name) throws FMILException { synchronized(syncObject) { try { // TODO: printtaa id ja name, jotta saadaan virheessä kiinni double result = getRealValue_(getModelIDNew(), variableMap.get(name)); System.err.println("getRealValue " + name + " = " + result); return result; } catch (UnsatisfiedLinkError err) { throw new FMILException(UNSATISFIED_LINK); } catch (Exception e) { throw new FMILException(e.getMessage()); } } } private native double getRealValue_(int id, int variableReference); // /** // * Get a string value for variable // * @param name Name of the variable // * @return value // * @throws FMILException // */ // public String getStringValue(String name) throws FMILException { // synchronized(syncObject) { // // try { // return getStringValue_(getModelID(), name); // } catch (UnsatisfiedLinkError err) { // throw new FMILException(UNSATISFIED_LINK); // } catch (Exception e) { // throw new FMILException(e.getMessage()); // } // } // } // // private native String getStringValue_(String id, String name); // // /** // * Get an integer value for variable // * @param name Name of the variable // * @return value // * @throws FMILException // */ // public int getIntegerValue(String name) throws FMILException { // synchronized(syncObject) { // // try { // return getIntegerValue_(getModelID(), name); // } catch (UnsatisfiedLinkError err) { // throw new FMILException(UNSATISFIED_LINK); // } catch (Exception e) { // throw new FMILException(e.getMessage()); // } // } // } // // private native int getIntegerValue_(String id, String name); // // /** // * Get a real (double) value for variable // * @param name Name of the variable // * @return value // * @throws FMILException // */ // public boolean getBooleanValue(String name) throws FMILException { // synchronized(syncObject) { // // try { // return getBooleanValue_(getModelID(), name); // } catch (UnsatisfiedLinkError err) { // throw new FMILException(UNSATISFIED_LINK); // } catch (Exception e) { // throw new FMILException(e.getMessage()); // } // } // } // // private native boolean getBooleanValue_(String id, String name); 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; } }