MAVLinkXMLParser.cc 41.2 KB
Newer Older
pixhawk's avatar
pixhawk committed
1 2 3 4 5 6 7 8 9
/*=====================================================================
======================================================================*/

/**
 * @file
 *   @brief Implementation of class MAVLinkXMLParser
 *   @author Lorenz Meier <mail@qgroundcontrol.org>
 */

pixhawk's avatar
pixhawk committed
10 11 12 13
#include <QFile>
#include <QDir>
#include <QPair>
#include <QList>
14
#include <QMap>
pixhawk's avatar
pixhawk committed
15
#include <QDateTime>
16
#include <QLocale>
pixhawk's avatar
pixhawk committed
17 18 19 20 21
#include "MAVLinkXMLParser.h"

#include <QDebug>

MAVLinkXMLParser::MAVLinkXMLParser(QDomDocument* document, QString outputDirectory, QObject* parent) : QObject(parent),
22 23 24
    doc(document),
    outputDirName(outputDirectory),
    fileName("")
pixhawk's avatar
pixhawk committed
25 26 27 28 29 30 31
{
}

MAVLinkXMLParser::MAVLinkXMLParser(QString document, QString outputDirectory, QObject* parent) : QObject(parent)
{
    doc = new QDomDocument();
    QFile file(document);
32
    if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
pixhawk's avatar
pixhawk committed
33 34 35
        const QString instanceText(QString::fromUtf8(file.readAll()));
        doc->setContent(instanceText);
    }
36
    fileName = document;
pixhawk's avatar
pixhawk committed
37 38 39 40 41 42 43
    outputDirName = outputDirectory;
}

MAVLinkXMLParser::~MAVLinkXMLParser()
{
}

pixhawk's avatar
pixhawk committed
44 45 46
/**
 * Generate C-code (C-89 compliant) out of the XML protocol specs.
 */
