src/parser.js
- import {crc32} from 'crc'
- import {set as objPathSet} from 'object-path'
-
- /**
- * Parser creation helper class. Any subclasses should reimplement the parse method,
- * calling super.parse in the beginning. You have useful methods for reading regular C
- * datatypes from the buffer: readInt, readFloat, readChar... To use it in node-serialport
- * or the serial wrapper just pass in ::Parser.parse
- * Keep in mind not initialize this class as is. RE-IMPLEMENT Parser.parse!
- */
- export default class Parser {
- /**
- * @param {String} [endianess='LE'] Set byte-order to big endian (BE) or little endian (LE).
- */
- constructor(endianess = 'LE') {
- /**
- * Current packet being parsed.
- * @type {!Object}
- * @protected
- */
- this._raw = undefined
-
- /**
- * Current position in the packet being parsed.
- * @type {Number}
- * @protected
- */
- this._i = 0
-
- /**
- * Parsed output container.
- * @type {Object}
- * @property {String} crc.sent The CRC32 checkum sent along with the packet.
- * @property {String} crc.local The CRC32 checksum calculated from the sent data.
- */
- this.packet = Object.create(null)
-
- /**
- * Raw packet byte endianess (Little Endian (LE) for the Arduino).
- * Acceptable values: LE, BE.
- * @type {String}
- */
- this.endianess = endianess
- }
-
- /**
- * Parses a packet. Default implementation resets state and calculates and extracts the CRC32
- * checksum before parsing the new packet.
- * That's why, if you want any parsing at all to be performed, YOU MUST RE-IMPLEMENT THIS METHOD.
- * Preferrably calling this implementation before any parsing(but after any decoding
- * *wink* *wink* quasi-binary decoder).
- * @param {Buffer} rawPacket The packet to be parsed.
- * @return {Object} The parsed object.
- */
- parse(rawPacket) {
- // Change the current packet
- this._raw = rawPacket
-
- // Reset state thingies
- this._i = 0
- this.packet = Object.create(null)
-
- // Calculate CRC
- if (this._raw.length > 4) {
- this.setValue('crc.sent', rawPacket[`readUInt${this.endianess}`](rawPacket.length - 4, 4).toString(16))
- this.setValue('crc.local', crc32(rawPacket.slice(0, rawPacket.length - 4)).toString(16))
- }
-
- // More parsing reserved to subclass
- return this.packet // Just to behave as the Docs say it will
- }
-
- /**
- * The value setter called by read* helper functions.
- * Uses object path dot notation in the key and can convert values in one line.
- * @param {String} key The object path where the value will be inserted.
- * @param {mixed} val Anything you want to put.
- * @param {Function} [converter=identity] A function that converts the value.
- * @return {mixed} The provided value (val).
- */
- setValue(key, val, converter = v => v) {
- // Set the value in this.packet.[key]
- objPathSet(this.packet, key, converter(val))
- return val
- }
-
- /**
- * Reads a signed integer of 1-6 bytes.
- * @param {String} key The dot notation path where the int goes to.
- * @param {Number} [size=1] The size in bytes of the integer (1 to 6 bytes only).
- * @param {Function} [converter=identity] A function that converts the value.
- * @return {Number} The read integer.
- */
- readInt(key, size = 1, converter) {
- // Get the value and update the index
- const val = this._raw[`readInt${this.endianess}`](this._i, size)
- this._i += size
-
- return this.setValue(key, val, converter)
- }
-
- /**
- * Reads an unsigned integer of 1-6 bytes.
- * @param {String} key The dot notation path where the int goes to.
- * @param {Number} [size=1] The size in bytes of the integer (1 to 6 bytes only).
- * @param {Function} [converter=identity] A function that converts the value.
- * @return {Number} The read integer.
- */
- readUInt(key, size = 1, converter) {
- // Get the value and update the index
- const val = this._raw[`readUInt${this.endianess}`](this._i, size)
- this._i += size
-
- return this.setValue(key, val, converter)
- }
-
- /**
- * Reads a 32-bit float.
- * @param {String} key The dot notation path where the float goes to.
- * @param {Function} [converter=identity] A function that converts the value.
- * @return {Number} The read float.
- */
- readFloat(key, converter) {
- // Get the value and update the index
- const val = this._raw[`readFloat${this.endianess}`](this._i)
- this._i += 4
-
- return this.setValue(key, val, converter)
- }
-
- /**
- * Reads a char.
- * @param {String} key The dot notation path where the char goes to.
- * @param {Function} [converter=identity] A function that converts the value.
- * @return {String} The read char.
- */
- readChar(key, converter) {
- return this.setValue(key, String.fromCharCode(this._raw[this._i++]), converter)
- }
-
- /**
- * Reads a boolean.
- * @param {String} key The dot notation path where the boolean goes to.
- * @param {Function} [converter=identity] A function that converts the value.
- * @return {Boolean} The read boolean.
- */
- readBoolean(key, converter) {
- return this.setValue(key, Boolean(this._raw[this._i++]), converter)
- }
- }