机器人地面站-[QGroundControl源码解析]-[2]

2023-05-16

目录

前言

一.QGC

二.QGCComboBox

三.QGCFileDownload

四.QGCLoggingCategory

五.QGCMapPalette

六.QGCPalette

七.QGCQGeoCoordinate

八.QGCTemporaryFile

九.QGCToolbox

十.RunGuard

十一.TerrainTile

总结


前言

上一节我们对一些工具类进行了注释和阅读,他们包括cmd用户输入的解析,json文件的读取加载和解析,kml文件的加载读取和解析,以及shape文件的读取和解析。今天我们来看一下qgc开头的几个文件。

一.QGC

这个类主要就是几个方法,可以看头文件中的注释,cc文件主要实现了这些方法,比较简单,cc文件这里就不再贴出了,一下是头文件内容。

/****************************************************************************
 *
 * (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/

#pragma once

#include <QDateTime>
#include <QColor>
#include <QThread>

#include "QGCConfig.h"

namespace QGC
{

/**
 * @brief Get the current ground time in microseconds. 获取当前地面时时间(以微秒为单位)。
 * @note This does not have microsecond precision, it is limited to millisecond precision. 这没有微秒精度,它被限制在毫秒精度。
 */
quint64 groundTimeUsecs();


/**
 *  @brief Get the current ground time in milliseconds 获取当前地面时间(以毫秒为单位)
 */
quint64 groundTimeMilliseconds();


/** 
 * @brief Get the current ground time in fractional seconds 以小数秒为单位获取当前地面时间
 * @note Precision is limited to milliseconds.
 */
qreal groundTimeSeconds();


/**
 *  @brief Returns the angle limited to -pi - pi 返回-pi 到-pi这个范围的的角度
 */
float limitAngleToPMPIf(double angle);


/**
 *  @brief Returns the angle limited to -pi - pi
*/
double limitAngleToPMPId(double angle);



/**
 *  @brief Records boot time (called from main)记录启动时间(从main调用)
*/
void initTimer();


/**
 *  @brief Get the ground time since boot in milliseconds 获取自启动后的地面时间(以毫秒为单位)
*/
quint64 bootTimeMilliseconds();


/**
 *  @brief Returns true if the two values are equal or close. Correctly handles 0 and NaN values. 如果两个值相等或接近,则返回true。 正确处理0和NaN值。
*/
bool fuzzyCompare(double value1, double value2);



class SLEEP : public QThread
{
    Q_OBJECT
public:
    using QThread::sleep;
    using QThread::msleep;
    using QThread::usleep;
};


///CRC的全称是循环冗余校验。
quint32 crc32(const quint8 *src, unsigned len, unsigned state);

}

二.QGCComboBox

/****************************************************************************
 *
 * (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/

#pragma once

#include <QComboBox>

/// @file
///     @brief Subclass of QComboBox. Mainly used for unit test so you can simulate a user selection
///             with correct signalling.  主要用于单元测试,可以用正确的信号模拟用户选择。
///
///     @author Don Gagne <don@thegagnes.com>

class QGCComboBox : public QComboBox {
    Q_OBJECT
    
public:
    QGCComboBox(QWidget* parent = nullptr);
    
	/// @brief Sets the current index on the combo. Signals activated, as well as currentIndexChanged.
    /// 设置组combo当前索引。 当信号被激活,currentIndexChanged也会被触发。
	void simulateUserSetCurrentIndex(int index);
};
/****************************************************************************
 *
 * (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/

#include "QGCComboBox.h"

QGCComboBox::QGCComboBox(QWidget* parent) :
    QComboBox(parent)
{

}

void QGCComboBox::simulateUserSetCurrentIndex(int index)
{
    Q_ASSERT(index >=0 && index < count());
    
    // This will signal currentIndexChanged
    setCurrentIndex(index);
    
    // We have to manually signal activated
    //手动触发信号activated textActivated信号
    emit activated(index);
    emit textActivated(itemText(index));
}

三.QGCFileDownload

此类利用QNetworkAccessManager进行文件的下载和存储。头文件注释如下

/****************************************************************************
 *
 * (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/

#pragma once

#include <QNetworkReply>


/**
 * @brief Get the current ground time in microseconds. 获取当前地面时时间(以微秒为单位)。
 * @note NetworkAccess API是围绕一个QNetworkAccessManager对象构造的,
 * 该对象包含它发送的请求的公共配置和设置。它包含代理和缓存配置,以及与此类问题相关的信号,以及可用于监视网络运行进度的应答信号。
 * 一个QNetworkAccessManager对于整个Qt应用程序应该已经足够了。
 * QNetworkAccessManager https://blog.csdn.net/jolin678/article/details/122984515
 */

class QGCFileDownload : public QNetworkAccessManager
{
    Q_OBJECT
    
public:
    QGCFileDownload(QObject* parent = nullptr);
    
    /// Download the specified remote file. 下载指定的远程文件。
    ///     @param remoteFile   File to download. Can be http address or file system path. 可以是http地址或文件系统路径。
    ///     @param redirect     true: call is internal due to redirect 用于重定向
    /// @return true: Asynchronous download has started, false: Download initialization failed
    /// true:异步下载已经启动,false:下载初始化失败
    bool download(const QString& remoteFile, bool redirect = false);

signals:
    //下载进度信号
    void downloadProgress(qint64 curr, qint64 total);
    //下载完成信号
    void downloadComplete(QString remoteFile, QString localFile, QString errorMsg);

private:
    //下载完成
    void _downloadFinished(void);
    //下载出现错误
    void _downloadError(QNetworkReply::NetworkError code);

    QString _originalRemoteFile;
};

cc文件如下

/****************************************************************************
 *
 * (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/


#include "QGCFileDownload.h"

#include <QFileInfo>
#include <QStandardPaths>
#include <QNetworkProxy>

QGCFileDownload::QGCFileDownload(QObject* parent)
    : QNetworkAccessManager(parent)
{

}

bool QGCFileDownload::download(const QString& remoteFile, bool redirect)
{
    //如果不重定向
    if (!redirect) {
        _originalRemoteFile = remoteFile;
    }

    if (remoteFile.isEmpty()) {
        qWarning() << "downloadFile empty";
        return false;
    }
    

    QUrl remoteUrl;
    //如果含有http则是网路路径否则是本地路径
    if (remoteFile.startsWith("http:") || remoteFile.startsWith("https:"))
    {
        remoteUrl.setUrl(remoteFile);
    }else
    {
        remoteUrl = QUrl::fromLocalFile(remoteFile);
    }
    if (!remoteUrl.isValid()) {
        qWarning() << "Remote URL is invalid" << remoteFile;
        return false;
    }
    
    QNetworkRequest networkRequest(remoteUrl);

    //代理设置
    QNetworkProxy tProxy;
    tProxy.setType(QNetworkProxy::DefaultProxy);
    setProxy(tProxy);
    
    QNetworkReply* networkReply = get(networkRequest);
    if (!networkReply) {
        qWarning() << "QNetworkAccessManager::get failed";
        return false;
    }

    //绑定槽函数
    connect(networkReply, &QNetworkReply::downloadProgress, this, &QGCFileDownload::downloadProgress);
    connect(networkReply, &QNetworkReply::finished, this, &QGCFileDownload::_downloadFinished);
    connect(networkReply, &QNetworkReply::errorOccurred, this, &QGCFileDownload::_downloadError);
    return true;
}

void QGCFileDownload::_downloadFinished(void)
{
    //当某一个Object emit一个signal的时候,它就是一个sender,系统会记录下当前是谁emit出这个signal的,所以你在对应的slot里就可以通过 sender()得到当前是那个对象调用了你的slot
    QNetworkReply* reply = qobject_cast<QNetworkReply*>(QObject::sender());

    // When an error occurs or the user cancels the download, we still end up here. So bail out in
    // those cases.
    //当出现错误或用户取消下载时,我们仍然会在这里结束。 所以,在这些情况下,你可以在这里释放对象。
    if (reply->error() != QNetworkReply::NoError) {
        reply->deleteLater();
        return;
    }

    // Check for redirection
    QVariant redirectionTarget = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
    if (!redirectionTarget.isNull()) {
        //如果重定向目标不为空
        QUrl redirectUrl = reply->url().resolved(redirectionTarget.toUrl());
        download(redirectUrl.toString(), true /* redirect */);
        reply->deleteLater();
        return;
    }

    // Split out filename from path
    //获取文件名
    QString remoteFileName = QFileInfo(reply->url().toString()).fileName();
    if (remoteFileName.isEmpty()) {
        qWarning() << "Unabled to parse filename from remote url" << reply->url().toString();
        //给个默认文件名
        remoteFileName = "DownloadedFile";
    }

    // Strip out http parameters from remote filename 从远程文件名中删除http的参数
    int parameterIndex = remoteFileName.indexOf("?");
    if (parameterIndex != -1) {
        remoteFileName  = remoteFileName.left(parameterIndex);
    }

    // Determine location to download file to 确定要下载文件的位置
    // TempLocation 临时文件路径
    QString downloadFilename = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
    if (downloadFilename.isEmpty()) {
        downloadFilename = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
        if (downloadFilename.isEmpty()) {
            emit downloadComplete(_originalRemoteFile, QString(), tr("Unabled to find writable download location. Tried downloads and temp directory."));
            return;
        }
    }
    downloadFilename += "/"  + remoteFileName;

    if (!downloadFilename.isEmpty()) {
        // Store downloaded file in download location
        QFile file(downloadFilename);
        if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
            emit downloadComplete(_originalRemoteFile, downloadFilename, tr("Could not save downloaded file to %1. Error: %2").arg(downloadFilename).arg(file.errorString()));
            return;
        }

        //写入
        file.write(reply->readAll());
        file.close();

        emit downloadComplete(_originalRemoteFile, downloadFilename, QString());
    } else {
        QString errorMsg = "Internal error";
        qWarning() << errorMsg;
        emit downloadComplete(_originalRemoteFile, downloadFilename, errorMsg);
    }

    //释放
    reply->deleteLater();
}

/// @brief Called when an error occurs during download
void QGCFileDownload::_downloadError(QNetworkReply::NetworkError code)
{
    //处理下载过程中的各种错误
    QString errorMsg;
    
    if (code == QNetworkReply::OperationCanceledError) {
        errorMsg = tr("Download cancelled");

    } else if (code == QNetworkReply::ContentNotFoundError) {
        errorMsg = tr("Error: File Not Found");

    } else {
        errorMsg = tr("Error during download. Error: %1").arg(code);
    }

    emit downloadComplete(_originalRemoteFile, QString(), errorMsg);
}

四.QGCLoggingCategory

这个类中干货很多,不仅有宏定义的一些使用技巧,还有宏定义中一些符号的使用方式。但是目前看代码还没了解这个类后边是如何被使用的,到后面看到的时候再提。头文件如下:

