Custom

Custom Conditions are designed to prepare raw input data for modeling. Some examples of this are converting XML to JSON, converting raw binary streams to data types, process counters, and more.

Custom Conditions expose an input for each source. If the source is {{Connection.opc.tag}} the conditioned input is {{Condition.myCondition.opc_tag}}. Reading this value returns the value of the input run through the expression.

Custom Conditions maintain the state of the last read and last returned values per source, allowing flexibility in handling things like state changes.

Expression

The JavaScript expression that runs every time a source is read. The expression contains the following variables that are kept for each source in the condition. These values are persisted on disk and persist between condition reads.

Syntax Description
{{this.currentValue}} The current value of the source being read. For example, this would be the current value of the OPC tag or MQTT topic.
{{this.lastValue}} The previous value for the source that was read the last time the expression was executed. For example, if the currentValue on the first read is 100, the lastValue on the next read is 100. This value is null when reading a source for the first time. This value is only cached if {{this.lastValue}} is used in the expression.
{{this.lastReturnValue}} The last value returned by the expression for the source. This is null on the first read of the source. This value is only cached if {{this.lastReturnValue}} is used in the expression.

In addition to the above variables, other sources can be directly included in the expression by dragging/dropping the reference from the reference panel.

Custom Condition Examples

Below are common examples of Expressions. Note Expressions use JavaScript (ECMAScript/ECMA-262 specification).

Convert Named Object to Array

Assume the input payload looks like this.

json
{
  "tag1":{
    "value": 100,
    "timestamp": "2023-05-31T12:30:56Z",
    "quality": "good"
  },
  "tag2": {
    "value": 60,
    "timestamp": "2023-05-31T12:30:56Z",
    "quality": "good"
  }
}

We want to convert the payload to this.

json
[
  {
    "name": "tag1",
    "value": 100,
    "timestamp": "2023-05-31T12:30:56Z",
    "quality": "good"
  },
  {
    "name": "tag2",
    "value": 60,
    "timestamp": "2023-05-31T12:30:56Z",
    "quality": "good"
  }
]

The following expression for the condition accomplishes this.

javascript
var rawObject = {{this.currentValue}}
var arrayOut = []

// Iterate each tag in the payload
for (const tagName in rawObject){
    // Create new object and add name
    let tagObject = {...rawObject[tagName]}
    tagObject["name"] = tagName

    // Add to the array
    arrayOut.push(tagObject)
}

// Return the array
arrayOut

Convert Array to Named Objects

Assume we want the reverse of the above example. In this case the raw data comes in as an array, and we want to convert it to named objects.

javascript
var rawArray = {{this.currentValue}}
var objectOut = {}

// Iterate each object in the array
for (const tag of rawArray){

    // Get the tag name
    let tagName = tag["name"]
    
    // Add the tag to the object, using the name as the key
    objectOut[tagName] = tag
    
    // Delete the name field
    delete objectOut[tagName]["name"]
}

// Return the object
objectOut

Return New Rows Only

In cases where an input returns all data, and you’re only concerned about new data, a condition can be used to filter out data that has already been read. Ideally this would be done by the input, for example using indexing in SQL, but in cases where it can’t a condition can perform the filtering.

Assume the raw input returns the following data.

json
[
  {
    "id": 1,
    "name": "tag1",
    "value": 100,
    "timestamp": "2023-05-31T12:30:56Z",
    "quality": "good"
  },
  {
    "id": 2,
    "name": "tag2",
    "value": 60,
    "timestamp": "2023-05-31T12:30:56Z",
    "quality": "good"
  }
]

On the first read this condition returns the two rows. On the second read the condition returns an empty array. If a new row is added with a new id, the third read returns the new row.

javascript
var currentReadRows = {{this.currentValue}}
var lastReadRows = {{this.lastValue}}
var retRows = []

if (lastReadRows == null){
    // First read, there is no last read
    currentReadRows
}
else{
    // Iterate each row from the latest read
    for (const currentRow of currentReadRows){
        let foundRow = false;
        
        // Iterate each row from the last read and see if there is a match by the id property
        for (const lastRow of lastReadRows){
            if (lastRow._id == currentRow._id){
                foundRow = true
                break;
            }
        }
        
        // If no match is found, return the new row
        if (!foundRow)
            retRows.push(currentRow)
    }

    retRows
}