Help understanding Task Cancellation Policy and Task Scheduling Policy

schedules
canceling

#1

1) Give a description of the problem
I have a piston to control the light in my garage that’s working perfectly, but I don’t understand why it’s working, specifically with respect to the Task Cancellation Policy and the Task Scheduling Policy. The green snapshot for the two relevant pistons are shown below, but here’s the intent of it:

If the overhead door is open:
      Turn the light off during the day.
      Turn the light on at night.
If the overhead door is closed:
      If the entry door (into the house) is opened, turn the light on.
      If the entry door is closed and the light is still on, check for motion for 4 minutes.
            If motion was detected, leave the light on.
            If no motion was detected, turn the light off.

2 & 3) What is the expected behavior? & What is happening/not happening?
For this piston, the Task Cancellation Policy has been left at “Cancel tasks on condition state change (default)” for all tasks, and the Task Scheduling Policy has been left at “Override any existing scheduled tasks (default)” for all tasks.

On lines 64-67, I’m checking to see if a variable is set to true, and then immediately setting it to false:
image
Why doesn’t the Task Cancellation Policy cause this to cancel all the remaining tasks in this if-block? I thought that maybe this only gets evaluated after a Wait task resumes execution, but there is a Wait task in this block and all of the tasks after the Wait still get executed correctly. I’ve seen this example used in other pistons, which is why I did it this way, but I don’t understand why it works.

Then, I have this sequence of events, where the initial state is all doors closed and the light off:
Entry door is opened.
      Light turns on.
Entry door is closed.
      Wait starts.
Overhead door is opened.
      Wait is canceled.

Is the Wait canceled because of the Task Cancellation Policy or the Task Scheduling Policy (or something else)? The only condition surrounding the Wait is the startTimer variable. This was set to false, but it didn’t seem to cancel the tasks in the previous scenario, so why did it do it in this one? For the Task Scheduling Policy, does it cancel any existing schedules every time the piston executes, or is it only when a subsequent execution creates a new schedule? Do the every blocks in this particular piston cause the piston to create a new schedule every time it executes?

4) Post a Green Snapshot of the pistonimage



#2

Can you make a post with only the 3b2go piston in it? With multiple attachments, a second mouse click advances to the next attachment. With one attachment, a second mouse click zooms in on the attachment - needed to make your original one legible.


#3

I completely understand. Please let me know if I did this correctly:


#4

I feel bad answering my own questions, but I think I’ve been able to figure out what’s going on. Hopefully someone smarter than me will come along and correct any mistakes that I’ve made.

When a piston executes, it’s behavior varies depending on how it was initiated:

  1. A trigger (or subscribed condition) occurs or the piston is executed explicitly from another piston
  2. A Wait task resumes the piston
  3. A Timer (every block) occurs

When the piston is executed due to a trigger, subscribed condition, or being explicitly executed, it always runs from top to bottom (or to the first Wait task), evaluating the if statements as it goes. IT NEVER BACKTRACKS! So, in the code I presented above:
image
setting startTimer to false does change the condition state for the if block; however, the Task Cancellation Policy doesn’t immediately cancel the following actions, because it has already evaluated this if block and it will not backtrack.

When the piston resumes from a Wait task, it picks up from where it left off, and again, it will not backtrack! So even though the if block surrounding the Wait task is no longer true, the Task Cancellation Policy won’t go back and reevaluate it. The piston will simply continue moving forward from the point it left off until it either reaches the end of the piston or another Wait task.

(Timers are the special case, where they execute in their own little world and don’t interact much with the rest of the piston. It’s not really relevant to the situation here, so I won’t delve deeper into that.)

The Task Cancellation Policy won’t realize that the if block is no longer true until the next time it runs from top to bottom (due to either another trigger, subscribed condition, or being explicitly executed). So for the sequence of events I described above, I believe this is what is happening:
Entry door is opened.
      Light turns on.
Entry door is closed.
      startTimer is set to true.
      if startTimer is true evaluates to true.
      startTimer is set to false.
      Wait starts.
Overhead door is opened.
      if startTimer is true evaluates to false. (state change)
      Task Cancellation Policy cancels the Wait task.

Hopefully I’ve understood all of that correctly. I’m still not sure how the Task Scheduling Policy interacts with Waits and Timers, but for now, I think I better understand how the piston execution works.


#5

You’re on the right track now!

Here is how the default task cancellation policy works (and causes grief for people using WAIT commands with triggers):

IF door changes to open
THEN
Turn light on
Wait 45 seconds
Turn light off

Using the normal Task Cancellation Policy, the above statement will never turn the light off. Here’s why:

The piston is triggered by the door opening and executes to the WAIT command. When it reaches the wait command, it sets a timer for 45 seconds in the future to wake up and continue execution.