/****************************************************************************
 *
 * (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/

#pragma once

#include <QLoggingCategory>
#include <QStringList>

// Add Global logging categories (not class specific) here using Q_DECLARE_LOGGING_CATEGORY
// 在这里使用Q_DECLARE_LOGGING_CATEGORY添加全局日志分类(不是特定于类的)
Q_DECLARE_LOGGING_CATEGORY(FirmwareUpgradeLog)  //固件升级日志
Q_DECLARE_LOGGING_CATEGORY(FirmwareUpgradeVerboseLog) //固件升级长日志
Q_DECLARE_LOGGING_CATEGORY(MissionCommandsLog) //任务命令日志
Q_DECLARE_LOGGING_CATEGORY(MissionItemLog)  //任务项目日志
Q_DECLARE_LOGGING_CATEGORY(ParameterManagerLog) //参数管理日志
Q_DECLARE_LOGGING_CATEGORY(GeotaggingLog) //地理标签日志
Q_DECLARE_LOGGING_CATEGORY(RTKGPSLog) //RTKGPS日志
Q_DECLARE_LOGGING_CATEGORY(GuidedActionsControllerLog) //指导行动控制日志
Q_DECLARE_LOGGING_CATEGORY(ADSBVehicleManagerLog) //ADS-B[1]是广播式自动相关监视的英文缩写,它主要实施空对空监视
Q_DECLARE_LOGGING_CATEGORY(LocalizationLog) //定位日志
Q_DECLARE_LOGGING_CATEGORY(VideoAllLog) // turns on all individual QGC video logs 打开所有单独的QGC视频日志

/// @def QGC_LOGGING_CATEGORY
/// This is a QGC specific replacement for Q_LOGGING_CATEGORY. It will register the category name into a
/// global list. It's usage is the same as Q_LOGGING_CATEOGRY.
/// 这是Q_LOGGING_CATEGORY的一个特定于QGC的替换
/// 它将把类别名注册到一个全局列表中。 它的用法与q_logging_categorogry相同。
/// 关于define中 #和##的使用 https://blog.csdn.net/Damon_X/article/details/113343979  https://blog.csdn.net/auccy/article/details/88833659
/// 相当于:例如FirmwareUpgradeLog 等价于 static QGCLoggingCategory qgcCategoryFirmwareUpgradeLog (__VA_ARGS__); __VA_ARGS__ 指代可变参数列表
#define QGC_LOGGING_CATEGORY(name, ...) \
    static QGCLoggingCategory qgcCategory ## name (__VA_ARGS__); \
    Q_LOGGING_CATEGORY(name, __VA_ARGS__)

class QGCLoggingCategoryRegister : public QObject
{
    Q_OBJECT

public:
    //单例模式
    static QGCLoggingCategoryRegister* instance(void);

    /// Registers the specified logging category to the system.
    void registerCategory(const char* category) { _registeredCategories << category; }

    /// Returns the list of available logging category names.
    /// 将指定的日志记录类别注册到系统 注意Q_INVOKABLE
    Q_INVOKABLE QStringList registeredCategories(void);

    /// Turns on/off logging for the specified category. State is saved in app settings.
    /// 为指定的类别打开/关闭日志记录。 状态保存在应用程序设置中
    Q_INVOKABLE void setCategoryLoggingOn(const QString& category, bool enable);

    /// Returns true if logging is turned on for the specified category.
    /// 如果为指定类别打开了日志记录,则返回true。
    Q_INVOKABLE bool categoryLoggingOn(const QString& category);

    /// Sets the logging filters rules from saved settings.
    /// 从保存的设置设置日志过滤规则。
    ///     @param commandLineLogggingOptions Logging options which were specified on the command line
    /// commandLineLogggingOptions在命令行上指定的日志选项
    void setFilterRulesFromSettings(const QString& commandLineLoggingOptions);

private:
    QGCLoggingCategoryRegister(void) { }
    
    QStringList _registeredCategories;
    QString     _commandLineLoggingOptions;

    static const char* _filterRulesSettingsGroup;
};
        
class QGCLoggingCategory
{
public:
    QGCLoggingCategory(const char* category) { QGCLoggingCategoryRegister::instance()->registerCategory(category); }
};

cc文件如下:

/****************************************************************************
 *
 * (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/


/// @file
///     @author Don Gagne <don@thegagnes.com>

#include "QGCLoggingCategory.h"

#include <QSettings>

static const char* kVideoAllLogCategory = "VideoAllLog";

// Add Global logging categories (not class specific) here using QGC_LOGGING_CATEGORY
//在这里使用QGC_LOGGING_CATEGORY添加全局日志分类(不是特定于类的)
//例如第一行相当于生成了两个静态对象 qgcCategoryFirmwareUpgradeLog 和
//然后定义了一个方法
//     static QGCLoggingCategory qgcCategoryFirmwareUpgradeLog("FirmwareUpgradeLog");
//     const QLoggingCategory &FirmwareUpgradeLog()
//     {
//         static const QLoggingCategory category("FirmwareUpgradeLog");
//         return category;
//     }
QGC_LOGGING_CATEGORY(FirmwareUpgradeLog,            "FirmwareUpgradeLog")
QGC_LOGGING_CATEGORY(FirmwareUpgradeVerboseLog,     "FirmwareUpgradeVerboseLog")
QGC_LOGGING_CATEGORY(MissionCommandsLog,            "MissionCommandsLog")
QGC_LOGGING_CATEGORY(MissionItemLog,                "MissionItemLog")
QGC_LOGGING_CATEGORY(ParameterManagerLog,           "ParameterManagerLog")
QGC_LOGGING_CATEGORY(GeotaggingLog,                 "GeotaggingLog")
QGC_LOGGING_CATEGORY(RTKGPSLog,                     "RTKGPSLog")
QGC_LOGGING_CATEGORY(GuidedActionsControllerLog,    "GuidedActionsControllerLog")
QGC_LOGGING_CATEGORY(ADSBVehicleManagerLog,         "ADSBVehicleManagerLog")
QGC_LOGGING_CATEGORY(LocalizationLog,               "LocalizationLog")
QGC_LOGGING_CATEGORY(VideoAllLog,                   kVideoAllLogCategory)

QGCLoggingCategoryRegister* _instance = nullptr;
const char* QGCLoggingCategoryRegister::_filterRulesSettingsGroup = "LoggingFilters";

QGCLoggingCategoryRegister* QGCLoggingCategoryRegister::instance(void)
{
    if (!_instance) {
        _instance = new QGCLoggingCategoryRegister();
        Q_CHECK_PTR(_instance);//检查指针是否为空
    }
    
    return _instance;
}

QStringList QGCLoggingCategoryRegister::registeredCategories(void)
{
    //排序后返回
    _registeredCategories.sort();
    return _registeredCategories;
}

void QGCLoggingCategoryRegister::setCategoryLoggingOn(const QString& category, bool enable)
{
    QSettings settings;

    settings.beginGroup(_filterRulesSettingsGroup);
    settings.setValue(category, enable);
}

bool QGCLoggingCategoryRegister::categoryLoggingOn(const QString& category)
{
    QSettings settings;

    settings.beginGroup(_filterRulesSettingsGroup);
    return settings.value(category, false).toBool();
}

void QGCLoggingCategoryRegister::setFilterRulesFromSettings(const QString& commandLineLoggingOptions)
{
    QString filterRules;
    QString filterRuleFormat("%1.debug=true\n");
    bool    videoAllLogSet = false;

    if (!commandLineLoggingOptions.isEmpty()) {
        _commandLineLoggingOptions = commandLineLoggingOptions;
    }

    filterRules += "*Log.debug=false\n";

    // Set up filters defined in settings 设置设置中定义的过滤器
    foreach (QString category, _registeredCategories) {
        if (categoryLoggingOn(category)) {
            filterRules += filterRuleFormat.arg(category);
            if (category == kVideoAllLogCategory) {
                videoAllLogSet = true;
            }
        }
    }

    // Command line rules take precedence, so they go last in the list 命令行规则优先,因此它们在列表中位于最后
    if (!_commandLineLoggingOptions.isEmpty()) {
        QStringList logList = _commandLineLoggingOptions.split(",");

        if (logList[0] == "full") {
            filterRules += "*Log.debug=true\n";
            for(int i=1; i<logList.count(); i++) {
                filterRules += filterRuleFormat.arg(logList[i]);
            }
        } else {
            for (auto& category: logList) {
                filterRules += filterRuleFormat.arg(category);
                if (category == kVideoAllLogCategory) {
                    videoAllLogSet = true;
                }
            }
        }
    }

    if (videoAllLogSet) {
        filterRules += filterRuleFormat.arg("VideoManagerLog");
        filterRules += filterRuleFormat.arg("VideoReceiverLog");
        filterRules += filterRuleFormat.arg("GStreamerLog");
    }

    // Logging from GStreamer library itself controlled by gstreamer debug levels is always turned on
    // 由GStreamer调试级别控制的GStreamer库本身的日志记录总是打开的
    filterRules += filterRuleFormat.arg("GStreamerAPILog");

    filterRules += "qt.qml.connections=false";

    qDebug() << "Filter rules" << filterRules;
    QLoggingCategory::setFilterRules(filterRules);
}

五.QGCMapPalette

这个类主要是用于存储了两种颜色,然后针对地图是亮色还是暗色, 能够切换这个调色板使得地图上的绘制能够区别于地图背景。

头文件如下:

/****************************************************************************
 *
 * (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/


#ifndef QGCMapPalette_h
#define QGCMapPalette_h

#include <QObject>
#include <QColor>

/*!
 QGCMapPalette is a variant of QGCPalette which is used to hold colors used for display over
 the map control. Since the coloring of a satellite map differs greatly from the coloring of
 a street map you need to be able to switch between sets of color based on map type.

 QGCMapPalette是QGCPalette的一个变体,用来保存用于显示的颜色

 地图上的控制。 由于卫星地图的着色与街道地图的着色有很大的不同,你需要能够基于地图类型的颜色集切换。

 Usage:

        import QGroundControl.Palette 1.0

        FlightMap {
            id:             map
            anchors.fill:   parent

            QGCMapPalette {
                id: mapPal
                lightColors: map.isSatelliteMap  根据这个值来构造调色板
            }

            QGCLabel {
                text:   "Text over map"
                color:  mapPal.text
            }
        }
**/

class QGCMapPalette : public QObject
{
    Q_OBJECT
    
    //定义三个属性  这三个属性的改变都会触发paletteChanged信号
    Q_PROPERTY(bool lightColors READ lightColors WRITE setLightColors NOTIFY paletteChanged)
    Q_PROPERTY(QColor text          READ text          NOTIFY paletteChanged)
    Q_PROPERTY(QColor textOutline   READ textOutline   NOTIFY paletteChanged)

public:    
    QGCMapPalette(QObject* parent = nullptr);
    
