Help with system.tag.queryTagHistory

I am running the following code:
endTime = system.date.now()
startTime = system.date.addDays(endTime, -1)
tubingPath = ‘Athena 1H/WellHead/Tubing’
tubingSet = system.tag.queryTagHistory(paths=[tubingPath], startDate=startTime, endDate=endTime, intervalMinutes=1, aggregationMode=“LastValue”, returnFormat=‘Tall’)

When I select a start time greater than 6 days, or 166 hours it fails. I’ve tried several different variations to select the dates, but I’ve had no luck.
This is the error I receive and for reference, line 14 is where I use the dataset. It acts like anything after 6 days is out of bounds and doesn’t pull the tag history.

Error:
Traceback (most recent call last):

File “event:actionPerformed”, line 14, in

AttributeError: ‘com.inductiveautomation.ignition.client.gateway_in’ object has no attribute ‘data’

Ignition v7.9.9 (b2018081621)
Java: Oracle Corporation 1.8.0_211

Can you post your entire script? The limited section you posted doesn’t indicate what could be the problem.

You may also get more info by printing the type() of whatever object you’re trying to retrieve the .data attribute of - it’s cut off in the error message, but it’s probably a client/gateway exception object.

Check the gateway logs when you run these long queries - you may be getting an error logged there at the same time.

I don’t see anything helpful to me in the logs when I run this script. In an effort to get it working I tried hours. It will work at 166 hours, but not anything greater. Here is the full script, and the full error. Thanks for the help.

tubingDS = []
tubingCount = 0

endTime = system.date.now()
#endTime = event.source.parent.getComponent('Date Range').endDate
startTime = system.date.addHours(endTime, -166)
#startTime = event.source.parent.getComponent('Date Range').startDate
tubingPath = "[MSSQL]"+event.source.parent.getComponent('Dropdown').selectedStringValue + '/WellHead/Tubing'
tubingSet = system.tag.queryTagHistory(paths=[tubingPath], intervalMinutes=1, rangeHours=-166, aggregationMode="LastValue", returnFormat='Tall', timeout=60000)
tubingWell = []
casingPath = "[MSSQL]"+event.source.parent.getComponent('Dropdown').selectedStringValue + '/WellHead/Casing'
casingSet = system.tag.queryTagHistory(paths=[casingPath], intervalMinutes=1, rangeHours=-166, aggregationMode="LastValue", returnFormat='Tall', timeout=60000)

for well in tubingSet.data[0]:
	wellStr = str(well)
	wellName = wellStr[:(len(wellStr)-16)]
	tubingWell.append(wellName)

tubingValue = []
for tvalues in tubingSet.data[1]:
	tubingValue.append(tvalues)

tubingDate = []
for dates in tubingSet.data[3]:
	tubingDate.append(dates)

casingValue = []
for cvalues in casingSet.data[1]:
	casingValue.append(cvalues)
	
tubingLen = len(tubingDate)
tubingDS = []
tubingCount = 0
ovr = event.source.parent.getComponent('Numeric Text Field').floatValue
for each in tubingWell:
	if tubingCount < tubingLen:
		wells = tubingWell[tubingCount]
		valuest = tubingValue[tubingCount]
		valuesc = casingValue[tubingCount]
		dates = tubingDate[tubingCount]
		combined = [wells, valuest, valuesc, dates]
		if valuest < ovr:
			tubingDS.append(combined)
	tubingCount += 1 

header = ["Well Name", "Tubing", "Casing", "DateTime"]
combinedDS = system.dataset.toDataSet(header, tubingDS)
comboDS = event.source.parent.getComponent('rawdata').data
comboData = system.dataset.appendDataset(combinedDS, comboDS)
event.source.parent.getComponent('rawdata').data = comboData
Traceback (most recent call last):
  File "<event:actionPerformed>", line 14, in <module>
AttributeError: 'com.inductiveautomation.ignition.client.gateway_in' object has no attribute 'data'

	at org.python.core.Py.AttributeError(Py.java:173)
	at org.python.core.PyObject.noAttributeError(PyObject.java:930)
	at org.python.core.PyObject.__getattr__(PyObject.java:925)
	at org.python.pycode._pyx25.f$0(<event:actionPerformed>:50)
	at org.python.pycode._pyx25.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:636)
	at com.inductiveautomation.factorypmi.application.binding.action.ActionAdapter.runActions(ActionAdapter.java:180)
	at com.inductiveautomation.factorypmi.application.binding.action.ActionAdapter.invoke(ActionAdapter.java:271)
	at com.inductiveautomation.factorypmi.application.binding.action.RelayInvocationHandler.invoke(RelayInvocationHandler.java:57)
	at com.sun.proxy.$Proxy30.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.mouseReleased(Unknown Source)
	at java.awt.Component.processMouseEvent(Unknown Source)
	at javax.swing.JComponent.processMouseEvent(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.LightweightDispatcher.retargetMouseEvent(Unknown Source)
	at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
	at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
	at java.awt.Container.dispatchEventImpl(Unknown Source)
	at java.awt.Window.dispatchEventImpl(Unknown Source)
	at java.awt.Component.dispatchEvent(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.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
	at java.awt.EventQueue$4.run(Unknown Source)
	at java.awt.EventQueue$4.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)

Ignition v7.9.9 (b2018081621)
Java: Oracle Corporation 1.8.0_211

Can you add
print type(tubingSet)
right before the line that’s erroring:
for well in tubingSet.data[0]:

when it works < 167 it shows <type ‘com.inductiveautomation.ignition.common.BasicDataset’>

when it fails >167 it shows <type ‘com.inductiveautomation.ignition.client.gateway_interface.OptimizedDataSet’>

Ah, okay, that makes sense.
So, there’s some ‘magic’ happening here that makes things more inscrutable.
When you write tubingSet.data[0] in Jython, you’re actually calling, Java-wise, tubingSet.getData()[0] - but, the Dataset interface doesn’t define a getData method - you’re supposed to directly iterate over the rows with a (non-pythonic) sequential getter, like:

for i in range(tubingSet.rowCount): #can also be `tubingSet.getRowCount()`
    row = tubingSet.getValueAt(i, "some_column")

To make things “more Pythonic”, we created “PyDatasets”, which you can convert any dataset into by calling the system.dataset.toPyDataset() function.

Using PyDatasets is the recommended way to iterate over datasets - using the data attribute as you have is relying on BasicDataset's data method, which returns a 2d array (list of lists, if you’re used to Python) of all the data inside the dataset.

The reason you don’t get a BasicDataset when you go over a certain number of rows in the query is that we’re attempting to optimize things. The “Optimized” dataset is automatically used when your query goes over, I believe, 10,000 rows - with an optimized dataset, query results are “streamed” to the client piecemeal, instead of the gateway retrieving all the results, serializing them into a single message, and sending that to the client as one value. Optimized datasets conform to the dataset interface, but lack the data attribute that BasicDatasets have.

1 Like

This worked great, thanks. The explanation makes a lot of sense.