UNPKG

@signalk/streams

Version:

Utilities for handling streams of Signal K data

187 lines (185 loc) 6.67 kB
"use strict"; /* * Copyright 2016 Teppo Kurki <teppo.kurki@iki.fi> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const stream_1 = require("stream"); const timestamp_throttle_1 = __importDefault(require("./timestamp-throttle")); const n2k_signalk_1 = __importDefault(require("./n2k-signalk")); const n2kAnalyzer_1 = __importDefault(require("./n2kAnalyzer")); const canboatjs_1 = __importDefault(require("./canboatjs")); const nmea0183_signalk_1 = __importDefault(require("./nmea0183-signalk")); class ToTimestamped extends stream_1.Transform { deMultiplexer; options; multiplexedFormat = false; constructor(deMultiplexer, options) { super({ objectMode: true }); this.deMultiplexer = deMultiplexer; this.options = options; } _transform(msg, encoding, done) { const str = msg.toString(); if (str.trim().length === 0) { done(); return; } this.multiplexedFormat = str.length > 16 && str.charAt(13) === ';' && str.split(';').length >= 3; if (this.multiplexedFormat) { if (this.options.noThrottle) { this.deMultiplexer.toTimestamped.pipe(this.deMultiplexer.splitter); } else { this.deMultiplexer.toTimestamped .pipe(this.deMultiplexer.timestampThrottle) .pipe(this.deMultiplexer.splitter); } this._transform = this.handleMultiplexed; } else { this._transform = this.handleMixed; } this._transform(msg, encoding, done); } handleMixed(msg, encoding, done) { const line = msg.toString(); const res = { timestamp: new Date().getTime(), data: line, discriminator: 'I' }; if (line.charAt(0) === '{') { res.discriminator = 'I'; } else if ((line.charAt(0) === '$' || line.charAt(0) === '!') && !line.startsWith('!PDGY')) { res.discriminator = 'N'; } else { res.discriminator = 'A'; } this.push(res); done(); } handleMultiplexed(msg, encoding, done) { const line = msg.toString(); const parts = line.split(';'); this.push({ timestamp: parts[0], discriminator: parts[1], data: parts.slice(2).join(';') }); done(); } } class Splitter extends stream_1.Transform { demuxEmitData; fromN2KJson; fromActisenseSerial; fromNMEA0183; constructor(deMultiplexer, options) { super({ objectMode: true }); this.demuxEmitData = (msg) => { deMultiplexer.emit('data', msg); }; this.fromN2KJson = new n2k_signalk_1.default(options); this.fromN2KJson.on('data', this.demuxEmitData); if (options.useCanboatjs === undefined || options.useCanboatjs) { this.fromActisenseSerial = new canboatjs_1.default(options); } else { this.fromActisenseSerial = new n2kAnalyzer_1.default(options); } this.fromActisenseSerial.pipe(this.fromN2KJson); this.fromNMEA0183 = new nmea0183_signalk_1.default(options); this.fromNMEA0183.on('data', this.demuxEmitData); } _transform(msg, encoding, done) { let actualDone = done; try { switch (msg.discriminator) { case 'A': { msg.fromFile = true; const result = this.fromActisenseSerial.write(msg, encoding); if (!result) { this.fromActisenseSerial.once('drain', done); actualDone = () => { }; } break; } case 'C': case 'N': case 'G': case 'M': this.fromNMEA0183.write({ line: msg.data, timestamp: msg.timestamp }, encoding); break; case 'I': default: try { const parsed = JSON.parse(msg.data); const timestamp = new Date(Number(msg.timestamp)); if (parsed.updates) { parsed.updates.forEach((update) => { update.timestamp = timestamp; }); } this.push(parsed); this.demuxEmitData(parsed); } catch (e) { console.error(e); } break; } } finally { actualDone(); } } pipe(target) { this.fromN2KJson.pipe(target); this.fromNMEA0183.pipe(target); return stream_1.Transform.prototype.pipe.call(this, target); } } class DeMultiplexer extends stream_1.Writable { toTimestamped; timestampThrottle; splitter; constructor(options) { super(); this.toTimestamped = new ToTimestamped(this, options); this.timestampThrottle = new timestamp_throttle_1.default({ getMilliseconds: (msg) => typeof msg.timestamp === 'string' ? Number(msg.timestamp) : msg.timestamp }); this.splitter = new Splitter(this, options); this.toTimestamped.on('drain', this.emit.bind(this, 'drain')); } pipe(target) { return this.splitter.pipe(target); } write(chunk, encodingOrCallback, callback) { if (typeof encodingOrCallback === 'function') { return this.toTimestamped.write(chunk, encodingOrCallback); } return this.toTimestamped.write(chunk, encodingOrCallback ?? 'utf8', callback); } } exports.default = DeMultiplexer;