Vision and tag design help - 6 cells of real[250] from plc

Getting an error that runPrepUpdate would not execute in the script console.

Then I ran it on a button from a vision client and got this error:
'RunPrepStmt': The current user does not have the required roles for this operation.


Went to project properties, set the vision permission to access the legacy database to enabled and specified the role. Might have to circle back to this to designate authenticated, not certain.
Can do inserts from the button, but not the script console.

The script console with the same code exactly that works on the button on the client, throws a generic error that it can't execute the same query and data.

Decided to write the value in a script to a table component instead of to a db.

I don't use Vision much.
Is there a table feature turn my two columns into the below or do I have to make each column in the script?
I am watching the videos over again and looking in customizers. I only see word wrap.


I scripted it successfully!
I am not sure if I should use a table or a power table.
I made both and wrote to both.
Just happy I could figure out how too script it.

How do I get rid of the header spaces on either table?
When I open the page, I don't need anything to show. Might show the index numbers and 0.0 in the value columns.
image

Is the main use case for an OPC read over a tag mainly for reading large chunks of data from arrays?

Or is it good for reading in individual values and booleans when using Vision?
Are there good benchmarks for when to use OPC reads vs tags?

Are system.opc.writeValues() calls more robust than tags flat-out?
Or are there tradeoffs?

No, I wouldn't say that. I would say that the main use case is when you want to read a value from a device that is needed only for one specific instance and isn't needed to be global to rest of the project.

Another popular use case is reading a value on "demand". Tags are subject to scan classes and that can sometimes lead to timing headaches. system.opc.readValues() is not beholden to the tag system and so can operate on an as needed basis.

It can be used to read individual values, though I wouldn't confine it to only Vision.

Typically you would use these functions when you don't want to be subject to the restrictions imposed by the tag system.

Let me explain a bit further. When you execute a tag read (system.tag.read*) you get the value of the tag. That, however, isn't necessarily the value in the device at that instant. Because, the tag system is divided into tag groups with different scan times. When you want to insure that you have the value of some memory register in the device at that instant, then you use system.opc.read*

I don't think so. There are tradeoffs, though I am less sure of what these are.

2 Likes

I mainly use it when I have timing constraints that aren't satisfied by an OPC tag subscription. (OPC tags that are on a pace do not get their values delivered in any guaranteed order.) Thus, a tag change event monitoring a trigger tag (subscribed) will then use system.opc.readValues() to read everything else from the PLC that the trigger indicates is new. (This is what a SQL Bridge Transaction Group does when set to OPC read mode.)

I also use system.opc.readValues() in cases where the PLC is using a ring buffer array to collect high-speed data, and Ignition monitors a head pointer into that ring buffer. Ignition can bulk read from the correct (varying) subscripts as the PLC stuffs data into the ring without the overhead of subscriptions.

2 Likes

When do you like to use system.opc.writeValues()?

When I have handshakes where Ignition has targets in the PLC that are write-only (from Ignition). OPC subscriptions (or other polling) are unnecessary for such handshakes and just waste bandwidth.

I am having a little trouble with my scripting.
I got this to work.

I think my For Loop could use work, or at least I believe there to be a way to get all those values better.

cellData1 = ["data1"]
cellData2 = ["data2"]
cellData3 = ["data3"]
cellData4 = ["data4"]
cellData5 = ["data5"]
cellData6 = ["data6"]

cellEnable1 = True
cellEnable2 = False
cellEnable3 = True
cellEnable4 = False
cellEnable5 = True
cellEnable6 = True

cell_data_list = []
baseCellData = 'cellData{}'
baseCellEnable ='cellEnable{}'

for [i,j] in [[cellEnable1,cellData1 ],[cellEnable2,cellData2],[cellEnable3,cellData3 ],[cellEnable4,cellData4],[cellEnable5,cellData5 ],[cellEnable6,cellData6]] :
    
    if i:
    	cell_data_list.append(j)
    	print i, j

print(cell_data_list)

I am going to append large lists of data to new rows in a list that I am exporting.
I only want to append the data from enabled test cells.

I want to know a better way to iterate through the numbered cells similar to how I can already iterate through tags using f strings.
I spent a couple days off and on trying to figure it out, hard stuck on figuring the better way.
I did get it to work that other way at least.

I mean, you can refactor the code you have here to this:

cellData = ['data1','data2','data3','data4','data5','data6']
cellEnables = [True,False,True,False,True,True]

