Function Script not working

Hi,

I have the following script:

def auto_select_operator_interaction(msg_nr, action):
    if action == 2:  # Outgoing alarm
        # If action was selected, then unselect
        if msg_nr == system.tag.readBlocking("[Test SCADA Ignition]OperatorIndexIgnition"):
            system.tag.writeBlocking("[Test SCADA Ignition]OperatorIndexIgnition", 0)

        # Shift all active alarms from the selected one
        for index in range(20):
            if system.tag.readBlocking("[Test SCADA Ignition]L4_Active_Operator_Msg[{}]".format(index)) == msg_nr:
                temp_index = index - 1
                while temp_index < 20:
                    temp_index += 1
                    index_van = temp_index + 1
                    system.tag.writeBlocking("[Test SCADA Ignition]L4_Active_Operator_Msg[{}]".format(temp_index),
                                     system.tag.readBlocking("[Test SCADA Ignition]L4_Active_Operator_Msg[{}]".format(index_van)))

        # Select new one
        if system.tag.readBlocking("[Test SCADA Ignition]OperatorIndexIgnition") == 0 and system.tag.readBlocking("[Test SCADA Ignition]L4_Active_Operator_Msg[1]") > 0:
            system.tag.writeBlocking("[Test SCADA Ignition]OperatorIndexIgnition", system.tag.readBlocking("[Test SCADA Ignition]L4_Active_Operator_Msg[1]"))

    elif action == 1:  # Incoming alarm
        for index in range(20):
            if system.tag.readBlocking("[Test SCADA Ignition]L4_Active_Operator_Msg[{}]".format(index)) == 0:
                system.tag.writeBlocking("[Test SCADA Ignition]L4_Active_Operator_Msg[{}]".format(index), msg_nr)

        # Select new one
        if system.tag.readBlocking("[Test SCADA Ignition]OperatorIndexIgnition") == 0:
            system.tag.writeBlocking("[Test SCADA Ignition]OperatorIndexIgnition", system.tag.readBlocking("[Test SCADA Ignition]L4_Active_Operator_Msg[1]"))

    else:  # Loop in alarm
        system.tag.writeBlocking("[Test SCADA Ignition]OperatorIndexIgnition", msg_nr)

and this in my script console:

operatorIndexScript.auto_select_operator_interaction(5, 1)

what the result should be after this is being executed is that the first number in my array of the tag 'L4_Active_Operator_Msg' should be 5. But instead it stays 0.
The function doesn't return anything so my return in the Console screen is empty.
It is not giving an error that something is wrong with the function so I don't know why it is not working.

Is there someone who can help me with this?

system.tag.read* functions return a list of qualified values. Its return will NEVER be equal to 0.

Now, another thing to note: avoid running multiple calls to the tag read and write functions. Build the data first, then pass everything in just one call. Or as few calls as possible.
It will also probably make the logic easier to follow.

1 Like

so instead of reading the tag 20 times make it in such a way that it has to read it only one time?

Let me try to figure out what your script actually does before answering :slight_smile:

edit:

for index in xrange(20):
	if system.tag.readBlocking("[Test SCADA Ignition]L4_Active_Operator_Msg[{}]".format(index)) == msg_nr:
		temp_index = index - 1
		while temp_index < 20:
			temp_index += 1
			index_van = temp_index + 1
			system.tag.writeBlocking("[Test SCADA Ignition]L4_Active_Operator_Msg[{}]".format(temp_index),
						system.tag.readBlocking("[Test SCADA Ignition]L4_Active_Operator_Msg[{}]".format(index_van)))

ew... What is going on here ?

What's in the [Test SCADA Ignition]L4_Active_Operator_Msg array ?
You're comparing its values to integers, but its name suggests it contains strings.

2 Likes

Yes, we covered that yesterday where you were running 48 × readBlocking which will run one after another with each waiting until it is finished before going on to the next one. Read them all at once if at all possible. (There will be a maximum limit - but it will be large.)

2 Likes

This code

for index in xrange(20):
	if system.tag.readBlocking("[Test SCADA Ignition]L4_Active_Operator_Msg[{}]".format(index)) == msg_nr:
		temp_index = index - 1
		while temp_index < 20:
			temp_index += 1
			index_van = temp_index + 1
			system.tag.writeBlocking("[Test SCADA Ignition]L4_Active_Operator_Msg[{}]".format(temp_index),
						system.tag.readBlocking("[Test SCADA Ignition]L4_Active_Operator_Msg[{}]".format(index_van)))

