MultiVehicleManager.cc 9.2 KB
Newer Older
1
/*=====================================================================
dogmaphobic's avatar
dogmaphobic committed
2

3
 QGroundControl Open Source Ground Control Station
dogmaphobic's avatar
dogmaphobic committed
4

5
 (c) 2009 - 2014 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
dogmaphobic's avatar
dogmaphobic committed
6

7
 This file is part of the QGROUNDCONTROL project
dogmaphobic's avatar
dogmaphobic committed
8

9 10 11 12
 QGROUNDCONTROL is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.
dogmaphobic's avatar
dogmaphobic committed
13

14 15 16 17
 QGROUNDCONTROL is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
dogmaphobic's avatar
dogmaphobic committed
18

19 20
 You should have received a copy of the GNU General Public License
 along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.
dogmaphobic's avatar
dogmaphobic committed
21

22 23 24 25 26 27 28
 ======================================================================*/

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

#include "MultiVehicleManager.h"
#include "AutoPilotPlugin.h"
29 30
#include "MAVLinkProtocol.h"
#include "UAS.h"
31
#include "QGCApplication.h"
32

Don Gagne's avatar
Don Gagne committed
33 34
QGC_LOGGING_CATEGORY(MultiVehicleManagerLog, "MultiVehicleManagerLog")

35 36
MultiVehicleManager::MultiVehicleManager(QGCApplication* app)
    : QGCTool(app)
37 38 39
    , _activeVehicleAvailable(false)
    , _parameterReadyVehicleAvailable(false)
    , _activeVehicle(NULL)
40 41 42 43
    , _firmwarePluginManager(NULL)
    , _autopilotPluginManager(NULL)
    , _joystickManager(NULL)
    , _mavlinkProtocol(NULL)
44
{
45

46 47
}

48
void MultiVehicleManager::setToolbox(QGCToolbox *toolbox)
49
{
50 51 52 53 54 55
   QGCTool::setToolbox(toolbox);

   _firmwarePluginManager =     _toolbox->firmwarePluginManager();
   _autopilotPluginManager =    _toolbox->autopilotPluginManager();
   _joystickManager =           _toolbox->joystickManager();
   _mavlinkProtocol =           _toolbox->mavlinkProtocol();
56

57 58
   QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
   qmlRegisterUncreatableType<MultiVehicleManager>("QGroundControl.MultiVehicleManager", 1, 0, "MultiVehicleManager", "Reference only");
dogmaphobic's avatar
dogmaphobic committed
59

60 61 62 63
}

bool MultiVehicleManager::notifyHeartbeatInfo(LinkInterface* link, int vehicleId, mavlink_heartbeat_t& heartbeat)
{
64
    if (!getVehicleById(vehicleId) && !_ignoreVehicleIds.contains(vehicleId)) {
65 66
        if (vehicleId == _mavlinkProtocol->getSystemId()) {
            _app->showToolBarMessage(QString("Warning: A vehicle is using the same system id as QGroundControl: %1").arg(vehicleId));
67
        }
dogmaphobic's avatar
dogmaphobic committed
68

69 70 71 72
        QSettings settings;
        bool mavlinkVersionCheck = settings.value("VERSION_CHECK_ENABLED", true).toBool();
        if (mavlinkVersionCheck && heartbeat.mavlink_version != MAVLINK_VERSION) {
            _ignoreVehicleIds += vehicleId;
73
            _app->showToolBarMessage(QString("The MAVLink protocol version on vehicle #%1 and QGroundControl differ! "
74 75 76 77
                                                 "It is unsafe to use different MAVLink versions. "
                                                 "QGroundControl therefore refuses to connect to vehicle #%1, which sends MAVLink version %2 (QGroundControl uses version %3).").arg(vehicleId).arg(heartbeat.mavlink_version).arg(MAVLINK_VERSION));
            return false;
        }
dogmaphobic's avatar
dogmaphobic committed
78

79
        Vehicle* vehicle = new Vehicle(link, vehicleId, (MAV_AUTOPILOT)heartbeat.autopilot, (MAV_TYPE)heartbeat.type, _firmwarePluginManager, _autopilotPluginManager, _joystickManager);
dogmaphobic's avatar
dogmaphobic committed
80

81 82 83 84
        if (!vehicle) {
            qWarning() << "New Vehicle allocation failed";
            return false;
        }
dogmaphobic's avatar
dogmaphobic committed
85

86
        connect(vehicle, &Vehicle::allLinksDisconnected, this, &MultiVehicleManager::_deleteVehiclePhase1);
87
        connect(vehicle->autopilotPlugin(), &AutoPilotPlugin::parametersReadyChanged, this, &MultiVehicleManager::_autopilotParametersReadyChanged);
88

89
        _vehicles.append(vehicle);
dogmaphobic's avatar
dogmaphobic committed
90

91
        emit vehicleAdded(vehicle);
dogmaphobic's avatar
dogmaphobic committed
92

93 94
        setActiveVehicle(vehicle);
    }
dogmaphobic's avatar
dogmaphobic committed
95

96 97 98 99 100
    return true;
}

