This article is based on a real project. It showcases an example of routing with multiple source addresses. It explains how to access the values of each source address inside the routing function.
Here each VAV (Variable Air Volume) controllers has 4 inputs of type bacnet analogValue
And a single output of type bacnet binaryValue (binaryValue:1).
Here the final goal is to define a routing function which takes as source values the 4 inputs, and as destination the binary value. The function must return 1 if one of the two following conditions is fulfilled:
There is two main methods to design routes with multiple sources
The same routing function is used for all VAV controllers, and each VAV controllers has its own route. Section 3 shows how to save the routing function and re-use it across different routes without having to copy/paste multiples times the same function.
In this section the inputs are passed as an array in Source address. So the inputs will be distinguished based on their position in the array.
["%bac:<deviceip_ip>/analogValue:1",
"%bac:<deviceip_ip>/analogValue:2",
"%bac:<deviceip_ip>/analogValue:3",
"%bac:<deviceip_ip>/analogValue:4"]
The fourth parameter of the routing function, here named infos, has a property sources which permits to access the sources by index or key.
function VAVregulation(v,src,dst,infos){
// retrives the source addresses values
// here the order of source addresses is important.
var temperatureSetpoint = infos.sources[0] && infos.sources[0].value
var actualTemperature = infos.sources[1] && infos.sources[1].value
var maximumAirflow = infos.sources[2] && infos.sources[2].value
var actualAirflow = infos.sources[3] && infos.sources[3].value
// checks that all values are defined to continue.
if(typeof temperatureSetpoint != "number") return
if(typeof actualTemperature != "number") return
if(typeof maximumAirflow != "number") return
if(typeof actualAirflow != "number") return
// computes the 2 conditions
var condition1 = actualAirflow > 0.85 * maximumAirflow
var condition2 = actualTemperature < temperatureSetpoint
// writes 1 or 0 to the destination binaryValue.
return (condition1 || condition2) ? 1 : 0
}
Below a summary example with bacnet device ip 192.168.178.66.

Using a dictionary with your textual keys (here "ts" for the temperature setpoint, "at" for the actual temperature, "ma" for the maximum airflow, and "aa" for the actual airflow) is a cleaner solution compared to simple arrays. It permits to define custom keys / labels for better comprehension and it avoids potential ordering mistakes possible with simple arrays.
The source address can also be an object with source name
{
"ts" : "%bac:<device_ip>/analogValue:1",
"at" : "%bac:<device_ip>/analogValue:2",
"ma" : "%bac:<device_ip>/analogValue:3",
"aa" : "%bac:<device_ip>/analogValue:4"
}
The routing function must be adapted to use the source addresses dictionary keys instead of the position / index in the array.
function VAVregulation(v,src,dst,infos){
// retrieves the addresses by their keyname and their value if the address exists
var temperatureSetpoint = infos.sources["ts"] && infos.sources["ts"].value
var actualTemperature = infos.sources["at"] && infos.sources["at"].value
var maximumAirflow = infos.sources["ma"] && infos.sources["ma"].value
var actualAirflow = infos.sources["aa"] infos.sources["aa"] aa.value
// checks that all values are defined to continue.
if(typeof temperatureSetpoint != "number") return
if(typeof actualTemperature != "number") return
if(typeof maximumAirflow != "number") return
if(typeof actualAirflow != "number") return
// computes the 2 conditions
var condition1 = actualAirflow > 0.85 * maximumAirflow
var condition2 = actualTemperature < temperatureSetpoint
// writes 1 or 0 to the destination binaryValue.
return (condition1 || condition2) ? 1 : 0
}
In order to avoid copy/pasting the routing function in all routes, we can either store the routing function in a routing function block (section 3.1) or in an address of the internal gateway (section 3.2)