    /// Text color
    /// 属性对应的读取函数 返回数组中对下标对应的颜色值 静态数组_text
    QColor text(void)           const { return _text[_lightColors ? 0 : 1]; }
    QColor textOutline(void)    const { return _textOutline[_lightColors ? 0 : 1]; }

    bool lightColors(void) const { return _lightColors; }
    void setLightColors(bool lightColors);    

signals:
    void paletteChanged(void);
    void lightColorsChanged(bool lightColors);
    
private:
    //是否是亮色
    bool _lightColors = false;

    //数组大小为2
    static const int _cColorGroups = 2;

    //静态数组_text
    static QColor _text[_cColorGroups];

    //静态数组_textOutline
    static QColor _textOutline[_cColorGroups];
};

#endif

cc文件如下:

#include "QGCMapPalette.h"

#include <QApplication>
#include <QPalette>

//定义两个静态数组
QColor QGCMapPalette::_text         [QGCMapPalette::_cColorGroups] = { QColor(255,255,255),     QColor(0,0,0) };
QColor QGCMapPalette::_textOutline  [QGCMapPalette::_cColorGroups] = { QColor(0,0,0,192),       QColor(255,255,255,192) };

QGCMapPalette::QGCMapPalette(QObject* parent) :
    QObject(parent)
{

}


void QGCMapPalette::setLightColors(bool lightColors)
{
    if ( _lightColors != lightColors) {
        _lightColors = lightColors;
        //前后设置的值不同触发信号
        emit paletteChanged();
    }
}

六.QGCPalette

这个类主要作用是,定义了全局调色板的功能,包括初始化所有主题对应的启用和停用和不同功能空间的颜色设置,对应的设置方法,获取方法,以及当主题发生更改后的消息通知给所有应用调色板的控件,基本上负责了整个应用主题更换,颜色更换的功能。这里文件注释也更加详尽。以下是头文件内用:

/****************************************************************************
 *
 * (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/

#ifndef QGCPalette_h
#define QGCPalette_h

#include <QObject>
#include <QColor>
#include <QMap>

//宏定义 定义一段函数
// 1.声明一个二维数组 第一维存储 light设置 和 dark设置 第二位存储这些设置对应的两个值
// 2.调用paletteOverride
// 3.设置_colorInfoMap 是一个三维数组 第一维两个元素分别是 Light和Dark 第二维ColorGroupEnabled和ColorGroupDisabled 第三维是#name的字符串内容
// 4.将#name存入_colors

#define DECLARE_QGC_COLOR(name, lightDisabled, lightEnabled, darkDisabled, darkEnabled) \
    { \
        PaletteColorInfo_t colorInfo = { \
            { QColor(lightDisabled), QColor(lightEnabled) }, \
            { QColor(darkDisabled), QColor(darkEnabled) } \
        }; \
        qgcApp()->toolbox()->corePlugin()->paletteOverride(#name, colorInfo); \
        _colorInfoMap[Light][ColorGroupEnabled][QStringLiteral(#name)] = colorInfo[Light][ColorGroupEnabled]; \
        _colorInfoMap[Light][ColorGroupDisabled][QStringLiteral(#name)] = colorInfo[Light][ColorGroupDisabled]; \
        _colorInfoMap[Dark][ColorGroupEnabled][QStringLiteral(#name)] = colorInfo[Dark][ColorGroupEnabled]; \
        _colorInfoMap[Dark][ColorGroupDisabled][QStringLiteral(#name)] = colorInfo[Dark][ColorGroupDisabled]; \
        _colors << #name; \
    }


//二维数组 第一维的两个元素相同 第二维分别是disabledColor和enabledColor
#define DECLARE_QGC_NONTHEMED_COLOR(name, disabledColor, enabledColor) \
    { \
        PaletteColorInfo_t colorInfo = { \
            { QColor(disabledColor), QColor(enabledColor) }, \
            { QColor(disabledColor), QColor(enabledColor) } \
        }; \
        qgcApp()->toolbox()->corePlugin()->paletteOverride(#name, colorInfo); \
        _colorInfoMap[Light][ColorGroupEnabled][QStringLiteral(#name)] = colorInfo[Light][ColorGroupEnabled]; \
        _colorInfoMap[Light][ColorGroupDisabled][QStringLiteral(#name)] = colorInfo[Light][ColorGroupDisabled]; \
        _colorInfoMap[Dark][ColorGroupEnabled][QStringLiteral(#name)] = colorInfo[Dark][ColorGroupEnabled]; \
        _colorInfoMap[Dark][ColorGroupDisabled][QStringLiteral(#name)] = colorInfo[Dark][ColorGroupDisabled]; \
        _colors << #name; \
    }


//colorInfo 所有元素相同
#define DECLARE_QGC_SINGLE_COLOR(name, color) \
    { \
        PaletteColorInfo_t colorInfo = { \
            { QColor(color), QColor(color) }, \
            { QColor(color), QColor(color) } \
        }; \
        qgcApp()->toolbox()->corePlugin()->paletteOverride(#name, colorInfo); \
        _colorInfoMap[Light][ColorGroupEnabled][QStringLiteral(#name)] = colorInfo[Light][ColorGroupEnabled]; \
        _colorInfoMap[Light][ColorGroupDisabled][QStringLiteral(#name)] = colorInfo[Light][ColorGroupDisabled]; \
        _colorInfoMap[Dark][ColorGroupEnabled][QStringLiteral(#name)] = colorInfo[Dark][ColorGroupEnabled]; \
        _colorInfoMap[Dark][ColorGroupDisabled][QStringLiteral(#name)] = colorInfo[Dark][ColorGroupDisabled]; \
        _colors << #name; \
    }

// 参数 name和setname
// 先定义两个变量 第一个变量是 QColor类型的  #name变量 第二个变量QStringList类型的 NAME ## Colors变量
// 定义 #name方法 (方法内容是返回_colorInfoMap对应下标的元素[风格][GroupEnabled / ColorGroupDisabled][#NAME])
// 定义 NAME ## Colors 方法 (方法内容是 向list添加 [亮][GroupEnabled][#NAME],[亮][GroupDisabled][#NAME],[暗][GroupEnabled][#NAME],[暗][GroupDisabled][#NAME])
// 定义 SETNAME方法 (方法内容是设置_colorInfoMap[风格][_colorGroupEnabled/ColorGroupDisabled][#NAME] = color 然后调用_signalPaletteChangeToAll())
#define DEFINE_QGC_COLOR(NAME, SETNAME) \
    Q_PROPERTY(QColor NAME READ NAME WRITE SETNAME NOTIFY paletteChanged) \
    Q_PROPERTY(QStringList NAME ## Colors READ NAME ## Colors NOTIFY paletteChanged) \
    QColor NAME() const { return _colorInfoMap[_theme][_colorGroupEnabled  ? ColorGroupEnabled : ColorGroupDisabled][QStringLiteral(#NAME)]; } \
    QStringList NAME ## Colors() const { \
        QStringList c; \
        c << _colorInfoMap[Light][ColorGroupEnabled][QStringLiteral(#NAME)].name(QColor::HexRgb); \
        c << _colorInfoMap[Light][ColorGroupDisabled][QStringLiteral(#NAME)].name(QColor::HexRgb); \
        c << _colorInfoMap[Dark][ColorGroupEnabled][QStringLiteral(#NAME)].name(QColor::HexRgb); \
        c << _colorInfoMap[Dark][ColorGroupDisabled][QStringLiteral(#NAME)].name(QColor::HexRgb); \
        return c; \
    } \
    void SETNAME(const QColor& color) { _colorInfoMap[_theme][_colorGroupEnabled  ? ColorGroupEnabled : ColorGroupDisabled][QStringLiteral(#NAME)] = color; _signalPaletteChangeToAll(); }

/*!
 QGCPalette is used in QML ui to expose color properties for the QGC palette. There are two
 separate palettes in QGC, light and dark. The light palette is for outdoor use and the dark
 palette is for indoor use. Each palette also has a set of different colors for enabled and
 disabled states.


QGCPalette在QML ui中用于为QGC调色板暴露颜色属性。
有两个QGC中单独的调色板,亮和暗。
浅色调色板适用于户外,深色调色板适用于室内。
每个调色板也有一组用于启用和停用的不同颜色
禁用状态。

 Usage:

        import QGroundControl.Palette 1.0

        Rectangle {
            anchors.fill:   parent
            color:          qgcPal.window

            QGCPalette {
                id: qgcPal
                colorGroupEnabled: enabled
            }
        }
*/

class QGCPalette : public QObject
{
    Q_OBJECT

public:
    enum ColorGroup {
        ColorGroupDisabled = 0,
        ColorGroupEnabled,
        cMaxColorGroup
    };

    enum Theme {
        Light = 0,
        Dark,
        cMaxTheme
    };
    Q_ENUM(Theme)

    typedef QColor PaletteColorInfo_t[cMaxTheme][cMaxColorGroup];

    //定义三个属性globalTheme colorGroupEnabled colors
    Q_PROPERTY(Theme        globalTheme         READ globalTheme        WRITE setGlobalTheme        NOTIFY paletteChanged)
    Q_PROPERTY(bool         colorGroupEnabled   READ colorGroupEnabled  WRITE setColorGroupEnabled  NOTIFY paletteChanged)
    Q_PROPERTY(QStringList  colors              READ colors             CONSTANT)


