UNPKG

@openhps/core

Version:

Open Hybrid Positioning System - Core component

252 lines 8.38 kB
import { __awaiter, __decorate, __metadata } from "tslib"; import { ReferenceSpace } from '../../../data/object'; import { Service } from '../../../service/Service'; import { DataService } from '../../../service/DataService'; import { GraphShape } from './GraphShape'; import { ServiceProxy } from '../../../service/_internal/ServiceProxy'; import { SerializableMapMember, SerializableMember, SerializableObject } from '../../../data/decorators'; import { DataServiceProxy } from '../../../service/_internal'; import { PushPromise } from '../../PushPromise'; /** * [[Model]] implementation */ let ModelGraph = class ModelGraph extends GraphShape { /** * Create a new OpenHPS model * @param {string} name Model name */ constructor(name = 'model') { super(); this._services = new Map(); this._dataServices = new Map(); this.name = name; this.referenceSpace = new ReferenceSpace(undefined); this.removeAllListeners('build'); this.removeAllListeners('destroy'); this.once('build', this._onModelBuild.bind(this)); this.once('destroy', this._onModelDestroy.bind(this)); } _onModelBuild(_) { return new Promise((resolve, reject) => { this.emit('prebuild', _); // First resolve the building of services this._buildServices().then(() => { for (const service of this.findAllServices()) { if (!service.isReady()) { service.emit('ready'); } } // Build nodes return this._buildNodes(_); }).then(() => { for (const node of this.nodes) { if (!node.isReady()) { node.emit('ready'); } } this.emit('ready'); this.emit('postbuild', this); resolve(); }).catch(reject); }); } _buildServices() { return new Promise((resolve, reject) => { const buildPromises = []; const loadService = service => __awaiter(this, void 0, void 0, function* () { if (service.isReady()) { return Promise.resolve(true); } const dependencies = service.dependencies || []; const dependencyPromises = dependencies.map(dep => { const depService = this.findService(dep); if (depService) { return loadService(depService); } else { return Promise.resolve(true); } }); yield Promise.all(dependencyPromises); const result = yield service.emitAsync('build'); service.emit('ready'); return result; }); this._services.forEach(service => { buildPromises.push(loadService(service)); }); this._dataServices.forEach(service => { buildPromises.push(loadService(service)); }); Promise.all(buildPromises).then(() => resolve()).catch(reject); }); } _buildNodes(_) { return new Promise((resolve, reject) => { const buildPromises = []; this.nodes.forEach(node => { if (!node.isReady()) { buildPromises.push(node.emitAsync('build', _)); } }); Promise.all(buildPromises).then(() => resolve()).catch(reject); }); } _onModelDestroy(_) { return new Promise((resolve, reject) => { const destroyPromises = []; this._services.forEach(service => { destroyPromises.push(service.emitAsync('destroy', _)); }); this._dataServices.forEach(service => { destroyPromises.push(service.emitAsync('destroy', _)); }); this.nodes.forEach(node => { destroyPromises.push(node.emitAsync('destroy', _)); }); Promise.all(destroyPromises).then(() => { resolve(); }).catch(reject); }); } findService(q) { let result = undefined; if (!q) { return undefined; } else if (typeof q === 'string') { result = this._services.get(q); } else { result = Array.from(this._services.values()).filter(s => s instanceof q)[0]; } if (!result) { result = this.findDataService(q); } return result; } findDataService(q) { let result; if (q === undefined) { result = undefined; } else if (typeof q === 'string') { // Find by name result = this._findDataServiceByUID(q); } else if (q.prototype instanceof DataService) { // Find by data service class result = this.findAllServices(q)[0]; } else if (q instanceof Function) { // Find by constructor result = this.findAllDataServices(q)[0]; } else { // Find by instance result = this.findDataService(q.constructor); } return result; } _findDataServiceByUID(uid) { return Array.from(this._dataServices.values()).filter(s => s.uid === uid)[0]; } /** * Find all services and data services * @param {typeof Service} [q] Service class * @returns {Service[]} Array of all services */ findAllServices(q) { if (q !== undefined) { return this.findAllServices().filter(s => s instanceof q) || []; } else { return Array.from(this._services.values()).concat(Array.from(this._dataServices.values())) || []; } } /** * Find all data services by data type * @param {typeof Service} [q] data type class * @returns {Service[]} Array of all services */ findAllDataServices(q) { if (q !== undefined) { return this.findAllDataServices().map(s => [s, ...this._instanceofPriority(q, s['target'].dataType)]).filter(s => s[1]).sort((a, b) => a[2] === b[2] ? b[0].priority - a[0].priority : a[2] - b[2]).map(s => s[0]) || []; } else { return Array.from(this._dataServices.values()) || []; } } _instanceofPriority(obj, constr) { if (obj === constr) { return [true, 0]; } let level = 1; while (obj = Object.getPrototypeOf(obj)) { if (obj === constr) { return [true, level]; } level++; } return [false, undefined]; } /** * Add service to model * @param {Service} service Service to add * @param {ProxyHandler} [proxy] Proxy handler */ addService(service, proxy) { service.model = this.graph === undefined ? this : this.model; if (service instanceof DataService) { // Data service this._dataServices.set(service.uid, new Proxy(service, proxy || new DataServiceProxy())); } else { // Normal service this._services.set(service.uid, new Proxy(service, proxy || new ServiceProxy())); } } push(frame, options) { return new PushPromise((resolve, reject, completed) => { const servicePromises = []; // Merge the changes in the frame service const frameService = this.findDataService(frame.constructor.name); if (frameService) { if (Array.isArray(frame)) { frame.forEach(f => { // Update the frame servicePromises.push(frameService.insert(f.uid, frame)); }); } else { // Update the frame servicePromises.push(frameService.insert(frame.uid, frame)); } } Promise.all(servicePromises).then(() => { const completedFrames = new Set(); this.once('completed', e => { if (Array.isArray(frame)) { frame.forEach(f => { if (e.frameUID === f.uid) { completedFrames.add(f.uid); } }); if (completedFrames.size === frame.length) { completed(); } } else { if (e.frameUID === frame.uid) { completed(); } } }); const promise = this.internalSource.push(frame, options); return promise; }).then(() => { resolve(); }).catch(reject); }); } destroy() { return this.emitAsync('destroy'); } }; __decorate([SerializableMapMember(String, Service, { name: 'services' }), __metadata("design:type", Map)], ModelGraph.prototype, "_services", void 0); __decorate([SerializableMapMember(String, DataService, { name: 'dataServices' }), __metadata("design:type", Map)], ModelGraph.prototype, "_dataServices", void 0); __decorate([SerializableMember(), __metadata("design:type", ReferenceSpace)], ModelGraph.prototype, "referenceSpace", void 0); ModelGraph = __decorate([SerializableObject(), __metadata("design:paramtypes", [Object])], ModelGraph); export { ModelGraph };