BACnet4J can fail while decoding a Who-Has request that includes device-instance limits, and it can acknowledge a SubscribeCOV request without later sending notifications. The evidence identifies separate causes and fixes for these behaviors; diagnose them independently.
Classify the observed BACnet4J behavior
| Observed behavior | Evidence-supported cause or status | Required action |
|---|---|---|
BACnetException: Error while creating APDU on a bounded Who-Has request |
Confirmed defect in Who-Has deserialization | Use a build containing the checked-in fix and update code for the changed WhoHasRequest constructor API. |
| SubscribeCOV receives an ACK but no COV notification follows | The lease may have expired, or the requested object type or property may not support COV in this implementation. Validation failures could still receive an ACK. | Check the expiry value, object type, and property before investigating value changes. |
| Local device listens on the wrong interface | The host is multihomed and no explicit bind address was selected. | Pass the required address through the localBindAddress constructor parameter. |
| AddListElement inserts duplicate members | The original implementation omitted the list-uniqueness check. | Use a build containing the committed uniqueness correction. |
Resolve bounded Who-Has deserialization failures
The failing request contains both a device-instance low limit and high limit. One captured case decoded those limits as 0 and 4194303 and included the object name ELEC. BACnet4J then raised BACnetException: Error while creating APDU; the nested call path reached Encodable.popStart, Encodable.readWrapped, and the WhoHasRequest constructor.
An early assessment questioned whether the incoming request was invalid, but the later confirmed finding was a defect in Who-Has deserialization. A correction was checked into the project repository, and the constructor API for the affected class changed slightly.
- Confirm that unbounded Who-Has requests succeed while requests containing the low and high device-instance limits fail.
- Capture the exception and verify that failure occurs while creating the APDU and constructing
WhoHasRequest. - Move to a BACnet4J source or build containing the Who-Has deserialization correction.
- Recompile client code against the changed constructor API instead of assuming binary or source compatibility.
- Repeat the bounded Who-Has request and verify that decoding completes and the local device can return I-Have when the requested object matches.
Diagnose ACK-without-notification COV behavior
An ACK does not prove that BACnet4J accepted a valid COV target in the implementation described by the evidence. The implementation could acknowledge SubscribeCOV even when its object-type or property validation should have rejected the request.
Apply this decision path:
- Check whether the subscription lease has expired. Verify the value supplied in the expiry property before waiting for notifications.
- Confirm that the subscribed object type is one of the supported types:
accumulator,binaryInput,binaryOutput,binaryValue,lifeSafetyPoint,multiStateInput,multiStateOutput, ormultiStateValue. - Confirm that the requested property is
presentValueorstatusFlags. - Change the subscribed value and observe network traffic for a COV notification while the lease remains active.
- If the type or property is outside those lists, treat the ACK as inconclusive rather than evidence that notifications will follow.
Bind a local device to the correct IP interface
For a multihomed host or a virtualized network, select the interface explicitly through the new localBindAddress parameter in the LocalDevice constructor. If no address is supplied, the implementation defaults to 0.0.0.0.
- Identify the local IP address attached to the network on which BACnet/IP traffic must be received.
- Supply that address as
localBindAddresswhen constructing the local device. - Initialize the device and confirm that its UDP socket listens on the selected interface.
- Issue a discovery request on that network and verify that the device receives it and responds from the intended interface.
This constructor support replaces the need to extend LocalDevice and IpMessageControl solely to bind a DatagramSocket to a selected address.
Account for committed fixes and unsupported services
The AddListElement correction enforces unique list members: an element already present must not be appended again. Verification should write an existing element, read the property back, and confirm that only one equal member remains.
AlarmAcknowledgement and GetEventInformation were explicitly not implemented in the described release state, and no implementation plan was stated. The library was written primarily for operator-side development. Do not design a device-side alarm workflow around those services without first confirming support in the exact source revision being deployed.
Verify the release contents and regression results
Version 1.02 included a source ZIP. Its example code was located under the source tree's test sub-package, and Javadocs were also added to the release.
Close the troubleshooting task only after all applicable checks pass: bounded Who-Has requests deserialize without the APDU exception; matching requests produce I-Have; COV tests use a supported object type and property with an active lease; the local socket binds to the selected interface; and AddListElement does not create a duplicate member.
FAQ
Why does BACnet4J throw Error while creating APDU for Who-Has?
A confirmed Who-Has deserialization defect affected requests containing device-instance low and high limits. Use a build containing the checked-in fix and adjust client code for the changed WhoHasRequest constructor API.
Why does BACnet4J acknowledge SubscribeCOV but send no notification?
Check the lease expiry, then verify that the target property is presentValue or statusFlags and that the object type is one of the eight supported types listed above. The described implementation could send an ACK even when these validations failed.
How do I bind BACnet4J to a specific network adapter?
Pass the adapter's IP address through the localBindAddress parameter of the LocalDevice constructor. Without an explicit address, the bind address defaults to 0.0.0.0.