pixhawk's avatar
pixhawk committed
47 48 49 50 51 52
bool MAVLinkXMLParser::generate()
{
    // Process result
    bool success = true;

    // Only generate if output dir is correctly set
53
    if (outputDirName == "") {
54 55 56 57 58
        emit parseState(tr("<font color=\"red\">ERROR: No output directory given.\nAbort.</font>"));
        return false;
    }

    QString topLevelOutputDirName = outputDirName;
pixhawk's avatar
pixhawk committed
59 60 61 62

    // print out the element names of all elements that are direct children
    // of the outermost element.
    QDomElement docElem = doc->documentElement();
63 64 65 66 67 68
    QDomNode n = docElem;//.firstChild();
    QDomNode p = docElem;

    // Sanity check variables
    QList<int>* usedMessageIDs = new QList<int>();
    QMap<QString, QString>* usedMessageNames = new QMap<QString, QString>();
69
    QMap<QString, QString>* usedEnumNames = new QMap<QString, QString>();
pixhawk's avatar
pixhawk committed
70 71 72 73

    QList< QPair<QString, QString> > cFiles;
    QString lcmStructDefs = "";

lm's avatar
lm committed
74
    QString pureFileName;
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
    QString pureIncludeFileName;

    QFileInfo fInfo(this->fileName);
    pureFileName = fInfo.baseName().split(".", QString::SkipEmptyParts).first();

    // XML parsed and converted to C code. Now generating the files
    outputDirName += QDir::separator() + pureFileName;
    QDateTime now = QDateTime::currentDateTime().toUTC();
    QLocale loc(QLocale::English);
    QString dateFormat = "dddd, MMMM d yyyy, hh:mm UTC";
    QString date = loc.toString(now, dateFormat);
    QString includeLine = "#include \"%1\"\n";
    QString mainHeaderName = pureFileName + ".h";
    QString messagesDirName = ".";//"generated";
    QDir dir(outputDirName + "/" + messagesDirName);

pixhawk's avatar
pixhawk committed
91 92
    int mavlinkVersion = 0;

93 94 95 96 97 98 99 100 101


    // Start main header
    QString mainHeader = QString("/** @file\n *\t@brief MAVLink comm protocol.\n *\t@see http://pixhawk.ethz.ch/software/mavlink\n *\t Generated on %1\n */\n#ifndef " + pureFileName.toUpper() + "_H\n#define " + pureFileName.toUpper() + "_H\n\n").arg(date); // The main header includes all messages
    // Mark all code as C code
    mainHeader += "#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n";
    mainHeader += "\n#include \"../protocol.h\"\n";
    mainHeader += "\n#define MAVLINK_ENABLED_" + pureFileName.toUpper() + "\n\n";

102 103
    QString enums;

lm's avatar
lm committed
104

105
    // Run through root children
106
    while(!n.isNull()) {
pixhawk's avatar
pixhawk committed
107 108
        // Each child is a message
        QDomElement e = n.toElement(); // try to convert the node to an element.
109 110
        if(!e.isNull()) {
            if (e.tagName() == "mavlink") {
111 112
                p = n;
                n = n.firstChild();
113
                while (!n.isNull()) {
114
                    e = n.toElement();
115
                    if (!e.isNull()) {
116
                        // Handle all include tags
117
                        if (e.tagName() == "include") {
118
                            QString incFileName = e.text();
119
                            // Load file
120
                            //QDomDocument includeDoc = QDomDocument();
pixhawk's avatar
pixhawk committed
121

122 123
                            // Prepend file path if it is a relative path and
                            // make it relative to opened file
124 125 126
                            QFileInfo fInfo(incFileName);

                            QString incFilePath;
127
                            if (fInfo.isRelative()) {
128
                                QFileInfo rInfo(this->fileName);
129 130
                                incFilePath = rInfo.absoluteDir().canonicalPath() + "/" + incFileName;
                                pureIncludeFileName = fInfo.baseName().split(".", QString::SkipEmptyParts).first();
131
                            }
pixhawk's avatar
pixhawk committed
132

133
                            QFile file(incFilePath);
134
                            if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
135 136 137 138 139 140 141 142 143 144 145
                                emit parseState(QString("<font color=\"green\">Included messages from file: %1</font>").arg(incFileName));
                                // NEW MODE: CREATE INDIVIDUAL FOLDERS
                                // Create new output directory, parse included XML and generate C-code
                                MAVLinkXMLParser includeParser(incFilePath, topLevelOutputDirName, this);
                                connect(&includeParser, SIGNAL(parseState(QString)), this, SIGNAL(parseState(QString)));
                                // Generate and write
                                includeParser.generate();
                                mainHeader += "\n#include \"../" + pureIncludeFileName + "/" + pureIncludeFileName + ".h\"\n";


                                // OLD MODE: MERGE BOTH FILES
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
                                //                                        const QString instanceText(QString::fromUtf8(file.readAll()));
                                //                                        includeDoc.setContent(instanceText);
                                //                                // Get all messages
                                //                                QDomNode in = includeDoc.documentElement().firstChild();
                                //                                QDomElement ie = in.toElement();
                                //                                if (!ie.isNull())
                                //                                {
                                //                                    if (ie.tagName() == "messages" || ie.tagName() == "include")
                                //                                    {
                                //                                        QDomNode ref = n.parentNode().insertAfter(in, n);
                                //                                        if (ref.isNull())
                                //                                        {
                                //                                            emit parseState(QString("<font color=\"red\">ERROR: Inclusion failed: XML syntax error in file %1. Wrong/misspelled XML?\nAbort.</font>").arg(fileName));
                                //                                            return false;
                                //                                        }
                                //                                    }
                                //                                }
163 164

                                emit parseState(QString("<font color=\"green\">End of inclusion from file: %1</font>").arg(incFileName));
165
                            } else {
166 167 168
                                // Include file could not be opened
                                emit parseState(QString("<font color=\"red\">ERROR: Failed including file: %1, file is not readable. Wrong/misspelled filename?\nAbort.</font>").arg(fileName));
                                return false;
169
                            }
170

pixhawk's avatar
pixhawk committed
171
                        }
172
                        // Handle all enum tags
173
                        else if (e.tagName() == "version") {
pixhawk's avatar
pixhawk committed
174 175 176 177 178
                            //QString fieldType = e.attribute("type", "");
                            //QString fieldName = e.attribute("name", "");
                            QString fieldText = e.text();

                            // Check if version has been previously set
179
                            if (mavlinkVersion != 0) {
pixhawk's avatar
pixhawk committed
180 181 182 183 184 185
                                emit parseState(QString("<font color=\"red\">ERROR: Protocol version tag set twice, please use it only once. First version was %1, second version is %2.\nAbort.</font>").arg(mavlinkVersion).arg(fieldText));
                                return false;
                            }

                            bool ok;
                            int version = fieldText.toInt(&ok);
186
                            if (ok && (version > 0) && (version < 256)) {
pixhawk's avatar
pixhawk committed
187 188
                                // Set MAVLink version
                                mavlinkVersion = version;
189
                            } else {
pixhawk's avatar
pixhawk committed
190 191 192 193 194
                                emit parseState(QString("<font color=\"red\">ERROR: Reading version string failed: %1, string is not an integer number between 1 and 255.\nAbort.</font>").arg(fieldText));
                                return false;
                            }
                        }
                        // Handle all enum tags
195
                        else if (e.tagName() == "enums") {
196 197 198
                            // One down into the enums list
                            p = n;
                            n = n.firstChild();
199
                            while (!n.isNull()) {
200 201 202 203 204 205 206
                                e = n.toElement();

                                QString currEnum;
                                QString currEnumEnd;
                                // Comment
                                QString comment;

207
                                if(!e.isNull() && e.tagName() == "enum") {
208 209
                                    // Get enum name
                                    QString enumName = e.attribute("name", "").toLower();
210
                                    if (enumName.size() == 0) {
211 212
                                        emit parseState(tr("<font color=\"red\">ERROR: Missing required name=\"\" attribute for tag %2 near line %1\nAbort.</font>").arg(QString::number(e.lineNumber()), e.tagName()));
                                        return false;
213
                                    } else {
214
                                        // Sanity check: Accept only enum names not used previously
215
                                        if (usedEnumNames->contains(enumName)) {
216 217
                                            emit parseState(tr("<font color=\"red\">ERROR: Enum name %1 used twice, second occurence near line %2 of file %3\nAbort.</font>").arg(enumName, QString::number(e.lineNumber()), fileName));
                                            return false;
218
                                        } else {
219 220 221 222 223
                                            usedEnumNames->insert(enumName, QString::number(e.lineNumber()));
                                        }

                                        // Everything sane, starting with enum content
                                        currEnum = "enum " + enumName.toUpper() + "\n{\n";
224
                                        currEnumEnd = QString("\t%1_ENUM_END\n};\n\n").arg(enumName.toUpper());
225 226 227

                                        int nextEnumValue = 0;

228
                                        // Get the enum fields
229
                                        QDomNode f = e.firstChild();
230
                                        while (!f.isNull()) {
231
                                            QDomElement e2 = f.toElement();
232
                                            if (!e2.isNull() && e2.tagName() == "entry") {
233 234 235 236 237 238
                                                QString fieldValue = e2.attribute("value", "");

                                                // If value was given, use it, if not, use the enum iterator
                                                // value. The iterator value gets reset by manual values

                                                QString fieldName = e2.attribute("name", "");
239
                                                if (fieldValue.length() == 0) {
240 241
                                                    fieldValue = QString::number(nextEnumValue);
                                                    nextEnumValue++;
242
                                                } else {
243 244
                                                    bool ok;
                                                    nextEnumValue = fieldValue.toInt(&ok) + 1;
245
                                                    if (!ok) {
246 247 248 249 250 251 252
                                                        emit parseState(tr("<font color=\"red\">ERROR: Enum entry %1 has not a valid number (%2) in the value field.\nAbort.</font>").arg(fieldName, fieldValue));
                                                        return false;
                                                    }
                                                }

                                                // Add comment of field if there is one
                                                QString fieldComment;
253
                                                if (e2.text().length() > 0) {
pixhawk's avatar
pixhawk committed
254
                                                    fieldComment = " /* " + e2.text() + "*/";
255
                                                    fieldComment = fieldComment.replace("\n", " ");
256 257
                                                }
                                                currEnum += "\t" + fieldName.toUpper() + "=" + fieldValue + "," + fieldComment + "\n";
258
                                            } else if(!e2.isNull() && e2.tagName() == "description") {
259
                                                comment = e2.text().replace("\n", " ") + comment;
260 261 262 263 264 265
                                            }
                                            f = f.nextSibling();
                                        }
                                    }
                                    // Add the last parsed enum
                                    // Remove the last comma, as the last value has none
266 267 268
                                    // ENUM END MARKER IS LAST ENTRY, COMMA REMOVAL NOT NEEDED
                                    //int commaPosition = currEnum.lastIndexOf(",");
                                    //currEnum.remove(commaPosition, 1);
269

270
                                    enums += "/** @brief " + comment  + " */\n" + currEnum + currEnumEnd;
271 272 273 274 275 276 277
                                } // Element is non-zero and element name is <enum>
                                n = n.nextSibling();
                            } // While through <enums>
                            // One up, back into the <mavlink> structure
                            n = p;
                        }

278
                        // Handle all message tags
279
                        else if (e.tagName() == "messages") {
280 281
                            p = n;
                            n = n.firstChild();
282
                            while (!n.isNull()) {
283
                                e = n.toElement();
284
                                if(!e.isNull()) {
285 286 287
                                    //if (e.isNull()) continue;
                                    // Get message name
                                    QString messageName = e.attribute("name", "").toLower();
288
                                    if (messageName.size() == 0) {
289 290
                                        emit parseState(tr("<font color=\"red\">ERROR: Missing required name=\"\" attribute for tag %2 near line %1\nAbort.</font>").arg(QString::number(e.lineNumber()), e.tagName()));
                                        return false;
291
                                    } else {
292 293 294 295 296 297
                                        // Get message id
                                        bool ok;
                                        int messageId = e.attribute("id", "-1").toInt(&ok, 10);
                                        emit parseState(tr("Compiling message <strong>%1 \t(#%3)</strong> \tnear line %2").arg(messageName, QString::number(n.lineNumber()), QString::number(messageId)));

                                        // Sanity check: Accept only message IDs not used previously
298
                                        if (usedMessageIDs->contains(messageId)) {
299 300
                                            emit parseState(tr("<font color=\"red\">ERROR: Message ID %1 used twice, second occurence near line %2 of file %3\nAbort.</font>").arg(QString::number(messageId), QString::number(e.lineNumber()), fileName));
                                            return false;
301
                                        } else {
302 303 304 305
                                            usedMessageIDs->append(messageId);
                                        }

                                        // Sanity check: Accept only message names not used previously
306
                                        if (usedMessageNames->contains(messageName)) {
307 308
                                            emit parseState(tr("<font color=\"red\">ERROR: Message name %1 used twice, second occurence near line %2 of file %3\nAbort.</font>").arg(messageName, QString::number(e.lineNumber()), fileName));
                                            return false;
309
                                        } else {
310 311 312
                                            usedMessageNames->insert(messageName, QString::number(e.lineNumber()));
                                        }

313 314
                                        QString channelType("mavlink_channel_t");
                                        QString messageType("mavlink_message_t");
315 316

                                        // Build up function call
pixhawk's avatar
pixhawk committed
317 318 319 320 321
                                        QString commentContainer("/**\n * @brief Pack a %1 message\n * @param system_id ID of this system\n * @param component_id ID of this component (e.g. 200 for IMU)\n * @param msg The MAVLink message to compress the data into\n *\n%2 * @return length of the message in bytes (excluding serial stream start sign)\n */\n");
                                        QString commentPackChanContainer("/**\n * @brief Pack a %1 message\n * @param system_id ID of this system\n * @param component_id ID of this component (e.g. 200 for IMU)\n * @param chan The MAVLink channel this message was sent over\n * @param msg The MAVLink message to compress the data into\n%2 * @return length of the message in bytes (excluding serial stream start sign)\n */\n");
                                        QString commentSendContainer("/**\n * @brief Send a %1 message\n * @param chan MAVLink channel to send the message\n *\n%2 */\n");
                                        QString commentEncodeContainer("/**\n * @brief Encode a %1 struct into a message\n *\n * @param system_id ID of this system\n * @param component_id ID of this component (e.g. 200 for IMU)\n * @param msg The MAVLink message to compress the data into\n * @param %1 C-struct to read the message contents from\n */\n");
                                        QString commentDecodeContainer("/**\n * @brief Decode a %1 message into a struct\n *\n * @param msg The message to decode\n * @param %1 C-struct to decode the message contents into\n */\n");
322
                                        QString commentEntry(" * @param %1 %2\n");
323
                                        QString idDefine = QString("#define MAVLINK_MSG_ID_%1 %2").arg(messageName.toUpper(), QString::number(messageId));
324
                                        QString arrayDefines;
325
                                        QString cStructName = QString("mavlink_%1_t").arg(messageName);
326 327 328
                                        QString cStruct("typedef struct __%1 \n{\n%2\n} %1;");
                                        QString cStructLines;
                                        QString encode("static inline uint16_t mavlink_msg_%1_encode(uint8_t system_id, uint8_t component_id, mavlink_message_t* msg, const %2* %1)\n{\n\treturn mavlink_msg_%1_pack(%3);\n}\n");
329

330 331
                                        QString decode("static inline void mavlink_msg_%1_decode(const mavlink_message_t* msg, %2* %1)\n{\n%3}\n");
                                        QString pack("static inline uint16_t mavlink_msg_%1_pack(uint8_t system_id, uint8_t component_id, mavlink_message_t* msg%2)\n{\n\tuint16_t i = 0;\n\tmsg->msgid = MAVLINK_MSG_ID_%3;\n\n%4\n\treturn mavlink_finalize_message(msg, system_id, component_id, i);\n}\n\n");
332 333
                                        QString packChan("static inline uint16_t mavlink_msg_%1_pack_chan(uint8_t system_id, uint8_t component_id, uint8_t chan, mavlink_message_t* msg%2)\n{\n\tuint16_t i = 0;\n\tmsg->msgid = MAVLINK_MSG_ID_%3;\n\n%4\n\treturn mavlink_finalize_message_chan(msg, system_id, component_id, chan, i);\n}\n\n");
                                        QString compactSend("#ifdef MAVLINK_USE_CONVENIENCE_FUNCTIONS\n\nstatic inline void mavlink_msg_%3_send(%1 chan%5)\n{\n\t%2 msg;\n\tmavlink_msg_%3_pack_chan(mavlink_system.sysid, mavlink_system.compid, chan, &msg%4);\n\tmavlink_send_uart(chan, &msg);\n}\n\n#endif");
334
                                        //QString compactStructSend = "#ifdef MAVLINK_USE_CONVENIENCE_FUNCTIONS\n\nstatic inline void mavlink_msg_%3_struct_send(%1 chan%5)\n{\n\t%2 msg;\n\tmavlink_msg_%3_encode(mavlink_system.sysid, mavlink_system.compid, &msg%4);\n\tmavlink_send_uart(chan, &msg);\n}\n\n#endif";
335 336 337 338 339 340 341 342 343 344
                                        QString unpacking;
                                        QString prepends;
                                        QString packParameters;
                                        QString packArguments("system_id, component_id, msg");
                                        QString packLines;
                                        QString decodeLines;
                                        QString sendArguments;
                                        QString commentLines;


345 346 347

                                        // Get the message fields
                                        QDomNode f = e.firstChild();
348
                                        while (!f.isNull()) {
349
                                            QDomElement e2 = f.toElement();
350
                                            if (!e2.isNull() && e2.tagName() == "field") {
351 352 353 354
                                                QString fieldType = e2.attribute("type", "");
                                                QString fieldName = e2.attribute("name", "");
                                                QString fieldText = e2.text();

355 356 357
                                                QString unpackingCode;
                                                QString unpackingComment = QString("/**\n * @brief Get field %1 from %2 message\n *\n * @return %3\n */\n").arg(fieldName, messageName, fieldText);

pixhawk's avatar
pixhawk committed
358
                                                // Send arguments do not work for the version field
359
                                                if (!fieldType.contains("uint8_t_mavlink_version")) {
pixhawk's avatar
pixhawk committed
360 361
                                                    // Send arguments are the same for integral types and arrays
                                                    sendArguments += ", " + fieldName;
362
                                                    commentLines += commentEntry.arg(fieldName, fieldText.replace("\n", " "));
pixhawk's avatar
pixhawk committed
363 364 365 366
                                                }

                                                // MAVLink version field
                                                // this is a special field always containing the version define
367
                                                if (fieldType.contains("uint8_t_mavlink_version")) {
pixhawk's avatar
pixhawk committed
368 369 370 371 372 373 374
                                                    // Add field to C structure
                                                    cStructLines += QString("\t%1 %2; ///< %3\n").arg("uint8_t", fieldName, fieldText);
                                                    // Add pack line to message_xx_pack function
                                                    packLines += QString("\ti += put_uint8_t_by_index(%1, i, msg->payload); // %2\n").arg(mavlinkVersion).arg(fieldText);
                                                    // Add decode function for this type
                                                    decodeLines += QString("\t%1->%2 = mavlink_msg_%1_get_%2(msg);\n").arg(messageName, fieldName);
                                                }
375 376

                                                // Array handling is different from simple types
377
                                                else if (fieldType.startsWith("array")) {
378 379 380 381 382 383 384 385
                                                    int arrayLength = QString(fieldType.split("[").at(1).split("]").first()).toInt();
                                                    QString arrayType = fieldType.split("[").first();
                                                    packParameters += QString(", const ") + QString("int8_t*") + " " + fieldName;
                                                    packArguments += ", " + messageName + "->" + fieldName;

                                                    // Add field to C structure
                                                    cStructLines += QString("\t%1 %2[%3]; ///< %4\n").arg("int8_t", fieldName, QString::number(arrayLength), fieldText);
                                                    // Add pack line to message_xx_pack function
pixhawk's avatar
pixhawk committed
386
                                                    packLines += QString("\ti += put_%1_by_index(%2, %3, i, msg->payload); // %4\n").arg(arrayType, fieldName, QString::number(arrayLength), fieldText);
387 388
                                                    // Add decode function for this type
                                                    decodeLines += QString("\tmavlink_msg_%1_get_%2(msg, %1->%2);\n").arg(messageName, fieldName);
389
                                                    arrayDefines += QString("#define MAVLINK_MSG_%1_FIELD_%2_LEN %3\n").arg(messageName.toUpper(), fieldName.toUpper(), QString::number(arrayLength));
390
                                                } else if (fieldType.startsWith("string")) {
391 392 393 394 395 396 397 398
                                                    int arrayLength = QString(fieldType.split("[").at(1).split("]").first()).toInt();
                                                    QString arrayType = fieldType.split("[").first();
                                                    packParameters += QString(", const ") + QString("char*") + " " + fieldName;
                                                    packArguments += ", " + messageName + "->" + fieldName;

                                                    // Add field to C structure
                                                    cStructLines += QString("\t%1 %2[%3]; ///< %4\n").arg("char", fieldName, QString::number(arrayLength), fieldText);
                                                    // Add pack line to message_xx_pack function
pixhawk's avatar
pixhawk committed
399
                                                    packLines += QString("\ti += put_%1_by_index(%2, %3, i, msg->payload); // %4\n").arg(arrayType, fieldName, QString::number(arrayLength), e2.text());
400 401
                                                    // Add decode function for this type
                                                    decodeLines += QString("\tmavlink_msg_%1_get_%2(msg, %1->%2);\n").arg(messageName, fieldName);
402
                                                    arrayDefines += QString("#define MAVLINK_MSG_%1_FIELD_%2_LEN %3\n").arg(messageName.toUpper(), fieldName.toUpper(), QString::number(arrayLength));
403
                                                }
404
                                                // Expand array handling to all valid mavlink data types
405
                                                else if(fieldType.contains('[') && fieldType.contains(']')) {
406 407 408 409 410 411 412 413
                                                    int arrayLength = QString(fieldType.split("[").at(1).split("]").first()).toInt();
                                                    QString arrayType = fieldType.split("[").first();
                                                    packParameters += QString(", const ") + arrayType + "* " + fieldName;
                                                    packArguments += ", " + messageName + "->" + fieldName;

                                                    // Add field to C structure
                                                    cStructLines += QString("\t%1 %2[%3]; ///< %4\n").arg(arrayType, fieldName, QString::number(arrayLength), fieldText);
                                                    // Add pack line to message_xx_pack function
414
                                                    packLines += QString("\ti += put_array_by_index((const int8_t*)%1, sizeof(%2)*%3, i, msg->payload); // %4\n").arg(fieldName, arrayType, QString::number(arrayLength), fieldText);
415 416 417 418 419 420 421
                                                    // Add decode function for this type
                                                    decodeLines += QString("\tmavlink_msg_%1_get_%2(msg, %1->%2);\n").arg(messageName, fieldName);
                                                    arrayDefines += QString("#define MAVLINK_MSG_%1_FIELD_%2_LEN %3\n").arg(messageName.toUpper(), fieldName.toUpper(), QString::number(arrayLength));

                                                    unpackingCode = QString("\n\tmemcpy(r_data, msg->payload%1, sizeof(%2)*%3);\n\treturn sizeof(%2)*%3;").arg(prepends, arrayType, QString::number(arrayLength));

                                                    unpacking += unpackingComment + QString("static inline uint16_t mavlink_msg_%1_get_%2(const mavlink_message_t* msg, %3* r_data)\n{\n%4\n}\n\n").arg(messageName, fieldName, arrayType, unpackingCode);
422
                                                    //                                                    decodeLines += "";
423 424
                                                    prepends += QString("+sizeof(%1)*%2").arg(arrayType, QString::number(arrayLength));

425
                                                } else
426 427 428 429 430 431 432 433
                                                    // Handle simple types like integers and floats
                                                {
                                                    packParameters += ", " + fieldType + " " + fieldName;
                                                    packArguments += ", " + messageName + "->" + fieldName;

                                                    // Add field to C structure
                                                    cStructLines += QString("\t%1 %2; ///< %3\n").arg(fieldType, fieldName, fieldText);
                                                    // Add pack line to message_xx_pack function
pixhawk's avatar
pixhawk committed
434
                                                    packLines += QString("\ti += put_%1_by_index(%2, i, msg->payload); // %3\n").arg(fieldType, fieldName, e2.text());
435 436 437
                                                    // Add decode function for this type
                                                    decodeLines += QString("\t%1->%2 = mavlink_msg_%1_get_%2(msg);\n").arg(messageName, fieldName);
                                                }
pixhawk's avatar
pixhawk committed
438

439

440
                                                //
441
                                                //                                                QString unpackingCode;
442

443
                                                if (fieldType == "uint8_t_mavlink_version") {
444
                                                    unpackingCode = QString("\treturn (%1)(msg->payload%2)[0];").arg("uint8_t", prepends);
445
                                                } else if (fieldType == "uint8_t" || fieldType == "int8_t") {
446
                                                    unpackingCode = QString("\treturn (%1)(msg->payload%2)[0];").arg(fieldType, prepends);
447
                                                } else if (fieldType == "uint16_t" || fieldType == "int16_t") {
448
                                                    unpackingCode = QString("\tgeneric_16bit r;\n\tr.b[1] = (msg->payload%1)[0];\n\tr.b[0] = (msg->payload%1)[1];\n\treturn (%2)r.s;").arg(prepends).arg(fieldType);
449
                                                } else if (fieldType == "uint32_t" || fieldType == "int32_t") {
450
                                                    unpackingCode = QString("\tgeneric_32bit r;\n\tr.b[3] = (msg->payload%1)[0];\n\tr.b[2] = (msg->payload%1)[1];\n\tr.b[1] = (msg->payload%1)[2];\n\tr.b[0] = (msg->payload%1)[3];\n\treturn (%2)r.i;").arg(prepends).arg(fieldType);
451
                                                } else if (fieldType == "float") {
452
                                                    unpackingCode = QString("\tgeneric_32bit r;\n\tr.b[3] = (msg->payload%1)[0];\n\tr.b[2] = (msg->payload%1)[1];\n\tr.b[1] = (msg->payload%1)[2];\n\tr.b[0] = (msg->payload%1)[3];\n\treturn (%2)r.f;").arg(prepends).arg(fieldType);
453
                                                } else if (fieldType == "uint64_t" || fieldType == "int64_t") {
454
                                                    unpackingCode = QString("\tgeneric_64bit r;\n\tr.b[7] = (msg->payload%1)[0];\n\tr.b[6] = (msg->payload%1)[1];\n\tr.b[5] = (msg->payload%1)[2];\n\tr.b[4] = (msg->payload%1)[3];\n\tr.b[3] = (msg->payload%1)[4];\n\tr.b[2] = (msg->payload%1)[5];\n\tr.b[1] = (msg->payload%1)[6];\n\tr.b[0] = (msg->payload%1)[7];\n\treturn (%2)r.ll;").arg(prepends).arg(fieldType);
455 456
                                                } else if (fieldType.startsWith("array")) {
                                                    // fieldtype formatis string[n] where n is the number of bytes, extract n from field type string
457
                                                    unpackingCode = QString("\n\tmemcpy(r_data, msg->payload%1, %2);\n\treturn %2;").arg(prepends, fieldType.split("[").at(1).split("]").first());
458 459
                                                } else if (fieldType.startsWith("string")) {
                                                    // fieldtype formatis string[n] where n is the number of bytes, extract n from field type string
460 461 462
                                                    unpackingCode = QString("\n\tstrcpy(r_data, msg->payload%1, %2);\n\treturn %2;").arg(prepends, fieldType.split("[").at(1).split("]").first());
                                                }

463

464
                                                // Generate the message decoding function
465
                                                if (fieldType.contains("uint8_t_mavlink_version")) {
466
                                                    unpacking += unpackingComment + QString("static inline %1 mavlink_msg_%2_get_%3(const mavlink_message_t* msg)\n{\n%4\n}\n\n").arg("uint8_t", messageName, fieldName, unpackingCode);
pixhawk's avatar
pixhawk committed
467 468 469
                                                    decodeLines += "";
                                                    prepends += "+sizeof(uint8_t)";
                                                }
470
                                                // Array handling is different from simple types
471
                                                else if (fieldType.startsWith("array")) {
472 473 474 475
                                                    unpacking += unpackingComment + QString("static inline uint16_t mavlink_msg_%1_get_%2(const mavlink_message_t* msg, int8_t* r_data)\n{\n%4\n}\n\n").arg(messageName, fieldName, unpackingCode);
                                                    decodeLines += "";
                                                    QString arrayLength = QString(fieldType.split("[").at(1).split("]").first());
                                                    prepends += "+" + arrayLength;
476
                                                } else if (fieldType.startsWith("string")) {
477 478 479 480
                                                    unpacking += unpackingComment + QString("static inline uint16_t mavlink_msg_%1_get_%2(const mavlink_message_t* msg, char* r_data)\n{\n%4\n}\n\n").arg(messageName, fieldName, unpackingCode);
                                                    decodeLines += "";
                                                    QString arrayLength = QString(fieldType.split("[").at(1).split("]").first());
                                                    prepends += "+" + arrayLength;
481
                                                } else if(fieldType.contains('[') && fieldType.contains(']')) {
482
                                                    // prevent this case from being caught in the following else
483
                                                } else {
484 485 486 487 488 489 490 491 492 493 494
                                                    unpacking += unpackingComment + QString("static inline %1 mavlink_msg_%2_get_%3(const mavlink_message_t* msg)\n{\n%4\n}\n\n").arg(fieldType, messageName, fieldName, unpackingCode);
                                                    decodeLines += "";
                                                    prepends += "+sizeof(" + e2.attribute("type", "void") + ")";
                                                }
                                            }
                                            f = f.nextSibling();
                                        }

                                        cStruct = cStruct.arg(cStructName, cStructLines);
                                        lcmStructDefs.append("\n").append(cStruct).append("\n");
                                        pack = pack.arg(messageName, packParameters, messageName.toUpper(), packLines);
495
                                        packChan = packChan.arg(messageName, packParameters, messageName.toUpper(), packLines);
496 497 498
                                        encode = encode.arg(messageName).arg(cStructName).arg(packArguments);
                                        decode = decode.arg(messageName).arg(cStructName).arg(decodeLines);
                                        compactSend = compactSend.arg(channelType, messageType, messageName, sendArguments, packParameters);
pixhawk's avatar
pixhawk committed
499
                                        QString cFile = "// MESSAGE " + messageName.toUpper() + " PACKING\n\n" + idDefine + "\n\n" + cStruct + "\n\n" + arrayDefines + "\n\n" + commentContainer.arg(messageName.toLower(), commentLines) + pack + commentPackChanContainer.arg(messageName.toLower(), commentLines) + packChan + commentEncodeContainer.arg(messageName.toLower()) + encode + "\n" + commentSendContainer.arg(messageName.toLower(), commentLines) + compactSend + "\n" + "// MESSAGE " + messageName.toUpper() + " UNPACKING\n\n" + unpacking + commentDecodeContainer.arg(messageName.toLower()) + decode;
500 501 502 503 504 505 506 507 508 509 510
                                        cFiles.append(qMakePair(QString("mavlink_msg_%1.h").arg(messageName), cFile));
                                    } // Check if tag = message
                                } // Check if e = NULL
                                n = n.nextSibling();
                            } // While through <message>
                            n = p;

                        } // Check if tag = messages
                    } // Check if e = NULL
                    n = n.nextSibling();
                } // While through include and messages
511
                // One up - current node = parent
512 513 514 515
                n = p;

            } // Check if tag = mavlink
        } // Check if e = NULL
pixhawk's avatar
pixhawk committed
516
        n = n.nextSibling();
517
    } // While through root children
