@openhps/core
Version:
Open Hybrid Positioning System - Core component
135 lines • 5.14 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MergeShape = void 0;
const ProcessingNode_1 = require("../ProcessingNode");
const utils_1 = require("../../utils");
const service_1 = require("../../service");
const events_1 = require("../../graph/events");
/**
* Merge data frames from two or more sources
* using a certain merge key (e.g. source uid, parent uid, node uid).
* @type {@link http://purl.org/poso/HighLevelFusion}
* @category Flow shape
*/
class MergeShape extends ProcessingNode_1.ProcessingNode {
constructor(mergeFn, groupFn, options) {
super(options);
this._queue = new Map();
this._mergeKeyFn = mergeFn;
this._groupFn = groupFn;
// Merge timeout
this.options.timeout = this.options.timeout || 100;
this.options.timeoutUnit = this.options.timeoutUnit || utils_1.TimeUnit.MILLISECOND;
this._timeout = this.options.timeoutUnit.convert(this.options.timeout, service_1.TimeService.getUnit());
this.once('build', this._start.bind(this));
this.once('destroy', this._stop.bind(this));
}
/**
* Start the timeout timer
* @returns {Promise<void>} Timer promise
*/
_start() {
return new Promise((resolve) => {
this.options.minCount = this.options.minCount || this.inlets.length;
this.options.maxCount = this.options.maxCount || this.inlets.length;
const interval = this.options.checkInterval || service_1.TimeService.getUnit().convert(this._timeout, utils_1.TimeUnit.MILLISECOND);
if (this._timeout > 0) {
this._timer = setInterval(this._timerTick.bind(this), interval);
}
resolve();
});
}
_timerTick() {
this._queue.forEach((queue) => {
this._purgeQueue(queue);
});
}
_purgeQueue(queue) {
const currentTime = service_1.TimeService.now();
if (queue !== undefined &&
this._queue.has(queue.key) &&
currentTime - queue.timestamp >= this._timeout &&
queue.frames.size >= this.options.minCount) {
const frames = Array.from(queue.frames.values());
try {
// Merge node
this.outlets.forEach((outlet) => outlet.push(this.merge(frames, queue.key)));
this._queue.delete(queue.key);
// Resolve pending promises
queue.promises.forEach((fn) => {
fn(undefined);
});
}
catch (ex) {
this.emit('error', new events_1.PushError(frames[0].uid, this.uid, ex));
}
return undefined;
}
else {
return queue;
}
}
_stop() {
if (this._timer !== undefined) {
clearInterval(this._timer);
}
}
process(frame, options) {
return new Promise((resolve) => {
if (this.options.maxCount === 1) {
return resolve(frame);
}
// Merge key(s)
const merge = this._mergeKeyFn(frame, options);
if (merge === undefined) {
return resolve(undefined);
}
(Array.isArray(merge) ? merge : [merge]).forEach((key) => {
let queue = this._purgeQueue(this._queue.get(key));
if (queue === undefined) {
// Create a new queued data frame based on the key
queue = new QueuedMerge(key);
queue.promises.push(resolve);
// Group the frames by the grouping function
queue.frames.set(this._groupFn(frame, options), frame);
this._queue.set(key, queue);
}
else {
const groupKey = this._groupFn(frame, options);
if (queue.frames.has(groupKey)) {
// Merge frames
queue.frames.set(groupKey, this.merge([queue.frames.get(groupKey), frame]));
}
else {
queue.frames.set(groupKey, frame);
}
// Check if there are enough frames
if (queue.frames.size >= this.options.maxCount) {
this._queue.delete(key);
const mergedFrame = this.merge(Array.from(queue.frames.values()), key);
resolve(mergedFrame);
queue.promises.forEach((fn) => {
fn(undefined);
});
}
else {
queue.promises.push(resolve);
}
}
});
});
}
}
exports.MergeShape = MergeShape;
/**
* Queued merge
*/
class QueuedMerge {
constructor(key) {
this.frames = new Map();
this.promises = [];
this.key = key;
this.timestamp = service_1.TimeService.now();
}
}
//# sourceMappingURL=MergeShape.js.map