Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/en/qgc-user-guide/getting_started/whats_new.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ Multiple fixed wing landing sequences can now be planned for landings at differe

Vehicle Configuration has been renamed from Vehicle Setup and is now intended mainly for initial vehicle design and configuration, not changes between flights.

### Parameter Editor — Mission Planner File Import

The parameter diff dialog can now load Mission Planner `.param` files (whitespace-separated 5-column format) in addition to the existing QGC `.params` format.
Select a `.param` file using the new **Mission Planner Files (*.param)** filter in the load dialog; differences from the vehicle's current values are shown as usual before any changes are applied.

### ArduPilot — Servo Outputs

A new **Servo Outputs** page provides real-time visualization and configuration of servo outputs for ArduPilot vehicles:
Expand Down
56 changes: 0 additions & 56 deletions src/FactSystem/ParameterManager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1200,62 +1200,6 @@ void ParameterManager::_tryCacheHashLoad(int vehicleId, int componentId, const Q
}
}

QString ParameterManager::readParametersFromStream(QTextStream &stream)
{
QString missingErrors;
QString typeErrors;

while (!stream.atEnd()) {
const QString line = stream.readLine();
if (!line.startsWith("#")) {
const QStringList wpParams = line.split("\t");
const int lineMavId = wpParams.at(0).toInt();
if (wpParams.size() == 5) {
if (_vehicle->id() != lineMavId) {
return QStringLiteral("The parameters in the stream have been saved from System Id %1, but the current vehicle has the System Id %2.").arg(lineMavId).arg(_vehicle->id());
}

const int componentId = wpParams.at(1).toInt();
const QString paramName = wpParams.at(2);
const QString valStr = wpParams.at(3);
const uint mavType = wpParams.at(4).toUInt();

if (!parameterExists(componentId, paramName)) {
QString error;
error += QStringLiteral("%1:%2").arg(componentId).arg(paramName);
missingErrors += error;
qCDebug(ParameterManagerLog) << "Skipped due to missing:" << error;
continue;
}

Fact *const fact = getParameter(componentId, paramName);
if (fact->type() != mavTypeToFactType(static_cast<MAV_PARAM_TYPE>(mavType))) {
QString error;
error = QStringLiteral("%1:%2 ").arg(componentId).arg(paramName);
typeErrors += error;
qCDebug(ParameterManagerLog) << "Skipped due to type mismatch: %1" << error;
continue;
}

qCDebug(ParameterManagerLog) << "Updating parameter" << componentId << paramName << valStr;
fact->setRawValue(valStr);
}
}
}

QString errors;

if (!missingErrors.isEmpty()) {
errors = tr("Parameters not loaded since they are not currently on the vehicle: %1\n").arg(missingErrors);
}

if (!typeErrors.isEmpty()) {
errors += tr("Parameters not loaded due to type mismatch: %1").arg(typeErrors);
}

return errors;
}

