Summary
client.getEventInformation() discards the moreEvents flag it has just decoded. Callers
therefore cannot tell a complete alarm list from a truncated one.
Where
lib/services/GetEventInformation.js decodes the flag correctly:
result = baAsn1.decodeTagNameAndValue(buffer, offset + len);
len += result.len;
value.moreEvents = buffer[offset + len] > 0;
but lib/client.js drops it:
const result = services_1.GetEventInformation.decodeAcknowledge(data.buffer, data.offset, data.length);
if (!result) {
throw new Error('INVALID_DECODING');
}
return result.events; // <-- result.moreEvents is lost here
and the declared return type is Promise<BACNetEventInformation[]>, so there is no place
for it in the public surface either.
Why it matters
GetEventInformation is explicitly a paged service (ASHRAE 135, 13.12): the device returns
as many events as fit in the APDU and sets moreEvents = TRUE when there are more. The
response is valid when truncated — no abort, no error, no NAK.
With a 1024-octet APDU (a value dictated by path MTU on tunnelled links, not by the device),
each event carries three timestamps and the response holds roughly 12–16 events. Beyond that
the list silently stops.
The consequence is not only "some alarms are missing from the display". A supervisor
typically treats an object absent from the response as no longer signalled, and clears its
alarm state accordingly — that is how a returned-to-normal alarm stops being reported. An
object left in an unread page is absent, therefore declared cleared. A site with thirty
active alarms shows fifteen, and the other fifteen are recorded as "returned to normal" on
the first poll. Nothing in the data distinguishes that from a genuine recovery.
Workaround
The standard continuation mechanism is public and works: call again passing the last
received object identifier as lastReceivedObjectIdentifier (context tag 0), until an empty
list comes back. That is what we do.
It costs one extra round-trip per poll on any device that has at least one alarm, and — more
importantly — it cannot distinguish two cases that the flag would separate:
- the device returned everything (
moreEvents = false), so an absent object really is no
longer signalled;
- the device ignores the continuation parameter and replays its first page, so the list is
incomplete and absence means nothing.
We currently detect the second case by requiring the object identifier to strictly increase
between pages, and refuse to clear anything when it does not. The flag would make that a
fact rather than an inference.
Suggested fix
Return the flag alongside the events, e.g.
getEventInformation(
receiver: BACNetAddress,
objectId?: BACNetObjectID | null,
options?: ServiceOptions,
): Promise<{ events: BACNetEventInformation[]; moreEvents: boolean }>
or, if the current signature must be preserved, expose it as a separate accessor or an
overload. getAlarmSummary decodes a moreAlarms flag in the same way and has the same
issue.
Environment
Summary
client.getEventInformation()discards themoreEventsflag it has just decoded. Callerstherefore cannot tell a complete alarm list from a truncated one.
Where
lib/services/GetEventInformation.jsdecodes the flag correctly:but
lib/client.jsdrops it:and the declared return type is
Promise<BACNetEventInformation[]>, so there is no placefor it in the public surface either.
Why it matters
GetEventInformationis explicitly a paged service (ASHRAE 135, 13.12): the device returnsas many events as fit in the APDU and sets
moreEvents = TRUEwhen there are more. Theresponse is valid when truncated — no abort, no error, no NAK.
With a 1024-octet APDU (a value dictated by path MTU on tunnelled links, not by the device),
each event carries three timestamps and the response holds roughly 12–16 events. Beyond that
the list silently stops.
The consequence is not only "some alarms are missing from the display". A supervisor
typically treats an object absent from the response as no longer signalled, and clears its
alarm state accordingly — that is how a returned-to-normal alarm stops being reported. An
object left in an unread page is absent, therefore declared cleared. A site with thirty
active alarms shows fifteen, and the other fifteen are recorded as "returned to normal" on
the first poll. Nothing in the data distinguishes that from a genuine recovery.
Workaround
The standard continuation mechanism is public and works: call again passing the last
received object identifier as
lastReceivedObjectIdentifier(context tag 0), until an emptylist comes back. That is what we do.
It costs one extra round-trip per poll on any device that has at least one alarm, and — more
importantly — it cannot distinguish two cases that the flag would separate:
moreEvents = false), so an absent object really is nolonger signalled;
incomplete and absence means nothing.
We currently detect the second case by requiring the object identifier to strictly increase
between pages, and refuse to clear anything when it does not. The flag would make that a
fact rather than an inference.
Suggested fix
Return the flag alongside the events, e.g.
or, if the current signature must be preserved, expose it as a separate accessor or an
overload.
getAlarmSummarydecodes amoreAlarmsflag in the same way and has the sameissue.
Environment
@bacnet-js/client3.3.2