UNPKG

@canboat/canboatjs

Version:

Native javascript version of canboat

376 lines 15.8 kB
"use strict"; 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