Gateway Events in 8.0.15 using Inheritance

I’m not exactly sure about the scenario you’re describing. You should be able to mock this up trivially and find out.

Don’t define any gateway event scripts in your inheritable project. Just the script module(s) with all the functions you expect to need everywhere. In a leaf project, establish the gateway events you need. I recommend those events contain a one-line function call into a script module, so that you avoid the legacy scoping issues in gateway events. The function called from the event should be in the same leaf project, so that edits to its functionality don’t cause scripting restarts in other projects.

I also recommend not moving script modules to an inheritable project until/unless they are well tested, also to minimize scripting restarts across your gateway.

We have gone both ways, in 7.9 the shared space seemed much more valuable than in 8 due to a lot of new features including diff-able code and the import/export is a lot easier. Lately we are reversing back to keeping more code in the project to keep testing simpler for global project changes.

Ryan,

Understood. I liked the shared space in 7.x world much better. Inheritance as it exists in 8.X is quite confusing. Another question that keeps popping into my head, why should a gateway event be associated with a “project” it should run in one and only one place, the gateway.

Frank

You can pretend new inheritance doesn’t exist and treat it like 7.9 by making a project called “global” and making every other project inherit from it. Voila, now it’s just like 7.9.

(which is exactly what happens to a 7.9 gateway on upgrade)

1 Like

Kevin,

Very good point! Thanks for all the great information guys.

Regards,

Frank

Hello,

I work with “franklepkowski” and we have setup the inheritance as you suggested above.

“MES_global” is at the very top, it is inheritable. It only has “project library” scripts in it. So that every project that inherits from it will have access to these scripts.

“MES_project” this inherits from “MES_global”. It has screens in it, nothing else.

We have been getting reports from the customer that SOME of their clients are very slow on the screens. So I did some testing on a machine that is NOT the gateway, but is in the same subnet. The network connection between them is 100 megabit.

I created a function in the “MES_global”:
def zPrintThis():
print str(system.date.now()) + " I am inside the function zPrintThis()"
return;

Over in the “MES_project”, I open the SCRIPT CONSOLE and execute this script:
print str(system.date.now()) + " BEFORE call to global function"
MES_global.zPrintThis()
print str(system.date.now()) + " AFTER call to global function"

My output surprised me. Here is the output the VERY FIRST time I executed in the script console:
Mon Sep 20 9:40:33 BEFORE call to global function
Mon Sep 20 9:40:37 I am inside the function zPrintThis()
Mon Sep 20 9:40:37 AFTER call to global function

That tells me it took about 3 seconds to get the “project library” loaded from the “MES_global” project down to the “MES_project”.

I executed the same script in the script console a second time by hitting the button:
Mon Sep 20 9:55:26 BEFORE call to global function
Mon Sep 20 9:55:26 I am inside the function zPrintThis()
Mon Sep 20 9:55:26 AFTER call to global function

It took “no time at all”.
This perplexed me. So I copied the script console script to a notepad file. Then I shutdown the designers. I opened the designer again for “MES_project”. I executed the script console again.
Same result, the very first time I executed, it takes about 4 seconds to execute.
The second time it is very very fast, all within the same second.

My question is why is this happening?
It seems as if the inheritable Project Library in “MES_global” project does not go down to the “MES_Project” until you try to execute something in it. Instead of being “right there” in case something
calls a script within it.

I am finding that in “MES_project” in a button script, every time I run it is like the very first time. It takes a few seconds to “download” the scripts. This happens every time I press the button, even though the screen has been visible the entire time. I can press the button 5 times in a row and all 5 times there is the 3-4 second “load time”.

What am I doing wrong here?

Thank you

Projects are combined into a flat “effective” project and fully downloaded by clients and designers. There’s no on-demand downloading or resolution of inherited resources, etc…

Does your MES_global module have a bunch of other functions and/or logic defined at the top level? It sounds like the first compilation/run of the module when you access it is slow for some reason.

I have been doing some more testing.
I think you are partially correct, it makes sense to me that their would have to be some type of compile/interpret when you call a function in the PROJECT LIBRARY.

