Transaction Group Efficiency

Hello there,
I have probably a simple problem that is brought on by me not completely understanding how to do things. I’m trying to set up a recipe store and write a function to the PLC. There is a total of 4 PLC’s with 53 recipes in each. Each recipe is an Array of 3023 tags. I have created 2 Block Item transaction group for each recipe ( one to write and one to store ) and they are writing to a MySQL server. I also have the Main Page with buttons to trigger the write and store functions. I only need this to be active once every blue moon when the operator needs to write a new recipe or have to restore one from the backup. The problem that I’m running into is if I enable all the transaction groups the system goes nuts on the ram usage I have 16G allocated to it, and it is still making the garbage collection work overtime. If I disable all of them, it’s stable at about 2G of ram. Am I doing something wrong or is this normal? Is there a way to only have them enabled when the operator hits one of the store or write buttons to do one transaction?

Thanks for any clarity that you guys can provide. I have attached a picture of my Memory Trend with them active and disabled.

Sounds like a transaction group is the wrong tool. I would do this with scripting and the system.opc.* functions.

Thanks for the tip,
Been playing around with the script but I can’t seem to get it to pull the tags. I’m not sure if I have the syntax correct tho.

server = “Ignition OPC-UA Server”
tags = system.opc.browse(opcServer=server, device="[Line 509 E80 Ext]", folderPath=“Line 509 E80 Ext/MainProgram/Prg_Store/Prg_Store[12]”)
for row in tags:
print row.getOpcServer(), row.getOpcItemPath(), row.getType(),
print row.getDisplayName(), row.getDisplayPath(), row.getDataType()


I would change this to either:
tags = system.opc.browse(opcServer=server, device="[Line 509 E80 Ext]", folderPath="MainProgram/Prg_Store/Prg_Store[12]")
or:
tags = system.opc.browse(opcServer=server, device="[Line 509 E80 Ext]")

And see what results you get.

Both ways I still get an error

> Traceback (most recent call last):
>   File "<event:actionPerformed>", line 2, in <module>
> 	at com.inductiveautomation.ignition.client.gateway_interface.GatewayInterface.newGatewayException(GatewayInterface.java:332)

> 	at com.inductiveautomation.ignition.client.gateway_interface.GatewayInterface.getResponse(GatewayInterface.java:491)

> 	at com.inductiveautomation.ignition.client.gateway_interface.GatewayInterface.sendMessage(GatewayInterface.java:268)

> 	at com.inductiveautomation.ignition.client.gateway_interface.GatewayInterface.sendMessage(GatewayInterface.java:263)

> 	at com.inductiveautomation.ignition.client.gateway_interface.GatewayInterface.invoke(GatewayInterface.java:889)

> 	at com.inductiveautomation.factorypmi.application.script.builtin.ialabs.IALabsOPCFunctions.doBrowse(IALabsOPCFunctions.java:13)

> 	at com.inductiveautomation.ignition.common.script.builtin.ialabs.AbstractIALabsOPCFunctions.browse(AbstractIALabsOPCFunctions.java:24)

> 	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

> 	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)

> 	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)

> 	at java.lang.reflect.Method.invoke(Unknown Source)


> com.inductiveautomation.ignition.client.gateway_interface.GatewayException: com.inductiveautomation.ignition.client.gateway_interface.GatewayException: Read timed out