/// This slot is connected to the Vehicle::allLinksDestroyed signal such that the Vehicle is deleted
/// and all other right things happen when the Vehicle goes away.
101
void MultiVehicleManager::_deleteVehiclePhase1(Vehicle* vehicle)
102
{
Don Gagne's avatar
Don Gagne committed
103 104
    qCDebug(MultiVehicleManagerLog) << "_deleteVehiclePhase1";

105 106 107
    _vehicleBeingDeleted = vehicle;

    // Remove from map
108 109 110 111
    bool found = false;
    for (int i=0; i<_vehicles.count(); i++) {
        if (_vehicles[i] == _vehicleBeingDeleted) {
            _vehicles.removeAt(i);
112 113 114 115 116 117 118
            found = true;
            break;
        }
    }
    if (!found) {
        qWarning() << "Vehicle not found in map!";
    }
dogmaphobic's avatar
dogmaphobic committed
119

120
    vehicle->setActive(false);
121
    vehicle->uas()->clearVehicle();
dogmaphobic's avatar
dogmaphobic committed
122

123 124 125 126 127 128
    // First we must signal that a vehicle is no longer available.
    _activeVehicleAvailable = false;
    _parameterReadyVehicleAvailable = false;
    emit activeVehicleAvailableChanged(false);
    emit parameterReadyVehicleAvailableChanged(false);
    emit vehicleRemoved(vehicle);
dogmaphobic's avatar
dogmaphobic committed
129

130 131 132 133 134 135 136 137 138 139 140
    // We must let the above signals flow through the system as well as get back to the main loop event queue
    // before we can actually delete the Vehicle. The reason is that Qml may be holding on the references to it.
    // Even though the above signals should unload any Qml which has references, that Qml will not be destroyed
    // until we get back to the main loop. So we set a short timer which will then fire after Qt has finished
    // doing all of its internal nastiness to clean up the Qml. This works for both the normal running case
    // as well as the unit testing case whichof course has a different signal flow!
    QTimer::singleShot(20, this, &MultiVehicleManager::_deleteVehiclePhase2);
}

void MultiVehicleManager::_deleteVehiclePhase2  (void)
{
Don Gagne's avatar
Don Gagne committed
141 142
    qCDebug(MultiVehicleManagerLog) << "_deleteVehiclePhase2";

143 144
    /// Qml has been notified of vehicle about to go away and should be disconnected from it by now.
    /// This means we can now clear the active vehicle property and delete the Vehicle for real.
dogmaphobic's avatar
dogmaphobic committed
145

146
    Vehicle* newActiveVehicle = NULL;
147 148
    if (_vehicles.count()) {
        newActiveVehicle = qobject_cast<Vehicle*>(_vehicles[0]);
149
    }
dogmaphobic's avatar
dogmaphobic committed
150

151 152
    _activeVehicle = newActiveVehicle;
    emit activeVehicleChanged(newActiveVehicle);
dogmaphobic's avatar
dogmaphobic committed
153

154
    if (_activeVehicle) {
155
        _activeVehicle->setActive(true);
156
        emit activeVehicleAvailableChanged(true);
157
        if (_activeVehicle->autopilotPlugin()->parametersReady()) {
158 159 160
            emit parameterReadyVehicleAvailableChanged(true);
        }
    }
dogmaphobic's avatar
dogmaphobic committed
161

162 163 164 165 166
    _vehicleBeingDeleted->deleteLater();
}

