UNPKG

@signalk/streams

Version:

Utilities for handling streams of Signal K data

447 lines (446 loc) 16.1 kB
"use strict"; 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 n2kAnalyzer_1 = __importDefault(require("./n2kAnalyzer")); const from_json_1 = __importDefault(require("./from_json")); const multiplexedlog_1 = __importDefault(require("./multiplexedlog")); const nmea0183_signalk_1 = __importDefault(require("./nmea0183-signalk")); const n2k_signalk_1 = __importDefault(require("./n2k-signalk")); const log_1 = __importDefault(require("./log")); const liner_1 = __importDefault(require("./liner")); const splitting_liner_1 = __importDefault(require("./splitting-liner")); const execute_1 = __importDefault(require("./execute")); const udp_1 = __importDefault(require("./udp")); const tcp_1 = __importDefault(require("./tcp")); const tcpserver_1 = __importDefault(require("./tcpserver")); const filestream_1 = __importDefault(require("./filestream")); const replacer_1 = __importDefault(require("./replacer")); const throttle_1 = __importDefault(require("./throttle")); const timestamp_throttle_1 = __importDefault(require("./timestamp-throttle")); const canboatjs_1 = __importDefault(require("./canboatjs")); const canboatjs_2 = require("@canboat/canboatjs"); const gpsd_1 = __importDefault(require("./gpsd")); const pigpio_seatalk_1 = __importDefault(require("./pigpio-seatalk")); const gpiod_seatalk_1 = __importDefault(require("./gpiod-seatalk")); const W2k01Ctor = canboatjs_2.W2k01; const Ydwg02Ctor = canboatjs_2.Ydwg02; const iKonvertCtor = canboatjs_2.iKonvert; const discriminatorByDataType = { NMEA2000JS: 'A', NMEA2000IK: 'A', NMEA2000YD: 'A', NMEA2000: 'A', NMEA0183: 'N', SignalK: 'I', Seatalk: 'N' }; const dataTypeMapping = { SignalK: (options) => options.subOptions.type !== 'wss' && options.subOptions.type !== 'ws' ? [new from_json_1.default()] : [], Seatalk: (options) => [ new nmea0183_signalk_1.default({ ...options.subOptions, validateChecksum: false }) ], NMEA0183: (options) => { const result = [new nmea0183_signalk_1.default(options.subOptions)]; if (options.type === 'FileStream') { result.unshift(new throttle_1.default({ rate: options.subOptions.throttleRate ?? 1000, chunksize: options.subOptions.throttleRate ?? 1000 })); } return result; }, NMEA2000: (options) => { const result = [new n2kAnalyzer_1.default(options.subOptions)]; if (options.type === 'FileStream') { result.push(new timestamp_throttle_1.default()); } return [...result, new n2k_signalk_1.default(options.subOptions)]; }, NMEA2000JS: (options) => { const result = [new canboatjs_1.default(options.subOptions)]; if (options.type === 'FileStream') { result.push(new timestamp_throttle_1.default()); } return [...result, new n2k_signalk_1.default(options.subOptions)]; }, NMEA2000IK: (options) => { const result = [new canboatjs_1.default(options.subOptions)]; if (options.type === 'FileStream') { result.push(new timestamp_throttle_1.default({ getMilliseconds: (msg) => { return msg.timer * 1000; } })); } { let subOptions; if (options.subOptions.type === 'navlink2-tcp-canboatjs') { subOptions = { ...options.subOptions, tcp: true }; } else { subOptions = options.subOptions; } result.unshift(new iKonvertCtor(subOptions)); } return [...result, new n2k_signalk_1.default(options.subOptions)]; }, NMEA2000YD: (options) => { const result = [ new Ydwg02Ctor({ ...options.subOptions }, options.subOptions.type === 'ydwg02-usb-canboatjs' ? 'usb' : 'network') ]; if (options.type === 'FileStream') { result.push(new timestamp_throttle_1.default()); } return [...result, new n2k_signalk_1.default(options.subOptions)]; }, NMEA2000W2K_ASCII: (options) => { const result = [ new W2k01Ctor({ format: 'ascii', ...options.subOptions }) ]; if (options.type === 'FileStream') { result.push(new timestamp_throttle_1.default()); } return [...result, new n2k_signalk_1.default(options.subOptions)]; }, NMEA2000W2K_ACTISENSE: (options) => { const result = [ new W2k01Ctor({ format: 'actisense', ...options.subOptions }) ]; if (options.type === 'FileStream') { result.push(new timestamp_throttle_1.default()); } return [...result, new n2k_signalk_1.default(options.subOptions)]; }, Multiplexed: (options) => [new multiplexedlog_1.default(options.subOptions)] }; function nmea2000input(subOptions, logging) { if (subOptions.type === 'ngt-1-canboatjs') { const ActisenseSerial = require('./actisense-serial'); const Ctor = ActisenseSerial.default ?? ActisenseSerial; return [ new Ctor({ ...subOptions, plainText: logging }) ]; } else if (subOptions.type === 'canbus-canboatjs') { const Canbus = require('./canbus'); const Ctor = Canbus.default ?? Canbus; return [ new Ctor({ ...subOptions, canDevice: subOptions.interface }) ]; } else if (subOptions.type === 'ikonvert-canboatjs') { const Serialport = require('./serialport'); const Ctor = Serialport.default ?? Serialport; return [ new Ctor({ ...subOptions, baudrate: 230400, toStdout: 'ikonvertOut' }) ]; } else if (subOptions.type === 'ydwg02-canboatjs') { return [ new tcp_1.default({ ...subOptions, outEvent: 'ydwg02-out' }), new liner_1.default(subOptions) ]; } else if (subOptions.type === 'ydwg02-udp-canboatjs') { return [ new udp_1.default({ ...subOptions, outEvent: 'ydwg02-out' }), new liner_1.default(subOptions) ]; } else if (subOptions.type === 'navlink2-tcp-canboatjs') { return [ new tcp_1.default({ ...subOptions, outEvent: 'navlink2-out' }), new liner_1.default(subOptions) ]; } else if (subOptions.type === 'w2k-1-n2k-ascii-canboatjs') { return [ new tcp_1.default({ ...subOptions, outEvent: 'w2k-1-out' }), new liner_1.default(subOptions), new W2k01Ctor(subOptions, 'ascii', 'w2k-1-out') ]; } else if (subOptions.type === 'w2k-1-n2k-actisense-canboatjs') { return [ new tcp_1.default({ ...subOptions, outEvent: 'w2k-1-out' }), new W2k01Ctor(subOptions, 'actisense', 'w2k-1-out') ]; } else if (subOptions.type === 'navlink2-udp-canboatjs') { return [ new udp_1.default(subOptions), new liner_1.default(subOptions) ]; } else if (subOptions.type === 'ydwg02-usb-canboatjs') { const Serialport = require('./serialport'); const Ctor = Serialport.default ?? Serialport; return [ new Ctor({ ...subOptions, baudrate: 38400, toStdout: 'ydwg02-out' }) ]; } else { let command; let toChildProcess; if (subOptions.type === 'ngt-1') { command = `actisense-serial -s ${subOptions.baudrate ?? 115200} ${subOptions.device}`; toChildProcess = 'nmea2000out'; } else if (subOptions.type === 'canbus') { command = `candump ${subOptions.interface} | candump2analyzer`; toChildProcess = undefined; } else { throw new Error(`unknown NMEA2000 type ${subOptions.type}`); } return [ new execute_1.default({ command, toChildProcess, app: subOptions.app, providerId: subOptions.providerId }), new liner_1.default(subOptions) ]; } } function nmea0183input(subOptions) { let pipePart; if (subOptions.type === 'tcp') { pipePart = [ new tcp_1.default(subOptions), new liner_1.default(subOptions) ]; } else if (subOptions.type === 'tcpserver') { pipePart = [new tcpserver_1.default(subOptions), new liner_1.default(subOptions)]; } else if (subOptions.type === 'udp') { pipePart = [ new udp_1.default(subOptions), new splitting_liner_1.default(subOptions) ]; } else if (subOptions.type === 'serial') { const Serialport = require('./serialport'); const Ctor = Serialport.default ?? Serialport; pipePart = [new Ctor(subOptions)]; } else if (subOptions.type === 'gpsd') { pipePart = [new gpsd_1.default(subOptions)]; } if (pipePart) { if (subOptions.removeNulls) { pipePart.push(new replacer_1.default({ regexp: '\u0000', template: '' })); } if (subOptions.ignoredSentences) { console.log(subOptions.ignoredSentences); subOptions.ignoredSentences.forEach((sentence) => { if (sentence.length > 0) { pipePart.push(new replacer_1.default({ regexp: `^...${sentence}.*`, template: '' })); } }); } return pipePart; } else { throw new Error(`Unknown networking type: ${subOptions.type}`); } } function executeInput(subOptions) { return [ new execute_1.default(subOptions), new liner_1.default(subOptions) ]; } function fileInput(subOptions) { return [ new filestream_1.default(subOptions), new liner_1.default(subOptions) ]; } function signalKInput(subOptions) { if (subOptions.type === 'ws' || subOptions.type === 'wss') { const MdnsWs = require('./mdns-ws'); const Ctor = MdnsWs.default ?? MdnsWs; return [new Ctor(subOptions)]; } else if (subOptions.type === 'tcp') { return [ new tcp_1.default(subOptions), new liner_1.default(subOptions) ]; } else if (subOptions.type === 'udp') { return [new udp_1.default(subOptions)]; } else if (subOptions.type === 'serial') { const Serialport = require('./serialport'); const Ctor = Serialport.default ?? Serialport; return [new Ctor(subOptions)]; } throw new Error(`unknown SignalK type: ${subOptions.type}`); } function seatalkInput(subOptions) { if (subOptions.type === 'gpiod') { return [new gpiod_seatalk_1.default(subOptions)]; } else { return [new pigpio_seatalk_1.default(subOptions)]; } } const pipeStartByType = { NMEA2000: nmea2000input, NMEA0183: nmea0183input, Execute: executeInput, FileStream: fileInput, SignalK: signalKInput, Seatalk: seatalkInput }; function getLoggerPipeline(app, logging, discriminator) { if (!logging) { return []; } return [ new log_1.default({ app, discriminator }) ]; } class Simple extends stream_1.Transform { pipeline; constructor(options) { super({ objectMode: true }); const { emitPropertyValue, onPropertyValues, createDebug } = options; const opts = { ...options }; opts.subOptions = { ...options.subOptions, emitPropertyValue, onPropertyValues, createDebug }; opts.subOptions.providerId = options.providerId; const dataType = opts.subOptions.dataType ?? options.type; if (!dataType) { throw new Error(`Unknown data type for ${options.type}`); } if (!pipeStartByType[options.type]) { throw new Error(`Invalid input type: ${options.type}`); } if (!dataTypeMapping[dataType]) { throw new Error(`Unknown data type: ${dataType}`); } if (dataType !== 'Multiplexed' && !discriminatorByDataType[dataType]) { throw new Error(`No discriminator for: ${dataType}`); } opts.subOptions.app = options.app; let mappingType = dataType; if (options.type === 'NMEA2000' && opts.subOptions) { if (opts.subOptions.type === 'ngt-1-canboatjs' || opts.subOptions.type === 'canbus-canboatjs' || opts.subOptions.type === 'w2k-1-n2k-actisense-canboatjs' || opts.subOptions.type === 'w2k-1-n2k-ascii-canboatjs') { mappingType = 'NMEA2000JS'; } else if (opts.subOptions.type === 'ikonvert-canboatjs' || opts.subOptions.type === 'navlink2-tcp-canboatjs') { mappingType = 'NMEA2000IK'; } else if (opts.subOptions.type === 'ydwg02-canboatjs' || opts.subOptions.type === 'ydwg02-udp-canboatjs' || opts.subOptions.type === 'ydwg02-usb-canboatjs') { mappingType = 'NMEA2000YD'; } options.app.on('nmea2000out', () => { setImmediate(() => options.app.emit('connectionwrite', { providerId: options.providerId })); }); options.app.on('nmea2000JsonOut', () => { setImmediate(() => options.app.emit('connectionwrite', { providerId: options.providerId })); }); } const pipeStart = pipeStartByType[options.type]; const dataMapper = dataTypeMapping[mappingType]; const pipeline = [ ...pipeStart(opts.subOptions, options.logging), ...getLoggerPipeline(options.app, options.logging, discriminatorByDataType[dataType]), ...dataMapper(opts) ]; const dataReceivedEventName = `${opts.subOptions.providerId}-received`; const spy = new stream_1.Transform({ objectMode: true, transform(chunk, encoding, callback) { options.app.emit(dataReceivedEventName, chunk); callback(null, chunk); } }); pipeline.splice(pipeline.length - 1, 0, spy); opts.subOptions.app.emitPropertyValue('pipedprovider', { id: opts.subOptions.providerId, type: mappingType, eventNames: { received: dataReceivedEventName } }); if (opts.subOptions.overrideTimestamp) { pipeline.push(new stream_1.Transform({ objectMode: true, transform(delta, encoding, callback) { if (delta.updates) { const now = new Date().toISOString(); delta.updates.forEach((update) => { update.timestamp = now; }); } callback(null, delta); } })); } for (let i = pipeline.length - 2; i >= 0; i--) { pipeline[i].pipe(pipeline[i + 1]); } pipeline[pipeline.length - 1].pipe(this); this.pipeline = pipeline; } _transform(msg, encoding, done) { this.push(msg); done(); } end() { this.pipeline[0].end(); return this; } } exports.default = Simple;