    //按照前面对DEFINE_QGC_COLOR的分析,第一行代码相当于
    //1.定义了两个属性 分别是window 和 windowColors
    //2.定义window方法 方法内容是返回_colorInfoMap对应下标的元素[当前风格][GroupEnabled / ColorGroupDisabled][window] 对应的color类
    //3.定义windowColors方法,(方法内容是 向list添加 [亮][GroupEnabled][window],[亮][GroupDisabled][window],[暗][GroupEnabled][window],[暗][GroupDisabled][window])
    //4.定义SETwindow方法 (方法内容是设置_colorInfoMap[风格][_colorGroupEnabled/ColorGroupDisabled][#NAME] = color 然后调用_signalPaletteChangeToAll())
    DEFINE_QGC_COLOR(window,                        setWindow)
    DEFINE_QGC_COLOR(windowShadeLight,              setWindowShadeLight)
    DEFINE_QGC_COLOR(windowShade,                   setWindowShade)
    DEFINE_QGC_COLOR(windowShadeDark,               setWindowShadeDark)
    DEFINE_QGC_COLOR(text,                          setText)
    DEFINE_QGC_COLOR(warningText,                   setWarningText)
    DEFINE_QGC_COLOR(button,                        setButton)
    DEFINE_QGC_COLOR(buttonText,                    setButtonText)
    DEFINE_QGC_COLOR(buttonHighlight,               setButtonHighlight)
    DEFINE_QGC_COLOR(buttonHighlightText,           setButtonHighlightText)
    DEFINE_QGC_COLOR(primaryButton,                 setPrimaryButton)
    DEFINE_QGC_COLOR(primaryButtonText,             setPrimaryButtonText)
    DEFINE_QGC_COLOR(textField,                     setTextField)
    DEFINE_QGC_COLOR(textFieldText,                 setTextFieldText)
    DEFINE_QGC_COLOR(mapButton,                     setMapButton)
    DEFINE_QGC_COLOR(mapButtonHighlight,            setMapButtonHighlight)
    DEFINE_QGC_COLOR(mapIndicator,                  setMapIndicator)
    DEFINE_QGC_COLOR(mapIndicatorChild,             setMapIndicatorChild)
    DEFINE_QGC_COLOR(mapWidgetBorderLight,          setMapWidgetBorderLight)
    DEFINE_QGC_COLOR(mapWidgetBorderDark,           setMapWidgetBorderDark)
    DEFINE_QGC_COLOR(mapMissionTrajectory,          setMapMissionTrajectory)
    DEFINE_QGC_COLOR(brandingPurple,                setBrandingPurple)
    DEFINE_QGC_COLOR(brandingBlue,                  setBrandingBlue)
    DEFINE_QGC_COLOR(colorGreen,                    setColorGreen)
    DEFINE_QGC_COLOR(colorOrange,                   setColorOrange)
    DEFINE_QGC_COLOR(colorRed,                      setColorRed)
    DEFINE_QGC_COLOR(colorGrey,                     setColorGrey)
    DEFINE_QGC_COLOR(colorBlue,                     setColorBlue)
    DEFINE_QGC_COLOR(alertBackground,               setAlertBackground)
    DEFINE_QGC_COLOR(alertBorder,                   setAlertBorder)
    DEFINE_QGC_COLOR(alertText,                     setAlertText)
    DEFINE_QGC_COLOR(missionItemEditor,             setMissionItemEditor)
    DEFINE_QGC_COLOR(statusFailedText,              setstatusFailedText)
    DEFINE_QGC_COLOR(statusPassedText,              setstatusPassedText)
    DEFINE_QGC_COLOR(statusPendingText,             setstatusPendingText)
    DEFINE_QGC_COLOR(surveyPolygonInterior,         setSurveyPolygonInterior)
    DEFINE_QGC_COLOR(surveyPolygonTerrainCollision, setSurveyPolygonTerrainCollision)
    DEFINE_QGC_COLOR(toolbarBackground,             setToolbarBackground)
    DEFINE_QGC_COLOR(toolStripFGColor,              setToolStripFGColor)
    DEFINE_QGC_COLOR(toolStripHoverColor,           setToolStripHoverColor)

     QGCPalette(QObject* parent = nullptr);
    ~QGCPalette();

    QStringList colors                      () const { return _colors; }
    bool        colorGroupEnabled           () const { return _colorGroupEnabled; }
    void        setColorGroupEnabled        (bool enabled);

    static Theme    globalTheme             () { return _theme; }
    static void     setGlobalTheme          (Theme newTheme);

signals:
    void paletteChanged ();

private:
    //创建map
    static void _buildMap                   ();
    static void _signalPaletteChangeToAll   ();
    void        _signalPaletteChanged       ();
    void        _themeChanged               ();

    static Theme                _theme;             ///< There is a single theme for all palettes 所有调色板都有一个单一的主题
    bool                        _colorGroupEnabled; ///< Currently selected ColorGroup. true: enabled, false: disabled 当前选中ColorGroup。 True:启用,false:禁用
    static QStringList          _colors;

    static QMap<int, QMap<int, QMap<QString, QColor>>> _colorInfoMap;   // theme -> colorGroup -> color name -> color
    static QList<QGCPalette*> _paletteObjects;    ///< List of all active QGCPalette objects 所有活动QGCPalette对象的列表
};

#endif

cc文件如下:

