From 87c06bed1415cc9b3a495040e52f9b87e6a95e44 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Tue, 5 May 2015 11:42:00 -0700 Subject: [PATCH] Much better handling of missing facts --- QGCApplication.pro | 5 +- qgroundcontrol.qrc | 1 + .../PX4/AirframeComponent.qml | 177 +++++++++--------- .../PX4/AirframeComponentController.cc | 22 +-- .../PX4/AirframeComponentController.h | 6 +- .../PX4/AirframeComponentSummary.qml | 44 +++-- .../PX4/FlightModesComponentSummary.qml | 50 ++--- .../PX4/PowerComponentSummary.qml | 41 ++-- .../PX4/RadioComponentSummary.qml | 96 +++++----- .../PX4/SafetyComponentSummary.qml | 71 +++---- .../PX4/SensorsComponentSummary.qml | 41 ++-- .../PX4/SensorsComponentSummaryFixedWing.qml | 60 +++--- src/FactSystem/Fact.h | 5 - src/FactSystem/FactBinder.cc | 35 +++- src/FactSystem/FactBinder.h | 10 + src/FactSystem/FactControls/FactPanel.qml | 61 ++++++ .../FactControls/FactPanelController.cc | 108 +++++++++++ .../FactControls/FactPanelController.h | 71 +++++++ src/FactSystem/FactControls/qmldir | 1 + src/FactSystem/FactSystem.cc | 2 + src/FactSystem/ParameterLoader.cc | 3 +- src/QGCApplication.cc | 30 ++- src/QGCApplication.h | 14 +- 23 files changed, 649 insertions(+), 305 deletions(-) create mode 100644 src/FactSystem/FactControls/FactPanel.qml create mode 100644 src/FactSystem/FactControls/FactPanelController.cc create mode 100644 src/FactSystem/FactControls/FactPanelController.h diff --git a/QGCApplication.pro b/QGCApplication.pro index acc9756e1..d9be8e776 100644 --- a/QGCApplication.pro +++ b/QGCApplication.pro @@ -635,7 +635,8 @@ SOURCES += \ # Fact System code INCLUDEPATH += \ - src/FactSystem + src/FactSystem \ + src/FactSystem/FactControls \ HEADERS += \ src/FactSystem/Fact.h \ @@ -644,6 +645,7 @@ HEADERS += \ src/FactSystem/FactSystem.h \ src/FactSystem/FactValidator.h \ src/FactSystem/ParameterLoader.h \ + src/FactSystem/FactControls/FactPanelController.h \ SOURCES += \ src/FactSystem/Fact.cc \ @@ -652,6 +654,7 @@ SOURCES += \ src/FactSystem/FactSystem.cc \ src/FactSystem/FactValidator.cc \ src/FactSystem/ParameterLoader.cc \ + src/FactSystem/FactControls/FactPanelController.cc \ # Android diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index c3b264b8a..b2fc060de 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -9,6 +9,7 @@ src/QmlControls/QmlTest.qml src/FactSystem/FactControls/qmldir + src/FactSystem/FactControls/FactPanel.qml src/FactSystem/FactControls/FactLabel.qml src/FactSystem/FactControls/FactTextField.qml src/FactSystem/FactControls/FactCheckBox.qml diff --git a/src/AutoPilotPlugins/PX4/AirframeComponent.qml b/src/AutoPilotPlugins/PX4/AirframeComponent.qml index 96ea11946..b7a483768 100644 --- a/src/AutoPilotPlugins/PX4/AirframeComponent.qml +++ b/src/AutoPilotPlugins/PX4/AirframeComponent.qml @@ -31,125 +31,132 @@ import QGroundControl.Palette 1.0 import QGroundControl.Controls 1.0 import QGroundControl.Controllers 1.0 -Rectangle { - AirframeComponentController { id: controller } - QGCPalette { id: qgcPal; colorGroupEnabled: true } +FactPanel { + id: panel - color: qgcPal.window + AirframeComponentController { id: controller; factPanel: panel } - Column { + Rectangle { anchors.fill: parent - QGCLabel { - text: "AIRFRAME CONFIG" - font.pointSize: 20 - } + QGCPalette { id: qgcPal; colorGroupEnabled: true } - Item { height: 20; width: 10 } // spacer + color: qgcPal.window - Row { - width: parent.width + Column { + anchors.fill: parent QGCLabel { - width: parent.width - applyButton.width - text: "Select you airframe type and specific vehicle bellow. Click 'Apply and Restart' when ready and your vehicle will be disconnected, rebooted to the new settings and re-connected." - wrapMode: Text.WordWrap + text: "AIRFRAME CONFIG" + font.pointSize: 20 } - QGCButton { - id: applyButton - text: "Apply and Restart" - onClicked: { controller.changeAutostart() } - } - } + Item { height: 20; width: 10 } // spacer - Item { height: 20; width: 10 } // spacer + Row { + width: parent.width - Flow { - width: parent.width - spacing: 10 + QGCLabel { + width: parent.width - applyButton.width + text: "Select you airframe type and specific vehicle bellow. Click 'Apply and Restart' when ready and your vehicle will be disconnected, rebooted to the new settings and re-connected." + wrapMode: Text.WordWrap + } - ExclusiveGroup { - id: airframeTypeExclusive + QGCButton { + id: applyButton + text: "Apply and Restart" + onClicked: { controller.changeAutostart() } + } } - Repeater { - model: controller.airframeTypes + Item { height: 20; width: 10 } // spacer - // Outer summary item rectangle - Rectangle { - readonly property real titleHeight: 30 - readonly property real innerMargin: 10 + Flow { + width: parent.width + spacing: 10 - width: 250 - height: 200 - color: qgcPal.windowShade + ExclusiveGroup { + id: airframeTypeExclusive + } + + Repeater { + model: controller.airframeTypes + // Outer summary item rectangle Rectangle { - id: title - width: parent.width - height: parent.titleHeight - color: qgcPal.windowShadeDark + readonly property real titleHeight: 30 + readonly property real innerMargin: 10 - Text { - anchors.fill: parent + width: 250 + height: 200 + color: qgcPal.windowShade - color: qgcPal.buttonText - font.pixelSize: 12 - text: modelData.name + Rectangle { + id: title + width: parent.width + height: parent.titleHeight + color: qgcPal.windowShadeDark - verticalAlignment: TextEdit.AlignVCenter - horizontalAlignment: TextEdit.AlignHCenter - } - } + Text { + anchors.fill: parent - Image { - id: image - x: innerMargin - width: parent.width - (innerMargin * 2) - height: parent.height - title.height - combo.height - (innerMargin * 3) - anchors.topMargin: innerMargin - anchors.top: title.bottom + color: qgcPal.buttonText + font.pixelSize: 12 + text: modelData.name - source: modelData.imageResource - fillMode: Image.PreserveAspectFit - smooth: true + verticalAlignment: TextEdit.AlignVCenter + horizontalAlignment: TextEdit.AlignHCenter + } + } - } + Image { + id: image + x: innerMargin + width: parent.width - (innerMargin * 2) + height: parent.height - title.height - combo.height - (innerMargin * 3) + anchors.topMargin: innerMargin + anchors.top: title.bottom + + source: modelData.imageResource + fillMode: Image.PreserveAspectFit + smooth: true - QGCCheckBox { - id: airframeCheckBox - anchors.bottom: image.bottom - anchors.right: image.right - checked: modelData.name == controller.currentAirframeType - exclusiveGroup: airframeTypeExclusive + } - onCheckedChanged: { - if (checked && combo.currentIndex != -1) { - controller.autostartId = modelData.airframes[combo.currentIndex].autostartId + QGCCheckBox { + id: airframeCheckBox + anchors.bottom: image.bottom + anchors.right: image.right + checked: modelData.name == controller.currentAirframeType + exclusiveGroup: airframeTypeExclusive + + onCheckedChanged: { + if (checked && combo.currentIndex != -1) { + controller.autostartId = modelData.airframes[combo.currentIndex].autostartId + } } } - } - QGCComboBox { - id: combo - objectName: modelData.airframeType + "ComboBox" - x: innerMargin - anchors.topMargin: innerMargin - anchors.top: image.bottom - width: parent.width - (innerMargin * 2) - model: modelData.airframes - currentIndex: (modelData.name == controller.currentAirframeType) ? controller.currentVehicleIndex : 0 - - onCurrentIndexChanged: { - if (airframeCheckBox.checked) { - controller.autostartId = modelData.airframes[currentIndex].autostartId + QGCComboBox { + id: combo + objectName: modelData.airframeType + "ComboBox" + x: innerMargin + anchors.topMargin: innerMargin + anchors.top: image.bottom + width: parent.width - (innerMargin * 2) + model: modelData.airframes + currentIndex: (modelData.name == controller.currentAirframeType) ? controller.currentVehicleIndex : 0 + + onCurrentIndexChanged: { + if (airframeCheckBox.checked) { + controller.autostartId = modelData.airframes[currentIndex].autostartId + } } } } } } - } + } } -} +} \ No newline at end of file diff --git a/src/AutoPilotPlugins/PX4/AirframeComponentController.cc b/src/AutoPilotPlugins/PX4/AirframeComponentController.cc index 6e367f175..d0c9bf04e 100644 --- a/src/AutoPilotPlugins/PX4/AirframeComponentController.cc +++ b/src/AutoPilotPlugins/PX4/AirframeComponentController.cc @@ -37,19 +37,13 @@ bool AirframeComponentController::_typesRegistered = false; -AirframeComponentController::AirframeComponentController(QObject* parent) : - QObject(parent), +AirframeComponentController::AirframeComponentController(void) : _uas(NULL), - _autoPilotPlugin(NULL), _currentVehicleIndex(0), _autostartId(0) { _uas = UASManager::instance()->getActiveUAS(); Q_ASSERT(_uas); - - _autoPilotPlugin = AutoPilotPluginManager::instance()->getInstanceForAutoPilotPlugin(_uas); - Q_ASSERT(_autoPilotPlugin); - Q_ASSERT(_autoPilotPlugin->pluginReady()); if (!_typesRegistered) { _typesRegistered = true; @@ -57,10 +51,16 @@ AirframeComponentController::AirframeComponentController(QObject* parent) : qmlRegisterUncreatableType("QGroundControl.Controllers", 1, 0, "Aiframe", "Can only reference Airframe"); } + QStringList usedFacts; + usedFacts << "SYS_AUTOSTART" << "SYS_AUTOCONFIG"; + if (!_allFactsExists(usedFacts)) { + return; + } + // Load up member variables bool autostartFound = false; - _autostartId = _autoPilotPlugin->getParameterFact("SYS_AUTOSTART")->value().toInt(); + _autostartId = _autopilot->getParameterFact("SYS_AUTOSTART")->value().toInt(); for (const AirframeComponentAirframes::AirframeType_t* pType=&AirframeComponentAirframes::rgAirframeTypes[0]; pType->name != NULL; pType++) { AirframeType* airframeType = new AirframeType(pType->name, pType->imageResource, this); @@ -81,7 +81,7 @@ AirframeComponentController::AirframeComponentController(QObject* parent) : _airframeTypes.append(QVariant::fromValue(airframeType)); } - + if (_autostartId != 0) { // FIXME: Should be a user error Q_UNUSED(autostartFound); @@ -101,8 +101,8 @@ void AirframeComponentController::changeAutostart(void) return; } - _autoPilotPlugin->getParameterFact("SYS_AUTOSTART")->setValue(_autostartId); - _autoPilotPlugin->getParameterFact("SYS_AUTOCONFIG")->setValue(1); + _autopilot->getParameterFact("SYS_AUTOSTART")->setValue(_autostartId); + _autopilot->getParameterFact("SYS_AUTOCONFIG")->setValue(1); qgcApp()->setOverrideCursor(Qt::WaitCursor); diff --git a/src/AutoPilotPlugins/PX4/AirframeComponentController.h b/src/AutoPilotPlugins/PX4/AirframeComponentController.h index 25ac266a3..0e8b5ced7 100644 --- a/src/AutoPilotPlugins/PX4/AirframeComponentController.h +++ b/src/AutoPilotPlugins/PX4/AirframeComponentController.h @@ -33,14 +33,15 @@ #include "UASInterface.h" #include "AutoPilotPlugin.h" +#include "FactPanelController.h" /// MVC Controller for AirframeComponent.qml. -class AirframeComponentController : public QObject +class AirframeComponentController : public FactPanelController { Q_OBJECT public: - AirframeComponentController(QObject* parent = NULL); + AirframeComponentController(void); ~AirframeComponentController(); Q_PROPERTY(QVariantList airframeTypes MEMBER _airframeTypes CONSTANT) @@ -63,7 +64,6 @@ private: static bool _typesRegistered; UASInterface* _uas; - AutoPilotPlugin* _autoPilotPlugin; QVariantList _airframeTypes; QString _currentAirframeType; QString _currentVehicleName; diff --git a/src/AutoPilotPlugins/PX4/AirframeComponentSummary.qml b/src/AutoPilotPlugins/PX4/AirframeComponentSummary.qml index 7677cb667..2d47877e6 100644 --- a/src/AutoPilotPlugins/PX4/AirframeComponentSummary.qml +++ b/src/AutoPilotPlugins/PX4/AirframeComponentSummary.qml @@ -1,35 +1,39 @@ import QtQuick 2.2 import QtQuick.Controls 1.2 -import QtQuick.Controls.Styles 1.2 import QGroundControl.FactSystem 1.0 import QGroundControl.FactControls 1.0 import QGroundControl.Controls 1.0 import QGroundControl.Controllers 1.0 -Column { - Fact { id: sysIdFact; name: "MAV_SYS_ID" } - Fact { id: sysAutoStartFact; name: "SYS_AUTOSTART" } +FactPanel { + id: panel + anchors.fill: parent - property bool autoStartSet: sysAutoStartFact.value != 0 + AirframeComponentController { id: controller; factPanel: panel } - anchors.fill: parent - anchors.margins: 8 + Fact { id: sysIdFact; name: "MAV_SYS_ID"; onFactMissing: showMissingFactOverlay(name) } + Fact { id: sysAutoStartFact; name: "SYS_AUTOSTART"; onFactMissing: showMissingFactOverlay(name) } - AirframeComponentController { id: controller } + property bool autoStartSet: sysAutoStartFact.value != 0 - VehicleSummaryRow { - labelText: "System ID:" - valueText: sysIdFact.valueString - } + Column { + anchors.fill: parent + anchors.margins: 8 - VehicleSummaryRow { - labelText: "Airframe type:" - valueText: autoStartSet ? controller.currentAirframeType : "Setup required" - } + VehicleSummaryRow { + labelText: "System ID:" + valueText: sysIdFact.valueString + } + + VehicleSummaryRow { + labelText: "Airframe type:" + valueText: autoStartSet ? controller.currentAirframeType : "Setup required" + } - VehicleSummaryRow { - labelText: "Vehicle:" - valueText: autoStartSet ? controller.currentVehicleName : "Setup required" + VehicleSummaryRow { + labelText: "Vehicle:" + valueText: autoStartSet ? controller.currentVehicleName : "Setup required" + } } -} +} \ No newline at end of file diff --git a/src/AutoPilotPlugins/PX4/FlightModesComponentSummary.qml b/src/AutoPilotPlugins/PX4/FlightModesComponentSummary.qml index a61aa6457..451bf8aa3 100644 --- a/src/AutoPilotPlugins/PX4/FlightModesComponentSummary.qml +++ b/src/AutoPilotPlugins/PX4/FlightModesComponentSummary.qml @@ -1,36 +1,40 @@ import QtQuick 2.2 import QtQuick.Controls 1.2 -import QtQuick.Controls.Styles 1.2 import QGroundControl.FactSystem 1.0 +import QGroundControl.FactControls 1.0 import QGroundControl.Controls 1.0 -Column { - Fact { id: modeSwFact; name: "RC_MAP_MODE_SW" } - Fact { id: posCtlSwFact; name: "RC_MAP_POSCTL_SW" } - Fact { id: loiterSwFact; name: "RC_MAP_LOITER_SW" } - Fact { id: returnSwFact; name: "RC_MAP_RETURN_SW" } +FactPanel { + anchors.fill: parent - anchors.fill: parent - anchors.margins: 8 + Fact { id: modeSwFact; name: "RC_MAP_MODE_SW"; onFactMissing: showMissingFactOverlay(name) } + Fact { id: posCtlSwFact; name: "RC_MAP_POSCTL_SW"; onFactMissing: showMissingFactOverlay(name) } + Fact { id: loiterSwFact; name: "RC_MAP_LOITER_SW"; onFactMissing: showMissingFactOverlay(name) } + Fact { id: returnSwFact; name: "RC_MAP_RETURN_SW"; onFactMissing: showMissingFactOverlay(name) } - VehicleSummaryRow { - labelText: "Mode switch:" - valueText: modeSwFact.value == 0 ? "Setup required" : modeSwFact.valueString - } + Column { + anchors.fill: parent + anchors.margins: 8 - VehicleSummaryRow { - labelText: "Position Ctl switch:" - valueText: posCtlSwFact.value == 0 ? "Disabled" : posCtlSwFact.valueString - } + VehicleSummaryRow { + labelText: "Mode switch:" + valueText: modeSwFact.value == 0 ? "Setup required" : modeSwFact.valueString + } - VehicleSummaryRow { - labelText: "Loiter switch:" - valueText: loiterSwFact.value == 0 ? "Disabled" : loiterSwFact.valueString - } + VehicleSummaryRow { + labelText: "Position Ctl switch:" + valueText: posCtlSwFact.value == 0 ? "Disabled" : posCtlSwFact.valueString + } + + VehicleSummaryRow { + labelText: "Loiter switch:" + valueText: loiterSwFact.value == 0 ? "Disabled" : loiterSwFact.valueString + } - VehicleSummaryRow { - labelText: "Return switch:" - valueText: returnSwFact.value == 0 ? "Disabled" : returnSwFact.valueString + VehicleSummaryRow { + labelText: "Return switch:" + valueText: returnSwFact.value == 0 ? "Disabled" : returnSwFact.valueString + } } } diff --git a/src/AutoPilotPlugins/PX4/PowerComponentSummary.qml b/src/AutoPilotPlugins/PX4/PowerComponentSummary.qml index 6577fe9bc..86c3800fa 100644 --- a/src/AutoPilotPlugins/PX4/PowerComponentSummary.qml +++ b/src/AutoPilotPlugins/PX4/PowerComponentSummary.qml @@ -27,32 +27,35 @@ import QtQuick 2.2 import QtQuick.Controls 1.2 -import QtQuick.Controls.Styles 1.2 import QGroundControl.FactSystem 1.0 import QGroundControl.FactControls 1.0 import QGroundControl.Controls 1.0 -Column { - Fact { id: batVChargedFact; name: "BAT_V_CHARGED" } - Fact { id: batVEmptyFact; name: "BAT_V_EMPTY" } - Fact { id: batCellsFact; name: "BAT_N_CELLS" } +FactPanel { + anchors.fill: parent - anchors.fill: parent - anchors.margins: 8 + Fact { id: batVChargedFact; name: "BAT_V_CHARGED"; onFactMissing: showMissingFactOverlay(name) } + Fact { id: batVEmptyFact; name: "BAT_V_EMPTY"; onFactMissing: showMissingFactOverlay(name) } + Fact { id: batCellsFact; name: "BAT_N_CELLS"; onFactMissing: showMissingFactOverlay(name) } - VehicleSummaryRow { - labelText: "Battery Full:" - valueText: batVChargedFact.valueString - } + Column { + anchors.fill: parent + anchors.margins: 8 - VehicleSummaryRow { - labelText: "Battery Empty:" - valueText: batVEmptyFact.valueString - } + VehicleSummaryRow { + labelText: "Battery Full:" + valueText: batVChargedFact.valueString + } + + VehicleSummaryRow { + labelText: "Battery Empty:" + valueText: batVEmptyFact.valueString + } - VehicleSummaryRow { - labelText: "Number of Cells:" - valueText: batCellsFact.valueString + VehicleSummaryRow { + labelText: "Number of Cells:" + valueText: batCellsFact.valueString + } } -} +} \ No newline at end of file diff --git a/src/AutoPilotPlugins/PX4/RadioComponentSummary.qml b/src/AutoPilotPlugins/PX4/RadioComponentSummary.qml index 4038a6161..d74e4552d 100644 --- a/src/AutoPilotPlugins/PX4/RadioComponentSummary.qml +++ b/src/AutoPilotPlugins/PX4/RadioComponentSummary.qml @@ -1,54 +1,58 @@ import QtQuick 2.2 import QtQuick.Controls 1.2 -import QtQuick.Controls.Styles 1.2 import QGroundControl.FactSystem 1.0 +import QGroundControl.FactControls 1.0 import QGroundControl.Controls 1.0 -Column { - Fact { id: mapRollFact; name: "RC_MAP_ROLL" } - Fact { id: mapPitchFact; name: "RC_MAP_PITCH" } - Fact { id: mapYawFact; name: "RC_MAP_YAW" } - Fact { id: mapThrottleFact; name: "RC_MAP_THROTTLE" } - Fact { id: mapFlapsFact; name: "RC_MAP_FLAPS" } - Fact { id: mapAux1Fact; name: "RC_MAP_AUX1" } - Fact { id: mapAux2Fact; name: "RC_MAP_AUX2" } - - anchors.fill: parent - anchors.margins: 8 - - VehicleSummaryRow { - labelText: "Roll:" - valueText: mapRollFact.value == 0 ? "Setup required" : mapRollFact.valueString - } - - VehicleSummaryRow { - labelText: "Pitch:" - valueText: mapPitchFact.value == 0 ? "Setup required" : mapPitchFact.valueString - } - - VehicleSummaryRow { - labelText: "Yaw:" - valueText: mapYawFact.value == 0 ? "Setup required" : mapYawFact.valueString - } - - VehicleSummaryRow { - labelText: "Throttle:" - valueText: mapThrottleFact.value == 0 ? "Setup required" : mapThrottleFact.valueString - } - - VehicleSummaryRow { - labelText: "Flaps:" - valueText: mapFlapsFact.value == 0 ? "Disabled" : mapFlapsFact.valueString - } - - VehicleSummaryRow { - labelText: "Aux1:" - valueText: mapAux1Fact.value == 0 ? "Disabled" : mapAux1Fact.valueString - } - - VehicleSummaryRow { - labelText: "Aux2:" - valueText: mapAux2Fact.value == 0 ? "Disabled" : mapAux2Fact.valueString +FactPanel { + anchors.fill: parent + + Fact { id: mapRollFact; name: "RC_MAP_ROLL"; onFactMissing: showMissingFactOverlay(name) } + Fact { id: mapPitchFact; name: "RC_MAP_PITCH"; onFactMissing: showMissingFactOverlay(name) } + Fact { id: mapYawFact; name: "RC_MAP_YAW"; onFactMissing: showMissingFactOverlay(name) } + Fact { id: mapThrottleFact; name: "RC_MAP_THROTTLE"; onFactMissing: showMissingFactOverlay(name) } + Fact { id: mapFlapsFact; name: "RC_MAP_FLAPS"; onFactMissing: showMissingFactOverlay(name) } + Fact { id: mapAux1Fact; name: "RC_MAP_AUX1"; onFactMissing: showMissingFactOverlay(name) } + Fact { id: mapAux2Fact; name: "RC_MAP_AUX2"; onFactMissing: showMissingFactOverlay(name) } + + Column { + anchors.fill: parent + anchors.margins: 8 + + VehicleSummaryRow { + labelText: "Roll:" + valueText: mapRollFact.value == 0 ? "Setup required" : mapRollFact.valueString + } + + VehicleSummaryRow { + labelText: "Pitch:" + valueText: mapPitchFact.value == 0 ? "Setup required" : mapPitchFact.valueString + } + + VehicleSummaryRow { + labelText: "Yaw:" + valueText: mapYawFact.value == 0 ? "Setup required" : mapYawFact.valueString + } + + VehicleSummaryRow { + labelText: "Throttle:" + valueText: mapThrottleFact.value == 0 ? "Setup required" : mapThrottleFact.valueString + } + + VehicleSummaryRow { + labelText: "Flaps:" + valueText: mapFlapsFact.value == 0 ? "Disabled" : mapFlapsFact.valueString + } + + VehicleSummaryRow { + labelText: "Aux1:" + valueText: mapAux1Fact.value == 0 ? "Disabled" : mapAux1Fact.valueString + } + + VehicleSummaryRow { + labelText: "Aux2:" + valueText: mapAux2Fact.value == 0 ? "Disabled" : mapAux2Fact.valueString + } } } diff --git a/src/AutoPilotPlugins/PX4/SafetyComponentSummary.qml b/src/AutoPilotPlugins/PX4/SafetyComponentSummary.qml index f55be12b6..f5acedbc0 100644 --- a/src/AutoPilotPlugins/PX4/SafetyComponentSummary.qml +++ b/src/AutoPilotPlugins/PX4/SafetyComponentSummary.qml @@ -1,43 +1,46 @@ import QtQuick 2.2 import QtQuick.Controls 1.2 -import QtQuick.Controls.Styles 1.2 import QGroundControl.FactSystem 1.0 import QGroundControl.FactControls 1.0 import QGroundControl.Controls 1.0 -Column { - Fact { id: returnAltFact; name: "RTL_RETURN_ALT" } - Fact { id: descendAltFact; name: "RTL_DESCEND_ALT" } - Fact { id: landDelayFact; name: "RTL_LAND_DELAY" } - Fact { id: commDLLossFact; name: "COM_DL_LOSS_EN" } - Fact { id: commRCLossFact; name: "COM_RC_LOSS_T" } - - anchors.fill: parent - anchors.margins: 8 - - VehicleSummaryRow { - labelText: "RTL min alt:" - valueText: returnAltFact.valueString - } - - VehicleSummaryRow { - labelText: "RTL home alt:" - valueText: descendAltFact.valueString - } - - VehicleSummaryRow { - labelText: "RTL loiter delay:" - valueText: landDelayFact.value < 0 ? "Disabled" : landDelayFact.valueString - } - - VehicleSummaryRow { - labelText: "Telemetry loss RTL:" - valueText: commDLLossFact.value != -1 ? "Disabled" : commDLLossFact.valueString - } - - VehicleSummaryRow { - labelText: "RC loss RTL (seconds):" - valueText: commRCLossFact.valueString +FactPanel { + anchors.fill: parent + + Fact { id: returnAltFact; name: "RTL_RETURN_ALT"; onFactMissing: showMissingFactOverlay(name) } + Fact { id: descendAltFact; name: "RTL_DESCEND_ALT"; onFactMissing: showMissingFactOverlay(name) } + Fact { id: landDelayFact; name: "RTL_LAND_DELAY"; onFactMissing: showMissingFactOverlay(name) } + Fact { id: commDLLossFact; name: "COM_DL_LOSS_EN"; onFactMissing: showMissingFactOverlay(name) } + Fact { id: commRCLossFact; name: "COM_RC_LOSS_T"; onFactMissing: showMissingFactOverlay(name) } + + Column { + anchors.fill: parent + anchors.margins: 8 + + VehicleSummaryRow { + labelText: "RTL min alt:" + valueText: returnAltFact.valueString + } + + VehicleSummaryRow { + labelText: "RTL home alt:" + valueText: descendAltFact.valueString + } + + VehicleSummaryRow { + labelText: "RTL loiter delay:" + valueText: landDelayFact.value < 0 ? "Disabled" : landDelayFact.valueString + } + + VehicleSummaryRow { + labelText: "Telemetry loss RTL:" + valueText: commDLLossFact.value != -1 ? "Disabled" : commDLLossFact.valueString + } + + VehicleSummaryRow { + labelText: "RC loss RTL (seconds):" + valueText: commRCLossFact.valueString + } } } diff --git a/src/AutoPilotPlugins/PX4/SensorsComponentSummary.qml b/src/AutoPilotPlugins/PX4/SensorsComponentSummary.qml index 3fabc547a..b79a2206f 100644 --- a/src/AutoPilotPlugins/PX4/SensorsComponentSummary.qml +++ b/src/AutoPilotPlugins/PX4/SensorsComponentSummary.qml @@ -3,32 +3,37 @@ import QtQuick.Controls 1.2 import QtQuick.Controls.Styles 1.2 import QGroundControl.FactSystem 1.0 +import QGroundControl.FactControls 1.0 import QGroundControl.Controls 1.0 /* IMPORTANT NOTE: Any changes made here must also be made to SensorsComponentSummary.qml */ -Column { - Fact { id: mag0IdFact; name: "CAL_MAG0_ID" } - Fact { id: gyro0IdFact; name: "CAL_GYRO0_ID" } - Fact { id: accel0IdFact; name: "CAL_ACC0_ID" } +FactPanel { + anchors.fill: parent - anchors.fill: parent - anchors.margins: 8 + Fact { id: mag0IdFact; name: "CAL_MAG0_ID"; onFactMissing: showMissingFactOverlay(name) } + Fact { id: gyro0IdFact; name: "CAL_GYRO0_ID"; onFactMissing: showMissingFactOverlay(name) } + Fact { id: accel0IdFact; name: "CAL_ACC0_ID"; onFactMissing: showMissingFactOverlay(name) } - VehicleSummaryRow { - labelText: "Compass:" - valueText: mag0IdFact.value == 0 ? "Setup required" : "Ready" - } + Column { + anchors.fill: parent + anchors.margins: 8 - VehicleSummaryRow { - labelText: "Gyro:" - valueText: gyro0IdFact.value == 0 ? "Setup required" : "Ready" - } + VehicleSummaryRow { + labelText: "Compass:" + valueText: mag0IdFact.value == 0 ? "Setup required" : "Ready" + } + + VehicleSummaryRow { + labelText: "Gyro:" + valueText: gyro0IdFact.value == 0 ? "Setup required" : "Ready" + } - VehicleSummaryRow { - labelText: "Accelerometer:" - valueText: accel0IdFact.value == 0 ? "Setup required" : "Ready" + VehicleSummaryRow { + labelText: "Accelerometer:" + valueText: accel0IdFact.value == 0 ? "Setup required" : "Ready" + } } -} +} \ No newline at end of file diff --git a/src/AutoPilotPlugins/PX4/SensorsComponentSummaryFixedWing.qml b/src/AutoPilotPlugins/PX4/SensorsComponentSummaryFixedWing.qml index 770508674..23580cafa 100644 --- a/src/AutoPilotPlugins/PX4/SensorsComponentSummaryFixedWing.qml +++ b/src/AutoPilotPlugins/PX4/SensorsComponentSummaryFixedWing.qml @@ -1,40 +1,44 @@ import QtQuick 2.2 import QtQuick.Controls 1.2 -import QtQuick.Controls.Styles 1.2 import QGroundControl.FactSystem 1.0 +import QGroundControl.FactControls 1.0 import QGroundControl.Controls 1.0 /* IMPORTANT NOTE: Any changes made here must also be made to SensorsComponentSummary.qml */ -Column { - Fact { id: mag0IdFact; name: "CAL_MAG0_ID" } - Fact { id: gyro0IdFact; name: "CAL_GYRO0_ID" } - Fact { id: accel0IdFact; name: "CAL_ACC0_ID" } - Fact { id: dPressOffFact; name: "SENS_DPRES_OFF" } - - anchors.fill: parent - anchors.margins: 8 - - VehicleSummaryRow { - labelText: "Compass:" - valueText: mag0IdFact.value == 0 ? "Setup required" : "Ready" - } - - VehicleSummaryRow { - labelText: "Gyro:" - valueText: gyro0IdFact.value == 0 ? "Setup required" : "Ready" - } - - VehicleSummaryRow { - labelText: "Accelerometer:" - valueText: accel0IdFact.value == 0 ? "Setup required" : "Ready" - } - - VehicleSummaryRow { - labelText: "Airspeed:" - valueText: dPressOffFact.value == 0 ? "Setup required" : "Ready" +FactPanel { + anchors.fill: parent + + Fact { id: mag0IdFact; name: "CAL_MAG0_ID"; onFactMissing: showMissingFactOverlay(name) } + Fact { id: gyro0IdFact; name: "CAL_GYRO0_ID"; onFactMissing: showMissingFactOverlay(name) } + Fact { id: accel0IdFact; name: "CAL_ACC0_ID"; onFactMissing: showMissingFactOverlay(name) } + Fact { id: dPressOffFact; name: "SENS_DPRES_OFF"; onFactMissing: showMissingFactOverlay(name) } + + Column { + anchors.fill: parent + anchors.margins: 8 + + VehicleSummaryRow { + labelText: "Compass:" + valueText: mag0IdFact.value == 0 ? "Setup required" : "Ready" + } + + VehicleSummaryRow { + labelText: "Gyro:" + valueText: gyro0IdFact.value == 0 ? "Setup required" : "Ready" + } + + VehicleSummaryRow { + labelText: "Accelerometer:" + valueText: accel0IdFact.value == 0 ? "Setup required" : "Ready" + } + + VehicleSummaryRow { + labelText: "Airspeed:" + valueText: dPressOffFact.value == 0 ? "Setup required" : "Ready" + } } } diff --git a/src/FactSystem/Fact.h b/src/FactSystem/Fact.h index 5ba111395..5d0cb58cf 100644 --- a/src/FactSystem/Fact.h +++ b/src/FactSystem/Fact.h @@ -35,16 +35,11 @@ #include /// @brief A Fact is used to hold a single value within the system. -/// -/// Along with the value property is a set of meta data which further describes the Fact. This information is -/// exposed through QObject Properties such that you can bind to it from QML as well as use it within C++ code. -/// Since the meta data is common to all instances of the same Fact, it is acually stored once in a seperate object. class Fact : public QObject { Q_OBJECT public: - //Fact(int componentId, QString name = "", FactMetaData::ValueType_t type = FactMetaData::valueTypeInt32, QObject* parent = NULL); Fact(int componentId, QString name, FactMetaData::ValueType_t type, QObject* parent = NULL); // Property system methods diff --git a/src/FactSystem/FactBinder.cc b/src/FactSystem/FactBinder.cc index d0979c390..55c1965d0 100644 --- a/src/FactSystem/FactBinder.cc +++ b/src/FactSystem/FactBinder.cc @@ -34,7 +34,8 @@ FactBinder::FactBinder(void) : _autopilotPlugin(NULL), _fact(NULL), - _componentId(FactSystem::defaultComponentId) + _componentId(FactSystem::defaultComponentId), + _factMissingSignalConnected(false) { UASInterface* uas = UASManager::instance()->getActiveUAS(); Q_ASSERT(uas); @@ -85,8 +86,12 @@ void FactBinder::setName(const QString& name) emit nameChanged(); emit metaDataChanged(); } else { - QString panicMessage("Required parameter (component id: %1, name: %2), is missing from vehicle. QGroundControl cannot operate with this firmware revision. QGroundControl will now shut down."); - qgcApp()->panicShutdown(panicMessage.arg(_componentId).arg(parsedName)); + qgcApp()->reportMissingFact(name); + if (_factMissingSignalConnected) { + emit factMissing(name); + } else { + _missedFactMissingSignals << name; + } } } } @@ -208,3 +213,27 @@ bool FactBinder::valueEqualsDefault(void) return false; } } + +void FactBinder::connectNotify(const QMetaMethod & signal) +{ + if (signal == QMetaMethod::fromSignal(&FactBinder::factMissing)) { + _factMissingSignalConnected = true; + if (_missedFactMissingSignals.count()) { + QTimer::singleShot(10, this, &FactBinder::_delayedFactMissing); + } + } +} + +void FactBinder::disconnectNotify(const QMetaMethod & signal) +{ + if (signal == QMetaMethod::fromSignal(&FactBinder::factMissing)) { + _factMissingSignalConnected = false; + } +} + +void FactBinder::_delayedFactMissing(void) +{ + foreach (QString name, _missedFactMissingSignals) { + emit factMissing(name); + } +} \ No newline at end of file diff --git a/src/FactSystem/FactBinder.h b/src/FactSystem/FactBinder.h index 8cac12080..22bb9f0c4 100644 --- a/src/FactSystem/FactBinder.h +++ b/src/FactSystem/FactBinder.h @@ -78,14 +78,24 @@ public: QString group(void); signals: + void factMissing(const QString& name); void nameChanged(void); void valueChanged(void); void metaDataChanged(void); +private slots: + void _delayedFactMissing(void); + private: + // Overrides from QObject + void connectNotify(const QMetaMethod & signal); + void disconnectNotify(const QMetaMethod & signal); + AutoPilotPlugin* _autopilotPlugin; Fact* _fact; int _componentId; + bool _factMissingSignalConnected; + QStringList _missedFactMissingSignals; }; #endif \ No newline at end of file diff --git a/src/FactSystem/FactControls/FactPanel.qml b/src/FactSystem/FactControls/FactPanel.qml new file mode 100644 index 000000000..dde0acf93 --- /dev/null +++ b/src/FactSystem/FactControls/FactPanel.qml @@ -0,0 +1,61 @@ +/*===================================================================== + + QGroundControl Open Source Ground Control Station + + (c) 2009 - 2015 QGROUNDCONTROL PROJECT + + This file is part of the QGROUNDCONTROL project + + 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. + + 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. + + You should have received a copy of the GNU General Public License + along with QGROUNDCONTROL. If not, see . + + ======================================================================*/ + +/// @file +/// @author Don Gagne + +import QtQuick 2.3 +import QtQuick.Controls 1.3 + +import QGroundControl.FactSystem 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.Palette 1.0 + +Item { + property string __missingFacts: "" + + function showMissingFactOverlay(missingFactName) { + if (__missingFacts.length != 0) { + __missingFacts = __missingFacts.concat(", ") + } + __missingFacts = __missingFacts.concat(missingFactName) + __missingFactOverlay.visible = true + } + + Rectangle { + QGCPalette { id: __qgcPal; colorGroupEnabled: true } + + id: __missingFactOverlay + anchors.fill: parent + z: 9999 + visible: false + color: __qgcPal.window + opacity: 0.85 + + QGCLabel { + anchors.fill: parent + wrapMode: Text.WordWrap + text: "Fact(s) missing: " + __missingFacts + } + } +} diff --git a/src/FactSystem/FactControls/FactPanelController.cc b/src/FactSystem/FactControls/FactPanelController.cc new file mode 100644 index 000000000..ddebadd4a --- /dev/null +++ b/src/FactSystem/FactControls/FactPanelController.cc @@ -0,0 +1,108 @@ +/*===================================================================== + + QGroundControl Open Source Ground Control Station + + (c) 2009 - 2014 QGROUNDCONTROL PROJECT + + This file is part of the QGROUNDCONTROL project + + 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. + + 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. + + You should have received a copy of the GNU General Public License + along with QGROUNDCONTROL. If not, see . + + ======================================================================*/ + +#include "FactPanelController.h" +#include "UASManager.h" +#include "AutoPilotPluginManager.h" +#include "QGCMessageBox.h" + +/// @file +/// @author Don Gagne + +FactPanelController::FactPanelController(void) : + _autopilot(NULL), + _factPanel(NULL) +{ + UASInterface* uas = UASManager::instance()->getActiveUAS(); + Q_ASSERT(uas); + + _autopilot = AutoPilotPluginManager::instance()->getInstanceForAutoPilotPlugin(uas); + Q_ASSERT(_autopilot); + Q_ASSERT(_autopilot->pluginReady()); + + // Do a delayed check for the _factPanel finally being set correctly from Qml + QTimer::singleShot(1000, this, &FactPanelController::_checkForMissingFactPanel); +} + +QQuickItem* FactPanelController::factPanel(void) +{ + return _factPanel; +} + +void FactPanelController::setFactPanel(QQuickItem* panel) +{ + // Once we finally have the _factPanel member set send any + // missing fact notices that were waiting to go out + + _factPanel = panel; + foreach (QString missingFact, _delayedMissingFacts) { + _notifyPanelMissingFact(missingFact); + } + _delayedMissingFacts.clear(); +} + +void FactPanelController::_notifyPanelMissingFact(const QString& missingFact) +{ + QVariant returnedValue; + + QMetaObject::invokeMethod(_factPanel, + "showMissingFactOverlay", + Q_RETURN_ARG(QVariant, returnedValue), + Q_ARG(QVariant, missingFact)); +} + +void FactPanelController::_reportMissingFact(const QString& missingFact) +{ + qgcApp()->reportMissingFact(missingFact); + + // If missing facts a reported from the constructor of a derived class we + // will not have access to _factPanel yet. Just record list of missing facts + // in that case instead of notify. Once _factPanel is available they will be + // send out for real. + if (_factPanel) { + _notifyPanelMissingFact(missingFact); + } else { + _delayedMissingFacts += missingFact; + } +} + +bool FactPanelController::_allFactsExists(QStringList factList) +{ + bool noMissingFacts = true; + + foreach (QString fact, factList) { + if (!_autopilot->parameterExists(fact)) { + _reportMissingFact(fact); + noMissingFacts = false; + } + } + + return noMissingFacts; +} + +void FactPanelController::_checkForMissingFactPanel(void) +{ + if (!_factPanel) { + QGCMessageBox::critical("Incorrect FactPanel Qml implementation", "FactPanelController used without passing in factPanel. This could lead to non-functioning user interface being displayed."); + } +} \ No newline at end of file diff --git a/src/FactSystem/FactControls/FactPanelController.h b/src/FactSystem/FactControls/FactPanelController.h new file mode 100644 index 000000000..094114320 --- /dev/null +++ b/src/FactSystem/FactControls/FactPanelController.h @@ -0,0 +1,71 @@ +/*===================================================================== + + QGroundControl Open Source Ground Control Station + + (c) 2009 - 2014 QGROUNDCONTROL PROJECT + + This file is part of the QGROUNDCONTROL project + + 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. + + 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. + + You should have received a copy of the GNU General Public License + along with QGROUNDCONTROL. If not, see . + + ======================================================================*/ + +#ifndef FactPanelController_H +#define FactPanelController_H + +/// @file +/// @author Don Gagne + +#include +#include + +#include "UASInterface.h" +#include "AutoPilotPlugin.h" +#include "UASManagerInterface.h" + +/// FactPanelController is used in combination with the FactPanel Qml control for handling +/// missing Facts from C++ code. +class FactPanelController : public QObject +{ + Q_OBJECT + +public: + FactPanelController(void); + + Q_PROPERTY(QQuickItem* factPanel READ factPanel WRITE setFactPanel) + + QQuickItem* factPanel(void); + void setFactPanel(QQuickItem* panel); + +protected: + /// Checks for existence of the specified facts + /// @return true: all facts exists, false: facts missing and reported + bool _allFactsExists(QStringList factList); + + /// Report a missing fact to the FactPanel Qml element + void _reportMissingFact(const QString& missingFact); + + AutoPilotPlugin* _autopilot; + +private slots: + void _checkForMissingFactPanel(void); + +private: + void _notifyPanelMissingFact(const QString& missingFact); + + QQuickItem* _factPanel; + QStringList _delayedMissingFacts; +}; + +#endif \ No newline at end of file diff --git a/src/FactSystem/FactControls/qmldir b/src/FactSystem/FactControls/qmldir index 9a6de26b5..b982d612f 100644 --- a/src/FactSystem/FactControls/qmldir +++ b/src/FactSystem/FactControls/qmldir @@ -1,5 +1,6 @@ Module QGroundControl.FactControls +FactPanel 1.0 FactPanel.qml FactLabel 1.0 FactLabel.qml FactTextField 1.0 FactTextField.qml FactCheckBox 1.0 FactCheckBox.qml diff --git a/src/FactSystem/FactSystem.cc b/src/FactSystem/FactSystem.cc index 450b0b8e3..257193792 100644 --- a/src/FactSystem/FactSystem.cc +++ b/src/FactSystem/FactSystem.cc @@ -29,6 +29,7 @@ #include "QGCApplication.h" #include "VehicleComponent.h" #include "FactBinder.h" +#include "FactPanelController.h" #include @@ -40,6 +41,7 @@ FactSystem::FactSystem(QObject* parent) : QGCSingleton(parent) { qmlRegisterType(_factSystemQmlUri, 1, 0, "Fact"); + qmlRegisterType(_factSystemQmlUri, 1, 0, "FactPanelController"); qmlRegisterUncreatableType(_factSystemQmlUri, 1, 0, "VehicleComponent", "Can only reference, cannot create"); } diff --git a/src/FactSystem/ParameterLoader.cc b/src/FactSystem/ParameterLoader.cc index 27d235fe3..94d635025 100644 --- a/src/FactSystem/ParameterLoader.cc +++ b/src/FactSystem/ParameterLoader.cc @@ -386,8 +386,7 @@ Fact* ParameterLoader::getFact(int componentId, const QString& name) componentId = _actualComponentId(componentId); if (!_mapParameterName2Variant.contains(componentId) || !_mapParameterName2Variant[componentId].contains(name)) { - QString panicMessage("Required parameter (component id: %1, name: %2), is missing from vehicle. QGroundControl cannot operate with this firmware revision. QGroundControl will now shut down."); - qgcApp()->panicShutdown(panicMessage.arg(componentId).arg(name)); + return NULL; } Fact* fact = _mapParameterName2Variant[componentId][name].value(); diff --git a/src/QGCApplication.cc b/src/QGCApplication.cc index b9db5f144..6f685abcf 100644 --- a/src/QGCApplication.cc +++ b/src/QGCApplication.cc @@ -166,6 +166,10 @@ QGCApplication::QGCApplication(int &argc, char* argv[], bool unitTesting) : #endif #endif + // Set up timer for delayed missing fact display + _missingFactDelayedDisplayTimer.setSingleShot(true); + _missingFactDelayedDisplayTimer.setInterval(_missingFactDelayedDisplayTimerTimeout); + connect(&_missingFactDelayedDisplayTimer, &QTimer::timeout, this, &QGCApplication::_missingFactsDisplay); // Set application information if (_runningUnitTests) { @@ -660,8 +664,28 @@ void QGCApplication::_reconnect(void) _reconnectLinkConfig = NULL; } -void QGCApplication::panicShutdown(const QString& panicMessage) +void QGCApplication::reportMissingFact(const QString& name) { - QGCMessageBox::critical("Panic Shutdown", panicMessage); - ::exit(0); + _missingFacts += name; + _missingFactDelayedDisplayTimer.start(); } + +/// Called when the delay timer fires to show the missing facts warning +void QGCApplication::_missingFactsDisplay(void) +{ + Q_ASSERT(_missingFacts.count()); + + QString facts; + foreach (QString fact, _missingFacts) { + if (facts.isEmpty()) { + facts += fact; + } else { + facts += QString(", %1").arg(fact); + } + } + _missingFacts.clear(); + + QGCMessageBox::critical("Missing Parameters", + QString("Parameters missing from firmware: %1.\n\n" + "You should quit QGroundControl immediately and update your firmware.").arg(facts)); +} \ No newline at end of file diff --git a/src/QGCApplication.h b/src/QGCApplication.h index 695d3d7d4..481f88319 100644 --- a/src/QGCApplication.h +++ b/src/QGCApplication.h @@ -33,6 +33,7 @@ #define QGCAPPLICATION_H #include +#include #include "LinkConfiguration.h" @@ -98,9 +99,9 @@ public: /// Disconnects the current link and waits for the specified number of seconds before reconnecting. void reconnectAfterWait(int waitSeconds); - /// Used to shutdown the app if a fatal condition occurs from which it cannot recover - /// @param panicMessage Message to display to user - void panicShutdown(const QString& panicMessage); + /// Used to report a missing Fact. Warning will be displayed to user. Method may be called + /// multiple times. + void reportMissingFact(const QString& name); public slots: /// You can connect to this slot to show an information message box from a different thread. @@ -143,6 +144,7 @@ public: private slots: void _reconnect(void); + void _missingFactsDisplay(void); private: void _createSingletons(void); @@ -165,7 +167,11 @@ private: static const char* _lightStyleFile; bool _styleIsDark; ///< true: dark style, false: light style - LinkConfiguration* _reconnectLinkConfig; ///< Configuration to reconnect for reconnectAfterWai + LinkConfiguration* _reconnectLinkConfig; ///< Configuration to reconnect for reconnectAfterWait + + static const int _missingFactDelayedDisplayTimerTimeout = 1000; ///< Timeout to wait for next missing fact to come in before display + QTimer _missingFactDelayedDisplayTimer; ///< Timer use to delay missing fact display + QStringList _missingFacts; ///< List of missing facts to be displayed /// Unit Test have access to creating and destroying singletons friend class UnitTest; -- 2.22.0