UNPKG

@openhps/core

Version:

Open Hybrid Positioning System - Core component

180 lines 5.98 kB
import { DataObject } from '../data'; import { TimeUnit } from '../utils'; import { Service } from './Service'; import { TimeService } from './TimeService'; /** * Location-Based Service * * ## Usage * * ### Creation * ```typescript * const service = new LocationBasedService< * DataObject, * GeographicalPosition * >(); * * ModelBuilder.create() * .addService(service) * .from() * .to().build(); * ``` * * ### Getting the current position * * ### Setting the current position * * ### Watching the position of an object */ export class LocationBasedService extends Service { constructor(options) { super(); this.watchers = new Map(); this.watchedObjects = new Map(); this.watchIndex = 1; this.options = options || {}; this.addDependency(TimeService); this.once('build', this._initLBS.bind(this)); this.once('destroy', this._destroy.bind(this)); } _initLBS() { // Default options this.options.pullNode = this.options.pullNode || this.model.internalSink.uid; this.options.dataService = this.options.dataService || DataObject; this.service = this.model.findDataService(this.options.dataService); this.service.on('insert', (uid, storedObject) => { const watchIds = this.watchedObjects.get(uid); if (watchIds) { const position = storedObject.position; watchIds.forEach(watchId => { const watcher = this.watchers.get(watchId); if (position) { watcher.callback(position); } }); } }); } _destroy() { Array.from(this.watchers.keys()).forEach(watcher => { this.clearWatch(watcher); }); } /** * Set the current position of an object * @param {DataObject | string} object Data object to get the current position of or uid * @param {AbsolutePosition} position Position to update * @returns {Promise<void>} Promise of updating */ setCurrentPosition(object, position) { return new Promise((resolve, reject) => { const uid = object instanceof DataObject ? object.uid : object; this.service.findByUID(uid).then(storedObj => { storedObj.setPosition(position); return this.service.insertObject(storedObj); }).then(() => { resolve(); }).catch(reject); }); } /** * Get the current position of a specific data object. * @param {DataObject | string} object Data object to get the current position of or uid * @param {GeoOptions} [options] Current position options * @returns {Promise<AbsolutePosition>} Promise of latest absolute position */ getCurrentPosition(object, options = {}) { return new Promise((resolve, reject) => { const maximumAge = options.maximumAge || Infinity; options.timeout = options.timeout || 10000; const uid = object instanceof DataObject ? object.uid : object; // Force update if (options.forceUpdate) { this.model.findNodeByUID(this.options.pullNode).pull({ requestedObjects: [uid] }); const timeout = setTimeout(() => { this.clearWatch(watchId); reject(new Error('Timeout error for getting current position!')); }, options.timeout); const watchId = this.watchPosition(object, (pos, err) => { this.clearWatch(watchId); clearTimeout(timeout); if (err) { return reject(err); } resolve(pos); }, Object.assign(Object.assign({}, options), { interval: -1, forceUpdate: false })); } else { this.service.findByUID(uid).then(storedObj => { const position = storedObj.position; const time = TimeService.getUnit().convert(TimeService.now(), TimeUnit.MILLISECOND); if (position && position.timestamp >= time - maximumAge) { // Stored position satisfies maximum age resolve(position); } else { return this.getCurrentPosition(object, Object.assign(Object.assign({}, options), { forceUpdate: true })); } }).then(resolve).catch(reject); } }); } /** * Watch for position changes * @param {DataObject | string} object Data object to watch for position changes for * @param {(position: AbsolutePosition, err?: Error) => void} callback Callback function * @param {GeoWatchOptions} [options] Watch options * @returns {number} Watch number */ watchPosition(object, callback, options = {}) { var _a; const uid = object instanceof DataObject ? object.uid : object; const watchId = this.watchIndex++; const interval = (_a = options.interval) !== null && _a !== void 0 ? _a : 1000; const timer = interval !== -1 ? setInterval(() => { this.getCurrentPosition(object, options).then(callback).catch(ex => { callback(undefined, ex); }); }, interval) : undefined; this.watchers.set(watchId, { timer, uid, callback }); this.watchObject(uid, watchId); return watchId; } watchObject(uid, watchId) { var _a; const existingIds = (_a = this.watchedObjects.get(uid)) !== null && _a !== void 0 ? _a : []; existingIds.push(watchId); this.watchedObjects.set(uid, existingIds); } unwatchObject(uid, watchId) { var _a; const existingIds = (_a = this.watchedObjects.get(uid)) !== null && _a !== void 0 ? _a : []; existingIds.splice(existingIds.indexOf(watchId), 1); if (existingIds.length === 0) { this.watchedObjects.delete(uid); } else { this.watchedObjects.set(uid, existingIds); } } /** * Clear a running position watch * @param {number} watchId Watch identifier */ clearWatch(watchId) { const watcher = this.watchers.get(watchId); if (watcher.timer !== undefined) { clearInterval(watcher.timer); } this.watchers.delete(watchId); this.unwatchObject(watcher.uid, watchId); } }