BACnet Simulator Lua Script Tutorial
This tutorial will outline how to write basic lua scripts to control the simulator.
Incrementing Present Values
This section will cover how to size object instances and add some logic to update present value properties in both the Run and Update functions.
Create Object Instances
function Run()
bacnet.createAnalogInputs(1)
bacnet.createAnalogValues(1)
bacnet.createAnalogOutputs(1)
bacnet.setAnalogInputName(0, "ANALOG_INPUT_0")
bacnet.setAnalogValueName(0, "ANALOG_VALUE_0")
bacnet.setAnalogOutputName(0, "ANALOG_OUTPUT_0")
end
This code will create 1 instance of Analog Input, Analog Output and Analog Value objects and set the object names for each of them.
Please note that object instances are indexed from 0 and so the 1st instance of an object will be referenced using 0.
Add Some Logic to the Run Function
As the Run function operates on its own thread separate from the main simulator thread, it can be kept running using the isBacnetRunning
function with a while loop.
local increment = 0
local priority = 1
local mod = 15
function sleep(time)
local duration = os.time() + time
while os.time() < duration do end
end
function Run()
bacnet.createAnalogInputs(1)
bacnet.createAnalogValues(1)
bacnet.createAnalogOutputs(1)
bacnet.setAnalogInputName(0, "ANALOG_INPUT_0")
bacnet.setAnalogValueName(0, "ANALOG_VALUE_0")
bacnet.setAnalogOutputName(0, "ANALOG_OUTPUT_0")
while isBacnetRunning() do
increment = math.fmod (increment, mod) + 1
bacnet.setAnalogValuePresentValue(0, increment, priority)
sleep(1)
end
end
Every second this logic will increment a value by 1, setting the present value of instance 0 of the Analog Value object. Once the value reaches 15 it will wrap around to 1 again. As lua does not provide a standard sleep function, one must be included.
Add Some Logic to the Update Function
The Update
function will be run on every process cycle of the simulator. As this is called from the main simulator thread, there should be no logic in here that blocks execution, ie sleeps etc.
function Update()
value = bacnet.getAnalogOutputPresentValue(0)
bacnet.setAnalogInputPresentValue(0, value, priority)
end
This logic will mirror whatever value that was written to the present value of the Analog Output object to the Analog Input instance.
Full code
local increment = 0
local priority = 1
local mod = 15
function sleep(time)
local duration = os.time() + time
while os.time() < duration do end
end
function Run()
bacnet.createAnalogInputs(1)
bacnet.createAnalogValues(1)
bacnet.createAnalogOutputs(1)
bacnet.setAnalogInputName(0, "ANALOG_INPUT_0")
bacnet.setAnalogValueName(0, "ANALOG_VALUE_0")
bacnet.setAnalogOutputName(0, "ANALOG_OUTPUT_0")
while isBacnetRunning() do
increment = math.fmod (increment, mod) + 1
bacnet.setAnalogValuePresentValue(0, increment, priority)
sleep(1)
end
end
function Update()
value = bacnet.getAnalogOutputPresentValue(0)
bacnet.setAnalogInputPresentValue(0, value, priority)
end
Generating Events
This section will demonstrate how to generate mock events and direct them to your BACnet client.
Create Notification Class Object Instance and Register Recipient
function Run()
bacnet.createNotificationClasses(1)
bacnet.registerRecipient (101, 0, false)
end
This code will create 1 instance of the Notification Class object and then register a BACnet client with ID 101 to the recipient list of this Notification Class instance. Any events sent to this device will be unconfirmed.
Please note that object instances are indexed from 0 and so the 1st instance of an object will be referenced using 0.
Add Some Logic to Generate Events
The Update function will be used to generate the mock events for every type supported on a 15 second interval.
local waitDuration = 15
local lastUpdate = os.time()
function Update()
if os.time() - lastUpdate > waitDuration then
bacnet.generateEvent(EventType.CHANGE_OF_BITSTRING, 0)
bacnet.generateEvent(EventType.CHANGE_OF_STATE, 0)
bacnet.generateEvent(EventType.CHANGE_OF_VALUE, 0)
bacnet.generateEvent(EventType.COMMAND_FAILURE, 0)
bacnet.generateEvent(EventType.FLOATING_LIMIT, 0)
bacnet.generateEvent(EventType.OUT_OF_RANGE, 0)
bacnet.generateEvent(EventType.CHANGE_OF_LIFE_SAFETY, 0)
bacnet.generateEvent(EventType.BUFFER_READY, 0)
bacnet.generateEvent(EventType.UNSIGNED_RANGE, 0)
bacnet.generateEvent(EventType.ACCESS_EVENT, 0)
bacnet.generateEvent(EventType.DOUBLE_OUT_OF_RANGE, 0)
bacnet.generateEvent(EventType.SIGNED_OUT_OF_RANGE, 0)
bacnet.generateEvent(EventType.UNSIGNED_OUT_OF_RANGE, 0)
bacnet.generateEvent(EventType.CHANGE_OF_CHARACTERSTRING, 0)
bacnet.generateEvent(EventType.CHANGE_OF_TIMER, 0)
lastUpdate = os.time()
end
end
These events will be routed through instance 0 of the Notification Class that was created above. As the BACnet device client with ID 101 has been registered in the recipient list, these events will be sent to that device.
Full code
function Run()
bacnet.createNotificationClasses(1)
bacnet.registerRecipient (101, 0, false)
end
local waitDuration = 15
local lastUpdate = os.time()
function Update()
if os.time() - lastUpdate > waitDuration then
bacnet.generateEvent(EventType.CHANGE_OF_BITSTRING, 0)
bacnet.generateEvent(EventType.CHANGE_OF_STATE, 0)
bacnet.generateEvent(EventType.CHANGE_OF_VALUE, 0)
bacnet.generateEvent(EventType.COMMAND_FAILURE, 0)
bacnet.generateEvent(EventType.FLOATING_LIMIT, 0)
bacnet.generateEvent(EventType.OUT_OF_RANGE, 0)
bacnet.generateEvent(EventType.CHANGE_OF_LIFE_SAFETY, 0)
bacnet.generateEvent(EventType.BUFFER_READY, 0)
bacnet.generateEvent(EventType.UNSIGNED_RANGE, 0)
bacnet.generateEvent(EventType.ACCESS_EVENT, 0)
bacnet.generateEvent(EventType.DOUBLE_OUT_OF_RANGE, 0)
bacnet.generateEvent(EventType.SIGNED_OUT_OF_RANGE, 0)
bacnet.generateEvent(EventType.UNSIGNED_OUT_OF_RANGE, 0)
bacnet.generateEvent(EventType.CHANGE_OF_CHARACTERSTRING, 0)
bacnet.generateEvent(EventType.CHANGE_OF_TIMER, 0)
lastUpdate = os.time()
end
end