void ParameterManager::writeParametersToStream(QTextStream &stream) const
{
stream << "# Onboard parameters for Vehicle " << _vehicle->id() << "\n";
Expand Down
3 changes: 0 additions & 3 deletions src/FactSystem/ParameterManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,6 @@ class ParameterManager : public QObject
/// @param name: Parameter name
Fact *getParameter(int componentId, const QString &paramName);

/// Returns error messages from loading
QString readParametersFromStream(QTextStream &stream);

void writeParametersToStream(QTextStream &stream) const;

bool pendingWrites() const;
Expand Down
2 changes: 1 addition & 1 deletion src/QmlControls/ParameterEditor.qml
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ Item {
QGCFileDialog {
id: fileDialog
folder: _appSettings.parameterSavePath
nameFilters: [ qsTr("Parameter Files (*.%1)").arg(_appSettings.parameterFileExtension) , qsTr("All Files (*)") ]
nameFilters: [ qsTr("Parameter Files (*.%1)").arg(_appSettings.parameterFileExtension), qsTr("Mission Planner Files (*.param)"), qsTr("All Files (*)") ]

onAcceptedForSave: (file) => {
controller.saveToFile(file)
Expand Down
11 changes: 9 additions & 2 deletions src/QmlControls/ParameterEditorController.cc
Original file line number Diff line number Diff line change
Expand Up @@ -397,12 +397,14 @@ bool ParameterEditorController::buildDiffFromFile(const QString& filename)

QTextStream stream(&file);

int parsedLineCount = 0;
int firstComponentId = -1;
while (!stream.atEnd()) {
QString line = stream.readLine();
if (!line.startsWith("#")) {
QStringList wpParams = line.split("\t");
if (!line.startsWith("#") && !line.trimmed().isEmpty()) {
QStringList wpParams = line.trimmed().split(QRegularExpression("[\\t ]+"));
if (wpParams.size() == 5) {
parsedLineCount++;
int vehicleId = wpParams.at(0).toInt();
int componentId = wpParams.at(1).toInt();
QString paramName = wpParams.at(2);
Expand Down Expand Up @@ -467,6 +469,11 @@ bool ParameterEditorController::buildDiffFromFile(const QString& filename)

file.close();

if (parsedLineCount == 0) {
QGC::showAppMessage(tr("No valid parameters found in file. Check that the file is in QGC or Mission Planner format."));
return false;
}

emit diffOtherVehicleChanged(_diffOtherVehicle);
emit diffMultipleComponentsChanged(_diffMultipleComponents);

Expand Down
1 change: 1 addition & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ add_qgc_test(FactSystemTestPX4 LABELS Integration Vehicle RESOURCE_LOCK MockLink
add_qgc_test(FactTest LABELS Unit)
add_qgc_test(FactValueSliderListModelTest LABELS Unit)
add_qgc_test(HashCheckTest LABELS Integration Vehicle SERIAL TIMEOUT ${QGC_TEST_TIMEOUT_EXTENDED})
add_qgc_test(ParameterEditorControllerTest LABELS Integration Vehicle RESOURCE_LOCK MockLink)
add_qgc_test(ParameterManagerTest LABELS Integration Vehicle SERIAL TIMEOUT ${QGC_TEST_TIMEOUT_EXTENDED})

# ----------------------------------------------------------------------------
Expand Down
2 changes: 2 additions & 0 deletions test/FactSystem/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ target_sources(${CMAKE_PROJECT_NAME}
FactValueSliderListModelTest.h
HashCheckTest.cc
HashCheckTest.h
ParameterEditorControllerTest.cc
ParameterEditorControllerTest.h
ParameterManagerTest.cc
ParameterManagerTest.h
ParameterMetaDataTestHelper.h
Expand Down
146 changes: 146 additions & 0 deletions test/FactSystem/ParameterEditorControllerTest.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
#include "ParameterEditorControllerTest.h"

#include <QtCore/QString>
#include <QtCore/QRegularExpression>
#include <QtTest/QTest>

#include "ParameterEditorController.h"
#include "QmlObjectListModel.h"
#include "Fixtures/RAIIFixtures.h"
#include "UnitTest.h"

UT_REGISTER_TEST(ParameterEditorControllerTest, TestLabel::Integration, TestLabel::Vehicle)

// QGC tab-delimited file with SYS_AUTOSTART changed from 4001 → 4002
static const char* kQGCParamsWithDiff =
"# Onboard parameters for Vehicle 1\n"
"#\n"
"# Vehicle-Id Component-Id Name Value Type\n"
"1\t1\tSYS_AUTOSTART\t4002\t6\n";

// QGC tab-delimited file with SYS_AUTOSTART matching vehicle default (4001)
static const char* kQGCParamsNoDiff =
"# Onboard parameters for Vehicle 1\n"
"#\n"
"# Vehicle-Id Component-Id Name Value Type\n"
"1\t1\tSYS_AUTOSTART\t4001\t6\n";

// Mission Planner space-delimited file with same diff
static const char* kMPParamsWithDiff =
"# Mission Planner parameter file\n"
"1 1 SYS_AUTOSTART 4002 6\n";

// Mission Planner space-delimited file matching vehicle default
static const char* kMPParamsNoDiff =
"# Mission Planner parameter file\n"
"1 1 SYS_AUTOSTART 4001 6\n";

// Garbage — no parseable lines
static const char* kBadFormatParams =
"This is not a parameter file\n"
"random text here\n";

// QGC file with a parameter that does not exist on the (PX4 mock) vehicle
static const char* kQGCParamsUnknownParam =
"# Onboard parameters for Vehicle 1\n"
"#\n"
"# Vehicle-Id Component-Id Name Value Type\n"
"1\t1\tNONEXISTENT_PARAM_XYZ\t1\t6\n";

void ParameterEditorControllerTest::_buildDiffQGCFormat()
{
TestFixtures::TempFileFixture tempFile(QStringLiteral("test_XXXXXX.params"));
QVERIFY(tempFile.isValid());
QVERIFY(tempFile.write(QByteArray(kQGCParamsWithDiff)));
QVERIFY(tempFile.file()->flush());

ParameterEditorController controller;
const bool result = controller.buildDiffFromFile(tempFile.path());

QVERIFY(result);
QCOMPARE(controller.diffList()->count(), 1);
const auto* diff = controller.diffList()->value<ParameterEditorDiff*>(0);
QVERIFY(diff);
QCOMPARE(diff->name, QStringLiteral("SYS_AUTOSTART"));
}

void ParameterEditorControllerTest::_buildDiffMPFormat()
{
TestFixtures::TempFileFixture tempFile(QStringLiteral("test_XXXXXX.param"));
QVERIFY(tempFile.isValid());
QVERIFY(tempFile.write(QByteArray(kMPParamsWithDiff)));
QVERIFY(tempFile.file()->flush());

ParameterEditorController controller;
const bool result = controller.buildDiffFromFile(tempFile.path());

QVERIFY(result);
QCOMPARE(controller.diffList()->count(), 1);
const auto* diff = controller.diffList()->value<ParameterEditorDiff*>(0);
QVERIFY(diff);
QCOMPARE(diff->name, QStringLiteral("SYS_AUTOSTART"));
}

void ParameterEditorControllerTest::_buildDiffNoDifferencesQGC()
{
TestFixtures::TempFileFixture tempFile(QStringLiteral("test_XXXXXX.params"));
QVERIFY(tempFile.isValid());
QVERIFY(tempFile.write(QByteArray(kQGCParamsNoDiff)));
QVERIFY(tempFile.file()->flush());

ParameterEditorController controller;
const bool result = controller.buildDiffFromFile(tempFile.path());

// parsedLineCount > 0 so returns true, but no diffs found
QVERIFY(result);
QCOMPARE(controller.diffList()->count(), 0);
}

void ParameterEditorControllerTest::_buildDiffNoDifferencesMP()
{
TestFixtures::TempFileFixture tempFile(QStringLiteral("test_XXXXXX.param"));
QVERIFY(tempFile.isValid());
QVERIFY(tempFile.write(QByteArray(kMPParamsNoDiff)));
QVERIFY(tempFile.file()->flush());

ParameterEditorController controller;
const bool result = controller.buildDiffFromFile(tempFile.path());

QVERIFY(result);
QCOMPARE(controller.diffList()->count(), 0);
}

void ParameterEditorControllerTest::_buildDiffBadFormat()
{
TestFixtures::TempFileFixture tempFile(QStringLiteral("test_XXXXXX.txt"));
QVERIFY(tempFile.isValid());
QVERIFY(tempFile.write(QByteArray(kBadFormatParams)));
QVERIFY(tempFile.file()->flush());

ParameterEditorController controller;
expectAppMessage(QRegularExpression("No valid parameters found"));
const bool result = controller.buildDiffFromFile(tempFile.path());

// parsedLineCount == 0 → returns false
QVERIFY(!result);
QCOMPARE(controller.diffList()->count(), 0);
}

void ParameterEditorControllerTest::_buildDiffMissingOnVehicle()
{
TestFixtures::TempFileFixture tempFile(QStringLiteral("test_XXXXXX.params"));
QVERIFY(tempFile.isValid());
QVERIFY(tempFile.write(QByteArray(kQGCParamsUnknownParam)));
QVERIFY(tempFile.file()->flush());

ParameterEditorController controller;
const bool result = controller.buildDiffFromFile(tempFile.path());

// Line was parsed (parsedLineCount > 0) → returns true
QVERIFY(result);
QCOMPARE(controller.diffList()->count(), 1);
const auto* diff = controller.diffList()->value<ParameterEditorDiff*>(0);
QVERIFY(diff);
QVERIFY(diff->noVehicleValue);
QCOMPARE(diff->name, QStringLiteral("NONEXISTENT_PARAM_XYZ"));
}
16 changes: 16 additions & 0 deletions test/FactSystem/ParameterEditorControllerTest.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#pragma once

#include "BaseClasses/ParameterTest.h"

class ParameterEditorControllerTest : public ParameterTest
{
Q_OBJECT

private slots:
void _buildDiffQGCFormat();
void _buildDiffMPFormat();
void _buildDiffNoDifferencesQGC();
void _buildDiffNoDifferencesMP();
void _buildDiffBadFormat();
void _buildDiffMissingOnVehicle();
};
Loading