Piston Task Cancellation even with "Never Cancel Tasks"


#1

I’m having an issue with tasks being canceled and pistons restarting or resetting even though the conditions of the initial ‘if’ are not fully met and “Never Cancel Tasks” has been enabled.

Attached is one of two or three examples I’ve been playing with in an effort to understand the underlying logic and behavior. I’ve tried a number of variations on the same theme, using conditions vs. triggers, a variety of settings and programming logic, but keep ending up at the same place. Clearly I do not fully understand the proper use of the tools provided.

In my example, the piston executes if any of the designated contact sensors stay open for longer than a set period (a very short 10 seconds here, just for testing purposes). This piston is then supposed to ‘do stuff,’ in this test piston wait ten minutes and then turn off a light. However, if any of the contact sensors opens or closes during the ‘wait’, even for a fraction of the 10 seconds minimum, all tasks are canceled and this piston restarts.

.

I have a feeling when someone points out how obvious my issue is I’m going to slap my forehead and curse quite loudly, but I’ve spent entirely too much time reading through the posts here and trying (unsuccessfully) to figure this one out on my own.


#2

Can I ask why you want them to stay open for a set period.
Why not if any opens or maybe closes.
Just trying to understand what you are trying to achieve.


#3

As well as having TCP ‘never cancel’ turned on, try also setting TEP to ‘on condition state change only’


#4

I’ve run into the same issue with very different pistons, so I don’t want to get too narrowly focused here. But I might use something like this to notify me if someone has left a door, window, or gate open for too long. I can check for open contacts and use age(), etc. but if I have a wait (for a second reminder perhaps) or other tasks, those task get canceled whenever another door is opened.


#5

I tried that as well (and just tried it again to be sure), but same result.

I note as well that {openDoors} doesn’t actually contain devices that match the ‘if’. It contains a list of any open contact, instantly reflecting any state changes in the contacts.


#6

@bthrock Are you saying this from a visual confirmation of the trace or a physical confirmation of the actual light staying on longer than 10 minutes.?

I may be wrong but if you enable full logs for this piston and then re-run your scenario and check to see if it is actually cancelling the next scheduled event of the light turning off or not?

It may just be that because you have opened or closed a door during the wait, that happening has triggered the piston to re evaluate again causing a fresh display on the trace for the new action, you may still find that the previous action is still going to happen after the 10 minutes are up.

Sorry if that isn’t explained very well, basically try again and during the 10 minute wait Open or close a door again so it looks like it’s cancelled the action but then wait and see if the light goes off after the 10 minutes are up.

Maybe shorten it to 2 minutes or something for testing purposes.


#7

That’s a very good point… the trace shows it cancel even though the never cancel setting keeps things running in the background.


#8

That was a far easier way of explaining it. Doh! :+1:t3::+1:t3:


#9

I tested this earlier and instead of lights, I used push notification. I got 2 notifications in my short testing…I only opened simulated contacts twice :slight_smile: (…and then I got sidetracked)


#10

Here’s a handy piston for sending reminders about an open door… this sends a message every 10 minutes with a running timer in the message,


#11

Actually, your explanation was quite clear. I now get that the live trace does not show pending tasks from a previous execution, something I hadn’t considered previously. So I changed the wait to 1 minute and tried it again.

  • If I opened a door during the 1 minute ‘wait,’ the light does not come on at one minute. In fact, I was able to delay the light from coming on almost indefinitely by repeatedly opening doors during the wait period.

  • If I closed all doors prior to the 1 minute period. The hallway light would eventually come on. So I think you’re right (and the logs seem to confirm this) that the tasks aren’t actually getting canceled, but they are getting pushed back.

That gets me closer to an understanding but doesn’t really resolve the issues created by the fact that the piston restarts on any open/close activity, not just activity that matches the specified triggers. Those unwanted restarts are wreaking havoc with timing and variables on more complex pistons. I’ll have to wrack my brain for a workaround. The behavior of “store matching devices to” is also not what I expected (in that it doesn’t really store devices that match the entire trigger), but at least that is an easy fix.

Thanks for your help!!


#12

Using push notification is definitely a better idea. I wish I’d thought of that. The lights won’t turn on more than once, but obviously push notifications can be sent multiple times. :grinning:


#13

