blob: 40d6708298fb9511b1fbefd8abfff3a1e4fb494e [file] [log] [blame]
/*****************************************************************************
* Copyright (c) 2016 CEA LIST.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* CEA LIST Initial API and implementation
*****************************************************************************/
package org.eclipse.papyrus.moka.fmi.master.fmuproxy;
import java.lang.reflect.Array;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.papyrus.moka.composites.Semantics.impl.CompositeStructures.StructuredClasses.CS_Object;
import org.eclipse.papyrus.moka.fmi.fmiprofile.ScalarVariable;
import org.eclipse.papyrus.moka.fmi.master.fmilibrary.Fmi2CausalityType;
import org.eclipse.papyrus.moka.fmi.master.fmilibrary.Fmi2Parameters;
import org.eclipse.papyrus.moka.fmi.master.fmilibrary.Fmi2Port;
import org.eclipse.papyrus.moka.fmi.master.fmilibrary.Fmi2ScalarVariable;
import org.eclipse.papyrus.moka.fmi.master.fmilibrary.Fmi2VariableType;
import org.eclipse.papyrus.moka.fmi.master.jnr.JNRFMUInterface;
import org.eclipse.papyrus.moka.fmi.master.jnr.JNRFMUInterface.Fmi2Status;
import org.eclipse.papyrus.moka.fmi.master.jnr.JNRFMUInterface.Fmi2StatusKind;
import org.eclipse.papyrus.moka.fmi.master.jnr.JNRFMUInterface.Fmi2Type;
import org.eclipse.uml2.uml.Class;
import org.eclipse.uml2.uml.Port;
import org.eclipse.uml2.uml.Property;
import org.eclipse.uml2.uml.Stereotype;
import org.eclipse.uml2.uml.util.UMLUtil;
import jnr.ffi.LibraryLoader;
import jnr.ffi.Memory;
import jnr.ffi.Pointer;
import jnr.ffi.Runtime;
import jnr.ffi.byref.DoubleByReference;
import jnr.ffi.byref.IntByReference;
import jnr.ffi.byref.PointerByReference;
public class Fmu2ProxyService extends CS_Object {
public Fmi2Parameters parameters;
public ArrayList<Fmi2Port> inputPorts = new ArrayList<Fmi2Port>();
public ArrayList<Fmi2Port> outputPorts = new ArrayList<Fmi2Port>();
public ArrayList<Fmi2ScalarVariable> variables = new ArrayList<Fmi2ScalarVariable>();
public JNRFMUInterface jnrIf;
private Pointer component = null;
public Pointer fmuState = null;
public Fmi2Status status = Fmi2Status.fmi2OK;
private static final int INTEGER_GET_INDEX = 0;
private static final int INTEGER_SET_INDEX = 1;
private static final int REAL_GET_INDEX = 2;
private static final int REAL_SET_INDEX = 3;
private static final int STRING_GET_INDEX = 4;
private static final int STRING_SET_INDEX = 5;
private static final int BOOLEAN_GET_INDEX = 6;
private static final int BOOLEAN_SET_INDEX = 7;
@SuppressWarnings("unchecked")
private ArrayList<Fmi2ScalarVariable>[] cacheableVariableArray = (ArrayList<Fmi2ScalarVariable>[]) Array.newInstance(ArrayList.class, 8);
private Pointer[] valueReferenceArrays = new Pointer[8];
private int[] numberOfValueReferencesArray = new int[8];
private Pointer getRealValues = null;
private Pointer setRealValues = null;
private Pointer getIntValues = null;
private Pointer setIntValues = null;
//Boolean size is implementation defined, safer to rely on
//jnr translation even if maybe slower than direct pointer
private boolean[] getBoolValues = null;
private boolean[] setBoolValues = null;
//TODO see if it's possible to handle char* pointers
//string translation seems to be tricky, safer to rely on JNR as well
private String[] getStringValues = null;
private String[] setStringValues = null;
protected Map<Property, Fmi2ScalarVariable> property2VarialeMap = new HashMap<>();
public void setComponent(Pointer component) {
this.component = component;
}
public Pointer getComponent() {
return component;
}
public Fmu2ProxyService(Class service) {
this.addType(service);
initialize();
}
public Fmi2ScalarVariable getVariable(Property p) {
return this.property2VarialeMap.get(p);
}
private void initialize() {
for (int index = 0; index < 8; index++) {
cacheableVariableArray[index] = new ArrayList<Fmi2ScalarVariable>();
}
for (Property p : types.get(0).getOwnedAttributes()) {
Stereotype st = null;
for (EObject application : p.getStereotypeApplications()) {
if (application instanceof ScalarVariable) {
st = UMLUtil.getStereotype(application);
break;
}
}
if (st != null) {
// String variableType = p.getType().getName();
Fmi2ScalarVariable variable;
if (p instanceof Port) {
variable = new Fmi2Port(this, (Port) p, st);
if (variable.getCausality().equals(Fmi2CausalityType.fmi2Output)) {
outputPorts.add((Fmi2Port) variable);
} else {
inputPorts.add((Fmi2Port) variable);
}
} else {
variable = new Fmi2ScalarVariable(this, p, st);
this.property2VarialeMap.put(p, variable);
}
variables.add(variable);
if (variable.getCausality().equals(Fmi2CausalityType.fmi2Input)) {
switch (variable.getType()) {
case Fmi2VariableType.fmi2Boolean:
cacheableVariableArray[BOOLEAN_SET_INDEX].add(variable);
break;
case Fmi2VariableType.fmi2Integer:
cacheableVariableArray[INTEGER_SET_INDEX].add(variable);
break;
case Fmi2VariableType.fmi2Real:
cacheableVariableArray[REAL_SET_INDEX].add(variable);
break;
case Fmi2VariableType.fmi2String:
cacheableVariableArray[STRING_SET_INDEX].add(variable);
default:
break;
}
} else if (variable.getCausality().equals(Fmi2CausalityType.fmi2Output)
// || variable.getCausality().equals(Fmi2CausalityType.fmi2Parameter)
// && !variable.getVariability().equals(Fmi2VariabilityType.fmi2Constant)
) {
switch (variable.getType()) {
case Fmi2VariableType.fmi2Boolean:
cacheableVariableArray[BOOLEAN_GET_INDEX].add(variable);
break;
case Fmi2VariableType.fmi2Integer:
cacheableVariableArray[INTEGER_GET_INDEX].add(variable);
break;
case Fmi2VariableType.fmi2Real:
cacheableVariableArray[REAL_GET_INDEX].add(variable);
break;
case Fmi2VariableType.fmi2String:
cacheableVariableArray[STRING_GET_INDEX].add(variable);
default:
break;
}
}
}
}
for (int cachedKind = 0; cachedKind < 8; cachedKind++) {
intializeVRS(cachedKind, cacheableVariableArray[cachedKind]);
}
if (!cacheableVariableArray[BOOLEAN_GET_INDEX].isEmpty()) {
getBoolValues = new boolean[cacheableVariableArray[BOOLEAN_GET_INDEX].size()];
}
if (!cacheableVariableArray[BOOLEAN_SET_INDEX].isEmpty()) {
setBoolValues = new boolean[cacheableVariableArray[BOOLEAN_SET_INDEX].size()];
}
if (!cacheableVariableArray[REAL_GET_INDEX].isEmpty()) {
getRealValues = Memory.allocateDirect(Runtime.getSystemRuntime(),cacheableVariableArray[REAL_GET_INDEX].size()*Double.BYTES);
}
if (!cacheableVariableArray[REAL_SET_INDEX].isEmpty()) {
setRealValues = Memory.allocateDirect(Runtime.getSystemRuntime(),cacheableVariableArray[REAL_SET_INDEX].size()*Double.BYTES);
}
if (!cacheableVariableArray[INTEGER_GET_INDEX].isEmpty()) {
getIntValues = Memory.allocateDirect(Runtime.getSystemRuntime(),cacheableVariableArray[INTEGER_GET_INDEX].size()*Integer.BYTES);
}
if (!cacheableVariableArray[INTEGER_SET_INDEX].isEmpty()) {
setIntValues =Memory.allocateDirect(Runtime.getSystemRuntime(),cacheableVariableArray[INTEGER_SET_INDEX].size()*Integer.BYTES);
}
if (!cacheableVariableArray[STRING_GET_INDEX].isEmpty()) {
getStringValues = new String[cacheableVariableArray[STRING_GET_INDEX].size()];
}
if (!cacheableVariableArray[STRING_SET_INDEX].isEmpty()) {
setStringValues = new String[cacheableVariableArray[STRING_SET_INDEX].size()];
}
}
private void intializeVRS(int cachedKind, ArrayList<Fmi2ScalarVariable> cacheableVariables) {
valueReferenceArrays[cachedKind] = Memory.allocateDirect(Runtime.getSystemRuntime(), cacheableVariables.size()*Integer.BYTES);
for (int i = 0; i < cacheableVariables.size(); i++) {
valueReferenceArrays[cachedKind].putInt(i*Integer.BYTES, (int) cacheableVariables.get(i).getValueReference());
}
numberOfValueReferencesArray[cachedKind] = cacheableVariables.size();
}
public void fetchGetCache() {
if (!cacheableVariableArray[REAL_GET_INDEX].isEmpty()) {
fetchRealCache();
}
if (!cacheableVariableArray[INTEGER_GET_INDEX].isEmpty()) {
fetchIntegerCache();
}
if (!cacheableVariableArray[BOOLEAN_GET_INDEX].isEmpty()) {
fetchBooleanCache();
}
if (!cacheableVariableArray[STRING_GET_INDEX].isEmpty()) {
fetchStringCache();
}
}
private void fetchStringCache() {
jnrIf.fmi2GetString(component, valueReferenceArrays[STRING_GET_INDEX], numberOfValueReferencesArray[STRING_GET_INDEX], getStringValues);
for (int i = 0; i < cacheableVariableArray[STRING_GET_INDEX].size(); i++) {
Fmi2ScalarVariable variable = cacheableVariableArray[STRING_GET_INDEX].get(i);
variable.setRuntimeValue(getStringValues[i]);
}
}
private void fetchBooleanCache() {
jnrIf.fmi2GetBoolean(component, valueReferenceArrays[BOOLEAN_GET_INDEX], numberOfValueReferencesArray[BOOLEAN_GET_INDEX], getBoolValues);
for (int i = 0; i < cacheableVariableArray[BOOLEAN_GET_INDEX].size(); i++) {
Fmi2ScalarVariable variable = cacheableVariableArray[BOOLEAN_GET_INDEX].get(i);
variable.setRuntimeValue(getBoolValues[i]);
}
}
private void fetchIntegerCache() {
jnrIf.fmi2GetInteger(component, valueReferenceArrays[INTEGER_GET_INDEX], numberOfValueReferencesArray[INTEGER_GET_INDEX], getIntValues);
for (int i = 0; i < cacheableVariableArray[INTEGER_GET_INDEX].size(); i++) {
Fmi2ScalarVariable variable = cacheableVariableArray[INTEGER_GET_INDEX].get(i);
variable.setRuntimeValue(getIntValues.getInt(i*Integer.BYTES));
}
}
private void fetchRealCache() {
jnrIf.fmi2GetReal(component, valueReferenceArrays[REAL_GET_INDEX], numberOfValueReferencesArray[REAL_GET_INDEX], getRealValues);
for (int i = 0; i < cacheableVariableArray[REAL_GET_INDEX].size(); i++) {
Fmi2ScalarVariable variable = cacheableVariableArray[REAL_GET_INDEX].get(i);
variable.setRuntimeValue(getRealValues.getDouble(i * Double.BYTES));
}
}
public void flushSetCache() {
if (!cacheableVariableArray[REAL_SET_INDEX].isEmpty()) {
flushRealCache();
}
if (!cacheableVariableArray[INTEGER_SET_INDEX].isEmpty()) {
flushIntegerCache();
}
if (!cacheableVariableArray[BOOLEAN_SET_INDEX].isEmpty()) {
flushBooleanCache();
}
if (!cacheableVariableArray[STRING_SET_INDEX].isEmpty()) {
flushStringCache();
}
}
private void flushStringCache() {
for (int i = 0; i < cacheableVariableArray[STRING_SET_INDEX].size(); i++) {
Fmi2ScalarVariable variable = cacheableVariableArray[STRING_SET_INDEX].get(i);
setStringValues[i] = (String) variable.getRuntimeValue();
}
jnrIf.fmi2SetString(component, valueReferenceArrays[STRING_SET_INDEX], numberOfValueReferencesArray[STRING_SET_INDEX], setStringValues);
}
private void flushBooleanCache() {
for (int i = 0; i < cacheableVariableArray[BOOLEAN_SET_INDEX].size(); i++) {
Fmi2ScalarVariable variable = cacheableVariableArray[BOOLEAN_SET_INDEX].get(i);
setBoolValues[i] = (boolean) variable.getRuntimeValue();
}
jnrIf.fmi2SetBoolean(component, valueReferenceArrays[BOOLEAN_SET_INDEX], numberOfValueReferencesArray[BOOLEAN_SET_INDEX], setBoolValues);
}
private void flushIntegerCache() {
for (int i = 0; i < cacheableVariableArray[INTEGER_SET_INDEX].size(); i++) {
Fmi2ScalarVariable variable = cacheableVariableArray[INTEGER_SET_INDEX].get(i);
setIntValues.putInt(i*Integer.BYTES, (int) variable.getRuntimeValue());
}
jnrIf.fmi2SetInteger(component, valueReferenceArrays[INTEGER_SET_INDEX], numberOfValueReferencesArray[INTEGER_SET_INDEX], setIntValues);
}
private void flushRealCache() {
for (int i = 0; i < cacheableVariableArray[REAL_SET_INDEX].size(); i++) {
Fmi2ScalarVariable variable = cacheableVariableArray[REAL_SET_INDEX].get(i);
setRealValues.putDouble(i*Double.BYTES,(double) variable.getRuntimeValue());
}
jnrIf.fmi2SetReal(component, valueReferenceArrays[REAL_SET_INDEX], numberOfValueReferencesArray[REAL_SET_INDEX], setRealValues);
}
public Fmi2Parameters getParameters() {
return parameters;
}
public void setParameters(Fmi2Parameters parameters) {
this.parameters = parameters;
this.jnrIf = LibraryLoader.create(JNRFMUInterface.class).load(parameters.getDllPath());
}
public Fmi2Status fmi2Instantiate(Pointer callbacksPointer, boolean debug) {
Pointer component = jnrIf.fmi2Instantiate(parameters.getModelIdentifier(), Fmi2Type.fmi2CoSimulation,
parameters.getGuid(), "file:///" + parameters.getResourceFolder(), callbacksPointer, false, debug);
if (component == null) {
status = Fmi2Status.fmi2Error;
// result.value = fmiStatus;
throw new RuntimeException("Could not instantiate model.");
} else {
status = Fmi2Status.fmi2OK;
setComponent(component);
}
return status;
}
public void fmi2Get(List<Fmi2ScalarVariable> variables) {
for (Fmi2ScalarVariable variable : variables) {
fmi2Get(variable);
}
}
public void fmi2Get(Fmi2ScalarVariable variable) {
int[] valueReferences = new int[1];
valueReferences[0] = (int) variable.getValueReference();
if (Fmi2VariableType.fmi2Boolean.equals(variable.getType())) {
boolean[] values = new boolean[1];
jnrIf.fmi2GetBoolean(component, valueReferences, 1, values);
variable.setRuntimeValue(values[0]);
} else if (Fmi2VariableType.fmi2Integer.equals(variable.getType())) {
int[] values = new int[1];
jnrIf.fmi2GetInteger(component, valueReferences, 1, values);
variable.setRuntimeValue(values[0]);
} else if (Fmi2VariableType.fmi2Real.equals(variable.getType())) {
double[] values = new double[1];
jnrIf.fmi2GetReal(component, valueReferences, 1, values);
variable.setRuntimeValue(values[0]);
} else if (Fmi2VariableType.fmi2String.equals(variable.getType())) {
String[] values = new String[1];
jnrIf.fmi2GetString(component, valueReferences, 1, values);
variable.setRuntimeValue(values[0]);
}
}
public void fmi2Set(List<Fmi2ScalarVariable> variables) {
for (Fmi2ScalarVariable variable : variables) {
fmi2Set(variable);
}
}
public void fmi2Set(Fmi2ScalarVariable variable) {
int[] valueReferences = new int[1];
valueReferences[0] = (int) variable.getValueReference();
if (Fmi2VariableType.fmi2Boolean.equals(variable.getType())) {
jnrIf.fmi2SetBoolean(component, valueReferences, 1, new boolean[] { getBoolean(variable.getRuntimeValue()) });
} else if (Fmi2VariableType.fmi2Integer.equals(variable.getType())) {
jnrIf.fmi2SetInteger(component, valueReferences, 1, new int[] { getInteger(variable.getRuntimeValue()) });
} else if (Fmi2VariableType.fmi2Real.equals(variable.getType())) {
jnrIf.fmi2SetReal(component, valueReferences, 1, new double[] { getDouble(variable.getRuntimeValue()) });
} else if (Fmi2VariableType.fmi2String.equals(variable.getType())) {
jnrIf.fmi2SetString(component, valueReferences, 1, new String[] { getString(variable.getRuntimeValue()) });
}
}
private boolean getBoolean(Object value) {
if (value instanceof Boolean) {
return (boolean) value;
} else if (value instanceof String) {
return Boolean.parseBoolean((String) value);
} else if (value instanceof Number) {
return ((Number) value).intValue() != 0;
}
return false;
}
private int getInteger(Object value) {
if (value instanceof String) {
return Integer.decode((String) value);
} else if (value instanceof Number) {
return ((Number) value).intValue();
}
return 0;
}
private double getDouble(Object value) {
if (value instanceof String) {
return Double.parseDouble((String) value);
} else if (value instanceof Number) {
return ((Number) value).doubleValue();
}
return 0.0;
}
private String getString(Object value) {
return value.toString();
}
public JNRFMUInterface getJnrIf() {
return jnrIf;
}
public Fmi2Status fmi2DoStep(double simulatedTime, double stepSize, boolean noSetPrior) {
return jnrIf.fmi2DoStep(component, simulatedTime, stepSize, noSetPrior);
}
public void dispose() {
// TODO: ensure that the DLL is disposed
jnrIf = null;
}
public void fmi2FreeInstance() {
jnrIf.fmi2FreeInstance(component);
}
public String fmi2GetTypesPlatform() {
return jnrIf.fmi2GetTypesPlatform();
}
public String fmi2GetVersion() {
return jnrIf.fmi2GetVersion();
}
public Fmi2Status fmi2SetDebugLogging(boolean loggingOn, int numberOfCategories, String categories[]) {
return jnrIf.fmi2SetDebugLogging(component, loggingOn, numberOfCategories, categories);
}
public Fmi2Status fmi2SetupExperiment(boolean toleranceDefined, double tolerance, double startTime, boolean stopTimeDefined, double stopTime) {
return jnrIf.fmi2SetupExperiment(component, toleranceDefined, tolerance, startTime, stopTimeDefined, stopTime);
}
public int fmi2EnterInitializationMode() {
return jnrIf.fmi2EnterInitializationMode(component);
}
public Fmi2Status fmi2ExitInitializationMode() {
return jnrIf.fmi2ExitInitializationMode(component);
}
public Fmi2Status fmi2Terminate() {
return jnrIf.fmi2Terminate(component);
}
public Fmi2Status fmi2Reset() {
return jnrIf.fmi2Reset(component);
}
public Fmi2Status fmi2GetFMUstate(PointerByReference state) {
return jnrIf.fmi2GetFMUstate(component, state);
}
public Fmi2Status fmi2SetFMUstate(Pointer state) {
return jnrIf.fmi2SetFMUstate(component, state);
}
public Fmi2Status fmi2FreeFMUstate(PointerByReference state) {
return jnrIf.fmi2FreeFMUstate(component, state);
}
public Fmi2Status fmi2SerializedFMUstateSize(Pointer state, IntByReference stateSize) {
return jnrIf.fmi2SerializedFMUstateSize(component, state, stateSize);
}
public Fmi2Status fmi2SerializeFMUstate(Pointer state, ByteBuffer serializedState, int serializedStateSize) {
return jnrIf.fmi2SerializeFMUstate(component, state, serializedState, serializedStateSize);
}
public Fmi2Status fmi2DeSerializeFMUstate(ByteBuffer serializedState, int size, PointerByReference state) {
return jnrIf.fmi2DeSerializeFMUstate(component, serializedState, size, state);
}
public Fmi2Status fmi2GetDirectionalDerivative(int unknownValueReferences[],
int numberOfUnknowns, int knownValueReferences[],
int numberOfKnowns, double knownDifferential[],
double unknownDifferential[]) {
return jnrIf.fmi2GetDirectionalDerivative(component, unknownValueReferences, numberOfUnknowns, knownValueReferences, numberOfKnowns, knownDifferential, unknownDifferential);
}
public Fmi2Status fmi2SetRealInputDerivatives(int valueReferences[],
int numberOfValueReferences, int orders[], double values[]) {
return jnrIf.fmi2SetRealInputDerivatives(component, valueReferences, numberOfValueReferences, orders, values);
}
public Fmi2Status fmi2GetRealOutputDerivatives(int valueReference[],
int numberOfValueReferences, int order[], double values[]) {
return jnrIf.fmi2GetRealOutputDerivatives(component, valueReference, numberOfValueReferences, order, values);
}
public Fmi2Status fmi2CancelStep() {
return jnrIf.fmi2CancelStep(component);
}
public Fmi2Status fmi2GetStatus(Fmi2StatusKind kind, PointerByReference status) {
return jnrIf.fmi2GetStatus(component, kind, status);
}
public Fmi2Status fmi2GetRealStatus(Fmi2StatusKind kind, DoubleByReference value) {
return jnrIf.fmi2GetRealStatus(component, kind, value);
}
public Fmi2Status fmi2GetIntegerStatus(Fmi2StatusKind kind, IntByReference value) {
return jnrIf.fmi2GetIntegerStatus(component, kind, value);
}
public Fmi2Status fmi2GetBooleanStatus(Fmi2StatusKind kind, IntByReference value) {
return jnrIf.fmi2GetBooleanStatus(component, kind, value);
}
public Fmi2Status fmi2GetStringStatus(Fmi2StatusKind kind, String value) {
return jnrIf.fmi2GetStringStatus(component, kind, value);
}
}