pixhawk's avatar
pixhawk committed
518

pixhawk's avatar
pixhawk committed
519 520 521 522 523 524
    // Add version to main header

    mainHeader += "// MAVLINK VERSION\n\n";
    mainHeader += QString("#ifndef MAVLINK_VERSION\n#define MAVLINK_VERSION %1\n#endif\n\n").arg(mavlinkVersion);
    mainHeader += QString("#if (MAVLINK_VERSION == 0)\n#undef MAVLINK_VERSION\n#define MAVLINK_VERSION %1\n#endif\n\n").arg(mavlinkVersion);

525 526 527 528 529
    // Add enums to main header

    mainHeader += "// ENUM DEFINITIONS\n\n";
    mainHeader += enums;
    mainHeader += "\n";
530

531
    mainHeader += "// MESSAGE DEFINITIONS\n\n";
pixhawk's avatar
pixhawk committed
532
    // Create directory if it doesn't exist, report result in success
533
    if (!dir.exists()) success = success && dir.mkpath(outputDirName + "/" + messagesDirName);
534
    for (int i = 0; i < cFiles.size(); i++) {
pixhawk's avatar
pixhawk committed
535 536 537 538
        QFile rawFile(dir.filePath(cFiles.at(i).first));
        bool ok = rawFile.open(QIODevice::WriteOnly | QIODevice::Text);
        success = success && ok;
        rawFile.write(cFiles.at(i).second.toLatin1());
539
        rawFile.close();
pixhawk's avatar
pixhawk committed
540 541 542 543 544
        mainHeader += includeLine.arg(messagesDirName + "/" + cFiles.at(i).first);
    }

    mainHeader += "#ifdef __cplusplus\n}\n#endif\n";
    mainHeader += "#endif";
