signalk-server
Version:
An implementation of a [Signal K](http://signalk.org) server for boats.
251 lines • 8.39 kB
JavaScript
"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