Commit ff32b9fe authored by Tomaz Canabrava's avatar Tomaz Canabrava

Add the initial stub for the AirFrame configuration for APM

This commit introduces the APM Airframe configuration. The interface
is the same as the PX4 one, but the way we deal with the uas is a bit different

Since the APM stack doesn't provide a xml with airframe definitions a
new one was created by hand with only the values that we need, wich will
trigger a download of the parameters file from the mavlink github

Now it correctly handles the FRAME variable and it's faster
regarding the download of the parameters for each type of Frame.

Only show Airframes for ArduCopter, not for ArduRover nor ArduPlane
Signed-off-by: 's avatarTomaz Canabrava <tomaz.canabrava@intel.com>
parent 46604020
......@@ -208,6 +208,9 @@
<file alias="AirframeFactMetaData.xml">src/AutoPilotPlugins/PX4/AirframeFactMetaData.xml</file>
<file alias="ParameterFactMetaData.xml">src/AutoPilotPlugins/PX4/ParameterFactMetaData.xml</file>
</qresource>
<qresource prefix="/AutoPilotPlugins/APM">
<file alias="AirframeFactMetaData.xml">src/AutoPilotPlugins/APM/AirframeFactMetaData.xml</file>
</qresource>
<qresource prefix="/FirmwarePlugin/APM">
<file alias="apm.pdef.xml">src/FirmwarePlugin/APM/apm.pdef.xml</file>
......
......@@ -282,7 +282,9 @@ HEADERS += \
src/uas/UASMessageHandler.h \
src/ui/toolbar/MainToolBarController.h \
src/AutoPilotPlugins/PX4/PX4AirframeLoader.h \
src/AutoPilotPlugins/APM/APMAirframeLoader.h \
src/QmlControls/QGCImageProvider.h \
src/AutoPilotPlugins/APM/APMRemoteParamsDownloader.h
DebugBuild {
HEADERS += \
......@@ -398,7 +400,9 @@ SOURCES += \
src/uas/UASMessageHandler.cc \
src/ui/toolbar/MainToolBarController.cc \
src/AutoPilotPlugins/PX4/PX4AirframeLoader.cc \
src/AutoPilotPlugins/APM/APMAirframeLoader.cc \
src/QmlControls/QGCImageProvider.cc \
src/AutoPilotPlugins/APM/APMRemoteParamsDownloader.cc
DebugBuild {
SOURCES += \
......@@ -550,6 +554,8 @@ HEADERS+= \
src/AutoPilotPlugins/AutoPilotPluginManager.h \
src/AutoPilotPlugins/APM/APMAutoPilotPlugin.h \
src/AutoPilotPlugins/APM/APMAirframeComponent.h \
src/AutoPilotPlugins/APM/APMAirframeComponentController.h \
src/AutoPilotPlugins/APM/APMAirframeComponentAirframes.h \
src/AutoPilotPlugins/APM/APMComponent.h \
src/AutoPilotPlugins/APM/APMRadioComponent.h \
src/AutoPilotPlugins/APM/APMFlightModesComponent.h \
......@@ -601,6 +607,7 @@ SOURCES += \
src/AutoPilotPlugins/AutoPilotPluginManager.cc \
src/AutoPilotPlugins/APM/APMAutoPilotPlugin.cc \
src/AutoPilotPlugins/APM/APMAirframeComponent.cc \
src/AutoPilotPlugins/APM/APMAirframeComponentController.cc \
src/AutoPilotPlugins/APM/APMComponent.cc \
src/AutoPilotPlugins/APM/APMRadioComponent.cc \
src/AutoPilotPlugins/APM/APMFlightModesComponent.cc \
......@@ -610,6 +617,7 @@ SOURCES += \
src/AutoPilotPlugins/APM/APMSensorsComponentController.cc \
src/AutoPilotPlugins/APM/APMTuningComponent.cc \
src/AutoPilotPlugins/Common/RadioComponentController.cc \
src/AutoPilotPlugins/APM/APMAirframeComponentAirframes.cc \
src/AutoPilotPlugins/Generic/GenericAutoPilotPlugin.cc \
src/AutoPilotPlugins/PX4/AirframeComponent.cc \
src/AutoPilotPlugins/PX4/AirframeComponentAirframes.cc \
......
......@@ -57,22 +57,13 @@ bool APMAirframeComponent::requiresSetup(void) const
bool APMAirframeComponent::setupComplete(void) const
{
// You'll need to figure out which parameters trigger setup complete
#if 0
return _autopilot->getParameterFact(FactSystem::defaultComponentId, "SYS_AUTOSTART")->rawValue().toInt() != 0;
#else
return true;
#endif
//: Not the correct one, but it works for the moment.
return _autopilot->getParameterFact(FactSystem::defaultComponentId, "FRAME")->rawValue().toInt() != -1;
}
QStringList APMAirframeComponent::setupCompleteChangedTriggerList(void) const
{
// You'll need to figure out which parameters trigger setup complete
#if 0
return QStringList("SYS_AUTOSTART");
#else
return QStringList();
#endif
}
QUrl APMAirframeComponent::setupSource(void) const
......
......@@ -21,7 +21,7 @@
======================================================================*/
import QtQuick 2.2
import QtQuick 2.5
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.2
import QtQuick.Dialogs 1.2
......@@ -39,13 +39,214 @@ QGCView {
QGCPalette { id: qgcPal; colorGroupEnabled: panel.enabled }
property real _minW: ScreenTools.defaultFontPixelWidth * 30
property real _boxWidth: _minW
property real _boxSpace: ScreenTools.defaultFontPixelWidth
property Fact sysIdFact: controller.getParameterFact(-1, "FRAME")
function computeDimensions() {
var sw = 0
var rw = 0
var idx = Math.floor(scroll.width / (_minW + ScreenTools.defaultFontPixelWidth))
if(idx < 1) {
_boxWidth = scroll.width
_boxSpace = 0
} else {
_boxSpace = 0
if(idx > 1) {
_boxSpace = ScreenTools.defaultFontPixelWidth
sw = _boxSpace * (idx - 1)
}
rw = scroll.width - sw
_boxWidth = rw / idx
}
}
APMAirframeComponentController {
id: controller
factPanel: panel
}
Component {
id: applyRestartDialogComponent
QGCViewDialog {
id: applyRestartDialog
Connections {
target: controller
onCurrentAirframeTypeChanged: {
airframePicker.model = controller.currentAirframeType.airframes;
}
onCurrentAirframeChanged : {
hideDialog();
}
}
QGCLabel {
id: applyParamsText
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.margins: _boxSpace
wrapMode: Text.WordWrap
text: "Select you drone to load the default parameters for it. "
}
Flow {
anchors.top : applyParamsText.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
spacing : _boxSpace
layoutDirection: Qt.Vertical;
anchors.margins : _boxSpace
Repeater {
id : airframePicker
model : controller.currentAirframeType.airframes;
delegate: QGCButton {
id: btnParams
width: parent.width / 2.1
height: (ScreenTools.defaultFontPixelHeight * 14) / 5
text: controller.currentAirframeType.airframes[index].name;
onClicked : {
controller.currentAirframe = controller.currentAirframeType.airframes[index]
}
}
}
}
}
}
QGCViewPanel {
id: panel
anchors.fill: parent
QGCLabel {
text: "Work in progress";
readonly property real spacerHeight: ScreenTools.defaultFontPixelHeight
onWidthChanged: {
computeDimensions()
}
Item {
id: helpApplyRow
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
height: Math.max(helpText.contentHeight, applyButton.height)
QGCLabel {
id: helpText
width: parent.width - applyButton.width - 5
text: qsTr("Please select your airframe type")
font.pixelSize: ScreenTools.mediumFontPixelSize
wrapMode: Text.WordWrap
}
QGCButton {
id: applyButton
anchors.right: parent.right
text: qsTr("Load common parameters")
onClicked: showDialog(applyRestartDialogComponent, qsTr("Load common parameters"), 50, StandardButton.Close)
}
}
Item {
id: lastSpacer
anchors.top: helpApplyRow.bottom
height: parent.spacerHeight
width: 10
}
Flickable {
id: scroll
anchors.top: lastSpacer.bottom
width: parent.width;
height: parent.height;
clip: true
boundsBehavior: Flickable.StopAtBounds
flickableDirection: Flickable.VerticalFlick
onWidthChanged: {
computeDimensions()
}
Flow {
id: flowView
width: scroll.width
spacing: _boxSpace
ExclusiveGroup {
id: airframeTypeExclusive
}
Repeater {
model: controller.airframeTypesModel
// Outer summary item rectangle
delegate : Rectangle {
id: airframeBackground
width: _boxWidth
height: ScreenTools.defaultFontPixelHeight * 14
color: qgcPal.windowShade;
readonly property real titleHeight: ScreenTools.defaultFontPixelHeight * 1.75
readonly property real innerMargin: ScreenTools.defaultFontPixelWidth
MouseArea {
anchors.fill: parent
onClicked: airframeCheckBox.checked = true;
}
Rectangle {
id: nameRect;
width: parent.width
height: parent.titleHeight
color: qgcPal.windowShadeDark
QGCLabel {
anchors.fill: parent
color: qgcPal.buttonText
verticalAlignment: TextEdit.AlignVCenter
horizontalAlignment: TextEdit.AlignHCenter
text: object.name
}
}
Image {
id: imageRect
anchors.topMargin: innerMargin
anchors.top: nameRect.bottom
width: parent.width * 0.75
height: parent.height - nameRect.height - (innerMargin * 3)
fillMode: Image.PreserveAspectFit
smooth: true
mipmap: true
source: object.imageResource
anchors.horizontalCenter: parent.horizontalCenter
}
QGCCheckBox {
id: airframeCheckBox
checked: object.type == sysIdFact.value
exclusiveGroup: airframeTypeExclusive
anchors.bottom: imageRect.bottom
anchors.right: parent.right
anchors.rightMargin: innerMargin
onCheckedChanged: {
if (checked) {
controller.currentAirframeType = object
}
airframeBackground.color = checked ? qgcPal.buttonHighlight : qgcPal.windowShade;
}
}
}
}
}
} // Scroll View - summary boxes
} // QGCViewPanel
} // QGCView
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009, 2015 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
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 <http://www.gnu.org/licenses/>.
======================================================================*/
/// @file
/// @author Don Gagne <don@thegagnes.com>
#include "APMAirframeComponentAirframes.h"
#include "APMAirframeComponentController.h"
QMap<QString, APMAirframeComponentAirframes::AirframeType_t*> APMAirframeComponentAirframes::rgAirframeTypes;
QMap<QString, APMAirframeComponentAirframes::AirframeType_t*>& APMAirframeComponentAirframes::get() {
return rgAirframeTypes;
}
void APMAirframeComponentAirframes::insert(const QString& group, int groupId, const QString& image,const QString& name, const QString& file)
{
AirframeType_t *g;
if (!rgAirframeTypes.contains(group)) {
g = new AirframeType_t;
g->name = group;
g->type = groupId;
g->imageResource = QString("qrc:/qmlimages/") + (!image.isEmpty() ? image : QString("AirframeStandardPlane.png"));
rgAirframeTypes.insert(group, g);
} else {
g = rgAirframeTypes.value(group);
}
if (!name.isEmpty() && !file.isEmpty())
g->rgAirframeInfo.append(new APMAirframe(name, file, g->type));
}
void APMAirframeComponentAirframes::clear() {
QList<AirframeType_t*> valueList = get().values();
foreach(AirframeType_t *pType, valueList) {
qDeleteAll(pType->rgAirframeInfo);
delete pType;
}
rgAirframeTypes.clear();
}
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009, 2015 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
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 <http://www.gnu.org/licenses/>.
======================================================================*/
/// @file
/// @author Don Gagne <don@thegagnes.com>
#ifndef APMAirframeComponentAirframes_H
#define APMAirframeComponentAirframes_H
#include <QObject>
#include <QQuickItem>
#include <QList>
#include <QMap>
#include "UASInterface.h"
#include "AutoPilotPlugin.h"
class APMAirframe;
/// MVC Controller for AirframeComponent.qml.
class APMAirframeComponentAirframes
{
public:
typedef struct {
QString name;
QString imageResource;
int type;
QList<APMAirframe*> rgAirframeInfo;
} AirframeType_t;
typedef QMap<QString, AirframeType_t*> AirframeTypeMap;
static AirframeTypeMap& get();
static void clear();
static void insert(const QString& group, int groupId, const QString& image,const QString& name = QString(), const QString& file = QString());
protected:
static AirframeTypeMap rgAirframeTypes;
private:
};
#endif
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009, 2015 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
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 <http://www.gnu.org/licenses/>.
======================================================================*/
/// @file
/// @author Don Gagne <don@thegagnes.com>
#include "APMAirframeComponentController.h"
#include "APMAirframeComponentAirframes.h"
#include "APMRemoteParamsDownloader.h"
#include "QGCMAVLink.h"
#include "MultiVehicleManager.h"
#include "AutoPilotPluginManager.h"
#include "QGCApplication.h"
#include <QVariant>
#include <QQmlProperty>
bool APMAirframeComponentController::_typesRegistered = false;
APMAirframeComponentController::APMAirframeComponentController(void) :
_airframeTypesModel(new QmlObjectListModel(this))
{
if (!_typesRegistered) {
_typesRegistered = true;
qmlRegisterUncreatableType<APMAirframeType>("QGroundControl.Controllers", 1, 0, "APMAiframeType", "Can only reference APMAirframeType");
qmlRegisterUncreatableType<APMAirframe>("QGroundControl.Controllers", 1, 0, "APMAiframe", "Can only reference APMAirframe");
}
_fillAirFrames();
Fact *frame = getParameterFact(FactSystem::defaultComponentId, "FRAME");
connect(frame, &Fact::vehicleUpdated, this, &APMAirframeComponentController::_factFrameChanged);
_factFrameChanged(frame->rawValue());
}
APMAirframeComponentController::~APMAirframeComponentController()
{
}
void APMAirframeComponentController::_factFrameChanged(QVariant value)
{
FrameId v = (FrameId) value.toInt();
for(int i = 0, size = _airframeTypesModel->count(); i < size; i++ ) {
APMAirframeType *airframeType = qobject_cast<APMAirframeType*>(_airframeTypesModel->get(i));
Q_ASSERT(airframeType);
if (airframeType->type() == v) {
_currentAirframeType = airframeType;
break;
}
}
emit currentAirframeTypeChanged(_currentAirframeType);
}
void APMAirframeComponentController::_fillAirFrames()
{
for (int tindex = 0; tindex < APMAirframeComponentAirframes::get().count(); tindex++) {
const APMAirframeComponentAirframes::AirframeType_t* pType = APMAirframeComponentAirframes::get().values().at(tindex);
APMAirframeType* airframeType = new APMAirframeType(pType->name, pType->imageResource, pType->type, this);
Q_CHECK_PTR(airframeType);
for (int index = 0; index < pType->rgAirframeInfo.count(); index++) {
const APMAirframe* pInfo = pType->rgAirframeInfo.at(index);
Q_CHECK_PTR(pInfo);
airframeType->addAirframe(pInfo->name(), pInfo->params(), pInfo->type());
}
_airframeTypesModel->append(airframeType);
}
emit loadAirframesCompleted();
}
void APMAirframeComponentController::_finishVehicleSetup() {
QDir dataLocation = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation).at(0)
+ QDir::separator() + qApp->applicationName();
QFile parametersFile(dataLocation.absoluteFilePath(_currentAirframe->params()));
parametersFile.open(QIODevice::ReadOnly);
QTextStream reader(&parametersFile);
while (!reader.atEnd()) {
QString line = reader.readLine().trimmed();
if (line.isEmpty() || line.at(0) == QChar('#')) {
continue;
}
QStringList aux = line.split(',');
if (parameterExists(-1, aux.at(0))) {
Fact *param = getParameterFact(-1, aux.at(0));
param->setRawValue(QVariant::fromValue(aux.at(1)));
}
}
qgcApp()->setOverrideCursor(Qt::ArrowCursor);
sender()->deleteLater();
emit currentAirframeChanged(_currentAirframe);
}
APMAirframeType::APMAirframeType(const QString& name, const QString& imageResource, int type, QObject* parent) :
QObject(parent),
_name(name),
_imageResource(imageResource),
_type(type),
_dirty(false)
{
}
APMAirframeType::~APMAirframeType()
{
}
void APMAirframeType::addAirframe(const QString& name, const QString& file, int type)
{
APMAirframe* airframe = new APMAirframe(name, file, type);
Q_CHECK_PTR(airframe);
_airframes.append(QVariant::fromValue(airframe));
}
APMAirframe::APMAirframe(const QString& name, const QString& paramsFile, int type, QObject* parent) :
QObject(parent),
_name(name),
_paramsFile(paramsFile),
_type(type)
{
}
QString APMAirframe::name() const
{
return _name;
}
QString APMAirframe::params() const
{
return _paramsFile;
}
int APMAirframe::type() const
{
return _type;
}
APMAirframe::~APMAirframe()
{
}
QString APMAirframeType::imageResource() const
{
return _imageResource;
}
QString APMAirframeType::name() const
{
return _name;
}
int APMAirframeType::type() const
{
return _type;
}
APMAirframeType *APMAirframeComponentController::currentAirframeType() const
{
return _currentAirframeType;
}
APMAirframe *APMAirframeComponentController::currentAirframe() const
{
return _currentAirframe;
}
void APMAirframeComponentController::setCurrentAirframe(APMAirframe *t)
{
_currentAirframe = t;
qgcApp()->setOverrideCursor(Qt::WaitCursor);
APMRemoteParamsDownloader *paramDownloader = new APMRemoteParamsDownloader(_currentAirframe->params());
connect(paramDownloader, &APMRemoteParamsDownloader::finished, this, &APMAirframeComponentController::_finishVehicleSetup);
}
void APMAirframeComponentController::setCurrentAirframeType(APMAirframeType *t)
{
Fact *param = getParameterFact(-1, "FRAME");
Q_ASSERT(param);
param->setRawValue(t->type());
}
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009, 2015 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
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 <http://www.gnu.org/licenses/>.
======================================================================*/
/// @file
/// @author Don Gagne <don@thegagnes.com>
#ifndef APMAirframeComponentController_H
#define APMAirframeComponentController_H
#include <QObject>
#include <QQuickItem>
#include <QList>
#include <QAbstractListModel>
#include "UASInterface.h"
#include "AutoPilotPlugin.h"
#include "FactPanelController.h"
#include "APMAirframeComponentAirframes.h"
class APMAirframeModel;
class APMAirframeType;
/// MVC Controller for APMAirframeComponent.qml.
class APMAirframeComponentController : public FactPanelController
{
Q_OBJECT
public:
enum FrameId{FRAME_TYPE_PLUS = 0,
FRAME_TYPE_X = 1,
FRAME_TYPE_V = 2,
FRAME_TYPE_H = 3,
FRAME_TYPE_NEWY6 = 10};
Q_ENUM(FrameId)
APMAirframeComponentController(void);
~APMAirframeComponentController();
Q_PROPERTY(QmlObjectListModel* airframeTypesModel MEMBER _airframeTypesModel CONSTANT)
Q_PROPERTY(APMAirframeType* currentAirframeType READ currentAirframeType WRITE setCurrentAirframeType NOTIFY currentAirframeTypeChanged)
Q_PROPERTY(APMAirframe* currentAirframe READ currentAirframe WRITE setCurrentAirframe NOTIFY currentAirframeChanged)
int currentAirframeIndex(void);
void setCurrentAirframeIndex(int newIndex);
signals:
void loadAirframesCompleted();
void currentAirframeTypeChanged(APMAirframeType* airframeType);
void currentAirframeChanged(APMAirframe* airframe);
public slots:
APMAirframeType *currentAirframeType() const;
APMAirframe *currentAirframe() const;
void setCurrentAirframeType(APMAirframeType *t);
void setCurrentAirframe(APMAirframe *t);
private slots:
void _fillAirFrames(void);
void _finishVehicleSetup(void);
void _factFrameChanged(QVariant v);
private:
static bool _typesRegistered;
APMAirframeType *_currentAirframeType;
APMAirframe *_currentAirframe;
int _waitParamWriteSignalCount;
QmlObjectListModel *_airframeTypesModel;
};
class APMAirframe : public QObject
{
Q_OBJECT
public:
APMAirframe(const QString& name, const QString& paramsFile, int type, QObject* parent = NULL);
~APMAirframe();
Q_PROPERTY(QString name MEMBER _name CONSTANT)
Q_PROPERTY(int type MEMBER _type CONSTANT)
Q_PROPERTY(QString params MEMBER _paramsFile CONSTANT)
QString name() const;
QString params() const;
int type() const;
private:
QString _name;
QString _paramsFile;
int _type;
};
class APMAirframeType : public QObject
{
Q_OBJECT
public:
APMAirframeType(const QString& name, const QString& imageResource, int type, QObject* parent = NULL);
~APMAirframeType();
Q_PROPERTY(QString name MEMBER _name CONSTANT)
Q_PROPERTY(QString imageResource MEMBER _imageResource CONSTANT)
Q_PROPERTY(QVariantList airframes MEMBER _airframes CONSTANT)
Q_PROPERTY(int type MEMBER _type CONSTANT)
Q_PROPERTY(bool dirty MEMBER _dirty CONSTANT)
void addAirframe(const QString& name, const QString& paramsFile, int type);
QString name() const;
QString imageResource() const;
int type() const;
private:
QString _name;
QString _imageResource;
QVariantList _airframes;
int _type;
bool _dirty;
};
#endif
......@@ -13,33 +13,27 @@ FactPanel {
color: qgcPal.windowShadeDark
QGCPalette { id: qgcPal; colorGroupEnabled: enabled }
APMAirframeComponentController {
id: controller
factPanel: panel
}
/*
property Fact sysIdFact: controller.getParameterFact(-1, "MAV_SYS_ID")
property Fact sysAutoStartFact: controller.getParameterFact(-1, "SYS_AUTOSTART")
property Fact sysIdFact: controller.getParameterFact(-1, "FRAME")
property bool autoStartSet: sysAutoStartFact.value != 0
*/
Column {
anchors.fill: parent
anchors.margins: 8
/*
VehicleSummaryRow {
labelText: "System ID:"
valueText: sysIdFact.valueString
}
VehicleSummaryRow {
labelText: "Airframe type:"
valueText: autoStartSet ? controller.currentAirframeType : "Setup required"
}
id: nameRow;
labelText: "Frame Type:"
valueText: sysIdFact.valueString === "0" ? "Plus"
: sysIdFact.valueString === "1" ? "X"
: sysIdFact.valueString === "2" ? "V"
: sysIdFact.valueString == "3" ? "H"
:/* Fact.value == 10 */ "New Y6";
VehicleSummaryRow {
labelText: "Vehicle:"
valueText: autoStartSet ? controller.currentVehicleName : "Setup required"
}
*/
}
}
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009 - 2014 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
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 <http://www.gnu.org/licenses/>.
======================================================================*/
/// @file
/// @author Don Gagne <don@thegagnes.com>
#include "APMAirframeLoader.h"
#include "QGCApplication.h"
#include "QGCLoggingCategory.h"
#include "APMAirframeComponentAirframes.h"
#include <QFile>
#include <QFileInfo>
#include <QDir>
#include <QDebug>
QGC_LOGGING_CATEGORY(APMAirframeLoaderLog, "APMAirframeLoaderLog")
bool APMAirframeLoader::_airframeMetaDataLoaded = false;
APMAirframeLoader::APMAirframeLoader(AutoPilotPlugin* autopilot, UASInterface* uas, QObject* parent)
{
Q_UNUSED(autopilot);
Q_UNUSED(uas);
Q_UNUSED(parent);
Q_ASSERT(uas);
}
/// Load Airframe Fact meta data
void APMAirframeLoader::loadAirframeFactMetaData(void)
{
if (_airframeMetaDataLoaded) {
return;
}
qCDebug(APMAirframeLoaderLog) << "Loading APM airframe fact meta data";
Q_ASSERT(APMAirframeComponentAirframes::get().count() == 0);
QString airframeFilename = ":/AutoPilotPlugins/APM/AirframeFactMetaData.xml";
qCDebug(APMAirframeLoaderLog) << "Loading meta data file:" << airframeFilename;
QFile xmlFile(airframeFilename);
Q_ASSERT(xmlFile.exists());
bool success = xmlFile.open(QIODevice::ReadOnly);
Q_UNUSED(success);
Q_ASSERT(success);
QXmlStreamReader xml(xmlFile.readAll());
xmlFile.close();
if (xml.hasError()) {
qCWarning(APMAirframeLoaderLog) << "Badly formed XML" << xml.errorString();
return;
}
QString airframeGroup;
QString image;
int groupId = 0;
while (!xml.atEnd()) {
if (xml.isStartElement()) {
QString elementName = xml.name().toString();
QXmlStreamAttributes attr = xml.attributes();
if (elementName == "airframe_group") {
airframeGroup = attr.value("name").toString();
image = attr.value("image").toString();
groupId = attr.value("id").toInt();
APMAirframeComponentAirframes::insert(airframeGroup, groupId, image);
} else if (elementName == "airframe") {
QString name = attr.value("name").toString();
QString file = attr.value("file").toString();
APMAirframeComponentAirframes::insert(airframeGroup, groupId, image, name, file);
}
}
xml.readNext();
}
_airframeMetaDataLoaded = true;
}
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009 - 2015 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
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 <http://www.gnu.org/licenses/>.
======================================================================*/
#ifndef APMAirframeLoader_H
#define APMAirframeLoader_H
#include <QObject>
#include <QMap>
#include <QXmlStreamReader>
#include <QLoggingCategory>
#include "ParameterLoader.h"
#include "FactSystem.h"
#include "UASInterface.h"
#include "AutoPilotPlugin.h"
/// @file APMAirframeLoader.h
/// @author Lorenz Meier <lm@qgroundcontrol.org>
Q_DECLARE_LOGGING_CATEGORY(APMAirframeLoaderLog)
/// Collection of Parameter Facts for PX4 AutoPilot
class APMAirframeLoader : QObject
{
Q_OBJECT
public:
/// @param uas Uas which this set of facts is associated with
APMAirframeLoader(AutoPilotPlugin* autpilot,UASInterface* uas, QObject* parent = NULL);
static void loadAirframeFactMetaData(void);
private:
static bool _airframeMetaDataLoaded; ///< true: parameter meta data already loaded
static QMap<QString, FactMetaData*> _mapParameterName2FactMetaData; ///< Maps from a parameter name to FactMetaData
};
#endif // APMAirframeLoader_H
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009 - 2015 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
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 <http://www.gnu.org/licenses/>.
======================================================================*/
#include "APMAutoPilotPlugin.h"
......@@ -26,6 +26,18 @@
#include "UAS.h"
#include "FirmwarePlugin/APM/APMParameterMetaData.h" // FIXME: Hack
#include "FirmwarePlugin/APM/APMFirmwarePlugin.h" // FIXME: Hack
#include "FirmwarePlugin/APM/ArduCopterFirmwarePlugin.h"
#include "APMComponent.h"
#include "APMAirframeComponent.h"
#include "APMAirframeComponentAirframes.h"
#include "APMAirframeComponentController.h"
#include "APMAirframeLoader.h"
#include "APMRemoteParamsDownloader.h"
#include "APMFlightModesComponent.h"
#include "APMRadioComponent.h"
#include "APMSafetyComponent.h"
#include "APMTuningComponent.h"
#include "APMSensorsComponent.h"
/// This is the AutoPilotPlugin implementatin for the MAV_AUTOPILOT_ARDUPILOT type.
APMAutoPilotPlugin::APMAutoPilotPlugin(Vehicle* vehicle, QObject* parent)
......@@ -37,8 +49,9 @@ APMAutoPilotPlugin::APMAutoPilotPlugin(Vehicle* vehicle, QObject* parent)
, _safetyComponent(NULL)
, _sensorsComponent(NULL)
, _tuningComponent(NULL)
, _airframeFacts(new APMAirframeLoader(this, vehicle->uas(), this))
{
Q_ASSERT(vehicle);
APMAirframeLoader::loadAirframeFactMetaData();
}
APMAutoPilotPlugin::~APMAutoPilotPlugin()
......@@ -52,12 +65,14 @@ const QVariantList& APMAutoPilotPlugin::vehicleComponents(void)
Q_ASSERT(_vehicle);
if (parametersReady()) {
_airframeComponent = new APMAirframeComponent(_vehicle, this);
if (_airframeComponent) {
_airframeComponent->setupTriggerSignals();
_components.append(QVariant::fromValue((VehicleComponent*)_airframeComponent));
} else {
qWarning() << "new APMAirframeComponent failed";
if (dynamic_cast<ArduCopterFirmwarePlugin*>(_vehicle->firmwarePlugin())){
_airframeComponent = new APMAirframeComponent(_vehicle, this);
if(_airframeComponent) {
_airframeComponent->setupTriggerSignals();
_components.append(QVariant::fromValue((VehicleComponent*)_airframeComponent));
} else {
qWarning() << "new APMAirframeComponent failed";
}
}
_flightModesComponent = new APMFlightModesComponent(_vehicle, this);
......@@ -122,9 +137,9 @@ void APMAutoPilotPlugin::_parametersReadyPreChecks(bool missingParameters)
"Please perform a Firmware Upgrade if you wish to use Vehicle Setup.");
}
#endif
Q_UNUSED(missingParameters);
_parametersReady = true;
_missingParameters = missingParameters;
_missingParameters = false; // we apply only the parameters that do exists on the FactSystem.
emit missingParametersChanged(_missingParameters);
emit parametersReadyChanged(_parametersReady);
}
......@@ -26,12 +26,14 @@
#include "AutoPilotPlugin.h"
#include "Vehicle.h"
#include "APMAirframeComponent.h"
#include "APMFlightModesComponent.h"
#include "APMRadioComponent.h"
#include "APMSafetyComponent.h"
#include "APMSensorsComponent.h"
#include "APMTuningComponent.h"
class APMAirframeComponent;
class APMAirframeLoader;
class APMFlightModesComponent;
class APMRadioComponent;
class APMTuningComponent;
class APMSafetyComponent;
class APMSensorsComponent;
/// This is the APM specific implementation of the AutoPilot class.
class APMAutoPilotPlugin : public AutoPilotPlugin
......@@ -66,6 +68,7 @@ private:
APMSafetyComponent* _safetyComponent;
APMSensorsComponent* _sensorsComponent;
APMTuningComponent* _tuningComponent;
APMAirframeLoader* _airframeFacts;
};
#endif
......@@ -37,10 +37,8 @@ APMComponent::APMComponent(Vehicle* vehicle, AutoPilotPlugin* autopilot, QObject
void APMComponent::setupTriggerSignals(void)
{
// Watch for changed on trigger list params
foreach (QString paramName, setupCompleteChangedTriggerList()) {
foreach (const QString& paramName, setupCompleteChangedTriggerList()) {
Fact* fact = _autopilot->getParameterFact(FactSystem::defaultComponentId, paramName);
connect(fact, &Fact::valueChanged, this, &APMComponent::_triggerUpdated);
}
}
......
......@@ -23,6 +23,8 @@
#include "APMFlightModesComponent.h"
#include "APMAutoPilotPlugin.h"
#include "APMAirframeComponent.h"
#include "APMRadioComponent.h"
APMFlightModesComponent::APMFlightModesComponent(Vehicle* vehicle, AutoPilotPlugin* autopilot, QObject* parent) :
APMComponent(vehicle, autopilot, parent),
......
......@@ -23,6 +23,7 @@
#include "APMRadioComponent.h"
#include "APMAutoPilotPlugin.h"
#include "APMAirframeComponent.h"
APMRadioComponent::APMRadioComponent(Vehicle* vehicle, AutoPilotPlugin* autopilot, QObject* parent) :
APMComponent(vehicle, autopilot, parent),
......
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009 - 2014 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
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 <http://www.gnu.org/licenses/>.
======================================================================*/
#include "APMRemoteParamsDownloader.h"
#include <QMessageBox>
#include <QDesktopServices>
#include <QFile>
#include <QFileDialog>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QTimer>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QCryptographicHash>
#include <QApplication>
#include "APMRemoteParamsDownloader.h"
#define FRAME_PARAMS_LIST QUrl("https://api.github.com/repos/diydrones/ardupilot/contents/Tools/Frame_params")
#define FRAME_PARAMS_URL "https://raw.github.com/diydrones/ardupilot/master/Tools/Frame_params/"
static QString dataLocation;
APMRemoteParamsDownloader::APMRemoteParamsDownloader(const QString& file) :
m_fileToDownload(file),
m_networkReply(NULL),
m_downloadedParamFile(NULL)
{
dataLocation = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation).at(0)
+ QDir::separator() + qApp->applicationName();
refreshParamList();
}
QString APMRemoteParamsDownloader::statusText() const
{
return m_statusText;
}
void APMRemoteParamsDownloader::setStatusText(const QString& text)
{
m_statusText = text;
}
void APMRemoteParamsDownloader::refreshParamList()
{
setStatusText(tr("Refresh Param file list"));
QUrl url = FRAME_PARAMS_LIST;
m_networkReply->deleteLater();
m_networkReply = m_networkAccessManager.get(QNetworkRequest(url));
connect(m_networkReply, SIGNAL(finished()), this, SLOT(httpParamListFinished()));
connect(m_networkReply, SIGNAL(downloadProgress(qint64,qint64)),
this, SLOT(updateDataReadProgress(qint64,qint64)));
}
/* Returned Json Example
"_links": {
"git":"https://api.github.com/repos/diydrones/ardupilot/git/blobs/a7074e606d695566f9a8c87724ad52e5e3baba7d",
"html":"https://github.com/diydrones/ardupilot/blob/master/Tools/Frame_params/Parrot_Bebop.param",
"self":"https://api.github.com/repos/diydrones/ardupilot/contents/Tools/Frame_params/Parrot_Bebop.param?ref=master"
},
"download_url":"https://raw.githubusercontent.com/diydrones/ardupilot/master/Tools/Frame_params/Parrot_Bebop.param",
"git_url":"https://api.github.com/repos/diydrones/ardupilot/git/blobs/a7074e606d695566f9a8c87724ad52e5e3baba7d",
"html_url":"https://github.com/diydrones/ardupilot/blob/master/Tools/Frame_params/Parrot_Bebop.param",
"name":"Parrot_Bebop.param","path":"Tools/Frame_params/Parrot_Bebop.param",""
"sha":"a7074e606d695566f9a8c87724ad52e5e3baba7d",
"size":533,
"type":"file",
"url":"https://api.github.com/repos/diydrones/ardupilot/contents/Tools/Frame_params/Parrot_Bebop.param?ref=master"
*/
void APMRemoteParamsDownloader::startFileDownloadRequest()
{
QUrl url;
QJsonObject obj;
// Find the correct file from the json file list.
while(curr != end) {
obj = (*curr).toObject();
url = QUrl(obj["download_url"].toString());
QString name = obj["name"].toString();
if (name == m_fileToDownload) {
break;
}
curr++;
}
if (curr == end)
return;
QDir parameterDir(dataLocation);
if (!parameterDir.exists())
parameterDir.mkpath(dataLocation);
QString filename = parameterDir.absoluteFilePath(obj["name"].toString());
if(m_downloadedParamFile)
m_downloadedParamFile->deleteLater();
m_downloadedParamFile = new QFile(filename);
m_downloadedParamFile->open(QIODevice::WriteOnly);
m_networkReply = m_networkAccessManager.get(QNetworkRequest(url));
connect(m_networkReply, SIGNAL(finished()), this, SLOT(httpFinished()));
connect(m_networkReply, SIGNAL(readyRead()), this, SLOT(httpReadyRead()));
connect(m_networkReply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(updateDataReadProgress(qint64,qint64)));
curr++;
}
void APMRemoteParamsDownloader::httpFinished()
{
m_downloadedParamFile->flush();
m_downloadedParamFile->close();
if (m_networkReply->error()) {
m_downloadedParamFile->remove();
setStatusText(tr("Download failed: %1.").arg(m_networkReply->errorString()));
}
m_networkReply->deleteLater();
m_networkReply = NULL;
delete m_downloadedParamFile;
m_downloadedParamFile = NULL;
emit finished();
}
void APMRemoteParamsDownloader::httpReadyRead()
{
if (m_downloadedParamFile)
m_downloadedParamFile->write(m_networkReply->readAll());
}
void APMRemoteParamsDownloader::updateDataReadProgress(qint64 bytesRead, qint64 totalBytes)
{
Q_UNUSED(bytesRead);
Q_UNUSED(totalBytes);
}
void APMRemoteParamsDownloader::httpParamListFinished()
{
if (m_networkReply->error()) {
qDebug() << "Download failed:" << m_networkReply->errorString();
return;
}
processDownloadedVersionObject(m_networkReply->readAll());
startFileDownloadRequest();
}
void APMRemoteParamsDownloader::processDownloadedVersionObject(const QByteArray &listObject)
{
QJsonParseError jsonErrorChecker;
QJsonDocument jsonDocument = QJsonDocument::fromJson(listObject, &jsonErrorChecker);
if (jsonErrorChecker.error != QJsonParseError::NoError) {
qDebug() << "Json error while parsing document:" << jsonErrorChecker.errorString();
return;
}
m_documentArray = jsonDocument.array();
curr = m_documentArray.constBegin();
end = m_documentArray.constEnd();
}
#ifndef APMREMOTEPARAMSCONTROLLER_H
#define APMREMOTEPARAMSCONTROLLER_H
#include <QObject>
#include <QNetworkAccessManager>
#include <QUrl>
#include <QJsonArray>
class QNetworkReply;
class QFile;
class QUrl;
class APMRemoteParamsDownloader : public QObject
{
Q_OBJECT
Q_PROPERTY(QString statusText READ statusText)
public:
explicit APMRemoteParamsDownloader(const QString& file);
QString statusText() const;
public slots:
void refreshParamList();
void httpParamListFinished();
void httpFinished();
void httpReadyRead();
void updateDataReadProgress(qint64 bytesRead, qint64 totalBytes);
private:
void setStatusText(const QString& text);
void startFileDownloadRequest();
void manualListSetup();
void processDownloadedVersionObject(const QByteArray& listObject);
void startDownloadingRemoteParams();
signals:
void finished();
private:
QString m_fileToDownload;
QString m_statusText;
QNetworkAccessManager m_networkAccessManager;
QNetworkReply* m_networkReply;
QFile* m_downloadedParamFile;
// the list of needed documents.
QJsonArray m_documentArray;
QJsonArray::const_iterator curr;
QJsonArray::const_iterator end;
};
#endif // APMREMOTEPARAMSCONTROLLER_H
......@@ -27,6 +27,7 @@
#include "APMSafetyComponent.h"
#include "QGCQmlWidgetHolder.h"
#include "APMAutoPilotPlugin.h"
#include "APMAirframeComponent.h"
APMSafetyComponent::APMSafetyComponent(Vehicle* vehicle, AutoPilotPlugin* autopilot, QObject* parent)
: APMComponent(vehicle, autopilot, parent)
......
......@@ -24,6 +24,7 @@
#include "APMSensorsComponent.h"
#include "APMAutoPilotPlugin.h"
#include "APMSensorsComponentController.h"
#include "APMAirframeComponent.h"
// These two list must be kept in sync
......
......@@ -23,6 +23,7 @@
#include "APMTuningComponent.h"
#include "APMAutoPilotPlugin.h"
#include "APMAirframeComponent.h"
APMTuningComponent::APMTuningComponent(Vehicle* vehicle, AutoPilotPlugin* autopilot, QObject* parent)
: APMComponent(vehicle, autopilot, parent)
......
<?xml version='1.0' encoding='UTF-8'?>
<airframes>
<version>1</version>
<airframe_group image="AirframeQuadRotorPlus.png" name="Plus Style" id="0">
<airframe name="3DR Aero M" file="3DR_AERO_M.param"/>
<airframe name="3DR Aero RTF" file="3DR_Aero_RTF.param"/>
<airframe name="3DR Rover" file="3DR_Rover.param"/>
<airframe name="3DR Tarot" file="3DR_Tarot.bgsc"/>
<airframe name="Parrot Bebop" file="Parrot_Bebop.param"/>
<airframe name="Storm32" file="SToRM32-MAVLink.param"/>
</airframe_group>
<airframe_group image="AirframeQuadRotorX.png" name="X Style" id="1">
<airframe name="3DR X8-M RTF" file="3DR_X8-M_RTF.param"/>
<airframe name="3DR Y6A" file="3DR_Y6A_RTF.param"/>
<airframe name="3DR X8+ RTF" file="3DR_X8+_RTF.param"/>
<airframe name="3DR QUAD X4 RTF" file="3DR_QUAD_X4_RTF.param"/>
<airframe name="3DR X8" file="3DR_X8_RTF.param"/>
</airframe_group>
<airframe_group image="AirframeSimulation.png" name="V Style" id="2">
<airframe name="Iris with GoPro" file="Iris with Front Mount Go Pro.param"/>
<airframe name="Iris with Tarot" file="Iris with Tarot Gimbal.param"/>
<airframe name="3DR Iris+" file="3DR_Iris+.param"/>
<airframe name="Iris" file="Iris.param"/>
</airframe_group>
<airframe_group image="AirframeQuadRotorH.png" name="H Style" id="3">
</airframe_group>
<airframe_group image="AirframeHexaRotorX.png" name="Y6B" id="10">
<airframe name="3DR Y6B" file="3DR_Y6B_RTF.param"/>
</airframe_group>
<airframes>
This diff is collapsed.
......@@ -77,6 +77,7 @@
#include "APM/ArduCopterFirmwarePlugin.h"
#include "APM/ArduPlaneFirmwarePlugin.h"
#include "APM/ArduRoverFirmwarePlugin.h"
#include "APM/APMAirframeComponentController.h"
#include "PX4/PX4FirmwarePlugin.h"
#include "Vehicle.h"
#include "MavlinkQmlSingleton.h"
......@@ -378,9 +379,10 @@ void QGCApplication::_initCommon(void)
qmlRegisterUncreatableType<JoystickManager> ("QGroundControl.JoystickManager", 1, 0, "JoystickManager", "Reference only");
qmlRegisterUncreatableType<Joystick> ("QGroundControl.JoystickManager", 1, 0, "Joystick", "Reference only");
qmlRegisterType<ParameterEditorController> ("QGroundControl.Controllers", 1, 0, "ParameterEditorController");
qmlRegisterType<ParameterEditorController> ("QGroundControl.Controllers", 1, 0, "ParameterEditorController");
qmlRegisterType<APMFlightModesComponentController> ("QGroundControl.Controllers", 1, 0, "APMFlightModesComponentController");
qmlRegisterType<FlightModesComponentController> ("QGroundControl.Controllers", 1, 0, "FlightModesComponentController");
qmlRegisterType<APMAirframeComponentController> ("QGroundControl.Controllers", 1, 0, "APMAirframeComponentController");
qmlRegisterType<AirframeComponentController> ("QGroundControl.Controllers", 1, 0, "AirframeComponentController");
qmlRegisterType<APMSensorsComponentController> ("QGroundControl.Controllers", 1, 0, "APMSensorsComponentController");
qmlRegisterType<SensorsComponentController> ("QGroundControl.Controllers", 1, 0, "SensorsComponentController");
......
......@@ -96,7 +96,7 @@ FactPanel {
__rejectButton.text = "Cancel"
__rejectButton.visible = true
} else if (buttons & StandardButton.Close) {
__rejectButton.text = "Cancel"
__rejectButton.text = "Close"
__rejectButton.visible = true
} else if (buttons & StandardButton.No) {
__rejectButton.text = "No"
......
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