void MultiVehicleManager::setActiveVehicle(Vehicle* vehicle)
{
Don Gagne's avatar
Don Gagne committed
167 168
    qCDebug(MultiVehicleManagerLog) << "setActiveVehicle" << vehicle;

169
    if (vehicle != _activeVehicle) {
Don Gagne's avatar
Don Gagne committed
170
        if (_activeVehicle) {
171
            _activeVehicle->setActive(false);
dogmaphobic's avatar
dogmaphobic committed
172

Don Gagne's avatar
Don Gagne committed
173 174
            // The sequence of signals is very important in order to not leave Qml elements connected
            // to a non-existent vehicle.
dogmaphobic's avatar
dogmaphobic committed
175

Don Gagne's avatar
Don Gagne committed
176 177 178 179 180 181 182
            // First we must signal that there is no active vehicle available. This will disconnect
            // any existing ui from the currently active vehicle.
            _activeVehicleAvailable = false;
            _parameterReadyVehicleAvailable = false;
            emit activeVehicleAvailableChanged(false);
            emit parameterReadyVehicleAvailableChanged(false);
        }
dogmaphobic's avatar
dogmaphobic committed
183

184 185 186 187 188 189 190 191
        // See explanation in _deleteVehiclePhase1
        _vehicleBeingSetActive = vehicle;
        QTimer::singleShot(20, this, &MultiVehicleManager::_setActiveVehiclePhase2);
    }
}

void MultiVehicleManager::_setActiveVehiclePhase2(void)
{
Don Gagne's avatar
Don Gagne committed
192 193
    qCDebug(MultiVehicleManagerLog) << "_setActiveVehiclePhase2";

194 195 196
    // Now we signal the new active vehicle
    _activeVehicle = _vehicleBeingSetActive;
    emit activeVehicleChanged(_activeVehicle);
dogmaphobic's avatar
dogmaphobic committed
197

198 199
    // And finally vehicle availability
    if (_activeVehicle) {
200
        _activeVehicle->setActive(true);
201 202
        _activeVehicleAvailable = true;
        emit activeVehicleAvailableChanged(true);
dogmaphobic's avatar
dogmaphobic committed
203

204
        if (_activeVehicle->autopilotPlugin()->parametersReady()) {
205 206 207 208 209 210
            _parameterReadyVehicleAvailable = true;
            emit parameterReadyVehicleAvailableChanged(true);
        }
    }
}

211
void MultiVehicleManager::_autopilotParametersReadyChanged(bool parametersReady)
212 213
{
    AutoPilotPlugin* autopilot = dynamic_cast<AutoPilotPlugin*>(sender());
dogmaphobic's avatar
dogmaphobic committed
214

215 216 217 218
    if (!autopilot) {
        qWarning() << "Dynamic cast failed!";
        return;
    }
dogmaphobic's avatar
dogmaphobic committed
219

Don Gagne's avatar
Don Gagne committed
220
    if (autopilot->vehicle() == _activeVehicle) {
221 222
        _parameterReadyVehicleAvailable = parametersReady;
        emit parameterReadyVehicleAvailableChanged(parametersReady);
223 224 225
    }
}

226 227 228 229 230 231 232 233 234 235 236
void MultiVehicleManager::saveSetting(const QString &name, const QString& value)
{
    QSettings settings;
    settings.setValue(name, value);
}

QString MultiVehicleManager::loadSetting(const QString &name, const QString& defaultValue)
{
    QSettings settings;
    return settings.value(name, defaultValue).toString();
}
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252

Vehicle* MultiVehicleManager::getVehicleById(int vehicleId)
{
    for (int i=0; i< _vehicles.count(); i++) {
        Vehicle* vehicle = qobject_cast<Vehicle*>(_vehicles[i]);
        if (vehicle->id() == vehicleId) {
            return vehicle;
        }
    }

    return NULL;
}

QList<Vehicle*> MultiVehicleManager::vehicles(void)
{
    QList<Vehicle*> list;
dogmaphobic's avatar
dogmaphobic committed
253

254 255 256
    for (int i=0; i< _vehicles.count(); i++) {
        list += qobject_cast<Vehicle*>(_vehicles[i]);
    }
dogmaphobic's avatar
dogmaphobic committed
257

258 259
    return list;
}
dogmaphobic's avatar
dogmaphobic committed
260 261