However, in further testing I have found the following.
On the GATEWAY in the “MES_global” project I have several modules that all call each other.
When I go to the script console and call a function it takes about 6 seconds to execute.

Now when I go to another machine that is not the gateway, open up the Designer and
then the script console and execute the same function it takes about 25 seconds.

This seems like a lot of “compile time” to me. However in this instance I am calling a module that calls 1 other module only, so MAYBE that has something to do with it.

Then I just copied the actual scripting from that function to the script console.
It ran in 6 seconds!

Here is my setup…
In my “MES_global” project. Under Project Library I have a PACKAGE “MES_gMod” .
Under there I have a MODULE named “Run”. This is where all my actual functions are.
So calls in the script console look like this:
MES_gMod.Run.xxxxfunctionname

“MES_global” in Project Library in a module called “Run” I have about 50 functions…here is one
Here is an example from “Run” module of one of the functions:

def getLotCustomPropertyNameValueDesc(sProcessName, sLotName):
	#It will return a list:    Name  Value  Description			All are type STRING
	try:
		oArtifactDictionary = {}
		oDuplicateName = {}
		oReturnList = []
		ds = None		
				
		#call Sepasoft function to get CUSTOM PROPERTIES
		ds = system.mes.getLotInfoByName(sLotName, False, True)
		if ds is not None:
			#Cycle through each Material Lot that was created by each process segment			
			for row in range(ds.rowCount):					
				cpDS = ds.getValueAt(row, 'CustomProperties')								
				for cpRow in range(cpDS.rowCount):											
					sCPName = cpDS.getValueAt(cpRow, 'Name')
					sCPName = sCPName.upper()																	
					sCPValue = cpDS.getValueAt(cpRow, 'Value')					
					sCPDatatype = cpDS.getValueAt(cpRow, 'ValueDataType')
					sCPDesc = cpDS.getValueAt(cpRow, 'Description')									
					oReturnList.append([sCPName, sCPValue, sCPDesc])
	return oReturnList;

In the CLIENT script console when I call this function:
MES_gMod.Run.getLotCustomPropertyNameValueDesc(sProcessName, sLotName)
It takes about 25 seconds. (Remember on the GW it took 6 seconds.)

However, if I copy all the script out of the getLotCustomePrpertyNameValueDesc function
and paste it into the script console, then it takes about 8 seconds.

I am confused!!!
I would think they would take PRETTY much the same amount of time.
They are DRASTICALLY different.

Thank you for any insight as to WHY the times are so different.

Script modules are executed (not compiled) all the way through at the top level the first time anything within them is referenced. This is the same behavior as CPython when importing a module. A function def is executed to define the function that you will later call. All of the defs will execute in the script module, however complicated, before the lookup of a function name can happen, and subsequently be called.

Experiment with split-up script modules if latency to first execution matters to you.

You probably ought to add logging at the beginning and end of each of your script modules (at the top level–not in a function) so you can see this behavior and the time it takes. And note the results in the gateway, too…

I have done some more testing to narrow down where the SLOWDOWNS are occurring.

I have a VPN connection to my customer that is SLOW. So I can really see anything that is NOT optimal.

I have narrowed it down to system.tag.readBlocking() in scripting.

In my modules, I check to see if CERTAIN tags have CERTAIN values in them to decide if
I need to call other functions. Well, the first time I call a function all these tag values are
set to values that cause me to call other functions. And so on and so on.

I did this because I have data that only changes about every 60 minutes.
So, if an OPERATOR hits a button and it has been LONGER than 60 minutes, then I need to
go refresh some tags. If it has only been 22 seconds since the last refresh, then chances are
that the values have not changed.

Well, some screens I do not have this feature.
So I always read the tags.
But it is not as easy as reading all 44 tags in one line of script code.
I read a tag, based on it’s value then a read some other tag. So on and so on.
So I basically have to read each tag ONE AT A TIME using system.tag.readBlocking().

