QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
Mixer.cc
Go to the documentation of this file.
1#include "Mixer.h"
2#include "ParameterManager.h"
3
4using namespace Mixer;
5
6ChannelConfigInstance* ChannelConfig::instantiate(int paramIndex, int actuatorTypeIndex,
7 ParameterManager* parameterManager, std::function<void(Function, Fact*)> factAddedCb)
8{
9 QString param = _parameter.param.name;
10 int usedParamIndex;
11 if (_isActuatorTypeConfig) {
12 usedParamIndex = actuatorTypeIndex + indexOffset();
13 } else {
14 usedParamIndex = paramIndex + indexOffset();
15 }
16 param.replace("${i}", QString::number(usedParamIndex));
17
18 Fact* fact = nullptr;
19 if (param == "" && !_isActuatorTypeConfig) { // constant value
20 float value = 0.f;
21 if (fixedValues().size() == 1) {
22 value = fixedValues()[0];
23 } else if (paramIndex < fixedValues().size()) {
24 value = fixedValues()[paramIndex];
25 }
26
28 metaData->setReadOnly(true);
29 metaData->setDecimalPlaces(4);
30 fact = new Fact("", metaData, this);
31 fact->setRawValue(value);
32
33 } else if (parameterManager->parameterExists(ParameterManager::defaultComponentId, param)) {
34 fact = parameterManager->getParameter(ParameterManager::defaultComponentId, param);
36 fact = new FactBitset(this, fact, usedParamIndex);
38 fact = new FactFloatAsBool(this, fact);
39 }
40 factAddedCb(function(), fact);
41 } else {
42 qCDebug(ActuatorsConfigLog) << "ActuatorOutputChannel: Param does not exist:" << param;
43 }
44
45 ChannelConfigInstance* instance = new ChannelConfigInstance(this, fact, *this);
46 channelInstanceCreated(instance);
47 return instance;
48}
49
51{
52 _instances.append(instance);
54 this, &ChannelConfig::instanceVisibleChanged);
55}
56
57void ChannelConfig::instanceVisibleChanged()
58{
59 bool visible = false;
60 for (const auto& instance : _instances) {
61 if (instance->visible()) {
62 visible = true;
63 }
64 }
65
66 if (visible != _visible) {
67 _visible = visible;
68 emit visibleChanged();
69 }
70}
71
73 [[maybe_unused]] int paramIndex, [[maybe_unused]] int actuatorTypeIndex,
74 [[maybe_unused]] ParameterManager *parameterManager,
75 [[maybe_unused]] std::function<void(Function, Fact *)> factAddedCb)
76{
78 channelInstanceCreated(instance);
79 return instance;
80}
81
83{
84 for (int i = 0; i < configInstances->count(); ++i) {
85 auto channelConfigInstance = configInstances->value<ChannelConfigInstance*>(i);
86 if (channelConfigInstance->channelConfig()->function() == Function::AxisX) {
87 _axes[0] = channelConfigInstance;
88 } else if (channelConfigInstance->channelConfig()->function() == Function::AxisY) {
89 _axes[1] = channelConfigInstance;
90 } else if (channelConfigInstance->channelConfig()->function() == Function::AxisZ) {
91 _axes[2] = channelConfigInstance;
92 }
93 }
94 Q_ASSERT(_axes[0] && _axes[1] && _axes[2]);
95
96 for (int i = 0; i < 3; ++i) {
97 if (!_axes[i]->fact())
98 return;
99 }
100
101 // Initialize fact
102 QStringList enumStrings{tr("Custom"), tr("Upwards"), tr("Downwards"), tr("Forwards"), tr("Backwards"),
103 tr("Leftwards"), tr("Rightwards")};
104 QVariantList enumValues{0, 1, 2, 3, 4, 5, 6};
106 metaData->setEnumInfo(enumStrings, enumValues);
107 _fact = new Fact("", metaData, this);
108 setFactFromAxes();
109
110 connect(_fact, &Fact::rawValueChanged, this, &ChannelConfigInstanceVirtualAxis::setAxesFromFact);
111 for (int i=0; i < 3; ++i) {
112 connect(_axes[i]->fact(), &Fact::rawValueChanged,
113 this, [this](){ ChannelConfigInstanceVirtualAxis::setFactFromAxes(true); });
114 }
115 // Inherit visibility & enabled from the first axis
116 connect(_axes[0], &ChannelConfigInstance::visibleChanged,
117 this, &ChannelConfigInstanceVirtualAxis::axisVisibleChanged);
118 connect(_axes[0], &ChannelConfigInstance::enabledChanged,
119 this, &ChannelConfigInstanceVirtualAxis::axisEnableChanged);
120 axisVisibleChanged();
121 axisEnableChanged();
122}
123
124void ChannelConfigInstanceVirtualAxis::axisVisibleChanged()
125{
126 if (_axes[0]->visibleRule() != visibleRule()) {
127 setVisibleRule(_axes[0]->visibleRule());
128 }
129}
130
131void ChannelConfigInstanceVirtualAxis::axisEnableChanged()
132{
133 if (_axes[0]->enabledRule() != enabledRule()) {
134 setEnabledRule(_axes[0]->enabledRule());
135 }
136}
137
138void ChannelConfigInstanceVirtualAxis::setFactFromAxes(bool keepVisible)
139{
140 if (_ignoreChange) {
141 return;
142 }
143 _ignoreChange = true;
144 float x = _axes[0]->fact()->rawValue().toFloat();
145 float y = _axes[1]->fact()->rawValue().toFloat();
146 float z = _axes[2]->fact()->rawValue().toFloat();
147 Direction direction{Direction::Custom}; // set to custom if no match
148 const float eps = 0.00001f;
149 if (fabsf(x) < eps && fabsf(y) < eps && fabsf(z + 1.f) < eps) {
150 direction = Direction::Upwards;
151 } else if (fabsf(x) < eps && fabsf(y) < eps && fabsf(z - 1.f) < eps) {
152 direction = Direction::Downwards;
153 } else if (fabsf(x - 1.f) < eps && fabsf(y) < eps && fabsf(z) < eps) {
154 direction = Direction::Forwards;
155 } else if (fabsf(x + 1.f) < eps && fabsf(y) < eps && fabsf(z) < eps) {
156 direction = Direction::Backwards;
157 } else if (fabsf(x) < eps && fabsf(y + 1.f) < eps && fabsf(z) < eps) {
158 direction = Direction::Leftwards;
159 } else if (fabsf(x) < eps && fabsf(y - 1.f) < eps && fabsf(z) < eps) {
160 direction = Direction::Rightwards;
161 }
162 _fact->setRawValue((uint32_t)direction);
163
164 bool visible = direction == Direction::Custom || keepVisible;
165 for(int i=0; i < 3; ++i) {
166 _axes[i]->setVisibleAxis(visible);
167 }
168 _ignoreChange = false;
169}
170
171void ChannelConfigInstanceVirtualAxis::setAxesFromFact()
172{
173 if (_ignoreChange) {
174 return;
175 }
176 _ignoreChange = true;
177 int directionIdx = _fact->rawValue().toInt();
178
179 if (directionIdx > 0) {
180 Direction direction = (Direction)directionIdx;
181 float x{}, y{}, z{};
182 switch (direction) {
184 x = 0.f; y = 0.f; z = -1.f;
185 break;
187 x = 0.f; y = 0.f; z = 1.f;
188 break;
190 x = 1.f; y = 0.f; z = 0.f;
191 break;
193 x = -1.f; y = 0.f; z = 0.f;
194 break;
196 x = 0.f; y = -1.f; z = 0.f;
197 break;
199 x = 0.f; y = 1.f; z = 0.f;
200 break;
202 break;
203 }
204 _axes[0]->fact()->setRawValue(x);
205 _axes[1]->fact()->setRawValue(y);
206 _axes[2]->fact()->setRawValue(z);
207 }
208
209
210 bool visible = directionIdx == 0;
211 for(int i=0; i < 3; ++i) {
212 _axes[i]->setVisibleAxis(visible);
213 }
214 _ignoreChange = false;
215}
216
217MixerChannel::MixerChannel(QObject *parent, const QString &label, int actuatorFunction, int paramIndex, int actuatorTypeIndex,
218 QmlObjectListModel &channelConfigs, ParameterManager* parameterManager, const Rule* rule,
219 std::function<void(Function, Fact*)> factAddedCb)
220 : QObject(parent), _label(label), _actuatorFunction(actuatorFunction), _paramIndex(paramIndex),
221 _actuatorTypeIndex(actuatorTypeIndex), _rule(rule)
222{
223 for (int i = 0; i < channelConfigs.count(); ++i) {
224 auto channelConfig = channelConfigs.value<ChannelConfig*>(i);
225
226 ChannelConfigInstance* instance = channelConfig->instantiate(paramIndex, actuatorTypeIndex, parameterManager, factAddedCb);
227 Fact* fact = instance->fact();
228
229 // if we have a valid rule, check the identifiers
230 if (rule) {
231 if (channelConfig->identifier() == rule->selectIdentifier) {
232 _ruleSelectIdentifierIdx = _configInstances->count();
233 if (fact) {
234 _currentSelectIdentifierValue = fact->rawValue().toInt();
235 }
236 } else {
237 for (int applyIndex = 0; applyIndex < rule->applyIdentifiers.size(); ++applyIndex) {
238 if (channelConfig->identifier() == rule->applyIdentifiers[applyIndex]) {
239 instance->setRuleApplyIdentifierIdx(applyIndex);
240 }
241 }
242 }
243
244 if (fact) {
245 connect(fact, &Fact::rawValueChanged, this, [this]() { applyRule(); });
246 }
247 }
248
249 _configInstances->append(instance);
250 }
251
252 for (int i = 0; i < _configInstances->count(); ++i) {
253 auto channelConfigInstance = _configInstances->value<ChannelConfigInstance*>(i);
254 channelConfigInstance->allInstancesInitialized(_configInstances);
255 }
256
257 applyRule(true);
258}
259
260void MixerChannel::applyRule(bool noConstraints)
261{
262 if (!_rule || _ruleSelectIdentifierIdx == -1 || _applyingRule) {
263 return;
264 }
265 _applyingRule = true; // prevent recursive signals
266
267 Fact* selectFact = _configInstances->value<ChannelConfigInstance*>(_ruleSelectIdentifierIdx)->fact();
268 if (selectFact && selectFact->type() == FactMetaData::valueTypeInt32) {
269 const int value = selectFact->rawValue().toInt();
270 if (_rule->items.contains(value)) {
271 bool valueChanged = value != _currentSelectIdentifierValue;
272 const auto& items = _rule->items[value];
273 for (int i = 0; i < _configInstances->count(); ++i) {
274 ChannelConfigInstance* configInstance = _configInstances->value<ChannelConfigInstance*>(i);
275 if (configInstance->fact() && configInstance->ruleApplyIdentifierIdx() >= 0) {
276 const Rule::RuleItem& ruleItem = items[configInstance->ruleApplyIdentifierIdx()];
277 double factValue = configInstance->fact()->rawValue().toDouble();
278 bool changed = false;
279 if (ruleItem.hasMin && factValue < ruleItem.min) {
280 factValue = ruleItem.min;
281 changed = true;
282 }
283 if (ruleItem.hasMax && factValue > ruleItem.max) {
284 factValue = ruleItem.max;
285 changed = true;
286 }
287 if (valueChanged && ruleItem.hasDefault) {
288 factValue = ruleItem.defaultVal;
289 changed = true;
290 }
291 if (changed && !noConstraints) {
292 // here we could notify the user that a constraint got applied...
293 configInstance->fact()->setRawValue(factValue);
294 }
295 configInstance->setVisibleRule(!ruleItem.hidden);
296 configInstance->setEnabledRule(!ruleItem.disabled);
297 }
298 }
299
300 } else {
301 // no rule set for this value, just reset
302 for (int i = 0; i < _configInstances->count(); ++i) {
303 ChannelConfigInstance* configInstance = _configInstances->value<ChannelConfigInstance*>(i);
304 configInstance->setVisibleRule(true);
305 configInstance->setEnabledRule(true);
306 }
307 }
308 _currentSelectIdentifierValue = value;
309 }
310 _applyingRule = false;
311}
312
314 ActuatorGeometry& geometry) const
315{
317 const auto iter = actuatorTypes.find(group.actuatorType);
318 if (iter == actuatorTypes.end()) {
319 return false;
320 }
321 geometry.index = _actuatorTypeIndex;
322 geometry.labelIndexOffset = iter->labelIndexOffset;
323 int numPositionAxis = 0;
324
325 for (int i = 0; i < _configInstances->count(); ++i) {
326 ChannelConfigInstance* configInstance = _configInstances->value<ChannelConfigInstance*>(i);
327 if (!configInstance->fact()) {
328 continue;
329 }
330 switch (configInstance->channelConfig()->function()) {
332 geometry.position.setX(configInstance->fact()->rawValue().toFloat());
333 ++numPositionAxis;
334 break;
336 geometry.position.setY(configInstance->fact()->rawValue().toFloat());
337 ++numPositionAxis;
338 break;
340 geometry.position.setZ(configInstance->fact()->rawValue().toFloat());
341 ++numPositionAxis;
342 break;
344 geometry.spinDirection = configInstance->fact()->rawValue().toBool() ?
346 break;
347 case Function::AxisX:
348 case Function::AxisY:
349 case Function::AxisZ:
350 case Function::Type:
352 break;
353 }
354 }
355
356 return numPositionAxis == 3;
357}
358
359Fact* MixerChannel::getFact([[maybe_unused]] Function function) const
360{
361 for (int i = 0; i < _configInstances->count(); ++i) {
362 ChannelConfigInstance* configInstance = _configInstances->value<ChannelConfigInstance*>(i);
363 if (configInstance->channelConfig()->function() == Function::Type) {
364 return configInstance->fact();
365 }
366 }
367 return nullptr;
368}
369
371{
372 _channelConfigs->append(channelConfig);
374}
375
377{
378 _channels->append(channel);
379 emit channelsChanged();
380}
381
383{
384 _params->append(param);
385}
386
387void Mixers::reset(const ActuatorTypes& actuatorTypes, const MixerOptions& mixerOptions,
388 const QMap<int, OutputFunction>& functions, const Rules& rules)
389{
390 _groups->clearAndDeleteContents();
391 _actuatorTypes = actuatorTypes;
392 _mixerOptions = mixerOptions;
393 _functions = functions;
394 _functionsSpecificLabel.clear();
395 _rules = rules;
396 _mixerConditions.clear();
397 for (const auto& mixerOption : _mixerOptions) {
398 _mixerConditions.append(Condition(mixerOption.option, _parameterManager, "mixer-option"));
399 }
400 update();
401}
402
404{
405 // clear first
406 _groups->clearAndDeleteContents();
407 _functionsSpecificLabel.clear();
408
409 unsubscribeFacts();
410
411 // find configured mixer
412 _selectedMixer = -1;
413 for (int i = 0; i < _mixerConditions.size(); ++i) {
414 if (_mixerConditions[i].evaluate()) {
415 _selectedMixer = i;
416 break;
417 }
418 }
419
420
421 if (_selectedMixer != -1) {
422
423 subscribeFact(_mixerConditions[_selectedMixer].fact());
424
425 qCDebug(ActuatorsConfigLog) << "selected mixer index:" << _selectedMixer;
426
427 const auto& actuatorGroups = _mixerOptions[_selectedMixer].actuators;
428 QMap<QString, int> actuatorTypeCount;
429 for (const auto &actuatorGroup : actuatorGroups) {
430 int count = actuatorGroup.fixedCount;
431 if (actuatorGroup.count != "") {
432 Fact* countFact = getFact(actuatorGroup.count);
433 if (countFact) {
434 count = countFact->rawValue().toInt();
435 }
436 }
437
438 int actuatorTypeIndex = actuatorTypeCount.value(actuatorGroup.actuatorType, 0);
439
440 MixerConfigGroup* currentMixerGroup = new MixerConfigGroup(this, actuatorGroup);
441
442 // params
443 const auto actuatorType = _actuatorTypes.find(actuatorGroup.actuatorType);
444 if (actuatorType != _actuatorTypes.end()) {
445 for (const auto& perItemParam : actuatorType->perItemParams) {
446 MixerParameter param{};
447 param.param = perItemParam;
448 currentMixerGroup->addChannelConfig(new ChannelConfig(currentMixerGroup, param, true));
449 }
450 }
451
452 const Rule* selectedRule{nullptr}; // at most 1 rule can be applied
453 int axisIdx[3]{-1, -1, -1};
454 for (const auto& perItemParam : actuatorGroup.perItemParameters) {
455 currentMixerGroup->addChannelConfig(new ChannelConfig(currentMixerGroup, perItemParam, false));
456
457 if (perItemParam.function == Function::AxisX) {
458 axisIdx[0] = currentMixerGroup->channelConfigs()->count() - 1;
459 } else if (perItemParam.function == Function::AxisY) {
460 axisIdx[1] = currentMixerGroup->channelConfigs()->count() - 1;
461 } else if (perItemParam.function == Function::AxisZ) {
462 axisIdx[2] = currentMixerGroup->channelConfigs()->count() - 1;
463 }
464
465 if (!perItemParam.identifier.isEmpty()) {
466 for (const auto& rule : _rules) {
467 if (rule.selectIdentifier == perItemParam.identifier) {
468 selectedRule = &rule;
469 }
470 }
471 }
472 }
473
474 // Add virtual axis dropdown configuration param if all 3 axes are found
475 if (axisIdx[0] >= 0 && axisIdx[1] >= 0 && axisIdx[2] >= 0) {
476 ChannelConfig* axisXConfig = currentMixerGroup->channelConfigs()->value<ChannelConfig*>(axisIdx[0]);
477 MixerParameter parameter = axisXConfig->config(); // use axisX as base (somewhat arbitrary)
479 parameter.param.name = "";
480 parameter.param.label = tr("Axis");
481 parameter.identifier = "";
482 ChannelConfig* virtualChannelConfig = new ChannelConfigVirtualAxis(currentMixerGroup, parameter);
483 currentMixerGroup->channelConfigs()->insert(axisIdx[0], virtualChannelConfig);
484 }
485
486 // 'count' param
487 if (actuatorGroup.count != "") {
488 currentMixerGroup->setCountParam(new ConfigParameter(currentMixerGroup, getFact(actuatorGroup.count),
489 "", false));
490 }
491
492 for (int actuatorIdx = 0; actuatorIdx < count; ++actuatorIdx) {
493 QString label = "";
494 int actuatorFunction = 0;
495 if (actuatorType != _actuatorTypes.end()) {
496 actuatorFunction = actuatorType->functionMin + actuatorTypeIndex;
497 label = _functions.value(actuatorFunction).label;
498 if (label == "") {
499 qCWarning(ActuatorsConfigLog) << "No label for output function" << actuatorFunction;
500 }
501 QString itemLabelPrefix{};
502 if (actuatorGroup.itemLabelPrefix.size() == 1) {
503 QString paramIndex = QString::number(actuatorIdx + 1);
504 itemLabelPrefix = actuatorGroup.itemLabelPrefix[0];
505 itemLabelPrefix.replace("${i}", paramIndex);
506 } else if (actuatorIdx < actuatorGroup.itemLabelPrefix.size()) {
507 itemLabelPrefix = actuatorGroup.itemLabelPrefix[actuatorIdx];
508 }
509 if (itemLabelPrefix != "") {
510 label = itemLabelPrefix + " (" + label + ")";
511 _functionsSpecificLabel[actuatorFunction] = itemLabelPrefix;
512 }
513 }
514 auto factAdded = [this](Function function, Fact* fact) {
515 // Type might change more than the geometry
516 subscribeFact(fact, function != Function::Type);
517 };
518 MixerChannel* channel = new MixerChannel(currentMixerGroup, label, actuatorFunction, actuatorIdx, actuatorTypeIndex,
519 *currentMixerGroup->channelConfigs(), _parameterManager, selectedRule, factAdded);
520 currentMixerGroup->addChannel(channel);
521 ++actuatorTypeIndex;
522 }
523
524 // config params
525 for (const auto& parameter : actuatorGroup.parameters) {
526 currentMixerGroup->addConfigParam(new ConfigParameter(currentMixerGroup, getFact(parameter.name),
527 parameter.label, parameter.advanced));
528 }
529
530 _groups->append(currentMixerGroup);
531 actuatorTypeCount[actuatorGroup.actuatorType] = actuatorTypeIndex;
532 }
533 }
534
535 emit groupsChanged();
536
537}
538
539QString Mixers::getSpecificLabelForFunction(int function) const
540{
541 // Try to get it from the actuator type param
542 Fact* typeFact = nullptr;
543 for (int mixerGroupIdx = 0; !typeFact && mixerGroupIdx < _groups->count(); ++mixerGroupIdx) {
544 Mixer::MixerConfigGroup* mixerGroup = _groups->value<Mixer::MixerConfigGroup*>(mixerGroupIdx);
545 for (int mixerChannelIdx = 0; !typeFact && mixerChannelIdx < mixerGroup->channels()->count(); ++mixerChannelIdx) {
546 Mixer::MixerChannel* mixerChannel = mixerGroup->channels()->value<Mixer::MixerChannel*>(mixerChannelIdx);
547
548 if (mixerChannel->actuatorFunction() != function) {
549 continue;
550 }
551
552 typeFact = mixerChannel->getFact(Function::Type);
553 }
554 }
555 if (typeFact) {
556 // Now check if we have multiple functions configured with the same type.
557 // If so, add the function label to disambiguate
558 for (int mixerGroupIdx = 0; mixerGroupIdx < _groups->count(); ++mixerGroupIdx) {
559 Mixer::MixerConfigGroup* mixerGroup = _groups->value<Mixer::MixerConfigGroup*>(mixerGroupIdx);
560 for (int mixerChannelIdx = 0; mixerChannelIdx < mixerGroup->channels()->count(); ++mixerChannelIdx) {
561 Mixer::MixerChannel* mixerChannel = mixerGroup->channels()->value<Mixer::MixerChannel*>(mixerChannelIdx);
562
563 if (mixerChannel->actuatorFunction() == function) {
564 continue;
565 }
566 Fact* typeFactOther = mixerChannel->getFact(Function::Type);
567 if (typeFactOther && typeFactOther->rawValue() == typeFact->rawValue()) {
568 return typeFact->enumOrValueString() + " (" + _functions.value(function).label +")";
569 }
570 }
571 }
572 return typeFact->enumOrValueString();
573 }
574
575 const auto iter = _functionsSpecificLabel.find(function);
576 if (iter == _functionsSpecificLabel.end()) {
577 return _functions.value(function).label;
578 }
579 return *iter;
580}
581
582QSet<int> Mixers::getFunctions(bool requiredOnly) const
583{
584 QSet<int> ret;
585 for (int mixerGroupIdx = 0; mixerGroupIdx < _groups->count(); ++mixerGroupIdx) {
586 Mixer::MixerConfigGroup* mixerGroup = _groups->value<Mixer::MixerConfigGroup*>(mixerGroupIdx);
587 if (!requiredOnly || mixerGroup->group().required) {
588 for (int mixerChannelIdx = 0; mixerChannelIdx < mixerGroup->channels()->count(); ++mixerChannelIdx) {
589 const Mixer::MixerChannel* mixerChannel = mixerGroup->channels()->value<Mixer::MixerChannel*>(mixerChannelIdx);
590 if (mixerChannel->actuatorFunction() != 0) {
591 ret.insert(mixerChannel->actuatorFunction());
592 }
593 }
594 }
595 }
596
597 return ret;
598}
599
601{
602 if (_selectedMixer == -1) {
603 return "";
604 }
605 return _mixerOptions[_selectedMixer].type;
606}
607
608QString Mixers::title() const
609{
610 if (_selectedMixer == -1) {
611 return "";
612 }
613 return _mixerOptions[_selectedMixer].title;
614}
615QString Mixers::helpUrl() const
616{
617 if (_selectedMixer == -1) {
618 return "";
619 }
620 return _mixerOptions[_selectedMixer].helpUrl;
621}
622
623Fact* Mixers::getFact(const QString& paramName)
624{
625 if (!_parameterManager->parameterExists(ParameterManager::defaultComponentId, paramName)) {
626 qCDebug(ActuatorsConfigLog) << "Mixers: Param does not exist:" << paramName;
627 return nullptr;
628 }
629 Fact* fact = _parameterManager->getParameter(ParameterManager::defaultComponentId, paramName);
630 subscribeFact(fact);
631 return fact;
632}
633
634void Mixers::subscribeFact(Fact* fact, bool geometry)
635{
636 if (geometry) {
637 if (fact && !_subscribedFactsGeometry.contains(fact)) {
639 _subscribedFactsGeometry.insert(fact);
640 }
641 } else {
642 if (fact && !_subscribedFacts.contains(fact)) {
643 connect(fact, &Fact::rawValueChanged, this, &Mixers::paramChanged);
644 _subscribedFacts.insert(fact);
645 }
646 }
647}
648
649void Mixers::unsubscribeFacts()
650{
651 for (Fact* fact : _subscribedFacts) {
652 disconnect(fact, &Fact::rawValueChanged, this, &Mixers::paramChanged);
653 }
654 _subscribedFacts.clear();
655
656 for (Fact* fact : _subscribedFactsGeometry) {
657 disconnect(fact, &Fact::rawValueChanged, this, &Mixers::geometryParamChanged);
658 }
659 _subscribedFactsGeometry.clear();
660}
void setDecimalPlaces(int decimalPlaces)
void setEnumInfo(const QStringList &strings, const QVariantList &values)
void setReadOnly(bool bValue)
A Fact is used to hold a single value within the system.
Definition Fact.h:19
void rawValueChanged(const QVariant &value)
void allInstancesInitialized(QmlObjectListModel *configInstances) override
Definition Mixer.cc:82
bool visibleRule() const
Definition Mixer.h:206
int ruleApplyIdentifierIdx() const
Definition Mixer.h:214
void setVisibleAxis(bool visible)
Definition Mixer.h:212
virtual void allInstancesInitialized(QmlObjectListModel *configInstances)
Definition Mixer.h:217
bool enabledRule() const
Definition Mixer.h:207
void setEnabledRule(bool enabled)
Definition Mixer.h:210
void setVisibleRule(bool visible)
Definition Mixer.h:209
ChannelConfigInstance * instantiate(int paramIndex, int actuatorTypeIndex, ParameterManager *parameterManager, std::function< void(Function, Fact *)> factAddedCb) override
Definition Mixer.cc:72
bool visible() const
Definition Mixer.h:143
const MixerParameter & config() const
Definition Mixer.h:152
void channelInstanceCreated(ChannelConfigInstance *instance)
Definition Mixer.cc:50
Function function() const
Definition Mixer.h:139
int indexOffset() const
Definition Mixer.h:148
const QList< float > & fixedValues() const
Definition Mixer.h:145
Parameter::DisplayOption displayOption() const
Definition Mixer.h:147
virtual ChannelConfigInstance * instantiate(int paramIndex, int actuatorTypeIndex, ParameterManager *parameterManager, std::function< void(Function, Fact *)> factAddedCb)
Definition Mixer.cc:6
int actuatorFunction() const
Definition Mixer.h:277
MixerChannel(QObject *parent, const QString &label, int actuatorFunction, int paramIndex, int actuatorTypeIndex, QmlObjectListModel &channelConfigs, ParameterManager *parameterManager, const Rule *rule, std::function< void(Function, Fact *)> factAddedCb)
Definition Mixer.cc:217
bool getGeometry(const ActuatorTypes &actuatorTypes, const MixerOption::ActuatorGroup &group, ActuatorGeometry &geometry) const
Definition Mixer.cc:313
void applyRule(bool noConstraints=false)
Definition Mixer.cc:260
Fact * getFact(Function function) const
Definition Mixer.cc:359
QmlObjectListModel * channelConfigs()
Definition Mixer.h:322
const MixerOption::ActuatorGroup & group() const
Definition Mixer.h:334
void setCountParam(ConfigParameter *param)
Definition Mixer.h:332
void addConfigParam(ConfigParameter *param)
Definition Mixer.cc:382
void addChannelConfig(ChannelConfig *channelConfig)
Definition Mixer.cc:370
QmlObjectListModel * channels()
Definition Mixer.h:325
void addChannel(MixerChannel *channel)
Definition Mixer.cc:376
const ActuatorTypes & actuatorTypes() const
Definition Mixer.h:378
QString configuredType() const
Definition Mixer.cc:600
void update()
Definition Mixer.cc:403
void groupsChanged()
const QMap< int, OutputFunction > & functions() const
Definition Mixer.h:396
void reset(const ActuatorTypes &actuatorTypes, const MixerOptions &mixerOptions, const QMap< int, OutputFunction > &functions, const Rules &rules)
Definition Mixer.cc:387
QString getSpecificLabelForFunction(int function) const
Definition Mixer.cc:539
QString title() const
Definition Mixer.cc:608
void geometryParamChanged()
void paramChanged()
QString helpUrl() const
Definition Mixer.cc:615
QSet< int > getFunctions(bool requiredOnly) const
Definition Mixer.cc:582
bool parameterExists(int componentId, const QString &paramName) const
Fact * getParameter(int componentId, const QString &paramName)
static constexpr int defaultComponentId
void append(QObject *object)
Caller maintains responsibility for object ownership and deletion.
T value(int index) const
int count() const override final
void clearAndDeleteContents() override final
Clears the list and calls deleteLater on each entry.
void insert(int index, QObject *object)
Definition Mixer.h:12
Function
Definition Mixer.h:14
@ SpinDirection
CCW = true or 1.
QList< Rule > Rules
Definition Mixer.h:95
QList< MixerOption > MixerOptions
Definition Mixer.h:76
QMap< QString, ActuatorType > ActuatorTypes
key is the group name, where 'DEFAULT' is the default group
Definition Mixer.h:52
int labelIndexOffset
Definition Common.h:146
static Type typeFromStr(const QString &type)
Definition Common.cc:244
QVector3D position
Definition Common.h:147
SpinDirection spinDirection
Definition Common.h:148
bool required
if true, actuator has to be configured for a valid setup
Definition Mixer.h:64
Function function
Definition Mixer.h:28
Parameter param
Definition Mixer.h:27
QList< QString > applyIdentifiers
Definition Mixer.h:91
QMap< int, QList< RuleItem > > items
Definition Mixer.h:92
QString selectIdentifier
Definition Mixer.h:90
@ Bitset
integer displayed as boolean (checkbox), where the index defines the bit
@ BoolTrueIfPositive
Show checkbox for float/int value.
QString name
vehicle parameter name, this may have an index in the form '${i}'
Definition Common.h:26