22
33Ground Control Station for MAVLink-enabled UAVs supporting PX4 and ArduPilot.
44
5+ ## Quick Start for AI Assistants
6+
7+ ** First-time here?** Start with these critical files:
8+ 1 . ** This file** - Architecture patterns and coding standards
9+ 2 . ` .github/CONTRIBUTING.md ` - Contribution workflow
10+ 3 . ` src/FactSystem/Fact.h ` - The most important pattern in QGC
11+ 4 . ` src/Vehicle/Vehicle.h ` - Core vehicle model
12+
13+ ** Most Critical Pattern** : The ** Fact System** handles ALL vehicle parameters. Never create custom parameter storage - always use Facts.
14+
15+ ** Golden Rule** : Multi-vehicle support means ALWAYS null-check ` MultiVehicleManager::instance()->activeVehicle() ` .
16+
517## Tech Stack
618- ** C++20** with ** Qt 6.10.0** (QtQml, QtQuick)
719- ** Build** : CMake 3.25+, Ninja
@@ -146,6 +158,13 @@ void method(Vehicle* vehicle) {
146158- Do NOT create verbose file headers or unnecessary documentation files
147159- Do NOT add README files unless explicitly requested
148160
161+ **Security & Dependencies:**
162+ - Never commit secrets, API keys, or credentials
163+ - Validate all external inputs (MAVLink messages, file uploads, user input)
164+ - Use Qt's built-in sanitization for SQL and string operations
165+ - When adding dependencies, check for known vulnerabilities
166+ - Prefer Qt's built-in functionality over external libraries
167+
149168## Common Pitfalls (DO NOT!)
150169
1511701. ❌ Assume single vehicle - Always null-check `activeVehicle()`
@@ -173,6 +192,55 @@ cmake --build build --config Debug
173192- ` QGC_ENABLE_BLUETOOTH ` - Bluetooth support
174193- ` QGC_DISABLE_APM_PLUGIN ` / ` QGC_DISABLE_PX4_PLUGIN `
175194
195+ ## Testing
196+
197+ ### Running Tests
198+ ``` bash
199+ # Run all unit tests
200+ ./build/Debug/QGroundControl --unittest
201+
202+ # Run specific test
203+ ./build/Debug/QGroundControl --unittest:< TestClassName>
204+
205+ # Run with verbose output
206+ ./build/Debug/QGroundControl --unittest --logging:full
207+ ```
208+
209+ ### Test Structure
210+ - Tests mirror ` src/ ` structure in ` test/ ` directory
211+ - Use ` UnitTest ` base class from Qt Test framework
212+ - Mock vehicle connections when testing vehicle-dependent code
213+ - Always test with null vehicle checks
214+
215+ ### Adding New Tests
216+ ``` cpp
217+ class MyComponentTest : public UnitTest {
218+ Q_OBJECT
219+ private slots:
220+ void init(); // Called before each test
221+ void cleanup(); // Called after each test
222+ void testMyFunction();
223+ };
224+ ```
225+
226+ ## Troubleshooting
227+
228+ ### Build Issues
229+ - **Qt not found**: Set `CMAKE_PREFIX_PATH` to Qt installation, or use qt-cmake
230+ - **Submodule errors**: Run `git submodule update --init --recursive`
231+ - **Missing dependencies**: Check platform-specific build instructions at https://dev.qgroundcontrol.com/
232+ - **CMake cache issues**: Delete `build/` directory and reconfigure
233+
234+ ### Runtime Issues
235+ - **Crash on startup**: Check log files in `~/.local/share/QGroundControl/` (Linux/macOS) or `%LOCALAPPDATA%\QGroundControl` (Windows)
236+ - **Vehicle not connecting**: Verify MAVLink protocol compatibility, check link configuration
237+ - **Parameter load failures**: Ensure `parametersReady` signal before accessing Facts
238+
239+ ### Development Environment
240+ - **Qt Creator recommended**: Import CMakeLists.txt as project
241+ - **clangd for VSCode**: Uses `.clangd` config in repo root
242+ - **Pre-commit hooks**: Run `pre-commit install` to enable automatic formatting
243+
176244## Performance Tips
177245- Batch updates: `fact->setSendValueChangedSignals(false)`
178246- Suppress live updates: `factGroup->setLiveUpdates(false)`
@@ -182,19 +250,116 @@ cmake --build build --config Debug
182250
183251## Common Tasks
184252
185- ** Add parameter:**
186- 1 . Access via ` vehicle->parameterManager()->getParameter() `
187- 2 . Validate with ` fact->validate() ` before setting
188- 3 . Listen to ` valueChanged() ` signal
253+ ### Working with Vehicle Parameters
254+ ```cpp
255+ // 1. Get parameter (always null-check!)
256+ Vehicle* vehicle = MultiVehicleManager::instance()->activeVehicle();
257+ if (!vehicle) return;
258+
259+ Fact* param = vehicle->parameterManager()->getParameter(-1, "PARAM_NAME");
260+ if (!param) {
261+ qCWarning(Log) << "Parameter not found";
262+ return;
263+ }
264+
265+ // 2. Validate before setting
266+ QString error = param->validate(newValue, false);
267+ if (!error.isEmpty()) {
268+ qCWarning(Log) << "Invalid value:" << error;
269+ return;
270+ }
271+
272+ // 3. Set value (cookedValue for UI with units)
273+ param->setCookedValue(newValue);
189274
190- ** Create settings group:**
191- 1 . Subclass ` SettingsGroup ` , use ` DEFINE_SETTINGFACT `
192- 2 . Create ` *.SettingsGroup.json ` metadata
193- 3 . Access via ` SettingsManager::instance() `
275+ // 4. Listen to changes
276+ connect(param, &Fact::valueChanged, this, [](QVariant value) {
277+ qCDebug(Log) << "Parameter changed:" << value;
278+ });
279+ ```
280+
281+ ### Creating a Settings Group
282+ ``` cpp
283+ // 1. Define in MySettings.h
284+ class MySettings : public SettingsGroup {
285+ Q_OBJECT
286+ public:
287+ DEFINE_SETTINGFACT(mySetting) // Creates Fact with JSON metadata
288+ };
289+
290+ // 2. Create MySettings.SettingsGroup.json with metadata
291+ {
292+ "mySetting": {
293+ "shortDescription": "My setting",
294+ "type": "uint32",
295+ "default": 100,
296+ "min": 0,
297+ "max": 1000
298+ }
299+ }
300+
301+ // 3. Access anywhere
302+ int value = SettingsManager::instance()->mySettings()->mySetting()->rawValue().toInt();
303+ ```
304+
305+ ### Adding a Vehicle Component
306+ ```cpp
307+ // 1. Create MyComponent.h (subclass VehicleComponent)
308+ class MyComponent : public VehicleComponent {
309+ Q_OBJECT
310+ public:
311+ MyComponent(Vehicle* vehicle, AutoPilotPlugin* autopilot, QObject* parent = nullptr);
312+
313+ QString name() const override { return "My Component"; }
314+ QString description() const override { return "Component description"; }
315+ QString iconResource() const override { return "/qmlimages/MyComponentIcon.svg"; }
316+ bool requiresSetup() const override { return true; }
317+ bool setupComplete() const override { return _setupComplete; }
318+ QUrl setupSource() const override { return QUrl::fromUserInput("qrc:/qml/MyComponentSetup.qml"); }
319+ };
320+
321+ // 2. Register in AutoPilotPlugin::getVehicleComponents()
322+ ```
323+
324+ ### Handling MAVLink Messages
325+ ``` cpp
326+ // In a FactGroup or custom component
327+ void MyFactGroup::handleMessage (Vehicle* vehicle, mavlink_message_t& message) {
328+ switch (message.msgid) {
329+ case MAVLINK_MSG_ID_MY_MESSAGE: {
330+ mavlink_my_message_t msg;
331+ mavlink_msg_my_message_decode(&message, &msg);
332+
333+ // Update Facts (thread-safe via Qt signals)
334+ myFact()->setRawValue(msg.value);
335+ break;
336+ }
337+ }
338+ }
339+ ```
194340
195- ** Add vehicle component:**
196- 1 . Subclass ` VehicleComponent ` , implement virtuals
197- 2 . Create QML UI, register in AutoPilotPlugin
341+ ### Adding a QML UI Component
342+ ```qml
343+ // 1. Create MyControl.qml
344+ import QtQuick
345+ import QGroundControl
346+ import QGroundControl.Controls
347+
348+ QGCButton {
349+ text: "My Action"
350+
351+ property var vehicle: QGroundControl.multiVehicleManager.activeVehicle
352+
353+ enabled: vehicle && vehicle.armed
354+
355+ onClicked: {
356+ if (vehicle) {
357+ // Always null-check vehicle!
358+ vehicle.sendMavCommand(...)
359+ }
360+ }
361+ }
362+ ```
198363
199364## Essential Files to Read
2003651 . ` .github/CONTRIBUTING.md ` - Contribution guidelines
0 commit comments