UNPKG

pondjs

Version:

A timeseries library build on top of immutable.js

125 lines (103 loc) 3.82 kB
/** * Copyright (c) 2016-2017, The Regents of the University of California, * through Lawrence Berkeley National Laboratory (subject to receipt * of any required approvals from the U.S. Dept. of Energy). * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. */ /*eslint no-console: 0 */ import _ from "underscore"; import Immutable from "immutable"; import Processor from "./processor"; import IndexedEvent from "../indexedevent"; import TimeRangeEvent from "../timerangeevent"; import { isPipeline } from "../pipeline"; import util from "../base/util"; /** * Simple processor generate the Rate of two Event objects and * emit them as a TimeRangeEvent. Can be used alone or chained * with the Align processor for snmp rates, etc. */ export default class Derivator extends Processor { constructor(arg1, options) { super(arg1, options); if (arg1 instanceof Derivator) { const other = arg1; this._fieldSpec = other._fieldSpec; this._allowNegative = other._allowNegative; } else if (isPipeline(arg1)) { const { fieldSpec, allowNegative } = options; this._fieldSpec = fieldSpec; this._allowNegative = allowNegative; } else { throw new Error("Unknown arg to Derivator constructor", arg1); } // // Internal members // this._previous = null; // work out field specs if (_.isString(this._fieldSpec)) { this._fieldSpec = [this._fieldSpec]; } else if (!this._fieldSpec) { this._fieldSpec = ["value"]; } } clone() { return new Derivator(this); } /** * Generate a new TimeRangeEvent containing the rate per second * between two events. */ getRate(event) { let d = new Immutable.Map(); const previousTime = this._previous.timestamp().getTime(); const currentTime = event.timestamp().getTime(); const deltaTime = (currentTime - previousTime) / 1000; this._fieldSpec.forEach(path => { const fieldPath = util.fieldPathToArray(path); const ratePath = fieldPath.slice(); ratePath[ratePath.length - 1] += "_rate"; const previousVal = this._previous.get(fieldPath); const currentVal = event.get(fieldPath); let rate = null; if (!_.isNumber(previousVal) || !_.isNumber(currentVal)) { console.warn( `Path ${fieldPath} contains a non-numeric value or does not exist` ); } else { rate = (currentVal - previousVal) / deltaTime; } if (this._allowNegative === false && rate < 0) { // don't allow negative differentials in certain cases d = d.setIn(ratePath, null); } else { d = d.setIn(ratePath, rate); } }); return new TimeRangeEvent([previousTime, currentTime], d); } /** * Perform the fill operation on the event and emit. */ addEvent(event) { if (event instanceof TimeRangeEvent || event instanceof IndexedEvent) { throw new Error( "TimeRangeEvent and IndexedEvent series can not be aligned." ); } if (this.hasObservers()) { if (!this._previous) { this._previous = event; return; } const outputEvent = this.getRate(event); this.emit(outputEvent); // The current event now becomes the previous event this._previous = event; } } }