stop/exit flow command in script

Post your feature requets for new triggers, conditions, actions and other improvements.

Moderator: Martin

Locked
natong
Posts: 80
Joined: 29 Aug 2015 05:24

stop/exit flow command in script

Post by natong » 19 Oct 2018 16:29

I like script action so much. By the way, it's no easy to stop/exit flow by script command.

1. Set variable in script.
2. Check condition.
Last edited by natong on 19 Oct 2018 16:40, edited 1 time in total.

User avatar
digitalstone
Posts: 342
Joined: 21 Oct 2017 12:36
Location: The Netherlands

Re: stop/exit/break flow command in script

Post by digitalstone » 19 Oct 2018 16:37

If you want to stop a flow, use the "stop Flows" action.
The script action is mainly (and greatly) used for the more abstract logic to get or generate values/data.
To my knowledge it is not possible to perform any direct type of action to a flow from the script action.
Phone: LG Nexus 5X (rooted vanilla Android 7.1.2)

natong
Posts: 80
Joined: 29 Aug 2015 05:24

Re: stop/exit flow command in script

Post by natong » 19 Oct 2018 16:42

So sad. Stop only the current (self) running flow is possible ?

User avatar
digitalstone
Posts: 342
Joined: 21 Oct 2017 12:36
Location: The Netherlands

Re: stop/exit flow command in script

Post by digitalstone » 19 Oct 2018 18:28

No that is not possible either. You can not stop a flow from a script, or the flow where the script in currently itself running in.
Why would you need this if i may ask?

What i can think of is a weak alternative, which is the sleep() function. But it pauses only the flow where it currently resides in for as long as the amount of milliseconds you give it.

Example: sleep(5000)
Let's the flow sleep for 5 seconds.
Phone: LG Nexus 5X (rooted vanilla Android 7.1.2)

natong
Posts: 80
Joined: 29 Aug 2015 05:24

Re: stop/exit flow command in script

Post by natong » 20 Oct 2018 06:52

Script action is very powerful about boolean check. So it will be good if we can decide the flow loop within the script action.
I have many flows that processing data in script action. Then set a variable to true/false, then use another one condition node.
On multi branches, it will wait 1 step on condition node.

User avatar
Desmanto
Posts: 2709
Joined: 21 Jul 2017 17:50

Re: stop/exit flow command in script

Post by Desmanto » 20 Oct 2018 09:52

By stopping script, do you mean to stop that script only or to stop the current running flow? Either way, both are similar and only 1 step logic difference. When you stop the script, you can simply leave the branching end without anything, thus stopping the flow.

Prologue
The main idea to stop the script usually involved checking some variable or condition. If it is true/false, then stop it. So in every method, you have to use if() as the checking. You can actually just put your script spread out in formation script-expression. So everytime you want the script to stop in the middle, you split out another expression. Example you have : A B C if(D) E F. then you need to put A B C in script 1, add expression to put if(D) then E F in script 2. In every additional stopping check, you require another pair of script-expression. But if you have 10 set of this, you will add unnecessary long pair of script-expression. So we need better design.


These are (my favourite) 3 ways to do it so far, each with its own plus and minus.
Stopping Script.png
Stopping Script.png (101.42 KiB) Viewed 15913 times
1. Single expression
The first is to put all your script into condition expression instead of action script. Since expression must always evaluated to true or false, you can use the true for the main logic branch and false for the stopping branch (or you can do something else too).

Code: Select all

start = "This line is executed normally";
//uncomment the value to test
//value = "stop listening";

if(value == "stop listening")
  return false;

// anything below here won't be executed if condition above is true
msg = "msg exists if the condition above is false";

return true;
Here, we will evaluate the {value}, if it is "stop listening", then we will use return false, so the expression immediately evaluated to false. Everything below the return false, won't be executed anymore.

However, if the value is not "stop listening", then the script continue as usual until end of script; assigning {msg} to the sentence. Then we must put return true at the end. Basically if nothing goes wrong (the if(value) is false), then the script always executed till the end, hence always end in true. From here we pull connection true to the Main Logic Branch (continue your other action/condition). Leaving your false branch open, means the script stop there.

Plus :
a. Require only single element, save flow area. This is the most compact way to stop the script in the middle. It also consume same element execution as you put into script; hence no need to worry about the AES.
b. return false can be placed anywhere and multiple times. You can put so many if() return false; to catch multiple possible stopping condition

