This varies with manufacturer/implementation. Some implementations make the "output image" available to "subsequent rungs" *before* the hardware is updated. This allows implementations that have predictable rung scan orders to allow rungs to act like "sequential statements" in a procedural language.
E.g. (fixed width font):
Start Stop Run
+---[ ]--+---[/]--------( )---+ | | | | | | | Run | | +---[ ]--+ | | | | Run Motor | +---[ ]-----------------( )---+causes "Motor" to be active "shortly after" Start is pressed (actuating "Run") *without* requiring another pass around the big_loop() to transfer the "output image table" to the physical outputs.
This sort of thing can lead to races as the order in which you arrange the rungs determines how the logic works.
A more intuitive form might be:
if ((conditionA || !conditionB) && ConditionC) { turn on output; } else { turn off output; }
And, for something this straightforward:
output = (conditionA || !conditionB) && ConditionC;
I would wrap this in a function invocation:
void rung28(void) { output = (conditionA || !conditionB) && ConditionC; }
so that big_loop just consists of a bunch of rungX()'s
The equivalent is:
void rung28(void) { if (!timer_expired(TIMER_ID)) return;
output = (conditionA || !conditionB) && ConditionC; }
So, timer_expired() just checks to see if there is any time remaining on timer #TIMER_ID. If so (timer NOT expired), then you just take an early exit from the function ("rung28()").
If you try to actually *watch* the current time, then you need to store an "expiration time" for each timer. Or, track it yourself, manually, and pass it as a parameter to timer_expired():
I.e., timer_expired(expiration) then becomes:
return (now > expiration);
Or, timer_expired(TIMER_ID) becomes:
return (now > timer[TIMER_ID]);
The problem with tracking it yourself is that you may want to set/specify an expiration time in one place and "wait on it" somewhere else. You have to propagate that *time* to the other place instead of just saying "wait for this TIMER"
Imagine setting up a rung that blinks a light at a certain rate. And, wanting to be able to blink the light at two
*different* rates to indicate two different "conditions". Using a TIMER_ID in the blink "rung()" lets that code ignore the details of the actual blink rate.(this depends on how you implement your timers)