Tough Scripting Problem

I am writing a value change script on a tag that will show 2 results to a test we are running. (The machine works like this and I can't change the PLC code to fix it) The code has the first test results pop up, then the results go to 0, then the second test results pop up, and then it goes to 0 again. I am trying to write the 2 different results to 2 different tags. I have a working version of the script:

def valueChanged(tag, tagPath, previousValue, currentValue, initialChange, missedEvents):
	#Get Variable tag paths for writing values
	TagPath1 = '[default]{ENumber}/{ENumber}/Test1OcclusionFinal'
	Var1 = system.tag.readBlocking(TagPath1)
	
	TagPath2 = '[default]{ENumber}/{ENumber}/Test2BurstFinal'
	Var2 = system.tag.readBlocking(TagPath2)
	
	programTagPath = '[default]{ENumber}/{ENumber}/ProgramNum/Current_Program_Number'
	Prgrm = system.tag.readBlocking(programTagPath)
	
	# Flip boolean
	system.tag.writeBlocking('[default]{ENumber}/{ENumber}/Results/Get_Results_Trigger', 0)
	
	#Program Numbers for Linked
	link = [3]
	
	#Program Numbers for Occlusion
	occ = [7 , 4]
	
	#Program Numbers for Burst
	burst = [2 , 5 , 6]
	
	
	#Write based off Program Number
	if Prgrm[0].value in link:
	
	# Write variable value to another tag for linked test (Test 3)
		if currentValue.value != 0 and currentValue.value < 2 and currentValue.value != Var2:
			system.tag.writeBlocking(['[default]{ENumber}/{ENumber}/Test1OcclusionFinal'], [currentValue.value])
		
		elif currentValue.value != 0 and currentValue.value != Var1:
			system.tag.writeBlocking(['[default]{ENumber}/{ENumber}/Test2BurstFinal'], [currentValue.value])
		
	elif Prgrm[0].value in occ and currentValue.value != 0:
		system.tag.writeBlocking(['[default]{ENumber}/{ENumber}/Test1OcclusionFinal'], [currentValue.value])
		
	elif Prgrm[0].value in burst and currentValue.value != 0:
		system.tag.writeBlocking(['[default]{ENumber}/{ENumber}/Test2BurstFinal'], [currentValue.value])
		
		

but one of the only reasons it works is because MOST of the time the first test results will be a value below 2. I am afraid that this script will not work correctly if there is an outlier for the first test and it ends up being above 2. I have also tried to work out a script that looks like this:

pro = 2
tags = ['[default]E398/E398/Test1OcclusionFinal']
x = system.tag.readBlocking(tags)
results = 6

if pro == 2:
	system.tag.writeBlocking(['[default]E398/E398/Test1OcclusionFinal'], [2])
	tags2 = ['[default]E398/E398/Test1OcclusionFinal']
	y = system.tag.readBlocking(tags2)
	print y
	print x
	if x != y and results != 0:
		system.tag.writeBlocking(['[default]E398/E398/Test1OcclusionFinal'], results)

that would read the first results before and after I write to the first tag, to see if it should write to the second tag. Unfortunatley I don't think this will work either because this script will be executed every time the value changes..

Is there any way to work around this?

Does the sequence have specific timing? If so, I'd use the first transition away from zero to initialize a state machine, and capture following values based on time.

@pturmel beat me to it, but what's the timing of these tag changes. I'm thinking you could determine when a new test is started based on delays. If there's like 5 seconds on each value, but a minute or more between tests, you could track how long since the last tag change, and if it's more than a certain time frame, know that a new test started.

Also, on your first script, you're doing a lot of individual readBlocking calls. I'd put those tags in an array/list and do a single call to clean it up, then split out the individual values.

It does! It use a sleep(3) function (unfortunately), so I know that it will be 3 seconds before the second result. So how would I do this? I would think that I would do something like this?:

if value != 0:
    system.tag.write(firstTagResults, results)
   sleep(3)
   system.tag.write(secondTagResults, results)

If these values are all coming in on the same tag, you'll only use data change events and you'll compare the currentValue.timestamp and the previousValue.timestamp to see how long it's been since the last value change. You'll want to give it some wiggle room, but maybe say if it's less than 10 seconds....it's the second test result, but if more, it's probably the first test result (assuming these tests are spaced out more than 1-15 seconds).

Actually, you'll have to account for 0, in this, but the results should be the same.

Can the test be interrupted and never give a second value ?

If not, then you can forget the delays and just use the order in which values come in.
I'd probably add another tag that would act like a flag indicating which value you're expecting next, and route the results to one of two tags depending on that.

something like

if system.tag.readBlocking(["flag"])[0].value:
    result_tag = "value1"
else:
    result_tag = "value2"
system.tag.writeBlocking([result_tag], [currentValue])

No, .sleep() is evil. (It can break your system if used improperly, especially in tag events.)

Make a new integer TestState memory tag alongside your others.
Put this in a project library script:

allTags = ['[default]E398/E398/%s' % x for x in ['Result', 'TestState', 'Test1OcclusionFinal', 'Test2BurstFinal']]
readTags = allTags[:2]
writeTags0 = allTags[1:2]
writeTags1 = allTags[1:3]
writeTags2 = allTags[1:2] + allTags[3:]

# Call this at quarter-second intervals.
def monitorE398():
	result, state = [x.value for x in system.tag.readBlocking(readTags)]
	if state == 0:
		# Check for non-zero result from PLC to start sequence.
		if result:
			state += 1
			system.tag.writeBlocking(writeTags1, [state, result])
	elif state < 8:
		# Dwelling between results.  Just increment the state.
		state += 1
		system.tag.writeBlocking(writeTags0, [state])
	elif state < 16:
		# Looking for result #2
		if result:
			state = 0
			system.tag.writeBlocking(writeTags2, [state, result])
		else:
			state += 1
			system.tag.writeBlocking(writeTags0, [state])
	else:
		# Timeout or invalid state.  Might want to log a message.
		state = 0
		system.tag.writeBlocking(writeTags0, [state])

Put this one-liner in a timer event set to 250ms:

myE398script.monitorE398()

Adjust as you see fit. (Presumably parameterizing things.)

2 Likes

I was thinking more along the lines of this in the valueChanged script (adjusting as necessary with relative paths for tags if you're using this in a UDT):

if currentValue.value:
	elapSec = system.date.secondsBetween(previousValue.timestamp, currentValue.timestamp)
	if elapSec > 5:
		# This is our first result
		system.tag.writeBlocking(['[default]E398/E398/Test1OcclusionFinal'],[currentValue.value])
	else:
		# This is our second result
		system.tag.writeBlocking(['[default]E398/E398/Test2BurstFinal'],[currentValue.value])

Edit: Had to fix my elapSec comparison to be greater than rather than less than.