QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
VehicleComponent.cc
Go to the documentation of this file.
1#include "VehicleComponent.h"
2#include "Fact.h"
3#include "ParameterManager.h"
5#include "Vehicle.h"
6
7#include <QtCore/QFile>
8#include <QtCore/QJsonArray>
9#include <QtCore/QJsonDocument>
10#include <QtCore/QJsonObject>
11#include <QtQml/QQmlContext>
12#include <QtQuick/QQuickItem>
13
14QGC_LOGGING_CATEGORY(VehicleComponentLog, "AutoPilotPlugins.VehicleComponent");
15
16VehicleComponent::VehicleComponent(Vehicle *vehicle, AutoPilotPlugin *autopilot, AutoPilotPlugin::KnownVehicleComponent KnownVehicleComponent, QObject *parent)
17 : QObject(parent)
18 , _vehicle(vehicle)
19 , _autopilot(autopilot)
20 , _KnownVehicleComponent(KnownVehicleComponent)
21{
22 // qCDebug(VehicleComponentLog) << Q_FUNC_INFO << this;
23
24 if (!vehicle || !autopilot) {
25 qCWarning(VehicleComponentLog) << "Internal error";
26 }
27}
28
30{
31 // qCDebug(VehicleComponentLog) << Q_FUNC_INFO << this;
32}
33
34QStringList VehicleComponent::sections() const
35{
36 _ensureSectionsCached();
37
38 if (_repeatFilters.isEmpty()) {
39 return _expandedSections;
40 }
41
42 auto *pm = _vehicle ? _vehicle->parameterManager() : nullptr;
43 if (!pm) {
44 return _expandedSections;
45 }
46
47 QStringList result = _expandedSections;
48 for (const auto &filter : _repeatFilters) {
49 bool hasDisabled = false;
50 for (int i = 0; i < filter.sectionNames.size(); i++) {
51 if (!pm->parameterExists(ParameterManager::defaultComponentId, filter.paramNames[i]))
52 continue;
53 if (pm->getParameter(ParameterManager::defaultComponentId, filter.paramNames[i])->rawValue().toInt() == filter.disabledValue) {
54 result.removeAll(filter.sectionNames[i]);
55 hasDisabled = true;
56 }
57 }
58 if (hasDisabled && !filter.disabledHeading.isEmpty()) {
59 result.append(filter.disabledHeading);
60 }
61 }
62
63 return result;
64}
65
67{
68 _ensureSectionsCached();
69 QVariantMap map;
70 for (auto it = _sectionKeywords.constBegin(); it != _sectionKeywords.constEnd(); ++it) {
71 map.insert(it.key(), it.value());
72 }
73 return map;
74}
75
76void VehicleComponent::_ensureSectionsCached() const
77{
78 if (_sectionsCached) {
79 return;
80 }
81 _sectionsCached = true;
82
83 const QString path = vehicleConfigJson();
84 if (path.isEmpty()) {
85 return;
86 }
87
88 QFile file(path);
89 if (!file.open(QIODevice::ReadOnly)) {
90 qCWarning(VehicleComponentLog) << "Failed to open page definition:" << path;
91 return;
92 }
93
94 QJsonParseError parseError;
95 const QJsonDocument doc = QJsonDocument::fromJson(file.readAll(), &parseError);
96 if (parseError.error != QJsonParseError::NoError || !doc.isObject()) {
97 qCWarning(VehicleComponentLog) << "Failed to parse page definition:" << path << parseError.errorString();
98 return;
99 }
100 const QJsonObject root = doc.object();
101 const QJsonObject constants = root.value("constants").toObject();
102 const QJsonArray sectionsArray = root.value("sections").toArray();
103
104 auto resolveConstantInt = [&](const QString &ref) -> int {
105 if (constants.contains(ref)) {
106 const QJsonValue v = constants.value(ref);
107 return v.isDouble() ? v.toInt() : v.toString().toInt();
108 }
109 // disabledParamValue must reference a constant or be a numeric literal
110 return ref.toInt();
111 };
112
113 for (const QJsonValue &val : sectionsArray) {
114 const QJsonObject secObj = val.toObject();
115 const QString name = secObj.value("title").toString();
116 if (name.isEmpty()) {
117 continue;
118 }
119
120 // Collect translatable search terms: title + explicit keywords + control labels
121 // Stored in original case so QML can pass them through qsTranslate() at search time
122 QStringList terms;
123 terms.append(name);
124 const QJsonArray kwArray = secObj.value("keywords").toArray();
125 for (const QJsonValue &kw : kwArray) {
126 terms.append(kw.toString());
127 }
128 for (const QJsonValue &ctrl : secObj.value("controls").toArray()) {
129 const QString label = ctrl.toObject().value("label").toString();
130 if (!label.isEmpty()) {
131 terms.append(label);
132 }
133 }
134 terms.removeDuplicates();
135
136 const QJsonObject repeatObj = secObj.value("repeat").toObject();
137 if (!repeatObj.isEmpty() && _vehicle && _vehicle->parameterManager()) {
138 const QString paramPrefix = repeatObj.value("paramPrefix").toString();
139 const QString probePostfix = repeatObj.value("probePostfix").toString();
140 const QString indexing = repeatObj.value("indexing").toString();
141 const int startIndex = repeatObj.value("startIndex").toInt(1);
142 const bool firstOmits = repeatObj.value("firstIndexOmitsNumber").toBool(false);
143
144 // Check for enableParam filtering
145 const QString enableParam = repeatObj.value("enableParam").toString();
146 const QString disabledParamValueRef = repeatObj.value("disabledParamValue").toString();
147 const QJsonObject disabledSectionObj = repeatObj.value("disabledSection").toObject();
148 const bool hasFilter = !enableParam.isEmpty() && !disabledParamValueRef.isEmpty();
149
150 RepeatFilter filter;
151 if (hasFilter) {
152 filter.disabledValue = resolveConstantInt(disabledParamValueRef);
153 filter.disabledHeading = disabledSectionObj.value("heading").toString();
154 }
155
156 int count = 0;
157 if (indexing == QStringLiteral("apm_battery")) {
158 auto battPrefix = [&](int i) -> QString {
159 if (i == 0) return paramPrefix + QStringLiteral("_");
160 if (i <= 8) return paramPrefix + QString::number(i + 1) + QStringLiteral("_");
161 return paramPrefix + QChar('A' + i - 9) + QStringLiteral("_");
162 };
163 auto battLabel = [](int i) -> QString {
164 if (i <= 8) return QString::number(i + 1);
165 return QString(QChar('A' + i - 9));
166 };
167 for (int i = 0; i < 16; i++) {
168 const QString probeParam = battPrefix(i) + probePostfix;
170 break;
171 count++;
172 }
173 if (count <= 1) {
174 _expandedSections.append(name);
175 _sectionKeywords.insert(name, terms);
176 if (hasFilter) {
177 filter.sectionNames.append(name);
178 filter.paramNames.append(battPrefix(0) + enableParam);
179 }
180 } else {
181 for (int i = 0; i < count; i++) {
182 const QString sectionName = name + QStringLiteral(" ") + battLabel(i);
183 _expandedSections.append(sectionName);
184 _sectionKeywords.insert(sectionName, terms);
185 if (hasFilter) {
186 filter.sectionNames.append(sectionName);
187 filter.paramNames.append(battPrefix(i) + enableParam);
188 }
189 }
190 }
191 } else {
192 for (int i = startIndex; ; i++) {
193 const QString idx = (firstOmits && i == startIndex) ? QString() : QString::number(i);
194 const QString probeParam = paramPrefix + idx + probePostfix;
196 break;
197 count++;
198 }
199
200 if (count <= 1) {
201 _expandedSections.append(name);
202 _sectionKeywords.insert(name, terms);
203 if (hasFilter) {
204 const QString idx = (firstOmits) ? QString() : QString::number(startIndex);
205 filter.sectionNames.append(name);
206 filter.paramNames.append(paramPrefix + idx + enableParam);
207 }
208 } else {
209 for (int i = 0; i < count; i++) {
210 const QString idx = (firstOmits && i == 0) ? QString() : QString::number(startIndex + i);
211 const QString sectionName = name + QStringLiteral(" ") + QString::number(startIndex + i);
212 _expandedSections.append(sectionName);
213 _sectionKeywords.insert(sectionName, terms);
214 if (hasFilter) {
215 filter.sectionNames.append(sectionName);
216 filter.paramNames.append(paramPrefix + idx + enableParam);
217 }
218 }
219 }
220 }
221
222 if (hasFilter) {
223 _repeatFilters.append(filter);
224 }
225 } else {
226 if (!_expandedSections.contains(name)) {
227 _expandedSections.append(name);
228 }
229 _sectionKeywords.insert(name, terms);
230 }
231 }
232}
233
234void VehicleComponent::addSummaryQmlComponent(QQmlContext *context, QQuickItem *parent)
235{
236 if (!context) {
237 qCWarning(VehicleComponentLog) << "Internal error";
238 return;
239 }
240
241 QQmlComponent component = new QQmlComponent(context->engine(), QUrl::fromUserInput("qrc:/qml/VehicleComponentSummaryButton.qml"), this);
242 if (component.status() == QQmlComponent::Error) {
243 qCWarning(VehicleComponentLog) << component.errors();
244 return;
245 }
246
247 QQuickItem *const item = qobject_cast<QQuickItem*>(component.create(context));
248 if (!item) {
249 qCWarning(VehicleComponentLog) << "Internal error";
250 return;
251 }
252
253 item->setParentItem(parent);
254 item->setProperty("vehicleComponent", QVariant::fromValue(this));
255}
256
258{
259 // Watch for changed on trigger list params
260 for (const QString &paramName: setupCompleteChangedTriggerList()) {
263 (void) connect(fact, &Fact::valueChanged, this, &VehicleComponent::_triggerUpdated);
264 }
265 }
266
267 // Watch enableParam facts so the sections list updates when items are enabled/disabled
268 _ensureSectionsCached();
269 for (const auto &filter : _repeatFilters) {
270 for (const QString &paramName : filter.paramNames) {
273 (void) connect(fact, &Fact::valueChanged, this, &VehicleComponent::sectionsChanged);
274 }
275 }
276 }
277}
#define QGC_LOGGING_CATEGORY(name, categoryStr)
The AutoPilotPlugin class is an abstract base class which represents the methods and objects which ar...
A Fact is used to hold a single value within the system.
Definition Fact.h:17
void valueChanged(const QVariant &value)
This signal is only meant for use by the QT property system. It should not be connected to by client ...
bool parameterExists(int componentId, const QString &paramName) const
Fact * getParameter(int componentId, const QString &paramName)
static constexpr int defaultComponentId
VehicleComponent(Vehicle *vehicle, AutoPilotPlugin *autopilot, AutoPilotPlugin::KnownVehicleComponent KnownVehicleComponent, QObject *parent=nullptr)
virtual ~VehicleComponent()
virtual QString vehicleConfigJson() const
Resource path to a VehicleConfig.json page definition, or empty if none.
virtual QStringList sections() const
void _triggerUpdated(QVariant)
virtual QStringList setupCompleteChangedTriggerList() const =0
virtual void setupTriggerSignals()
QVariantMap sectionKeywords() const
Search keywords per section, keyed by section title. Values are original-case translatable terms.
virtual QString name() const =0
void sectionsChanged()
virtual void addSummaryQmlComponent(QQmlContext *context, QQuickItem *parent)
ParameterManager * parameterManager()
Definition Vehicle.h:573