I’m trying to develop a scripting function with the java SDK and I would like to implement optional arguments. Is there an elegant way to achieve this? Typically, how are out of the box scripting functions with optional arguments programmed by IA ?
Thanks in advance.
Here’s a snippet from my Ethernet/IP module that should get you started:
@KeywordArgs(names = { "name", "slot", "id" }, types = { String.class, Integer.class, Long.class })
static public PyObject getDevice(PyObject[] args, String[] keywords) {
ArgParser ap = new ArgParser("getDevice", args, keywords, new String[] { "name", "slot", "id" });
String name = ap.getString(0, null);
int slot = ap.getInt(1, -1);
long id = (Long) Py.tojava(ap.getPyObject(2, new PyLong(0L)), Long.class);
Thanks for your answer. I understand your code and it is exactly what I’m looking for.
That being said, my function’s declaration in the AbstractScriptModule file should then look like:
@Override
@ScriptFunction(docBundlePrefix = "AbstractScriptModule")
public String getRawData(PyObject[] args, String[] keywords) {
return getRawDataImpl(args, keywords);
}
protected abstract String getRawDataImpl(PyObject[] args, String[] keywords);
So there won’t be any input parameter with the @ScriptArg annotation like shown in the examples right? Is that ok?
I’m not sure how to combine the @KeywordArgs
annotation with the @ScriptFunction
annotation. I don’t use the latter. I have a custom doc provider with similar functionality.
It’s perfectly fine to have both the @ScriptFunction
and @KeywordArgs
annotations on the same function. We do this all the time internally.
To answer @mehdi.bouzit1 's question, with an example from our code…
The superclass looks like this:
Dataset queryAgentStatus(PyObject[] pyArgs, String[] keywords);
(no annotations at all)
One of the subclasses looks like this:
@ScriptFunction(docBundlePrefix = "EAMScriptingFunctions")
@KeywordArgs(names = {"groupIds", "agentIds", "isConnected"}, types = {String[].class,
String[].class, Boolean.class})
public Dataset queryAgentStatus(PyObject[] pyArgs, String[] keywords) {
// some code here
}
If you're on newer 8.0.X+ SDK, you could try out PyArgParser
in common - it's much more reasonable about automatic casting (imo) than PyArgumentMap
.
Also, TypeUtilities.pyToJava
is nice if you could have someone pass very large numbers - at some point, Jython switches to BigInteger/BigDecimal as the return from tojava
, which the TypeUtilities method will automatically unpack.
As an aside for @mehdi.bouzit1 - while the PyObject[], String[]
signature is definitely preferred, Jython also negotiates 'optional' arguments just fine if you do plain Java overloads - sometimes that can be easier for functions that only accept a couple arguments, since you also get automatic Jython type coercion and don't have to worry about unpacking the pyArgs
array.
Most of my projects are dual 7.9/8.1. I don't actually use your SDK, per se.
Ah, handy. Will probably use next time.
I do this most of the time.
Thanks everyone, I’ve been able to successfully create my method with optional arguments with the ArgParser function.
That being said, I tried to use PyArgParser
(probably the wrong way) as follows:
@Override
@ScriptFunction(docBundlePrefix = "AbstractScriptModule")
@KeywordArgs(names = { "arg1", "arg2", "arg3", ... },
types = { String.class, String.class,String.class,String.class,String.class, ...})
protected String getRawDataImpl(PyObject[] args, String[] keywords) throws IOException{
PyArgParser pap = new PyArgParser(args, keywords);
String[] cmdArray = new String[16];
cmdArray[0] = pap.arg(0,null).toString();
cmdArray[1] = pap.arg(1,null).toString();
cmdArray[2] = pap.arg(2,null).toString();
...
and ended having a NullPointerException if I hit an argument I didn’t provide.
Furthermore, on the designer, if I hit Ctrl+Space to display my function’s tooltip, it’s displaying something like:
getRawData(PyObject[], String[]) → String
Get raw data from the VSE.
Parameters:
PyObject[] - No description provided.
String[] - No description provided.
Returns:
No return value.
Is there any way to display a tooltip with all my arguments ?
Thank you.
I'm pretty sure you're using the wrong class; there's a PyArgParser
in Jython, but the one to use is
com.inductiveautomation.ignition.common.script.PyArgParser
.
Do you have each parameter identified in your .properties file?
You are right, I was using com.ziclix.python.sql.util.PyArgParser
...
I had to explicitly add:
import com.inductiveautomation.ignition.common.script.PyArgParser;
and import the maven dependancy.
I used this function as follows:
@Override
@ScriptFunction(docBundlePrefix = "AbstractScriptModule")
@KeywordArgs(names = { "arg1", "arg2",...},types = { String.class, String.class,String.class,...})
protected String getRawDataImpl(PyObject[] args, String[] keywords) throws IOException{
PyArgParser pap = PyArgParser.parseArgs(args,keywords,new String[] { "arg1", "arg2",...},
new Class[]{String.class, String.class,...},"getRawDataImpl");
// Get the parameters
pap.getString("arg1").orElse("default value 1");
pap.getString("arg2").orElse("default value 2");
...
As for the .properties file issue, I used the SDK example philosophy and used a myMethod and myMethodImpl, as used with the multiply method. My .properties file was linked to getRawData, so I just had to add a @KeywordArgs(names = { "arg1", "arg2",...},types = { String.class, String.class,String.class,...})
on top of the getRawData declaration in the AbstractScriptModule file.
Thank you for your support !