The reason that this happens this way is because the code is evaluating static-states only. The code is acting as nothing more than a dumb switch-handler.
The code needs to have some "smarts"... it needs to have a sense of "history".
Let's revisit the original conditional statements...
If the output is NOT ON when the pushbutton is operated, then turn the output ON.
If the output IS ON when the pushbutton is operated, then turn the output OFF.
The operative word here is... "when".
The operative phrase is... "when the pushbutton is operated"
This means... "when the pushbutton transitions from OFF to ON".
So... how to detect when the pushbutton comes ON... Some PLCs have "transition detection". This is often indicated by --|/\|-- for OFF-to-ON, and --|\/|-- for ON-to-OFF. The particular element will be true for one scan.
OK, so that will provide for allowing further evaluation in the line only when the transition occurs. That means, even if the button is held for several thousand scans, the evaluation occurs only on that scan when the pushbutton was first pressed.
Is this enough? No.
Both conditional statements are looking for an OFF-to-ON transition. When an OFF-to-ON transition occurs, it will apply to and activate both lines of code. Unless something can prevent it from occurring, this will lead to the apparent "hang" that was described... "Never ON", or, "Once ON, always ON".
Sorta, kinda like...
Imagine walking into one end of a dark room. You know that when you walk into a dark room you need to flip the light switch... so you do... the light comes ON. Then you spy another switch at the other end of the room... you go down there and flip that switch (it's a compulsive thing)... the room goes dark. Obviously, a three-way switch.
It doesn't matter which end you enter first, and it doesn't matter what the current state of the light is... for whatever reason, you have this "thing" that you have to flip both switches. So, upon entering, you flip the first switch and then you flip the other. When you leave, you go through the same ritual... both switches get flipped.
If the light in the room was OFF when you first entered then the light will go ON when you flip the first switch, and OFF when you flip the second switch... leaving you in the dark. As you leave, you again flip both switches... the light goes ON, then the light goes OFF. Except for those times when you are traveling between switches (from first code line to second code line), the light is ALWAYS OFF!
That's kinda like opening and closing the refrigerator door, over and over, hoping that this time, when the door is open, there WILL BE BEER! Damn! Let me try again... and again... and again... maybe this time... damn!
If the light in the room was ON when you first entered then the light will go OFF when you flip the first switch, and, if you can find your way, the light will go ON when you flip the second switch. As you leave you again flip both switches... the light goes OFF, then the light goes ON. Except for those times when you are traveling between switches (from first code line to second code line), the light is ALWAYS ON!
I guess that would be kinda like... there's ALWAYS BEER! WOW... that could be cool... do ya think?
When some of you normal folks go into a dark room you turn on the light switch. If light appears then you are satisfied... you ignore the other switch! Regardless of which switch you flip first, if you have light, as you expect, then you ignore the second switch.
This amounts to... when entering, it was OFF, now it's ON, ignore the other switch. When leaving, it was ON, now it's OFF, ignore the other switch.
The key to this is... if one switch is operated, ignore the other.
In context... if one code-line causes a change in the status of the output-bit, ignore the other code-line.
So, somehow, you need to prevent the second line from executing if the first line causes a change in the state of the output-bit.
You could try something like...
//LINE-1//
{ Push_Button -AND- NOT Output -AND- NOT Ignore_Line_1 }
=> SET Output, and Ignore_Line_2 (where... Ignore_Line_2 is a 1-Shot)
//LINE-2//
{ Push_Button -AND- Output -AND- NOT Ignore_Line_2 }
=> RST Output, and Ignore_Line_1 (where... Ignore_Line_1 is a 1-Shot)
If Line-1 is executed and result is SET Output, then Ignore_Line_2 will go ON... for one scan. This will indeed prevent Line-2 from executing... on this scan. The actual PLC output will go ON at the output update.
On the next scan, however, if the pushbutton is still pressed, then in Line-1, because the output-bit is ON, Line-1 is False and it will turn OFF Ignore_Line_2. Now, in Line-2, because the pushbutton is pressed, and the output-bit is ON, and Ignore_Line_2 is now OFF, the output-bit will be RST and Ignore_Line_1 will go ON... for one scan. The actual PLC output will go OFF at the output update.
On the following scan, Ignore_Line_1 is ON so...
This amounts to the output-bit toggling ON and OFF every other scan for as long as the button is pressed. This is clearly not what is expected. We need a better way...
Once a line causes a change in the state of the output-bit, we need to prevent either of the lines from executing until the next time that the button goes ON. Once a change occurs, and for as long as the button is held, ignore BOTH lines!
//LINE-1//
{ Push_Button -AND- NOT Output -AND- NOT Ignore_Both_Lines }
=> SET Output
=> SET Ignore_Both_Lines
//LINE-2//
{ Push_Button -AND- Output -AND- NOT Ignore_Both_Lines }
=> RST Output
=> SET Ignore_Both_Lines
"... and for as long as the button is held, ignore BOTH lines!"
//LINE-3//
{ NOT Push_Button -AND- Ignore_Both_Lines }
=> RST Ignore_Both_Lines
When the button is released, re-enable both lines to execute. Upon the next button press, the first line to execute will perform the particular output-bit modification and then prevent any subsequent evaluations until the button is released.
Now, if I can just get that damned refrigerator to co-operate...
(60)