diff --git a/de.prob.core/.classpath b/de.prob.core/.classpath index 5004b8808efadf10e19fa4e2470fc73ccdd87d92..335ff9a8928bfc22ed8542d5eabf96a22f2a582b 100644 --- a/de.prob.core/.classpath +++ b/de.prob.core/.classpath @@ -1,9 +1,10 @@ <?xml version="1.0" encoding="UTF-8"?> <classpath> + <classpathentry exported="true" kind="lib" path="lib/dependencies/jfmi-1.0.2-SNAPSHOT.jar"/> + <classpathentry exported="true" kind="lib" path="lib/dependencies/jna-3.4.0.jar"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/> <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> <classpathentry kind="src" path="src"/> - <classpathentry kind="output" path="bin"/> <classpathentry exported="true" kind="lib" path="lib/dependencies/answerparser-2.4.22-SNAPSHOT.jar"/> <classpathentry exported="true" kind="lib" path="lib/dependencies/bparser-2.4.22-SNAPSHOT.jar"/> <classpathentry exported="true" kind="lib" path="lib/dependencies/cliparser-2.4.22-SNAPSHOT.jar"/> @@ -18,4 +19,5 @@ <classpathentry exported="true" kind="lib" path="lib/dependencies/xmlpull-1.1.3.1.jar"/> <classpathentry exported="true" kind="lib" path="lib/dependencies/xpp3_min-1.1.4c.jar"/> <classpathentry exported="true" kind="lib" path="lib/dependencies/xstream-1.4.3.jar"/> + <classpathentry kind="output" path="bin"/> </classpath> diff --git a/de.prob.core/META-INF/MANIFEST.MF b/de.prob.core/META-INF/MANIFEST.MF index f909299e5350c0dd7344b7c0f9de8f1aaa2ec559..83fe4c54078113f1ed4d0d5a83910db86a9c88ae 100644 --- a/de.prob.core/META-INF/MANIFEST.MF +++ b/de.prob.core/META-INF/MANIFEST.MF @@ -82,6 +82,7 @@ Export-Package: com.thoughtworks.xstream, de.prob.core.sablecc.parser, de.prob.core.translator, de.prob.core.types, + de.prob.cosimulation, de.prob.eventb.translator, de.prob.eventb.translator.flow, de.prob.eventb.translator.internal, @@ -113,7 +114,10 @@ Export-Package: com.thoughtworks.xstream, org.apache.commons.lang.mutable, org.apache.commons.lang.reflect, org.apache.commons.lang.text, - org.apache.commons.lang.time + org.apache.commons.lang.time, + org.ptolemy.fmi, + org.ptolemy.fmi.driver, + org.ptolemy.fmi.type Bundle-Activator: de.prob.core.internal.Activator Eclipse-BuddyPolicy: registered Bundle-RequiredExecutionEnvironment: JavaSE-1.6 @@ -132,4 +136,6 @@ Bundle-ClassPath: ., lib/dependencies/jsr305-1.3.9.jar, lib/dependencies/xmlpull-1.1.3.1.jar, lib/dependencies/xpp3_min-1.1.4c.jar, - lib/dependencies/xstream-1.4.3.jar + lib/dependencies/xstream-1.4.3.jar, + lib/dependencies/jfmi-1.0.2-SNAPSHOT.jar, + lib/dependencies/jna-3.4.0.jar diff --git a/de.prob.core/build.gradle b/de.prob.core/build.gradle index a4692401acacc2acb2529817321b113b81f4a2ec..60dd16f13dc3acdbf16941f1423292a2600a4663 100644 --- a/de.prob.core/build.gradle +++ b/de.prob.core/build.gradle @@ -14,4 +14,6 @@ dependencies { compile 'commons-lang:commons-lang:2.6' compile 'commons-codec:commons-codec:1.6' compile 'com.thoughtworks.xstream:xstream:1.4.3' + compile group: 'net.java.dev.jna', name: 'jna', version: '3.4.0' + compile group: 'edu.berkeley.eecs.ptolemy', name: 'jfmi', version: '1.0.2-SNAPSHOT' } \ No newline at end of file diff --git a/de.prob.core/build.properties b/de.prob.core/build.properties index 1a6cddddba5661de2f965051163daed933d90407..99230d528427c77989fe9534724cf6533e521d2e 100644 --- a/de.prob.core/build.properties +++ b/de.prob.core/build.properties @@ -8,6 +8,8 @@ bin.includes = META-INF/,\ lib/dependencies/commons-codec-1.6.jar,\ lib/dependencies/xmlpull-1.1.3.1.jar,\ lib/dependencies/xpp3_min-1.1.4c.jar,\ - lib/dependencies/xstream-1.4.3.jar + lib/dependencies/xstream-1.4.3.jar,\ + lib/dependencies/jfmi-1.0.2-SNAPSHOT.jar,\ + lib/dependencies/jna-3.4.0.jar diff --git a/de.prob.core/src/de/prob/cosimulation/FMU.java b/de.prob.core/src/de/prob/cosimulation/FMU.java new file mode 100644 index 0000000000000000000000000000000000000000..d726cdea926eb06c21100d0c7da0774fb18d216a --- /dev/null +++ b/de.prob.core/src/de/prob/cosimulation/FMU.java @@ -0,0 +1,240 @@ +package de.prob.cosimulation; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.ptolemy.fmi.FMICallbackFunctions; +import org.ptolemy.fmi.FMILibrary; +import org.ptolemy.fmi.FMIModelDescription; +import org.ptolemy.fmi.FMIScalarVariable; +import org.ptolemy.fmi.FMUFile; +import org.ptolemy.fmi.FMULibrary; + +import com.sun.jna.Function; +import com.sun.jna.NativeLibrary; +import com.sun.jna.Pointer; + +public class FMU { + + private static final double TIMEOUT = 1000; + + /** The modelIdentifier from modelDescription.xml. */ + String _modelIdentifier; + + /** The NativeLibrary that contains the functions. */ + NativeLibrary _nativeLibrary; + private Pointer component; + + private final FMIModelDescription modelDescription; + + private final Map<String, FMIScalarVariable> variables = new HashMap<String, FMIScalarVariable>(); + + private final Set<IFMUListener> listeners = new HashSet<IFMUListener>(); + + public void registerListener(final IFMUListener listener) { + listeners.add(listener); + } + + public void unregisterListener(final IFMUListener listener) { + listeners.remove(listener); + } + + public FMU(final String fmuFileName) throws IOException { + modelDescription = FMUFile.parseFMUFile(fmuFileName); + String sharedLibrary = FMUFile.fmuSharedLibrary(modelDescription); + + for (FMIScalarVariable fmiScalarVariable : modelDescription.modelVariables) { + variables.put(fmiScalarVariable.name, fmiScalarVariable); + } + + _nativeLibrary = NativeLibrary.getInstance(sharedLibrary); + + // The modelName may have spaces in it. + _modelIdentifier = modelDescription.modelIdentifier; + + // The URL of the fmu file. + String fmuLocation = new File(fmuFileName).toURI().toURL().toString(); + + // The tool to use if we have tool coupling. + String mimeType = "application/x-fmu-sharedlibrary"; + + // Timeout in ms., 0 means wait forever. + double timeout = TIMEOUT; + + // There is no simulator UI. + byte visible = 0; + // Run the simulator without user interaction. + byte interactive = 0; + // Callbacks + FMICallbackFunctions.ByValue callbacks = new FMICallbackFunctions.ByValue( + new FMULibrary.FMULogger(), new FMULibrary.FMUAllocateMemory(), + new FMULibrary.FMUFreeMemory(), + new FMULibrary.FMUStepFinished()); + // Logging tends to cause segfaults because of vararg callbacks. + byte loggingOn = (byte) 0; + + component = instantiateFMU(fmuLocation, mimeType, timeout, visible, + interactive, callbacks, loggingOn); + + if (component.equals(Pointer.NULL)) { + throw new RuntimeException("Could not instantiate model."); + } + } + + public String getFmiVersion() { + assert _nativeLibrary != null; + Function function = getFunction("_fmiGetVersion"); + return (String) function.invoke(String.class, new Object[0]); + } + + public void initialize(final double startTime, final double endTime) { + invoke("_fmiInitializeSlave", new Object[] { component, startTime, + (byte) 1, endTime }, "Could not initialize slave: "); + } + + public boolean getBoolean(final String name) { + FMIScalarVariable fmiScalarVariable = variables.get(name); + return fmiScalarVariable.getBoolean(component); + } + + public double getDouble(final String name) { + FMIScalarVariable fmiScalarVariable = variables.get(name); + return fmiScalarVariable.getDouble(component); + } + + public int getInt(final String name) { + FMIScalarVariable fmiScalarVariable = variables.get(name); + return fmiScalarVariable.getInt(component); + } + + public String getString(final String name) { + FMIScalarVariable fmiScalarVariable = variables.get(name); + return fmiScalarVariable.getString(component); + } + + public void set(final String name, final boolean b) { + FMIScalarVariable fmiScalarVariable = variables.get(name); + fmiScalarVariable.setBoolean(component, b); + } + + public void set(final String name, final int i) { + FMIScalarVariable fmiScalarVariable = variables.get(name); + fmiScalarVariable.setInt(component, i); + } + + public void set(final String name, final double d) { + FMIScalarVariable fmiScalarVariable = variables.get(name); + fmiScalarVariable.setDouble(component, d); + } + + public void set(final String name, final String s) { + FMIScalarVariable fmiScalarVariable = variables.get(name); + fmiScalarVariable.setString(component, s); + } + + public double doStep(final double time, final double delta_t) { + Function doStep = getFunction("_fmiDoStep"); + invoke(doStep, new Object[] { component, time, delta_t, (byte) 1 }, + "Could not simulate, time was " + time + ": "); + + for (IFMUListener l : listeners) { + l.trigger(variables); + } + return time + delta_t; + } + + public void terminate() { + invoke("_fmiTerminateSlave", new Object[] { component }, + "Could not terminate slave: "); + invoke("_fmiFreeSlaveInstance", new Object[] { component }, + "Could not dispose resources of slave: "); + component = null; + } + + @Override + protected void finalize() throws Throwable { + this.terminate(); + super.finalize(); + } + + public void reset() { + invoke("_fmiResetSlave", new Object[] { component }, + "Could not reset slave: "); + } + + public FMIModelDescription getModelDescription() { + return modelDescription; + } + + private Pointer instantiateFMU(final String fmuLocation, + final String mimeType, final double timeout, final byte visible, + final byte interactive, + final FMICallbackFunctions.ByValue callbacks, final byte loggingOn) { + Function instantiateSlave = getFunction("_fmiInstantiateSlave"); + Pointer fmiComponent = (Pointer) instantiateSlave.invoke(Pointer.class, + new Object[] { _modelIdentifier, modelDescription.guid, + fmuLocation, mimeType, timeout, visible, interactive, + callbacks, loggingOn }); + return fmiComponent; + } + + /** + * Return a function by name. + * + * @param name + * The name of the function. The value of the modelIdentifier is + * prepended to the value of this parameter to yield the function + * name. + * @return the function. + */ + public Function getFunction(final String name) { + // This is syntactic sugar. + return _nativeLibrary.getFunction(_modelIdentifier + name); + } + + /** + * Invoke a function that returns an integer representing the FMIStatus + * return value. + * + * @param name + * The name of the function. + * @param arguments + * The arguments to be passed to the function. + * @param message + * The error message to be used if there is a problem. The + * message should end with ": " because the return value of the + * function will be printed after the error message. + */ + public void invoke(final String name, final Object[] arguments, + final String message) { + Function function = getFunction(name); + invoke(function, arguments, message); + } + + /** + * Invoke a function that returns an integer representing the FMIStatus + * return value. + * + * @param function + * The function to be invoked. + * @param arguments + * The arguments to be passed to the function. + * @param message + * The error message to be used if there is a problem. The + * message should end with ": " because the return value of the + * function will be printed after the error message. + */ + public void invoke(final Function function, final Object[] arguments, + final String message) { + int fmiFlag = ((Integer) function.invoke(Integer.class, arguments)) + .intValue(); + if (fmiFlag > FMILibrary.FMIStatus.fmiWarning) { + throw new RuntimeException(message + fmiFlag); + } + } + +} diff --git a/de.prob.core/src/de/prob/cosimulation/IFMUListener.java b/de.prob.core/src/de/prob/cosimulation/IFMUListener.java new file mode 100644 index 0000000000000000000000000000000000000000..2001ffddf65a1e1aa8941b133b3d81cf65cb5b6b --- /dev/null +++ b/de.prob.core/src/de/prob/cosimulation/IFMUListener.java @@ -0,0 +1,11 @@ +package de.prob.cosimulation; + +import java.util.Map; + +import org.ptolemy.fmi.FMIScalarVariable; + +public interface IFMUListener { + + void trigger(Map<String, FMIScalarVariable> variables); + +}