UNPKG

vrack-db

Version:

This is an In Memory database designed for storing time series (graphs).

211 lines (193 loc) 6.29 kB
/* * Copyright © 2023 Boris Bobylev. All rights reserved. * Licensed under the Apache License, Version 2.0 */ import AlertCondition from "./AlertCondtition"; import ICondition from "./ICondition"; import AlertQuery from "./AlertQuery"; import SingleDB from "./SingleDB"; import ErrorManager from "./Errors/ErrorManager"; import Interval from "./Interval"; import MetricResult from "./MetricResult"; import Utility from "./Utility"; /** * Alert status list */ export enum EAlertStatus { created = 'created', updated = 'updated', ok = 'ok', } export interface IAlertingPoint { readonly path: string, readonly query: AlertQuery, readonly condition: AlertCondition, readonly additional: { [key: string]: any }, readonly id: string status: keyof typeof EAlertStatus count: number created: number } export interface IAlert { id: string, status: keyof typeof EAlertStatus, value: number | null, count: number, timestamp: number, created: number, condition: ICondition, areas: Array<Array<number | null>>, threshholds: Array<number>, additional: { [key: string]: any } } ErrorManager.register('9gaaPv5DFoh5', 'VDB_ALERTING_DUBLICATE_UID', 'Dublicate key of watch point') /** * Simple implementation of data tracking to generate alarm messages * * Used with the AlertCondition and AlertQuery classes */ export default class Alerting { protected database: SingleDB protected listeners: Array<(alert: IAlert) => void> = [] /** * @param database VRack db database instance * */ constructor(database: SingleDB) { this.database = database } /** * Alerting points * Each point have a unique ID * @see watch */ protected points: { [key: string]: IAlertingPoint } = {} /** * List of watch timers * List item contain Array of point IDS * @see initTimer */ protected timers: { [key: string]: Array<string> } = {} /** * Add listener for getting alert messages * * @param f function for listening * @see makeAlert */ addListener(f: (alert: IAlert) => void) { this.listeners.push(f) } /** * Start watch of alert point * * Automatically queries the data at the specified `query` interval for the `path` * metric and checks the received data against `condition`. * * @param path Metric path in vrack database * @param query Query parameters * @param condition Condition settings class * @param id Unique id for this point. If you don't specify it, it will be filled in automatically * @param additional Additional information that will be specified in the created message */ watch(path: string, query: AlertQuery, condition: AlertCondition, id: string, additional: { [key: string]: any }) { if (id === "") id = Utility.uid() if (this.points[id] !== undefined) throw ErrorManager.make(new Error, 'VDB_ALERTING_DUBLICATE_UID', { id }) const fconf: IAlertingPoint = { status: 'ok',created: 0, path, query, condition, additional, id, count: 0 } this.points[id] = fconf this.initTimer(id) return id } /** * Stop watching * * @see watch * @param id Unique id of point */ unwatch(id: string) { if (this.points[id]) { const tInterval = this.points[id].query.getEvaluateInterval() const isec = Interval.parseInterval(tInterval); const timer = this.timers[isec] for (let i = 0; i < timer.length; i++) if (timer[i] === id) timer.splice(i, 1) delete this.points[id] } } /** * Initializing the Data Refresh Timer * * @param id Unique id of point */ protected initTimer(id: string) { const tInterval = this.points[id].query.query.evaluateInterval const isec = Interval.parseInterval(tInterval); if (this.timers[isec] === undefined) { this.timers[isec] = [] setInterval(() => { this.runTimer(isec) }, isec * 1000) } this.timers[isec].push(id) } /** * Run a timer * * @see initTimer * * @param isec Timer evaluate interval in second */ protected runTimer(isec: number) { for (const id of this.timers[isec]) this.evaluate(id) } /** * Running a evaluate AlertPoint * * @param id Unique id of point */ protected evaluate(id: string) { const point = this.points[id] const value = this.getQueryValue(point) if (point.condition.check(value, id)) { point.count++ if (point.status === 'ok'){ point.status = "created" point.created = Math.floor(Date.now() / 1000) return this.makeAlert(id, value, point) } point.status = "updated" this.makeAlert(id, value, point) } else { if (point.status !== "ok") { point.status = "ok" point.count = 0 this.makeAlert(id, value, point) } } } /** * Return query result * * @param point Alert point */ protected getQueryValue(point: IAlertingPoint) { const query = point.query const qr = this.database.read(point.path, query.query.period, query.query.interval) return MetricResult.aggregate(qr, query.query.func) } /** * Creates an alarm and sends information to all subscribers * * @param id UID of point * @param value alert evaluate value * @param point */ protected makeAlert(id: string, value: number | null, point: IAlertingPoint) { const nAlert: IAlert = { id, value, status: this.points[id].status, count: this.points[id].count, timestamp: Math.floor(Date.now() / 1000), created: this.points[id].created, condition: point.condition.condition, areas: point.condition.areas(), threshholds: point.condition.threshholds(), additional: point.additional } for (const f of this.listeners) f(nAlert) } }