Commit b3aa8423 authored by Nico Mack's avatar Nico Mack

Integrated support for SpatialVariables in PythonExecutor

parent 37e3695e
......@@ -5,6 +5,11 @@
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.python.pydev.PyDevBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
......@@ -19,5 +24,6 @@
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
<nature>org.python.pydev.pythonNature</nature>
</natures>
</projectDescription>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?eclipse-pydev version="1.0"?><pydev_project>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.7</pydev_property>
<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
<path>/${PROJECT_DIR_NAME}/python</path>
</pydev_pathproperty>
</pydev_project>
'''
Created on Aug 16, 2017
@author: mack
'''
def AddPoints (A, B):
C = {}
C['x'] = A['x'] + B['x']
C['y'] = A['y'] + B['y']
C['a'] = A['a'] + B['a']
return C
\ No newline at end of file
......@@ -52,7 +52,7 @@ public class DisplayWidgetBootstrapper implements BootstrapCallback {
// * Constants *
// ***************************************************************************
private static Logger LOGGER = LoggerFactory.getLogger(DisplayWidgetBootstrapper.class.getName());
private static final Logger LOGGER = LoggerFactory.getLogger(DisplayWidgetBootstrapper.class.getName());
// ---------------------------------------------------------------------------
// ***************************************************************************
......
......@@ -54,7 +54,7 @@ public class Equation {
private String script;
private boolean isolated;
private static Logger LOGGER = LoggerFactory.getLogger(Equation.class.getSimpleName());
private static final Logger LOGGER = LoggerFactory.getLogger(Equation.class.getSimpleName());
/**
* Constructor
......
package lu.list.itis.dkd.tui.cps.system.executor;
import lu.list.itis.dkd.tui.cps.system.Import;
import lu.list.itis.dkd.tui.cps.variable.SpatialVariable;
import lu.list.itis.dkd.tui.cps.variable.Variable;
import lu.list.itis.dkd.tui.cps.variable.VectorVariable;
import org.python.core.Py;
import org.python.core.PyArray;
import org.python.core.PyBoolean;
import org.python.core.PyDictionary;
import org.python.core.PyFloat;
import org.python.core.PyInteger;
import org.python.core.PyObject;
......@@ -14,19 +17,35 @@ import org.python.util.PythonInterpreter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
// ***************************************************************************
// * Class Definition *
// ***************************************************************************
public class PythonExecutor extends Executor {
private PythonInterpreter engine;
private static final String SCRIPT_ENGINE_WORKING_DIR = "python.workingDir"; //$NON-NLS-1$
private static final String PARAMETER_TEMPLATE = "Parameter -> {} = {}"; //$NON-NLS-1$
private static final Pattern TUPLE_PATTERN = Pattern.compile("^([a-z0-9_]+)\\.([a-z0-9_]+)", Pattern.CASE_INSENSITIVE); //$NON-NLS-1$
private static Logger LOGGER = LoggerFactory.getLogger(PythonExecutor.class.getSimpleName());
private static final Logger LOGGER = LoggerFactory.getLogger(PythonExecutor.class.getSimpleName());
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Constructor(s) *
// ***************************************************************************
// ---------------------------------------------------------------------------
public PythonExecutor(Properties properties) {
super(properties);
......@@ -36,71 +55,314 @@ public class PythonExecutor extends Executor {
engine.exec("import sys"); //$NON-NLS-1$
engine.exec("sys.path.append ('" + properties.getProperty(SCRIPT_ENGINE_WORKING_DIR) + "')"); //$NON-NLS-1$//$NON-NLS-2$
// engine.exec("print sys.path"); //$NON-NLS-1$
}
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Primitive(s) *
// ***************************************************************************
// ---------------------------------------------------------------------------
// ============================================================================
// = Low level conversion for scalars from Java to Python and back again
// ============================================================================
// ---------------------------------------------------------------------------
/**
* Converts a java double value into a python double.
*
* @param value
* specifies the java double value to convert
* @return a python double initialized to the same value as the specified java double.
*/
// ---------------------------------------------------------------------------
private PyObject pythonizeDouble(Double value) {
return (value != null) ? new PyFloat(value) : Py.None;
}
// ---------------------------------------------------------------------------
/**
* Converts a python double value into a java double.
*
* @param value
* specifies the python double value to convert
* @return a java double initialized to the same value as the specified python double.
*/
// ---------------------------------------------------------------------------
private Double depythonizeDouble(PyObject object) {
return (object != null) ? (Double) object.__tojava__(Double.class) : null;
}
// ---------------------------------------------------------------------------
/**
* Converts a java integer value into a python integer.
*
* @param value
* specifies the java integer value to convert
* @return a python integer initialized to the same value as the specified java integer.
*/
// ---------------------------------------------------------------------------
private PyObject pythonizeInteger(Integer value) {
return (value != null) ? new PyInteger(value) : Py.None;
}
// ---------------------------------------------------------------------------
/**
* Converts a python integer value into a java integer.
*
* @param value
* specifies the python integer value to convert
* @return a java integer initialized to the same value as the specified python integer.
*/
// ---------------------------------------------------------------------------
private Integer depythonizeInteger(PyObject object) {
return (object != null) ? (Integer) object.__tojava__(Integer.class) : null;
}
// ---------------------------------------------------------------------------
/**
* Converts a java boolean value into a python boolean.
*
* @param value
* specifies the java boolean value to convert
* @return a python boolean initialized to the same value as the specified java boolean.
*/
// ---------------------------------------------------------------------------
private PyObject pythonizeBoolean(Boolean value) {
return (value != null) ? new PyBoolean(value) : Py.None;
}
// ---------------------------------------------------------------------------
/**
* Converts a python boolean value into a java boolean.
*
* @param value
* specifies the python boolean value to convert
* @return a java boolean initialized to the same value as the specified python boolean.
*/
// ---------------------------------------------------------------------------
private Boolean depythonizeBoolean(PyObject object) {
return (object != null) ? (Boolean) object.__tojava__(Boolean.class) : null;
}
// ---------------------------------------------------------------------------
// ============================================================================
// = Low level conversion for vectors from Java to Python and back again
// ============================================================================
// ---------------------------------------------------------------------------
private PyArray pythonizeDoubleVector(List<Double> doubles) {
PyArray array = new PyArray(Double.class, doubles.size());
int index = 0;
for (Double value : doubles) {
array.set(index++, pythonizeDouble(value));
}
return array;
}
// ---------------------------------------------------------------------------
private PyArray pythonizeIntegerVector(List<Integer> integers) {
PyArray array = new PyArray(Integer.class, integers.size());
int index = 0;
for (Integer value : integers) {
array.set(index++, pythonizeInteger(value));
}
return array;
}
// ---------------------------------------------------------------------------
private PyArray pythonizeBooleanVector(List<Boolean> booleans) {
PyArray array = new PyArray(Boolean.class, booleans.size());
int index = 0;
for (Boolean value : booleans) {
array.set(index++, pythonizeBoolean(value));
}
return array;
}
// ---------------------------------------------------------------------------
@SuppressWarnings("unchecked")
private PyArray pythonizeVectorVariable(VectorVariable<?> vector) {
PyArray array = null;
if (!vector.isEmpty()) {
if (Double.class.isAssignableFrom(vector.getClassOfValues())) {
List<Double> doubles = (List<Double>) vector.getValue();
array = this.pythonizeDoubleVector(doubles);
} else if (Integer.class.isAssignableFrom(vector.getClassOfValues())) {
List<Integer> integers = (List<Integer>) vector.getValue();
array = this.pythonizeIntegerVector(integers);
} else if (Boolean.class.isAssignableFrom(vector.getClassOfValues())) {
List<Boolean> booleans = (List<Boolean>) vector.getValue();
array = this.pythonizeBooleanVector(booleans);
}
} else {
LOGGER.warn("Empty Vector {}", vector.getName()); //$NON-NLS-1$
array = new PyArray(Object.class, 0);
}
return array;
}
// ---------------------------------------------------------------------------
private VectorVariable<?> depythonizeVector(VectorVariable<?> vector, PyObject array) {
vector.suspendListenerNotification(true);
vector.clear();
if (Double.class.isAssignableFrom(vector.getClassOfValues())) {
for (PyObject item : array.asIterable()) {
vector.add(this.depythonizeDouble(item));
}
} else if (Integer.class.isAssignableFrom(vector.getClassOfValues())) {
for (PyObject item : array.asIterable()) {
vector.add(this.depythonizeInteger(item));
}
} else if (Boolean.class.isAssignableFrom(vector.getClassOfValues())) {
for (PyObject item : array.asIterable()) {
vector.add(this.depythonizeBoolean(item));
}
} else {
for (PyObject item : array.asIterable()) {
vector.add(item.__tojava__(Object.class));
}
}
vector.suspendListenerNotification(false);
vector.notifyInputChangeListeners();
return vector;
}
// ---------------------------------------------------------------------------
private PyObject pythonizeStructuredVariable(Variable<?> structured) {
PyObject pythonized = Py.None;
Map<String, Method> getters = Variable.getValueGetters(structured.getClass());
if (!getters.isEmpty()) {
pythonized = new PyDictionary();
for (Entry<String, Method> entry : getters.entrySet()) {
String identifier = entry.getKey().intern();
Method getter = entry.getValue();
if (Double.class.isAssignableFrom(getter.getReturnType())) {
pythonized.__setitem__(identifier, pythonizeDouble((Double) Variable.invokeVariableMethod(getter, structured, null)));
} else if (Integer.class.isAssignableFrom(getter.getReturnType())) {
pythonized.__setitem__(identifier, pythonizeInteger((Integer) Variable.invokeVariableMethod(getter, structured, null)));
} else if (Boolean.class.isAssignableFrom(getter.getReturnType())) {
pythonized.__setitem__(identifier, pythonizeBoolean((Boolean) Variable.invokeVariableMethod(getter, structured, null)));
}
}
} else {
LOGGER.warn("Failed to determine getters for structured variable {}", structured.getName()); //$NON-NLS-1$
}
return pythonized;
}
// ---------------------------------------------------------------------------
private Variable<?> depythonizeStructuredVariable(Variable<?> structured, PyObject dictionary) {
Map<String, Method> setters = Variable.getValueSetters(structured.getClass());
if (!setters.isEmpty()) {
for (Entry<String, Method> entry : setters.entrySet()) {
String identifier = entry.getKey().intern();
Method setter = entry.getValue();
if (Double.class.isAssignableFrom((setter.getParameters())[0].getType())) {
ArrayList<Double> doubles = new ArrayList<>();
doubles.add(this.depythonizeDouble(dictionary.__finditem__(identifier)));
Variable.invokeVariableMethod(setter, structured, doubles.toArray());
} else if (Integer.class.isAssignableFrom((setter.getParameters())[0].getType())) {
ArrayList<Integer> integers = new ArrayList<>();
integers.add(this.depythonizeInteger(dictionary.__finditem__(identifier)));
Variable.invokeVariableMethod(setter, structured, integers.toArray());
} else if (Boolean.class.isAssignableFrom((setter.getParameters())[0].getType())) {
ArrayList<Boolean> booleans = new ArrayList<>();
booleans.add(this.depythonizeBoolean(dictionary.__finditem__(identifier)));
Variable.invokeVariableMethod(setter, structured, booleans.toArray());
}
}
} else {
LOGGER.warn("Failed to determine setters for structured variable {}", structured.getName()); //$NON-NLS-1$
}
return structured;
}
// ---------------------------------------------------------------------------
private PyObject retrieveVariable(Variable<?> variable) {
PyObject item = null;
String equate = variable.getEquate();
if ((equate != null) && (equate.length() > 0)) {
Matcher tupleMatcher = TUPLE_PATTERN.matcher(equate);
if (tupleMatcher.matches()) {
PyTuple tuple = (PyTuple) engine.get(tupleMatcher.group(1));
item = tuple.__getattr__(tupleMatcher.group(2));
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Result <- {} = {}.{} = {}", variable.getName(), tupleMatcher.group(1), tupleMatcher.group(2), item); //$NON-NLS-1$
}
}
} else {
item = engine.get(variable.getName());
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Result <- {} = {}", variable.getName(), item); //$NON-NLS-1$
}
}
return item;
}
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Class Body *
// ***************************************************************************
// ---------------------------------------------------------------------------
@Override
public void resolve(List<Import> imports) {
for (Import declaration : imports) {
StringBuilder importBuilder = new StringBuilder("from "); //$NON-NLS-1$
importBuilder.append(declaration.getFrom()).append(" import ").append(declaration.getFunction()); //$NON-NLS-1$
engine.exec(importBuilder.toString());
LOGGER.info(importBuilder.toString());
String importStatement = importBuilder.toString();
engine.exec(importStatement);
LOGGER.info(importStatement);
}
}
@SuppressWarnings("unchecked")
// ---------------------------------------------------------------------------
@Override
public void set(Variable<?> variable) {
if (variable instanceof VectorVariable) {
VectorVariable<?> vector = (VectorVariable<?>) variable;
if (!vector.isEmpty()) {
PyArray array = null;
if (Double.class.isAssignableFrom(vector.getClassOfValues())) {
List<Double> list = (List<Double>) vector.getValue();
array = new PyArray(Double.class, list.size());
int index = 0;
for (Double value : list) {
// if (value == null)
// value = Double.valueOf(0);
// array.set(index++, new PyFloat(value));
array.set(index++, (value != null) ? new PyFloat(value) : Py.None);
}
engine.set(variable.getName(), array);
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Parameter -> {} = {}", variable.getName(), array.toString()); //$NON-NLS-1$
}
} else if (Integer.class.isAssignableFrom(vector.getClassOfValues())) {
List<Integer> list = (List<Integer>) vector.getValue();
array = new PyArray(Integer.class, list.size());
int index = 0;
for (Integer value : list) {
// if (value == null)
// value = Integer.valueOf(0);
// array.set(index++, new PyInteger(value));
array.set(index++, (value != null) ? new PyInteger(value) : Py.None);
}
engine.set(variable.getName(), array);
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Parameter -> {} = {}", variable.getName(), array.toString()); //$NON-NLS-1$
}
}
} else {
LOGGER.warn("Empty Vector {}", variable.getName()); //$NON-NLS-1$
engine.set(variable.getName(), new PyArray(Object.class, 0));
PyArray array = this.pythonizeVectorVariable((VectorVariable<?>) variable);
engine.set(variable.getName(), array);
if (LOGGER.isInfoEnabled()) {
LOGGER.info(PARAMETER_TEMPLATE, variable.getName(), (array != null) ? array.toString() : "null"); //$NON-NLS-1$
}
} else if (variable instanceof SpatialVariable) {
PyObject structured = this.pythonizeStructuredVariable(variable);
engine.set(variable.getName(), structured);
if (LOGGER.isInfoEnabled()) {
LOGGER.info(PARAMETER_TEMPLATE, variable.getName(), (structured != null) ? structured.toString() : "null"); //$NON-NLS-1$
}
} else {
Double numericValue = Double.valueOf(variable.getValue().toString());
engine.set(variable.getName(), new PyFloat(numericValue));
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Parameter -> {} = {}", variable.getName(), variable.getValue()); //$NON-NLS-1$
LOGGER.info(PARAMETER_TEMPLATE, variable.getName(), variable.getValue());
}
}
}
// ---------------------------------------------------------------------------
@Override
public boolean eval(String script) {
boolean success = true;
......@@ -111,7 +373,7 @@ public class PythonExecutor extends Executor {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Eval took {} ms | {}", elapsed, script); //$NON-NLS-1$
}
if (this.executionErrors.size() > 0) {
if (this.executionErrors.size() > 0 && LOGGER.isErrorEnabled()) {
LOGGER.error("Eval returned an error: {}", this.executionErrors.toString()); //$NON-NLS-1$
success = false;
}
......@@ -119,51 +381,31 @@ public class PythonExecutor extends Executor {
return success;
}
// ---------------------------------------------------------------------------
@Override
public Variable<?> get(Variable<?> variable) {
PyObject result = null;
String equate = variable.getEquate();
if ((equate != null) && (equate.length() > 0)) {
Matcher tupleMatcher = TUPLE_PATTERN.matcher(equate);
if (tupleMatcher.matches()) {
PyTuple tuple = (PyTuple) engine.get(tupleMatcher.group(1));
result = tuple.__getattr__(tupleMatcher.group(2));
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Result <- {} = {}.{} = {}", variable.getName(), tupleMatcher.group(1), tupleMatcher.group(2), result); //$NON-NLS-1$
}
}
} else {
result = engine.get(variable.getName());
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Result <- {} = {}", variable.getName(), result); //$NON-NLS-1$
}
result = this.retrieveVariable(variable);
if (result == null) {
return variable;
}
if ((variable instanceof VectorVariable) && (result != null)) {
if (variable instanceof VectorVariable) {
VectorVariable<?> vector = (VectorVariable<?>) variable;
vector.suspendListenerNotification(true);
vector.clear();
if (Double.class.isAssignableFrom(vector.getClassOfValues())) {
for (PyObject item : result.asIterable()) {
vector.add(item.__tojava__(Double.class));
}
} else if (Integer.class.isAssignableFrom(vector.getClassOfValues())) {
for (PyObject item : result.asIterable()) {
vector.add(item.__tojava__(Integer.class));
}
} else {
for (PyObject item : result.asIterable()) {
vector.add(item.__tojava__(Object.class));
}
}
vector.suspendListenerNotification(false);
vector.notifyInputChangeListeners();
variable = this.depythonizeVector(vector, result);
} else if (variable instanceof SpatialVariable) {
SpatialVariable spatial = (SpatialVariable) variable;
variable = this.depythonizeStructuredVariable(spatial, result);
} else {
variable.setValueFromObject((result != null) ? result.__tojava__(Double.class) : null);
variable.setValueFromObject(result.__tojava__(Double.class));
}
return variable;
}
// ---------------------------------------------------------------------------
/** {@inheritDoc} */
@Override
public void resetContext() {
......
......@@ -165,11 +165,11 @@ public class NumericalVariable extends Variable<Double> {
this.setValue(this.valueFromObject(object));
}
/** {@inheritDoc} */
@Override
public Double getValue() {
return this.value;
}
// /** {@inheritDoc} */
// @Override
// public Double getValue() {
// return super.getValue();
// }
/**
*
......
......@@ -135,42 +135,42 @@ public class SpatialVariable extends Variable<Point> {
}
@ExecutorScope(identifier = "x")
public double getX() {
return (this.value != null) ? this.value.getX() : 0;
public Double getX() {
return (this.value != null) ? Double.valueOf(this.value.x) : 0;
}
@ExecutorScope(identifier = "x")
public void setX(double value) {
public void setX(Double value) {
if (this.value == null) {
this.value = new Point();
}
this.value.x = (float) value;
this.value.x = (value != null) ? value.floatValue() : 0f;
}
@ExecutorScope(identifier = "y")
public double getY() {
return (this.value != null) ? this.value.getY() : 0;
public Double getY() {
return (this.value != null) ? Double.valueOf(this.value.y) : 0;
}
@ExecutorScope(identifier = "y")
public void setY(double value) {
public void setY(Double value) {
if (this.value == null) {
this.value = new Point();
}