@canboat/canboatjs
Version:
Native javascript version of canboat
296 lines • 10.6 kB
JavaScript
"use strict";
/**
* Copyright 2018 Scott Bender (scott@scottbender.net)
*
* 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 });
exports.Ydgw02Stream = Ydgw02Stream;
const utilities_1 = require("./utilities");
const stream_1 = require("stream");
const fromPgn_1 = require("./fromPgn");
const yddevice_1 = require("./yddevice");
const toPgn_1 = require("./toPgn");
const canId_1 = require("./canId");
const util_1 = __importDefault(require("util"));
//const pgnsSent = {}
function Ydgw02Stream(options, type) {
if (this === undefined) {
return new Ydgw02Stream(options, type);
}
stream_1.Transform.call(this, {
objectMode: true
});
this.debug = (0, utilities_1.createDebug)('canboatjs:ydgw02', options);
this.debugOut = (0, utilities_1.createDebug)('canboatjs:n2k-out', options);
this.sentAvailable = false;
this.options = options;
this.outEvent = options.ydgwOutEvent || 'ydwg02-out';
this.device = undefined;
this.devices = {};
this.supportsDeviceCreation = true;
this.fromPgn = new fromPgn_1.Parser(options);
this.fromPgn.on('warning', (_pgn, _warning) => {
//debug(`[warning] ${pgn.pgn} ${warning}`)
});
this.fromPgn.on('error', (pgn, error) => {
this.debug(`[error] ${pgn.pgn} ${error}`);
});
if (options.app) {
const outEvents = (this.options.outEvent || 'nmea2000out')
.split(',')
.map((event) => event.trim());
outEvents.forEach((event) => {
options.app.on(event, (msg) => {
if (typeof msg === 'string') {
this.sendYdgwPGN(msg);
}
else {
this.sendPGN(msg);
}
options.app.emit('connectionwrite', { providerId: options.providerId });
});
});
const jsonOutEvents = (options.jsonOutEvent || 'nmea2000JsonOut')
.split(',')
.map((event) => event.trim());
jsonOutEvents.forEach((event) => {
options.app.on(event, (msg) => {
this.sendPGN(msg);
options.app.emit('connectionwrite', { providerId: options.providerId });
});
});
options.app.on('ydFullRawOut', (msgs) => {
this.sendYdgwFullPGN(msgs);
options.app.emit('connectionwrite', { providerId: options.providerId });
});
//this.sendString('$PDGY,N2NET_OFFLINE')
if (type === 'usb') {
// set ydnu to RAW mode
options.app.emit(this.outEvent, Buffer.from([0x30, 0x0a]));
}
this.debug('started');
}
}
Ydgw02Stream.prototype.cansend = function (_msg) {
return this.options.createDevice
? this.device && this.device.cansend
: this.sentAvailable;
};
Ydgw02Stream.prototype.sendString = function (msg, forceSend) {
if (this.cansend() || forceSend === true) {
this.debugOut('sending %s', msg);
if (this.options.app.listenerCount('canboatjs:rawsend') > 0) {
this.options.app.emit('canboatjs:rawsend', {
knownSrc: this.device !== undefined,
data: msg
});
}
this.options.app.emit(this.outEvent, msg);
}
};
Ydgw02Stream.prototype.sendPGN = function (pgn, force) {
if (this.cansend() || pgn.forceSend === true || force === true) {
//let now = Date.now()
//let lastSent = pgnsSent[pgn.pgn]
let msgs;
if (pgn.ydFullFormat === true || this.device !== undefined) {
// Only stamp src from this.device when the stream actually owns
// a device. ydFullFormat senders (e.g. YdDevice.sendPGN for
// Heartbeat / ISO Request responses) already set pgn.src to the
// sender's claimed address, so leaving it intact is correct.
// Without this guard, every heartbeat from a YdDevice attached
// via app event-bus to a Ydgw02Stream that hasn't yet received
// an inbound packet (so this.device is still undefined) throws
// 'Cannot read properties of undefined (reading address)'.
if (pgn.src !== 254 && this.device !== undefined) {
pgn.src = this.device.address;
}
msgs = (0, toPgn_1.pgnToYdgwFullRawFormat)(pgn);
}
else {
msgs = (0, toPgn_1.pgnToYdgwRawFormat)(pgn);
}
msgs.forEach((raw) => {
this.sendString(raw + '\r\n', pgn.forceSend);
});
if (this.device !== undefined) {
if (pgn.pgn === 126996 || pgn.pgn === 126998 || pgn.pgn === 60928) {
// forward on so these are seen by the server
this.push({ ...pgn, timestamp: new Date().toISOString() });
}
if (pgn.pgn == 59904 &&
pgn.src !== 254 &&
(pgn.dst == 255 || pgn.dst == this.address)) {
if (pgn.PGN !== undefined) {
pgn.fields = { pgn: pgn.PGN, ...pgn.fields };
}
this.device.n2kMessage(pgn);
}
}
//pgnsSent[pgn.pgn] = now
}
};
Ydgw02Stream.prototype.sendYdgwFullPGN = function (msgs) {
msgs.forEach((raw) => {
this.sendString(raw + '\r\n');
});
};
Ydgw02Stream.prototype.sendYdgwPGN = function (msg) {
let msgs;
if (this.device != undefined) {
msgs = (0, toPgn_1.actisenseToYdgwFullRawFormat)(msg);
}
else {
msgs = (0, toPgn_1.actisenseToYdgwRawFormat)(msg);
}
msgs.forEach((raw) => {
this.sendString(raw + '\r\n');
});
};
util_1.default.inherits(Ydgw02Stream, stream_1.Transform);
Ydgw02Stream.prototype._transform = function (chunk, encoding, done) {
const line = chunk.toString().trim();
if (line.length < 23) {
//bad data
done();
return;
}
if (this.sentAvailable === false &&
(this.options.createDevice === false ||
(this.device && this.device.cansend))) {
this.sentAvailable = true;
if (this.options.createDevice === false) {
this.debug('emit nmea2000OutAvailable');
this.options.app.emit('nmea2000OutAvailable');
}
if (this.options.app.emitPropertyValue) {
this.options.app.emitPropertyValue('canboatjsUtils', {
id: this.options.id,
utils: this
});
}
}
if (this.options.createDevice === true) {
if (this.device === undefined) {
this.device = new yddevice_1.YdDevice(this, this.options);
this.device.start();
}
if (this.cansend() && line.charAt(13) === 'T') {
//11:54:07.833 R 18eafffe 00 ee 00
const canId = (0, canId_1.parseCanIdStr)(line.slice(15, 23));
if (canId.src === this.device.address) {
//ignore pgn we're transmitting
done();
return;
}
}
}
else {
if (line.charAt(13) === 'T') {
//ignore pgns we're transmitting
done();
return;
}
}
const pgn = this.fromPgn.parseYDGW02(line);
this.options.app.emit('canboatjs:rawoutput', line);
if (pgn !== undefined) {
this.push(pgn);
this.options.app.emit(this.options.analyzerOutEvent || 'N2KAnalyzerOut', pgn);
Object.values(this.devices).forEach((device) => {
const yd = device;
yd.pgnReceived(pgn);
});
}
done();
};
Ydgw02Stream.prototype.end = function () { };
Ydgw02Stream.prototype.createEmulator = function (id, options, addressClaim, productInfo, configInfo) {
const device = new YDDeviceEmulator(this, id, options, addressClaim, productInfo, configInfo);
this.devices[id] = device;
return device;
};
Ydgw02Stream.prototype.removeEmulator = function (id) {
const device = this.devices[id];
if (device) {
device.stop();
delete this.devices[id];
}
};
class YDDeviceEmulator extends stream_1.EventEmitter {
stream;
device;
id;
config;
constructor(stream, id, options, addressClaim, productInfo, configInfo) {
super();
this.stream = stream;
this.id = id;
this.config = { configPath: stream.options.app?.config?.configPath };
this.device = new yddevice_1.YdDevice(this, {
app: this,
providerId: 'emulator-' + id,
addressClaim,
productInfo,
configurationInfo: configInfo
});
this.device.start();
}
stop() {
this.device.stop();
this.removeAllListeners();
}
pgnReceived(pgn) {
this.emit('N2KAnalyzerOut', pgn);
}
sendPGN(pgn, force) {
if (force || this.device.cansend) {
if (pgn.src !== 254) {
pgn.src = this.device.address;
}
const msgs = (0, toPgn_1.pgnToYdgwFullRawFormat)(pgn);
msgs.forEach((raw) => {
this.stream.sendString(raw + '\r\n', pgn.forceSend);
});
}
}
send(pgn) {
if (typeof pgn === 'string') {
const src = this.device.address;
const split = pgn.split(',');
split[3] = src.toString();
pgn = split.join(',');
const msgs = (0, toPgn_1.actisenseToYdgwFullRawFormat)(pgn);
msgs.forEach((raw) => {
this.stream.sendString(raw + '\r\n');
});
}
else {
this.sendPGN(pgn, false);
}
}
onPGN(cb) {
this.on('N2KAnalyzerOut', cb);
}
setProviderError(id, error) {
console.error(`${id}:${this.id} ${error}`);
}
setProviderStatus(id, status) {
console.log(`${id}:${this.id} ${status}`);
}
}
//# sourceMappingURL=ydgw02.js.map