/****************************************************************************
 *
 * (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/


/// @file
///     @author Don Gagne <don@thegagnes.com>

#include "QGCPalette.h"
#include "QGCApplication.h"
#include "QGCCorePlugin.h"

#include <QApplication>
#include <QPalette>

//变脸赋值
QList<QGCPalette*>   QGCPalette::_paletteObjects;

//默认dark
QGCPalette::Theme QGCPalette::_theme = QGCPalette::Dark;

QMap<int, QMap<int, QMap<QString, QColor>>> QGCPalette::_colorInfoMap;

QStringList QGCPalette::_colors;

QGCPalette::QGCPalette(QObject* parent) :
    QObject(parent),
    _colorGroupEnabled(true)
{
    if (_colorInfoMap.isEmpty()) {
        _buildMap();
    }

    // We have to keep track of all QGCPalette objects in the system so we can signal theme change to all of them
    // 我们必须跟踪系统中的所有QGCPalette对象,以便向所有对象发出主题更改的信号
    _paletteObjects += this;
}

QGCPalette::~QGCPalette()
{
    // 清除QGCPalette对象 这样当主题更改的时,信号将不会发送到这里
    bool fSuccess = _paletteObjects.removeOne(this);
    if (!fSuccess) {
        qWarning() << "Internal error";
    }
}

void QGCPalette::_buildMap()
{
    // 构建_colorInfoMap和_colors
    //                                      Light                 Dark
    //                                      Disabled   Enabled    Disabled   Enabled
    DECLARE_QGC_COLOR(window,               "#ffffff", "#ffffff", "#222222", "#222222")
    DECLARE_QGC_COLOR(windowShadeLight,     "#909090", "#828282", "#707070", "#626262")
    DECLARE_QGC_COLOR(windowShade,          "#d9d9d9", "#d9d9d9", "#333333", "#333333")
    DECLARE_QGC_COLOR(windowShadeDark,      "#bdbdbd", "#bdbdbd", "#282828", "#282828")
    DECLARE_QGC_COLOR(text,                 "#9d9d9d", "#000000", "#707070", "#ffffff")
    DECLARE_QGC_COLOR(warningText,          "#cc0808", "#cc0808", "#f85761", "#f85761")
    DECLARE_QGC_COLOR(button,               "#ffffff", "#ffffff", "#707070", "#626270")
    DECLARE_QGC_COLOR(buttonText,           "#9d9d9d", "#000000", "#A6A6A6", "#ffffff")
    DECLARE_QGC_COLOR(buttonHighlight,      "#e4e4e4", "#946120", "#3a3a3a", "#fff291")
    DECLARE_QGC_COLOR(buttonHighlightText,  "#2c2c2c", "#ffffff", "#2c2c2c", "#000000")
    DECLARE_QGC_COLOR(primaryButton,        "#585858", "#8cb3be", "#585858", "#8cb3be")
    DECLARE_QGC_COLOR(primaryButtonText,    "#2c2c2c", "#000000", "#2c2c2c", "#000000")
    DECLARE_QGC_COLOR(textField,            "#ffffff", "#ffffff", "#707070", "#ffffff")
    DECLARE_QGC_COLOR(textFieldText,        "#808080", "#000000", "#000000", "#000000")
    DECLARE_QGC_COLOR(mapButton,            "#585858", "#000000", "#585858", "#000000")
    DECLARE_QGC_COLOR(mapButtonHighlight,   "#585858", "#be781c", "#585858", "#be781c")
    DECLARE_QGC_COLOR(mapIndicator,         "#585858", "#be781c", "#585858", "#be781c")
    DECLARE_QGC_COLOR(mapIndicatorChild,    "#585858", "#766043", "#585858", "#766043")
    DECLARE_QGC_COLOR(colorGreen,           "#009431", "#009431", "#00e04b", "#00e04b")
    DECLARE_QGC_COLOR(colorOrange,          "#b95604", "#b95604", "#de8500", "#de8500")
    DECLARE_QGC_COLOR(colorRed,             "#ed3939", "#ed3939", "#f32836", "#f32836")
    DECLARE_QGC_COLOR(colorGrey,            "#808080", "#808080", "#bfbfbf", "#bfbfbf")
    DECLARE_QGC_COLOR(colorBlue,            "#1a72ff", "#1a72ff", "#536dff", "#536dff")
    DECLARE_QGC_COLOR(alertBackground,      "#eecc44", "#eecc44", "#eecc44", "#eecc44")
    DECLARE_QGC_COLOR(alertBorder,          "#808080", "#808080", "#808080", "#808080")
    DECLARE_QGC_COLOR(alertText,            "#000000", "#000000", "#000000", "#000000")
    DECLARE_QGC_COLOR(missionItemEditor,    "#585858", "#dbfef8", "#585858", "#585d83")
    DECLARE_QGC_COLOR(toolStripHoverColor,  "#585858", "#9D9D9D", "#585858", "#585d83")
    DECLARE_QGC_COLOR(statusFailedText,     "#9d9d9d", "#000000", "#707070", "#ffffff")
    DECLARE_QGC_COLOR(statusPassedText,     "#9d9d9d", "#000000", "#707070", "#ffffff")
    DECLARE_QGC_COLOR(statusPendingText,    "#9d9d9d", "#000000", "#707070", "#ffffff")
    DECLARE_QGC_COLOR(toolbarBackground,    "#ffffff", "#ffffff", "#222222", "#222222")

    // Colors not affecting by theming 不受主题影响的颜色
    //                                              Disabled    Enabled
    DECLARE_QGC_NONTHEMED_COLOR(brandingPurple,     "#4A2C6D", "#4A2C6D")
    DECLARE_QGC_NONTHEMED_COLOR(brandingBlue,       "#48D6FF", "#6045c5")
    DECLARE_QGC_NONTHEMED_COLOR(toolStripFGColor,   "#707070", "#ffffff")

    // Colors not affecting by theming or enable/disable 不受主题或启用/禁用影响的颜色
    DECLARE_QGC_SINGLE_COLOR(mapWidgetBorderLight,          "#ffffff")
    DECLARE_QGC_SINGLE_COLOR(mapWidgetBorderDark,           "#000000")
    DECLARE_QGC_SINGLE_COLOR(mapMissionTrajectory,          "#be781c")
    DECLARE_QGC_SINGLE_COLOR(surveyPolygonInterior,         "green")
    DECLARE_QGC_SINGLE_COLOR(surveyPolygonTerrainCollision, "red")
}


//对外接口 设置ColorGroupEnabled
void QGCPalette::setColorGroupEnabled(bool enabled)
{
    _colorGroupEnabled = enabled;
    emit paletteChanged();
}

//对外接口 设置GlobalTheme
void QGCPalette::setGlobalTheme(Theme newTheme)
{
    // Mobile build does not have themes
    if (_theme != newTheme) {
        _theme = newTheme;
        _signalPaletteChangeToAll();
    }
}

//将主题发生变化的信号发送给所有注册的对象
void QGCPalette::_signalPaletteChangeToAll()
{
    // Notify all objects of the new theme 通知新主题的所有对象
    foreach (QGCPalette* palette, _paletteObjects) {
        palette->_signalPaletteChanged();
    }
}

void QGCPalette::_signalPaletteChanged()
{
    emit paletteChanged();
}

七.QGCQGeoCoordinate

此类主要定义了一个继承自QObject的QGeoCoordinate,它可以在QmlObjectListModel上使用。

代码如下:

/****************************************************************************
 *
 * (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/

#pragma once

#include <QObject>
#include <QGeoCoordinate>

/// This is a QGeoCoordinate within a QObject such that it can be used on a QmlObjectListModel
/// 这是一个继承自QObject的QGeoCoordinate,它可以在QmlObjectListModel上使用
class QGCQGeoCoordinate : public QObject
{
    Q_OBJECT

public:
    QGCQGeoCoordinate(const QGeoCoordinate& coord, QObject* parent = nullptr);

    //定义coordinate属性 和set get  和信号 函数
    Q_PROPERTY(QGeoCoordinate   coordinate  READ coordinate WRITE setCoordinate NOTIFY coordinateChanged)

    //脏数据
    Q_PROPERTY(bool             dirty       READ dirty      WRITE setDirty      NOTIFY dirtyChanged)

    QGeoCoordinate  coordinate      (void) const { return _coordinate; }
    void            setCoordinate   (const QGeoCoordinate& coordinate);
    bool            dirty           (void) const { return _dirty; }
    void            setDirty        (bool dirty);

signals:
    void coordinateChanged  (QGeoCoordinate coordinate);
    void dirtyChanged       (bool dirty);

private:
    QGeoCoordinate  _coordinate;
    bool            _dirty;
};
/****************************************************************************
 *
 * (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/

#include "QGCQGeoCoordinate.h"

#include <QQmlEngine>

QGCQGeoCoordinate::QGCQGeoCoordinate(const QGeoCoordinate& coord, QObject* parent)
    : QObject       (parent)
    , _coordinate   (coord)
    , _dirty        (false)
{
    //设置该对象的从属关系是cpp而不是qml https://blog.csdn.net/baidu_41388533/article/details/116973961
    QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
}

void QGCQGeoCoordinate::setCoordinate(const QGeoCoordinate& coordinate)
{
    //不相等则修改
    if (_coordinate != coordinate) {
        _coordinate = coordinate;
        emit coordinateChanged(coordinate);
        setDirty(true);
    }
}

void QGCQGeoCoordinate::setDirty(bool dirty)
{
    if (_dirty != dirty) {
        _dirty = dirty;
        emit dirtyChanged(dirty);
    }
}

八.QGCTemporaryFile

该类主要模仿QTemporaryFile,主要用于创建临时文件,打开临时文件,删除临时文件。代码如下:

/****************************************************************************
 *
 * (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/

#pragma once

#include <QFile>

/// @file
///     @brief This class mimics QTemporaryFile. We have our own implementation due to the fact that
///				QTemporaryFile implemenation differs cross platform making it unusable for our use-case.
///				Look for bug reports on QTemporaryFile keeping the file locked for details.
///    这个类模仿QTemporaryFile。 我们有自己的实现,因为 QTemporaryFile在跨平台上的实现是不同的,这使得它在我们的用例中不可用。 详细信息请查看QTemporaryFile上的bug报告。
///     @author Don Gagne <don@thegagnes.com>

class QGCTemporaryFile : public QFile {
    Q_OBJECT
    
public:
    /// @brief Creates a new temp file object. QGC temp files are always created in the
    ///			QStandardPaths::TempLocation directory.
    ///         创建一个新的临时文件对象。 QGC临时文件总是创建在QStandardPaths::TempLocation目录中。
    ///	@param template Template for file name following QTemporaryFile rules. Template should NOT include
    ///							directory path, only file name.
    ///         QTemporaryFile规则下的文件名模板。 模板不应包含  目录路径,只有文件名。
    QGCTemporaryFile(const QString& fileTemplate, QObject* parent = nullptr);

    ~QGCTemporaryFile();

	bool open(OpenMode openMode = ReadWrite);

    void setAutoRemove(bool autoRemove) { _autoRemove = autoRemove; }
    
private:
    static QString _newTempFileFullyQualifiedName(const QString& fileTemplate);

    QString _template;
    bool    _autoRemove = false;
};
/****************************************************************************
 *
 * (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/


/// @file
///     @brief This class mimics QTemporaryFile. We have our own implementation due to the fact that
///				QTemporaryFile implemenation differs cross platform making it unusable for our use-case.
///				Look for bug reports on QTemporaryFile keeping the file locked for details.
///
///     @author Don Gagne <don@thegagnes.com>

#include "QGCTemporaryFile.h"

#include <QDir>
#include <QRandomGenerator>
#include <QStandardPaths>

QGCTemporaryFile::QGCTemporaryFile(const QString& fileTemplate, QObject* parent) :
    QFile(parent),
    _template(fileTemplate)
{

}

QGCTemporaryFile::~QGCTemporaryFile()
{
    //在析构函数中,判断属性_autoRemove 如果是这自动删除文件
    if (_autoRemove) {
        remove();
    }
}

bool QGCTemporaryFile::open(QFile::OpenMode openMode)
{
    //设置名称
    setFileName(_newTempFileFullyQualifiedName(_template));
    return QFile::open(openMode);
}

QString QGCTemporaryFile::_newTempFileFullyQualifiedName(const QString& fileTemplate)
{
    QString nameTemplate = fileTemplate;
    //创建文件夹 QStandardPaths::TempLocation
    QDir tempDir(QStandardPaths::writableLocation(QStandardPaths::TempLocation));

    // Generate unique, non-existing filename  生成惟一的、不存在的文件名

    static const char rgDigits[] = "0123456789";

    QString tempFilename;

    do {
        QString uniqueStr;
        for (int i=0; i<6; i++) {
            uniqueStr += rgDigits[QRandomGenerator::global()->generate() % 10];
        }

        if (fileTemplate.contains("XXXXXX")) {
            tempFilename = nameTemplate.replace("XXXXXX", uniqueStr, Qt::CaseSensitive);
        } else {
            tempFilename = nameTemplate + uniqueStr;
        }
    } while (tempDir.exists(tempFilename));

    return tempDir.filePath(tempFilename);
}

九.QGCToolbox

此类管理所有的上层服务和插件,提供了QGCToolbox和QGCTool,所有的插件和服务都继承自QGCTool,QGCTool又继承自QObject,但在构造时构造父类的时候传入了QGCToolbox。QGCToolbox在构造时创建了所有插件和服务。

我的理解是,首先QGCToolbox被构建,在构造函数中,

例如,当我执行第一个 SettingsManager的创建时,SettingsManager的构造函数被执行。

 构造函数中调用了QGCTool,和一些初始化操作。QGCTool的构造函数具体做了什么。

 主要看初始化父类这里,将toolbox设置为了QGCTool的父类。于是这里我的理解是:QGCToolBox首先拥有所有的插件和服务,然后这些插件和服务的父类又指向QGCToolBox,第二步骤在QGCToolBox完成初始后,再调用setToolbox函数设置toolbox,从而实现每个插件和服务是可以获取其他插件和服务的。下面放出代码:

/****************************************************************************
 *
 * (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/


#ifndef QGCToolbox_h
#define QGCToolbox_h

#include <QObject>

class FactSystem; //自动校准系统
class FirmwarePluginManager; //固件插件管理
class AudioOutput; //音频输出
class GPSManager; //gps管理
class JoystickManager; //手柄管理
class FollowMe;  //跟随我
class LinkManager; //连接管理
class MAVLinkProtocol; //mavlink协议
class MissionCommandTree; //任务命令管理
class MultiVehicleManager; //多设备管理
class QGCMapEngineManager; //地图引擎管理
class QGCApplication; //qgc应用
class QGCImageProvider; //图像提供者
class UASMessageHandler; //网络无人机系操作类
class QGCPositionManager; //位置管理
class VideoManager; //视频管理
class MAVLinkLogManager; //mavlink协议管理
class QGCCorePlugin; //核心插件
class SettingsManager; //设置管理
class AirspaceManager; //空域管理
class ADSBVehicleManager; //空对空监视设备管理

#if defined(QGC_ENABLE_PAIRING)
class PairingManager;
#endif
#if defined(QGC_GST_TAISYNC_ENABLED)
class TaisyncManager;
#endif
#if defined(QGC_GST_MICROHARD_ENABLED)
class MicrohardManager;
#endif

/// This is used to manage all of our top level services/tools
/// 这用于管理我们所有的上层服务/工具
class QGCToolbox : public QObject {
    Q_OBJECT

public:
    QGCToolbox(QGCApplication* app);

    //定义公有接口
    FirmwarePluginManager*      firmwarePluginManager   () { return _firmwarePluginManager; }
    AudioOutput*                audioOutput             () { return _audioOutput; }
    JoystickManager*            joystickManager         () { return _joystickManager; }
    LinkManager*                linkManager             () { return _linkManager; }
    MAVLinkProtocol*            mavlinkProtocol         () { return _mavlinkProtocol; }
    MissionCommandTree*         missionCommandTree      () { return _missionCommandTree; }
    MultiVehicleManager*        multiVehicleManager     () { return _multiVehicleManager; }
    QGCMapEngineManager*        mapEngineManager        () { return _mapEngineManager; }
    QGCImageProvider*           imageProvider           () { return _imageProvider; }
    UASMessageHandler*          uasMessageHandler       () { return _uasMessageHandler; }
    FollowMe*                   followMe                () { return _followMe; }
    QGCPositionManager*         qgcPositionManager      () { return _qgcPositionManager; }
    VideoManager*               videoManager            () { return _videoManager; }
    MAVLinkLogManager*          mavlinkLogManager       () { return _mavlinkLogManager; }
    QGCCorePlugin*              corePlugin              () { return _corePlugin; }
    SettingsManager*            settingsManager         () { return _settingsManager; }
    AirspaceManager*            airspaceManager         () { return _airspaceManager; }
    ADSBVehicleManager*         adsbVehicleManager      () { return _adsbVehicleManager; }
#if defined(QGC_ENABLE_PAIRING)
    PairingManager*             pairingManager          () { return _pairingManager; }
#endif
#ifndef __mobile__
    GPSManager*                 gpsManager              () { return _gpsManager; }
#endif
#if defined(QGC_GST_TAISYNC_ENABLED)
    TaisyncManager*             taisyncManager          () { return _taisyncManager; }
#endif
#if defined(QGC_GST_MICROHARD_ENABLED)
    MicrohardManager*           microhardManager        () { return _microhardManager; }
#endif

private:

    //设置子toolbox
    void setChildToolboxes(void);

    //扫描和加载插件
    void _scanAndLoadPlugins(QGCApplication *app);


    //定义私有变量
    AudioOutput*                _audioOutput            = nullptr;
    FactSystem*                 _factSystem             = nullptr;
    FirmwarePluginManager*      _firmwarePluginManager  = nullptr;
#ifndef __mobile__
    GPSManager*                 _gpsManager             = nullptr;
#endif
    QGCImageProvider*           _imageProvider          = nullptr;
    JoystickManager*            _joystickManager        = nullptr;
    LinkManager*                _linkManager            = nullptr;
    MAVLinkProtocol*            _mavlinkProtocol        = nullptr;
    MissionCommandTree*         _missionCommandTree     = nullptr;
    MultiVehicleManager*        _multiVehicleManager    = nullptr;
    QGCMapEngineManager*        _mapEngineManager       = nullptr;
    UASMessageHandler*          _uasMessageHandler      = nullptr;
    FollowMe*                   _followMe               = nullptr;
    QGCPositionManager*         _qgcPositionManager     = nullptr;
    VideoManager*               _videoManager           = nullptr;
    MAVLinkLogManager*          _mavlinkLogManager      = nullptr;
    QGCCorePlugin*              _corePlugin             = nullptr;
    SettingsManager*            _settingsManager        = nullptr;
    AirspaceManager*            _airspaceManager        = nullptr;
    ADSBVehicleManager*         _adsbVehicleManager     = nullptr;
#if defined(QGC_ENABLE_PAIRING)
    PairingManager*             _pairingManager         = nullptr;
#endif
#if defined(QGC_GST_TAISYNC_ENABLED)
    TaisyncManager*             _taisyncManager         = nullptr;
#endif
#if defined(QGC_GST_MICROHARD_ENABLED)
    MicrohardManager*           _microhardManager       = nullptr;
#endif

    //定义友元类
    friend class QGCApplication;
};

/// This is the base class for all tools
/// 这是所有工具类的基类
class QGCTool : public QObject {
    Q_OBJECT

public:
    // All tools must be parented to the QGCToolbox and go through a two phase creation. In the constructor the toolbox
    // should only be passed to QGCTool constructor for correct parenting. It should not be referenced or set in the
    // protected member. Then in the second phase of setToolbox calls is where you can reference the toolbox.、
    //所有的工具都必须被父级导入到QGCToolbox中,并经过两个阶段的创建。
    //在构造函数中,工具箱应该只传递给QGCTool构造函数,以便进行正确的加载。
    //不应该在受保护的成员中引用或设置它。 然后,在setToolbox调用的第二阶段是可以引用工具箱的地方。
    QGCTool(QGCApplication* app, QGCToolbox* toolbox);

    // If you override this method, you must call the base class.
    // 如果重写这个方法你必须调用这个基类方法
    virtual void setToolbox(QGCToolbox* toolbox);

protected:
    QGCApplication* _app;
    QGCToolbox*     _toolbox;
};

#endif
 /****************************************************************************
 *
 * (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/


#include "FactSystem.h"
#include "FirmwarePluginManager.h"
#include "AudioOutput.h"
#ifndef __mobile__
#include "GPSManager.h"
#endif
#include "JoystickManager.h"
#include "LinkManager.h"
#include "MAVLinkProtocol.h"
#include "MissionCommandTree.h"
#include "MultiVehicleManager.h"
#include "QGCImageProvider.h"
#include "UASMessageHandler.h"
#include "QGCMapEngineManager.h"
#include "FollowMe.h"
#include "PositionManager.h"
#include "VideoManager.h"
#include "MAVLinkLogManager.h"
#include "QGCCorePlugin.h"
#include "QGCOptions.h"
#include "SettingsManager.h"
#include "QGCApplication.h"
#include "ADSBVehicleManager.h"

#if defined(QGC_ENABLE_PAIRING)
#include "PairingManager.h"
#endif
#if defined(QGC_AIRMAP_ENABLED)
#include "AirMapManager.h"
#else
#include "AirspaceManager.h"
#endif
#if defined(QGC_GST_TAISYNC_ENABLED)
#include "TaisyncManager.h"
#endif
#if defined(QGC_GST_MICROHARD_ENABLED)
#include "MicrohardManager.h"
#endif

#if defined(QGC_CUSTOM_BUILD)
#include CUSTOMHEADER
#endif

QGCToolbox::QGCToolbox(QGCApplication* app)
{
    // SettingsManager must be first so settings are available to any subsequent tools
    // SettingsManager必须是第一个,这样之后的任何工具都可以使用设置
    // 以下这些类都继承自QGCTool
    _settingsManager        = new SettingsManager           (app, this);
    //-- Scan and load plugins
    _scanAndLoadPlugins(app);
    _audioOutput            = new AudioOutput               (app, this);
    _factSystem             = new FactSystem                (app, this);
    _firmwarePluginManager  = new FirmwarePluginManager     (app, this);
#ifndef __mobile__
    _gpsManager             = new GPSManager                (app, this);
#endif
    _imageProvider          = new QGCImageProvider          (app, this);
    _joystickManager        = new JoystickManager           (app, this);
    _linkManager            = new LinkManager               (app, this);
    _mavlinkProtocol        = new MAVLinkProtocol           (app, this);
    _missionCommandTree     = new MissionCommandTree        (app, this);
    _multiVehicleManager    = new MultiVehicleManager       (app, this);
    _mapEngineManager       = new QGCMapEngineManager       (app, this);
    _uasMessageHandler      = new UASMessageHandler         (app, this);
    _qgcPositionManager     = new QGCPositionManager        (app, this);
    _followMe               = new FollowMe                  (app, this);
    _videoManager           = new VideoManager              (app, this);
    _mavlinkLogManager      = new MAVLinkLogManager         (app, this);
    _adsbVehicleManager     = new ADSBVehicleManager        (app, this);
#if defined(QGC_ENABLE_PAIRING)
    _pairingManager         = new PairingManager            (app, this);
#endif
    //-- Airmap Manager
    //-- This should be "pluggable" so an arbitrary AirSpace manager can be used 他应该是“可插拔的”,这样就可以使用任意的空域管理器
    //-- For now, we instantiate the one and only AirMap provider 现在,我们实例化唯一的AirMap提供程序
#if defined(QGC_AIRMAP_ENABLED)
    _airspaceManager        = new AirMapManager             (app, this);
#else
    _airspaceManager        = new AirspaceManager           (app, this);
#endif
#if defined(QGC_GST_TAISYNC_ENABLED)
    _taisyncManager         = new TaisyncManager            (app, this);
#endif
#if defined(QGC_GST_MICROHARD_ENABLED)
    _microhardManager       = new MicrohardManager          (app, this);
#endif
}

void QGCToolbox::setChildToolboxes(void)
{
    // SettingsManager must be first so settings are available to any subsequent tools
    _settingsManager->setToolbox(this);

    _corePlugin->setToolbox(this);
    _audioOutput->setToolbox(this);
    _factSystem->setToolbox(this);
    _firmwarePluginManager->setToolbox(this);
#ifndef __mobile__
    _gpsManager->setToolbox(this);
#endif
    _imageProvider->setToolbox(this);
    _joystickManager->setToolbox(this);
    _linkManager->setToolbox(this);
    _mavlinkProtocol->setToolbox(this);
    _missionCommandTree->setToolbox(this);
    _multiVehicleManager->setToolbox(this);
    _mapEngineManager->setToolbox(this);
    _uasMessageHandler->setToolbox(this);
    _followMe->setToolbox(this);
    _qgcPositionManager->setToolbox(this);
    _videoManager->setToolbox(this);
    _mavlinkLogManager->setToolbox(this);
    _airspaceManager->setToolbox(this);
    _adsbVehicleManager->setToolbox(this);
#if defined(QGC_GST_TAISYNC_ENABLED)
    _taisyncManager->setToolbox(this);
#endif
#if defined(QGC_GST_MICROHARD_ENABLED)
    _microhardManager->setToolbox(this);
#endif
#if defined(QGC_ENABLE_PAIRING)
    _pairingManager->setToolbox(this);
#endif
}

void QGCToolbox::_scanAndLoadPlugins(QGCApplication* app)
{
 //利用宏定义加载插件
#if defined (QGC_CUSTOM_BUILD)
    //-- Create custom plugin (Static)
    _corePlugin = (QGCCorePlugin*) new CUSTOMCLASS(app, this);
    if(_corePlugin) {
        return;
    }
#endif
    //-- No plugins found, use default instance
    _corePlugin = new QGCCorePlugin(app, this);
}

QGCTool::QGCTool(QGCApplication* app, QGCToolbox* toolbox)
    : QObject(toolbox)
    , _app(app)
    , _toolbox(nullptr)
{
}

void QGCTool::setToolbox(QGCToolbox* toolbox)
{
    _toolbox = toolbox;
}

十.RunGuard

这个类主要是对共享内存的操作,感觉和guard守卫的意思没多大关系,整个代码都是在介绍对共享内存的申请,绑定,释放还有锁和随机key的生成这些操作,具体代码的注释已经放在代码中。

/****************************************************************************
 *
 * (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/

#ifndef RunGuard_H
#define RunGuard_H

#include <QObject>
#include <QSharedMemory>
#include <QSystemSemaphore>

//顾名思义 运行守卫
class RunGuard
{
public:
    //构造函数用key创建
    RunGuard( const QString& key );
    ~RunGuard();

    //是否是另外的运行程序
    bool isAnotherRunning();

    //尝试运行
    bool tryToRun();

    //释放
    void release();

private:
    //键
    const QString key;

    //内存锁键
    const QString memLockKey;

    //共享内存键
    const QString sharedmemKey;

    //共享内存
    QSharedMemory sharedMem;

    //内存锁
    QSystemSemaphore memLock;

    //Q_DISABLE_COPY宏作用是禁止对给定的类使用复制构造函数和赋值运算符。
    Q_DISABLE_COPY( RunGuard )
};

#endif
/****************************************************************************
 *
 * (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/

#include "RunGuard.h"

#include <QCryptographicHash>

namespace
{

//key生成函数
QString generateKeyHash( const QString& key, const QString& salt )
{
    QByteArray data;

    data.append( key.toUtf8() );
    data.append( salt.toUtf8() );
    data = QCryptographicHash::hash( data, QCryptographicHash::Sha1 ).toHex();

    return data;
}

}


//构造函数 构造时使用锁
RunGuard::RunGuard( const QString& key )
    : key( key )
    , memLockKey( generateKeyHash( key, "_memLockKey" ) )
    , sharedmemKey( generateKeyHash( key, "_sharedmemKey" ) )
    , sharedMem( sharedmemKey )
    , memLock( memLockKey, 1 )
{
    memLock.acquire();
    {
        //QSharedMemory 类提供了对一段共享内存的访问。既提供了被多进程和多线程共享的一段内存的访问。也为单线程或单进程锁定内存以实现互斥访问提供了方法。
        // 详见 https://blog.csdn.net/ddllrrbb/article/details/82729671
        QSharedMemory fix( sharedmemKey );    // Fix for *nix: http://habrahabr.ru/post/173281/
        //试图将进程附着在通过key所标识的共享内存段上
        fix.attach();
    }
    memLock.release();
}

RunGuard::~RunGuard()
{
    release();
}

bool RunGuard::isAnotherRunning()
{
    //如果当前内存已经被附着返回false
    if ( sharedMem.isAttached() )
        return false;

    //锁
    memLock.acquire();
    //进行附着,如果附着成功返回true
    const bool isRunning = sharedMem.attach();
    //如果附着不成功则进行将进程和共享内存段分离
    if ( isRunning )
        sharedMem.detach();
    memLock.release();

    return isRunning;
}

bool RunGuard::tryToRun()
{
    //isRunning返回true 则这里返回false
    if ( isAnotherRunning() )   // Extra check
        return false;

    //否则isRunning返回false则进行锁操作
    memLock.acquire();
    //创建一段大小为8byte共享内存
    const bool result = sharedMem.create( sizeof( quint64 ) );
    memLock.release();
    if ( !result )
    {
        release();
        return false;
    }

    return true;
}

void RunGuard::release()
{
    memLock.acquire();
    if ( sharedMem.isAttached() )
        sharedMem.detach();
    memLock.release();
}

十一.TerrainTile

这个类主要是用于地形数据的读取,计算,加载,解析。主要注释都加载代码中。

头文件如下:

#ifndef TERRAINTILE_H
#define TERRAINTILE_H

#include "QGCLoggingCategory.h"

#include <QGeoCoordinate>


//相当于 extern const QLoggingCategory &TerrainTileLog();
//具体声明在cc文件中解释
Q_DECLARE_LOGGING_CATEGORY(TerrainTileLog)

/**
 * @brief The TerrainTile class
 * 实现https://developers.airmap.com/v2.0/docs/elevation-api的接口
 * Implements an interface for https://developers.airmap.com/v2.0/docs/elevation-api
 */