cell_data_list = [j for i,j in zip(cellEnables,cellData) if i]

print cell_data_list

But that seems very simplistic. I suppose I don't totally understand exactly what you're trying to achieve?

1 Like

Zip and an If, that is so simple when you show it to me.
I struggled to think of that.

Each string labeled data# represents a list of 250 lists of the enumerated index and test value.

I am not sure yet how to keep track of which cell it came from, but I think I can enumerate and zip 3 things.

Are you appending data from each "row" by column? As in the same cell from each of the 250 rows? Or will each row have it's own set of cells from which to append data?

1 Like

I think I would say each row will have a set of the cells.
I append the data to the rows.
I have the rows being appended to values portion of the dataset.
I export the dataset one of the pages in workbook.

valuesIn2 = system.opc.readValues(server, pathList2)
#print valuesIn2


cellData1 	= [ [i, round(qv.value,4)] for i, qv in enumerate(valuesIn2[0:240],start=1)]
print  'cell1', cellData1

cellData2 	= [ [i, round(qv.value,4)] for i, qv in enumerate(valuesIn2[240:480],start=1)]
print  'cell2', cellData2

I make a dataset of rows containing 10 columns of meta data, then 240 columns of samples for each of 6 testing stations/cells, but only the enabled ones.

So, assuming that ds is a pyDataSet and enabledCells is a list of the statuses for the work cells, why wont something like this work:

cell_data_list = [row[col] for col,status in enumerate(enabledCells) for row in ds if status]
1 Like

Trying to wrap my head around what you're doing.

  • Are you also only wanting to read values only from the enabled cells?
  • I think I read it that you want the data in a wide, rather than a tall format, yes?
1 Like

I am working on it. I think I can get it with that piece that last piece LRose gave me.

My status unstuck :slight_smile:

But if you want to noodle it for fun, I am this far on my dummy data for testing:

cellEnable1 = True
cellEnable2 = False
cellEnable3 = True
cellEnable4 = False
cellEnable5 = True
cellEnable6 = True

cellEnables = [cellEnable1,cellEnable2,cellEnable3,cellEnable4,cellEnable5,cellEnable6]


cellMeta1 = ["cell1Meta{}".format(i) for i in xrange(0,3)]
cellMeta2 = ["cell2Meta1"]
cellMeta3 = ["cell3Meta1"]
cellMeta4 = ["cell4Meta1"]
cellMeta5 = ["cell5Meta1"]
cellMeta6 = ["cell6Meta1"]
cellMetas = [cellMeta1,cellMeta2,cellMeta3,cellMeta4,cellMeta5,cellMeta6]


cellData1 = ["cell1sample{}".format(i) for i in xrange(0,3)]
cellData2 = ["data2"]
cellData3 = ["data3"]
cellData4 = ["data4"]
cellData5 = ["data5"]
cellData6 = ["data6"]
cellDatas = [cellData1,cellData2,cellData3,cellData4,cellData5,cellData6]


cell_meta_list = []
cell_data_list = []
# keep only enabled datasets
cellList = [[i,j,k] for i,j,k in zip(cellEnables,cellMetas,cellDatas) if i]
print 'cellList',len(cellList), cellList

#metaValues = [ 
print 'j',  [ j for [i,j,k] in cellList]
metaValues= [ a for [i,j,k] in cellList for a in j]
dataValues= [ a for [i,j,k] in cellList for a in k]
datasetExportData=[]
datasetExportData.extend(metaValues)
datasetExportData.extend(dataValues)
print 'datasetExportData',len(datasetExportData),datasetExportData


datasetExportHeaders = ['testHeader{}'.format(i) for i in xrange(16)]				#for sublist in cellList for i in sublist ]
print 'headers', len(datasetExportHeaders),datasetExportHeaders


datasetExport	= system.dataset.toDataSet(datasetExportHeaders, datasetExportData)


#end=time.clock()
#print end-start
datasetOut=[]
fileSuffix	= system.date.format(today, "yyyyMMddHHmm")
fileName 	= 'Test' +fileSuffix+'.xlsx'
ss 			= system.dataset.toExcel(True, [datasetExport, datasetOut], sheetNames = [' Test','Sample Data Grid View'])


filePath = system.file.saveFile(fileName)
if filePath is not None:
	system.file.writeFile(filePath, ss)

