Skip to content
Snippets Groups Projects
ADSBVehicleManager.cc 6.33 KiB
Newer Older
  • Learn to ignore specific revisions
  • /****************************************************************************
     *
     * (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 "ADSBVehicleManager.h"
    #include "QGCLoggingCategory.h"
    #include "QGCApplication.h"
    #include "SettingsManager.h"
    #include "ADSBVehicleManagerSettings.h"
    
    #include <QDebug>
    
    ADSBVehicleManager::ADSBVehicleManager(QGCApplication* app, QGCToolbox* toolbox)
        : QGCTool(app, toolbox)
    {
    }
    
    void ADSBVehicleManager::setToolbox(QGCToolbox* toolbox)
    {
        QGCTool::setToolbox(toolbox);
    
        connect(&_adsbVehicleCleanupTimer, &QTimer::timeout, this, &ADSBVehicleManager::_cleanupStaleVehicles);
        _adsbVehicleCleanupTimer.setSingleShot(false);
        _adsbVehicleCleanupTimer.start(1000);
    
        ADSBVehicleManagerSettings* settings = qgcApp()->toolbox()->settingsManager()->adsbVehicleManagerSettings();
        if (settings->adsbServerConnectEnabled()->rawValue().toBool()) {
            _tcpLink = new ADSBTCPLink(settings->adsbServerHostAddress()->rawValue().toString(), settings->adsbServerPort()->rawValue().toInt(), this);
            connect(_tcpLink, &ADSBTCPLink::adsbVehicleUpdate,  this, &ADSBVehicleManager::adsbVehicleUpdate,   Qt::QueuedConnection);
            connect(_tcpLink, &ADSBTCPLink::error,              this, &ADSBVehicleManager::_tcpError,           Qt::QueuedConnection);
        }
    }
    
    void ADSBVehicleManager::_cleanupStaleVehicles()
    {
        // Remove all expired ADSB vehicles
        for (int i=_adsbVehicles.count()-1; i>=0; i--) {
            ADSBVehicle* adsbVehicle = _adsbVehicles.value<ADSBVehicle*>(i);
            if (adsbVehicle->expired()) {
                qCDebug(ADSBVehicleManagerLog) << "Expired" << QStringLiteral("%1").arg(adsbVehicle->icaoAddress(), 0, 16);
                _adsbVehicles.removeAt(i);
                _adsbICAOMap.remove(adsbVehicle->icaoAddress());
                adsbVehicle->deleteLater();
            }
        }
    }
    
    void ADSBVehicleManager::adsbVehicleUpdate(const ADSBVehicle::VehicleInfo_t vehicleInfo)
    {
        uint32_t icaoAddress = vehicleInfo.icaoAddress;
    
        if (_adsbICAOMap.contains(icaoAddress)) {
            _adsbICAOMap[icaoAddress]->update(vehicleInfo);
        } else {
            if (vehicleInfo.availableFlags & ADSBVehicle::LocationAvailable) {
                ADSBVehicle* adsbVehicle = new ADSBVehicle(vehicleInfo, this);
                _adsbICAOMap[icaoAddress] = adsbVehicle;
                _adsbVehicles.append(adsbVehicle);
            }
        }
    }
    
    void ADSBVehicleManager::_tcpError(const QString errorMsg)
    {
        qgcApp()->showAppMessage(tr("ADSB Server Error: %1").arg(errorMsg));
    }
    
    
    ADSBTCPLink::ADSBTCPLink(const QString& hostAddress, int port, QObject* parent)
        : QThread       (parent)
        , _hostAddress  (hostAddress)
        , _port         (port)
    {
        moveToThread(this);
        start();
    }
    
    ADSBTCPLink::~ADSBTCPLink(void)
    {
        if (_socket) {
            QObject::disconnect(_socket, &QTcpSocket::readyRead, this, &ADSBTCPLink::_readBytes);
            _socket->disconnectFromHost();
            _socket->deleteLater();
            _socket = nullptr;
        }
        quit();
        wait();
    }
    
    void ADSBTCPLink::run(void)
    {
        _hardwareConnect();
        exec();
    }
    
    void ADSBTCPLink::_hardwareConnect()
    {
        _socket = new QTcpSocket();
    
        QObject::connect(_socket, &QTcpSocket::readyRead, this, &ADSBTCPLink::_readBytes);
    
        _socket->connectToHost(_hostAddress, static_cast<quint16>(_port));
    
        // Give the socket a second to connect to the other side otherwise error out
        if (!_socket->waitForConnected(1000)) {
            qCDebug(ADSBVehicleManagerLog) << "ADSB Socket failed to connect";
            emit error(_socket->errorString());
            delete _socket;
            _socket = nullptr;
            return;
        }
    
        qCDebug(ADSBVehicleManagerLog) << "ADSB Socket connected";
    }
    
    void ADSBTCPLink::_readBytes(void)
    {
        if (_socket) {
            QByteArray bytes = _socket->readLine();
            _parseLine(QString::fromLocal8Bit(bytes));
        }
    }
    
    void ADSBTCPLink::_parseLine(const QString& line)
    {
        if (line.startsWith(QStringLiteral("MSG"))) {
            qCDebug(ADSBVehicleManagerLog) << "ADSB SBS-1" << line;
    
            QStringList values = line.split(QStringLiteral(","));
    
            if (values[1] == QStringLiteral("3")) {
                bool icaoOk, altOk, latOk, lonOk;
    
                uint32_t    icaoAddress =   values[4].toUInt(&icaoOk, 16);
                int         modeCAltitude = values[11].toInt(&altOk);
                double      lat =           values[14].toDouble(&latOk);
                double      lon =           values[15].toDouble(&lonOk);
                QString     callsign =      values[10];
    
                if (!icaoOk || !altOk || !latOk || !lonOk) {
                    return;
                }
                if (lat == 0 && lon == 0) {
                    return;
                }
    
                double altitude = modeCAltitude / 3.048;
                QGeoCoordinate location(lat, lon);
    
                ADSBVehicle::VehicleInfo_t adsbInfo;
                adsbInfo.icaoAddress = icaoAddress;
                adsbInfo.callsign = callsign;
                adsbInfo.location = location;
                adsbInfo.altitude = altitude;
                adsbInfo.availableFlags = ADSBVehicle::CallsignAvailable | ADSBVehicle::LocationAvailable | ADSBVehicle::AltitudeAvailable;
                emit adsbVehicleUpdate(adsbInfo);
            } else if (values[1] == QStringLiteral("4")) {
                bool icaoOk, headingOk;
    
                uint32_t    icaoAddress =   values[4].toUInt(&icaoOk, 16);
                double      heading =       values[13].toDouble(&headingOk);
    
                if (!icaoOk || !headingOk) {
                    return;
                }
    
                ADSBVehicle::VehicleInfo_t adsbInfo;
                adsbInfo.icaoAddress = icaoAddress;
                adsbInfo.heading = heading;
                adsbInfo.availableFlags = ADSBVehicle::HeadingAvailable;
                emit adsbVehicleUpdate(adsbInfo);
            } else if (values[1] == QStringLiteral("1")) {
                bool icaoOk;
    
                uint32_t icaoAddress = values[4].toUInt(&icaoOk, 16);
                if (!icaoOk) {
                    return;
                }
    
                ADSBVehicle::VehicleInfo_t adsbInfo;
                adsbInfo.icaoAddress = icaoAddress;
                adsbInfo.callsign = values[10];
                adsbInfo.availableFlags = ADSBVehicle::CallsignAvailable;
                emit adsbVehicleUpdate(adsbInfo);
            }
        }
    }