Refreshing Project Library Script or creating bindings with gateway script values

My goal is to read and format data using scripts in the project libary, then load that data into Perspective to create screens to display it. I have been storing the data in a variable in a script, then referencing it from Perspective using an expression binding.

*In script to be updated regularly
robots = projectFuncs.robotsInfo()

My issue is that the scripts in the project library only run under specific conditions such as when they are saved. Currently, I have to resave the script in order to refresh the values, which is obviously not a long term solution. Is there a way to call a script in the project library in order to make it run, refreshing the value of this variable? The other solution I was looking into was defining this variable in a gateway scope script. Is there a way to pull data into perspective from the gateway script?

I'm trying to avoid creating a ton of tags but am not sure this is possible.

No, the top level of a project library script is executed once and only once (pseudo-auto-imported) upon the first reference to it after a save. However, you can create a top level dictionary that you can write into later. (Or lists, or other python collection objects.)

That said, why are you doing this? Please explain your application requirements in more detail.

I'm using ignition as a bridge between a PLC and another device I can communicate using REST commands. In order to make a consolidated display using information from both the PLC and the other device I am using Perspective. The only way that I can get information from the other device is using a variety of "get" commands, at which point I have the information in a python script. Since I need to clean up the data significantly, it seems better to me to use one get request and organizing the data within a script rather than using 25 or so individual HTTP/rest bindings made available in perspective.
Basically, since I already have the data in the script, my preference would be to send it directly to the perspective screen using a expression binding, but I'm finding difficulty refreshing the data in the script.

The typical approach to PLC-ish execution models is a gateway timer event. (Or an SFC, but I despise those.) Perform your efficient HTTP get (via system.net.httpClient, if not already) in the timer event, perform any post-processing, and break the results out into tags for your UI to consume. On a case-by-case basis, use dataset and/or document tags for data that can be consumed whole by a UI.

Ignition is a distributed application, not a one-off machine HMI--don't hobble it by putting control logic in the UI. Drive the UI with tags and database queries. With limited exceptions, have the UI's control inputs go through tags or database updates.

I had already been using a global list to store the data. Looking into the way that global variables worked, I found a post in a different thread about updating global variables and saw that I missed the "global [variable]" line within my function which is why it wasn't updating.
-link to other thread

My script now contains the following code and works correctly.

In perspective, I'm using the "getrobot" function in order to reference different parts of the variable "robots" to display the data. Why would it be better to take all the data and write it to a tag, then create a display using tag bindings instead of just creating the display using expression bindings like the following?

runScript("MiRData.getrobot",5000,[index number],[dictionary key])

"robots" is a list of dictionaries

An expression binding can dereference entries in a document tag much faster and more efficiently that jython can. runScript() and friends have substantial startup overhead. If MiRData.robots is composed of python-standard lists and/or dictionaries, it is directly assignable to a document tag.

You might find this topic useful:

Among other things, it's objectScript() expression function includes a shortcircuit for referencing project library top-level objects without actually launching a jython interpreter. That is, instead of:

runScript("MiRData.getrobot",5000,{path.to.index},{path.to.key})

You could use:

objectScript("MiRData.robots")[{path.to.index}][{path.to.key}]

But this is probably best:

{path/to/MiRData/robotsDocumentTag}[{path.to.index}][{path.to.key}]

Note the use of an expression binding's [] operator for list or map dereferencing.

(You can wrap these expressions with timeMe() to compare performance.)