Each row has the cell meta data in workbook cells, then the samples in workbook cells.
Then the next row the same, but for the next testing cell.
There are 6 testing cells.
Probably confusing to call them testing cells, they are like testing stations.
Each sample of the test has a cell in the workbook.


I will be slow to get back to it, I keep having an issue where my gateway hickups and the DHRIO connections drop and don't automatically reconnect.

Thanks guys!

Now that I got the dummy variables working, I was able figure out a strategy for my script.

Read OPCs
Read Tags
Create data rows
Calculate meta values
Create meta rows
zip the meta and data to a list of rows
zip the enable with the new rows
create the final rows eliminating rows that aren't enabled
Export

It seems so obvious now.
I was really stuck, thanks for the help.

Is there a better way to read in component properties?

propTests = event.source.parent.getComponent('Text Test').text
propSets  = event.source.parent.getComponent('Text Set').text)

You could do it dynamically if you have many components of the same type and you want the same property from all of them.

[component.text for component in event.source.parent.getComponents() if isinstance(component,'PMITextField')

You can find the type of your component with type() if needed.

1 Like

I think I did well.
Dunning Krueger says I wouldn't know it if I didn't do well.
Last year, someone said my code only worked because of a bug and helped me haha.
So I hope that is not the case now.
I wanted a really small footprint so I avoided defining functions.

propsValues is a list of 6 rows of strings.

#Process Tags
cells			= [['Cell{}'.format(i)] for i in xrange(1,7)]
todays 			= [[str(system.date.format(system.date.now(),'yyyy/MM/dd'))] for i in xrange(1,7)]
testStarts		= [[str(system.date.format(tagValues[0].value,'HH:mm:ss'))] for i in xrange(1,7)]
testEnds		= [[str(system.date.format(i.value,'HH:mm:ss'))] for i in tagValues[1:7]]
seconds			= [[system.date.secondsBetween(i, j)] for [i,j] in zip((tagValues[0].value for i in xrange(1,7)),(i.value for i in tagValues[1:7]))]
durations 		= [['{:02d}:{:02d}:{:02d}'.format(i*1//3600,i//60%60,i%60)] for [i] in seconds]
dW				= [[round(i.value,2)] for i in tagValues[7:13]]
wW				= [[round(i.value,2)] for i in tagValues[13:19]]
zW				= [[round(j-i,2)] for [i,j] in zip((i for [i] in dryWeights), (i for [i] in wW))]
ratio 			= [[round((i/j) if i>0 and j>0 else 0,2)] for (i,j) in zip((i for [i] in zW),(i for [i] in dW))]
rate 			= [[round((i/j) if i>0 and j>0 else 0,2)] for (i,j) in zip((i for [i] in zW),(i for [i] in seconds))] 
enables			= [[i.value] for i in tagValues[19:25]]
enables			= [j for i in enables for j in i]

#Process OPCs
sampleValues	= [[[i, round(qv.value,2)] for i, qv in enumerate(opcValues[240*p:240*p+ 240],start=1)] for p in xrange(6)]
sampleValues 	= [[float(j) for i,j in m]for m in sampleValues]
sampleHeaders 	= ['Sample '+str(i)	for i,j in enumerate(sampleValues[0])]

#Create Dataset
data = []
data = list(zip(cells, todays, testStarts, testEnds, durations, propValues, dW, wW,zW, ratio, rate, sampleValues))
data = [list(i) for i in data ]
data = [[j for i in data[p] for j in i ] for p in xrange(6)]

metaHeaders = ['Cell', 'Day', 'Start', 'End', 'Duration',
'Text1', 'Text2', 'Text3', 'Text4', 'Text5', 'Text6', 'Text7',
'dW', 'wW', 'zW', 'dW/zW', 'zW/Second']
headers = metaHeaders+sampleHeaders

#Filter Data
data = [j for i,j in zip(enables,data) if i==True]

dataOut	= system.dataset.toDataSet(headers, data)

print len(headers), len(data[0])
print 'headers', headers
for row in data:
	print 'filtered data', row

#Export Dataset
today		= system.date.now()
fileSuffix	= system.date.format(today, "yyyyMMddHHmm")
fileName 	= 'Results' +fileSuffix+'.xlsx'
ss 			= system.dataset.toExcel(True, [dataOut], sheetNames = ['Results'])

#end=time.clock()
#print end-start

filePath = system.file.saveFile(fileName)
if filePath is not None:
	system.file.writeFile(filePath, ss)

1 Like