Getting Started
Behaviour Scripts run on the server-side platform and determine how the information from Providers is processed for Assets. Information from the field is sent to the system and processed by the provider's gateway. That data is then applied to assets on the map through the behaviours system. By doing things this way, the system becomes nearly infinitely flexible and adaptive to all customer requirements.
Behaviours and Scripts
There are two separate but equals pieces to making a customized action in the system; a script, and a behaviour. The difference between the two is that a script is the actual business logic and the behaviour is the implementation of that logic.
Simply put, a Behaviour Script will have the full source code and a list of configurable variables, and a Behaviour is a group of values for the script's variables and a list of assets to target.
For example, a script may add a "speeding" tag to an asset if it moves faster an X. Two behaviours can be configured where one targets only some assets with a threshold of X = 110 Km/h, and the other behaviour targets other assets with X = 70 mph threshold.
Writing a Script
The behaviours system implements its own JavaScript execution context similar to a web browser. However, unlike a browser, the built in functions, objects, and constants are different. Writing a great behaviour script requires a new design pattern as well.
Closures
Each script is wrapped into a JavaScript closure when it is embedded into an asset's execution context. The closure allows you to define variables and working with them without worrying that other scripts might interfere with your values.
How the behaviour and script are embedded into the context
(function(/* script argument names are inserted here */) {
/*
* script code is inserted here
*/
})(/* behaviour values are inserted here */);
You do still have access to the global object, but it is recommended that you do not use it because other scripts (and script authors) also have access and if two scripts try to use the same global variable, cross-pollination can cause strange side-effects.
Lifetime
The execution of your scripts must be fast! Script execution is monitored and the service can choose to end execution of any script if it takes too long to run. The maximum duration of any script is 500 milliseconds.
Since execution should be as fast as possible, there are no embedded functions or methods for slowing script execution. Global functions such as setTimeout and setInterval are not defined, and the XMLHttpRequest is also not defined.
Lastly, the context is created and destroyed based on activity and memory pressure on the executing server. The context is destroyed if there is no activity from any of the asset's providers for up to 15 seconds, but the context may also be killed immediately if there are no new events to process. Since the context does not usually live from one event to the next, take care not to rely on variables that act as counters. Instead, you can use an asset attribute, or a status tag to indicate an event state.
Events
When scripts are embedded into the execution context they are executed immediately, not when data from the provider is processed. To properly handle processing events from a provider you need to bind to the events which are raised when new data arrives.
In order to listen for an handle provider data events, you can use the function Asset.on. To unbind a handler, you use Asset.off. Each script event is slightly different, but they all share some of the same properties.
Events are fired in this order
- position PositionEvent
Provider sends updated GPS information is sent. - place PlaceEvent (multiple)
For each Place interacted with by the Asset. - wiring WiringEvent (multiple)
Raised for each wiring state (or other sensor) the provider sends. - vbus VBusEvent (multiple)
Raised for each VBus value the provider sends. - dispatch DispatchEvent
Asset updates a DispatchTask. - finalize FinalizeEvent
After all events have been fired, this one happens last so developers can perform any last checks or clean-ups.
Or, when handling a timer event
- timer TimerEvent
An event fired by a invoking setTimer during a previous execution. - finalize FinalizeEvent
After all events have been fired, this one happens last so developers can perform any last checks or clean-ups.
Example script: Add or remove the "ignition" tag
To run through an example, the "Tag Ignition" script will add or remove the "ignition" tag to an asset when the provider's wiring state changes. The script can be configured to use a digital wire input, or an analog wire input, or a combination.
Arguments
- digitalWire number
The number of the digital input of the provider hooked up to sense ignition state. To disable using the digital input as ignition sense, configure the behaviour's argument with -1. - analogWire number
The number of the analog input of the provider hooked up to sense ignition state. To disable using the analog input as ignition sense, configure the behaviour's argument with -1. - analogThreshold number
The voltage threshold used to determine if the ignition is on or off. If the voltage detected by the provider is greater than or equal to the threshold, it's considered on. The value of this argument only matters if the analogWire is set to a value greater than -1.
Source
- var tagIgn = "ignition", // the status tag being applied
- digitalIgnition = digitalWire < 0, // a boolean to indicating whether the digital wire is "on" or unused
- analogIgnition = analogWire < 0; // a boolean to indicating whether the analog wire is "on" or unused
- if (asset.kind === "vehicle") { // this script only applies to vehicle type assets
- asset.on("wiring", function(event) {
- var attr = event.attribute; // event.attribute is an AssetAttribute
- if (!digitalIgnition && attr.name === "DIGITAL_INPUT_" + digitalWire) {
- digitalIgnition = !!attr.raw; // digital wire is "on"
- }
- if (!analogIgnition && attr.name === "ANALOG_INPUT_" + analogWire) {
- analogIgnition = attr.raw >= parseFloat(analogThreshold); // analog wire is "on"
- }
- });
- }
- asset.on("finalize", function(event) {
- var indexIgnition = asset.tags.indexOf(tagIgn); // is the "ignition" tag already applied
- if (digitalIgnition && analogIgnition) { // ignition is "on"
- if (indexIgnition < 0) asset.tags.push(tagIgn); // apply tag if needed
- } else { // ignition is "off"
- if (indexIgnition > -1) asset.tags.splice(indexIgnition, 1); // remove tag if needed
- }
- });
Pro tips!
- The event handlers are only bound for vehicles to reduce processing time.
- The FinalizeEvent is used because the digital and analog wire events are raised separately.
- Even for non-vehicles, the ignition will be removed if present.
Configuring a Behaviour
A behaviour the combination of a script and values for the script's arguments, and an expression used to choose which assets will implement the behaviour. It is possible (and even desirable) to use the same script for multiple behaviours if different departments of a company monitor their fleet in different ways.
When adding argument values, make sure to check the argument's notes for hints as to the kind of value the script expects. If a numeric argument expects an integer value and you supply a decimal, it can cause unintended consequences.
Behaviours are executed in an ordered sequence based on the Behaviour.priority value. For behaviours with the same priority, the list is sorted on the secondary column of Behaviour.id.
Targeting search expression patterns
A targeting pattern is a string that represents an in-the-moment search for objects. Search expression patterns are powerful and meant to be used by power users. When dealing with behaviours, the expression searches for assets.
The expressions are parsed into patterns composed of operators and terms. For a pattern to match an asset, all of the operators and terms must match. For an expression to match an asset, only one of the patterns needs to match. So, an expression is a group of patterns, and a pattern is composed of operators and terms. The order of the operators and terms do not matter.
It's easiest to look at a few examples.
Asset 1
{
"id": 1234,
"name": "Truck",
"kind": "vehicle",
"labels": [
"east-coast",
"head-office"
]
}
Asset 2
{
"id": 4567,
"name": "Alex",
"kind": "person",
"labels": [
"drivers",
"head-office"
]
}
Asset 3
{
"id": 7890,
"name": "Mike",
"kind": "person",
"labels": [
"east-coast",
"service"
]
}
In the above three example assets we have removed some of the properties and only kept those that will be used to target our examples. In the examples we have a combination of three different labels used to categorize the fleet in a specific way.
Operators
An operator is a known keyword, followed by a colon, followed by search terms. No spaces can be put in between the keyword, colon, or search terms. If the search terms have spaces, they can be wrapped in quotation marks (").
For example: to search for objects with the label East Coast you'd write the expression labels:"East Coast" or labels:east-coast. If you wanted to find all the vehicles, you'd write vehicles:*. And if you wanted to find all the vehicles labelled East Coast you'd write vehicles:* labels:east-coast.
Terms
Terms are anything that is not part of an operator. Terms will match an asset based only on name. Terms have their own magic though, and you can target a pattern much more precisely with terms than with operators. By using the # prefix followed by a number, you can target an object by its unique identifier. So by writing #1234 you will match any object that has the unique identifier of 1234. And to match just the asset with the id of 1234 you'd write asset:#1234.
Examples
- To match all East Coast assets (Asset 1 and 3)
- label:east-coast or labels:east-coast or label:"East Coast"
- To match all people (Asset 2 and 3)
- person:* or people:*
- To match Asset 1 specifically
- #1234
- To match assets with the string "ike" in the name (Asset 3)
- ike
- To match people labelled East Coast (Asset 3)
- person:* label:east-coast
- To match all assets labelled East Coast and labelled Head Office (Asset 1)
- label:east-coast,head-office or label:east-coast label:head-office
- To match all assets labelled East Coast or labelled Head Office (Asset 1, 2, and 3)
- label:"East Coast" | label:head-office
When configuring behaviours, it's possible to target an asset twice (or more times). Normally, this is the intended setup. However, if the same script configured twice with different argument values, it can play havoc with a company's asset.
Pro-tip!
The system only saves and returns the parsed format of the expression.
Input | Parsed | Description |
---|---|---|
labels:"East Coast" | labels:east-coast | Operator values are codified. |
Alex label:drivers | label:drivers alex | Terms come after operators. |
label:east-coast label:head-office | label:east-coast,head-office | Operators are grouped and their values are concatenated. |
label:"East Coast" label:east-coast | label:east-coast | Duplicate operators and terms are discarded. |
label:"East Coast" | label:head-office | label:east-coast|label:head-office | White-space is removed from between multiple patterns in an expression. |