is changing the first array value for the second array value. basically moving the whole array one to the front.

This is an integer array. The values in the array will point to a text thats why it's called Msg

I will change my code in the way we talked about yesterday

But you still have 20 × readBlocking and up to 20 × writeBlocking + readBlocking = 20 × (20 + 20) = 800 read/write operations!

  • Read everything you need in one readBlocking.
  • Do the loop and update the list to be send out.
  • Send out the list in one writeBlocking.

I'm changing it. Give me some time. I will send the updated code when I'm done.

I'm not sure this does what it's supposed to do.
Can you explain in details what you want to happen, maybe with a couple of examples ?
Can you explain WHY you're moving things around ?

I rewrote it to read and write just once, keeping (I believe) the same logic:

def auto_select_operator_interaction(msg_nr, action):
	op_index_path = "[Test SCADA Ignition]OperatorIndexIgnition"
	op_msg_path = "[Test SCADA Ignition]L4_Active_Operator_Msg"
	op_index, op_msg = [qval.value for qval in system.tag.readBlocking([op_index_path, op_msg_path])]

	if action == 2:  # Outgoing alarm
		# If action was selected, then unselect
		
		if msg_nr == op_index:
			op_index = 0

		# Shift all active alarms from the selected one
		for index in xrange(20):
			if op_msg[index] == msg_nr:
				temp_index = index - 1
				while temp_index < 20:
					temp_index += 1
					op_msg[temp_index] = op_msg[temp_index + 1]

		# Select new one
		if op_index == 0 and op_msg[1] > 0:
			op_index = op_msg[1]

	elif action == 1:  # Incoming alarm
		for index in range(20):
			if op_msg[index] == 0:
				op_msg[index] = msg_nr

		# Select new one
		if op_index == 0:
			op_index = op_msg[1]

	else:  # Loop in alarm
		op_index = msg_nr

	system.tag.writeBlocking([op_index_path, op_msg_path], [op_index, op_msg])

It still needs some work to make it... let's say sane.
But while I can see what's going on line by line, I still can't figure out the big picture (which is often a sign that it needs some rework).
I have the feeling there's a much simpler way, but without understanding the goal I'm left with just that, a feeling.

Though, now it's a bit simpler to read and reason about, so I'm a bit more comfortable to start simplifying things

1 Like

Are you trying to create a shift-register?

input = ["zero", "one", "two", "three", "four"]
print input
print input[1:]

input[1:] will give you the complete list but without input[0].

['zero', 'one', 'two', 'three', 'four']
['one', 'two', 'three', 'four']
>>>

Can you use that syntax to simplify your code?

Okay I will try to explane to you. It has to do with the thing we worked on yesterday.

The action and msg_nr are passed to this script when my alarm of the tag values prompt goes off (same prompt tags as yesterday so there are around 20 of these prompt tags).
image
All those action datatypes have this prompt tag.

if the one of the alarms of these prompt tags goes off (this happends when prompt tag value is > 0) then action = 1 so it will run this function and then action 1 of it.

so this part of your updated code.
if alarm goes away because it is being cleared. It will pass action = 2 and run:

msg_nr is the parameter that points to the right prompt tag that is >0. This parameter is passed when the alarm goes off just like action parameter.

L4_Active_Operator_Msg will be the array of msg_nr.
for example: if prompt tag of Action1 is >0 then msg_nr is 1 and the alarm goes off because this prompt is now >0 so it will pass msg_nr = 1 and action= 1

what must happen now is that the msg_nr is the first number in the L4_Active_Operator_Msg array.
if not one but 2 prompt tags are >0 then the second one that is >0 must be stored in the array after the first one. So after this there should be 2 msg_nr in the array.

as soon as one alarm is cleared the parameter action passes 2 so it wil run the action = 2 code. This code must delete the msg_nr of the alarm that is just been cleared and move the whole array so that the alarm that is still on is in the first place in the array