class TerrainTile
{
public:
    TerrainTile();
    ~TerrainTile();

    /**
    * Constructor from serialized elevation data (either from file or web)
    * 构造函数从序列化的海拔数据(从文件或web)
    * @param document
    */
    TerrainTile(QByteArray byteArray);

    /**
    * Check whether valid data is loaded
    * 检查是否加载了有效数据
    * @return true if data is valid
    */
    bool isValid(void) const { return _isValid; }

    /**
    * Evaluates the elevation at the given coordinate
    * 计算给定坐标上的海拔高度
    * @param coordinate
    * @return elevation
    */
    double elevation(const QGeoCoordinate& coordinate) const;

    /**
    * Accessor for the minimum elevation of the tile
    * 获取瓦片的最小高度
    * @return minimum elevation
    */
    double minElevation(void) const { return _minElevation; }

    /**
    * Accessor for the maximum elevation of the tile
    * 获取瓦片的最大高度
    * @return maximum elevation
    */
    double maxElevation(void) const { return _maxElevation; }

    /**
    * Accessor for the average elevation of the tile
    * 获取瓦片的平均高度
    * @return average elevation
    */
    double avgElevation(void) const { return _avgElevation; }

    /**
    * Accessor for the center coordinate
    * 中心坐标的访问器
    * @return center coordinate
    */
    QGeoCoordinate centerCoordinate(void) const;