When it resumes execution, it doesn’t start at the WAIT command! Rather, it evaluates the preceding condition first… in this case, “IF door changes to open”. Since the door changed to open 45 seconds ago, this trigger will evaluate false now, and the piston will cancel any remaining tasks it had lined up for that IF condition. In this example, that is the “turn light off” command.

If you change your Task Cancellation Policy from the default “cancel tasks on condition or state change” to “never cancel tasks”… it will still re-evaluate the IF condition when the resumes after 45 seconds but the change in the condition from true to false will have no effect on the piston’s execution.

The same above applies to timers. If you have something executing at 9am that contains a wait, it’s no longer 9am when the piston resumes so that part of the code will evaluate false and the rest of the actions will be cancelled (with the default Task Cancellation Policy).

If that is as clear as mud, let me know and I’ll try to explain it more/better.


Help me fix this please
#6

Hmm, I don’t think that’s quite right. I created the example piston that you described, and the logs indicate that the piston resumes from the exact point it left off. It did not reevaluate the surrounding condition. The light turned off after 45 seconds when I left the door open the entire time.

I think what happens for most people is that they cause a second event before the Wait period ends. In this example, they close the door. This second event will evaluate the piston from top to bottom, which is what reevaluates the condition, detects that the state has changed, and cancels the Wait task.

Am I interpreting the logs correctly here?

7/13/2018, 7:25:20 PM +88ms
+1ms ╔Received event [Home].time = 1531527921107 with a delay of -1020ms
+99ms ║RunTime Analysis CS > 18ms > PS > 64ms > PE > 18ms > CE
+102ms ║Runtime (37182 bytes) successfully initialized in 64ms (v0.3.105.20180628) (100ms)
+103ms ║╔Execution stage started
+145ms ║║Executed physical command [Garage Light].off() (28ms)
+146ms ║║Executed [Garage Light].off (30ms)
+149ms ║╚Execution stage complete. (46ms)
+151ms ╚Event processed successfully (151ms)
7/13/2018, 7:24:35 PM +945ms
+1ms ╔Received event [Garage Entry Door].contact = open with a delay of 474ms
+93ms ║RunTime Analysis CS > 24ms > PS > 51ms > PE > 18ms > CE
+95ms ║Runtime (37168 bytes) successfully initialized in 51ms (v0.3.105.20180628) (93ms)
+96ms ║╔Execution stage started
+103ms ║║Comparison (enum) open changes_to (string) open = true (0ms)
+105ms ║║Cancelling condition #6’s schedules…
+105ms ║║Condition #6 evaluated true (4ms)
+106ms ║║Cancelling condition #1’s schedules…
+107ms ║║Condition group #1 evaluated true (state changed) (7ms)
+109ms ║║Cancelling statement #2’s schedules…
+157ms ║║Executed physical command [Garage Light].on() (45ms)
+157ms ║║Executed [Garage Light].on (45ms)
+160ms ║║Executed virtual command [Garage Light].wait (0ms)
+161ms ║║Requesting a wake up for Fri, Jul 13 2018 @ 7:25:21 PM CDT (in 45.0s)
+165ms ║╚Execution stage complete. (69ms)
+167ms ║Setting up scheduled job for Fri, Jul 13 2018 @ 7:25:21 PM CDT (in 44.996s)
+175ms ╚Event processed successfully (174ms)


#7

You are. That’s certainly not the way it has worked in the past, so it’s possibly a change in one of the webCoRE updates. I’ll have to play around with a few pistons in a couple days to see if I get the same results consistently. I’m still leery of changing anything in my pistons at the moment.


#8

I thought that I understood TCP, but I am still confused. I wrote the following test piston. It triggers when the switch is turned off. I have TCP set to NEVER CANCEL. If I turn the switch back on during the WAIT, I expected the piston to NOT cancel and complete the THEN statements. But when I turn the switch back on before the wait is over, it re-evaluates the IF and skips the command after the WAIT. I tried this with a 1 minute wait with the same result. The test results are the same with TCP set to default. So setting TCP to NEVER did not change the execution of this piston.


#9

Try with TCP set to Never on the WITH that contains the WAIT.
(not on the IF)


#10

So that’s the secret. It worked that way. But I don’t see the N next to the WAIT when not in edit mode. So how is one to know when someone puts NEVER on their WAIT? (when helping others)


#11

The “N” is on the WITH that contains the WAIT.

It is usually seen in non-edit mode or green snapshots…
but I have seen browsers (or platforms) that do not show them.


Here is Windows using a Mozilla based browser, in non-edit mode:

pic


#12

@WCmore, My Mozilla

edit mode
w10

Non-edit mode
w10%202


#13

For this edit / non-edit mode issue, you can use $location vs. Location and it likely will show up the same in both scenarios (if that is what you want)