Commit aa1b3191 authored by Don Gagne's avatar Don Gagne

Merge pull request #1382 from DonLakeFlyer/LinkDelete

Use QSharedPointer for cross-thread Link references
parents 86268aeb 1d737cb8
...@@ -48,7 +48,7 @@ void FactSystemTestBase::_init(MAV_AUTOPILOT autopilot) ...@@ -48,7 +48,7 @@ void FactSystemTestBase::_init(MAV_AUTOPILOT autopilot)
MockLink* link = new MockLink(); MockLink* link = new MockLink();
link->setAutopilotType(autopilot); link->setAutopilotType(autopilot);
_linkMgr->addLink(link); _linkMgr->_addLink(link);
_linkMgr->connectLink(link); _linkMgr->connectLink(link);
// Wait for the uas to work it's way through the various threads // Wait for the uas to work it's way through the various threads
......
...@@ -35,7 +35,7 @@ Rectangle { ...@@ -35,7 +35,7 @@ Rectangle {
QGCLabel { QGCLabel {
width: parent.width width: parent.width
text: "You must be connected to your board to use all available setup options." text: "Full setup options are only available when connected to vehicle and full parameter list has completed downloading."
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
} }
......
...@@ -64,7 +64,7 @@ void SetupViewTest::_clickThrough_test(void) ...@@ -64,7 +64,7 @@ void SetupViewTest::_clickThrough_test(void)
MockLink* link = new MockLink(); MockLink* link = new MockLink();
Q_CHECK_PTR(link); Q_CHECK_PTR(link);
link->setAutopilotType(MAV_AUTOPILOT_PX4); link->setAutopilotType(MAV_AUTOPILOT_PX4);
LinkManager::instance()->addLink(link); LinkManager::instance()->_addLink(link);
linkMgr->connectLink(link); linkMgr->connectLink(link);
QTest::qWait(5000); // Give enough time for UI to settle and heartbeats to go through QTest::qWait(5000); // Give enough time for UI to settle and heartbeats to go through
......
...@@ -37,6 +37,7 @@ along with PIXHAWK. If not, see <http://www.gnu.org/licenses/>. ...@@ -37,6 +37,7 @@ along with PIXHAWK. If not, see <http://www.gnu.org/licenses/>.
#include <QMutex> #include <QMutex>
#include <QMutexLocker> #include <QMutexLocker>
#include <QMetaType> #include <QMetaType>
#include <QSharedPointer>
class LinkManager; class LinkManager;
class LinkConfiguration; class LinkConfiguration;
...@@ -50,38 +51,10 @@ class LinkInterface : public QThread ...@@ -50,38 +51,10 @@ class LinkInterface : public QThread
{ {
Q_OBJECT Q_OBJECT
// Only LinkManager is allowed to _connect, _disconnect or delete a link // Only LinkManager is allowed to create/delete or _connect/_disconnect a link
friend class LinkManager; friend class LinkManager;
public: public:
LinkInterface() :
QThread(0),
_ownedByLinkManager(false),
_deletedByLinkManager(false),
_flaggedForDeletion(false)
{
// Initialize everything for the data rate calculation buffers.
inDataIndex = 0;
outDataIndex = 0;
// Initialize our data rate buffers.
memset(inDataWriteAmounts, 0, sizeof(inDataWriteAmounts));
memset(inDataWriteTimes, 0, sizeof(inDataWriteTimes));
memset(outDataWriteAmounts,0, sizeof(outDataWriteAmounts));
memset(outDataWriteTimes, 0, sizeof(outDataWriteTimes));
qRegisterMetaType<LinkInterface*>("LinkInterface*");
}
/**
* @brief Destructor
* LinkManager take ownership of Links once they are added to it. Once added to LinkManager
* use LinkManager::deleteLink to remove if necessary.
**/
virtual ~LinkInterface() {
Q_ASSERT(!_ownedByLinkManager || _deletedByLinkManager);
}
/** /**
* @brief Get link configuration (if used) * @brief Get link configuration (if used)
* @return A pointer to the instance of LinkConfiguration if supported. NULL otherwise. * @return A pointer to the instance of LinkConfiguration if supported. NULL otherwise.
...@@ -169,7 +142,25 @@ public slots: ...@@ -169,7 +142,25 @@ public slots:
* @param length The length of the data array * @param length The length of the data array
**/ **/
virtual void writeBytes(const char *bytes, qint64 length) = 0; virtual void writeBytes(const char *bytes, qint64 length) = 0;
protected:
// Links are only created by LinkManager so constructor is not public
LinkInterface() :
QThread(0)
{
// Initialize everything for the data rate calculation buffers.
inDataIndex = 0;
outDataIndex = 0;
// Initialize our data rate buffers.
memset(inDataWriteAmounts, 0, sizeof(inDataWriteAmounts));
memset(inDataWriteTimes, 0, sizeof(inDataWriteTimes));
memset(outDataWriteAmounts,0, sizeof(outDataWriteAmounts));
memset(outDataWriteTimes, 0, sizeof(outDataWriteTimes));
qRegisterMetaType<LinkInterface*>("LinkInterface*");
}
signals: signals:
/** /**
...@@ -338,10 +329,8 @@ private: ...@@ -338,10 +329,8 @@ private:
* @return True if connection could be terminated, false otherwise * @return True if connection could be terminated, false otherwise
**/ **/
virtual bool _disconnect(void) = 0; virtual bool _disconnect(void) = 0;
bool _ownedByLinkManager; ///< true: This link has been added to LinkManager, false: Link not added to LinkManager
bool _deletedByLinkManager; ///< true: Link being deleted from LinkManager, false: error, Links should only be deleted from LinkManager
bool _flaggedForDeletion; ///< true: Garbage colletion ready
}; };
typedef QSharedPointer<LinkInterface> SharedLinkInterface;
#endif // _LINKINTERFACE_H_ #endif // _LINKINTERFACE_H_
...@@ -52,6 +52,7 @@ LinkManager::LinkManager(QObject* parent) ...@@ -52,6 +52,7 @@ LinkManager::LinkManager(QObject* parent)
, _configUpdateSuspended(false) , _configUpdateSuspended(false)
, _configurationsLoaded(false) , _configurationsLoaded(false)
, _connectionsSuspended(false) , _connectionsSuspended(false)
, _nullSharedLink(NULL)
{ {
connect(&_portListTimer, &QTimer::timeout, this, &LinkManager::_updateConfigurationList); connect(&_portListTimer, &QTimer::timeout, this, &LinkManager::_updateConfigurationList);
_portListTimer.start(1000); _portListTimer.start(1000);
...@@ -68,7 +69,7 @@ LinkManager::~LinkManager() ...@@ -68,7 +69,7 @@ LinkManager::~LinkManager()
Q_ASSERT_X(_links.count() == 0, "LinkManager", "LinkManager::_shutdown should have been called previously"); Q_ASSERT_X(_links.count() == 0, "LinkManager", "LinkManager::_shutdown should have been called previously");
} }
LinkInterface* LinkManager::createLink(LinkConfiguration* config) LinkInterface* LinkManager::createConnectedLink(LinkConfiguration* config)
{ {
Q_ASSERT(config); Q_ASSERT(config);
LinkInterface* pLink = NULL; LinkInterface* pLink = NULL;
...@@ -89,33 +90,31 @@ LinkInterface* LinkManager::createLink(LinkConfiguration* config) ...@@ -89,33 +90,31 @@ LinkInterface* LinkManager::createLink(LinkConfiguration* config)
#endif #endif
} }
if(pLink) { if(pLink) {
addLink(pLink); _addLink(pLink);
connectLink(pLink);
} }
return pLink; return pLink;
} }
LinkInterface* LinkManager::createLink(const QString& name) LinkInterface* LinkManager::createConnectedLink(const QString& name)
{ {
Q_ASSERT(name.isEmpty() == false); Q_ASSERT(name.isEmpty() == false);
for(int i = 0; i < _linkConfigurations.count(); i++) { for(int i = 0; i < _linkConfigurations.count(); i++) {
LinkConfiguration* conf = _linkConfigurations.at(i); LinkConfiguration* conf = _linkConfigurations.at(i);
if(conf && conf->name() == name) if(conf && conf->name() == name)
return createLink(conf); return createConnectedLink(conf);
} }
return NULL; return NULL;
} }
void LinkManager::addLink(LinkInterface* link) void LinkManager::_addLink(LinkInterface* link)
{ {
Q_ASSERT(link); Q_ASSERT(link);
// Take ownership for delete
link->_ownedByLinkManager = true;
_linkListMutex.lock(); _linkListMutex.lock();
if (!_links.contains(link)) { if (!containsLink(link)) {
_links.append(link); _links.append(QSharedPointer<LinkInterface>(link));
_linkListMutex.unlock(); _linkListMutex.unlock();
emit newLink(link); emit newLink(link);
} else { } else {
...@@ -145,14 +144,12 @@ bool LinkManager::connectAll() ...@@ -145,14 +144,12 @@ bool LinkManager::connectAll()
bool allConnected = true; bool allConnected = true;
_linkListMutex.lock(); foreach (SharedLinkInterface sharedLink, _links) {
foreach (LinkInterface* link, _links) { Q_ASSERT(sharedLink.data());
Q_ASSERT(link); if (!sharedLink.data()->_connect()) {
if (!link->_connect()) {
allConnected = false; allConnected = false;
} }
} }
_linkListMutex.unlock();
return allConnected; return allConnected;
} }
...@@ -161,15 +158,12 @@ bool LinkManager::disconnectAll() ...@@ -161,15 +158,12 @@ bool LinkManager::disconnectAll()
{ {
bool allDisconnected = true; bool allDisconnected = true;
_linkListMutex.lock(); foreach (SharedLinkInterface sharedLink, _links) {
foreach (LinkInterface* link, _links) Q_ASSERT(sharedLink.data());
{ if (!sharedLink.data()->_disconnect()) {
Q_ASSERT(link);
if (!link->_disconnect()) {
allDisconnected = false; allDisconnected = false;
} }
} }
_linkListMutex.unlock();
return allDisconnected; return allDisconnected;
} }
...@@ -197,51 +191,34 @@ bool LinkManager::disconnectLink(LinkInterface* link) ...@@ -197,51 +191,34 @@ bool LinkManager::disconnectLink(LinkInterface* link)
if(config) { if(config) {
config->setLink(NULL); config->setLink(NULL);
} }
// Link is now done and over with. We can't yet delete it because it _deleteLink(link);
// takes a while for the MAVLink protocol to take notice of it. We
// flag it for delayed deletion for final clean up.
link->_flaggedForDeletion = true;
QTimer::singleShot(1000, this, &LinkManager::_delayedDeleteLink);
return true; return true;
} else { } else {
return false; return false;
} }
} }
void LinkManager::_delayedDeleteLink() void LinkManager::_deleteLink(LinkInterface* link)
{
_linkListMutex.lock();
foreach (LinkInterface* link, _links)
{
Q_ASSERT(link);
if (link->_flaggedForDeletion) {
qDebug() << "Link deleted: " << link->getName();
_linkListMutex.unlock();
deleteLink(link);
return;
}
}
_linkListMutex.unlock();
}
void LinkManager::deleteLink(LinkInterface* link)
{ {
Q_ASSERT(link); Q_ASSERT(link);
_linkListMutex.lock(); _linkListMutex.lock();
Q_ASSERT(_links.contains(link)); bool found = false;
_links.removeOne(link); for (int i=0; i<_links.count(); i++) {
Q_ASSERT(!_links.contains(link)); if (_links[i].data() == link) {
_links.removeAt(i);
found = true;
break;
}
}
Q_UNUSED(found);
Q_ASSERT(found);
_linkListMutex.unlock(); _linkListMutex.unlock();
// Emit removal of link // Emit removal of link
emit linkDeleted(link); emit linkDeleted(link);
Q_ASSERT(link->_ownedByLinkManager);
link->_deletedByLinkManager = true; // Signal that this is a valid delete
delete link;
} }
/** /**
...@@ -249,29 +226,13 @@ void LinkManager::deleteLink(LinkInterface* link) ...@@ -249,29 +226,13 @@ void LinkManager::deleteLink(LinkInterface* link)
*/ */
const QList<LinkInterface*> LinkManager::getLinks() const QList<LinkInterface*> LinkManager::getLinks()
{ {
_linkListMutex.lock(); QList<LinkInterface*> list;
QList<LinkInterface*> ret(_links);
_linkListMutex.unlock(); foreach (SharedLinkInterface sharedLink, _links) {
return ret; list << sharedLink.data();
}
const QList<SerialLink *> LinkManager::getSerialLinks()
{
_linkListMutex.lock();
QList<SerialLink*> s;
foreach (LinkInterface* link, _links)
{
Q_ASSERT(link);
SerialLink* serialLink = qobject_cast<SerialLink*>(link);
if (serialLink)
s.append(serialLink);
} }
_linkListMutex.unlock();
return list;
return s;
} }
/// @brief If all new connections should be suspended a message is displayed to the user and true /// @brief If all new connections should be suspended a message is displayed to the user and true
...@@ -296,10 +257,8 @@ void LinkManager::setConnectionsSuspended(QString reason) ...@@ -296,10 +257,8 @@ void LinkManager::setConnectionsSuspended(QString reason)
void LinkManager::_shutdown(void) void LinkManager::_shutdown(void)
{ {
QList<LinkInterface*> links = _links; while (_links.count() != 0) {
foreach(LinkInterface* link, links) { disconnectLink(_links[0].data());
disconnectLink(link);
deleteLink(link);
} }
} }
...@@ -489,3 +448,43 @@ void LinkManager::_updateConfigurationList(void) ...@@ -489,3 +448,43 @@ void LinkManager::_updateConfigurationList(void)
} }
} }
bool LinkManager::containsLink(LinkInterface* link)
{
bool found = false;
foreach (SharedLinkInterface sharedLink, _links) {
if (sharedLink.data() == link) {
found = true;
break;
}
}
return found;
}
bool LinkManager::anyConnectedLinks(void)
{
bool found = false;
foreach (SharedLinkInterface sharedLink, _links) {
if (sharedLink.data()->isConnected()) {
found = true;
break;
}
}
return found;
}
SharedLinkInterface& LinkManager::sharedPointerForLink(LinkInterface* link)
{
for (int i=0; i<_links.count(); i++) {
if (_links[i].data() == link) {
return _links[i];
}
}
// This should never happen
Q_ASSERT(false);
return _nullSharedLink;
}
...@@ -103,21 +103,18 @@ public: ...@@ -103,21 +103,18 @@ public:
/// Sets the flag to allow new connections to be made /// Sets the flag to allow new connections to be made
void setConnectionsAllowed(void) { _connectionsSuspended = false; } void setConnectionsAllowed(void) { _connectionsSuspended = false; }
/// Creates (and adds) a link based on the given configuration instance. LinkManager takes ownership of this object. To delete /// Creates, connects (and adds) a link based on the given configuration instance.
/// it, call LinkManager::deleteLink. LinkInterface* createConnectedLink(LinkConfiguration* config);
LinkInterface* createLink(LinkConfiguration* config);
/// Creates (and adds) a link based on the given configuration name. LinkManager takes ownership of this object. To delete /// Creates, connects (and adds) a link based on the given configuration name.
/// it, call LinkManager::deleteLink. LinkInterface* createConnectedLink(const QString& name);
LinkInterface* createLink(const QString& name);
/// Adds the link to the LinkManager. LinkManager takes ownership of this object. To delete /// Returns true if the link manager is holding this link
/// it, call LinkManager::deleteLink. bool containsLink(LinkInterface* link);
void addLink(LinkInterface* link);
/// Returns the QSharedPointer for this link. You must use SharedLinkInterface if you are going to
/// Deletes the specified link. Will disconnect if connected. /// keep references to a link in a thread other than the main ui thread.
// TODO Will also crash if called. MAVLink protocol is not handling the disconnect properly. SharedLinkInterface& sharedPointerForLink(LinkInterface* link);
void deleteLink(LinkInterface* link);
/// Re-connects all existing links /// Re-connects all existing links
bool connectAll(); bool connectAll();
...@@ -130,6 +127,14 @@ public: ...@@ -130,6 +127,14 @@ public:
/// Disconnect the specified link /// Disconnect the specified link
bool disconnectLink(LinkInterface* link); bool disconnectLink(LinkInterface* link);
/// Returns true if there are any connected links
bool anyConnectedLinks(void);
// The following APIs are public but should not be called in normal use. The are mainly exposed
// here for unit test code.
void _deleteLink(LinkInterface* link);
void _addLink(LinkInterface* link);
signals: signals:
void newLink(LinkInterface* link); void newLink(LinkInterface* link);
...@@ -141,13 +146,12 @@ signals: ...@@ -141,13 +146,12 @@ signals:
private slots: private slots:
void _linkConnected(void); void _linkConnected(void);
void _linkDisconnected(void); void _linkDisconnected(void);
void _delayedDeleteLink();
private: private:
/// All access to LinkManager is through LinkManager::instance /// All access to LinkManager is through LinkManager::instance
LinkManager(QObject* parent = NULL); LinkManager(QObject* parent = NULL);
~LinkManager(); ~LinkManager();
virtual void _shutdown(void); virtual void _shutdown(void);
bool _connectionsSuspendedMsg(void); bool _connectionsSuspendedMsg(void);
...@@ -155,7 +159,12 @@ private: ...@@ -155,7 +159,12 @@ private:
SerialConfiguration* _findSerialConfiguration(const QString& portName); SerialConfiguration* _findSerialConfiguration(const QString& portName);
QList<LinkConfiguration*> _linkConfigurations; ///< List of configured links QList<LinkConfiguration*> _linkConfigurations; ///< List of configured links
QList<LinkInterface*> _links; ///< List of available links
/// List of available links kept as QSharedPointers. We use QSharedPointer since
/// there are other objects that maintain copies of these links in other threads.
/// The reference counting allows for orderly deletion.
QList<SharedLinkInterface> _links;
QMutex _linkListMutex; ///< Mutex for thread safe access to _links list QMutex _linkListMutex; ///< Mutex for thread safe access to _links list
bool _configUpdateSuspended; ///< true: stop updating configuration list bool _configUpdateSuspended; ///< true: stop updating configuration list
...@@ -163,6 +172,8 @@ private: ...@@ -163,6 +172,8 @@ private:
bool _connectionsSuspended; ///< true: all new connections should not be allowed bool _connectionsSuspended; ///< true: all new connections should not be allowed
QString _connectionsSuspendedReason; ///< User visible reason for suspension QString _connectionsSuspendedReason; ///< User visible reason for suspension
QTimer _portListTimer; QTimer _portListTimer;
SharedLinkInterface _nullSharedLink;
}; };
#endif #endif
...@@ -175,8 +175,12 @@ void MAVLinkProtocol::_linkStatusChanged(LinkInterface* link, bool connected) ...@@ -175,8 +175,12 @@ void MAVLinkProtocol::_linkStatusChanged(LinkInterface* link, bool connected)
Q_ASSERT(link); Q_ASSERT(link);
if (connected) { if (connected) {
Q_ASSERT(!_connectedLinks.contains(link)); foreach (SharedLinkInterface sharedLink, _connectedLinks) {
_connectedLinks.append(link); Q_ASSERT(sharedLink.data() != link);
}
// Use the same shared pointer as LinkManager
_connectedLinks.append(LinkManager::instance()->sharedPointerForLink(link));
if (_connectedLinks.count() == 1) { if (_connectedLinks.count() == 1) {
// This is the first link, we need to start logging // This is the first link, we need to start logging
...@@ -192,8 +196,16 @@ void MAVLinkProtocol::_linkStatusChanged(LinkInterface* link, bool connected) ...@@ -192,8 +196,16 @@ void MAVLinkProtocol::_linkStatusChanged(LinkInterface* link, bool connected)
link->writeBytes(cmd, strlen(cmd)); link->writeBytes(cmd, strlen(cmd));
link->writeBytes(init, 4); link->writeBytes(init, 4);
} else { } else {
Q_ASSERT(_connectedLinks.contains(link)); bool found = false;
_connectedLinks.removeOne(link); for (int i=0; i<_connectedLinks.count(); i++) {
if (_connectedLinks[i].data() == link) {
found = true;
_connectedLinks.removeAt(i);
break;
}
}
Q_UNUSED(found);
Q_ASSERT(found);
if (_connectedLinks.count() == 0) { if (_connectedLinks.count() == 0) {
// Last link is gone, close out logging // Last link is gone, close out logging
......
...@@ -285,7 +285,10 @@ private: ...@@ -285,7 +285,10 @@ private:
void _startLogging(void); void _startLogging(void);
void _stopLogging(void); void _stopLogging(void);
QList<LinkInterface*> _connectedLinks; ///< List of all links connected to protocol /// List of all links connected to protocol. We keep SharedLinkInterface objects
/// which are QSharedPointer's in order to maintain reference counts across threads.
/// This way Link deletion works correctly.
QList<SharedLinkInterface> _connectedLinks;
bool _logSuspendError; ///< true: Logging suspended due to error bool _logSuspendError; ///< true: Logging suspended due to error
bool _logSuspendReplay; ///< true: Logging suspended due to replay bool _logSuspendReplay; ///< true: Logging suspended due to replay
......
...@@ -106,7 +106,7 @@ MAVLinkSimulationLink::MAVLinkSimulationLink(QString readFile, QString writeFile ...@@ -106,7 +106,7 @@ MAVLinkSimulationLink::MAVLinkSimulationLink(QString readFile, QString writeFile
srand(QTime::currentTime().msec()); srand(QTime::currentTime().msec());
maxTimeNoise = 0; maxTimeNoise = 0;
this->id = getNextLinkId(); this->id = getNextLinkId();
LinkManager::instance()->addLink(this); LinkManager::instance()->_addLink(this);
} }
MAVLinkSimulationLink::~MAVLinkSimulationLink() MAVLinkSimulationLink::~MAVLinkSimulationLink()
......
...@@ -102,12 +102,11 @@ private: ...@@ -102,12 +102,11 @@ private:
class SerialLink : public LinkInterface class SerialLink : public LinkInterface
{ {
Q_OBJECT Q_OBJECT
friend class SerialConfiguration; friend class SerialConfiguration;
friend class LinkManager;
public: public:
SerialLink(SerialConfiguration* config);
~SerialLink();
// LinkInterface // LinkInterface
LinkConfiguration* getLinkConfiguration(); LinkConfiguration* getLinkConfiguration();
...@@ -154,9 +153,13 @@ private slots: ...@@ -154,9 +153,13 @@ private slots:
void _rerouteDisconnected(void); void _rerouteDisconnected(void);
private: private:
// Links are only created/destroyed by LinkManager so constructor/destructor is not public
SerialLink(SerialConfiguration* config);
~SerialLink();
// From LinkInterface // From LinkInterface
bool _connect(void); virtual bool _connect(void);
bool _disconnect(void); virtual bool _disconnect(void);
// Internal methods // Internal methods
void _emitLinkError(const QString& errorMsg); void _emitLinkError(const QString& errorMsg);
......
...@@ -115,12 +115,12 @@ private: ...@@ -115,12 +115,12 @@ private:
class TCPLink : public LinkInterface class TCPLink : public LinkInterface
{ {
Q_OBJECT Q_OBJECT
friend class TCPLinkUnitTest; friend class TCPLinkUnitTest;
friend class TCPConfiguration; friend class TCPConfiguration;
public: friend class LinkManager;
TCPLink(TCPConfiguration* config);
~TCPLink();
public:
QTcpSocket* getSocket(void) { return _socket; } QTcpSocket* getSocket(void) { return _socket; }
void signalBytesWritten(void); void signalBytesWritten(void);
...@@ -159,9 +159,13 @@ protected: ...@@ -159,9 +159,13 @@ protected:
virtual void run(void); virtual void run(void);
private: private:
// Links are only created/destroyed by LinkManager so constructor/destructor is not public
TCPLink(TCPConfiguration* config);
~TCPLink();
// From LinkInterface // From LinkInterface
bool _connect(void); virtual bool _connect(void);
bool _disconnect(void); virtual bool _disconnect(void);
bool _hardwareConnect(); bool _hardwareConnect();
void _restartConnection(); void _restartConnection();
......
...@@ -143,11 +143,11 @@ private: ...@@ -143,11 +143,11 @@ private:
class UDPLink : public LinkInterface class UDPLink : public LinkInterface
{ {
Q_OBJECT Q_OBJECT
friend class UDPConfiguration; friend class UDPConfiguration;
friend class LinkManager;
public: public:
UDPLink(UDPConfiguration* config);
~UDPLink();
void requestReset() { } void requestReset() { }
bool isConnected() const; bool isConnected() const;
QString getName() const; QString getName() const;
...@@ -192,6 +192,10 @@ protected: ...@@ -192,6 +192,10 @@ protected:
int _id; int _id;
private: private:
// Links are only created/destroyed by LinkManager so constructor/destructor is not public
UDPLink(UDPConfiguration* config);
~UDPLink();
// From LinkInterface // From LinkInterface
virtual bool _connect(void); virtual bool _connect(void);
virtual bool _disconnect(void); virtual bool _disconnect(void);
......
...@@ -78,7 +78,7 @@ void LinkManagerTest::_add_test(void) ...@@ -78,7 +78,7 @@ void LinkManagerTest::_add_test(void)
Q_ASSERT(_linkMgr->getLinks().count() == 0); Q_ASSERT(_linkMgr->getLinks().count() == 0);
MockLink* link = new MockLink(); MockLink* link = new MockLink();
_linkMgr->addLink(link); _linkMgr->_addLink(link);
QList<LinkInterface*> links = _linkMgr->getLinks(); QList<LinkInterface*> links = _linkMgr->getLinks();
QCOMPARE(links.count(), 1); QCOMPARE(links.count(), 1);
...@@ -91,8 +91,8 @@ void LinkManagerTest::_delete_test(void) ...@@ -91,8 +91,8 @@ void LinkManagerTest::_delete_test(void)
Q_ASSERT(_linkMgr->getLinks().count() == 0); Q_ASSERT(_linkMgr->getLinks().count() == 0);
MockLink* link = new MockLink(); MockLink* link = new MockLink();
_linkMgr->addLink(link); _linkMgr->_addLink(link);
_linkMgr->deleteLink(link); _linkMgr->_deleteLink(link);
QCOMPARE(_linkMgr->getLinks().count(), 0); QCOMPARE(_linkMgr->getLinks().count(), 0);
} }
...@@ -104,7 +104,7 @@ void LinkManagerTest::_addSignals_test(void) ...@@ -104,7 +104,7 @@ void LinkManagerTest::_addSignals_test(void)
Q_ASSERT(_multiSpy->checkNoSignals() == true); Q_ASSERT(_multiSpy->checkNoSignals() == true);
MockLink* link = new MockLink(); MockLink* link = new MockLink();
_linkMgr->addLink(link); _linkMgr->_addLink(link);
QCOMPARE(_multiSpy->checkOnlySignalByMask(newLinkSignalMask), true); QCOMPARE(_multiSpy->checkOnlySignalByMask(newLinkSignalMask), true);
QSignalSpy* spy = _multiSpy->getSpyByIndex(newLinkSignalIndex); QSignalSpy* spy = _multiSpy->getSpyByIndex(newLinkSignalIndex);
...@@ -125,10 +125,10 @@ void LinkManagerTest::_deleteSignals_test(void) ...@@ -125,10 +125,10 @@ void LinkManagerTest::_deleteSignals_test(void)
Q_ASSERT(_multiSpy->checkNoSignals() == true); Q_ASSERT(_multiSpy->checkNoSignals() == true);
MockLink* link = new MockLink(); MockLink* link = new MockLink();
_linkMgr->addLink(link); _linkMgr->_addLink(link);
_multiSpy->clearAllSignals(); _multiSpy->clearAllSignals();
_linkMgr->deleteLink(link); _linkMgr->_deleteLink(link);
QCOMPARE(_multiSpy->checkOnlySignalByMask(linkDeletedSignalMask), true); QCOMPARE(_multiSpy->checkOnlySignalByMask(linkDeletedSignalMask), true);
QSignalSpy* spy = _multiSpy->getSpyByIndex(linkDeletedSignalIndex); QSignalSpy* spy = _multiSpy->getSpyByIndex(linkDeletedSignalIndex);
......
...@@ -67,7 +67,7 @@ void MainWindowTest::_connectWindowClose_test(MAV_AUTOPILOT autopilot) ...@@ -67,7 +67,7 @@ void MainWindowTest::_connectWindowClose_test(MAV_AUTOPILOT autopilot)
MockLink* link = new MockLink(); MockLink* link = new MockLink();
Q_CHECK_PTR(link); Q_CHECK_PTR(link);
link->setAutopilotType(autopilot); link->setAutopilotType(autopilot);
LinkManager::instance()->addLink(link); LinkManager::instance()->_addLink(link);
linkMgr->connectLink(link); linkMgr->connectLink(link);
QTest::qWait(5000); // Give enough time for UI to settle and heartbeats to go through QTest::qWait(5000); // Give enough time for UI to settle and heartbeats to go through
......
...@@ -140,7 +140,7 @@ void MavlinkLogTest::_connectLog_test(void) ...@@ -140,7 +140,7 @@ void MavlinkLogTest::_connectLog_test(void)
MockLink* link = new MockLink(); MockLink* link = new MockLink();
Q_CHECK_PTR(link); Q_CHECK_PTR(link);
LinkManager::instance()->addLink(link); LinkManager::instance()->_addLink(link);
linkMgr->connectLink(link); linkMgr->connectLink(link);
QTest::qWait(5000); // Give enough time for UI to settle and heartbeats to go through QTest::qWait(5000); // Give enough time for UI to settle and heartbeats to go through
......
...@@ -380,8 +380,7 @@ bool UAS::getSelected() const ...@@ -380,8 +380,7 @@ bool UAS::getSelected() const
void UAS::receiveMessage(LinkInterface* link, mavlink_message_t message) void UAS::receiveMessage(LinkInterface* link, mavlink_message_t message)
{ {
if (!links.contains(link)) if (!_containsLink(link)) {
{
addLink(link); addLink(link);
// qDebug() << __FILE__ << __LINE__ << "ADDED LINK!" << link->getName(); // qDebug() << __FILE__ << __LINE__ << "ADDED LINK!" << link->getName();
} }
...@@ -873,8 +872,6 @@ void UAS::receiveMessage(LinkInterface* link, mavlink_message_t message) ...@@ -873,8 +872,6 @@ void UAS::receiveMessage(LinkInterface* link, mavlink_message_t message)
} }
positionLock = true; positionLock = true;
isGlobalPositionKnown = true; isGlobalPositionKnown = true;
//TODO fix this hack for forwarding of global position for patch antenna tracking
//forwardMessage(message);
} }
break; break;
case MAVLINK_MSG_ID_GPS_RAW_INT: case MAVLINK_MSG_ID_GPS_RAW_INT:
...@@ -1725,49 +1722,21 @@ void UAS::sendMessage(mavlink_message_t message) ...@@ -1725,49 +1722,21 @@ void UAS::sendMessage(mavlink_message_t message)
return; return;
} }
if (links.count() < 1) { if (_links.count() < 1) {
qDebug() << "NO LINK AVAILABLE TO SEND!"; qDebug() << "NO LINK AVAILABLE TO SEND!";
} }
// Emit message on all links that are currently connected // Emit message on all links that are currently connected
foreach (LinkInterface* link, links) { foreach (SharedLinkInterface sharedLink, _links) {
LinkInterface* link = sharedLink.data();
Q_ASSERT(link);
if (link->isConnected()) { if (link->isConnected()) {
sendMessage(link, message); sendMessage(link, message);
} }
} }
} }
/**
* Forward a message to all links that are currently connected.
* @param message that is to be forwarded
*/
void UAS::forwardMessage(mavlink_message_t message)
{
// Emit message on all links that are currently connected
QList<LinkInterface*>link_list = LinkManager::instance()->getLinks();
foreach(LinkInterface* link, link_list)
{
if (link)
{
SerialLink* serial = dynamic_cast<SerialLink*>(link);
if(serial != 0)
{
for(int i=0; i<links.size(); i++)
{
if(serial != links.at(i))
{
if (link->isConnected()) {
qDebug()<<"Antenna tracking: Forwarding Over link: "<<serial->getName()<<" "<<serial;
sendMessage(serial, message);
}
}
}
}
}
}
}
/** /**
* Send a message to the link that is connected. * Send a message to the link that is connected.
* @param link that the message will be sent to * @param link that the message will be sent to
...@@ -3274,9 +3243,9 @@ const QString& UAS::getShortMode() const ...@@ -3274,9 +3243,9 @@ const QString& UAS::getShortMode() const
*/ */
void UAS::addLink(LinkInterface* link) void UAS::addLink(LinkInterface* link)
{ {
if (!links.contains(link)) if (!_containsLink(link))
{ {
links.append(link); _links.append(LinkManager::instance()->sharedPointerForLink(link));
qCDebug(UASLog) << "addLink:" << QString("%1").arg((ulong)link, 0, 16); qCDebug(UASLog) << "addLink:" << QString("%1").arg((ulong)link, 0, 16);
connect(link, SIGNAL(destroyed(QObject*)), this, SLOT(removeLink(QObject*))); connect(link, SIGNAL(destroyed(QObject*)), this, SLOT(removeLink(QObject*)));
} }
...@@ -3285,16 +3254,16 @@ void UAS::addLink(LinkInterface* link) ...@@ -3285,16 +3254,16 @@ void UAS::addLink(LinkInterface* link)
void UAS::removeLink(QObject* object) void UAS::removeLink(QObject* object)
{ {
qCDebug(UASLog) << "removeLink:" << QString("%1").arg((ulong)object, 0, 16); qCDebug(UASLog) << "removeLink:" << QString("%1").arg((ulong)object, 0, 16);
qCDebug(UASLog) << "link count:" << links.count(); qCDebug(UASLog) << "link count:" << _links.count();
// Do not dynamic cast or de-reference QObject, since object is either in destructor or may have already LinkInterface* link = dynamic_cast<LinkInterface*>(object);
// been destroyed.
LinkInterface* link = (LinkInterface*)object; for (int i=0; i<_links.count(); i++) {
if (_links[i].data() == link) {
int index = links.indexOf(link); _links.removeAt(i);
Q_ASSERT(index != -1); break;
links.removeAt(index); }
}
} }
/** /**
...@@ -3302,7 +3271,13 @@ void UAS::removeLink(QObject* object) ...@@ -3302,7 +3271,13 @@ void UAS::removeLink(QObject* object)
*/ */
QList<LinkInterface*> UAS::getLinks() QList<LinkInterface*> UAS::getLinks()
{ {
return links; QList<LinkInterface*> list;
foreach (SharedLinkInterface sharedLink, _links) {
list << sharedLink.data();
}
return list;
} }
/** /**
...@@ -3429,3 +3404,14 @@ void UAS::unsetRCToParameterMap() ...@@ -3429,3 +3404,14 @@ void UAS::unsetRCToParameterMap()
sendMessage(message); sendMessage(message);
} }
} }
bool UAS::_containsLink(LinkInterface* link)
{
foreach (SharedLinkInterface sharedLink, _links) {
if (sharedLink.data() == link) {
return true;
}
}
return false;
}
...@@ -341,7 +341,11 @@ protected: //COMMENTS FOR TEST UNIT ...@@ -341,7 +341,11 @@ protected: //COMMENTS FOR TEST UNIT
/// LINK ID AND STATUS /// LINK ID AND STATUS
int uasId; ///< Unique system ID int uasId; ///< Unique system ID
QMap<int, QString> components;///< IDs and names of all detected onboard components QMap<int, QString> components;///< IDs and names of all detected onboard components
QList<LinkInterface*> links; ///< List of links this UAS can be reached by
/// List of all links associated with this UAS. We keep SharedLinkInterface objects which are QSharedPointer's in order to
/// maintain reference counts across threads. This way Link deletion works correctly.
QList<SharedLinkInterface> _links;
QList<int> unknownPackets; ///< Packet IDs which are unknown and have been received QList<int> unknownPackets; ///< Packet IDs which are unknown and have been received
MAVLinkProtocol* mavlink; ///< Reference to the MAVLink instance MAVLinkProtocol* mavlink; ///< Reference to the MAVLink instance
CommStatus commStatus; ///< Communication status CommStatus commStatus; ///< Communication status
...@@ -812,9 +816,6 @@ public slots: ...@@ -812,9 +816,6 @@ public slots:
/** @brief Send a message over all links this UAS can be reached with (!= all links) */ /** @brief Send a message over all links this UAS can be reached with (!= all links) */
void sendMessage(mavlink_message_t message); void sendMessage(mavlink_message_t message);
/** @brief Temporary Hack for sending packets to patch Antenna. Send a message over all serial links except for this UAS's */
void forwardMessage(mavlink_message_t message);
/** @brief Set this UAS as the system currently in focus, e.g. in the main display widgets */ /** @brief Set this UAS as the system currently in focus, e.g. in the main display widgets */
void setSelected(); void setSelected();
...@@ -951,6 +952,9 @@ protected slots: ...@@ -951,6 +952,9 @@ protected slots:
void writeSettings(); void writeSettings();
/** @brief Read settings from disk */ /** @brief Read settings from disk */
void readSettings(); void readSettings();
private:
bool _containsLink(LinkInterface* link);
}; };
......
...@@ -642,15 +642,7 @@ void MainWindow::normalActionItemCallback() ...@@ -642,15 +642,7 @@ void MainWindow::normalActionItemCallback()
void MainWindow::closeEvent(QCloseEvent *event) void MainWindow::closeEvent(QCloseEvent *event)
{ {
// Disallow window close if there are active connections // Disallow window close if there are active connections
bool foundConnections = false; if (LinkManager::instance()->anyConnectedLinks()) {
foreach(LinkInterface* link, LinkManager::instance()->getLinks()) {
if (link->isConnected()) {
foundConnections = true;
break;
}
}
if (foundConnections) {
QGCMessageBox::StandardButton button = QGCMessageBox::StandardButton button =
QGCMessageBox::warning( QGCMessageBox::warning(
tr("QGroundControl close"), tr("QGroundControl close"),
...@@ -658,9 +650,7 @@ void MainWindow::closeEvent(QCloseEvent *event) ...@@ -658,9 +650,7 @@ void MainWindow::closeEvent(QCloseEvent *event)
QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes | QMessageBox::Cancel,
QMessageBox::Cancel); QMessageBox::Cancel);
if (button == QMessageBox::Yes) { if (button == QMessageBox::Yes) {
foreach(LinkInterface* link, LinkManager::instance()->getLinks()) { LinkManager::instance()->disconnectAll();
LinkManager::instance()->disconnectLink(link);
}
} else { } else {
event->ignore(); event->ignore();
return; return;
...@@ -669,11 +659,10 @@ void MainWindow::closeEvent(QCloseEvent *event) ...@@ -669,11 +659,10 @@ void MainWindow::closeEvent(QCloseEvent *event)
// This will process any remaining flight log save dialogs // This will process any remaining flight log save dialogs
qgcApp()->processEvents(QEventLoop::ExcludeUserInputEvents); qgcApp()->processEvents(QEventLoop::ExcludeUserInputEvents);
// Should not be any active connections // Should not be any active connections
foreach(LinkInterface* link, LinkManager::instance()->getLinks()) { Q_ASSERT(!LinkManager::instance()->anyConnectedLinks());
Q_UNUSED(link);
Q_ASSERT(!link->isConnected());
}
_storeCurrentViewState(); _storeCurrentViewState();
storeSettings(); storeSettings();
UASManager::instance()->storeSettings(); UASManager::instance()->storeSettings();
...@@ -1355,11 +1344,7 @@ void MainWindow::restoreLastUsedConnection() ...@@ -1355,11 +1344,7 @@ void MainWindow::restoreLastUsedConnection()
if(settings.contains(key)) { if(settings.contains(key)) {
QString connection = settings.value(key).toString(); QString connection = settings.value(key).toString();
// Create a link for it // Create a link for it
LinkInterface* link = LinkManager::instance()->createLink(connection); LinkManager::instance()->createConnectedLink(connection);
if(link) {
// Connect it
LinkManager::instance()->connectLink(link);
}
} }
} }
......
...@@ -104,10 +104,8 @@ void QGCLinkConfiguration::on_connectLinkButton_clicked() ...@@ -104,10 +104,8 @@ void QGCLinkConfiguration::on_connectLinkButton_clicked()
LinkManager::instance()->disconnectLink(link); LinkManager::instance()->disconnectLink(link);
} }
} else { } else {
LinkInterface* link = LinkManager::instance()->createLink(config); LinkInterface* link = LinkManager::instance()->createConnectedLink(config);
if(link) { if(link) {
// Connect it
LinkManager::instance()->connectLink(link);
// Now go hunting for the parent so we can shut this down // Now go hunting for the parent so we can shut this down
QWidget* pQw = parentWidget(); QWidget* pQw = parentWidget();
while(pQw) { while(pQw) {
......
...@@ -246,18 +246,7 @@ void QGCMAVLinkLogPlayer::updatePositionSliderUi(float percent) ...@@ -246,18 +246,7 @@ void QGCMAVLinkLogPlayer::updatePositionSliderUi(float percent)
void QGCMAVLinkLogPlayer::_selectLogFileForPlayback(void) void QGCMAVLinkLogPlayer::_selectLogFileForPlayback(void)
{ {
// Disallow replay when any links are connected // Disallow replay when any links are connected
if (LinkManager::instance()->anyConnectedLinks()) {
bool foundConnection = false;
LinkManager* linkMgr = LinkManager::instance();
QList<LinkInterface*> links = linkMgr->getLinks();
foreach(LinkInterface* link, links) {
if (link->isConnected()) {
foundConnection = true;
break;
}
}
if (foundConnection) {
QGCMessageBox::information(tr("Log Replay"), tr("You must close all connections prior to replaying a log.")); QGCMessageBox::information(tr("Log Replay"), tr("You must close all connections prior to replaying a log."));
return; return;
} }
...@@ -326,7 +315,7 @@ bool QGCMAVLinkLogPlayer::loadLogFile(const QString& file) ...@@ -326,7 +315,7 @@ bool QGCMAVLinkLogPlayer::loadLogFile(const QString& file)
// If there's an existing MAVLinkSimulationLink() being used for an old file, // If there's an existing MAVLinkSimulationLink() being used for an old file,
// we replace it. // we replace it.
if (logLink) { if (logLink) {
LinkManager::instance()->deleteLink(logLink); LinkManager::instance()->_deleteLink(logLink);
} }
logLink = new MAVLinkSimulationLink(""); logLink = new MAVLinkSimulationLink("");
......
...@@ -676,10 +676,8 @@ void QGCToolBar::_connectButtonClicked(bool checked) ...@@ -676,10 +676,8 @@ void QGCToolBar::_connectButtonClicked(bool checked)
// Get the configuration name // Get the configuration name
QString confName = _linkCombo->currentText(); QString confName = _linkCombo->currentText();
// Create a link for it // Create a link for it
LinkInterface* link = _linkMgr->createLink(confName); LinkInterface* link = _linkMgr->createConnectedLink(confName);
if(link) { if(link) {
// Connect it
_linkMgr->connectLink(link);
// Save last used connection // Save last used connection
MainWindow::instance()->saveLastUsedConnection(confName); MainWindow::instance()->saveLastUsedConnection(confName);
} }
......
...@@ -163,10 +163,8 @@ void MainToolBar::onConnect(QString conf) ...@@ -163,10 +163,8 @@ void MainToolBar::onConnect(QString conf)
// We don't want the combo box updating under our feet // We don't want the combo box updating under our feet
LinkManager::instance()->suspendConfigurationUpdates(true); LinkManager::instance()->suspendConfigurationUpdates(true);
// Create a link // Create a link
LinkInterface* link = LinkManager::instance()->createLink(_currentConfig); LinkInterface* link = LinkManager::instance()->createConnectedLink(_currentConfig);
if(link) { if(link) {
// Connect it
LinkManager::instance()->connectLink(link);
// Save last used connection // Save last used connection
MainWindow::instance()->saveLastUsedConnection(_currentConfig); MainWindow::instance()->saveLastUsedConnection(_currentConfig);
} }
......
...@@ -118,7 +118,7 @@ void UASListWidget::updateStatus() ...@@ -118,7 +118,7 @@ void UASListWidget::updateStatus()
LinkInterface* link = i.key(); LinkInterface* link = i.key();
// Paranoid sanity check // Paranoid sanity check
if (!LinkManager::instance()->getLinks().contains(link)) if (!LinkManager::instance()->containsLink(link))
continue; continue;
if (!link) if (!link)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment