Inline Insight Functions¶
ExoSense Administrators have three options for adding additional Insight modules to an ExoSense application:
- Third-party Insights from Exchange
- Create custom inline functions from within ExoSense
- Building a hosted external web service
Once insight functions have been enabled, end-users with the Asset Management permission can then use these functions in their asset configuration.
This document covers how to create and use custom inline Insight modules and functions from within ExoSense.
Custom Inline Insights¶
Advanced Topic: Building custom Inline Insights typically is an exercise typically done by a developer with some software background.
Custom Insights can be created and managed within an ExoSense application itself. These 'inline' functions operate natively in the data pipeline and do not require any external services.
Access to Custom Insights are controlled by your ExoSense Tier and management is available by administrative user permission.
User Experience¶
An end user who has the Asset Management permission can use these functions in their pipeline configuration for the asset. They will be provided with a list of modules and functions to choose from including the custom functions. Users are then able to choose matching signals and define any constants.
Creating Modules¶
Within ExoSense's INSIGHTS tab, you may define new modules for your collections of custom functions.
Creating Functions¶
Once you have a module, you can create and edit functions, which includes giving it a name, description, type (Transform or Rule), defining Inputs, Outputs, Constants and the actual function logic.
Duplicating¶
Functions can also be quickly duplicated, which provides a quick place to start to generate a new similar function.
Properties¶
Inputs, Outputs, and Constants must be defined. These properties inform and guide users who are configuring the specific function for an asset. If a function is mean to operate only on Temperature data, than the input definitions allow you to limit the application to only allow Temperature signals, with the ability to even restrict down to the unit type.
Logic¶
The custom insights support MathJS or JSON-e expressions. Reference syntax for these languages can be found here:
Math.js
When using MathJS, the 'Logic' is the contents to be provided within a math.evaluate() function
JSON-e
When using JSON-e, the 'Logic' is the JSON-e 'Template'. The Inputs(A,B,C) and Constants are the 'Context' given to the 'Template'
Consideration for functions with more than one input
Functions with more than one input are called each time a new value happens for any signal. When first used, the first signal value that triggers calling the function will likely be in a situation where other signals may not have values yet. In this case, the result is 'undefined', When a function returns undefined it is dropped.
To have a function be able to see which signal input (e.g. A
or B
), use meta.inlet
. See examples below.
Using Inputs in functions¶
Inputs are available as variables using the tags A
, B
, C
, D
, E
(up to 5 inputs are supported).
Example Function code with 3 Input Signals that are added
A + B + C
{ "$eval": "A + B + C" }
Using constants in functions¶
Constants that are defined are available by simply using the name of the defined constant
Handling Outputs in functions
If there is only one output, the result of the function automatically is applied to the output value.
Example Function
If A = 50 then the Output Signal = 150
A + 100
{ "$eval": "A + 100" }
In the case of multiple output signals, to reference the outlets, we use the set_outlet()
function. Normally, the results of a function go into the first outlet, but if we set the first outlet to a value, then that is used instead of the function results. Also, the set_outlet()
function matches the indexing of the language. So in Math.js the first outlet is 1
, whereas in JSON-e the first outlet is 0
.
Example with multiple output signals
If A = 50; then Output Signal 1 will be 50 and Output Signal 2 will be 1050
set_outlet(2, A + 1000); set_outlet(1, A)
[
{ "$eval": "set_outlet(1, A + 1000)" },
{ "$eval": "set_outlet(0, A)" }
]
If you don't want to return a value to each output every time, then set some of them to undefined
. As long as one outlet has a value, then function will produce output. We can show this with a slightly more complex bit of logic that filters values to one of the two outlets.
Example the shows multiple outputs but filters which output is used
In this case if A = 50; then Output Signal 1 = undefined (not set) and Output Signal 2 = 50
(A > 100)?outlets[1]=A:outlets[2]=A; outlets[1]
{
"$if": "A > 100",
"then": [
{ "$eval": "set_outlet(0,A)" },
{ "$eval": "set_outlet(1,null)" }
],
"else": [
{ "$eval": "set_outlet(0,null)" },
{ "$eval": "set_outlet(1,A)" }
]
}
Using Output Signal Values in functions¶
Sometimes you will have logic that builds on prior results. The outlets are available under the variables in order Z
, Y
, X
W
, V
with Z being the first output signal.
Example using the last output signals value (last result)
(isNumeric(Z)?Z:0) + A
{ "$if": "typeof(Z) == 'number'",
"then": { "$eval": "Z + A" },
"else": { "$eval": "A" }
}
Functions with non-numerical data¶
Functions can work with strings, boolean, JSON, and binary data also.
Example using JSON-e to generate a JSON output value
{
"url": {$eval: "A"},
"title": {$eval: "B"}
}
You can use the normalize()
function in JSON-e, which takes 1 or 2 parameters. First is the string to normalize()
, and second is a string of how. The second parameter is one of NFC, NFD, NFKC, NFKD. See https://www.unicode.org/reports/tr15/#Norm_Forms for more information.
Example using Math.js to generate a JSON output value
{
url: A,
title: B
}
Using the function meta object¶
The meta object contains details about the signal data value that triggered the function to run.
Key | Description |
---|---|
ts | The Unix timestamp in microseconds of the Signal Datapoint's timestamp which can be near real-time or a recorded timestamp from the device origination source which is common for batched data. Example: 1741713742462000 |
gts | The Unix timestamp in microseconds of the the platform's time of processing the value (near real-time). Example: 1741713114684839 |
intlet | The Signal inlet (e.g. "A", "B", etc) to allow for different logic based on the input signal. Example: "A" |
The timestamps in this object can be used to filter out historically recorded and batched data and to compare a timestamp vs the most recent (wall-clock). This allows for check if the data may be out of order which may cause unexpected results for functions that assume ordered data. Note that the output signal values of these functions will use the datapoint's recorded timestamp (gts).
Batched Recorded Data / Out of Order Data in Pipeline
Although the platform supports recording signal data at any historical timestamp , when these signals are connected in the pipeline to real-time event functions (like transforms and rules), it requires reviewing the scenarios that can happen from historical and batched recorded data. If required, handling this type of data scenario in the pipeline should be carefully designed (from edge device source to the pipeline functions to the expected actions) to help eliminate unexpected data results and/or confusing user experiences.
Recommendation
The general recommendation is for edge devices to send data in order (oldest first) when batched recorded data is required (network disconnections, etc). Otherwise try not use recorded data, instead writing data without timestamps and allowing the platform to timestamp the values while allowing data to be in order.
Example using the meta object to ignore value with timestamps that have been recorded at at historical time at least 5 milliseconds in the past.
abs(meta.ts - meta.gts) < 5000 ? A : undefined
Code example to return the items in the meta object.
{"$eval":"meta"}
Result
{
"ts": 1741713114684839,
"gts": 1741713742462000,
"inlet": "A"
}
Using historical data points¶
Functions provide an array for recent signal values, accessed from the prior
variable. Two data points are accessible along with their timestamps in this array - being the two most recent value/timestamps in terms of wall clock (now). Typically the first most recent is the data point that triggered this even to run but there is an exception (see below for more info) The first dimension of the array, denotes the data point (first or second). The second dimension of the array accesses the timestamp (first index) and the value in second index. (Math.js the indexes are 1
for timestamp and 2
for the value. In JSON-e, the indexes will be 0
for timestamp and 1
for the value.)
When using prior
, the current value is the most recent "historical" value for the signal, that is, A
is equivalent to prior.inlets.A[1,2]
(in Math.js). This makes the previous historical datapoint prior.inlets.A[2,2]
. There is an exception for this, for data sent using the record function (with a timestamp), this may be written historically and may not be the most recent prior
value (based on wall-clock ).
Example finding the difference between the current and previous first inlet
Note: the syntax for Math.js arrays would use prior.inlets.A[1,2]
(most recent) & prior.inlets.A[2,2]
(next recent) values
delta = A - prior.inlets.A[2,2]
Note: the syntax for JSON-e arrays would use prior.inlets.A[0,1]
(most recent) & prior.inlets.A[1,1]
(next recent) values
{ "$eval": "A - prior.inlets.A[1,1]" }
Creating Custom Rules¶
Multi-Inlet Rules are not supported
ExoSense does not support multi-inlet rules throughout the entire application, although there are ways to create them such as in the inline insight interface or building a custom external function.
To create a custom select "Rule" as the Insight Type
Rules must return an output of type "JSON"
This output must return an object that contains a field "level". This level will indicate the state of the rule.
State | Value |
---|---|
Normal | 0 |
Info | 1 |
Warning | 2 |
Critical | 3 |
Error | 4 |
This can be returned either directly or the user can select the state to return via "Constants"
An example where the state is returned directly
normal = 0
info = 1
warning = 2
critical = 3
error = 4
{level: (A == 90) ? warning : normal }
{ "$let": {
normal: 0,
info: 1,
warning: 2,
critical: 3,
error: 4
},
in: { "$if": "A == 90",
"then": { "$eval": "warning" },
"else": { "$eval": "normal" },
}
}
An example where the state is specified in the Rule's configuration
{level: (A == 90) ? level : 0}
{
"level": {
"$if": "A == 90",
"then": { "$eval": "level" },
"else: 0
}
}
Testing¶
The interface also allows in-browser testing of your functions by providing a form to set your input signal values, constants, and last output value (if necessary).
Importing/Exporting¶
When creating a new function, you have the option to Import a file that will generate the entire function for you. Insight files can be exported from your own custom insights, provided to you by Exosite for reference examples or by third-party or partner. Functions can be exported from the Insights interface.
Examples¶
Hint
Users can view the source for off the shelf standard functions included in ExoSense and may duplicate and export these to use for starting a new customized function.
Linear Gain
A
is the input signal, gain
and offset
are constants set by user
A * gain + offset
{ "$eval": "A * gain + offset" }
Continuous Accumulation
A
is the input signal and Z
is the output
A + (isNumeric(Z)?Z:initialValue)
{ "$if": "typeof(Z) == 'number'",
"then": { "$eval": "A + Z" },
"else": { "$eval": "initialValue" }
}
Mapping a signal's numeric range to a status
For example 0 to 10 is off, 10 to 40 is on, and greater than 40 is error
(A>0 and A<10)?"off":(A>10 and A<40)?"on":"error"
{ "$switch": {
"A > 0 && A < 10": "off",
"A > 10 && A < 40": "on",
"A <= 0 || A >= 40": "error"
}
}
Self-Hosted Custom Insights¶
Custom Self-Hosted Insights can be published in the Exosite IoT Marketplace for private or public use. This interface requires hosting a web-service that supports HTTP requests using Exosite's external Insight schema for handling streaming data requests and responses.
These self-hosted Insights can run anywhere you see fit—in Exosite Murano as a custom solution, Amazon Web Services, Microsoft Azure, Google Cloud Products, or any other hosting platform.
Self-Hosted external insight functions have the same properties and functionality as the custom inline insights, with the noted benefits:
- Code-base can be hosted and maintained on any infrastructure you want
- Allows for using external services and platforms for more advanced analytics and functionality
- Insights can be published in Exosite's IoT Marketplace for use by other Exosite customers.
Warning
As insights are streaming data logic operations, functions must be able to respond quickly.