Minus :
a. Visual design is not good. We tends to use expression as a small value checking element. We don't expect to see a full-blown script put inside expression. Hence when we design this flow, forget about it and return back; it will be harder to spot that this expression contains all the script we need. Other also will have problem to understand your logic (if you share the flow).
b. Doesn't work with Control UI function. Expression is same as script, it doesn't have the power of accessibility. To use with Control UI function, you must use other method.


2. Script + Expression
Second method allows you to put the script as usual in action script/control UI. The script can be stopped at any point by using return. But unlike expression which always branch out, using return will always execute the next element after the script. Hence, we need additional expression to check the value. But how can we know the script stop in the middle of execution? That's why we need to assign a temporary variable to let the expression know that the it jumps directly from the return.

Code: Select all

start = "This line is executed normally";
//uncomment the value to test
//value = "stop listening";

if(value == "stop listening")
{
  command = "stop";
  return;
}

// anything below here won't be executed if condition above is true
msg = "msg exists if the condition above is false";
As you can see, it is similar to the first method which use single expression. Except that we have to assign additional variable to know that we stop in the middle. Here I use {command} and assign it with "stop". Later at the expression, we check whether the {command} isn't "stop". You can use any other variable name, as long as you remember that this one is use only for this purpose, not for other calculation.

When the value is "stop listening", then command will be "stop". return will stop the script and don't execute anything below the return. It continues to the expression and check if command IS NOT "stop". In this return case, command is "stop", so expression evaluate to false ("stop" != "stop" evaluated to false). Since the false branch is open, nothing happen, flow ends there.

However, if value is not "stop listening", the if() lines are not executed, script continues as usual, creating {msg}. Script end normally (without the need of return just like in expression, since script always ends normally) without creating {command}. Hence is null. When continuing to expression to check if {command} IS NOT "stop", it will evaluated to true (null != "stop" evaluated to true). So, the expression continues to the true and execute your Main logic branch.

Plus :
a. Works with Control UI. You can replace the script with Control UI and this method still works. We need this method, since CUI function can only be put inside Control UI.
b. Visual design is good. This separate the script and the stopping expression in two different element. So we use the script for the full-blown coding and the expression just a single line to check something. Much easier to troubleshoot later or for other to understand your flow.
c. return can be placed anywhere and multiple times. Same as single expression method, you can place multiple if() return.

Minus :
a. Require 2 elements, can be bad for big flows or tight space design. Sometimes the aesthetic of the flow design (simmetry or some shape) can be ruined because of extra element. This is of course very minor, just visual thing.
b. Adding up unnecessary overhead. Because the expression elements is always executed, since it is added before the main branch. Each script/expression will add approx 3-4 ms overhead (depends on the phone and perfomance mode, it can be up to 10-20 ms). This is small, but when you use loop, let say 100 times checking for some var, it can add up to 1-2 seconds of delay.
c. Adding up element execution count. Since it add extra element, if your flow is executed frequently, you might need to increase the AES count (Automagic Emergency Stop). This will be amplified again if you use loop also.


