UNPKG

@iotize/tap

Version:

IoTize Device client for Javascript

297 lines 20.6 kB
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; import { combineLatest, Observable, Subject } from 'rxjs'; import { map, startWith } from 'rxjs/operators'; import { createMonitoringTicker, MonitoringController, } from '../monitor/monitor-engine'; import { TapDataError } from '../tap-data-error'; import { ModbusAccessVariable } from '../target-variable/modbus/modbus-access-variable'; import { createTapBundleFromConfig, } from '../target-variable/tap-bundle/tap-bundle'; import { createTapVariableFromConfig, } from '../target-variable/tap-variable/tap-variable'; import { TargetMemoryVariable } from '../target-variable/target-memory/target-memory-variable'; import { createDataManagerConfigFromTapDataConfig } from './create-data-manager-config-from-tap-data-config'; import { createTapBundlesFromConfig, readDataManagerTapConfigFromTap, } from './create-data-manager-from-tap-config'; const TAG = 'DataManager'; /** * Typed data manager is usually build from Tap configuration * * It allow you to have compile time check for bundle/variable types. * However, you cannot add/remove/edit variables (as it would change types) * Use `DataManager` if you want this features */ export class TypedDataManager { constructor(tap, bundles, // variables: Record<VariableKey, EditableDataStream<VariableByType[VariableKey]>> monitoring = new MonitoringController()) { // const variablesCache: Record<string, EditableDataStream<VariableByType[VariableKey], unknown>> = {}; // for (const bundleKey in this.bundles) { // const bundle = this.bundles[bundleKey]; // for (const variableKey in bundle.variables) { // const variable = bundle.variables[variableKey]; // variablesCache[variable.config.name] = variable; // } // } // this.variablesCache = variablesCache as Record<VariableKey, EditableDataStream<VariableByType[VariableKey], unknown>>; // this.setupStreams(); this.tap = tap; this.bundles = bundles; this.monitoring = monitoring; // switchableStream!: SwitchableStream; // dataStream: Observable<Record<string, any>>; // tlvConverter: TlvBundleConverter<Record<string, Uint8Array>>; // sourceControllers: Record<string, DataSourceController> = {}; // protected variablesCache: Record<VariableKey, EditableDataStream<VariableByType[VariableKey], unknown>>; // private tickers: Record<string, { // controller: MonitoringControllerSubject, // ticker: Observable<MonitoringTickerState> // }> = {}; // private ticker = createMonitoringTicker(this.monitoring); this._events = new Subject(); // this.variablesCache = {} as any; const ticker = createMonitoringTicker(this.monitoring); ticker.subscribe((newTick) => __awaiter(this, void 0, void 0, function* () { // console.log('New tick', newTick); yield this.refreshValues(); })); } get values() { const bundles = this.listBundles(); return combineLatest(bundles.map((v) => v.values.pipe(startWith(undefined)))).pipe(map((values) => { return values.reduce((accumulator, v, index) => { const bundleStreamId = bundles[index].id; accumulator[bundleStreamId] = v; return accumulator; }, {}); })); } get events() { return this._events.asObservable(); } listVariables() { return this.listBundles().reduce((acc, bundle) => { acc.push(...bundle.variables); return acc; }, []); } bundle(key) { if (!(key in this.bundles)) { throw TapDataError.bundleNotFound(key); } return this.bundles[key]; } // TODO fix typing variable(key) { const variable = this.listVariables().find((v) => v.id === key); if (!variable) { throw TapDataError.variableNotFound(key); } return variable; } // public addBundleFromConfig(key: BundleKey, bundleConfig: BundleConfig) { // // const key = bundleConfig.name || bundleConfig.id.toString(); // // this.bundles[key] = createTapBundleDataFromConfig(bundleConfig); // throw new Error(`Not implemented yet`); // return this; // } destroy() { this.monitoring.stop(); } refreshValues() { return __awaiter(this, void 0, void 0, function* () { const values = {}; for (const bundle of this.listBundles()) { values[bundle.id] = yield bundle .read() .catch((error) => { this._events.next({ error, type: DataManager.EventType.error, }); return error; }); } return values; }); } listBundles() { return Object.values(this.bundles); } // public setupStreams() { // // this.bundle('x').monitor.start(); // // this.variable('y').monitor. // const variableStream = this.ticker // .pipe( // switchMap(() => { // return merge( // this.listVariables().map(v => defer(() => v.read())) // ); // }) // ); // const bundleStream = this.ticker // .pipe( // switchMap(() => { // return merge( // this.listBundles().map(v => defer(() => v.read())) // ); // }) // ); // // const profileStream = this.ticker // // .pipe( // // switchMap(async () => { // // // debug(TAG, `New call ${time} to read profile`); // // const profileData = (await this.tap.service.variable.readProfile()).body(); // // const decoded = this.tlvConverter.decode(profileData); // // this.rawDataStream.next(decoded); // // }) // // ); // this.switchableStream // .addStream( // 'variable', // variableStream // ) // .addStream( // 'bundle', // bundleStream // ) // // .addStream( // // 'profile', // // profileStream // // ); // } hasBundle(bundleKey) { return bundleKey in this.bundles; } createConfiguredBundle(key, config) { return createTapBundleFromConfig(this.tap, key, config); } createConfiguredVariable(key, config) { return createTapVariableFromConfig(this.tap.service.variable, key, config); } /** * Create a variable with direct modbus access * May/May not be available according to the configured target protocol on your device * * @param id * @param options */ createModbusAccessVariable(id, options) { return new ModbusAccessVariable(id, { config: options.config, targetService: this.tap.service.target, converter: options.converter, }); } /** * Create direct target access to variable * May/May not be available according to configured target protocol on your device * @param id * @param options */ createTargetMemoryVariable(id, options) { return new TargetMemoryVariable(id, { config: options.config, targetService: this.tap.service.target, converter: options.converter, }); } } export class DataManager extends TypedDataManager { constructor(tap, bundles = {}, // variables: Record<VariableKey, EditableDataStream<VariableByType[VariableKey]>> monitoring = new MonitoringController()) { super(tap, bundles, monitoring); } // registerConfiguredBundle(config: BundleConfig) { // throw new Error('Not implemented yet'); // } // registerConfiguredVariable(config: VariableConfig) { // // this.variables.push(); // throw new Error('Not implemented yet'); // } // createTapBundle<T>(name: string, bundleId: number, variables: T): BundleDataStreamInterface<T> { // return createTapBundleFromConfig( // this.tap, // name, // { // id: bundleId, // variables // } // ); // } setBundles(bundles) { this.bundles = bundles; } addBundle(bundle) { this.bundles[bundle.id] = bundle; } removeBundle(bundleKey) { delete this.bundles[bundleKey]; } addVariable(bundleKey, variable) { const bundle = this.bundle(bundleKey); bundle.variables.push(variable); // TODO fix typing } /** * Register bundles configured in the Tap */ registerBundles(bundlesConfig) { const tapBundles = createTapBundlesFromConfig(this.tap, bundlesConfig); this.setBundles(tapBundles); return tapBundles; } registerBundle(key, config) { const tapBundle = createTapBundleFromConfig(this.tap, key, config); this.addBundle(tapBundle); } clear() { this.bundles = {}; } /** * Synchronize DataManager bundles and variables with the connected Tap device * It will the current profile accessible variables/bundles */ synchronizeTapConfig() { return new Observable((observer) => { try { readDataManagerTapConfigFromTap(this.tap, { observer, }) .then((config) => { const newBundles = this.registerBundles(config); observer.next({ step: 'done', bundles: newBundles, }); observer.complete(); }) .catch((err) => { observer.error(err); }); } catch (err) { observer.error(err); } }); } /** * Configure DataManager bundles and variables from a DataConfig object (found in Tap configuration files) * Data decoder will be dedecuced from dataType/length field. */ configureWithDataConfig(config) { this.clear(); this.registerBundles(createDataManagerConfigFromTapDataConfig(config).bundles); } } (function (DataManager) { let EventType; (function (EventType) { EventType["error"] = "error"; })(EventType = DataManager.EventType || (DataManager.EventType = {})); })(DataManager || (DataManager = {})); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGF0YS1tYW5hZ2VyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vLi4vZGF0YS9zcmMvbGliL2RhdGEtbWFuYWdlci9kYXRhLW1hbmFnZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7O0FBS0EsT0FBTyxFQUFFLGFBQWEsRUFBRSxVQUFVLEVBQUUsT0FBTyxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQzFELE9BQU8sRUFBRSxHQUFHLEVBQUUsU0FBUyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFFaEQsT0FBTyxFQUNMLHNCQUFzQixFQUN0QixvQkFBb0IsR0FDckIsTUFBTSwyQkFBMkIsQ0FBQztBQUNuQyxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFFakQsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0sa0RBQWtELENBQUM7QUFDeEYsT0FBTyxFQUNMLHlCQUF5QixHQUcxQixNQUFNLDBDQUEwQyxDQUFDO0FBQ2xELE9BQU8sRUFDTCwyQkFBMkIsR0FFNUIsTUFBTSw4Q0FBOEMsQ0FBQztBQUN0RCxPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSx5REFBeUQsQ0FBQztBQU0vRixPQUFPLEVBQUUsd0NBQXdDLEVBQUUsTUFBTSxtREFBbUQsQ0FBQztBQUM3RyxPQUFPLEVBRUwsMEJBQTBCLEVBRTFCLCtCQUErQixHQUNoQyxNQUFNLHVDQUF1QyxDQUFDO0FBRS9DLE1BQU0sR0FBRyxHQUFHLGFBQWEsQ0FBQztBQXlCMUI7Ozs7OztHQU1HO0FBQ0gsTUFBTSxPQUFPLGdCQUFnQjtJQWtDM0IsWUFDUyxHQUFRLEVBQ0wsT0FHVDtJQUNELGtGQUFrRjtJQUMzRSxhQUFhLElBQUksb0JBQW9CLEVBQUU7UUFFOUMsdUdBQXVHO1FBQ3ZHLDBDQUEwQztRQUMxQyw4Q0FBOEM7UUFDOUMsb0RBQW9EO1FBQ3BELDBEQUEwRDtRQUMxRCwyREFBMkQ7UUFDM0QsUUFBUTtRQUNSLElBQUk7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