UNPKG

signalk-server

Version:

An implementation of a [Signal K](http://signalk.org) server for boats.

251 lines 8.39 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.NotificationManager = exports.buildKey = void 0; const server_api_1 = require("@signalk/server-api"); const alarm_1 = require("./alarm"); const uuid = __importStar(require("uuid")); const _ = __importStar(require("lodash")); const CLEAN_INTERVAL = 60000; const buildKey = (context, path, source) => { return `${context}/${path}/${source}`; }; exports.buildKey = buildKey; /** * Class to manage the lifecycle of alarms */ class NotificationManager { server; app; alarms = new Map(); deltaVersion = server_api_1.SKVersion.v1; cleanTimer; forCleaning = []; constructor(server) { this.server = server; this.app = server; this.cleanTimer = setInterval(() => this.clean(), CLEAN_INTERVAL); } emitNotification(alarm) { this.app.handleMessage('notificationApi', alarm?.delta, this.deltaVersion); } get list() { const l = {}; this.alarms.forEach((v, k) => { l[k] = v.properties; }); return l; } get(id) { if (!id) { throw new Error('Notification identifier not supplied!'); } return this.alarms.get(id)?.properties; } getPath(path) { if (!path) { throw new Error('Notification path not supplied!'); } const l = {}; this.alarms.forEach((v, k) => { if (v.properties.path === path) { l[k] = v.properties; } }); return l; } raise(options) { if (!options) { throw new Error('Notification properties not supplied!'); } const { state, message, context, path, idInPath, includePosition, includeCreatedAt, data } = options; if (!state || !message) { throw new Error('Notification `state` or `message` properties are missing!'); } const id = uuid.v4(); const alarm = new alarm_1.Alarm(id); if (context) { alarm.setContext(context); } alarm.value.state = state; alarm.status.canSilence = state === server_api_1.ALARM_STATE.emergency ? false : true; alarm.value.message = message; if (path) { alarm.setPath(path, idInPath ? id : undefined); } if (includePosition || state === server_api_1.ALARM_STATE.emergency) { alarm.value.position = /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ _.get(this.app.signalk.self, 'navigation.position')?.value ?? null; } if (includeCreatedAt || state === server_api_1.ALARM_STATE.emergency) { alarm.value.createdAt = new Date().toISOString(); } if (data) { alarm.value.data = structuredClone(data); } this.alarms.set(id, alarm); this.emitNotification(alarm); return id; } update(id, options) { if (!id) { throw new Error('Notification identifier not supplied!'); } const alarm = this.alarms.get(id); if (!alarm) { throw new Error('Notification not found!'); } if (!options) { throw new Error('Notification options not supplied!'); } const { state, message, data } = options; const stateChanged = alarm.value.state !== state; alarm.value.state = state ?? alarm.value.state; alarm.status.canSilence = state && state === server_api_1.ALARM_STATE.emergency ? false : true; alarm.value.message = message ?? alarm.value.message; if (stateChanged) { alarm.status.silenced = false; alarm.status.acknowledged = false; } if (data) { alarm.value.data = structuredClone(data); } this.alarms.set(id, alarm); this.emitNotification(alarm); } mob(message) { return this.raise({ state: server_api_1.ALARM_STATE.emergency, message: message ?? 'Person Overboard!', path: 'mob', idInPath: true, includePosition: true, includeCreatedAt: true }); } silenceAll() { this.alarms.forEach((alarm) => { try { alarm?.silence(); this.emitNotification(alarm); } catch { // already silenced } }); } silence(id) { if (!id) { throw new Error('Notification identifier not supplied!'); } if (!this.alarms.has(id)) { throw new Error('Alarm not found!'); } const alarm = this.alarms.get(id); alarm?.silence(); this.emitNotification(alarm); } acknowledgeAll() { this.alarms.forEach((alarm) => { alarm?.acknowledge(); this.emitNotification(alarm); }); } acknowledge(id) { if (!id) { throw new Error('Notification identifier not supplied!'); } if (!this.alarms.has(id)) { throw new Error('Alarm not found!'); } const alarm = this.alarms.get(id); alarm?.acknowledge(); this.emitNotification(alarm); } clear(id) { if (!id) { throw new Error('Notification identifier not supplied!'); } if (!this.alarms.has(id)) { throw new Error('Alarm not found!'); } const alarm = this.alarms.get(id); alarm?.clear(); this.emitNotification(alarm); } processNotificationUpdate(u, context) { if ((0, server_api_1.hasValues)(u) && u.values.length) { const id = u.notificationId; let alarm; if (this.alarms.has(id)) { alarm = this.alarms.get(id); alarm.syncFromNotificationUpdate(u, context); this.alarms.set(id, alarm); } else { alarm = new alarm_1.Alarm(); alarm.syncFromNotificationUpdate(u, context); this.alarms.set(id, alarm); } this.emitNotification(alarm); } } /** * Clean out alarms that have returned to and remained in NORMAL state * for the duration of CLEAN_INTERVAL */ clean() { const idsToDelete = []; const nextClean = []; this.alarms.forEach((v, k) => { if (this.forCleaning.includes(k) && v.value?.state === server_api_1.ALARM_STATE.normal) { idsToDelete.push(k); } else { nextClean.push(k); } }); this.forCleaning = nextClean; idsToDelete.forEach((id) => { this.alarms.delete(id); }); } } exports.NotificationManager = NotificationManager; //# sourceMappingURL=notificationManager.js.map