    //序列化数据从map的json数据中
    static QByteArray serializeFromAirMapJson(QByteArray input);

    ///< Each terrain tile represents a square area .01 degrees in lat/lon
    /// 每个地形贴图代表一个纬度/经度0.01度的正方形区域
    static constexpr double tileSizeDegrees         = 0.01;

    ///< 1 Arc-Second spacing of elevation values
    /// 1arc秒间距的高度值
    static constexpr double tileValueSpacingDegrees = 1.0 / 3600;
    static constexpr double tileValueSpacingMeters  = 30.0;

private:
    typedef struct {
        double  swLat,swLon, neLat, neLon;
        int16_t minElevation;
        int16_t maxElevation;
        double  avgElevation;
        int16_t gridSizeLat;
        int16_t gridSizeLon;
    } TileInfo_t;

    QGeoCoordinate      _southWest;                                     /// South west corner of the tile  西南角的瓦片
    QGeoCoordinate      _northEast;                                     /// North east corner of the tile  东北角的瓦片

    int16_t             _minElevation;                                  /// Minimum elevation in tile 瓦片上的最小海拔高度
    int16_t             _maxElevation;                                  /// Maximum elevation in tile  瓦片上的最大海拔高度
    double              _avgElevation;                                  /// Average elevation of the tile 瓦片上的平均海拔高度

    int16_t**           _data;                                          /// 2D elevation data array 2维海拔高度数组
    int16_t             _gridSizeLat;                                   /// data grid size in latitude direction  纬度方向的数据网格大小
    int16_t             _gridSizeLon;                                   /// data grid size in longitude direction 经度方向的数据网格大小
    bool                _isValid;                                       /// data loaded is valid 加载的数据是否可用

    // Json keys
    static const char*  _jsonStatusKey;
    static const char*  _jsonDataKey;
    static const char*  _jsonBoundsKey;
    static const char*  _jsonSouthWestKey;
    static const char*  _jsonNorthEastKey;
    static const char*  _jsonStatsKey;
    static const char*  _jsonMaxElevationKey;
    static const char*  _jsonMinElevationKey;
    static const char*  _jsonAvgElevationKey;
    static const char*  _jsonCarpetKey;
};

#endif // TERRAINTILE_H

cc文件如下:

/****************************************************************************
 *
 * (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/

#include "TerrainTile.h"
#include "JsonHelper.h"
#include "QGCMapEngine.h"
#include "QGC.h"

#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QDataStream>
#include <QtMath>

//     等价于
//     static QGCLoggingCategory qgcCategoryTerrainTileLog("TerrainTileLog");
//     const QLoggingCategory &TerrainTileLog()
//     {
//         static const QLoggingCategory category("TerrainTileLog");
//         return category;
//     }
QGC_LOGGING_CATEGORY(TerrainTileLog, "TerrainTileLog");

const char*  TerrainTile::_jsonStatusKey        = "status";
const char*  TerrainTile::_jsonDataKey          = "data";
const char*  TerrainTile::_jsonBoundsKey        = "bounds";
const char*  TerrainTile::_jsonSouthWestKey     = "sw";
const char*  TerrainTile::_jsonNorthEastKey     = "ne";
const char*  TerrainTile::_jsonStatsKey         = "stats";
const char*  TerrainTile::_jsonMaxElevationKey  = "max";
const char*  TerrainTile::_jsonMinElevationKey  = "min";
const char*  TerrainTile::_jsonAvgElevationKey  = "avg";
const char*  TerrainTile::_jsonCarpetKey        = "carpet";

TerrainTile::TerrainTile()
    : _minElevation(-1.0)
    , _maxElevation(-1.0)
    , _avgElevation(-1.0)
    , _data(nullptr)
    , _gridSizeLat(-1)
    , _gridSizeLon(-1)
    , _isValid(false)
{

}

TerrainTile::~TerrainTile()
{
    if (_data) {
        for (int i = 0; i < _gridSizeLat; i++) {
            delete[] _data[i];
        }
        delete[] _data;
        _data = nullptr;
    }
}

TerrainTile::TerrainTile(QByteArray byteArray)
    : _minElevation(-1.0)
    , _maxElevation(-1.0)
    , _avgElevation(-1.0)
    , _data(nullptr)
    , _gridSizeLat(-1)
    , _gridSizeLon(-1)
    , _isValid(false)
{
    //计算结构体TileInfo_t的大小作为 header
    int cTileHeaderBytes = static_cast<int>(sizeof(TileInfo_t));
    //计算byteArray的大小作为TileBytes
    int cTileBytesAvailable = byteArray.size();

    //大小不能比头还小
    if (cTileBytesAvailable < cTileHeaderBytes) {
        qWarning() << "Terrain tile binary data too small for TileInfo_s header";
        return;
    }

    //byteArray转为TileInfo_t
    // reinterpret_cast可以允许任何指针类型(引用)之间的转换,整型与指针类型间的转换以及指针与足够大的整型之间的转换。
    // reinterpret_cast转换的实质是让编译器重新给出对象的比例模型而不进行二值转换。也就是让编译器按照目标转换类型在内存中所占的比特位重新编译原始数据类型。转换前后对象的地址值是不变的,只是对象的读取内存的比例变了而已。
    // reinterpret_cast运算符是用来处理无关类型之间的转换;它会产生一个新的值,这个值会有与原始参数(expressoin)有完全相同的比特位。
    // 与static_cast
    // 通常这个运算符用来进行数值类型间的转化,比如int到float,也可以是自定义的某些数据类型的转化。这个操作符通常也可以用于类指针之间的转化,但是这个运算符在父类到子类的转化中,没有做检查并不安全。
    const TileInfo_t* tileInfo = reinterpret_cast<const TileInfo_t*>(byteArray.constData());

    //读取完成后设置值
    _southWest.setLatitude(tileInfo->swLat);
    _southWest.setLongitude(tileInfo->swLon);
    _northEast.setLatitude(tileInfo->neLat);
    _northEast.setLongitude(tileInfo->neLon);
    _minElevation = tileInfo->minElevation;
    _maxElevation = tileInfo->maxElevation;
    _avgElevation = tileInfo->avgElevation;
    _gridSizeLat = tileInfo->gridSizeLat;
    _gridSizeLon = tileInfo->gridSizeLon;

    //QLoggingCategory可以控制打印输出类别和模块。方便在调试时,过滤掉不关心的打印信息
    qCDebug(TerrainTileLog) << "Loading terrain tile: " << _southWest << " - " << _northEast;
    qCDebug(TerrainTileLog) << "min:max:avg:sizeLat:sizeLon" << _minElevation << _maxElevation << _avgElevation << _gridSizeLat << _gridSizeLon;

    int cTileDataBytes = static_cast<int>(sizeof(int16_t)) * _gridSizeLat * _gridSizeLon;
    if (cTileBytesAvailable < cTileHeaderBytes + cTileDataBytes) {
        qWarning() << "Terrain tile binary data too small for tile data";
        return;
    }

    //创建二维数组
    _data = new int16_t*[_gridSizeLat];
    for (int k = 0; k < _gridSizeLat; k++) {
        _data[k] = new int16_t[_gridSizeLon];
    }

    int valueIndex = 0;
    //首先将输入的bytearray转为8bit的数组然后将下标行进到header的尾部
    //然后将head后的数据转为16bit的数组,头后边的数据正是要放入二维数组的数据
    const int16_t* pTileData = reinterpret_cast<const int16_t*>(&reinterpret_cast<const uint8_t*>(byteArray.constData())[cTileHeaderBytes]);

    //设置二维网格数据的值
    for (int i = 0; i < _gridSizeLat; i++) {
        for (int j = 0; j < _gridSizeLon; j++) {
            _data[i][j] = pTileData[valueIndex++];
        }
    }

    _isValid = true;

    return;
}

double TerrainTile::elevation(const QGeoCoordinate& coordinate) const
{
    if (_isValid && _southWest.isValid() && _northEast.isValid()) {
        qCDebug(TerrainTileLog) << "elevation: " << coordinate << " , in sw " << _southWest << " , ne " << _northEast;

        // The lat/lon values in _northEast and _southWest coordinates can have rounding errors such that the coordinate
        // request may be slightly outside the tile box specified by these values. So we clamp the incoming values to the
        // edges of the tile if needed.

        //在_northEast和_southWest坐标中的lat/lon值可能有误差,从而使坐标要求可能略超出这些值指定的瓦片框。 所以如果需要我们对传入值进行差值在贴图的边缘。

        //求当前坐标和西南角的最大经度
        double clampedLon = qMax(coordinate.longitude(), _southWest.longitude());

        //求当前坐标和西南角的最大维度
        double clampedLat = qMax(coordinate.latitude(), _southWest.latitude());

        // Calc the index of the southernmost and westernmost index data value
        // 求最南和最西索引数据值的索引
        int lonIndex = qFloor((clampedLon - _southWest.longitude()) / tileValueSpacingDegrees);
        int latIndex = qFloor((clampedLat - _southWest.latitude()) / tileValueSpacingDegrees);

        // Calc how far along in between the known values the requested lat/lon is fractionally
        //计算在已知值和请求的lat/lon之间有多远
        double lonIndexLongitude    = _southWest.longitude() + (static_cast<double>(lonIndex) * tileValueSpacingDegrees);
        double lonFraction          = (clampedLon - lonIndexLongitude) / tileValueSpacingDegrees;
        double latIndexLatitude     = _southWest.latitude() + (static_cast<double>(latIndex) * tileValueSpacingDegrees);
        double latFraction          = (clampedLat - latIndexLatitude) / tileValueSpacingDegrees;

        // Calc the elevation as the average across the four known points
        // 用四个已知点的平均值来计算海拔高度
        double known00      = _data[latIndex][lonIndex];
        double known01      = _data[latIndex][lonIndex+1];
        double known10      = _data[latIndex+1][lonIndex];
        double known11      = _data[latIndex+1][lonIndex+1];
        double lonValue1    = known00 + ((known01 - known00) * lonFraction);
        double lonValue2    = known10 + ((known11 - known10) * lonFraction);
        double latValue     = lonValue1 + ((lonValue2 - lonValue1) * latFraction);

        return latValue;
    } else {
        qCWarning(TerrainTileLog) << "elevation: Internal error - invalid tile";
        return qQNaN();
    }
}

QGeoCoordinate TerrainTile::centerCoordinate(void) const
{
    return _southWest.atDistanceAndAzimuth(_southWest.distanceTo(_northEast) / 2.0, _southWest.azimuthTo(_northEast));
}

QByteArray TerrainTile::serializeFromAirMapJson(QByteArray input)
{
    QJsonParseError parseError;
    QJsonDocument document = QJsonDocument::fromJson(input, &parseError);
    if (parseError.error != QJsonParseError::NoError) {
        QByteArray emptyArray;
        return emptyArray;
    }

    if (!document.isObject()) {
        qCDebug(TerrainTileLog) << "Terrain tile json doc is no object";
        QByteArray emptyArray;
        return emptyArray;
    }

    //json解析完成
    QJsonObject rootObject = document.object();

    QString errorString;

    // typedef struct {
    //      const char*         key;        ///< json key name
    //      QJsonValue::Type    type;       ///< required type for key, QJsonValue::Null specifies double with possible NaN
    //      bool                required;   ///< true: key must be present
    // } KeyValidateInfo;
    QList<JsonHelper::KeyValidateInfo> rootVersionKeyInfoList = {
        { _jsonStatusKey, QJsonValue::String, true },
        { _jsonDataKey,   QJsonValue::Object, true },
    };


    if (!JsonHelper::validateKeys(rootObject, rootVersionKeyInfoList, errorString)) {
        qCDebug(TerrainTileLog) << "Error in reading json: " << errorString;
        QByteArray emptyArray;
        return emptyArray;
    }

    if (rootObject[_jsonStatusKey].toString() != "success") {
        qCDebug(TerrainTileLog) << "Invalid terrain tile.";
        QByteArray emptyArray;
        return emptyArray;
    }
    const QJsonObject& dataObject = rootObject[_jsonDataKey].toObject();
    QList<JsonHelper::KeyValidateInfo> dataVersionKeyInfoList = {
        { _jsonBoundsKey, QJsonValue::Object, true },
        { _jsonStatsKey,  QJsonValue::Object, true },
        { _jsonCarpetKey, QJsonValue::Array, true },
    };
    if (!JsonHelper::validateKeys(dataObject, dataVersionKeyInfoList, errorString)) {
        qCDebug(TerrainTileLog) << "Error in reading json: " << errorString;
        QByteArray emptyArray;
        return emptyArray;
    }

    // Bounds
    const QJsonObject& boundsObject = dataObject[_jsonBoundsKey].toObject();
    QList<JsonHelper::KeyValidateInfo> boundsVersionKeyInfoList = {
        { _jsonSouthWestKey, QJsonValue::Array, true },
        { _jsonNorthEastKey, QJsonValue::Array, true },
    };
    if (!JsonHelper::validateKeys(boundsObject, boundsVersionKeyInfoList, errorString)) {
        qCDebug(TerrainTileLog) << "Error in reading json: " << errorString;
        QByteArray emptyArray;
        return emptyArray;
    }
    const QJsonArray& swArray = boundsObject[_jsonSouthWestKey].toArray();
    const QJsonArray& neArray = boundsObject[_jsonNorthEastKey].toArray();
    if (swArray.count() < 2 || neArray.count() < 2 ) {
        qCDebug(TerrainTileLog) << "Incomplete bounding location";
        QByteArray emptyArray;
        return emptyArray;
    }

    // Stats
    const QJsonObject& statsObject = dataObject[_jsonStatsKey].toObject();
    QList<JsonHelper::KeyValidateInfo> statsVersionKeyInfoList = {
        { _jsonMinElevationKey, QJsonValue::Double, true },
        { _jsonMaxElevationKey, QJsonValue::Double, true },
        { _jsonAvgElevationKey, QJsonValue::Double, true },
    };
    if (!JsonHelper::validateKeys(statsObject, statsVersionKeyInfoList, errorString)) {
        qCDebug(TerrainTileLog) << "Error in reading json: " << errorString;
        QByteArray emptyArray;
        return emptyArray;
    }

    // Carpet
    const QJsonArray& carpetArray = dataObject[_jsonCarpetKey].toArray();
    int gridSizeLat = carpetArray.count();
    int gridSizeLon = carpetArray[0].toArray().count();

    TileInfo_t tileInfo;

    tileInfo.swLat = swArray[0].toDouble();
    tileInfo.swLon = swArray[1].toDouble();
    tileInfo.neLat = neArray[0].toDouble();
    tileInfo.neLon = neArray[1].toDouble();
    tileInfo.minElevation = static_cast<int16_t>(statsObject[_jsonMinElevationKey].toInt());
    tileInfo.maxElevation = static_cast<int16_t>(statsObject[_jsonMaxElevationKey].toInt());
    tileInfo.avgElevation = statsObject[_jsonAvgElevationKey].toDouble();
    tileInfo.gridSizeLat = static_cast<int16_t>(gridSizeLat);
    tileInfo.gridSizeLon = static_cast<int16_t>(gridSizeLon);

    // We require 1-arc second value spacing
    double neCornerLatExpected = tileInfo.swLat + ((tileInfo.gridSizeLat - 1) * tileValueSpacingDegrees);
    double neCornerLonExpected = tileInfo.swLon + ((tileInfo.gridSizeLon - 1) * tileValueSpacingDegrees);
    if (!QGC::fuzzyCompare(tileInfo.neLat, neCornerLatExpected) || !QGC::fuzzyCompare(tileInfo.neLon, neCornerLonExpected)) {
        qCWarning(TerrainTileLog) << QStringLiteral("serialize: Internal error - distance between values incorrect neExpected(%1, %2) neActual(%3, %4) sw(%5, %6) gridSize(%7, %8)")
                                     .arg(neCornerLatExpected).arg(neCornerLonExpected).arg(tileInfo.neLat).arg(tileInfo.neLon).arg(tileInfo.swLat).arg(tileInfo.swLon).arg(tileInfo.gridSizeLat).arg(tileInfo.gridSizeLon);
        QByteArray emptyArray;
        return emptyArray;
    }

    int cTileHeaderBytes = static_cast<int>(sizeof(TileInfo_t));
    int cTileDataBytes = static_cast<int>(sizeof(int16_t)) * gridSizeLat * gridSizeLon;

    QByteArray byteArray(cTileHeaderBytes + cTileDataBytes, 0);


    //这里和读取的方法对应
    TileInfo_t* pTileInfo = reinterpret_cast<TileInfo_t*>(byteArray.data());
    int16_t*    pTileData = reinterpret_cast<int16_t*>(&reinterpret_cast<uint8_t*>(byteArray.data())[cTileHeaderBytes]);

    *pTileInfo = tileInfo;

    int valueIndex = 0;
    for (int i = 0; i < gridSizeLat; i++) {
        const QJsonArray& row = carpetArray[i].toArray();
        if (row.count() < gridSizeLon) {
            qCDebug(TerrainTileLog) << "Expected row array of " << gridSizeLon << ", instead got " << row.count();
            QByteArray emptyArray;
            return emptyArray;
        }
        for (int j = 0; j < gridSizeLon; j++) {
            pTileData[valueIndex++] = static_cast<int16_t>(row[j].toDouble());
        }
    }

    return byteArray;
}

总结

这篇文章主要介绍了QGC开头的几个文件,大多还是一些工具类,如日志,toolbox工具箱,调色板,共享内存守卫,地形数据,文件下载,临时文件管理等,细心的朋友可能发现,其中QGCApplicaion和main没有介绍,是因为这两个文件里含有大量的还没接触到的文件和类,所以这里我准备先搁置一下。下篇我将按照文件夹的顺序进行代码的解读首先是ADSB文件夹下的代码,并尽量了解工作原理。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

机器人地面站-[QGroundControl源码解析]-[2] 的相关文章

随机推荐