LP-56 - Better txpid option namings, fix tabs-spaces, tooltips. headers, variable...
[librepilot.git] / ground / openpilotgcs / src / plugins / coreplugin / connectionmanager.cpp
bloba225b97e665884626e76e0e988c63b57a4eea7e3
1 /**
2 ******************************************************************************
4 * @file connectionmanager.cpp
5 * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
6 * Parts by Nokia Corporation (qt-info@nokia.com) Copyright (C) 2009.
7 * @addtogroup GCSPlugins GCS Plugins
8 * @{
9 * @addtogroup CorePlugin Core Plugin
10 * @{
11 * @brief The Core GCS plugin
12 *****************************************************************************/
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 3 of the License, or
17 * (at your option) any later version.
19 * This program is distributed in the hope that it will be useful, but
20 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
21 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 * for more details.
24 * You should have received a copy of the GNU General Public License along
25 * with this program; if not, write to the Free Software Foundation, Inc.,
26 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 #include "connectionmanager.h"
31 #include <aggregation/aggregate.h>
32 #include <coreplugin/iconnection.h>
33 #include <extensionsystem/pluginmanager.h>
34 #include <QtSerialPort/QSerialPort>
35 #include <QtSerialPort/QSerialPortInfo>
36 #include <QDebug>
37 #include <QLabel>
38 #include <QHBoxLayout>
39 #include <QComboBox>
40 #include <QEventLoop>
42 namespace Core {
43 ConnectionManager::ConnectionManager(Internal::MainWindow *mainWindow) :
44 QWidget(mainWindow),
45 m_availableDevList(0),
46 m_connectBtn(0),
47 m_ioDev(NULL),
48 polling(true),
49 m_mainWindow(mainWindow)
51 // device list
52 m_availableDevList = new QComboBox;
53 m_availableDevList->setMinimumContentsLength(tr("USB: OPLinkMini").length());
54 m_availableDevList->setContextMenuPolicy(Qt::CustomContextMenu);
56 // connect button
57 m_connectBtn = new QPushButton(tr("Connect"));
58 m_connectBtn->setEnabled(false);
60 // put everything together
61 QHBoxLayout *layout = new QHBoxLayout;
62 layout->setSpacing(6);
63 layout->setContentsMargins(5, 2, 5, 2);
64 setLayout(layout);
66 layout->addWidget(new QLabel(tr("Connections:")), 0, Qt::AlignVCenter);
67 layout->addWidget(m_availableDevList, 0, Qt::AlignVCenter);
68 layout->addWidget(m_connectBtn, 0, Qt::AlignVCenter);
70 QObject::connect(m_connectBtn, SIGNAL(clicked()), this, SLOT(onConnectClicked()));
71 QObject::connect(m_availableDevList, SIGNAL(currentIndexChanged(int)), this, SLOT(onDeviceSelectionChanged(int)));
73 // setup our reconnect timers
74 reconnect = new QTimer(this);
75 reconnectCheck = new QTimer(this);
76 connect(reconnect, SIGNAL(timeout()), this, SLOT(reconnectSlot()));
77 connect(reconnectCheck, SIGNAL(timeout()), this, SLOT(reconnectCheckSlot()));
80 ConnectionManager::~ConnectionManager()
82 disconnectDevice();
83 suspendPolling();
86 void ConnectionManager::init()
88 // register to the plugin manager so we can receive
89 // new connection object from plugins
90 QObject::connect(ExtensionSystem::PluginManager::instance(), SIGNAL(objectAdded(QObject *)), this, SLOT(objectAdded(QObject *)));
91 QObject::connect(ExtensionSystem::PluginManager::instance(), SIGNAL(aboutToRemoveObject(QObject *)), this, SLOT(aboutToRemoveObject(QObject *)));
95 // TODO needs documentation?
96 void ConnectionManager::addWidget(QWidget *widget)
98 QHBoxLayout *l = (QHBoxLayout *)layout();
100 l->insertWidget(0, widget, 0, Qt::AlignVCenter);
104 * Method called when the user clicks the "Connect" button
106 bool ConnectionManager::connectDevice(DevListItem device)
108 Q_UNUSED(device);
109 QString deviceName = m_availableDevList->itemData(m_availableDevList->currentIndex(), Qt::ToolTipRole).toString();
110 DevListItem connection_device = findDevice(deviceName);
112 if (!connection_device.connection) {
113 return false;
116 QIODevice *io_dev = connection_device.connection->openDevice(connection_device.device.name);
117 if (!io_dev) {
118 return false;
121 io_dev->open(QIODevice::ReadWrite);
123 // check if opening the device worked
124 if (!io_dev->isOpen()) {
125 return false;
128 // we appear to have connected to the device OK
129 // remember the connection/device details
130 m_connectionDevice = connection_device;
131 m_ioDev = io_dev;
133 connect(m_connectionDevice.connection, SIGNAL(destroyed(QObject *)), this, SLOT(onConnectionDestroyed(QObject *)), Qt::QueuedConnection);
135 // signal interested plugins that we connected to the device
136 emit deviceConnected(io_dev);
138 m_connectBtn->setText(tr("Disconnect"));
139 m_availableDevList->setEnabled(false);
141 return true;
145 * Method called by plugins who want to force a disconnection.
146 * Used by Uploader gadget for instance.
148 bool ConnectionManager::disconnectDevice()
150 if (!m_ioDev) {
151 // apparently we are already disconnected: this can
152 // happen if a plugin tries to force a disconnect whereas
153 // we are not connected. Just return.
154 return false;
157 // We are connected - disconnect from the device
159 // stop our timers
160 if (reconnect->isActive()) {
161 reconnect->stop();
163 if (reconnectCheck->isActive()) {
164 reconnectCheck->stop();
167 // signal interested plugins that user is disconnecting his device
168 emit deviceAboutToDisconnect();
170 try {
171 if (m_connectionDevice.connection) {
172 m_connectionDevice.connection->closeDevice(m_connectionDevice.getConName());
174 } catch(...) { // handle exception
175 qDebug() << "Exception: m_connectionDevice.connection->closeDevice(" << m_connectionDevice.getConName() << ")";
178 m_connectionDevice.connection = NULL;
179 m_ioDev = NULL;
181 // signal interested plugins that we disconnected from the device
182 emit deviceDisconnected();
184 m_connectBtn->setText(tr("Connect"));
185 m_availableDevList->setEnabled(true);
187 return true;
191 * Slot called when a plugin added an object to the core pool
193 void ConnectionManager::objectAdded(QObject *obj)
195 // Check if a plugin added a connection object to the pool
196 IConnection *connection = Aggregation::query<IConnection>(obj);
198 if (!connection) {
199 return;
202 // register devices and populate CB
203 devChanged(connection);
205 // Keep track of the registration to be able to tell plugins
206 // to do things
207 m_connectionsList.append(connection);
209 QObject::connect(connection, SIGNAL(availableDevChanged(IConnection *)), this, SLOT(devChanged(IConnection *)));
212 void ConnectionManager::aboutToRemoveObject(QObject *obj)
214 // Check if a plugin added a connection object to the pool
215 IConnection *connection = Aggregation::query<IConnection>(obj);
217 if (!connection) {
218 return;
221 if (m_connectionDevice.connection && m_connectionDevice.connection == connection) { // we are currently using the one that is about to be removed
222 disconnectDevice();
223 m_connectionDevice.connection = NULL;
224 m_ioDev = NULL;
227 if (m_connectionsList.contains(connection)) {
228 m_connectionsList.removeAt(m_connectionsList.indexOf(connection));
233 void ConnectionManager::onConnectionDestroyed(QObject *obj)
235 Q_UNUSED(obj)
236 disconnectDevice();
240 * Slot called when the user selects a device from the combo box
242 void ConnectionManager::onDeviceSelectionChanged(int index)
244 if (index >= 0) {
245 QString deviceName = m_availableDevList->itemData(index, Qt::ToolTipRole).toString();
246 m_availableDevList->setToolTip(deviceName);
251 * Slot called when the user clicks the connect/disconnect button
253 void ConnectionManager::onConnectClicked()
255 // Check if we have a ioDev already created:
256 if (!m_ioDev) {
257 // connecting to currently selected device
258 QString deviceName = m_availableDevList->itemData(m_availableDevList->currentIndex(), Qt::ToolTipRole).toString();
259 DevListItem device = findDevice(deviceName);
260 if (device.connection) {
261 connectDevice(device);
263 } else {
264 // disconnecting
265 disconnectDevice();
270 * Slot called when the telemetry is connected
272 void ConnectionManager::telemetryConnected()
274 qDebug() << "TelemetryMonitor: connected";
276 if (reconnectCheck->isActive()) {
277 reconnectCheck->stop();
282 * Slot called when the telemetry is disconnected
284 void ConnectionManager::telemetryDisconnected()
286 qDebug() << "TelemetryMonitor: disconnected";
288 if (m_ioDev) {
289 if (m_connectionDevice.connection->shortName() == "Serial") {
290 if (!reconnect->isActive()) {
291 reconnect->start(1000);
297 void ConnectionManager::reconnectSlot()
299 qDebug() << "reconnect";
300 if (m_ioDev->isOpen()) {
301 m_ioDev->close();
304 if (m_ioDev->open(QIODevice::ReadWrite)) {
305 qDebug() << "reconnect successfull";
306 reconnect->stop();
307 reconnectCheck->start(20000);
308 } else {
309 qDebug() << "reconnect NOT successfull";
313 void ConnectionManager::reconnectCheckSlot()
315 reconnectCheck->stop();
316 reconnect->start(1000);
320 * Find a device by its displayed (visible on screen) name
322 DevListItem ConnectionManager::findDevice(const QString &devName)
324 foreach(DevListItem d, m_devList) {
325 if (d.getConName() == devName) {
326 return d;
330 qDebug() << "findDevice: cannot find " << devName << " in device list";
332 DevListItem d;
333 d.connection = NULL;
334 return d;
338 * Tells every connection plugin to stop polling for devices if they
339 * are doing that.
342 void ConnectionManager::suspendPolling()
344 foreach(IConnection * cnx, m_connectionsList) {
345 cnx->suspendPolling();
348 m_connectBtn->setEnabled(false);
349 m_availableDevList->setEnabled(false);
350 polling = false;
354 * Tells every connection plugin to resume polling for devices if they
355 * are doing that.
357 void ConnectionManager::resumePolling()
359 foreach(IConnection * cnx, m_connectionsList) {
360 cnx->resumePolling();
363 m_connectBtn->setEnabled(true);
364 m_availableDevList->setEnabled(true);
365 polling = true;
369 * Synchronize the list of connections displayed with those physically
370 * present
371 * @param[in] connection Connection type that you want to forget about :)
373 void ConnectionManager::updateConnectionList(IConnection *connection)
375 // Get the updated list of devices
376 QList <IConnection::device> availableDev = connection->availableDevices();
378 // Go through the list of connections of that type. If they are not in the
379 // available device list then remove them. If they are connected, then
380 // disconnect them.
381 for (QLinkedList<DevListItem>::iterator iter = m_devList.begin(); iter != m_devList.end();) {
382 if (iter->connection != connection) {
383 ++iter;
384 continue;
387 // See if device exists in the updated availability list
388 bool found = availableDev.contains(iter->device);
389 if (!found) {
390 // we are currently using the one we are about to erase
391 if (m_connectionDevice.connection && m_connectionDevice.connection == connection && m_connectionDevice.device == iter->device) {
392 disconnectDevice();
395 iter = m_devList.erase(iter);
396 } else {
397 ++iter;
401 // Add any back to list that don't exist
402 foreach(IConnection::device dev, availableDev) {
403 bool found = m_devList.contains(DevListItem(connection, dev));
405 if (!found) {
406 registerDevice(connection, dev);
412 * Register a device from a specific connection plugin
413 * @param devN contains the connection shortname + device name which is diplayed in the tooltip
414 * @param disp is the name that is displayed in the dropdown menu
415 * @param name is the actual device name
417 void ConnectionManager::registerDevice(IConnection *conn, IConnection::device device)
419 DevListItem d(conn, device);
421 m_devList.append(d);
425 * A device plugin notified us that its device list has changed
426 * (eg: user plug/unplug a usb device)
427 * \param[in] connection Connection type which signaled the update
429 void ConnectionManager::devChanged(IConnection *connection)
431 if (!ExtensionSystem::PluginManager::instance()->allPluginsLoaded()) {
432 connectionBackup.append(connection);
433 connect(ExtensionSystem::PluginManager::instance(), SIGNAL(pluginsLoadEnded()), this, SLOT(connectionsCallBack()), Qt::UniqueConnection);
434 return;
436 // clear device list combobox
437 m_availableDevList->clear();
439 // remove registered devices of this IConnection from the list
440 updateConnectionList(connection);
442 updateConnectionDropdown();
444 qDebug() << "# devices " << m_devList.count();
445 emit availableDevicesChanged(m_devList);
448 // disable connection button if the liNameif (m_availableDevList->count() > 0)
449 if (m_availableDevList->count() > 0) {
450 m_connectBtn->setEnabled(true);
451 } else {
452 m_connectBtn->setEnabled(false);
456 void ConnectionManager::updateConnectionDropdown()
458 // add all the list again to the combobox
459 foreach(DevListItem d, m_devList) {
460 m_availableDevList->addItem(d.getConName());
461 m_availableDevList->setItemData(m_availableDevList->count() - 1, d.getConName(), Qt::ToolTipRole);
462 if (!m_ioDev && d.getConName().startsWith("USB")) {
463 if (m_mainWindow->generalSettings()->autoConnect() || m_mainWindow->generalSettings()->autoSelect()) {
464 m_availableDevList->setCurrentIndex(m_availableDevList->count() - 1);
466 if (m_mainWindow->generalSettings()->autoConnect() && polling) {
467 qDebug() << "Automatically opening device";
468 connectDevice(d);
469 qDebug() << "ConnectionManager::updateConnectionDropdown autoconnected USB device";
473 if (m_ioDev) {
474 // if a device is connected make it the one selected on the dropbox
475 for (int i = 0; i < m_availableDevList->count(); i++) {
476 QString deviceName = m_availableDevList->itemData(i, Qt::ToolTipRole).toString();
477 if (m_connectionDevice.getConName() == deviceName) {
478 m_availableDevList->setCurrentIndex(i);
482 // update combo box tooltip
483 onDeviceSelectionChanged(m_availableDevList->currentIndex());
486 void Core::ConnectionManager::connectionsCallBack()
488 foreach(IConnection * con, connectionBackup) {
489 devChanged(con);
491 connectionBackup.clear();
492 disconnect(ExtensionSystem::PluginManager::instance(), SIGNAL(pluginsLoadEnded()), this, SLOT(connectionsCallBack()));
494 } // namespace Core