@canboat/canboatjs
Version:
Native javascript version of canboat
376 lines • 15.8 kB
JavaScript
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseN2KOver0183 = exports.isN2KOver0183 = exports.isN2KString = exports.parseN2kString = exports.parseCandump3 = exports.isCandump3 = exports.parseCandump2 = exports.isCandump2 = exports.parseCandump1 = exports.isCandump1 = exports.parsePDGYdebug = exports.isPDGYdebug = exports.encodePDGY = exports.parsePDGY = exports.isPDGY = exports.encodeMXPGN = exports.parseMXPGN = exports.isMXPGN = exports.encodePCDIN = exports.parsePCDIN = exports.isPCDIN = exports.encodeYDRAWFull = exports.encodeYDRAW = exports.parseYDRAWOut = exports.isYDRAWOut = exports.parseYDRAW = exports.isYDRAW = exports.encodeActisenseN2KACSII = exports.parseActisenseN2KASCII = exports.isActisenseN2KASCII = exports.toActisenseSerialFormat = exports.encodeActisense = exports.parseActisense = exports.isActisense = void 0;
const fp_1 = require("lodash/fp");
const utilities_1 = require("./utilities");
const canId_1 = require("./canId");
const moment_1 = __importDefault(require("moment"));
/**
* Helper function that helps merge canId fields with format, data, and others.
* The idea here is to reflect what was in the source and not remove or add.
* If the source has a len or timestamp attribute it should be added but not created.
* @param {Object} canIdInfo The result of parseCanId, parseCanIdStr, or buildCanId.
* @param {string} format String that defines the source format.
* @param {Buffer} data Buffer array that contains the fields data.
* @param {Object} [rest={}] Anything else to be added like len, timestamp, direction.
* @return {Object} All canId fields with format and data props added.
*/
function buildMsg(_canIdInfo, format, data, rest = {}) {
const canIdInfo = Object.assign({}, _canIdInfo, {
format,
data
});
for (const property in rest) {
if (canIdInfo[property] === undefined) {
canIdInfo[property] = rest[property];
}
}
return canIdInfo;
}
function buildErrMsg(msg, input) {
if (input !== undefined && input.length > 0)
return `${msg} - ${input}`;
return msg;
}
const buildErr = (format, msg, input = undefined) => {
return {
error: new Error(buildErrMsg(msg, input)),
format,
input
};
};
function toPaddedHexString(num, len) {
const str = num.toString(16).toUpperCase();
return '0'.repeat(len - str.length) + str;
}
// 2016-02-28T19:57:02.364Z,2,127250,7,255,8,ff,10,3b,ff,7f,ce,f5,fc
const isActisense = (input) => (input.charAt(10) === 'T' && input.charAt(23) === 'Z') ||
(input.charAt(10) === '-' && input.charAt(23) === ',');
exports.isActisense = isActisense;
const parseActisense = (input) => {
const [timestamp, prio, pgn, src, dst, len, ...data] = input.split(',');
return buildMsg((0, canId_1.buildCanId)(prio, pgn, dst, src), 'Actisense', Buffer.from(data.join(''), 'hex'), { len: Number(len), timestamp });
};
exports.parseActisense = parseActisense;
const encodeActisense = ({ pgn, data, timestamp, prio = 2, dst = 255, src = 0 }) => [
timestamp || new Date().toISOString(),
prio,
pgn,
src,
dst,
data.length,
(0, utilities_1.byteString)(data)
].join(',');
exports.encodeActisense = encodeActisense;
const toActisenseSerialFormat = (pgn, data, dst = 255, src = 0, prio = 2) => exports.encodeActisense({
pgn,
data,
dst,
src,
prio
});
exports.toActisenseSerialFormat = toActisenseSerialFormat;
// A764027.880 05FF7 1EF00 E59861060202FFFFFFFF03030000FFFFFFFFFFFFFFFFFFFF0000FFFFFF7FFFFFFF7FFFFFFF7F0000FF7F
const isActisenseN2KASCII = (input) => input.charAt(0) === 'A' && input.charAt(7) === '.' && input.charAt(11) === ' ';
exports.isActisenseN2KASCII = isActisenseN2KASCII;
const parseActisenseN2KASCII = (input) => {
const [timestamp, srcdstp, pgn, data] = input.split(' ');
const src = parseInt(srcdstp.substring(0, 2), 16);
const dst = parseInt(srcdstp.substring(2, 4), 16);
const prio = parseInt(srcdstp.substring(4));
return buildMsg((0, canId_1.buildCanId)(prio, parseInt(pgn, 16), dst, src), 'Actisense N2K ASCII', Buffer.from(data, 'hex'), { len: data.length, time: timestamp.substring(1) });
};
exports.parseActisenseN2KASCII = parseActisenseN2KASCII;
const encodeActisenseN2KACSII = ({ pgn, data, timestamp, prio = 2, dst = 255, src = 0 }) => {
timestamp = 'A000000.000';
const srcdstp = (0, utilities_1.hexByte)(src) + (0, utilities_1.hexByte)(dst) + prio;
return [
timestamp,
srcdstp.toUpperCase(),
toPaddedHexString(pgn, 5).toUpperCase(),
(0, utilities_1.byteString)(data, '').toUpperCase()
].join(' ');
};
exports.encodeActisenseN2KACSII = encodeActisenseN2KACSII;
// 16:29:27.082 R 09F8017F 50 C3 B8 13 47 D8 2B C6
const isYDRAW = (input) => {
if (input.charAt(2) !== ':')
return false;
const direction = input.substr(12, 3);
return direction === ' R ' || direction === ' T ';
};
exports.isYDRAW = isYDRAW;
const parseYDRAW = (input) => {
const parts = input.split(' ');
if (parts.length < 4)
return buildErr('YDRAW', 'Invalid parts.', input);
const [time, direction, canId, ...data] = parts; // time format HH:mm:ss.SSS
return buildMsg((0, canId_1.parseCanIdStr)(canId), 'YDRAW', (0, utilities_1.arrBuff)(data), {
direction,
time
});
};
exports.parseYDRAW = parseYDRAW;
const isYDRAWOut = (input) => {
if (input.charAt(8) !== ' ')
return false;
return true;
};
exports.isYDRAWOut = isYDRAWOut;
const parseYDRAWOut = (input) => {
const parts = input.split(' ');
if (parts.length < 4)
return buildErr('YDRAW', 'Invalid parts.', input);
const [canId, ...data] = parts; // time format HH:mm:ss.SSS
return buildMsg((0, canId_1.parseCanIdStr)(canId), 'YDRAW', (0, utilities_1.arrBuff)(data));
};
exports.parseYDRAWOut = parseYDRAWOut;
//19F51323 01 02<CR><LF>
const encodeYDRAW = ({ data, ...canIdInfo }) => {
const canId = (0, canId_1.encodeCanIdString)(canIdInfo);
const pgns = data.length > 8 || canIdInfo.pgn == 126720 ? (0, utilities_1.getPlainPGNs)(data) : [data];
return pgns.map((buffer) => canId + ' ' + (0, utilities_1.byteString)(buffer, ' '));
};
exports.encodeYDRAW = encodeYDRAW;
//16:29:27.082 R 19F51323 01 02<CR><LF>
const encodeYDRAWFull = ({ data, ...canIdInfo }) => {
const canId = (0, canId_1.encodeCanIdString)(canIdInfo);
const pgns = data.length > 8 || canIdInfo.pgn == 126720 ? (0, utilities_1.getPlainPGNs)(data) : [data];
return pgns.map((buffer) => (0, moment_1.default)().utc().format('hh:mm:ss.SSS') +
' R ' +
canId +
' ' +
(0, utilities_1.byteString)(buffer, ' '));
};
exports.encodeYDRAWFull = encodeYDRAWFull;
const get0183Sentence = (msg) => {
let sentence = msg;
if (sentence.charAt(0) === '\\') {
const split = sentence.split('\\');
if (split.length < 3) {
return undefined;
}
sentence = split[2];
}
return sentence;
};
// $PCDIN,01F119,00000000,0F,2AAF00D1067414FF*59
const isPCDIN = (msg) => {
const sentence = get0183Sentence(msg);
return sentence ? sentence.startsWith('$PCDIN,') : false;
};
exports.isPCDIN = isPCDIN;
const parsePCDIN = (input) => {
const sentence = get0183Sentence(input);
if (sentence) {
const [prefix, pgn, timeHex, src, data] = sentence.split(',');
let timer = parseInt(timeHex, 32);
timer = timer / 1024;
timer = timer + 1262304000; // starts epoch time from 1/1/2010
timer = timer * 1000;
return buildMsg((0, canId_1.buildCanId)(0, parseInt(pgn, 16), 255, parseInt(src, 16)), 'PCDIN', Buffer.from((0, utilities_1.rmChecksum)(data), 'hex'), { coalesced: true, prefix, timer, timestamp: new Date(timer) });
}
};
exports.parsePCDIN = parsePCDIN;
const encodePCDIN = ({ prefix = '$PCDIN', pgn, data, dst = 255 }) => {
const sentence = [
prefix,
toPaddedHexString(pgn, 6),
'0000180C',
(0, utilities_1.hexByte)(dst).toUpperCase(),
(0, utilities_1.byteString)(data, '').toUpperCase()
].join(',');
return sentence + (0, utilities_1.compute0183Checksum)(sentence);
};
exports.encodePCDIN = encodePCDIN;
const changeEndianness = (string) => {
const result = [];
let len = string.length - 2;
while (len >= 0) {
result.push(string.substr(len, 2));
len -= 2;
}
return result.join('');
};
// $MXPGN,01F801,2801,C1308AC40C5DE343*19
const isMXPGN = (msg) => {
const sentence = get0183Sentence(msg);
return sentence ? sentence.startsWith('$MXPGN,') : false;
};
exports.isMXPGN = isMXPGN;
const parseMXPGN = (input, options = undefined) => {
const sentence = get0183Sentence(input);
if (sentence) {
const [prefix, pgn, attr_word, data] = sentence.split(',');
const send_prio_len = parseInt(attr_word.substr(0, 2), 16)
.toString(2)
.padStart(8, '0');
const addr = parseInt(attr_word.substr(2, 2), 16);
const send = parseInt(send_prio_len.substr(0, 1), 2);
const prio = parseInt(send_prio_len.substr(1, 3), 2);
//const len = parseInt(send_prio_len.substr(4,4), 2);
let src = 0, dst = 255;
send ? (dst = addr) : (src = addr);
let reversed;
if (options && options.littleEndianMXPGN)
reversed = changeEndianness((0, utilities_1.rmChecksum)(data));
else
reversed = data;
return buildMsg((0, canId_1.buildCanId)(prio, parseInt(pgn, 16), dst, src), 'MXPGN', Buffer.from(reversed, 'hex'), { coalesced: true, prefix });
}
};
exports.parseMXPGN = parseMXPGN;
const encodeMXPGN = ({ prefix = '$MXPGN', pgn, prio, src, data }) => {
if (src > 255)
src = 255;
if (!prio)
prio = 3;
if (!src)
src = 255;
const dataLength = (0, utilities_1.hexByte)(128 + prio * 16 + (0, utilities_1.byteString)(data, '').toUpperCase().length / 2).toUpperCase();
const attribWord = dataLength + (0, utilities_1.hexByte)(src).toUpperCase();
const buff = Buffer.from((0, utilities_1.byteString)(data, ''), 'hex');
for (let i = 0, j = buff.length - 1; i < j; ++i, --j) {
const t = buff[j];
buff[j] = buff[i];
buff[i] = t;
}
const sentence = [
prefix,
toPaddedHexString(pgn, 6),
attribWord,
buff.toString('hex').toUpperCase()
].join(',');
return sentence + (0, utilities_1.compute0183Checksum)(sentence);
};
exports.encodeMXPGN = encodeMXPGN;
// iKonvert
// !PDGY,126992,3,2,255,0.563,d2009e45b3b8821d
exports.isPDGY = (0, fp_1.startsWith)('!PDGY,');
const parsePDGY = (input) => {
const parts = input.split(',');
if (parts.length === 7) {
const [prefix, pgn, prio, src, dst, timer, data] = parts;
return buildMsg((0, canId_1.buildCanId)(prio, pgn, dst, src), 'PDGY', Buffer.from(data, 'base64'), { timer: Number(timer), prefix, coalesced: true });
}
else if (parts.length === 4) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [prefix, pgn, dst, data] = parts;
return buildMsg((0, canId_1.buildCanId)(0, pgn, dst, 0), 'PDGY', Buffer.from(data, 'base64'), { coalesced: true });
}
else {
return buildErr('iKonvert', 'Invalid parts.', input);
}
};
exports.parsePDGY = parsePDGY;
const encodePDGY = ({ prefix = '!PDGY', pgn, data, dst = 255 }) => [prefix, pgn, dst, data.toString('base64')].join(',');
exports.encodePDGY = encodePDGY;
exports.isPDGYdebug = (0, fp_1.startsWith)('$PDGY,');
const parsePDGYdebug = (input) => {
const [prefix, pgn, ...fieldParts] = input.split(',');
const fieldVals = fieldParts.map(fp_1.toNumber);
const fields = (0, fp_1.zipObject)(['busLoad', 'errors', 'deviceCount', 'timer', 'gatewaySrc', 'rejectedTX'], fieldVals);
const src = fields.gatewaySrc;
return buildMsg((0, canId_1.buildCanId)(3, pgn, src, src), 'PDGYdebug', Buffer.from(fieldVals), { fields, prefix });
};
exports.parsePDGYdebug = parsePDGYdebug;
// candump1 Angstrom
// <0x18eeff01> [8] 05 a0 be 1c 00 a0 a0 c0
exports.isCandump1 = (0, fp_1.startsWith)('<0x');
const parseCandump1 = (input) => {
const [canId, len, ...data] = input.split(' ');
return buildMsg((0, canId_1.parseCanIdStr)((0, utilities_1.trimWrap)(canId)), 'candump1', (0, utilities_1.arrBuff)(data), {
len: Number((0, utilities_1.trimWrap)(len))
});
};
exports.parseCandump1 = parseCandump1;
// candump2 Debian
// can0 09F8027F [8] 00 FC FF FF 00 00 FF FF
exports.isCandump2 = (0, fp_1.startsWith)('can');
const parseCandump2 = (input) => {
const [bus, canId, len, ...data] = (0, fp_1.compact)(input.split(' '));
return buildMsg((0, canId_1.parseCanIdStr)(canId), 'candump2', (0, utilities_1.arrBuff)(data), {
bus,
len: Number((0, utilities_1.trimWrap)(len))
});
};
exports.parseCandump2 = parseCandump2;
// candump3 log
// (1502979132.106111) slcan0 09F50374#000A00FFFF00FFFF
exports.isCandump3 = (0, fp_1.startsWith)('(');
const parseCandump3 = (input) => {
const [timestamp, bus, canFrame] = input.split(' ');
const [canId, data] = canFrame.split('#');
return buildMsg((0, canId_1.parseCanIdStr)(canId), 'candump3', Buffer.from(data, 'hex'), {
timestamp,
bus
});
};
exports.parseCandump3 = parseCandump3;
const hasErr = (0, fp_1.overSome)([(0, fp_1.negate)(fp_1.isString), fp_1.isEmpty]);
const parseN2kString = (str, options) => {
if (hasErr(str)) {
return buildErr('INVALID', 'Input not string or empty.', str);
}
if ((0, exports.isActisense)(str)) {
return (0, exports.parseActisense)(str);
}
if ((0, exports.isYDRAW)(str)) {
return (0, exports.parseYDRAW)(str);
}
if ((0, exports.isYDRAWOut)(str)) {
return (0, exports.parseYDRAWOut)(str);
}
if ((0, exports.isPCDIN)(str)) {
return (0, exports.parsePCDIN)(str);
}
if ((0, exports.isMXPGN)(str)) {
return (0, exports.parseMXPGN)(str, options);
}
if ((0, exports.isPDGY)(str)) {
return (0, exports.parsePDGY)(str);
}
if ((0, exports.isCandump1)(str)) {
return (0, exports.parseCandump1)(str);
}
if ((0, exports.isCandump2)(str)) {
return (0, exports.parseCandump2)(str);
}
if ((0, exports.isCandump3)(str)) {
return (0, exports.parseCandump3)(str);
}
if ((0, exports.isPDGYdebug)(str)) {
return (0, exports.parsePDGYdebug)(str);
}
if ((0, exports.isActisenseN2KASCII)(str)) {
return (0, exports.parseActisenseN2KASCII)(str);
}
return buildErr('MISSING_PARSER', 'Parser not found for input.', str);
};
exports.parseN2kString = parseN2kString;
exports.isN2KString = (0, fp_1.cond)([
[hasErr, () => false],
[exports.isActisense, () => true],
[exports.isYDRAW, () => true],
[exports.isPCDIN, () => true],
[exports.isMXPGN, () => true],
[exports.isPDGY, () => true],
[exports.isCandump1, () => true],
[exports.isCandump2, () => true],
[exports.isCandump3, () => true],
[exports.isPDGYdebug, () => true],
[exports.isActisenseN2KASCII, () => true],
[fp_1.stubTrue, () => false]
]);
const isN2KOver0183 = (msg) => {
return (0, exports.isPCDIN)(msg) || (0, exports.isMXPGN)(msg);
};
exports.isN2KOver0183 = isN2KOver0183;
const parseN2KOver0183 = (msg) => {
return (0, exports.parsePCDIN)(msg) || (0, exports.parseMXPGN)(msg);
};
exports.parseN2KOver0183 = parseN2KOver0183;
//# sourceMappingURL=stringMsg.js.map
;