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.
| Property | Value |
|---|---|
| File | bacnet-priority-scan.py |
| Size | 7,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.
bacnet-sweep
Broadcasts a BACnet/IP Who-Is, tables the devices that answer, and dumps a named device's object list — object name, present value and units — to a table or to CSV. It can encode two BACnet services and no others: Who-Is and ReadProperty.
- BACnet/IP
- Who-Is
- CSV out
mqtt-tap
Subscribes to a broker and prints what is actually on it: the topic tree with a count, a rate, a payload-type guess and the last value per topic, plus the retained topics that stopped updating. It sends five packet types and none of them is PUBLISH.
- MQTT
- Topic tree
- Retained
decoder-check
Runs a LoRaWAN device vendor's payload decoder against your frames in a sealed vm context and reports what a station would actually get back: crashes on a short frame, types that change between uplinks, units glued into values, keys a station has to escape. It reads frames and nothing else - no network, no broker, no network server.
- LoRaWAN
- Decoder
- Sandboxed
modbus-address-scan
Reads modbusCore-rt.jar out of a Niagara installation with javap and prints the register a point of each address format actually asks for — including the four band boundaries that all resolve to the same one.
- Modbus
- Shipped jars
- No station
ede-check
Reads the EDE import rules out of bacnetEDE-wb.jar with javap, then reports line by line what the shipped parser would reject in your point file and what it would silently default.
- EDE
- Point lists
- Line by line
module-sign-scan
Reads the verification code out of a Niagara installation and prints the four modes, the signature state each one accepts or refuses, and the exact log line a station writes — including the warning that only becomes a refusal when a certificate expires.
- Module signing
- Shipped jars
- No station
alarm-route-scan
Reads alarm-rt.jar and baja.jar with javap and prints what happens to an alarm between the source and the recipient: one queue, one worker thread, the coalesce key that decides which duplicate is dropped, and why the invocation that lost that collision still reports success.
- Alarms
- Shipped jars
- No station
alarm-recipient-scan
Reads the recipient side of alarm-rt.jar with javap and prints why returning false from sendAlarm drops the alarm silently, what throwing does instead, how long the retry loop runs, and which four properties are the only evidence a site can send you.
- Alarms
- Retry
- No station
schedule-scan
Reads schedule-rt.jar with javap and prints the 90-day scanLimit horizon that turns a far-off change into no change at all, why nextCov steps over a boundary whose value matches, and the one serial uncapped queue every control schedule shares.
- Schedules
- Shipped jars
- No station
workbook-scan
Opens an .xlsx as the zip of XML it is and reports what is in the bytes: formulas saved holding an error, links into files that may be gone, saved queries to one person's mapped drive, approximate VLOOKUPs, hidden sheets, and rules nothing protects. It reads the old binary .xls too.
- Excel
- No install
- JSON out
poll-scheduler-scan
Reads the poll scheduler out of driver-rt.jar and prints the arithmetic: three rate defaults, one point polled per pass, and the bucket size at which the thread stops sleeping and the real cycle time stretches.
- Niagara
- javap
- Read-only
tuning-stale-scan
Reads the tuning policy and the stale branch out of the shipped jars: the default staleTime of zero, the clock it measures, and why an unsolicited device can sit dead with an ok status for as long as the station runs.
- Niagara
- javap
- Read-only
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.