so Action1/prompt >0
array: [1]
now Action5/prompt also >0
array:[1,5]
alarm of Action1/prompt <=0
array: [5]
This is what must happen yes:

but then with normal integers

Is L4_Active_Operator_Msg supposed to be a queue then ?
Except you're not necessarily popping the first element ?
Are there always 20 elements in that array ?

I kind of feel like you're not using the right tools for this job.

I'm not sure because it code that rewrote into ignition of my company that I work for.

The values that will be in the array are the msg_nrs.
most of the time there will be 0,1 or 2 msg_nrs in the array because there are often 0,1,2 alarms on at the same time

Alright, let's see if I got things right:
alarm becomes active: Put the corresponding number in the queue
alarm cleared: Remove it from the queue

Is that it ?

What's the role of the operator index in all this ? It doesn't seem to affect the messages in any way.

can, I think, be rewriten as,

if op_msg[index] == msg_nr:
	op_msg = op_msg[1:]

This will drop the zero element of the list and move everything else left one position.

1 Like

This is right

def auto_select_operator_interaction(msg_nr, action):

	tagPaths = []
	arrayZero = []
	if action == 2:  # Outgoing alarm
        # If action was selected, then unselect
        if msg_nr == system.tag.readBlocking("[Test SCADA Ignition]OperatorIndexIgnition"):
            system.tag.writeBlocking("[Test SCADA Ignition]OperatorIndexIgnition", 0)

        # Shift all active alarms from the selected one
        for index in range(20):
            if system.tag.readBlocking("[Test SCADA Ignition]L4_Active_Operator_Msg[{}]".format(index)) == msg_nr:
                temp_index = index - 1
                while temp_index < 20:
                    temp_index += 1
                    index_van = temp_index + 1
                    system.tag.writeBlocking("[Test SCADA Ignition]L4_Active_Operator_Msg[{}]".format(temp_index), system.tag.readBlocking("[Test SCADA Ignition]L4_Active_Operator_Msg[{}]".format(index_van)))

        # Select new one
        if system.tag.readBlocking("[Test SCADA Ignition]OperatorIndexIgnition") == 0 and system.tag.readBlocking("[Test SCADA Ignition]L4_Active_Operator_Msg[1]") > 0:
            system.tag.writeBlocking("[Test SCADA Ignition]OperatorIndexIgnition", system.tag.readBlocking("[Test SCADA Ignition]L4_Active_Operator_Msg[1]"))

    elif action == 1:  # Incoming alarm
        for index in range(20):
        	tagPaths.append("[Test SCADA Ignition]L4_Active_Operator_Msg[{}]".format(index))
        
        tagValues = system.tag.readBlocking(tagPaths)
        for index in range(20):
        	if tagValues[index] == 0:
           		tagValues[index] = msg_nr
        	
            # Select new one
        	if system.tag.readBlocking("[Test SCADA Ignition]OperatorIndexIgnition") == 0:
           	 	system.tag.writeBlocking("[Test SCADA Ignition]OperatorIndexIgnition", system.tag.readBlocking("[Test SCADA Ignition]L4_Active_Operator_Msg[1]"))

    else:  # Loop in alarm
        system.tag.writeBlocking("[Test SCADA Ignition]OperatorIndexIgnition", msg_nr)

If you look in this code that I had earlier operatorIndex is being written by msg_nr or set back to 0.
I use this tag to point to prompt values again so they will display a text in my operator screen like I said yesterday

You guys are pretty quick with all the coding so I will try to see if the current solution you guys have given to me work and let you know if I managed. I'm not that quick with all this sorry for that. It's all very new for me

Not if elements from the middle of the list can be popped.
What I think he wants is just

del op_msg[index]

maybe ?
or

op_msg = [msg for msg in op_msg if msg != msg_nr]

But I still think the whole thing took a wrong turn at some point and there's a better way to do all this.

edit: side note
If you're in a situation where you want to be doing some_list[1:],
consider using a queue. I'm sure Java has some types that can be used for this, or there's collections.deque with .pop() and .popleft(). It even has a rotate() method !
It's not always worth it, but it's always nice to know it exists.

1 Like

There are several options, depending on requirements that can be used.

https://docs.oracle.com/javase/8/docs/api/java/util/AbstractQueue.html

I know that the ConcurrentLinkedQueue is thread safe.