-
-
Notifications
You must be signed in to change notification settings - Fork 5k
feat(px4): add failure injection setup page #14578
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Claudio-Chies
wants to merge
9
commits into
mavlink:master
Choose a base branch
from
Claudio-Chies:cch/qgc-failure-injection
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+1,308
−5
Open
Changes from 7 commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
18fe464
Add Failure Injection feature to PX4 AutoPilot Plugin
9e70ba0
slight refactor to improve test coverage
5beeecf
fix(ui): fix android related rendering issues
1f2f461
fix(FailureInjection): reorder injection call for improved logging
9a51f7b
feat(FailureInjection): add detail parameters for failure types and u…
385636c
feat(FailureInjection): improve detail parameter handling and add uti…
10aaec0
refactor(FailureInjection): improve code readability and consistency …
b1ef562
feat(FailureInjection): incoperate copilot feedback
5d0273c
feat(FailureInjection): add GPS wrong detail parameter and update rel…
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,174 @@ | ||
| /**************************************************************************** | ||
| * FailureInjection.cc | ||
| ****************************************************************************/ | ||
| #include "FailureInjection.h" | ||
|
|
||
| #include <QtCore/QMetaEnum> | ||
| #include <QtCore/QVariantMap> | ||
|
|
||
| #include "MAVLinkEnumsQml.h" // MAVLinkEnums::FAILURE_UNIT / FAILURE_TYPE, Q_ENUM_NS-reflected from the MAVLink dialect | ||
| #include "MAVLinkLib.h" // MAV_RESULT_* for resolveResult() | ||
| #include "QGCMAVLink.h" // QGCMAVLink::mavResultToString() fallback for resolveResult() | ||
|
|
||
| namespace { | ||
|
|
||
| QVariantMap _makeUnit(const QString& name, int unit) | ||
| { | ||
| return QVariantMap{{"name", name}, {"unit", unit}}; | ||
| } | ||
|
|
||
| QVariantMap _makeType(const QString& name, int type) | ||
| { | ||
| return QVariantMap{{"name", name}, {"type", type}}; | ||
| } | ||
|
|
||
| /// Strip the longest matching MAVLink enum prefix, e.g. FAILURE_UNIT_SENSOR_GYRO -> GYRO. | ||
| QString _stripPrefix(const QString& key, const QStringList& prefixesLongestFirst) | ||
| { | ||
| for (const QString& prefix : prefixesLongestFirst) { | ||
| if (key.startsWith(prefix)) { | ||
| return key.mid(prefix.length()); | ||
| } | ||
| } | ||
| return key; | ||
| } | ||
|
|
||
| /// Builds a {name, value} catalog from the Q_ENUM_NS-exposed enum on MAVLinkEnums::staticMetaObject, | ||
| /// looked up by name. | ||
| QVariantList _buildCatalog(const char* enumName, const QStringList& prefixesLongestFirst, | ||
| QVariantMap (*makeEntry)(const QString&, int)) | ||
| { | ||
| QVariantList list; | ||
| const QMetaObject& enumsMetaObject = MAVLinkEnums::staticMetaObject; | ||
| const QMetaEnum me = enumsMetaObject.enumerator(enumsMetaObject.indexOfEnumerator(enumName)); | ||
| for (int i = 0; i < me.keyCount(); ++i) { | ||
| const QString key = QString::fromLatin1(me.key(i)); | ||
| if (key.endsWith(QStringLiteral("_ENUM_END"))) { | ||
| continue; // dialect sentinel, not a real value | ||
| } | ||
| list.append(makeEntry(_stripPrefix(key, prefixesLongestFirst), me.value(i))); | ||
| } | ||
| return list; | ||
| } | ||
|
|
||
| } // namespace | ||
|
|
||
| FailureInjection::FailureInjection(QObject* parent) : QObject(parent) | ||
| { | ||
| _units = _buildCatalog("FAILURE_UNIT", | ||
| {QStringLiteral("FAILURE_UNIT_SENSOR_"), QStringLiteral("FAILURE_UNIT_SYSTEM_"), | ||
| QStringLiteral("FAILURE_UNIT_")}, | ||
| _makeUnit); | ||
| _types = _buildCatalog("FAILURE_TYPE", {QStringLiteral("FAILURE_TYPE_")}, _makeType); | ||
| } | ||
|
|
||
| void FailureInjection::logRow(const QString& unitName, const QString& typeName, const QString& instanceLabel, | ||
| const QString& time) | ||
| { | ||
| _activity.prepend(QVariantMap{{"time", time}, | ||
| {"unitName", unitName}, | ||
| {"typeName", typeName}, | ||
| {"instance", instanceLabel}, | ||
| {"result", QStringLiteral("pending")}}); | ||
| emit activityChanged(); | ||
| } | ||
|
|
||
| void FailureInjection::logInjection(const QString& unitName, const QString& typeName, int unitEnum, | ||
| const QString& instanceLabel, const QString& time) | ||
| { | ||
| logRow(unitName, typeName, instanceLabel, time); | ||
| if (!_injectedUnits.contains(unitEnum)) { | ||
| _injectedUnits.append(unitEnum); | ||
| } | ||
| } | ||
|
|
||
| void FailureInjection::resolveResult(int ackResult) | ||
| { | ||
| // ACKs for MAV_CMD_INJECT_FAILURE arrive in send order; resolve the oldest pending row (highest index, since newest | ||
| // is prepended). | ||
| int pendingIndex = -1; | ||
| for (int i = _activity.size() - 1; i >= 0; --i) { | ||
| if (_activity.at(i).toMap().value(QStringLiteral("result")).toString() == QStringLiteral("pending")) { | ||
| pendingIndex = i; | ||
| break; | ||
| } | ||
| } | ||
| if (pendingIndex < 0) { | ||
| return; | ||
| } | ||
|
|
||
| if (ackResult == MAV_RESULT_IN_PROGRESS) { | ||
| return; // not a terminal result; leave pending | ||
| } | ||
|
|
||
| QString reason; | ||
| switch (ackResult) { | ||
| // Not tr()'d: FailureInjectionComponent.qml matches this exact literal to style the row green. | ||
| case MAV_RESULT_ACCEPTED: | ||
| reason = QStringLiteral("accepted"); | ||
| break; | ||
| case MAV_RESULT_TEMPORARILY_REJECTED: | ||
| reason = tr("Temporarily rejected"); | ||
| break; | ||
| case MAV_RESULT_DENIED: | ||
| reason = tr("Denied"); | ||
| break; | ||
| case MAV_RESULT_UNSUPPORTED: | ||
| reason = tr("Unsupported"); | ||
| break; | ||
| case MAV_RESULT_FAILED: | ||
| reason = tr("Failed"); | ||
| break; | ||
| case MAV_RESULT_CANCELLED: | ||
| reason = tr("Cancelled"); | ||
| break; | ||
| default: | ||
| reason = QGCMAVLink::mavResultToString(static_cast<uint8_t>(ackResult)); | ||
| break; | ||
| } | ||
|
|
||
| QVariantMap row = _activity.at(pendingIndex).toMap(); | ||
| row[QStringLiteral("result")] = reason; | ||
| _activity[pendingIndex] = row; | ||
| emit activityChanged(); | ||
| } | ||
|
|
||
| QVariantList FailureInjection::injectedUnits(void) const | ||
| { | ||
| QVariantList list; | ||
| for (int unitEnum : _injectedUnits) { | ||
| list.append(unitEnum); | ||
| } | ||
| return list; | ||
| } | ||
|
|
||
| void FailureInjection::clearInjectedUnits(void) | ||
| { | ||
| _injectedUnits.clear(); | ||
| } | ||
|
|
||
| QVariantList FailureInjection::detailParams(int unitEnum, int typeEnum) const | ||
| { | ||
| // Vehicle parameters that refine how a failure manifests, keyed by (unit, type). The page shows | ||
| // an editor per entry, but only when the connected vehicle actually exposes the parameter. | ||
| // Adding a new combo is one more table line. | ||
| struct DetailParam | ||
| { | ||
| int unitEnum; | ||
| int typeEnum; | ||
| const char* param; | ||
| const char* label; | ||
| }; | ||
|
|
||
| static const DetailParam table[] = { | ||
| {FAILURE_UNIT_SYSTEM_BATTERY, FAILURE_TYPE_WRONG, "SYS_FAIL_BAT_LVL", QT_TR_NOOP("Battery level")}, | ||
| }; | ||
|
|
||
| QVariantList list; | ||
| for (const DetailParam& entry : table) { | ||
| if (entry.unitEnum == unitEnum && entry.typeEnum == typeEnum) { | ||
| list.append(QVariantMap{{"param", QString::fromLatin1(entry.param)}, {"label", tr(entry.label)}}); | ||
| } | ||
| } | ||
| return list; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| /**************************************************************************** | ||
| * FailureInjection.h | ||
| * | ||
| * QML singleton backing the Failure Injection page. It serves two roles: | ||
| * | ||
| * 1. Static catalog: the MAVLink FAILURE_UNIT / FAILURE_TYPE enums. Values and | ||
| * names come from the MAVLink dialect headers (tracking common.xml); the .cc | ||
| * derives each display name by stripping the enum-name prefix. | ||
| * | ||
| * 2. Session state: the activity log and the set of units injected this | ||
| * session, persisted here so they survive navigating away from and back | ||
| * to the page, which destroys/recreates the page via its SetupPage Loader. | ||
| ****************************************************************************/ | ||
| #pragma once | ||
|
|
||
| #include <QtCore/QObject> | ||
| #include <QtCore/QVariantList> | ||
| #include <QtQmlIntegration/QtQmlIntegration> | ||
|
|
||
| class FailureInjection : public QObject | ||
| { | ||
| Q_OBJECT | ||
| QML_NAMED_ELEMENT(FailureInjection) | ||
| QML_SINGLETON | ||
| Q_PROPERTY(QVariantList units READ units CONSTANT) ///< [{ name, unit }] | ||
| Q_PROPERTY(QVariantList types READ types CONSTANT) ///< [{ name, type }] | ||
| Q_PROPERTY(QVariantList activity READ activity NOTIFY | ||
| activityChanged) ///< newest first: [{ time, unitName, typeName, instance, result }] | ||
|
|
||
| public: | ||
| explicit FailureInjection(QObject* parent = nullptr); | ||
|
|
||
| QVariantList units(void) const { return _units; } | ||
|
|
||
| QVariantList types(void) const { return _types; } | ||
|
|
||
| QVariantList activity(void) const { return _activity; } | ||
|
|
||
| /// Add a log row (prepended, newest first, result "pending") without tracking the unit for Reset. | ||
| /// instanceLabel is a display descriptor for the affected instance(s), e.g. "all", "2", "1, 3, 5". | ||
| Q_INVOKABLE void logRow(const QString& unitName, const QString& typeName, const QString& instanceLabel, | ||
| const QString& time); | ||
| /// Record one injected failure: adds a log row and remembers the unit so Reset can restore it. | ||
| Q_INVOKABLE void logInjection(const QString& unitName, const QString& typeName, int unitEnum, | ||
| const QString& instanceLabel, const QString& time); | ||
| /// Resolve the oldest still-pending injection with a MAV_RESULT ack code; sets the row result. | ||
| Q_INVOKABLE void resolveResult(int ackResult); | ||
| /// Distinct FAILURE_UNIT values injected this session, so Reset restores only those. | ||
| Q_INVOKABLE QVariantList injectedUnits(void) const; | ||
| /// Forget the injected-units set (after a Reset reverted them); the activity log is left intact. | ||
| Q_INVOKABLE void clearInjectedUnits(void); | ||
| /// Vehicle parameters that refine how a (unit, type) failure manifests, e.g. BATTERY+WRONG -> | ||
| /// SYS_FAIL_BAT_LVL. Returns [{ param, label }]; empty when the combo has no detail parameters. | ||
| Q_INVOKABLE QVariantList detailParams(int unitEnum, int typeEnum) const; | ||
|
|
||
| signals: | ||
| void activityChanged(void); | ||
|
|
||
| private: | ||
| QVariantList _units; | ||
| QVariantList _types; | ||
| QVariantList _activity; | ||
| QList<int> _injectedUnits; | ||
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| /**************************************************************************** | ||
| * FailureInjectionComponent.cc | ||
| ****************************************************************************/ | ||
| #include "FailureInjectionComponent.h" | ||
|
|
||
| #include "AutoPilotPlugin.h" | ||
| #include "Vehicle.h" | ||
|
|
||
| FailureInjectionComponent::FailureInjectionComponent(Vehicle* vehicle, AutoPilotPlugin* autopilot, QObject* parent) | ||
| : VehicleComponent(vehicle, autopilot, AutoPilotPlugin::UnknownVehicleComponent, parent), | ||
| _name(tr("Failure Injection")) | ||
| {} | ||
|
|
||
| QString FailureInjectionComponent::name(void) const | ||
| { | ||
| return _name; | ||
| } | ||
|
|
||
| QString FailureInjectionComponent::description(void) const | ||
| { | ||
| return tr( | ||
| "Failure Injection is used to simulate sensor and system failures (MAV_CMD_INJECT_FAILURE) " | ||
| "to validate failsafes. Requires SYS_FAILURE_EN = 1 and a vehicle reboot."); | ||
| } | ||
|
|
||
| QString FailureInjectionComponent::iconResource(void) const | ||
| { | ||
| return QStringLiteral("/qmlimages/WarningEmergency.svg"); | ||
| } | ||
|
|
||
| QUrl FailureInjectionComponent::setupSource(void) const | ||
| { | ||
| return QUrl::fromUserInput( | ||
| QStringLiteral("qrc:/qml/QGroundControl/AutoPilotPlugins/PX4/FailureInjectionComponent.qml")); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| /**************************************************************************** | ||
| * FailureInjectionComponent.h | ||
| * Vehicle Setup component wrapper for the Failure Injection QML page. | ||
| ****************************************************************************/ | ||
| #pragma once | ||
|
|
||
| #include "VehicleComponent.h" | ||
|
|
||
| class FailureInjectionComponent : public VehicleComponent | ||
| { | ||
| Q_OBJECT | ||
| public: | ||
| FailureInjectionComponent(Vehicle* vehicle, AutoPilotPlugin* autopilot, QObject* parent = nullptr); | ||
|
|
||
| QString name(void) const override; | ||
| QString description(void) const override; | ||
| QString iconResource(void) const override; | ||
|
|
||
| bool requiresSetup(void) const override { return false; } | ||
|
|
||
| bool setupComplete(void) const override { return true; } | ||
|
|
||
| QStringList setupCompleteChangedTriggerList(void) const override { return QStringList(); } | ||
|
|
||
| QUrl setupSource(void) const override; | ||
|
|
||
| QUrl summaryQmlSource(void) const override { return QUrl(); } | ||
|
|
||
| // Failure injection is meant to be exercised in flight (validate failsafes/EKF), so keep the page usable while | ||
| // armed. | ||
| bool allowSetupWhileArmed(void) const override { return true; } | ||
|
|
||
| bool allowSetupWhileFlying(void) const override { return true; } | ||
|
|
||
| private: | ||
| const QString _name; | ||
| }; |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.