UNPKG

sphero

Version:
375 lines (299 loc) 9.19 kB
"use strict"; var inherits = require("util").inherits, EventEmitter = require("events").EventEmitter; var utils = require("./utils"), RES_PARSER = require("./parsers/response.js"), ASYNC_PARSER = require("./parsers/async.js"); var MIN_BUFFER_SIZE = 6, FIELDS = { size: 5, sop1: { pos: 0, hex: 0xFF }, sop2: { pos: 1, sync: 0xFF, async: 0xFE, }, mrspHex: 0x00, seqHex: 0x00, mrspIdCode: 2, seqMsb: 3, dlenLsb: 4, checksum: 5, didHex: 0x00, cidHex: 0x01 }; var Packet = module.exports = function(opts) { this.partialBuffer = new Buffer(0); this.partialCounter = 0; opts = opts || {}; this.emitPacketErrors = opts.emitPacketErrors || false; }; inherits(Packet, EventEmitter); Packet.prototype.create = function(opts) { opts = opts || {}; var sop1 = (opts.sop1 === undefined) ? FIELDS.sop1.hex : opts.sop1, sop2 = (opts.sop2 === undefined) ? FIELDS.sop2.sync : opts.sop2, did = (opts.did === undefined) ? FIELDS.didHex : opts.did, cid = (opts.cid === undefined) ? FIELDS.cidHex : opts.cid, seq = (opts.seq === undefined) ? FIELDS.seqHex : opts.seq, data = (!opts.data) ? [] : opts.data, // Add 1 to dlen, since it also counts the checksum byte dlen = data.length + 1, checksum = 0x00; this.emitPacketErrors = opts.emitPacketErrors || false; // Create array with packet bytes var packet = [ sop1, sop2, did, cid, seq, dlen ].concat(data); // Get checksum for final byte in packet checksum = utils.checksum(packet.slice(2)); // Add checksum to packet packet.push(checksum); return packet; }; Packet.prototype.parse = function(buffer) { if (this.partialBuffer.length > 0) { buffer = Buffer.concat( [this.partialBuffer, buffer], buffer.length + this.partialBuffer.length ); this.partialBuffer = new Buffer(0); } else { this.partialBuffer = new Buffer(buffer); } if (this._checkSOPs(buffer)) { // Check the packet is at least 6 bytes long if (this._checkMinSize(buffer)) { // Check the buffer length matches the // DLEN value specified in the buffer if (this._checkExpectedSize(buffer) > -1) { // If the packet looks good parse it return this._parse(buffer); } } this.partialBuffer = new Buffer(buffer); } return null; }; Packet.prototype._parse = function(buffer) { var packet = {}; packet.sop1 = buffer[FIELDS.sop1.pos]; packet.sop2 = buffer[FIELDS.sop2.pos]; var bByte2 = buffer[FIELDS.mrspIdCode], bByte3 = buffer[FIELDS.seqMsb], bByte4 = buffer[FIELDS.dlenLsb]; if (FIELDS.sop2.sync === buffer[FIELDS.sop2.pos]) { packet.mrsp = bByte2; packet.seq = bByte3; packet.dlen = bByte4; } else { packet.idCode = bByte2; packet.dlenMsb = bByte3; packet.dlenLsb = bByte4; } packet.dlen = this._extractDlen(buffer); // Create new Buffer for data that is dlen -1 (minus checksum) in size packet.data = new Buffer(packet.dlen - 1); // Copy data from buffer into packet.data buffer.copy(packet.data, 0, FIELDS.size, FIELDS.size + packet.dlen - 1); packet.checksum = buffer[FIELDS.size + packet.dlen - 1]; this._dealWithExtraBytes(buffer); return this._verifyChecksum(buffer, packet); }; Packet.prototype._dealWithExtraBytes = function(buffer) { // If the packet was parsed successfully, and the buffer and // expected size of the buffer are the same,clean up the // partialBuffer, otherwise assign extrabytes to partialBuffer var expectedSize = this._checkExpectedSize(buffer); if (buffer.length > expectedSize) { this.partialBuffer = new Buffer(buffer.length - expectedSize); buffer.copy(this.partialBuffer, 0, expectedSize); } else { this.partialBuffer = new Buffer(0); } }; Packet.prototype._verifyChecksum = function(buffer, packet) { var bSlice = buffer.slice( FIELDS.mrspIdCode, FIELDS.checksum + packet.dlen - 1 ), checksum = utils.checksum(bSlice); // If we got an incorrect checksum we cleanup the packet, // partialBuffer, return null and emit an error event if (checksum !== packet.checksum) { packet = null; this.partialBuffer = new Buffer(0); if (this.emitPacketErrors) { this.emit("error", new Error("Incorrect checksum, packet discarded!")); } } return packet; }; Packet.prototype.parseAsyncData = function(payload, ds) { var parser = ASYNC_PARSER[payload.idCode]; return this._parseData(parser, payload, ds); }; Packet.prototype.parseResponseData = function(cmd, payload) { if (!cmd || cmd.did === undefined || cmd.cid === undefined) { return payload; } var parserId = cmd.did.toString(16) + ":" + cmd.cid.toString(16), parser = RES_PARSER[parserId]; return this._parseData(parser, payload); }; Packet.prototype._parseData = function(parser, payload, ds) { var data = payload.data, pData, fields, field; if (parser && (data.length > 0)) { ds = this._checkDSMasks(ds, parser); if (ds === -1) { return payload; } fields = parser.fields; pData = { desc: parser.desc, idCode: parser.idCode, event: parser.event, did: parser.did, cid: parser.cid, packet: payload }; var dsIndex = 0, dsFlag = 0, i = 0; while (i < fields.length) { field = fields[i]; dsFlag = this._checkDSBit(ds, field); if (dsFlag === 1) { field.from = dsIndex; field.to = dsIndex = dsIndex + 2; } else if (dsFlag === 0) { i = this._incParserIndex(i, fields, data, dsFlag, dsIndex); continue; } pData[field.name] = this._parseField(field, data, pData); i = this._incParserIndex(i, fields, data, dsFlag, dsIndex); } } else { pData = payload; } return pData; }; Packet.prototype._checkDSMasks = function(ds, parser) { if (parser.idCode === 0x03) { if (!(ds && ds.mask1 != null && ds.mask2 != null)) { return -1; } } else { return null; } return ds; }; Packet.prototype._incParserIndex = function(i, fields, data, dsFlag, dsIndex) { i++; if ((dsFlag >= 0) && (i === fields.length) && (dsIndex < data.length)) { i = 0; } return i; }; Packet.prototype._checkDSBit = function(ds, field) { if (!ds) { return -1; } if (Math.abs(ds[field.maskField] & field.bitmask) > 0) { return 1; } return 0; }; Packet.prototype._parseField = function(field, data, pData) { var pField; var width; data = data.slice(field.from, field.to); pField = utils.bufferToInt(data); switch (field.type) { case "number": if (field.format === "hex") { pField = "0x" + pField.toString(16).toUpperCase(); } break; case "string": pField = data.toString(field.format).replace(/\0/g, "0"); break; case "raw": pField = new Buffer(data); break; case "predefined": if (field.mask != null) { pField &= field.mask; } pField = field.values[pField]; break; case "bitmask": pField = this._parseBitmaskField(pField, field, pData); break; case "signed": width = 8 * (field.to - field.from); if (pField >= Math.pow(2, width - 1)) { pField = pField - Math.pow(2, width); } break; default: this.emit("error", new Error("Data could not be parsed!")); pField = "Data could not be parsed!"; break; } return pField; }; Packet.prototype._parseBitmaskField = function(val, field, pData) { var pField = {}; if (val > field.range.top) { val = utils.twosToInt(val, 2); } if (pData[field.name]) { pField = pData[field.name]; pField.value.push(val); } else { pField = { sensor: field.sensor, range: field.range, units: field.units, value: [val] }; } return pField; }; Packet.prototype._checkSOPs = function(buffer) { return (this._checkSOP1(buffer)) ? this._checkSOP2(buffer) : false; }; Packet.prototype._checkSOP1 = function(buffer) { return (buffer[FIELDS.sop1.pos] === FIELDS.sop1.hex); }; Packet.prototype._checkSOP2 = function(buffer) { var sop2 = buffer[FIELDS.sop2.pos]; if (sop2 === FIELDS.sop2.sync) { return "sync"; } else if (sop2 === FIELDS.sop2.async) { return "async"; } return false; }; Packet.prototype._checkExpectedSize = function(buffer) { // Size = buffer fields size (SOP1, SOP2, MSRP, SEQ and DLEN) + DLEN value var expectedSize = FIELDS.size + this._extractDlen(buffer), bufferSize = buffer.length; return (bufferSize < expectedSize) ? -1 : expectedSize; }; Packet.prototype._checkMinSize = function(buffer) { return (buffer.length >= MIN_BUFFER_SIZE); }; Packet.prototype._extractDlen = function(buffer) { if (buffer[FIELDS.sop2.pos] === FIELDS.sop2.sync) { return buffer[FIELDS.dlenLsb]; } // We shift the dlen MSB 8 bits and then do a binary OR // between the two values to obtain the dlen value return (buffer[FIELDS.seqMsb] << 8) | buffer[FIELDS.dlenLsb]; };