> 	at org.python.core.Py.JavaError(Py.java:495)
> 	at org.python.core.Py.JavaError(Py.java:488)
> 	at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:188)
> 	at com.inductiveautomation.ignition.common.script.ScriptManager$ReflectedInstanceFunction.__call__(ScriptManager.java:438)
> 	at org.python.core.PyObject.__call__(PyObject.java:320)
> 	at org.python.pycode._pyx10.f$0(<event:actionPerformed>:3)
> 	at org.python.pycode._pyx10.call_function(<event:actionPerformed>)
> 	at org.python.core.PyTableCode.call(PyTableCode.java:165)
> 	at org.python.core.PyCode.call(PyCode.java:18)
> 	at org.python.core.Py.runCode(Py.java:1275)
> 	at com.inductiveautomation.ignition.common.script.ScriptManager.runCode(ScriptManager.java:650)
> 	at com.inductiveautomation.factorypmi.application.binding.action.ActionAdapter.runActions(ActionAdapter.java:183)
> 	at com.inductiveautomation.factorypmi.application.binding.action.ActionAdapter.invoke(ActionAdapter.java:284)
> 	at com.inductiveautomation.factorypmi.application.binding.action.RelayInvocationHandler.invoke(RelayInvocationHandler.java:55)
> 	at com.sun.proxy.$Proxy31.actionPerformed(Unknown Source)
> 	at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
> 	at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
> 	at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
> 	at javax.swing.DefaultButtonModel.setPressed(Unknown Source)
> 	at javax.swing.plaf.basic.BasicButtonListener.focusLost(Unknown Source)
> 	at java.awt.AWTEventMulticaster.focusLost(Unknown Source)
> 	at java.awt.AWTEventMulticaster.focusLost(Unknown Source)
> 	at java.awt.Component.processFocusEvent(Unknown Source)
> 	at java.awt.Component.processEvent(Unknown Source)
> 	at java.awt.Container.processEvent(Unknown Source)
> 	at java.awt.Component.dispatchEventImpl(Unknown Source)
> 	at java.awt.Container.dispatchEventImpl(Unknown Source)
> 	at java.awt.Component.dispatchEvent(Unknown Source)
> 	at java.awt.KeyboardFocusManager.redispatchEvent(Unknown Source)
> 	at java.awt.DefaultKeyboardFocusManager.typeAheadAssertions(Unknown Source)
> 	at java.awt.DefaultKeyboardFocusManager.dispatchEvent(Unknown Source)
> 	at java.awt.Component.dispatchEventImpl(Unknown Source)
> 	at java.awt.Container.dispatchEventImpl(Unknown Source)
> 	at java.awt.Component.dispatchEvent(Unknown Source)
> 	at java.awt.KeyboardFocusManager.dispatchAndCatchException(Unknown Source)
> 	at java.awt.KeyboardFocusManager.processCurrentLightweightRequests(Unknown Source)
> 	at java.awt.KeyboardFocusManager$4.run(Unknown Source)
> 	at java.awt.event.InvocationEvent.dispatch(Unknown Source)
> 	at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
> 	at java.awt.EventQueue.access$500(Unknown Source)
> 	at java.awt.EventQueue$3.run(Unknown Source)
> 	at java.awt.EventQueue$3.run(Unknown Source)
> 	at java.security.AccessController.doPrivileged(Native Method)
> 	at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
> 	at java.awt.EventQueue.dispatchEvent(Unknown Source)
> 	at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
> 	at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
> 	at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
> 	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
> 	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
> 	at java.awt.EventDispatchThread.run(Unknown Source)
> Caused by: com.inductiveautomation.ignition.client.gateway_interface.GatewayException: Read timed out
> 	at com.inductiveautomation.ignition.client.gateway_interface.GatewayInterface.newGatewayException(GatewayInterface.java:332)
> 	at com.inductiveautomation.ignition.client.gateway_interface.GatewayInterface.getResponse(GatewayInterface.java:491)
> 	at com.inductiveautomation.ignition.client.gateway_interface.GatewayInterface.sendMessage(GatewayInterface.java:268)
> 	at com.inductiveautomation.ignition.client.gateway_interface.GatewayInterface.sendMessage(GatewayInterface.java:263)
> 	at com.inductiveautomation.ignition.client.gateway_interface.GatewayInterface.invoke(GatewayInterface.java:889)
> 	at com.inductiveautomation.factorypmi.application.script.builtin.ialabs.IALabsOPCFunctions.doBrowse(IALabsOPCFunctions.java:13)
> 	at com.inductiveautomation.ignition.common.script.builtin.ialabs.AbstractIALabsOPCFunctions.browse(AbstractIALabsOPCFunctions.java:24)
> 	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
> 	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
> 	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
> 	at java.lang.reflect.Method.invoke(Unknown Source)
> 	at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:186)
> 	... 48 more
> Caused by: java.net.SocketTimeoutException: Read timed out
> 	at java.net.SocketInputStream.socketRead0(Native Method)
> 	at java.net.SocketInputStream.socketRead(Unknown Source)
> 	at java.net.SocketInputStream.read(Unknown Source)
> 	at java.net.SocketInputStream.read(Unknown Source)
> 	at java.io.BufferedInputStream.fill(Unknown Source)
> 	at java.io.BufferedInputStream.read1(Unknown Source)
> 	at java.io.BufferedInputStream.read(Unknown Source)
> 	at sun.net.www.http.HttpClient.parseHTTPHeader(Unknown Source)
> 	at sun.net.www.http.HttpClient.parseHTTP(Unknown Source)
> 	at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(Unknown Source)
> 	at sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown Source)
> 	at java.net.HttpURLConnection.getResponseCode(Unknown Source)
> 	at com.inductiveautomation.ignition.client.gateway_interface.GatewayInterface.getResponse(GatewayInterface.java:411)
> 	... 58 more