3. Script + exception
Third method use a rarely used connection in our daily flow, exception (to see how to create it, check this out : viewtopic.php?f=5&t=7314#p20955). This is similar to method 2, script + expression. But instead of assigning {command} then return, we purposely create error to trigger error exception. Then we catch the error by adding new element hooked up using exception connection. This is not a common way to stop a script, so actually not recommended.

Code: Select all

start = "This line is executed normally";
//uncomment the value to test
//value = "stop listening";

if(value == "stop listening")
  1/0;

// anything below here won't be executed if condition above is true
msg = "msg exists if the condition above is false";
Again, the script is every similar to method 1 and 2. The difference now, we don't deal with return anymore. We simply create error by using 1/0. This guarantee 100% error. You can use your own method, but I use this because very short and my own personal preference.

If the value is "stop listening", the 1/0 will be executed and raised error exception. Since we have attached another element to catch the exception, the flow branch to the exception connection. The element must be added, even if it is a blank element or just blank script. I usually just use Notification on Screen (toast message) to notify that the flow has stopped. It serves as the exception catcher and gives visual clue at the same time.

If value is not "stop listening", 1/0 not executed. The script continue as usual and continue to the Main logic branch as nothing has happened. There is no need additional expression to check it anymore.


Plus :
a. Only the script is in the main branch. This doesn't add any overhead or addition element execution to the whole flow.
b. Works with Control UI. You can replace script with Control UI, and still works find
c. Visual design is good. Again, you main script still inside the same script element.
d. Error creation can be placed anywhere and multiple times. You can use multiple if() 1/0.

Minus :
a. Require 2 elements, can be bad for big flows or tight space design, same as method 2. The exception must be caught with the exception connection. If not, your flow will always stop in error.
b. Difficult to trace other exception error. If there is other error happen, exception always executed. So you don't if the error is caused by you intentional error or other error. That's why you should use this only if you can be so sure that there is no other error can occur.
c. Not a good practice to stop a flow. Since it involves intentional error, it seems to violate our usual way of scripting. Ues this only if the exception branch occur less likely than the main branch.


Test Flow
You can download my test flow and connect each branches accordingly : Stopping Script
Simply connect from the trigger to the branch you want. Then connect the Main logic branch to the Debug dialog to test it.

At each method, if you execute directly, you will see {msg} appear in debug dialog. This is normal execution without stopping the script.
Now if you uncomment the value (3rd line in each method), then it will trigger the stopping script. Hence {msg} never assigned with value and you will never see it in the debug dialog (or no debug dialog at all for the exception method).


Advance
At each method, you can shorten or put some extra script
1. At expression, actually you only need the boolean true or false. So you instead of return true at the end, you can just put

Code: Select all

true
This will still result in true branching. But as a good practice, always use return true (or false), to let you identify the stopping easier.
2. Script at method 2 can be coupled with other condition, not necessaryly expression. You can use confimation dialog or maybe assigning some variable and use condition which check for this var (example Host reachable, app task running, location)
3. At exception method, you can assign some value to certain var first before creating the error. This can help you to separate the error message for each different case. Example : for path not set, notify with error message "path is not set", or for location is not correct, notify with message "location is too near/far"

Variation
At method 2 with script + expression, I have some variation that I use to branch out different logic. In the above method, we can stop the flow for false branch. I use the false branch for other purpose. And I give if() block to all available code. If the above method use A B C if(D) E F; the if(D) is the checking part. My variation will be if(A) if(B) if(C) if(D) if(E) if(F), every block is protected by if(). Then each block assign certain value, so it can be splitted to multiple expression. I use this for a gigantic code in a script, then splitted out accordingly after the script. Saving so many spaces, while keeping the main code in single script element.

Combination
You can combine method 3 (exception) with method 1 and 2. Single expression usually ended up in true or false, so 2 branch. You can add the 3rd branch by using the intentional error creation, forcing it to go to the exception branch. You can also add expression after the exception to check what is the error and branch accordingly again.

Which to use
Since there are 3 methods here, which one should we use? Depends on your need and style. I use all of them in different situation and needs. To summarize it
1. Single expression : Using if() to return false and stop the flow; or return true at the end of the expression to go to the main branch
2. Script + expression : Using if() to set a variable and return. Then using expression to check the variable, decide whether to stop the flow or continue to main branch
3. Script + exception : Using if() to create error, then branch to exception branch as to stop the flow. If no error, continue to main branch as usual

In general I will use method
1. Single expression : when the script is short, only several lines. Expression are meant to be short and branch fast
2. Script + expression : the script is full-blown, can be up to hundreds of lines, or it is Control UI. Or maybe I need other checking beside expression, that can be combined with script too.
3. Script + exception : when the script is less likely to be error. I usually use this with control UI, coupled with the while()-sleep looping. See the topic about how to set the value of the sleep needed in Control UI : viewtopic.php?f=5&t=7108


Epilogue
As usual, there is no strict guideline. Just use your own style and method. You can follow these, or you can modify it to suit your needs. Happy scripting.
Index of Automagic useful thread List of my other useful posts (and others')
Xiaomi Redmi Note 5 (whyred), AOSP Extended v6.7 build 20200310 Official, Android Pie 9.0, Rooted.

natong
Posts: 80
Joined: 29 Aug 2015 05:24

Re: stop/exit flow command in script

Post by natong » 20 Oct 2018 13:36

Thanks for good document. Currently I using method 2 and worry about unnecessary overhead task too. Method 3 is very good trick. I never think about it.

Locked