Well, over a VPN connection to the GATEWAY, this is slow.
I think each time a client calls system.tag.readBlocking() it has no choice but to go all the way
to the GATEWAY and get the value. That is a “round trip” on the network.
EVEN if the tag is on a 60 second TAG GROUP direct update rate.
If the client reads the tag with system.tag.readBlocking(), then 3 seconds later it
does the exact same system.tag.readBlocking() with the same tag…It must go all the way to the GATEWAY.

So I think I am concluding that reading tags in a client through scripting is WAY slow over a slow connection. HOWEVER, when I just put a tag on a screen in a TEXTBOX, I do not see this slow down.

hmmm, what to do.

Tag bindings are asynchronous by nature; the gateway notifies clients it has new tag updates at whatever rate it generates them, and the bindings react accordingly.

There are a lot of potentials ways to obviate your performance issue.
One of the most obvious would be to simply read all the tags at once, pack them into some data structure, then extract the results at the points you need to. Even a useless tag read, batched, is going to be faster when the limiting factor is network latency.

Another option would be to put ~all your tag reads into an asynchronous-capable mechanism (Vision client tags can have expressions, which would allow you to read, though not write, transparently from ‘real’ tags), and then just refer to those values at your scripting ‘checkpoints’.

Bindings are subscriptions. So every time your client polls for tag changes, you get just the changes. To leverage this in your situation, use a client tag change event script subscribed to all of the relevant tags. Cache the values in a script module top level dictionary. Have the rest of your scripts read from the cache.

When you say “tag binding” are you refering to a TAG on a screen that is in say a TEXTBOX?

So let’s say that I implement something that executes in a script module.

  1. I read all the tags using system.tag.readblocking() with say 50 tags into an DICTIONARY.
  2. I do some scripting based on those values, maybe some SQL QUERIES or something
    that takes a few seconds.
  3. In the mean time, a tag value could have changed on the gateway.
  4. Now in my script I look at my DICTIONARY for a tag value…but it is stale! It is actually the wrong value!
  5. This is the very reason that I did NOT read all my tag values at the very beginning and process
    based on those values. Because while I am processing, some values could change!
    Therefore I have to read the tag values in the script logic at the time I am making decisions in script.

What I THOUGHT would happen in scripting would be something similar to what a screen does.

  1. scripting would do a system.tag.readBlocking().
  2. Ignition would create a tag “somewhere on the CLIENT”. Set up it’s subscribe to the GATEWAY. So that it could get updates, etc.
  3. The very first system.tag.readBlocking() might be slightly slow since it has to do all this work above.
  4. Then I “assumed” that subsequent calls to system.tag.readBlocking() would only go to this
    tag “somewhere on the CLIENT”, that is getting asynchronously updated behind the scenes.
  5. This is NOT what is happening. Reading tags through scripting is NOT the same as however
    the IGNITION client shows tags on a screen in a TEXTBOX.

hmmm

No. system.tag.read*() of any kind is a snapshot with a gateway round trip. The only subscriptions are bindings and client tag change scripts.

I'm confused, could you clarify what functions are in the parent project and what functions are in the leaf ?
Context: I've been tasked with 'refactoring' 5 similar projects into 1 global project that will be inherited by the 5 children, so that each of them only needs minimal resources that are only relevant to that specific project (coordinates, devices, etc.).
Scripts should not require any override as every project will have the same behavior.

Parent projects should have scripts that you expect to never need any edits. That is, test and develop in the leaf, even if you need to duplicate some functionality, until the functions are stable. Then, on a day when a multi-project disruption is permissible, move the well-tested functions into the parent project, then removing from the leaf projects.

Editing and saving a leaf project only disrupts gateway scripts for that one leaf. Editing and saving a parent project disrupts gateway scripts for all of its children. This can be very bad in a production system.

If you are doing all of your development in a suitable dev environment, deploying to production in appropriate time windows, this approach is less important. I still recommend it even then, as emergency edits to production may still be needed.

2 Likes

Also, gateway event scripts are a single resource. If any leaf needs an extra gateway event, the entire resource gets overridden. Just don’t allow inherited events. At all.

Alright, so in the end they should be on the parent project.
In the meantime, minimize changes on a runnable project because it’s disruptive to the user.

I’m actually in the process of setting up a pseudo-replica of a real project, to be able to test things without bothering anyone.