> Ignition v7.9.2 (b2017041315)
> Java: Oracle Corporation 1.8.0_131

Read Timeout means that the request may or may not execute successfully - but the client only gives the gateway 60s (by default) to respond. If you have ~3000 tags in a flat folder that would make sense. You can try increasing the read timeout in Project Properties -> Client -> Timing; but you’ll probably be better off with a pre-generated list of addresses. If it’s something like Prg_Store[12].Drg_Store.Tag[0] it would be easy enough to generate all the item paths with Python string manipulation.

1 Like

Sorry for the delay (lunch time)

I tried your suggestion with increasing the read timeout I went all the way up to 300sec and still did not work. I tried breaking it down to even one tag.

server = "Ignition OPC-UA Server"
tags = system.opc.browse(opcServer=server, device="[Line 509 E80 Ext]", folderPath="MainProgram/Prg_Store/Prg_Store[12]/Prg_Store/DESCRIPTION/String")
for row in tags:
    print row.getOpcServer(), row.getOpcItemPath(), row.getType(),
    print row.getDisplayName(), row.getDisplayPath(), row.getDataType()

But I still get the same error, and as you can see from the picture it won’t be that easy to do each tag.

You really don’t want to browse the device every time you read or write a recipe. I would drag one entire recipe folder, PrgStore[n] above, into a temporary SQLtags folder. Then export that XML and extract all of the OPC item paths offline. Sort, and use that to work out a function that will deliver the list of item paths for a given recipe, in the order you need to match the data from the database. Possibly create a database table just to contain a mapping from location (table & column) in the database to location in your PLCs.

I was hoping to have this fully automated and simple where the operator could just hit a button to store and write. On top of that, I’m new to the SQL environment so not a 100% sure how to accomplish what you’re suggesting.

Yes, that's what I have in mind. The list of tasks I suggested were for a jython script author. (-:

Well, the SQL configurable script is just an option. The point is that the OPC item paths you need to use have substantial similarity from device to device and recipe slot to slot, making them relative easy to construct programmatically with jython string manipulation. That might be your next learning task.

Well, I know a little python, so that has been helping me from time to time, but right now I can’t figure out how to even pull the tags from the OPC. I think my syntax is probably off causing it to timeout on a retrieval. If I can get it to be able to pull one tag at least, I can do the leg work to get the rest.

How else would you retrieve the data from the PLC?

Browsing != Reading.
Browsing, on brands that allow it, is a series of queries to the device that expose what names and data types and array dimensions are present.
Reading and writing need known names (item paths) for each piece of data you wish to read or write. In the designer, Ignition will browse for you (the OPC browser window) and allows you to create SQLtags (the Tag browser window) of the items you find (drag and drop). You don’t need to do this discovery every time.

So doing a system.opc.browse is not a good idea? so you would want to use system.opc.browseSimple?

are you talking about moving a PrgStore[n] into the Tab Browser?

Basically, Ray, what Phil and I are both suggesting is this:
Have either a static construction of OPC item paths (or build them dynamically) such that you can simply pass the item references into a scripting function like system.opc.writeValues. system.opc.write* allows you to pass in any valid OPC item path on the server - it doesn’t have to be a valid tag in Ignition. So you can send a write directly to a PLC tag with something like:
system.opc.writeValue("Ignition OPC-UA Server", "[Line 509 E80 Ext]"MainProgram/Prg_Store/Prg_Store[12]/Prg_Store/DESCRIPTION/String", "New Value For String Tag")

1 Like

Yes. Just to expose all of the OPC Item Paths you will need. You do not need to use system.opc.browse*(). Just system.opc.readValues() and .writeValues().
You will need to become comfortable with Ignition's general structure and scripting techniques. Ignition University is the best way to achieve this.

1 Like

That is what I needed I just got it to work, and I can pull the tags. With this, I will fiddle around and get it to work the way I want. Thank you guys so much for the help and the patients with helping a new guy.