Is it possible to pass variable from gateway scope to client scope?

I have a GW timer script that is calling a script library function which updates a module variable defined within the library itself. For example, the GW timer calls shared.library.updateModuleVariable():

var = {}

def updateModuleVariable():
    var = {'Hello': 'World'}

From the client, I see the value of shared.library.var equal {}, but in the gateway scope it’s the hello: world key:value. Is there any way to “share” this into the client scope?

No. The easiest way is to use a tag for storing data a client will need to access.

I suppose you could also implement a message handler that lets clients query for the value of a key in that dictionary too. But understand that this globally defined variable has a complicated lifetime that only @pturmel has a chance of explaining :stuck_out_tongue:

1 Like

I figure that would be the answer :frowning: I was hoping to avoid storing a large dictionary inside a tag… but let’s see how it goes

Yeah that’s a little yucky because you have to make sure it’s JSON/Document before writing it to the tag.

I’m trying to avoid jsonEncode/Decode, so I was thinking of just writing the dictionary as a string to a string tag. The client only needs to read it every so often, so I thought using jsonDecode to decode the dictionary as a string at the client would reduce performance impact? rather than encoding to store in tag, then decoding to read it and perform other python actions on it.

Edit:
Nope, that doesn’t work for more complicated dictionaries… the string just gets un-decodable :frowning:

JSON is a string-based encoding for dictionaries that the client should be able to decode :stuck_out_tongue:

Hah! And on this topic I sometimes confuse myself.

As to the question, I would weigh use of a tag against use of a gateway message broadcast to a client-side message handler. You say the dictionary is large, so I'd probably do the latter. Smaller content, or content that must persist in the gateway through a restart, and I'd favor the tag.

Some other considerations to balance in your decision:

  • A broadcast message will need to go to all live clients (in the project). A tag will only be transmitted to the clients actually subscribed to it. So, less network traffic for the latter.

  • A broadcast message will lower the latency for clients if the need for this info is random-ish. (The data will likely be on the client side already.) Tag bindings have some startup latency, or if using scripted reads, latency every time.

  • At new client startup, the dictionary in client scope won't exist or will be empty. A gateway request for the current value would be needed in a client startup script. If the information is always needed at client startup, a client tag bound to a gateway tag would be quicker, I think. And would keep the data fresh in the client for random-ish use.

  • If the dictionary is large but the changes small, a broadcast message could send just the changes. Tag transfers will always be the whole thing.

1 Like

Well, I tried jsonEncode to write to a tag and I got a max recursion level reached error… I guess I’m not going down that path then!

I’ve instead copied the dictionary into a project script library local variable and have been fighting with it getting it working. I had the script to update the dictionary set to run within a gateway timer script, and then the tags were calling runScript to call a function within the same library to retrieve this dictionary and filter it, however the function was always reading the dictionary as blank. I finally realised that the tag and gateway scripts must be different scopes. I moved the populating script over into a clock signal tag (flip-flip boolean) and now it’s working perfectly, however this brings me to a question:
How many python scopes are there, and what are they?

  • gateway scripts
  • tag scripts
  • client scripts
  • … any more?

In Python’s memory model (along with just about every language on the planet), variables are local to the machine running the code. Python running in the designer has variables local to the designer. So too with Vision clients and the Gateway. Values are passed between different computers by messages. Tag value distribution is done with messages. Tag writes are messages. Add-on Modules talk between gateway and clients with messages. Users can use explicit messages and handlers. (Vision clients poll for the latest messages at a fixed pace.)

This is then multiplied by the environment in which a script runs. In the Designer and in Vision clients, there’s just one script environment, encompassing the current project’s script modules, any non-overridden inherited script modules, java classes reachable by the local JVM, and the python modules distributed by the gateway. In the Gateway, there’s a separate script environment for every leaf project, and one more for the gateway scripting project.

In any of these jython script environments, a script module’s top level is executed by jython’s import machinery the first time any event asks for something (function or variable) within it (after startup or a script reload).

My first reply in this topic presented the solution as a choice between a gateway tag and some kind of explicit messaging. I wasn’t just speculating. Those are your only two choices.

Yep, you’re right, I am merging two independent but related issues that I am trying to solve:

  1. use the dictionary within tags and,
  2. use the same dictionary within the client (as this topic was created for)

I was still trying to figure out (1) with scoping and it didn’t register that I would be seeing the same scoping issues for both problems. My reply above however was related more to (1) rather than (2), but I see now that I still have my (2) issue to solve which, considering option “gateway tag” is out due to json size, it appears that my only remaining option will be to use messaging.

At this point, I would normally point you at system.util.getGlobals(), as that is the dictionary you would use to share items across the gateway, and persist them through script restarts. By you tagged this topic with v8.0, and that function is broken in v8.0. You really need to upgrade to v8.1 for a solid solution. (Search for me and getGlobals here on the forum.)

Whoops, I didn't realise there was a tag for 8.1; we're using 8.1.0, but I did try using getGlobals() initially, but it didn't appear to carry the value across different scopes.. maybe it was fixed later than 8.1.0?

It should work across the gateway. Not from gateway to client or designer.

Now I’ll have to test that. /:

getGlobals() is not synced across scopes and it’s also not synced to the backup in redundant pair.

So messaging it is I guess

Well, shoot. I guess I’ll be bringing my LifeCycle module to v8.1 after all.

Say again? What does (did?) this module do?

Are you using a database that supports JSON columns? If so, utilize that. On your gateway timer, update the db with your new values and have your clients poll it.