Thanks. Again, I don’t want to get too narrowly focused on the open door reminder. I have a piston that does this and much more. Trying to understand the unwanted piston restarts and “never cancel tasks” is important to a several pistons I’ve been working on. I think I’ve got a grasp on the latter issue now; the first one is a problem for which I have to consider further in terms of a solution.

Appreciate all the help!!


#14

pistons subscribe to a set of events from specified devices. these events are usually generated by various devices. some of which can also be virtual devices, like location.

whenever any event the piston is subscribed to is generated by the device, the piston code is called with that event. then its up to the piston code to process that event and decide if it matches any of the triggers specified in the piston.

without the piston “restarting” it really has no way of knowing if the event matches the triggers contained in the piston.


#16

@bthrock if you are after a piston that doesn’t re-evaluate upon every event subscribed to from the door, you could always create you specific If statement with the subscriptions and then for the action make it execute a separate piston containing the actions you require.

Basically the second piston will not evaluate on any event change from your doors. It will only evaluate once called by the first piston. :+1:t3:

Piston No.1 subscribes to 3 events
If
Any of X,Y, Z stay open for xMinutes (subscribed)
Then
Execute piston No.2

Piston No.2 subscribes to no events
Do
Wait 10 minutes
Turn off


#17

You might want to enable parrallisem. This will essentially act like a new piston with each successful evaluation.

If I am understanding correctly.


#18

Absolutely. That was the solution I began working towards after I realized the piston was re-evaluating and executing in parallel multiple times. I would have preferred to have everything all in one piston, but if I can’t, no big deal.

The only minor complication is that in the example I provided, the piston re-evaluates every time a door is opened. If any door that was previously left open is still open, the first piston would evaluate as true and execute the second piston multiple times. So, same problem. The solution, however, was easy enough: have the second piston pause the first piston until the second piston has completed.


#19

I did try that. It doesn’t work. The parallelism toggle appears intended only to prevent a race condition where a piston is being executed multiple times in very rapid succession. I think it only delays additional executions by a few (10?) seconds or so.

Having a toggle that would preclude any parallel execution or re-evaluation might be nice, but it seems likely to be well down the list given its limited utility.


#21

I’ve run into this same scenario. I came up with a way, using the combination of a boolean flag and a TCP of Never, to make it work. Following is a simple example. I’ve used this technique successfully in more complex Pistons.

The only question left in my mind is if all statements inside the outermost “if”, or just those that could cause the Piston execution to be split (such as those containing waits), need to have their TCP set to Never. Even though it’s a bit of a hassle, I go the safe route and set TCP to Never for everything.

Side note… I’ve only used this for Pistons that have no triggers (or, more precisely, do not subscribe to any events.) These Pistons are meant to be “procedures” that can be executed by other Pistons that do have triggers and/or conditions that subscribe to events.

Here are two sample runs. The first I let finish. For the second run I click Test when the wait is about half done. You can see the Piston runs again, but the first wait is not extended or canceled, but completes at the expected time.

Second run:

2/21/2018, 9:44:18 AM +226ms
+1ms	╔Received event [Doral Ct].time = 1519227858471 with a delay of -246ms
+113ms	║Done!
+120ms	╚Event processed successfully (120ms)
2/21/2018, 9:44:11 AM +292ms
+0ms	╔Received event [Doral Ct].test = 1519227851291 with a delay of 0ms
+81ms	║Running!
+90ms	║Setting up scheduled job for Wed, Feb 21 2018 @ 9:44:18 AM CST (in 7.089s)
+104ms	╚Event processed successfully (104ms)
2/21/2018, 9:44:03 AM +365ms
+0ms	╔Received event [Doral Ct].test = 1519227843365 with a delay of 1ms
+81ms	║Running!
+99ms	║Starting...
+110ms	║Setting up scheduled job for Wed, Feb 21 2018 @ 9:44:18 AM CST (in 14.996s)
+119ms	╚Event processed successfully (118ms)

First run:

2/21/2018, 9:43:55 AM +121ms
+1ms	╔Received event [Doral Ct].time = 1519227836544 with a delay of -1423ms
+165ms	║Done!
+174ms	╚Event processed successfully (174ms)
2/21/2018, 9:43:41 AM +413ms
+3ms	╔Received event [Doral Ct].test = 1519227821412 with a delay of 0ms
+107ms	║Running!
+126ms	║Starting...
+139ms	║Setting up scheduled job for Wed, Feb 21 2018 @ 9:43:56 AM CST (in 14.996s)
+146ms	╚Event processed successfully (146ms)