UNPKG

@bacnet-js/device

Version:

A TypeScript library for implementing BACnet IP devices in Node.js.

87 lines 3.65 kB
import { ApplicationTag, } from '@bacnet-js/client'; import { getPropertyUID, } from '../../uids.js'; import {} from './types.js'; import { EMPTY_ARRAY } from '../../constants.js'; /** * Comparator for sorting subscriptions by expiration date in ascending order. */ const subscriptionArraySortingFn = (a, b) => { return a.expiresAt - b.expiresAt; }; /** * Comparator for sorting subscriptions data instances by expiration date in ascending order. */ const subscriptionDataArraySortingFn = (a, b) => { return subscriptionArraySortingFn(a.value, b.value); }; /** * Like Array.prototype.findIndex, but returns the length of the array if no element satisfies the predicate. */ const findIndexOrLength = (arr, predicate) => { const idx = arr.findIndex(predicate); return idx === -1 ? arr.length : idx; }; /** * Helper class to keep track of active subscriptions. */ export class SubscriptionStore { /** Array of subscriptions sorted by expiration date in ascending order. */ #deviceSubData; /** Maps each property (through its UID) to the array of subscriptions for * that property, sorted by expiration date in ascending order. */ #propertySubs; /** Timeout ID for clearing expired subscriptions. */ #clearTimeoutId; constructor() { this.#deviceSubData = []; this.#propertySubs = new Map(); this.#clearTimeoutId = null; } #setClearTimeout() { if (this.#clearTimeoutId === null && this.#deviceSubData.length > 0) { this.#clearTimeoutId = setTimeout(this.#onClearTimeout, this.#deviceSubData[0].value.expiresAt - Date.now()); } } #onClearTimeout = () => { const now = Date.now(); this.#deviceSubData = this.#deviceSubData.slice(findIndexOrLength(this.#deviceSubData, sub => sub.value.expiresAt > now)); for (const [propertyUid, propertySubs] of this.#propertySubs.entries()) { this.#propertySubs.set(propertyUid, propertySubs.slice(findIndexOrLength(propertySubs, item => item.expiresAt > now))); } this.#clearTimeoutId = null; this.#setClearTimeout(); }; add(subscription) { const propertyUid = getPropertyUID(subscription.object.uid, subscription.property.identifier); let propertySubscriptions = this.#propertySubs.get(propertyUid); if (!propertySubscriptions) { propertySubscriptions = []; this.#propertySubs.set(propertyUid, propertySubscriptions); } const previousSub = propertySubscriptions.find((existingSub) => { return existingSub.subscriber.address === subscription.subscriber.address && existingSub.subscriptionProcessId === subscription.subscriptionProcessId; }); if (previousSub) { previousSub.expiresAt = subscription.expiresAt; } else { propertySubscriptions.push(subscription); this.#deviceSubData.push({ type: ApplicationTag.COV_SUBSCRIPTION, value: subscription }); } propertySubscriptions.sort(subscriptionArraySortingFn); this.#deviceSubData.sort(subscriptionDataArraySortingFn); this.#setClearTimeout(); } getPropertySubscriptions(propertyUid) { return this.#propertySubs.get(propertyUid) ?? EMPTY_ARRAY; } getDeviceSubscriptionData() { const now = Date.now(); for (const subscription of this.#deviceSubData) { subscription.value.timeRemaining = Math.floor((subscription.value.expiresAt - now) / 1000); } return this.#deviceSubData; } } //# sourceMappingURL=subscriptionstore.js.map