#!/usr/bin/env python ''' parse a MAVLink protocol XML file and generate a C implementation Copyright Andrew Tridgell 2011 Released under GNU GPL version 3 or later ''' from __future__ import print_function from future.utils import iteritems from builtins import range from builtins import object import os from . import mavparse, mavtemplate t = mavtemplate.MAVTemplate() def generate_version_h(directory, xml): '''generate version.h''' f = open(os.path.join(directory, "version.h"), mode='w') t.write(f,''' /** @file * @brief MAVLink comm protocol built from ${basename}.xml * @see http://mavlink.org */ #pragma once #ifndef MAVLINK_VERSION_H #define MAVLINK_VERSION_H #define MAVLINK_BUILD_DATE "${parse_time}" #define MAVLINK_WIRE_PROTOCOL_VERSION "${wire_protocol_version}" #define MAVLINK_MAX_DIALECT_PAYLOAD_SIZE ${largest_payload} #endif // MAVLINK_VERSION_H ''', xml) f.close() def generate_mavlink_h(directory, xml): '''generate mavlink.h''' f = open(os.path.join(directory, "mavlink.h"), mode='w') t.write(f,''' /** @file * @brief MAVLink comm protocol built from ${basename}.xml * @see http://mavlink.org */ #pragma once #ifndef MAVLINK_H #define MAVLINK_H #define MAVLINK_PRIMARY_XML_IDX ${xml_idx} #ifndef MAVLINK_STX #define MAVLINK_STX ${protocol_marker} #endif #ifndef MAVLINK_ENDIAN #define MAVLINK_ENDIAN ${mavlink_endian} #endif #ifndef MAVLINK_ALIGNED_FIELDS #define MAVLINK_ALIGNED_FIELDS ${aligned_fields_define} #endif #ifndef MAVLINK_CRC_EXTRA #define MAVLINK_CRC_EXTRA ${crc_extra_define} #endif #ifndef MAVLINK_COMMAND_24BIT #define MAVLINK_COMMAND_24BIT ${command_24bit_define} #endif #include "version.h" #include "${basename}.h" #endif // MAVLINK_H ''', xml) f.close() def generate_main_h(directory, xml): '''generate main header per XML file''' f = open(os.path.join(directory, xml.basename + ".h"), mode='w') t.write(f, ''' /** @file * @brief MAVLink comm protocol generated from ${basename}.xml * @see http://mavlink.org */ #pragma once #ifndef MAVLINK_${basename_upper}_H #define MAVLINK_${basename_upper}_H #ifndef MAVLINK_H #error Wrong include order: MAVLINK_${basename_upper}.H MUST NOT BE DIRECTLY USED. Include mavlink.h from the same directory instead or set ALL AND EVERY defines from MAVLINK.H manually accordingly, including the #define MAVLINK_H call. #endif #undef MAVLINK_THIS_XML_IDX #define MAVLINK_THIS_XML_IDX ${xml_idx} #ifdef __cplusplus extern "C" { #endif // MESSAGE LENGTHS AND CRCS #ifndef MAVLINK_MESSAGE_LENGTHS #define MAVLINK_MESSAGE_LENGTHS {${message_lengths_array}} #endif #ifndef MAVLINK_MESSAGE_CRCS #define MAVLINK_MESSAGE_CRCS {${message_crcs_array}} #endif #include "../protocol.h" #define MAVLINK_ENABLED_${basename_upper} // ENUM DEFINITIONS ${{enum: /** @brief ${description} */ #ifndef HAVE_ENUM_${name} #define HAVE_ENUM_${name} typedef enum ${name} { ${{entry: ${name}=${value}, /* ${description} |${{param:${description}| }} */ }} } ${name}; #endif }} // MAVLINK VERSION #ifndef MAVLINK_VERSION #define MAVLINK_VERSION ${version} #endif #if (MAVLINK_VERSION == 0) #undef MAVLINK_VERSION #define MAVLINK_VERSION ${version} #endif // MESSAGE DEFINITIONS ${{message:#include "./mavlink_msg_${name_lower}.h" }} // base include ${{include_list:#include "../${base}/${base}.h" }} #undef MAVLINK_THIS_XML_IDX #define MAVLINK_THIS_XML_IDX ${xml_idx} #if MAVLINK_THIS_XML_IDX == MAVLINK_PRIMARY_XML_IDX # define MAVLINK_MESSAGE_INFO {${message_info_array}} # define MAVLINK_MESSAGE_NAMES {${message_name_array}} # if MAVLINK_COMMAND_24BIT # include "../mavlink_get_info.h" # endif #endif #ifdef __cplusplus } #endif // __cplusplus #endif // MAVLINK_${basename_upper}_H ''', xml) f.close() def generate_message_h(directory, m): '''generate per-message header for a XML file''' f = open(os.path.join(directory, 'mavlink_msg_%s.h' % m.name_lower), mode='w') t.write(f, ''' #pragma once // MESSAGE ${name} PACKING #define MAVLINK_MSG_ID_${name} ${id} MAVPACKED( typedef struct __mavlink_${name_lower}_t { ${{ordered_fields: ${type} ${name}${array_suffix}; /*< ${units} ${description}*/ }} }) mavlink_${name_lower}_t; #define MAVLINK_MSG_ID_${name}_LEN ${wire_length} #define MAVLINK_MSG_ID_${name}_MIN_LEN ${wire_min_length} #define MAVLINK_MSG_ID_${id}_LEN ${wire_length} #define MAVLINK_MSG_ID_${id}_MIN_LEN ${wire_min_length} #define MAVLINK_MSG_ID_${name}_CRC ${crc_extra} #define MAVLINK_MSG_ID_${id}_CRC ${crc_extra} ${{array_fields:#define MAVLINK_MSG_${msg_name}_FIELD_${name_upper}_LEN ${array_length} }} #if MAVLINK_COMMAND_24BIT #define MAVLINK_MESSAGE_INFO_${name} { \\ ${id}, \\ "${name}", \\ ${num_fields}, \\ { ${{fields: { "${name}", ${c_print_format}, MAVLINK_TYPE_${type_upper}, ${array_length}, ${wire_offset}, offsetof(mavlink_${name_lower}_t, ${name}) }, \\ }} } \\ } #else #define MAVLINK_MESSAGE_INFO_${name} { \\ "${name}", \\ ${num_fields}, \\ { ${{fields: { "${name}", ${c_print_format}, MAVLINK_TYPE_${type_upper}, ${array_length}, ${wire_offset}, offsetof(mavlink_${name_lower}_t, ${name}) }, \\ }} } \\ } #endif /** * @brief Pack a ${name_lower} message * @param system_id ID of this system * @param component_id ID of this component (e.g. 200 for IMU) * @param msg The MAVLink message to compress the data into * ${{arg_fields: * @param ${name} ${units} ${description} }} * @return length of the message in bytes (excluding serial stream start sign) */ static inline uint16_t mavlink_msg_${name_lower}_pack(uint8_t system_id, uint8_t component_id, mavlink_message_t* msg, ${{arg_fields: ${array_const}${type} ${array_prefix}${name},}}) { #if MAVLINK_NEED_BYTE_SWAP || !MAVLINK_ALIGNED_FIELDS char buf[MAVLINK_MSG_ID_${name}_LEN]; ${{scalar_fields: _mav_put_${type}(buf, ${wire_offset}, ${putname}); }} ${{array_fields: _mav_put_${type}_array(buf, ${wire_offset}, ${name}, ${array_length}); }} memcpy(_MAV_PAYLOAD_NON_CONST(msg), buf, MAVLINK_MSG_ID_${name}_LEN); #else mavlink_${name_lower}_t packet; ${{scalar_fields: packet.${name} = ${putname}; }} ${{array_fields: mav_array_memcpy(packet.${name}, ${name}, sizeof(${type})*${array_length}); }} memcpy(_MAV_PAYLOAD_NON_CONST(msg), &packet, MAVLINK_MSG_ID_${name}_LEN); #endif msg->msgid = MAVLINK_MSG_ID_${name}; return mavlink_finalize_message(msg, system_id, component_id, MAVLINK_MSG_ID_${name}_MIN_LEN, MAVLINK_MSG_ID_${name}_LEN, MAVLINK_MSG_ID_${name}_CRC); } /** * @brief Pack a ${name_lower} message on a channel * @param system_id ID of this system * @param component_id ID of this component (e.g. 200 for IMU) * @param chan The MAVLink channel this message will be sent over * @param msg The MAVLink message to compress the data into ${{arg_fields: * @param ${name} ${units} ${description} }} * @return length of the message in bytes (excluding serial stream start sign) */ static inline uint16_t mavlink_msg_${name_lower}_pack_chan(uint8_t system_id, uint8_t component_id, uint8_t chan, mavlink_message_t* msg, ${{arg_fields:${array_const}${type} ${array_prefix}${name},}}) { #if MAVLINK_NEED_BYTE_SWAP || !MAVLINK_ALIGNED_FIELDS char buf[MAVLINK_MSG_ID_${name}_LEN]; ${{scalar_fields: _mav_put_${type}(buf, ${wire_offset}, ${putname}); }} ${{array_fields: _mav_put_${type}_array(buf, ${wire_offset}, ${name}, ${array_length}); }} memcpy(_MAV_PAYLOAD_NON_CONST(msg), buf, MAVLINK_MSG_ID_${name}_LEN); #else mavlink_${name_lower}_t packet; ${{scalar_fields: packet.${name} = ${putname}; }} ${{array_fields: mav_array_memcpy(packet.${name}, ${name}, sizeof(${type})*${array_length}); }} memcpy(_MAV_PAYLOAD_NON_CONST(msg), &packet, MAVLINK_MSG_ID_${name}_LEN); #endif msg->msgid = MAVLINK_MSG_ID_${name}; return mavlink_finalize_message_chan(msg, system_id, component_id, chan, MAVLINK_MSG_ID_${name}_MIN_LEN, MAVLINK_MSG_ID_${name}_LEN, MAVLINK_MSG_ID_${name}_CRC); } /** * @brief Encode a ${name_lower} struct * * @param system_id ID of this system * @param component_id ID of this component (e.g. 200 for IMU) * @param msg The MAVLink message to compress the data into * @param ${name_lower} C-struct to read the message contents from */ static inline uint16_t mavlink_msg_${name_lower}_encode(uint8_t system_id, uint8_t component_id, mavlink_message_t* msg, const mavlink_${name_lower}_t* ${name_lower}) { return mavlink_msg_${name_lower}_pack(system_id, component_id, msg,${{arg_fields: ${name_lower}->${name},}}); } /** * @brief Encode a ${name_lower} struct on a channel * * @param system_id ID of this system * @param component_id ID of this component (e.g. 200 for IMU) * @param chan The MAVLink channel this message will be sent over * @param msg The MAVLink message to compress the data into * @param ${name_lower} C-struct to read the message contents from */ static inline uint16_t mavlink_msg_${name_lower}_encode_chan(uint8_t system_id, uint8_t component_id, uint8_t chan, mavlink_message_t* msg, const mavlink_${name_lower}_t* ${name_lower}) { return mavlink_msg_${name_lower}_pack_chan(system_id, component_id, chan, msg,${{arg_fields: ${name_lower}->${name},}}); } /** * @brief Send a ${name_lower} message * @param chan MAVLink channel to send the message * ${{arg_fields: * @param ${name} ${units} ${description} }} */ #ifdef MAVLINK_USE_CONVENIENCE_FUNCTIONS static inline void mavlink_msg_${name_lower}_send(mavlink_channel_t chan,${{arg_fields: ${array_const}${type} ${array_prefix}${name},}}) { #if MAVLINK_NEED_BYTE_SWAP || !MAVLINK_ALIGNED_FIELDS char buf[MAVLINK_MSG_ID_${name}_LEN]; ${{scalar_fields: _mav_put_${type}(buf, ${wire_offset}, ${putname}); }} ${{array_fields: _mav_put_${type}_array(buf, ${wire_offset}, ${name}, ${array_length}); }} _mav_finalize_message_chan_send(chan, MAVLINK_MSG_ID_${name}, buf, MAVLINK_MSG_ID_${name}_MIN_LEN, MAVLINK_MSG_ID_${name}_LEN, MAVLINK_MSG_ID_${name}_CRC); #else mavlink_${name_lower}_t packet; ${{scalar_fields: packet.${name} = ${putname}; }} ${{array_fields: mav_array_memcpy(packet.${name}, ${name}, sizeof(${type})*${array_length}); }} _mav_finalize_message_chan_send(chan, MAVLINK_MSG_ID_${name}, (const char *)&packet, MAVLINK_MSG_ID_${name}_MIN_LEN, MAVLINK_MSG_ID_${name}_LEN, MAVLINK_MSG_ID_${name}_CRC); #endif } /** * @brief Send a ${name_lower} message * @param chan MAVLink channel to send the message * @param struct The MAVLink struct to serialize */ static inline void mavlink_msg_${name_lower}_send_struct(mavlink_channel_t chan, const mavlink_${name_lower}_t* ${name_lower}) { #if MAVLINK_NEED_BYTE_SWAP || !MAVLINK_ALIGNED_FIELDS mavlink_msg_${name_lower}_send(chan,${{arg_fields: ${name_lower}->${name},}}); #else _mav_finalize_message_chan_send(chan, MAVLINK_MSG_ID_${name}, (const char *)${name_lower}, MAVLINK_MSG_ID_${name}_MIN_LEN, MAVLINK_MSG_ID_${name}_LEN, MAVLINK_MSG_ID_${name}_CRC); #endif } #if MAVLINK_MSG_ID_${name}_LEN <= MAVLINK_MAX_PAYLOAD_LEN /* This varient of _send() can be used to save stack space by re-using memory from the receive buffer. The caller provides a mavlink_message_t which is the size of a full mavlink message. This is usually the receive buffer for the channel, and allows a reply to an incoming message with minimum stack space usage. */ static inline void mavlink_msg_${name_lower}_send_buf(mavlink_message_t *msgbuf, mavlink_channel_t chan, ${{arg_fields: ${array_const}${type} ${array_prefix}${name},}}) { #if MAVLINK_NEED_BYTE_SWAP || !MAVLINK_ALIGNED_FIELDS char *buf = (char *)msgbuf; ${{scalar_fields: _mav_put_${type}(buf, ${wire_offset}, ${putname}); }} ${{array_fields: _mav_put_${type}_array(buf, ${wire_offset}, ${name}, ${array_length}); }} _mav_finalize_message_chan_send(chan, MAVLINK_MSG_ID_${name}, buf, MAVLINK_MSG_ID_${name}_MIN_LEN, MAVLINK_MSG_ID_${name}_LEN, MAVLINK_MSG_ID_${name}_CRC); #else mavlink_${name_lower}_t *packet = (mavlink_${name_lower}_t *)msgbuf; ${{scalar_fields: packet->${name} = ${putname}; }} ${{array_fields: mav_array_memcpy(packet->${name}, ${name}, sizeof(${type})*${array_length}); }} _mav_finalize_message_chan_send(chan, MAVLINK_MSG_ID_${name}, (const char *)packet, MAVLINK_MSG_ID_${name}_MIN_LEN, MAVLINK_MSG_ID_${name}_LEN, MAVLINK_MSG_ID_${name}_CRC); #endif } #endif #endif // MESSAGE ${name} UNPACKING ${{fields: /** * @brief Get field ${name} from ${name_lower} message * * @return ${units} ${description} */ static inline ${return_type} mavlink_msg_${name_lower}_get_${name}(const mavlink_message_t* msg${get_arg}) { return _MAV_RETURN_${type}${array_tag}(msg, ${array_return_arg} ${wire_offset}); } }} /** * @brief Decode a ${name_lower} message into a struct * * @param msg The message to decode * @param ${name_lower} C-struct to decode the message contents into */ static inline void mavlink_msg_${name_lower}_decode(const mavlink_message_t* msg, mavlink_${name_lower}_t* ${name_lower}) { #if MAVLINK_NEED_BYTE_SWAP || !MAVLINK_ALIGNED_FIELDS ${{ordered_fields: ${decode_left}mavlink_msg_${name_lower}_get_${name}(msg${decode_right}); }} #else uint8_t len = msg->len < MAVLINK_MSG_ID_${name}_LEN? msg->len : MAVLINK_MSG_ID_${name}_LEN; memset(${name_lower}, 0, MAVLINK_MSG_ID_${name}_LEN); memcpy(${name_lower}, _MAV_PAYLOAD(msg), len); #endif } ''', m) f.close() def generate_testsuite_h(directory, xml): '''generate testsuite.h per XML file''' f = open(os.path.join(directory, "testsuite.h"), mode='w') t.write(f, ''' /** @file * @brief MAVLink comm protocol testsuite generated from ${basename}.xml * @see http://qgroundcontrol.org/mavlink/ */ #pragma once #ifndef ${basename_upper}_TESTSUITE_H #define ${basename_upper}_TESTSUITE_H #ifdef __cplusplus extern "C" { #endif #ifndef MAVLINK_TEST_ALL #define MAVLINK_TEST_ALL ${{include_list:static void mavlink_test_${base}(uint8_t, uint8_t, mavlink_message_t *last_msg); }} static void mavlink_test_${basename}(uint8_t, uint8_t, mavlink_message_t *last_msg); static void mavlink_test_all(uint8_t system_id, uint8_t component_id, mavlink_message_t *last_msg) { ${{include_list: mavlink_test_${base}(system_id, component_id, last_msg); }} mavlink_test_${basename}(system_id, component_id, last_msg); } #endif ${{include_list:#include "../${base}/testsuite.h" }} ${{message: static void mavlink_test_${name_lower}(uint8_t system_id, uint8_t component_id, mavlink_message_t *last_msg) { #ifdef MAVLINK_STATUS_FLAG_OUT_MAVLINK1 mavlink_status_t *status = mavlink_get_channel_status(MAVLINK_COMM_0); if ((status->flags & MAVLINK_STATUS_FLAG_OUT_MAVLINK1) && MAVLINK_MSG_ID_${name} >= 256) { return; } #endif mavlink_message_t msg; uint8_t buffer[MAVLINK_MAX_PACKET_LEN]; uint16_t i; mavlink_${name_lower}_t packet_in = { ${{ordered_fields:${c_test_value},}} }; mavlink_${name_lower}_t packet1, packet2; memset(&packet1, 0, sizeof(packet1)); ${{scalar_fields:packet1.${name} = packet_in.${name}; }} ${{array_fields:mav_array_memcpy(packet1.${name}, packet_in.${name}, sizeof(${type})*${array_length}); }} #ifdef MAVLINK_STATUS_FLAG_OUT_MAVLINK1 if (status->flags & MAVLINK_STATUS_FLAG_OUT_MAVLINK1) { // cope with extensions memset(MAVLINK_MSG_ID_${name}_MIN_LEN + (char *)&packet1, 0, sizeof(packet1)-MAVLINK_MSG_ID_${name}_MIN_LEN); } #endif memset(&packet2, 0, sizeof(packet2)); mavlink_msg_${name_lower}_encode(system_id, component_id, &msg, &packet1); mavlink_msg_${name_lower}_decode(&msg, &packet2); MAVLINK_ASSERT(memcmp(&packet1, &packet2, sizeof(packet1)) == 0); memset(&packet2, 0, sizeof(packet2)); mavlink_msg_${name_lower}_pack(system_id, component_id, &msg ${{arg_fields:, packet1.${name} }}); mavlink_msg_${name_lower}_decode(&msg, &packet2); MAVLINK_ASSERT(memcmp(&packet1, &packet2, sizeof(packet1)) == 0); memset(&packet2, 0, sizeof(packet2)); mavlink_msg_${name_lower}_pack_chan(system_id, component_id, MAVLINK_COMM_0, &msg ${{arg_fields:, packet1.${name} }}); mavlink_msg_${name_lower}_decode(&msg, &packet2); MAVLINK_ASSERT(memcmp(&packet1, &packet2, sizeof(packet1)) == 0); memset(&packet2, 0, sizeof(packet2)); mavlink_msg_to_send_buffer(buffer, &msg); for (i=0; i