@openhps/core
Version:
Open Hybrid Positioning System - Core component
206 lines • 6.53 kB
JavaScript
import 'reflect-metadata';
import { DataObject, ReferenceSpace } from './data';
import { GraphValidator, ModelGraph } from './graph/_internal/implementations';
import { GraphBuilder } from './graph/builders/GraphBuilder';
import { NodeData, TimeService, DataObjectService, MemoryDataService, NodeDataService } from './service';
/**
* Model builder to construct and build a {@link Model} consisting of graph shapes and services.
*
* ## Usage
* Models can be created using the {@link ModelBuilder}. Once you have added all services and constructed the graph, you can build the model using the ```build()``` function. A promise will be returned with the created model.
*
* ```typescript
* import { ModelBuilder } from '@openhps/core';
*
* ModelBuilder.create()
* .build().then(model => {
* // ...
* });
* ```
* The graph shape of a model is immutable and can not be altered after building.
*
* ### Shape Builder
* Shapes can be created by starting with the ```from()``` function. This function takes an optional
* parameter of one or multiple [source nodes](#sourcenode).
*
* In order to end a shape, the ```to()``` function needs to be called with one or more optional [sink nodes](#sinknode).
* ```typescript
* import { ModelBuilder } from '@openhps/core';
*
* ModelBuilder.create()
* .from()
* .to()
* .build().then(model => {
* // ...
* });
* ```
*
* Alternatively for readability with multiple shapes, the shapes can individually be created using the ```addShape()``` function as shown below.
* ```typescript
* import { ModelBuilder, GraphBuilder } from '@openhps/core';
*
* ModelBuilder.create()
* .addShape(
* GraphBuilder.create()
* .from()
* .to())
* .build().then(model => {
* // ...
* });
* ```
*
* #### Building Source Processors
* It is possible to have multiple processing nodes between the source and sink. These processing nodes can manipulate the data frame
* when it traverses from node to node.
* ```typescript
* import { ModelBuilder } from '@openhps/core';
*
* ModelBuilder.create()
* .from(...)
* .via(new ComputingNode())
* .via(new AnotherComputingNode())
* .to(...)
* .build().then(model => {
* // ...
* });
* ```
*
* #### Helper Functions
* Helper functions can replace the ```via()``` function. Commonly used nodes such as frame filters, merging of data frames from
* multiple sources, ... can be replaced with simple functions as ```filter()``` or ```merge()``` respectively.
* ```typescript
* import { ModelBuilder } from '@openhps/core';
* import { CSVSourceNode, CSVSinkNode } from '@openhps/csv';
*
* ModelBuilder.create()
* .from(
* new CSVSourceNode('scanner1.csv', ...),
* new CSVSourceNode('scanner2.csv', ...),
* new CSVSourceNode('scanner3.csv', ...)
* )
* .filter((frame: DataFrame) => true)
* .merge((frame: DataFrame) => frame.source.uid)
* .via(new ComputingNode())
* .via(new AnotherComputingNode())
* .to(new CSVSinkNode('output.csv', ...))
* .build().then(model => {
* // ...
* });
* ```
*
* ### Debug Logging
* When building the model, you can provide a logger callback that has two arguments. An error level complying
* with normal log levels and a log object that represents an object.
* ```typescript
* import { ModelBuilder } from '@openhps/core';
*
* ModelBuilder.create()
* // Set the logger that will be used by all nodes and services
* .withLogger((level: string, log: any) => {
* console.log(log);
* })
* // ...
* .build().then(model => {
* // ...
* });
* ```
*
* ### Adding Services
* Adding services can be done using the ```addService()``` function in the model builder.
* ```typescript
* import { ModelBuilder } from '@openhps/core';
*
* ModelBuilder.create()
* .addService(...)
* // ...
* .build().then(model => {
*
* });
* ```
*/
export class ModelBuilder extends GraphBuilder {
constructor() {
super(new ModelGraph());
this.graph.name = 'model';
// Store data objects
this.graph.addService(new DataObjectService(new MemoryDataService(DataObject, {
keepChangelog: false
})));
// Store spaces in their own memory data object service
this.graph.addService(new DataObjectService(new MemoryDataService(ReferenceSpace, {
keepChangelog: false
})));
// Store node data
this.graph.addService(new NodeDataService(new MemoryDataService(NodeData, {
keepChangelog: false
})));
// Default time service using system time
this.graph.addService(new TimeService());
}
static create() {
return new ModelBuilder();
}
/**
* Model logger
* @param {Function} logger Logging function
* @returns {ModelBuilder} Model builder instance
*/
withLogger(logger) {
this.graph.logger = logger;
return this;
}
withReferenceSpace(space) {
this.graph.referenceSpace = space;
return this;
}
/**
* Add a service to the model
* @param {Service} service Service to add
* @param {ProxyHandler} [proxy] Proxy handler
* @returns {ModelBuilder} Model builder instance
*/
addService(service, proxy) {
this.graph.addService(service, proxy);
return this;
}
/**
* Add multiple services to the model
* @param {Service[]} services Services to add
* @returns {ModelBuilder} Model builder instance
*/
addServices(...services) {
services.forEach(service => this.addService(service));
return this;
}
/**
* Add graph shape to graph
* @param {GraphBuilder | GraphShape | Model} shape Graph builder or abstract graph
* @returns {GraphBuilder} Current graph builder instance
*/
addShape(shape) {
if (shape instanceof ModelGraph) {
// Add services
shape.findAllServices().forEach(service => {
this.addService(service);
});
} else if (shape instanceof ModelBuilder) {
shape.graph.findAllServices().forEach(service => {
this.addService(service);
});
}
return super.addShape(shape);
}
build() {
return new Promise((resolve, reject) => {
GraphValidator.validate(this.graph);
this.graph.once('ready', () => {
resolve(this.graph);
});
this.graph.emitAsync('build', this).catch(ex => {
// Destroy model
this.graph.emit('destroy');
reject(ex);
});
});
}
}