I am using com.inductiveautomation.ignition.common.script.ScriptContext#defaultProject() to get the project from the client calling a script, so that I can make sure I read the resource out of the right project.
What I am running into is that if I run this in the Gateway scope (A Perspective Button) this seems to work fine. However when I try this out of the client scope (Script console) I am getting a null in response?
I am following the recommendation from this forum post, however potentially I am missing something here.
Here is a link to the script within the repo with the project, the specific use of the function is on line 38
Gotcha, is there another way to get a specific project resource in a called script like this? Or would it just be to pass the project name into the function when it's being called?
But that said, I don’t know if it even matters. There’s zero chance the code you linked to will run in any client scope. It’s importing and using things that only exist in the gateway. Is this something called by an RPC from the client or am I missing something?
Yes it's called via RPC from com.bwdesigngroup.ignition.configmanager.client.scripting.ClientScriptModule, I was about to call that out when you said it was never client scope. There is an RPC to the com.bwdesigngroup.ignition.configmanager.gateway.scripting.GatewayScriptModule that is reading the requested resource, will deserialize the json into a PyObject and return it back to the client (When its done). The resources are project based, not gateway based
For now you’ll have to pass the project name as a parameter. If we end up setting it on the ScriptContext you’ll still have to pass it as a parameter in the RPC call, but you could omit it in the client script function and retrieve it from the ScriptContext so the caller doesn’t have to specify it.
The gateway hook’s RPC method is given client session and project information. If you need it in your implementation, you should be creating appropriate RPC instances carrying this information (from a constructor, presumably). Probably should cache them, if they carry any other state.
Maybe I misunderstood this, but I took this as "Get the ScriptContext before the RPC, and it should have the project info client side"
However I tried that, and it yielded the same result, I think I understand adding the project info to the RPCs constructor, but I don't know if I am understanding where I get the project info in the first place?
I think Phil is suggesting that your GatewayScriptModule should take the session and projectName provided by GatewayHook::getRPCHandler as constructor parameters.
(and these multiple GatewayScriptModule objects could be potentially cached, if needed).
Yes, getRPCHandler should be creating handler instances that “know” what session and project they are serving. So methods on the handler can access the handler’s own field with that information. No use of ScriptContext at all.
Note that if you cache these, you should hold a weak reference to the session.
In my GatewayHook I have the following to add my script module,
@Override
public void initializeScriptManager(ScriptManager manager) {
super.initializeScriptManager(manager);
manager.addScriptModule(
"system.config",
scriptModule,
new PropertiesFileDocProvider());
}
I am passing it the constant instance of the ScriptModule. I interpret this as making a singular instance of the module accessible under system.config and how would that work if I created a different instance based off different projects?
If I was creating a new instance of the scriptModule based off the project being called, would I just assume that the one added in initializeScriptManager is just being used for the designer documentation, and the one I have instantiated is doing the actual execution of the script?
What I was thinking about doing
@Override
public Object getRPCHandler(ClientReqSession session, String projectName) {
return new ScriptModule(session, projectName);
}
initializeScriptManager is called separately for every leaf project in the gateway, and for the gateway scripting project, to support scripts running in the gateway. Each call should get a customized instance if there is different behavior based on the project. Every script environment gets its own tree under system.*.
And no, your script module cannot be your RPC handler. That way lies madness.
Keep in mind that your gateway scripts are not called through the RPC handler–the RPC handler is only getting referenced if your Vision client or the designer invoke it.
Also, initializeScriptManager can be called to set up a script environment for other modules, too, like devices or custom tag providers. So you might encounter a ScriptManager without a project name.
Every public function of your script module gets installed in the python module tree. This doesn't permit you to have differing code between pure-gateway calls and RPC calls, which I would expect any non-trivial library to need.
Hmm. I haven't had to do this, but you can use the script manager you are given to run system.util.getProjectName(). If you really need to, you can reflect ProjectScriptManager to access the private defaultProject. (Probably shouldn't...)
Hmmm I will try to figure out how to do this and report back. My guess is it lies somewhere within ScriptManager::runFunction, but now I guess I get to dig into executing Python code through Python a little better.
Create a localsMap. Lookup system. Look up util in system. Look up getProjectName in util. Pass that to runFunction. Use py2java on the return to get your string.
hmmm not having a lot of luck digging around in the python objects.
This is what I currently have for trying to dig around in it, do you know of a good source of documentation for accessing python content through Jython? Everything I am seeing on google keeps taking me to the Jython documentation from a python standpoint, not necessarily the Java standpoint.