43. Expressions¶
Many SolarNode components support a general "expressions" framework that can be used to calculate values using a scripting language. SolarNode comes with the Spel scripting language by default, so this guide describes that language.
A common use case for expressions is to derive datum property values out of the raw property values captured from a device. In the SolarNode Setup App a typical datum data source component might present a configurable list of expression settings like this:
In this example, each time the data source captures a datum from the device it is communicating with
it will add a new watts
property by multiplying the captured amps
and volts
property values.
In essence the expression is like this code:
watts = amps × volts
43.1 Datum Expressions¶
Many SolarNode expressions are evaluated in the context of a datum, typically one captured from a device SolarNode is collecting data from. In this context, the expression supports accessing datum properties directly as expression variables, and some helpful functions are provided.
43.1.1 Datum property variables¶
All datum properties with simple names can be referred to directly as variables. Here simple just means a name that is also a legal variable name. The property classifications do not matter in this context: the expression will look for properties in all classifications.
For example, given a datum like this:
{
"i": {
"watts" : 123
},
"a": {
"wattHours" : 987654321
},
"s": {
"mode" : "auto"
}
}
The expression can use the variables watts
, wattHours
, and mode
.
43.1.2 Other variables¶
A datum expression will also provide the following variables:
Property | Type | Description |
---|---|---|
datum |
Datum |
A Datum object, in case you need direct access to the functions provided there. |
meta |
DatumMetadataOperations |
Get datum metadata for the current source ID. |
parameters |
Map<String,Object> |
Simple map-based access to all parameters passed to the expression. The available parameters depend on the context of the expression evaluation, but often include things like placeholder values or parameters generated by previously evaluated expressions. These values are also available directly as variables, this is rarely needed but can be helpful for accessing dynamically-calculated property names or properties with names that are not legal variable names. |
props |
Map<String,Object> |
Simple map based access to all properties in datum . As datum properties are also available directly as variables, this is rarely needed but can be helpful for accessing dynamically-calculated property names or properties with names that are not legal variable names. |
sourceId |
String |
The source ID of the current datum. |
43.1.3 Functions¶
Some functions are provided to help with datum-related expressions.
43.1.3.1 Bit functions¶
The following functions help with bitwise integer manipulation operations:
Function | Arguments | Result | Description |
---|---|---|---|
and(n1,n2) |
Number , Number |
Number |
Bitwise and, i.e. (n1 & n2) |
andNot(n1,n2) |
Number , Number |
Number |
Bitwise and-not, i.e. (n1 & ~n2) |
narrow(n,s) |
Number , Number |
Number |
Return n as a reduced-size but equivalent number of a minimum power-of-two byte size s |
narrow8(n) |
Number |
Number |
Return n as a reduced-size but equivalent number narrowed to a minimum of 8-bits |
narrow16(n) |
Number |
Number |
Return n as a reduced-size but equivalent number narrowed to a minimum of 16-bits |
narrow32(n) |
Number |
Number |
Return n as a reduced-size but equivalent number narrowed to a minimum of 32-bits |
narrow64(n) |
Number |
Number |
Return n as a reduced-size but equivalent number narrowed to a minimum of 64-bits |
not(n) |
Number |
Number |
Bitwise not, i.e. (~n) |
or(n1,n2) |
Number , Number |
Number |
Bitwise or, i.e. (n1 | n2) |
shiftLeft(n,c) |
Number , Number |
Number |
Bitwise shift left, i.e. (n << c) |
shiftRight(n,c) |
Number , Number |
Number |
Bitwise shift left, i.e. (n >> c) |
testBit(n,i) |
Number , Number |
boolean |
Test if bit i is set in integer n , i.e. ((n & (1 << i)) != 0) |
xor(n1,n2) |
Number , Number |
Number |
Bitwise xor, i.e. (n1 ^ n2) |
Tip
All number arguments will be converted to BigInteger
values for the bitwise operations, and
BigInteger
values are returned.
43.1.3.2 Datum stream functions¶
The following functions deal with datum streams. The latest()
and offset()
functions give you
access to recently-captured datum from any SolarNode source, so you can refer to any datum stream
being generated in SolarNode. They return another datum expression root object, which means you have
access to all the variables and functions documented on this page with them as well.
Function | Arguments | Result | Description |
---|---|---|---|
hasLatest(source) |
String |
boolean |
Returns true if a datum with source ID source is available via the latest(source) function. |
hasLatestMatching(pattern) |
String |
Collection<DatumExpressionRoot> |
Returns true if latestMatching(pattern) returns a non-empty collection. |
hasLatestOtherMatching(pattern) |
String |
Collection<DatumExpressionRoot> |
Returns true if latestOthersMatching(pattern) returns a non-empty collection. |
hasMeta() |
boolean |
Returns true if metadata for the current source ID is available. |
|
hasMeta(source) |
String |
boolean |
Returns true if datumMeta(source) would return a non-null value. |
hasOffset(offset) |
int |
boolean |
Returns true if a datum is available via the offset(offset) function. |
hasOffset(source,offset) |
String , int |
boolean |
Returns true if a datum with source ID source is available via the offset(source,int) function. |
latest(source) |
String |
DatumExpressionRoot |
Provides access to the latest available datum matching the given source ID, or null if not available. This is a shortcut for calling offset(source,0) . |
latestMatching(pattern) |
String |
Collection<DatumExpressionRoot> |
Return a collection of the latest available datum matching a given source ID wildcard pattern. |
latestOthersMatching(pattern) |
String |
Collection<DatumExpressionRoot> |
Return a collection of the latest available datum matching a given source ID wildcard pattern, excluding the current datum if its source ID happens to match the pattern. |
meta(source) |
String |
DatumMetadataOperations |
Get datum metadata for a specific source ID. |
metaMatching(pattern) |
String |
Collection<DatumMetadataOperations> |
Find datum metadata for sources matching a given source ID wildcard pattern. |
offset(offset) |
int |
DatumExpressionRoot |
Provides access to a datum from the same stream as the current datum, offset by offset in time, or null if not available. Offset 1 means the datum just before this datum, and so on. |
offset(source,offset) |
String , int |
DatumExpressionRoot |
Provides access to an offset from the latest available datum matching the given source ID, or null if not available. Offset 0 represents the "latest" datum, 1 the one before that, and so on. SolarNode only maintains a limited history for each source, do do not rely on more than a few datum to be available via this method. This history is also cleared when SolarNode restarts. |
selfAndLatestMatching(pattern) |
String |
Collection<DatumExpressionRoot> |
Return a collection of the latest available datum matching a given source ID wildcard pattern, including the current datum. The current datum will always be the first datum returned. |
43.1.3.3 Math functions¶
Expressions support basic math operators like +
for addition and *
for
multiplication. The following functions help with other math operations:
Function | Arguments | Result | Description |
---|---|---|---|
avg(collection) |
Collection<Number> |
Number |
Calculate the average (mean) of a collection of numbers. Useful when combined with the group(pattern) function. |
ceil(n) |
Number |
Number |
Round a number larger, to the nearest integer. |
ceil(n,significance) |
Number , Number |
Number |
Round a number larger, to the nearest integer multiple of significance . |
down(n) |
Number |
Number |
Round numbers towards zero, to the nearest integer. |
down(n,significance) |
Number , Number |
Number |
Round numbers towards zero, to the nearest integer multiple of significance . |
floor(n) |
Number |
Number |
Round a number smaller, to the nearest integer. |
floor(n,significance) |
Number , Number |
Number |
Round a number smaller, to the nearest integer multiple of significance . |
max(collection) |
Collection<Number> |
Number |
Return the largest value from a set of numbers. |
max(n1,n2) |
Number , Number |
Number |
Return the larger of two numbers. |
min(collection) |
Collection<Number> |
Number |
Return the smallest value from a set of numbers. |
min(n1,n2) |
Number , Number |
Number |
Return the smaler of two numbers. |
mround(n,significance) |
Number , Number |
Number |
Round a number to the nearest integer multiple of significance . |
round(n) |
Number |
Number |
Round a number to the nearest integer. |
round(n,digits) |
Number , Number |
Number |
Round a number to the nearest number with digits decimal digits. |
roundDown(n,digits) |
Number , Number |
Number |
Round a number towards zero to the nearest number with digits decimal digits. |
roundUp(n,digits) |
Number , Number |
Number |
Round a number away from zero to the nearest number with digits decimal digits. |
sum(collection) |
Collection<Number> |
Number |
Calculate the sum of a collection of numbers. Useful when combined with the group(pattern) function. |
up(n) |
Number |
Number |
Round numbers away from zero, to the nearest integer. |
up(n,significance) |
Number , Number |
Number |
Round numbers away from zero, to the nearest integer multiple of significance . |
43.1.3.4 Node metadata functions¶
All the Datum Metadata functions like metadataAtPath(path)
can be invoked
directly, operating on the node's own metadata instead of a datum stream's metadata.
43.1.3.5 Operational functions¶
The following functions deal with general SolarNode operations:
Function | Arguments | Result | Description |
---|---|---|---|
isOpMode(mode) |
String |
boolean |
Returns true if the mode operational mode is active. |
43.1.3.6 Property functions¶
The following functions help with expression properties (variables):
Function | Arguments | Result | Description |
---|---|---|---|
has(name) |
String |
boolean |
Returns true if a property named name is defined. Can be used to prevent expression errors on datum property variables that are missing. |
group(pattern) |
String |
Collection<Number> |
Creates a collection out of numbered properties whose name matches the given regular expression pattern . |
43.2 Expression examples¶
Let's assume a captured datum like this, expressed as JSON:
{
"i" : {
"amps" : 4.2,
"volts" : 240.0
},
"a" : {
"reading" : 38009138
},
"s" : {
"state" : "Ok"
}
}
Then here are some example Spel expressions and the results they would produce:
Expression | Result | Comment |
---|---|---|
state |
Ok |
Returns the state status property directly, which is Ok . |
datum.s['state'] |
Ok |
Returns the state status property explicitly. |
props['state'] |
Ok |
Same result as datum.s['state'] but using the short-cut props accessor. |
amps * volts |
1008.0 |
Returns the result of multiplying the amps and volts properties together: 4.2 × 240.0 = 1008.0 . |
43.2.1 Datum stream history¶
Building on the previous example datum, let's assume an earlier datum for the same source ID had been collected with these properties (the classifications have been omitted for brevity):
{
"amps" : 3.1,
"volts" : 241.0,
"reading" : 38009130,
"state" : "Ok"
}
Then here are some example expressions and the results they would produce given the original datum example:
Expression | Result | Comment |
---|---|---|
hasOffset(1) |
true |
Returns true because of the earlier datum that is available. |
hasOffset(2) |
false |
Returns false because only one earlier datum is available. |
amps - offset(1).amps |
1.1 |
Computes the difference between the current and previous amps properties, which is 4.2 - 3.1 = 1.1 . |
43.2.2 Other datum stream history¶
Other datum stream histories collected by SolarNode can also be accessed via the
offset(source,offset)
function. Let's assume SolarNode is collecting a datum stream for the source
ID solar
, and had amassed the following history, in newest-to-oldest order:
[
{"amps" : 6.0, "volts" : 240.0 },
{"amps" : 5.9, "volts" : 239.9 }
]
Then here are some example expressions and the results they would produce given the original datum example:
Expression | Result | Comment |
---|---|---|
hasLatest('solar') |
true |
Returns true because of a datum for source solar is available. |
hasOffset('solar',2) |
false |
Returns false because only one earlier datum from the latest with source solar is available. |
(amps * volts) - (latest('solar').amps * latest('solar').volts) |
432.0 |
Computes the difference in energy between the latest solar datum and the current datum, which is (6.0 × 240.0) - (4.2 × 240.0) = 432.0 . |
If we add another datum stream for the source ID solar1
like this:
[
{"amps" : 1.0, "volts" : 240.0 }
]
If we also add another datum stream for the source ID solar2
like this:
[
{"amps" : 3.0, "volts" : 240.0 }
]
Then here are some example expressions and the results they would produce given the previous datum examples:
Expression | Result | Comment |
---|---|---|
sum(latestMatching('solar*').?[amps>1].![amps * volts]) |
2160 |
Returns the sum power of the latest solar and solar2 datum. The solar1 power is omitted because its amps property is not greater than 1 , so we end up with (6 * 240) + (3 * 240) = 2160 . |
43.3 Datum metadata¶
Some functions return DatumMetadataOperations
objects. These objects
provide metadata for things like a specific source ID on SolarNode.
43.3.1 Datum metadata properties¶
The properties available on datum metadata objects are:
Property | Type | Description |
---|---|---|
empty |
boolean |
Is true if the metadata does not contain any values. |
info |
Map<String,Object> |
Simple map based access to the general metadata (e.g. the keys of the m metadata map). |
infoKeys |
Set<String> |
The set of general metadata keys available (e.g. the keys of the m metadata map). |
propertyInfoKeys |
Set<String> |
The set of property metadata keys available (e.g. the keys of the pm metadata map). |
tags |
Set<String> |
A set of tags associated with the metadata. |
43.3.2 Datum metadata general info functions¶
The following functions available on datum metadata objects support access to the general
metadata (e.g. the m
metadata map):
Function | Arguments | Result | Description |
---|---|---|---|
getInfo(key) |
String |
Object |
Get the general metadata value for a specific key. |
getInfoNumber(key) |
String |
Number |
Get a general metadata value for a specific key as a Number . Other more specific number value functions are also available such as getInfoInteger(key) or getInfoBigDecimal(key) . |
getInfoString(key) |
String |
String |
Get a general metadata value for a specific key as a String . |
hasInfo(key) |
String |
boolean |
Returns true if a non-null general metadata value exists for the given key. |
43.3.3 Datum metadata property info functions¶
The following functions available on datum metadata objects support access to the property
metadata (e.g. the pm
metadata map):
Function | Arguments | Result | Description |
---|---|---|---|
getPropertyInfo(prop) |
String |
Map<String,Object> |
Get the property metadata for a specific property. |
getInfoNumber(prop,key) |
String , String |
Number |
Get a property metadata value for a specific property and key as a Number . Other more specific number value functions are also available such as getInfoInteger(prop,key) or getInfoBigDecimal(prop,key) . |
getInfoString(prop,key) |
String , String |
String |
Get a property metadata value for a specific property and key as a String . |
hasInfo(prop,key) |
String , String |
String |
Returns true if a non-null property metadata value exists for the given property and key. |
43.3.4 Datum metadata global functions¶
The following functions available on datum metadata objects support access to both general and property metadata:
Function | Arguments | Result | Description |
---|---|---|---|
differsFrom(metadata) |
DatumMetadataOperations |
boolean |
Returns true if the given metadata has any different values than the receiver. |
hasTag(tag) |
String |
boolean |
Returns true if the given tag is available. |
metadataAtPath(path) |
String |
Object |
Get the metadata value at a metadata key path. |
hasMetadataAtPath(path) |
String |
boolean |
Returns true if metadataAtPath(path) would return a non-null value. |