@openhps/core
Version:
Open Hybrid Positioning System - Core component
252 lines • 8.38 kB
JavaScript
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 };