From e29da008bb6d1d04bbf499b82d2b73dc63acbac4 Mon Sep 17 00:00:00 2001 From: DonLakeFlyer <don@thegagnes.com> Date: Sat, 2 Jan 2021 11:03:08 -0800 Subject: [PATCH] Fix/Rework signal compress code to fix signal loss --- src/QGCApplication.cc | 96 +++++++++++++++++++++++++++++++++++++++++ src/QGCApplication.h | 115 +++++++++++--------------------------------------- 2 files changed, 120 insertions(+), 91 deletions(-) diff --git a/src/QGCApplication.cc b/src/QGCApplication.cc index c495e3a..09ea42e 100644 --- a/src/QGCApplication.cc +++ b/src/QGCApplication.cc @@ -969,3 +969,99 @@ QString QGCApplication::cachedAirframeMetaDataFile(void) QDir airframeDir = QFileInfo(settings.fileName()).dir(); return airframeDir.filePath(QStringLiteral("PX4AirframeFactMetaData.xml")); } + +/// Returns a signal index that is can be compared to QMetaCallEvent.signalId +int QGCApplication::CompressedSignalList::_signalIndex(const QMetaMethod & method) +{ + if (method.methodType() != QMetaMethod::Signal) { + qWarning() << "Internal error: QGCApplication::CompressedSignalList::_signalIndex not a signal" << method.methodType(); + return -1; + } + + int index = -1; + const QMetaObject* metaObject = method.enclosingMetaObject(); + for (int i=0; i<=method.methodIndex(); i++) { + if (metaObject->method(i).methodType() != QMetaMethod::Signal) { + continue; + } + index++; + } + return index; +} + +void QGCApplication::CompressedSignalList::add(const QMetaMethod & method) +{ + const QMetaObject* metaObject = method.enclosingMetaObject(); + int signalIndex = _signalIndex(method); + + if (signalIndex != -1 && !contains(metaObject, signalIndex)) { + _signalMap[method.enclosingMetaObject()].insert(signalIndex); + } +} + +void QGCApplication::CompressedSignalList::remove(const QMetaMethod & method) +{ + int signalIndex = _signalIndex(method); + const QMetaObject* metaObject = method.enclosingMetaObject(); + + if (signalIndex != -1 && _signalMap.contains(metaObject) && _signalMap[metaObject].contains(signalIndex)) { + _signalMap[metaObject].remove(signalIndex); + if (_signalMap[metaObject].count() == 0) { + _signalMap.remove(metaObject); + } + } +} + +bool QGCApplication::CompressedSignalList::contains(const QMetaObject* metaObject, int signalIndex) +{ + return _signalMap.contains(metaObject) && _signalMap[metaObject].contains(signalIndex); +} + +void QGCApplication::addCompressedSignal(const QMetaMethod & method) +{ + _compressedSignals.add(method); +} + +void QGCApplication::removeCompressedSignal(const QMetaMethod & method) +{ + _compressedSignals.remove(method); +} + +bool QGCApplication::compressEvent(QEvent*event, QObject* receiver, QPostEventList* postedEvents) +{ + if (event->type() != QEvent::MetaCall) { + return QApplication::compressEvent(event, receiver, postedEvents); + } + + QMetaCallEvent* mce = static_cast<QMetaCallEvent*>(event); + if (!mce->sender() || !_compressedSignals.contains(mce->sender()->metaObject(), mce->signalId())) { + return QApplication::compressEvent(event, receiver, postedEvents); + } + + for (QPostEventList::iterator it = postedEvents->begin(); it != postedEvents->end(); ++it) { + QPostEvent &cur = *it; + if (cur.receiver != receiver || cur.event == 0 || cur.event->type() != event->type()) { + continue; + } + QMetaCallEvent *cur_mce = static_cast<QMetaCallEvent*>(cur.event); + if (cur_mce->sender() != mce->sender() || cur_mce->signalId() != mce->signalId() || cur_mce->id() != mce->id()) { + continue; + } + /* Keep The Newest Call */ + // We can't merely qSwap the existing posted event with the new one, since QEvent + // keeps track of whether it has been posted. Deletion of a formerly posted event + // takes the posted event list mutex and does a useless search of the posted event + // list upon deletion. We thus clear the QEvent::posted flag before deletion. + struct EventHelper : private QEvent { + static void clearPostedFlag(QEvent * ev) { + (&static_cast<EventHelper*>(ev)->t)[1] &= ~0x8001; // Hack to clear QEvent::posted + } + }; + EventHelper::clearPostedFlag(cur.event); + delete cur.event; + cur.event = event; + return true; + } + + return false; +} diff --git a/src/QGCApplication.h b/src/QGCApplication.h index 97b03a3..3d5b12b 100644 --- a/src/QGCApplication.h +++ b/src/QGCApplication.h @@ -98,14 +98,18 @@ public: QTranslator& qgcJSONTranslator(void) { return _qgcTranslatorJSON; } - static QString cachedParameterMetaDataFile(void); - static QString cachedAirframeMetaDataFile(void); - void setLanguage(); QQuickItem* mainRootWindow(); - uint64_t msecsSinceBoot(void) { return _msecsElapsedTime.elapsed(); } + /// Registers the signal such that only the last duplicate signal added is left in the queue. + void addCompressedSignal(const QMetaMethod & method); + + void removeCompressedSignal(const QMetaMethod & method); + + static QString cachedParameterMetaDataFile(void); + static QString cachedAirframeMetaDataFile(void); + public slots: /// You can connect to this slot to show an information message box from a different thread. void informationMessageBoxOnMainThread(const QString& title, const QString& msg); @@ -178,6 +182,8 @@ private: void _checkForNewVersion (); void _exitWithError (QString errorMessage); + // Overrides from QApplication + bool compressEvent(QEvent *event, QObject *receiver, QPostEventList *postedEvents) override; bool _runningUnitTests; ///< true: running unit tests, false: normal app static const int _missingParamsDelayedDisplayTimerTimeout = 1000; ///< Timeout to wait for next missing fact to come in before display @@ -204,102 +210,29 @@ private: QList<QPair<QString /* title */, QString /* message */>> _delayedAppMessages; - static const char* _settingsVersionKey; ///< Settings key which hold settings version - static const char* _deleteAllSettingsKey; ///< If this settings key is set on boot, all settings will be deleted + class CompressedSignalList { + Q_DISABLE_COPY(CompressedSignalList) - /// Unit Test have access to creating and destroying singletons - friend class UnitTest; - -private: - /*! Keeps a list of singal indices for one or more meatobject classes. - * The indices are signal indices as given by QMetaCallEvent.signalId. - * On Qt 5, those do *not* match QMetaObject::methodIndex since they - * exclude non-signal methods. */ - class SignalList { - Q_DISABLE_COPY(SignalList) - typedef QMap<const QMetaObject *, QSet<int> > T; - T m_data; - /*! Returns a signal index that is can be compared to QMetaCallEvent.signalId. */ - static int signalIndex(const QMetaMethod & method) { - Q_ASSERT(method.methodType() == QMetaMethod::Signal); - - int index = -1; - const QMetaObject * mobj = method.enclosingMetaObject(); - for (int i = 0; i <= method.methodIndex(); ++i) { - if (mobj->method(i).methodType() != QMetaMethod::Signal) continue; - ++ index; - } - return index; - } public: - SignalList() {} - void add(const QMetaMethod & method) { - m_data[method.enclosingMetaObject()].insert(signalIndex(method)); - } - void remove(const QMetaMethod & method) { - T::iterator it = m_data.find(method.enclosingMetaObject()); - if (it != m_data.end()) { - it->remove(signalIndex(method)); - if (it->empty()) m_data.erase(it); - } - } - bool contains(const QMetaObject * metaObject, int signalId) { - T::const_iterator it = m_data.find(metaObject); - return it != m_data.end() && it.value().contains(signalId); - } - }; + CompressedSignalList() {} - SignalList m_compressedSignals; + void add (const QMetaMethod & method); + void remove (const QMetaMethod & method); + bool contains (const QMetaObject * metaObject, int signalIndex); -public: - void addCompressedSignal(const QMetaMethod & method) { m_compressedSignals.add(method); } - void removeCompressedSignal(const QMetaMethod & method) { m_compressedSignals.remove(method); } + private: + static int _signalIndex(const QMetaMethod & method); -private: - struct EventHelper : private QEvent { - static void clearPostedFlag(QEvent * ev) { - (&static_cast<EventHelper*>(ev)->t)[1] &= ~0x8001; // Hack to clear QEvent::posted - } + QMap<const QMetaObject*, QSet<int> > _signalMap; }; - bool compressEvent(QEvent *event, QObject *receiver, QPostEventList *postedEvents) { - if (event->type() != QEvent::MetaCall) - return QApplication::compressEvent(event, receiver, postedEvents); - - QMetaCallEvent *mce = static_cast<QMetaCallEvent*>(event); - - if (mce->sender() && !m_compressedSignals.contains(mce->sender()->metaObject(), mce->signalId())) { - return false; - } - - for (QPostEventList::iterator it = postedEvents->begin(); it != postedEvents->end(); ++it) { - QPostEvent &cur = *it; - if (cur.receiver != receiver || cur.event == 0 || cur.event->type() != event->type()) - continue; - QMetaCallEvent *cur_mce = static_cast<QMetaCallEvent*>(cur.event); - if (cur_mce->sender() != mce->sender() || cur_mce->signalId() != mce->signalId() || - cur_mce->id() != mce->id()) - continue; - if (true) { - /* Keep The Newest Call */ - // We can't merely qSwap the existing posted event with the new one, since QEvent - // keeps track of whether it has been posted. Deletion of a formerly posted event - // takes the posted event list mutex and does a useless search of the posted event - // list upon deletion. We thus clear the QEvent::posted flag before deletion. - EventHelper::clearPostedFlag(cur.event); - delete cur.event; - cur.event = event; - } else { - /* Keep the Oldest Call */ - delete event; - } - return true; - } - return false; - } - + CompressedSignalList _compressedSignals; + static const char* _settingsVersionKey; ///< Settings key which hold settings version + static const char* _deleteAllSettingsKey; ///< If this settings key is set on boot, all settings will be deleted + /// Unit Test have access to creating and destroying singletons + friend class UnitTest; }; /// @brief Returns the QGCApplication object singleton.