#!/usr/bin/env python ''' parse a MAVLink protocol XML file and generate a Node.js javascript module implementation Based on original work Copyright Andrew Tridgell 2011 Released under GNU GPL version 3 or later ''' from __future__ import print_function from builtins import range import os import textwrap from . import mavtemplate t = mavtemplate.MAVTemplate() def generate_preamble(outf, msgs, args, xml): print("Generating preamble") t.write(outf, """ /* MAVLink protocol implementation for node.js (auto-generated by mavgen_javascript.py) Generated from: ${FILELIST} Note: this file has been auto-generated. DO NOT EDIT */ jspack = require("jspack").jspack, _ = require("underscore"), events = require("events"), util = require("util"); // Add a convenience method to Buffer Buffer.prototype.toByteArray = function () { return Array.prototype.slice.call(this, 0) } mavlink = function(){}; // Implement the X25CRC function (present in the Python version through the mavutil.py package) mavlink.x25Crc = function(buffer, crc) { var bytes = buffer; var crc = crc || 0xffff; _.each(bytes, function(e) { var tmp = e ^ (crc & 0xff); tmp = (tmp ^ (tmp << 4)) & 0xff; crc = (crc >> 8) ^ (tmp << 8) ^ (tmp << 3) ^ (tmp >> 4); crc = crc & 0xffff; }); return crc; } mavlink.WIRE_PROTOCOL_VERSION = "${WIRE_PROTOCOL_VERSION}"; mavlink.MAVLINK_TYPE_CHAR = 0 mavlink.MAVLINK_TYPE_UINT8_T = 1 mavlink.MAVLINK_TYPE_INT8_T = 2 mavlink.MAVLINK_TYPE_UINT16_T = 3 mavlink.MAVLINK_TYPE_INT16_T = 4 mavlink.MAVLINK_TYPE_UINT32_T = 5 mavlink.MAVLINK_TYPE_INT32_T = 6 mavlink.MAVLINK_TYPE_UINT64_T = 7 mavlink.MAVLINK_TYPE_INT64_T = 8 mavlink.MAVLINK_TYPE_FLOAT = 9 mavlink.MAVLINK_TYPE_DOUBLE = 10 // Mavlink headers incorporate sequence, source system (platform) and source component. mavlink.header = function(msgId, mlen, seq, srcSystem, srcComponent) { this.mlen = ( typeof mlen === 'undefined' ) ? 0 : mlen; this.seq = ( typeof seq === 'undefined' ) ? 0 : seq; this.srcSystem = ( typeof srcSystem === 'undefined' ) ? 0 : srcSystem; this.srcComponent = ( typeof srcComponent === 'undefined' ) ? 0 : srcComponent; this.msgId = msgId } mavlink.header.prototype.pack = function() { return jspack.Pack('BBBBBB', [${PROTOCOL_MARKER}, this.mlen, this.seq, this.srcSystem, this.srcComponent, this.msgId]); } // Base class declaration: mavlink.message will be the parent class for each // concrete implementation in mavlink.messages. mavlink.message = function() {}; // Convenience setter to facilitate turning the unpacked array of data into member properties mavlink.message.prototype.set = function(args) { _.each(this.fieldnames, function(e, i) { this[e] = args[i]; }, this); }; // This pack function builds the header and produces a complete MAVLink message, // including header and message CRC. mavlink.message.prototype.pack = function(mav, crc_extra, payload) { this.payload = payload; this.header = new mavlink.header(this.id, payload.length, mav.seq, mav.srcSystem, mav.srcComponent); this.msgbuf = this.header.pack().concat(payload); var crc = mavlink.x25Crc(this.msgbuf.slice(1)); // For now, assume always using crc_extra = True. TODO: check/fix this. crc = mavlink.x25Crc([crc_extra], crc); this.msgbuf = this.msgbuf.concat(jspack.Pack('= 1 && this.buf[0] != 254 ) { // Strip the offending initial byte and throw an error. var badPrefix = this.buf[0]; this.bufInError = this.buf.slice(0,1); this.buf = this.buf.slice(1); this.expected_length = 6; // TODO: enable subsequent prefix error suppression if robust_parsing is implemented //if(!this.have_prefix_error) { // this.have_prefix_error = true; throw new Error("Bad prefix ("+badPrefix+")"); //} } //else if( this.buf.length >= 1 && this.buf[0] == 254 ) { // this.have_prefix_error = false; //} } // Determine the length. Leaves buffer untouched. MAVLink.prototype.parseLength = function() { if( this.buf.length >= 2 ) { var unpacked = jspack.Unpack('BB', this.buf.slice(0, 2)); this.expected_length = unpacked[1] + 8; // length of message + header + CRC } } // input some data bytes, possibly returning a new message MAVLink.prototype.parseChar = function(c) { var m = null; try { this.pushBuffer(c); this.parsePrefix(); this.parseLength(); m = this.parsePayload(); } catch(e) { this.log('error', e.message); this.total_receive_errors += 1; m = new mavlink.messages.bad_data(this.bufInError, e.message); this.bufInError = new Buffer(0); } if(null != m) { this.emit(m.name, m); this.emit('message', m); } return m; } MAVLink.prototype.parsePayload = function() { var m = null; // If we have enough bytes to try and read it, read it. if( this.expected_length >= 8 && this.buf.length >= this.expected_length ) { // Slice off the expected packet length, reset expectation to be to find a header. var mbuf = this.buf.slice(0, this.expected_length); // TODO: slicing off the buffer should depend on the error produced by the decode() function // - if a message we find a well formed message, cut-off the expected_length // - if the message is not well formed (correct prefix by accident), cut-off 1 char only this.buf = this.buf.slice(this.expected_length); this.expected_length = 6; // w.info("Attempting to parse packet, message candidate buffer is ["+mbuf.toByteArray()+"]"); try { m = this.decode(mbuf); this.total_packets_received += 1; } catch(e) { // Set buffer in question and re-throw to generic error handling this.bufInError = mbuf; throw e; } } return m; } // input some data bytes, possibly returning an array of new messages MAVLink.prototype.parseBuffer = function(s) { // Get a message, if one is available in the stream. var m = this.parseChar(s); // No messages available, bail. if ( null === m ) { return null; } // While more valid messages can be read from the existing buffer, add // them to the array of new messages and return them. var ret = [m]; while(true) { m = this.parseChar(); if ( null === m ) { // No more messages left. return ret; } ret.push(m); } return ret; } /* decode a buffer as a MAVLink message */ MAVLink.prototype.decode = function(msgbuf) { var magic, mlen, seq, srcSystem, srcComponent, unpacked, msgId; // decode the header try { unpacked = jspack.Unpack('cBBBBB', msgbuf.slice(0, 6)); magic = unpacked[0]; mlen = unpacked[1]; seq = unpacked[2]; srcSystem = unpacked[3]; srcComponent = unpacked[4]; msgId = unpacked[5]; } catch(e) { throw new Error('Unable to unpack MAVLink header: ' + e.message); } if (magic.charCodeAt(0) != 254) { throw new Error("Invalid MAVLink prefix ("+magic.charCodeAt(0)+")"); } if( mlen != msgbuf.length - 8 ) { throw new Error("Invalid MAVLink message length. Got " + (msgbuf.length - 8) + " expected " + mlen + ", msgId=" + msgId); } if( false === _.has(mavlink.map, msgId) ) { throw new Error("Unknown MAVLink message ID (" + msgId + ")"); } // decode the payload // refs: (fmt, type, order_map, crc_extra) = mavlink.map[msgId] var decoder = mavlink.map[msgId]; // decode the checksum try { var receivedChecksum = jspack.Unpack('