@openhps/core
Version:
Open Hybrid Positioning System - Core component
209 lines • 7.73 kB
JavaScript
import { __awaiter } from "tslib";
import 'reflect-metadata';
import { Subject } from 'threads/observable';
import { DataSerializer, ModelBuilder, WorkerServiceProxy, CallbackSourceNode, CallbackSinkNode, DataService, DummyDataService, DummyService, ModelSerializer } from '..'; // external @openhps/core
export class WorkerBase {
constructor() {
this.pullOutput = new Subject();
this.pushOutput = new Subject();
this.serviceOutputCall = new Subject();
this.serviceOutputResponse = new Subject();
this.eventOutput = new Subject();
this.customMethods = new Map();
}
setShape(shape) {
this.shape = shape;
}
init(config) {
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
this.config = config;
const importFn = typeof process !== 'object' ? config.type === 'module' ? file => import( /* webpackIgnore: true */file) // ES6
: file => Promise.resolve(importScripts( /* webpackIgnore: true */file)) // CJS
: file => Promise.resolve(require( /* webpackIgnore: true */file)); // eslint-disable-line
// Set global dir name
__dirname = config.directory; // eslint-disable-line
// Load external scripts
if (config.imports && config.imports.length > 0) {
config.imports.forEach(importFile => {
importFn(importFile);
});
}
// Create model
const modelBuilder = ModelBuilder.create();
// Add remote worker services if not already added
this.config.services.forEach(service => {
if (service.dataType) {
const DataType = DataSerializer.findTypeByName(service.dataType);
modelBuilder.addService(new DummyDataService(service.uid, DataType), new WorkerServiceProxy({
uid: service.uid,
callObservable: this.serviceOutputCall,
responseObservable: this.serviceOutputResponse
}));
} else {
modelBuilder.addService(new DummyService(service.uid), new WorkerServiceProxy({
uid: service.uid,
callObservable: this.serviceOutputCall,
responseObservable: this.serviceOutputResponse
}));
}
});
this._initModel(modelBuilder);
// eslint-disable-next-line
const path = this.config.imports.length > 0 ? undefined : typeof process !== 'object' ? undefined : require('path');
try {
if (this.config.serialized) {
const traversalBuilder = modelBuilder.from();
const modelOrNode = ModelSerializer.deserializeNode(this.config.serialized);
traversalBuilder.via(modelOrNode);
traversalBuilder.to();
} else if (this.config.builder) {
const traversalBuilder = modelBuilder.from();
const builderCallback = eval(this.config.builder);
builderCallback(traversalBuilder, modelBuilder, this.config.args);
traversalBuilder.to();
} else if (this.config.shape) {
const graph = yield importFn(path ? path.join(__dirname, this.config.shape) : this.config.shape);
if (graph) {
modelBuilder.addShape(graph.default);
}
} else if (this.shape) {
modelBuilder.addShape(this.shape);
}
} catch (ex) {
// Error deserializing, did you import the nodes?
reject(ex);
return;
}
modelBuilder.build().then(m => {
this.model = m;
// Load methods
this.config.methods.forEach(serializedMethod => {
const method = eval(serializedMethod.handlerFn);
this.customMethods.set(serializedMethod.name, (model, ...args) => {
return Promise.resolve(method(model, ...args));
});
});
resolve();
}).catch(reject);
}));
}
invokeMethod(methodName, ...args) {
return new Promise((resolve, reject) => {
const method = this.customMethods.get(methodName);
if (!method) {
return reject(new Error(`Unable to invoke unknown method '${methodName}'!`));
}
method(this.model, ...args.map(a => DataSerializer.deserialize(a))).then(result => {
resolve(DataSerializer.serialize(result));
}).catch(reject);
});
}
/**
* Pull from this work
* @param {PullOptions} [options] Pull options
* @returns {Promise<void>} Pull promise
*/
pull(options) {
return this.model.pull(options);
}
/**
* Push to this worker
* @param {DataFrame} frame Data frame
* @param {PushOptions} [options] Push options
* @returns {Promise<void>} Push promise
*/
push(frame, options) {
return this.model.push(DataSerializer.deserialize(frame), options);
}
/**
* Init the model internal input and internal output
* @param {ModelBuilder} modelBuilder Model builder
*/
_initModel(modelBuilder) {
const internalSource = new CallbackSourceNode(options => {
// Send a pull request to the main thread
this.pullOutput.next(options);
return undefined;
});
const internalSink = new CallbackSinkNode(frame => {
// Serialize the frame and transmit it to the main thread
this.pushOutput.next(DataSerializer.serialize(frame));
});
modelBuilder.graph.deleteNode(modelBuilder.graph.internalSource);
modelBuilder.graph.internalSource = internalSource;
internalSource.on('error', event => {
this.eventOutput.next({
name: 'error',
event
});
});
internalSource.on('completed', event => {
this.eventOutput.next({
name: 'completed',
event
});
});
modelBuilder.graph.addNode(modelBuilder.graph.internalSource);
modelBuilder.graph.deleteNode(modelBuilder.graph.internalSink);
modelBuilder.graph.internalSink = internalSink;
modelBuilder.graph.addNode(modelBuilder.graph.internalSink);
}
findAllServices() {
return new Promise(resolve => {
const services = this.model.findAllServices();
const servicesArray = services.filter(service => !(service instanceof DummyDataService || service instanceof DummyService)).map(service => {
// Services are wrapped in a proxy. Get prototype
const serviceBase = Object.getPrototypeOf(service);
return {
uid: service.uid,
type: serviceBase.constructor.name,
dataType: service instanceof DataService ? service.driver.dataType.name : undefined
};
});
resolve(servicesArray);
});
}
callService(call) {
return new Promise((resolve, reject) => {
const service = this.model.findDataService(call.serviceUID) || this.model.findService(call.serviceUID);
if (service[call.method]) {
const serializedParams = call.parameters;
const params = [];
serializedParams.forEach(param => {
if (param['__type']) {
params.push(DataSerializer.deserialize(param));
} else {
params.push(param);
}
});
const promise = service[call.method](...params);
Promise.resolve(promise).then(_ => {
if (Array.isArray(_)) {
const result = [];
_.forEach(r => {
result.push(DataSerializer.serialize(r));
});
resolve({
id: call.id,
success: true,
result
});
} else {
const result = DataSerializer.serialize(_);
resolve({
id: call.id,
success: true,
result
});
}
}).catch(ex => {
reject({
id: call.id,
success: false,
result: ex
});
});
}
});
}
}