UNPKG

@signalk/streams

Version:

Utilities for handling streams of Signal K data

126 lines (125 loc) 4.7 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const child_process_1 = require("child_process"); const stream_1 = require("stream"); const any_shell_escape_1 = __importDefault(require("any-shell-escape")); class SerialStream extends stream_1.Transform { options; debug; serial = null; reconnectEnabled; reconnectDelay = 1000; isFirstError = true; maxPendingWrites; reconnectTimeout = null; constructor(options) { super(); this.options = options; this.reconnectEnabled = options.reconnect ?? true; this.maxPendingWrites = options.maxPendingWrites ?? 5; const createDebug = options.createDebug ?? require('debug'); this.debug = createDebug('signalk:streams:serialport'); let pendingWrites = 0; const stdOutEvents = Array.isArray(this.options.toStdout) ? [...this.options.toStdout] : [this.options.toStdout].filter(Boolean); const standardOutEventName = `serial-${this.options.providerId}-toStdout`; stdOutEvents.push(standardOutEventName); const onDrain = () => { pendingWrites--; }; for (const event of stdOutEvents) { this.options.app.on(event, (d) => { if (pendingWrites > this.maxPendingWrites) { this.debug('Buffer overflow, not writing:' + d); return; } this.debug('Writing:' + d); if (Buffer.isBuffer(d)) { this.serial?.write(d); } else { this.serial?.write(d + '\r\n'); } setImmediate(() => { this.options.app.emit('connectionwrite', { providerId: this.options.providerId }); }); pendingWrites++; this.serial?.drain(onDrain); }); } this.options.app.emitPropertyValue('serialport', { id: this.options.providerId, eventNames: { toStdout: standardOutEventName } }); this.start(); } start() { if (this.serial !== null) { this.serial.unpipe(this); this.serial.removeAllListeners(); this.serial = null; } if (this.reconnectEnabled === false) { return; } if (process.env['PRESERIALCOMMAND']) { (0, child_process_1.execSync)(`${process.env['PRESERIALCOMMAND']} ${(0, any_shell_escape_1.default)(this.options.device)}`); } const { SerialPort } = require('serialport'); const { ReadlineParser } = require('@serialport/parser-readline'); this.serial = new SerialPort({ path: this.options.device, baudRate: this.options.baudrate }); this.serial.on('open', () => { this.reconnectDelay = 1000; this.options.app.setProviderStatus(this.options.providerId, `Connected to ${this.options.device}`); this.isFirstError = true; const parser = new ReadlineParser(); this.serial.pipe(parser).pipe(this); }); this.serial.on('error', (x) => { this.options.app.setProviderError(this.options.providerId, x.message); if (this.isFirstError) { console.log(x.message); } this.debug(x.message); this.isFirstError = false; this.scheduleReconnect(); }); this.serial.on('close', () => { this.options.app.setProviderError(this.options.providerId, 'Closed, reconnecting...'); this.scheduleReconnect(); }); } end() { this.reconnectEnabled = false; if (this.reconnectTimeout) { clearTimeout(this.reconnectTimeout); } if (this.serial) { this.serial.close(); } return this; } _transform(chunk, encoding, done) { this.push(chunk); done(); } scheduleReconnect() { this.reconnectDelay *= this.reconnectDelay < 60 * 1000 ? 1.5 : 1; const msg = `Not connected (retry delay ${(this.reconnectDelay / 1000).toFixed(0)} s)`; this.debug(msg); this.options.app.setProviderStatus(this.options.providerId, msg); this.reconnectTimeout = setTimeout(() => this.start(), this.reconnectDelay); } } exports.default = SerialStream;