Example for if
…else
:
In this example, if tag A
equals 1, assign 100 to A
. Otherwise, assign 500 to A
.
Code Block | ||
---|---|---|
| ||
if (tag.read("A") == 1) {
tag.write("A", 100.0);
} else {
tag.write("A", 500);
} |
...
The tag "A_WHILE" value will consistently be read and written to the variable val
in this example. If the value in the variable val
is less than 500, keep incrementing by 1 and store the incremented value to the tag "A_WHILE" in each loop. Once the variable val
equals 500, end the loop and exit the script.
Code Block | ||
---|---|---|
| ||
var val = tag.read("A_WHILE");
while (val < 500) {
val = val + 1;
tag.write("A_WHILE", val)
thread.msleep(10)
} |
Example for do
...while
:
The tag "DO_VAL" value will consistently be read and written to the variable do_val
in this example. The do
loop will increment by 1 and store the value into the tag “DO_VAL” before executing the while
loop. Once the variable do_val
equals 200, the while
loop will terminate, but the do
statement will stay executable allowing a user to increment by 1 manually.
Code Block | ||
---|---|---|
| ||
var do_val = tag.read("DO_VAL");
do {
do_val = do_val + 1;
tag.write("DO_VAL", do_val)
thread.msleep(100)
} while (do_val < 200); |
Example for for
In this example, the initial expression is 0. When i
is less than or equal to 100, i
will increment by 1 and store the result in A
every execution until i
equals 100. Once i
equals 100, the for
loop will end.
Code Block | ||
---|---|---|
| ||
var i = tag.read("FOR_VAL");
for (i = 0; i <= 100; i = i + 1) {
tag.write("FOR_VAL", i)
thread.msleep(100)
} |
Example for switch
…case
…break
The tag "PET" value will consistently be read and written to the variable pet
in this example. The switch
will start matching the values between the variable pet
and the tag "PET." If the case matches, the expression in the case will execute accordingly. When finished, the break
statement will terminate the switch
or loop altogether.
Code Block | ||
---|---|---|
| ||
var pet = tag.read("PET")
switch (pet) {
case "cat":
tag.write("CASE_STR", "I love cats too!");
break;
case "dog":
tag.write("CASE_STR", "I love dogs too!");
break;
} |
...
In this example, the function
keyword calls the values in myFunction
to perform multiplication and store the results to the tag "RETURN_ONE." The return
statement then outputs the value in the tag "RETURN_ONE."
Code Block | ||
---|---|---|
| ||
function myFunction(a, b) {
a = a * b;
tag.write("RETURN_ONE", a);
return tag.read("RETURN_ONE");
}
var x = myFunction(4, 3); |
Example for default
In this example, when the script executes, the tag "REPLY" will be assigned the string value "Hello, my name is CIMON. What is your name?" by default. If the user enters their name, the "NAME" string tag will store their name. If the name entered is "Christian," the "Hi Christian!" will update the "REPLY" string tag.
...
language | js |
---|
...
The following table is a set of statements and functions for the script.
...
Type
...
Description
...
if
…else
Statement
...
The if
statement executes the instruction code when the condition is true. Otherwise, the else
statement will execute its instruction code. if
...else
statements can be nested inside another if
...else
statement using the else if
statement which creates a nested loop. Please note, there is no limitation on how many else if
can be nested inside if
...else
statement.
...
while
Statement
...
The while
statement execution will continue until the expression becomes false
.
...
do
…while
Statement
...
The do
statement is executed at least once before while
condition is evaluated. The loop will repeat until while
condition becomes false
.
...
for
Statement
...
A for
loop repeats until a specified condition becomes false
. Below is the standard syntax used by a for
loop:
Code Block | ||
---|---|---|
| ||
for ([initialExpression]; [conditionExpression]; [incrementExpression]) {
// instruction code
} |
...
switch
…case
Statement
...
Only one default statement can be used in the switch
statement. The switch
expression is evaluated once by comparing the expression value with the values of each case
. If matched, the associated instruction code is executed. Otherwise, default
instruction code is executed.
Additionally, case
statement must be followed by a constant value and terminated with a colon. Tags, variables, or expressions cannot be used in a case
statement.
...
break
Statement
...
Terminates a switch
or a loop completely. The break
statement prevents the execution of the following case
in the switch
statement. Adding a break
function to the last case
is good practice. If return
is present, break
statement is not necessary.
...
function
Keyword
...
A block of code designed to perform a particular task and will only executes when something calls it.
...
return
Statement
...
Ends function execution and the function caller will return specifies a value.
...
default
Statement
...
The default
statement is optional and does not need a constant. break
statement is not needed either. This statement will execute if no cases get executed.
...
continue
Statement
...
The continue
statement terminates execution of the current iteration in a loop and then execute the next iteration in a loop. This statement does not terminate the loop completely.
...
system.runScript()
Function
...
When an external program is called directly, the caller will be in standby mode until the called program (the external program) ends its operation. However, if an external program is called using runScript()
, the caller will not wait until the end of the called program. Both the caller and called program will be executed in parallel.
The following table lists keywords and statements that can be used in a script for better control flow.
Name | Description | |||||
| The | |||||
| A | |||||
| The | |||||
| A
A | |||||
| Terminates a | |||||
| The | |||||
| The | |||||
| The | |||||
| A function is a block of code designed to perform a particular task. Functions can take in a set of arguments and can return a value. Functions are one of the primary methods for organizing and re-using code. | |||||
| Ends the function execution, and the function will return the specified value to the caller. | |||||
| Can be used within a function to get a list of values that were passed into the function call. | |||||
| The | |||||
| Throws an |
Examples for if
…else
and else if
:
In this example, a notification saying “boolTag is true” will be displayed if the tag’s value is true.
Code Block |
---|
if (tag.read("boolTag")) notification.send("boolTag is true."); |
You can execute multiple lines of code after an if
statement by enclosing the code in curly braces:
Code Block |
---|
if (tag.read("boolTag")) {
notification.send("boolTag is true.");
thread.sleep(1);
} |
In the example below, if tag “A” equals 1, we assign 100 to tag “B”. If “A” equals 2, we assign 200 to “B”. Otherwise, we assign 500 to “B”.
Code Block | ||
---|---|---|
| ||
var value = tag.read("A");
if (value == 1) {
tag.write("B", 100);
} else if (value == 2) {
tag.write("B", 200);
} else {
tag.write("B", 500);
} |
Note that the conditions for if
and else if
must be enclosed in parentheses.
Examples forwhile
:
A while
loop is like an if
statement that keeps executing the specified code until the condition becomes false
.
In the example below, the tag "A" will be incremented from 0 to 100 over the course of about one second:
Code Block | ||
---|---|---|
| ||
var val = 0;
while (val <= 100) {
tag.write("A", val);
val = val + 1;
thread.msleep(10);
} |
Example for do
…while
:
A do
while
loop is a while
loop that always executes the specified code at least once.
This can be useful for buttons that perform some looping action while being held down. Even if the button is immediately released, the action will be performed at least once.
When the script below is executed, the tag “boolTag” will be toggled at least once. If the tag “doLoop” is true after one second, “boolTag” will continue to toggle once per second. Once “doLoop” becomes false
, the toggling will stop and the script will end.
Code Block |
---|
do {
tag.write("boolTag", !tag.read("boolTag"));
thread.sleep(1);
} while (tag.read("doLoop")); |
In practice, the script above might be tied to a button’s “On Press” action. Then, for the “On Release” action, you would just need to set the “doLoop” tag to false:
Code Block |
---|
tag.write("doLoop", false); |
This would cause the while
loop in the “On Press” script to end.
Examples for for
A for
loop lets you provide 3 expressions that define the behavior of the loop.
The first expression will be run once before the loop first executes. Typically, this is used to initialize a counter variable like
i
orj
.The second expression is a condition that will be checked before each execution of the loop. If the condition is
false
, the loop will not execute, and thefor
loop will end. Often, this is used to check if the counter variable has reached some value.The third expression is run at the end of each execution of the loop. Typically, this is used to increment the counter variable.
In this example, the initial value of i
is 0. When i
is less than or equal to 100, i
will increment by 1 and store the result in the tag “myTag” every iteration until i
equals 100. Once i
equals 100, the for
loop will execute one more time, then end.
Code Block | ||
---|---|---|
| ||
for (var i = 0; i <= 100; i++) {
tag.write("myTag", i)
thread.msleep(100)
} |
Looping Over Arrays
A for
loop can also be used to iterate through arrays using the array index.
Note: the first element in an array uses index 0. The last element will use the index array.length - 1
.
Code Block |
---|
// Define an array
var myArray = [
"val1",
"val2",
"val3"
]
// Get the indices
for (var index in myArray) {
notification.send(index);
} // 0, 1, 2
// Get the values
for (var index in myArray) {
var value = myArray[index];
notification.send(value);
} // val1, val2, val3 |
Looping Over Objects
A for
loop can also be used to iterate through objects using keys.
Code Block |
---|
// Define an object
var myObject = {
"key1": "val1",
"key2": "val2",
"key3": "val3"
}
// Get the keys
for (var key in myObject) {
notification.send(key);
} // key1, key2, key3
// Get the values
for (var key in myObject) {
var value = myObject[key];
notification.send(value);
} // val1, val2, val3 |
Example forcontinue
The continue
keyword can be used in a while
or for
loop to move to the next iteration without breaking out of the loop. This can be used to skip specific items.
In this example, a notification will be shown for each number from 0 to 9, except for 5:
Code Block | ||
---|---|---|
| ||
for (var i = 0; i < 10; i++) {
if (i == 5) {
continue;
}
notification.send(i);
} // 0, 1, 2, 3, 4, 6, 7, 8, 9 |
Using a while
loop instead:
Code Block |
---|
var i = 0;
while (i < 10) {
if (i == 5) {
i++; // Don't forget to increment the counter before continuing!
continue;
}
notification.send(i);
i++;
} // 0, 1, 2, 3, 4, 6, 7, 8, 9 |
Examples for switch
…case
, break
, and default
In this example, when the script executes, we will send the notification "Hello, <name>"
by default. If the name value is empty, we send "Could not load name."
instead. Finally, if the name is "admin", then the admin console page will be opened.
The script below could be bound to the “Value Changed” property for the “name” tag.
Code Block | ||
---|---|---|
| ||
var name = tag.read("name");
switch (name) {
case false:
notification.send("Could not load name.");
break;
case "admin":
notification.send("Loading admin console.");
page.open("adminConsole");
break;
default:
notification.send("Hello, " + String(name) + ".");
} |
The break
statement is used to break out of the switch
statement and prevent any other case
s from executing. If break
is not used, then multiple cases (including the default) may be executed. This can lead to unexpected behavior, so usually, each case
should have a break
statement.
Examples for function
,return
, and arguments
This example defines a function called multiply
which takes in two arguments a
and b
, then returns the product a * b
. The function is then used to set the value of x
.
Code Block | ||
---|---|---|
| ||
function multiply(a, b) {
return a * b;
}
var x = multiply(4, 3); // x is now equal to 12 |
Optional Arguments
Functions do not need to have any arguments. For example, here is a function that outputs a message, waits for 3 seconds, then exits the project.
Code Block |
---|
function fancyExit() {
notification.send("Closing the project in 3 seconds...");
thread.sleep(3);
system.exit();
} |
Function calls are not required to have the same number of arguments as the function definition. Any missing arguments will be set to undefined
. This can be used to set default values and optional arguments.
In the example below, the function multiplyDefault
returns a * b
if both a
and b
are specified. If b
is not specified, the function instead returns a * 10
.
Code Block |
---|
function multiplyDefault(a, b) {
if (b === undefined) {
b = 10;
}
return a * b;
}
multiplyDefault(2, 3); // 6
multiplyDefault(2); // 20 |
Note: when checking if a value is undefined
, you should use ===
instead of ==
. This is because undefined
, 0
, and ""
all evaluate to false
when using the ==
operator. So for example, 0 == undefined
will return true
.
The arguments
keyword
There is also a special arguments
keyword, which returns an array containing the values which were passed into the function. This allows functions to use a variable number of arguments.
For example, we can access the nth argument which was passed to the function:
Code Block |
---|
function returnThirdArg() {
return arguments[2];
}
returnThirdArg("a", "b", "c"); // "c" |
You can also use arguments
in a function definition, in which case arguments
will not include any of the named arguments. This is useful when you have a fixed number of required arguments and a variable number of optional arguments.
Code Block |
---|
function returnFirstExtraArg(x, y, z, arguments) {
return arguments[0];
}
returnFirstExtraArg("a", "b", "c", "d"); // "d" |
Examples for try
…catch
and throw
A try
…catch
statement can be used to gracefully handle situations that would normally cause the script to terminate. If the code in the try
block fails, this error is returned to the catch
block, which can then use (or ignore) the error.
In the catch
statement, you must give a name for the error from the try
block. It is common practice to use the name error
.
In the example below, the script is able to continue executing even though the function thisFunctionDoesNotExist
does not exist. Without the try
…catch
block, the script would instead throw an error.
Code Block |
---|
try {
thisFunctionDoesNotExist();
} catch (error) {
// Do nothing
}
notification.send("Executing the rest of the script...");
// Perform more actions |
Checking Whether a Tag is Loaded or Not
When a project first starts, some tags may not be loaded yet, in which case tag.read
or tag.write
will throw an error. Remote tags may take a few seconds to load, depending on the polling rate, communication method, and the size of the project.
The example script below would run on project startup and wait for a remote tag to become readable. Once the tag can be read, we run the rest of the startup actions (which might depend on the tag being loaded).
Code Block |
---|
// Inside main startup script "Startup1"
var loading = true;
var waited = 0;
while (loading) {
try {
tag.read("remoteTag");
loading = false;
} catch (error) {
if (waited > 10000) {
throw Error('Could not load tag "remoteTag" after 10 seconds. Please check the connection and restart the project.');
}
thread.msleep(100);
waited += 100;
}
}
// Now do any other startup actions |
Checking an Error Code From a Tag
The throw
statement can be used to manually create an error. This is typically used within a try
block, so that the custom Error
object can then be handled by a catch
block.
The throw
statement can also be used outside of a try
block, in which case the entire script will terminate with an error. This can be useful for scripts that are expected to be called via system.importScript
, or when you wish to immediately terminate a script.
The example below checks the value of an error code tag “errorCode” before attempting to turn a motor on using the tag “motorOn”. Depending on the error code, the script will display a different notification. If there is a problem with tag.read
or tag.write
, this will also be handled using the notification instead of terminating the script.
Code Block |
---|
try {
var errorCode = tag.read("errorCode");
switch (errorCode) {
case 0:
tag.write("motorOn", true);
break;
case 1:
throw Error("Power Disconnected");
break;
case 2:
throw Error("Mechanical Failure")
break;
case 3:
throw Error("Invalid Operation Mode")
break;
default:
throw Error("Unrecognized Error Code")
}
} catch (error) {
var errorString = "Could not turn on motor: "
errorString += error.message;
notification.send(errorString)
} |
Built-In Functions for Script Importing and Execution
Canvas also provides built-in functions which allow you to utilize scripts from within other scripts.
Name | Description |
| Calls another script in the project directly by name. This pauses the calling script until the target script finishes executing. This can also be used to import functions and variables from other scripts. Returns the value of the last expression in the target script. |
| Runs another script immediately in a separate thread. Both the calling and the target script will be executed simultaneously. Returns Note: Up to 100 scripts can run at the same time. Additional scripts will display a warning and fail to run. Once the scripts finish executing, more can be run. |
Examples forsystem.importScript
and system.runScript
Both system.importScript
and system.runScript
can be used to execute scripts in the project from within another script. The difference is that importScript
runs the script in the same thread, and runScript
runs the script in a different thread.
To illustrate the difference, consider the scripts “A”, “B”, and “C” below:
Code Block |
---|
// A
thread.sleep(1);
notification.send("In A"); |
Code Block |
---|
// B
notification.send("In B");
thread.sleep(1);
notification.send("Stil in B") |
Code Block |
---|
// C
notification.send("In C"); |
Running Scripts in Order
We can use system.importScript
to execute the scripts in order, like so:
Code Block |
---|
system.importScript("A");
system.importScript("B");
system.importScript("C"); |
The result would be:
sleep for 1 second
display “In A”
display “In B”
sleep for 1 second
display “Still in B”
display “In C”
With system.importScript
, the calling script is paused until the target scripts finishes. If there is an error in the target script, the calling script will throw an error as well.
Running Scripts Immediately
Using system.runScript
lets you run scripts in parallel. The target script will immediately begin running on another thread.
If we try calling the 3 scripts defined above using runScript
…
Code Block |
---|
system.runScript("A");
system.runScript("B");
system.runScript("C"); |
The result will be:
display “In B”
display “In C”
sleep for 1 second
display “In A”
display “Still in B”
With runScript
, the calling script continues executing, and the target script begins shortly after.
Note that with runScript
you cannot guarantee the order of execution. In this case, the order of steps 1 and 2 may be reversed, as well as steps 4 and 5.
Note: in general, running multiple script threads is slower than running multiple actions in a single script. This is because it takes time for the operating system to switch between threads.
Communicating between Script Threads
Currently, there is no way to directly pass values between script threads.
However, scripts can read and write tag values. This can be used as a simple way to send messages between scripts.
For example, consider the following scripts:
Code Block |
---|
// Event Loop, runs on startup while (true) { if (tag.read("triggerTag")) { notification.send("The event was recognized!"); tag.write("REPLY", "Hi Christian!")triggerTag", false); } breakthread.msleep(100); } |
Code Block |
---|
// Trigger script, runs default:on button press, page load, etc. tag.write("REPLYtriggerTag", "Hello, my name is CIMON. What is your name?"); } |
...
The tag "TIMER" value will consistently be read and written to the variable timer
in this example. When the value in the variable timer
is less than 5000 and 10,000, the value in the variable timer
will be incremented by 101 each loop until it reaches 5000. When the value in the variable timer
reached 5000, the value in the variable timer
will be incremented by 1 instead. Once the value in the variable timer
reaches 10,000, the while loop will be terminated.
Code Block | ||
---|---|---|
| ||
var timer = tag.read("TIMER");
while (timer < 10000) {
timer = timer + 1;
tag.write("TIMER", timer);
thread.msleep(100)
if (timer < 5000) {
timer = timer + 100;
tag.write("TIMER", timer);
continue;
thread.msleep(100)
}
} |
...
In this example, the system.runScript
function will call and executes the script name “WHILE_Script.”
Code Block | ||
---|---|---|
| ||
system.runScript("WHILE_Script")true); |
When the trigger script is executed, the event loop script will see the changed tag value and display a notification. Then it will set the trigger tag to false
again so that future events can be processed.
Note that reading and writing to a tag using multiple loops can significantly reduce project performance. In general, you should avoid having more than one loop running at any given time.
Importing Functions and Variables
system.importScript
can be used to import functions and variables from other scripts. In this example, we have a script called "myFunctionScript", which has a function fib
that returns the Fibonacci sum:
Code Block |
---|
// myFunctionScript
function fib(n) {
if (n <= 0) {
return 0;
} else if (n == 1) {
return 1;
} else {
return fib(n - 1) + fib(n - 2);
}
} |
If we want to use the function fib
in a different script, we just need to import "myFunctionScript" first:
Code Block |
---|
system.importScript("myFunctionScript");
notification.send(fib(7)) // 13 |
This also works with variables. For example, say we have another script called “myVariableScript”:
Code Block |
---|
// myVariableScript
var x = 7; |
We could then load the variable x
from another script as shown below:
Code Block |
---|
system.importScript("myVariableScript");
notification.send(x); // 7 |
Getting a Return Value From a Script
system.importScript
returns the value of the last expression in the target script.
Here is a simple example where the target script returns the value 12. This value is then used in the calling script.
Code Block |
---|
//targetScript
var x = 7;
x + 5; |
Code Block |
---|
// Calling Script
var returnValue = system.importScript("targetScript");
notification.send(returnValue); // 12 |