I fail to see the issue…
Do you mean you want to do something like this ?
paths = (
[baseTag.format(i, i, i, counterS) for i in exceptions]
+ [baseTag.format(i, i, i, counterA) for i in nonExceptions]
)
I fail to see the issue…
Do you mean you want to do something like this ?
paths = (
[baseTag.format(i, i, i, counterS) for i in exceptions]
+ [baseTag.format(i, i, i, counterA) for i in nonExceptions]
)
I think you were reading about nested for loops in one comprehension. Both expanded and comprehension forms in this example:
numMachines = 18
exceptions =[2,4,5,8,9,10,11,12]
nonExceptions = [3,6,14,15,16,17,18]
counters = ['NetSCounter', 'NetACounter']
baseNetTags ='[Test]M{machineNum:02d}/M{machineNum:02d}ProductionData/M{machineNum:02d}/{counter}'
pathsNewNet = pathsNewNet=[baseNetTags.format(machineNum=i,counter=k) for i in xrange(1,numMachines + 1) for k in counters]
print 'Comprehension:'
print pathsNewNet
expandedLoopTags = []
for i in xrange(1,numMachines + 1):
for k in counters:
expandedLoopTags.append(baseNetTags.format(machineNum=i,counter=k))
print '----------------'
print 'Expanded Loop:'
print expandedLoopTags
I think I can help clarify this a little, although, this is just my outsider understanding of the issue and why I think the change was a good one.
In 7.9 there were 6 functions that worked with the tag system
This was confusing for some users who expected similarly named functions to behave the same. For instance, system.tag.write()
is an *asynchronous * function, meaning that it does not block the thread on which it is called. However, system.tag.read()
is a synchronous function, meaning that it does block the thread on which it is called.
If you wanted a write operation to block the thread, then you needed to explicitly call system.tag.writeSyncronous()
.
There was no direct function for an asynchronous read and if you wanted to do that then you had to do it explicitly.
I think you can see how this could be confusing. In then end asynchronous or not, all of the functions eventually did one of two operations, readBlocking
or writeBlocking
, just sometimes that was wrapped inside of another function which made the operation asynchronous or not.
In order to clear up some of the potential confusion they removed the abstraction that was in place where it could be.
So we arrived at the now 4 functions with a clear meaning from just the naming convention.
Now when you call these functions you know what you get just from the name. readBlocking
will block the thread, readAsync
will not block the thread.
I don't know if there were any changes under the hood, but even so I feel the name change was a great move.
I call a script at 6:58am to sum net and gross quantities.
Do you recommend to use :
system.tag.readAsync()
and
system.tag.writeAsync()
?
and scenario 2
I have read a ton of things, do math, write to a ton of things
then it is recommended to use as few of blocking as I can?
I am researching when to use asych programming, but I am having a hard time with the abstractness/subjectiveness
What you use depends on what you need to happen in the script.
Off the top of my head, I would say the majority of the time you don't need anything other than readBlocking()
and writeBlocking()
the other functions are there for the times when you do need that special functionality.
I missed some other, probably important infomration in my last post. The biggest change, was the removal of the readAll()
, writeAll()
wrappers. Now the functions all take a list of tagPaths, and values if writing, removing the need for a readAllBlocking()
.
Always minimize the number of times you block a thread.
Scripts which I write that utilize the tag system always follow this pattern:
Don't use 5 calls to readBlocking
when you can use just one.
On the other hand don't go looking for optimizations just to look for optimizations. For instance look at this code snipit
from java.lang import System
values = range(1,100000)
test1Total = 0
test2Total = 0
test3Total = 0
for i in range(10):
start = System.nanoTime()
netTotal = sum([i for i in values if i % 2])
end = System.nanoTime()
test1Total += end - start
start = System.nanoTime()
netTotal = sum(i for i in values if i % 2)
end = System.nanoTime()
test2Total += end - start
start = System.nanoTime()
evenValues = []
for i in values:
if i % 2:
evenValues.append(i)
netTotal = sum(evenValues)
end = System.nanoTime()
test3Total += end - start
print 'Test 1 took an average of {} ms'.format(test1Total/10000000)
print 'Test 2 took an average of {} ms'.format(test2Total/10000000)
print 'Test 3 took an average of {} ms'.format(test3Total/10000000)
I ran this 10 times on my system, here is the output:
>>>
Test 1 took an average of 14 ms
Test 2 took an average of 5 ms
Test 3 took an average of 15 ms
>>>
Test 1 took an average of 24 ms
Test 2 took an average of 4 ms
Test 3 took an average of 27 ms
>>>
Test 1 took an average of 14 ms
Test 2 took an average of 4 ms
Test 3 took an average of 15 ms
>>>
Test 1 took an average of 24 ms
Test 2 took an average of 4 ms
Test 3 took an average of 26 ms
>>>
Test 1 took an average of 24 ms
Test 2 took an average of 4 ms
Test 3 took an average of 28 ms
>>>
Test 1 took an average of 24 ms
Test 2 took an average of 4 ms
Test 3 took an average of 26 ms
>>>
Test 1 took an average of 24 ms
Test 2 took an average of 4 ms
Test 3 took an average of 26 ms
>>>
Test 1 took an average of 23 ms
Test 2 took an average of 5 ms
Test 3 took an average of 26 ms
>>>
Test 1 took an average of 14 ms
Test 2 took an average of 5 ms
Test 3 took an average of 15 ms
>>>
Test 1 took an average of 14 ms
Test 2 took an average of 5 ms
Test 3 took an average of 15 ms
>>>
All three tests have the exact same end result, and it's clear which code executes the quickest, however, when you're trouble shooting an issue are you generally looking for a code change which gives you a 10-20 ms boost? No, you're usually looking for more.
I'm not saying not to worry about performance, I'm saying you have to balance what it's worth spending your time on.
The real problem is that programmers have spent far too much time worrying about efficiency in the wrong places and at the wrong times ; premature optimization is the root of all evil (or at least most of it) in programming.
Good advice. The only times I would say its not true is if the optimization is obvious and easy (ie indexing a db table on a column you know will be used a lot) or if the optimization happens to also align with good design/coding (in this case, using one call to readBlocking instead of 5 - this will make the code simpler and more easily maintainable).
Beyond these two things, its hard to tell what will be a bottleneck that requires optimization prior to it happening.
I ran the test on a subsection of my code and I got 2ms with a bunch of read calls then 0ms with the single read call.
I thought the way the script ran before had caused a crash though.
At the time of running the script, I saw a crash on the gateway.
I don’t know how why the timing of the script would be the same as the crash though if the time was only like 20ms at worst case.
I am a little concerned I revised the script and didn’t really get as much benefit as hoped.
At least my code is now more robust though as even if the tags are not reading correctly the code will run.
ah, I can’t tell if that helps either. Before the script would error out. Then we wouldn’t have any numbers totaled. Now if a tag is bad, we get all the numbers except for the machine that had bad numbers. So we might miss this now.
If you’re worried about missing information about bad tag quality, you can be more specific about how you handle quality.
for example, if you want to filter out tags that don’t exist, but retain tags that do exist but returned a bad quality (note that I don’t have ignition on this computer, so I can’t test if everything works, but I hope you’ll get the point):
tags = [
{
'path': path,
'qval': tag
} for path, tag in zip(paths, system.tag.readBlocking(paths)) if tag.quality.code == 519
]
total = sum(tag['qval'].value for tag in tags if tag['qval'].quality.isGood())
bad_tags = [tag['path'] for tag in tags if tag['qval'].quality.isNotGood()]
I really appreciating seeing how to do multiple lists.
update, figured this out.
I don’t know how to strikeout, but now sorting makes sense.
The two list example did not order them 1-17.
It had one set, then the next.
How is the sorting working?
Once with two lists, the suffix was ignored, and the results were sorted 1-17
Once with four lists, the suffix order was maintained and results were sorted 1-17 of each in order gross, net, sticks, standard volumes
I am happy with the sorting both last time and this time.
I want to know how it worked in both cases though. I expected one of them to be in a different order.
For different counters on the end of paths, they were in the correct order 1-17 with different suffixes.
paths = ( [baseTag.format(i, i, i, counterS) for i in exceptions]+
[baseTag.format(i, i, i, counterA) for i in nonExceptions])
Results were in sequence from 1-17 each with their unique suffix counter in the path
In this example, my paths came out in the order that they were put in.
1-17 gross
1-17 net
1-17 sticks
1-17 standard volumes
If it were just ascii, then I think Standard would be before Sticks.
I used this to check them all.
Below is a snip of some of the results.
numMachines=18
baseGrossPaths='[Test]here/M{0:02d}/Combined Shifts/Gross'
#then the next three base paths
paths = ([baseGrossPaths.format(i) for i in xrange(1,numMachines + 1) if i not in exclusions]+
[baseNetPaths.format(i) for i in xrange(1,numMachines + 1) if i not in exclusions]+
[baseSticksPaths.format(i) for i in xrange(1,numMachines + 1) if i not in exclusions]+
[baseStandardVolumePaths.format(i) for i in xrange(1,numMachines + 1) if i not in exclusions])
paths[0]
paths[1]
paths[2]
paths[3]
paths[4]
paths[5]
paths[6]
paths[7]
paths[8]
paths[9]
paths[10]
paths[11]
paths[12]
paths[13]
paths[14]
paths[15]
paths[16]
paths[17]
paths[18]
paths[19]
paths[20]
paths[21]
paths[22]
paths[23]
paths[24]
paths[25]
paths[26]
paths[27]
paths[28]
paths[29]
paths[30]
paths[31]
paths[32]
paths[33]
paths[34]
paths[35]
paths[36]
paths[37]
paths[38]
paths[39]
paths[40]
paths[41]
paths[42]
paths[43]
paths[44]
paths[45]
paths[46]
paths[47]
paths[48]
paths[49]
paths[50]
paths[51]
paths[52]
paths[53]
paths[54]
paths[55]
paths[56]
paths[57]
paths[58]
paths[59]
paths[60]
paths[61]
paths[62]
paths[63]
paths[64]
paths[65]
paths[66]
paths[67]
Values17by4Spaces = [0]*17*4
Values17by4Spaces
First… may i suggest not copying 67 lines of print ?
print '\n'.join(paths)
Lists keep their order, dicts don’t. If you end up with two different ordering in lists, then you didn’t put the elements in in the same order.
Surround the text with html-ish <s>...</s>.
This in the comment editor:
<s>This is a strikeout test.</s> This is normal.
Becomes this:
thanks very much for the
print '\n'.join(paths)
That will save me some excel time for sure.
I got this list while testing and trying to do the next thing
notGoodTags =[ p for p in system.tag.readBlocking(paths) if 'Good' not in str(p.quality) ]
It didn’t tell me which machines had the bad tags though.
I am working on a situation like this right now:
17 variables = 17 reads
if statement
17 writes of the 17 variables
I think my solution is like:
read the good values to a list
append a list of i values that were good
generate the writing paths based on the good i values
check if the lists match
write from one list to the other
trying this, but it gives me the tags, and I want to get the numbers
numMachines = 18
exceptions =[2,4,5,8,9,10,11,12]
nonExceptions = [3,6,14,15,16,17,18]
baseTag ='[Test]M{0:02d}/M{0:02d}ProductionData/M{0:02d}{counter}'
paths = ([baseTag.format(i, i, i, counter='S') for i in exceptions] +
[baseTag.format(i, i, i, counter='A') for i in nonExceptions])
goodTagsForSticks =[ i for in system.tag.readBlocking(paths) if i.quality.good]
I think could do this with Jordan’s demonstration above several posts.
I was trying to do it within a list comprehension nested for loop, but maybe that is not the way.
Update:
goodSticksTagsMachines =[ p for p in system.tag.readBlocking(paths) ]
goodSticksTagsMachines
goodtags= [k for k in exceptions if 'Good' not in goodSticksTagsMachines ]
goodtags
I got it
this did not work
I’m not quite sure what you’re trying to do… But a few things:
baseTag ='[Test]M{0:02d}/M{0:02d}ProductionData/M{0:02d}{counter}'
baseTag.format(i, i, i, counter='S')
You don’t need to repeat the i
here. you’re using {0:02d}
which means ‘use the first argument’, so they’re all using the first i
you’re passing. Which I expect is the reason you named the {counter}
one.
baseTag ='[Test]M{0:02d}/M{0:02d}ProductionData/M{0:02d}{1}'
baseTag.format(i, 'S')
will do the same thing.
goodSticksTagsMachines = [p for p in system.tag.readBlocking(paths)]
You don’t need a list comprehension here. What this does is take each element in the list, then make another list with them.
goodTagsForSticks = [ i for in system.tag.readBlocking(paths) if i.quality.good]
This will only filter out the tags that have a bad quality. You need to get the value from them if you want the numbers:
goodTagsForSticks = [tag.value for tag in system.tag.readBlocking(paths) if tag.quality.good]
I encourage you to use more descriptive names for your variables, it might help you catch logical flaws like this one
Thanks again Pascal
regarding:
goodTagsForSticks = [tag.value for tag in system.tag.readBlocking(paths) if tag.quality.good]
Is there a good method to get which machine numbers have bad tags from within a single list comprehension, or is it best to make lists and run the check, then append?
I am switching to designating tag when I want the tag, thanks for the recommendation.
In this case though, I really wanted to know the number of the machine, and didn’t figure out how to get it without making a list, checking against it, and appending/writing a new list.
I am reading 17 machine values to write 17 value snapshots
Right now the script:
17 variables = 17 reads
if statement
17 writes of the 17 variables that were read
my solution
numMachines = 18
exceptions =[2,4,5,7,8,9,10,11,12]
nonExceptions = [3,6,14,15,16,17,18]
baseTag ='[Test]M{0:02d}/M{0:02d}ProductionData/M{0:02d}{counter}'
pathsStickValues = ([baseTag.format(i,counter='S') for i in exceptions] +
[baseTag.format(i,counter='A') for i in nonExceptions])
valuesSticks= [i.value for i in system.tag.readBlocking(pathsStickValues)]
baseSticksSnapshots ='[Test]M{0:02d}/M{0:02d}ProductionData/Sticks Snapshot'
pathsSticksSnapshots=([baseSticksSnapshots.format(i) for i in exceptions]+
[baseSticksSnapshots.format(i) for i in nonExceptions])
print '\n'.join(pathsSticksSnapshots)
#system.tag.writeblocking(pathsSticksSnapshots,valueSticks)
updated
valuesSticks= [i.value for i in system.tag.readBlocking(pathsStickValues)]
was
valuesSticks= [i.value for i in system.tag.readBlocking(paths)]
I still have no idea what you’re trying to do
What are ‘sticks’ ? What are ‘snapshots’ ?
pathsSticksSnapshots=([baseSticksSnapshots.format(i) for i in exceptions]+
[baseSticksSnapshots.format(i) for i in nonExceptions])
# could do this instead:
pathsSticksSnapshots = [baseSticksSnapshots.format(i) for i in exceptions + nonExceptions]
sticks/pieces/products
just a name for products in this case
snapshots, well that is a name that some people used to describe a scenario where they wanted put a value that was changing and kind of capture how it was at a certain time like a freezeframe/snapshot is a still picture of life.
probably has to be a really odd thing to explain if English isn’t a native language
It combines idioms and words that formerly were used more frequently.
We end up making new meanings for words that aren’t very common.
Then later, words will be even more confusing because they will have so many more meanings. haha
unless you meant more generally what am I doing?
I am learning a ton and revising some scripts that were causing issues on my gateway so I can add a couple more machines we need and smooth performance both.
Personally, I’d likely use a dictionary to store lookup information. In your case, it’s just one thing to look up, a counter type; however, you can easily add other lookup information as needed.
# Lookup dictionary. Note the ones that are commented out.
machineDict = {
#1 : {'counterType' : None},
2 : {'counterType' : 'S'},
3 : {'counterType' : 'A'},
4 : {'counterType' : 'S'},
5 : {'counterType' : 'S'},
6 : {'counterType' : 'A'},
7 : {'counterType' : 'S'},
8 : {'counterType' : 'S'},
9 : {'counterType' : 'S'},
10 : {'counterType' : 'S'},
11 : {'counterType' : 'S'},
12 : {'counterType' : 'S'},
#13 : {'counterType' : None},
14 : {'counterType' : 'A'},
15 : {'counterType' : 'A'},
16 : {'counterType' : 'A'},
17 : {'counterType' : 'A'},
18 : {'counterType' : 'A'}
}
baseTag = '[Test]M{0:02d}/M{0:02d}ProductionData/M{0:02d}{1}'
baseSticksSnapshots = '[Test]M{0:02d}/M{0:02d}ProductionData/Sticks Snapshot'
# You can create your input and output tag lists in one iteration.
# Just because you can use a comprehension doesn't always mean that you should. ;)
pathsStickValues, pathsSticksSnapshots = [], []
for i in machineDict.keys():
pathsStickValues.append = baseTag.format(i, machineDict[i]['counterType'])
pathsSticksSnapshots.append = baseSticksSnapshots.format(i)
valuesSticks = [i.value for i in system.tag.readBlocking(paths)]
An example of a more complex lookup dictionary, just to illustrate. This particular one is used to parse a bill of materials into something more useful for the assembly line.
bomLookupDict = {
'dcm' : {'desc' : ['DCM']},
'psat' : {'desc' : ['Pressure']},
'harness' : {'desc' : ['Harness']},
'latch' : {'desc' : ['Latch']},
'speaker' : {'desc' : ['Speaker']},
'antenna' : {'desc' : ['Antenna']},
'module' : {'desc' : [
'Assem Frt',
'Asm Frt',
'AssemFrt',
'AsemFrtSht',
'AssemFrtSht'
],
'68223827AI' : {'type' : 'long',
'drive' : 'manual',
'handle' : 'BLACK',
'lock_knob' : 'BLACK'
},
'68429127AI' : {'type' : 'long',
'drive' : 'manual',
'handle' : 'BLACK',
'lock_knob' : 'BLACK'
},
'68429135AD' : {'type' : 'long',
'drive' : 'manual',
'handle' : 'BLACK',
'lock_knob' : 'BLACK'
},
'68501197AA' : {'type' : 'long',
'drive' : 'power',
'handle' : 'BLACK',
'lock_knob' : 'BLACK'
},
'68429077AA' : {'type' : 'long',
'drive' : 'power',
'handle' : 'SZ0',
'lock_knob' : 'BLACK'
},
'68429075AA' : {'type' : 'long',
'drive' : 'power',
'handle' : 'SZ0',
'lock_knob' : 'SZ6'
},
'68429081AA' : {'type' : 'long',
'drive' : 'power',
'handle' : 'SZ6',
'lock_knob' : 'SZ6'
},
'68568935AA' : {'type' : 'long',
'drive' : 'manual',
'handle' : 'BLACK',
'lock_knob' : 'BLACK'
},
'68568939AA' : {'type' : 'long',
'drive' : 'power',
'handle' : 'BLACK',
'lock_knob' : 'BLACK'
},
'68568945AA' : {'type' : 'long',
'drive' : 'power',
'handle' : 'SZ6',
'lock_knob' : 'SZ6'
},
'68429087AA' : {'type' : 'short',
'drive' : 'power',
'handle' : 'BLACK',
'lock_knob' : 'BLACK'
},
'68429091AA' : {'type' : 'short',
'drive' : 'power',
'handle' : 'SZ6',
'lock_knob' : 'SZ6'
},
'68429099AA' : {'type' : 'short',
'drive' : 'power',
'handle' : 'SZ0',
'lock_knob' : 'SZ0'
},
'68429093AA' : {'type' : 'short',
'drive' : 'power',
'handle' : 'TZ3',
'lock_knob' : 'SZ6'
},
'68429095AA' : {'type' : 'short',
'drive' : 'power',
'handle' : 'TZ6',
'lock_knob' : 'SZ6'
},
'68429097AA' : {'type' : 'short',
'drive' : 'power',
'handle' : 'TZS',
'lock_knob' : 'SZ6'
},
'68526619AA' : {'type' : 'short',
'drive' : 'power',
'handle' : 'SKS',
'lock_knob' : 'SZ6'
},
'68429089AA' : {'type' : 'short',
'drive' : 'power',
'handle' : 'SZ0',
'lock_knob' : 'BLACK'
},
'68570689AA' : {'type' : 'short',
'drive' : 'power',
'handle' : 'BLACK',
'lock_knob' : 'BLACK'
},
'68570723AA' : {'type' : 'short',
'drive' : 'power',
'handle' : 'SZ6',
'lock_knob' : 'SZ6'
},
'68570735AA' : {'type' : 'short',
'drive' : 'power',
'handle' : 'TZ3',
'lock_knob' : 'SZ6'
},
'68570737AA' : {'type' : 'short',
'drive' : 'power',
'handle' : 'TZ6',
'lock_knob' : 'SZ6'
},
'68570739AA' : {'type' : 'short',
'drive' : 'power',
'handle' : 'TZS',
'lock_knob' : 'SZ6'
},
'68570741AA' : {'type' : 'short',
'drive' : 'power',
'handle' : 'SKS',
'lock_knob' : 'SZ6'
}
}
}
When I see a dictionary, I think of it as a future relatively unchanging table that will make for using in queries. Or are dictionaries more optimal than queries in that application?
I only made a few dictionaries and in my application there were superseded by tables and queries.
When using the Ignition reporting, is it more common to use the query table?
I was thinking if I weren’t adding two machines and smoothing the scripts, that are linked to simple table report components, I would be making the query tables.
The answer, as with most things, is ‘it depends’.
In my particular application, the bom info for a production order comes from a file. Since my lookups are defined in a dictionary, I don’t have the round trip to the db. Until model year changes, it’s relatively unchanging.
That said, I also use dictionaries to manipulate datasets. You’ll notice I have a lot of such posts on here.
Joins on db tables are great and useful and are more efficient if they are in the same data source, but if you need to use datasets from two different sources, you’re hosed.
I find that db people will tend to do more with joins and stored procedures, and programmers will tend to do more through scripting.
I had an error in my post that I had spotted when I saw that last line of code referencing my code.
Thanks
I revised and notated the change there.
When the script says:
x= system read
system write ([“singlepath”],[ x ])
I have that figured out now.
I get the paths for both
make a values list
write that list to the list of paths with system write blocking
This time it is different though.
M1gross = system.tag.read[("[Test]m01/m01Prod/m01gross")
16 machines more of these and then several sets of 17 machines for others
then later on in the script they basically are combining totals from each shift by adding the current to the running total:
system.tag.writeBlocking(["[Test]thisplace/somestuff/M1 gross"], [sytem.tag.read([Test]thisplace/somestuff/M1 gross") +M1gross.value ])
my solution isn’t working, I tried a lot of stuff, and the closes so far I got was like this:
thisList =([i.value+k.value for i in system.tag.readBlocking(pathDailySticks) if i.quality.good for k in system.tag.readBlocking(pathsSticks) if k.quality.good ])
valueShiftSticks
valueDailySticks
thisList
output
[8100, 18450, 20007, None, 17342, 29520, 0, 19388, 20910, 0, 0, 0, 0, None, 25077, 0, 16717]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[8100, 18450, 20007, 17342, 29520, 0, 19388, 20910, 0, 0, 0, 0, 25077, 0, 16717, 8100, 18450, 20007, 17342, 29520, 0, and on and on and on
these tags are not numbered the same
one set uses two digits and one set use one digit when below 10…grrr
I think I found the way, and it is what Jordan said to do already.
for (item1, item2) in zip(list1, list2):
sum_list.append(item1+item2)
print(sum_list)