lm's avatar
lm committed
545 546
    // Newline to make compiler happy
    mainHeader += "\n";
pixhawk's avatar
pixhawk committed
547 548 549 550 551 552

    // Write main header
    QFile rawHeader(outputDirName + "/" + mainHeaderName);
    bool ok = rawHeader.open(QIODevice::WriteOnly | QIODevice::Text);
    success = success && ok;
    rawHeader.write(mainHeader.toLatin1());
553 554 555 556 557 558 559 560 561 562 563 564
    rawHeader.close();

    // Write alias mavlink header
    QFile mavlinkHeader(outputDirName + "/mavlink.h");
    ok = mavlinkHeader.open(QIODevice::WriteOnly | QIODevice::Text);
    success = success && ok;
    QString mHeader = QString("/** @file\n *\t@brief MAVLink comm protocol.\n *\t@see http://pixhawk.ethz.ch/software/mavlink\n *\t Generated on %1\n */\n#ifndef MAVLINK_H\n#define MAVLINK_H\n\n").arg(date); // The main header includes all messages
    // Mark all code as C code
    mHeader += "#include \"" + mainHeaderName + "\"\n\n";
    mHeader += "#endif\n";
    mavlinkHeader.write(mHeader.toLatin1());
    mavlinkHeader.close();
pixhawk's avatar
pixhawk committed
565 566

    // Write C structs / lcm definitions
567 568 569 570
    //    QFile lcmStructs(outputDirName + "/mavlink.lcm");
    //    ok = lcmStructs.open(QIODevice::WriteOnly | QIODevice::Text);
    //    success = success && ok;
    //    lcmStructs.write(lcmStructDefs.toLatin1());
pixhawk's avatar
pixhawk committed
571 572 573

    return success;
}