Skip to content

getEventInformation() drops the decoded moreEvents flag — truncated alarm lists are indistinguishable from complete ones #85

Description

@Gus0711

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions