Skip to content

Free tool

bacnet-priority-scan — how a station decides to write your point

A gateway that exposes a value as an AnalogValue instead of an AnalogOutput changes how Niagara writes to it, not just what the object is called. bacnet-priority-scan reads bacnet-rt.jar with javap and prints which object types the station assumes are prioritized, which ones it probes on the wire first, and what happens to a point whose probe times out.

  • BACnet
  • Priority array
  • Reads the shipped jars
  • No station needed
  • Read-only
  • MIT

Install and run

One file, standard library only, and nothing on the network: it reads a jar on disk. It needs a Niagara installation and a javap on the path, which the JDK that ships with Niagara already provides. Point it at the installation or set NIAGARA_HOME.

# download it next to wherever you are working
curl -O https://plantroomlabs.com/tools/bacnet-priority-scan.py

# read the BACnet driver out of an installation
python3 bacnet-priority-scan.py /opt/Niagara/Niagara-4.15.5.22

What it printed here

This is the whole of one run against the installation on the machine that built this page, pasted by the script that publishes it rather than retyped. The headline is the split in the table: AnalogOutput, BinaryOutput and MultiStateOutput are assumed to have a priority array and are written without anything being asked on the wire, while the Value objects are probed once with a single ReadProperty for property 87 at array index 0. If that probe errors or times out, the answer is cached in the point's device facets as priPV and the point writes straight to present-value until a discovery is forced again.

$ python3 bacnet-priority-scan.py $NIAGARA_HOME
/opt/Niagara/Niagara-4.15.5.22/modules/bacnet-rt.jar
javap: 1.8.0_504

BBacnetProxyExt.discoverPrioritizedPresentValue(boolean) switches on object type:

  type  name                   priorityArray in Niagara table  what the station does
     1  AnalogOutput           required                        assumed prioritized, nothing asked
     4  BinaryOutput           required                        assumed prioritized, nothing asked
    14  MultiStateOutput       required                        assumed prioritized, nothing asked
     2  AnalogValue            optional                        probed once over the wire
     5  BinaryValue            optional                        probed once over the wire
    19  MultiStateValue        optional                        probed once over the wire
    40  CharacterStringValue   optional                        probed once over the wire
    45  IntegerValue           optional                        probed once over the wire
    46  LargeAnalogValue       optional                        probed once over the wire
    48  PositiveIntegerValue   required                        probed once over the wire
  else  (any other type)       -                               set to not prioritized

The probe, in BacnetDiscoveryUtil.checkForPriorityArray:
  one ReadProperty for property 87 (priorityArray), array index 0
  caught: java/lang/Exception -> returns FALSE
  the answer is stored in the point's device facets as 'priPV'
  re-probed on later discovery only when forced: yes

Read from Niagara-4.15.5.22. Three facts a Wireshark trace on the gateway can check:
  1. a write to an AO/BO/MO point addresses the priority array without a prior read
  2. an AV/BV/MV writable point is preceded by exactly one priority-array read
  3. if that read errors or times out, no priority-array read follows and writes
     go to present-value directly

What it reads, and what that does not cover

It reads bacnet-rt.jar with javap and nothing else. No station is contacted, no device is polled and nothing is written — which is also the limit of what the output is worth. It is a reading of compiled code, so it tells you what the driver is built to do rather than what a gateway answered yesterday. The three facts at the end of the output are the ones a Wireshark trace on the gateway can confirm in an afternoon.

The numbers above came from one installation, 4.15.5.22, and the output names it. The controller this work usually targets runs 4.14.0.162. A different Niagara version is a different answer, so run it against yours rather than trusting this page.

Why a writable point ignores the value you set is the station side of the same question, and what a write and a relinquish actually send is what goes on the wire once the station has decided.

The repository

The same file, MIT licensed, at github.com/UsamaIqbal0304/bacnet-priority-scan. Its README carries the finding and a run of the output, so the repository is checkable without downloading anything. Issues and pull requests are read.

The file you are downloading

Published here so the download is checkable rather than trusted. Both figures are read off the file served at /tools/bacnet-priority-scan.py when this page is built, so they cannot disagree with it.

PropertyValue
Filebacnet-priority-scan.py
Size7,350 bytes
SHA-256 515649bfbd847006f4ea8fc43e0026033552359e652c6470a0dcc7c921b6a696
Licence MIT — LICENSE.txt
Source github.com/UsamaIqbal0304/bacnet-priority-scan

To check it, on Linux sha256sum bacnet-priority-scan.py, on macOS shasum -a 256 bacnet-priority-scan.py, on Windows certutil -hashfile bacnet-priority-scan.py SHA256. A different digest means a different file — not necessarily a hostile one, but not this one.

The repository holds the same file, byte for byte, together with everything needed to re-run the checks this page's claims rest on — so they can be run rather than read about. Issues and pull requests there are read.

Also free

The others

Same idea, a different protocol or a different file. Every free tool.

Next step

Send the point that writes to the wrong place.

The object type, the probe and the facet it was cached in are three separate things, and a trace on the gateway usually shows which of them the write went through. That read costs nothing either way.