@signalk/streams
Version:
Utilities for handling streams of Signal K data
447 lines (446 loc) • 16.1 kB
JavaScript
"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;