I’d like to execute a python expression and get the result as a String from inside my module. There is a swing component which has dynamic title. Let’s say, I pass python expression ‘now(1000)’, without quotes, through property editor and the title will be dynamically showing datetime string.
According to the answer from @Carl.Gould at this thread I have created a ScriptRunner
class.
public class ScriptRunner {
private final Logger logger = LoggerFactory.getLogger(getClass());
private final ScriptManager scriptManager;
public ScriptRunner(ScriptManager scriptManager) {
assert scriptManager != null : "ScriptManager cannot be null";
this.scriptManager = scriptManager;
}
public String runIt(String scriptCode) {
if (StringUtils.isBlank(scriptCode)) {
logger.debug("runIt(ScriptCode) is null or empty");
return "";
}
PyObject compiledScript =
Py.compile_command_flags(scriptCode,
"<whatever-filename>",
CompileMode.eval,
CompilerFlags.getCompilerFlags(),
false);
try {
return scriptManager.runFunction(compiledScript).asString();
} catch (JythonExecException | NullPointerException e) {
logger.error("Error running {}", scriptCode, e);
}
return "NO_RESULT";
}
}
I get an instance of the ScriptManager
from the component by calling:
VisionClientContext context = getAppContext();
ScriptManager scriptManager = context.getScriptManager();
this.scriptRunner = new ScriptRunner(scriptManager);
Unfortunately, when I call scriptRunner.runIt("now(1000)")
I get an exception:
com.inductiveautomation.ignition.common.script.JythonExecException: TypeError: 'tablecode' object is not callable
at org.python.core.Py.TypeError(Py.java:235)
at org.python.core.PyObject.__call__(PyObject.java:316)
at org.python.core.PyObject.__call__(PyObject.java:357)
at com.inductiveautomation.ignition.common.script.ScriptManager.runFunction(ScriptManager.java:670)
at mycompany.ScriptRunner.runIt(ScriptRunner.java:35)
Could help figuring this out?
Thanks in advance,
Almer
1 Like
now()
is part of our expression language, which is an entirely separate thing from executing arbitrary Jython code.
Hi @Kevin.Herron ,
I am new to Ignition platform. I might have mis-explained my problem. What if I only want to execute Ignition expressions and get back the results as string, for example?
This post may be helpful. You can ignore the alarm-specific parts, but basically what you need is an ExpressionParseContext
. Searching for that term yields a couple other results too.
Hi @Kevin.Herron,
thanks, I got it working using this class you wrote and this usage example.
In case if anyone needs a running example, I will leave the following code snippets here:
public class CustomParseContext implements ExpressionParseContext {
private final TagManagerBase tagManagerBase;
private final FunctionFactory functionFactory;
public CustomParseContext(TagManagerBase tagManagerBase, FunctionFactory functionFactory) {
this.tagManagerBase = tagManagerBase;
this.functionFactory = functionFactory;
}
@Override
public Expression createBoundExpression(String path) throws RuntimeException {
try {
TagPath tagPath = TagPathParser.parse(path);
Tag tag = tagManagerBase.getTag(tagPath);
if (tag == null) throw new TagNotFoundException(tagPath);
TagValue tagValue = tag.getAttribute(tagPath.getProperty());
TagListener listener = new TagListener(
tagPath,
tagValue.getValue(),
tagValue.getQuality()
);
Class type = TagProp.Value.equals(tagPath.getProperty()) ?
tag.getDataType().getJavaType() :
tagPath.getProperty().getType();
BoundTagExpression expression = new BoundTagExpression(tagPath);
expression.setType(type);
expression.setTagListenerDelegate(listener);
return expression;
} catch (IllegalArgumentException | IOException | TagNotFoundException e) {
throw new RuntimeException(e);
}
}
@Override
public FunctionFactory getFunctionFactory() {
return functionFactory;
}
}
public class MyComponent extends AbstractVisionPanel {
private final Logger logger = LoggerFactory.getLogger(getClass());
...
@Override
protected void onStartup() {
super.onStartup();
VisionClientContext appContext = getAppContext();
FunctionFactory functionFactory = appContext.getExpressionFunctionFactory();
ClientTagManager tagManager = appContext.getTagManager();
CustomParseContext customParseContext = new CustomParseContext(tagManager, functionFactory);
Parser parser = new ELParserHarness();
String expressionString = "now(1000)";
try {
Expression expression = parser.parse(expressionString, customParseContext);
expression.connect(appContext, () -> {
try {
logger.debug("updating");
String value = TypeUtilities.toString(expression.execute().getValue());
System.out.printf("value = %s\n", value);
} catch (ExpressionException e) {
logger.warn("Error parsing expression '{}'", expressionString);
}
});
expression.startup();
// Execute the expression 'now(1000)' once
System.out.println("execute: " + TypeUtilities.toString(expression.execute().getValue()));
} catch (Exception e) {
logger.error("Error starting ELParser", e);
}
}
...
}
I will mark this question as solved if you don’t mind