8#include <QtCore/QString>
10#include <QtCore/QJsonArray>
11#include <QtCore/QJsonObject>
20 : QObject(parent), _actuatorTest(vehicle), _mixer(vehicle->parameterManager()),
21 _motorAssignment(
nullptr, vehicle, _actuatorOutputs), _vehicle(vehicle)
25 qRegisterMetaType<Actuators*>(
"Actuators*");
34 QPointF clickPosition{ x, y };
36 qCDebug(ActuatorsLog) <<
"Image clicked: position:" << clickPosition <<
"displaySize:" << displaySize <<
"motor index:" << motorIndex;
38 if (_motorAssignment.
active()) {
39 QList<ActuatorGeometry>& actuators = provider->
actuators();
41 for (
auto& actuator : actuators) {
43 actuator.renderOptions.highlight =
false;
47 updateGeometryImage();
58 if (index >= _actuatorOutputs->
count() || index < 0) {
61 _selectedActuatorOutput = index;
66 if (_actuatorOutputs->
count() == 0) {
72void Actuators::updateGeometryImage()
76 QList<ActuatorGeometry>& actuators = provider->
actuators();
77 QList<ActuatorGeometry> previousActuators = actuators;
81 for (
int mixerGroupIdx = 0; mixerGroupIdx < _mixer.
groups()->count(); ++mixerGroupIdx) {
83 for (
int mixerChannelIdx = 0; mixerChannelIdx < mixerGroup->
channels()->
count(); ++mixerChannelIdx) {
87 actuators.append(geometry);
88 qCDebug(ActuatorsLog) <<
"Airframe actuator:" << geometry.
index <<
"pos:" << geometry.position;
94 if (previousActuators.size() == actuators.size()) {
95 for (
int i = 0; i < actuators.size(); ++i) {
96 if (previousActuators[i].type == actuators[i].type && previousActuators[i].index == actuators[i].index) {
97 actuators[i].renderOptions = previousActuators[i].renderOptions;
102 _imageRefreshFlag = !_imageRefreshFlag;
105 _motorAssignmentEnabled = provider->
numMotors() > 0;
118 QFile file(json_file);
119 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
120 _initError = QStringLiteral(
"Could not open metadata file: %1").arg(file.fileName());
122 qCWarning(ActuatorsLog) << _initError;
126 const QByteArray json_data = file.readAll();
129 QJsonParseError parseError;
130 _jsonMetadata = QJsonDocument::fromJson(json_data, &parseError);
131 if (_jsonMetadata.isNull()) {
132 _initError = QStringLiteral(
"Invalid JSON in metadata file %1: %2").arg(file.fileName(), parseError.errorString());
134 qCWarning(ActuatorsLog) << _initError;
146 qWarning() <<
"Incorrect calling order, parameters not yet ready";
149 if (!parseJson(_jsonMetadata)) {
155 for (
int groupIdx = 0; groupIdx < _actuatorOutputs->
count(); groupIdx++) {
156 ActuatorOutput* group = qobject_cast<ActuatorOutput*>(_actuatorOutputs->
get(groupIdx));
158 qCDebug(ActuatorsLog) <<
"Removing actuator group w/o function parameters at" << groupIdx;
159 _actuatorOutputs->
removeAt(groupIdx);
171 qCDebug(ActuatorsLog) <<
"Param update";
176 QList<int> allFunctions;
177 for (
int groupIdx = 0; groupIdx < _actuatorOutputs->
count(); groupIdx++) {
178 ActuatorOutput* group = qobject_cast<ActuatorOutput*>(_actuatorOutputs->
get(groupIdx));
180 QList<Fact*> groupFunctions;
182 for (
const auto& groupFunction : groupFunctions) {
183 int function = groupFunction->rawValue().toInt();
185 allFunctions.append(function);
189 const auto iter = _mixer.
functions().find(function);
190 if (iter != _mixer.
functions().end() && iter->note !=
"") {
191 if (iter->noteCondition.evaluate()) {
192 qCDebug(ActuatorsLog) <<
"Showing Note:" << iter->note;
199 for (
int subbroupIdx = 0; subbroupIdx < group->
subgroups()->count(); subbroupIdx++) {
201 for (
int channelIdx = 0; channelIdx < subgroup->
channelConfigs()->count(); channelIdx++) {
208 std::sort(allFunctions.begin(), allFunctions.end());
211 QList<ActuatorTesting::Actuator*> actuators;
212 QSet<int> uniqueConfiguredFunctions;
214 for (
int function : allFunctions) {
215 if (uniqueConfiguredFunctions.find(function) != uniqueConfiguredFunctions.end()) {
218 uniqueConfiguredFunctions.insert(function);
222 bool excludeFromActuatorTesting =
false;
223 const auto iter = _mixer.
functions().find(function);
225 excludeFromActuatorTesting = iter->excludeFromActuatorTesting;
229 if (!excludeFromActuatorTesting) {
231 for (
const auto& actuatorTypeName : actuatorTypes.keys()) {
242 if (!found && actuatorTypes.find(
"DEFAULT") != actuatorTypes.end()) {
253 QSet<int> requiredFunctions = _mixer.
getFunctions(
true);
254 _hasUnsetRequiredFunctions =
false;
255 for (
int requiredFunction : requiredFunctions) {
256 if (uniqueConfiguredFunctions.find(requiredFunction) == uniqueConfiguredFunctions.end()) {
257 _hasUnsetRequiredFunctions =
true;
262 updateFunctionMetadata();
264 updateActuatorActions();
266 updateGeometryImage();
269void Actuators::updateFunctionMetadata()
274 QSet<int> usedMixerFunctions = _mixer.
getFunctions(
false);
276 QMap<int, QString> usedMixerLabels;
277 for (
int usedMixerFunction : usedMixerFunctions) {
281 if (_usedMixerLabels == usedMixerLabels) {
285 _usedMixerLabels = usedMixerLabels;
288 QSet<int> removedMixerFunctions;
289 for(Mixer::ActuatorTypes::const_iterator iter = _mixer.
actuatorTypes().constBegin();
291 if (iter.key() ==
"DEFAULT")
294 for (
int i = iter.value().functionMin; i <= iter.value().functionMax; ++i) {
295 if (!usedMixerFunctions.contains(i)) {
296 removedMixerFunctions.insert(i);
302 for (
int groupIdx = 0; groupIdx < _actuatorOutputs->
count(); groupIdx++) {
303 ActuatorOutput* group = qobject_cast<ActuatorOutput*>(_actuatorOutputs->
get(groupIdx));
307 if (!enumStrings.empty()) {
308 QVariantList enumValues = fact->enumValues();
311 for (int usedMixerFunction : usedMixerFunctions) {
312 QString label = usedMixerLabels[usedMixerFunction];
313 int index = enumValues.indexOf(usedMixerFunction);
316 bool inserted = false;
317 for (index = 0; index < enumValues.count() && !inserted; ++index) {
318 if (enumValues[index].toInt() > usedMixerFunction) {
319 enumValues.insert(index, usedMixerFunction);
320 enumStrings.insert(index, label);
325 enumValues.append(usedMixerFunction);
326 enumStrings.append(label);
329 enumStrings[index] = label;
334 for (
int removedMixerFunction : removedMixerFunctions) {
335 int index = enumValues.indexOf(removedMixerFunction);
337 enumValues.removeAt(index);
338 enumStrings.removeAt(index);
348void Actuators::updateActuatorActions()
351 QSet<int> addedFunctions;
352 for (
int groupIdx = 0; groupIdx < _actuatorOutputs->
count(); groupIdx++) {
353 ActuatorOutput* group = qobject_cast<ActuatorOutput*>(_actuatorOutputs->
get(groupIdx));
356 int outputFunctionVal = fact->
rawValue().toInt();
357 if (outputFunctionVal != 0 && !addedFunctions.contains(outputFunctionVal)) {
358 auto outputFunctionIter = _mixer.functions().find(outputFunctionVal);
359 if (outputFunctionIter != _mixer.functions().end()) {
360 const Mixer::Mixers::OutputFunction& outputFunction = outputFunctionIter.value();
361 for (const auto& action : subgroup->actions()) {
362 if (!action.condition.evaluate()) {
365 if (!action.actuatorTypes.empty() && action.actuatorTypes.find(outputFunction.actuatorType) == action.actuatorTypes.end()) {
370 auto actuatorAction = new ActuatorActions::Action(this, action, outputFunction.label, outputFunctionVal, _vehicle);
371 ActuatorActions::ActionGroup* actionGroup = nullptr;
373 for (int actionGroupIdx = 0; actionGroupIdx < _actuatorActions->count(); actionGroupIdx++) {
374 ActuatorActions::ActionGroup* curActionGroup =
375 qobject_cast<ActuatorActions::ActionGroup*>(_actuatorActions->get(actionGroupIdx));
376 if (curActionGroup->type() == action.type) {
377 actionGroup = curActionGroup;
383 QString groupLabel = action.typeToLabel();
384 actionGroup = new ActuatorActions::ActionGroup(this, groupLabel, action.type);
385 _actuatorActions->append(actionGroup);
387 actionGroup->addAction(actuatorAction);
388 addedFunctions.insert(outputFunctionVal);
395 emit actuatorActionsChanged();
398bool Actuators::parseJson(
const QJsonDocument &json)
402 QJsonObject obj = json.object();
403 QJsonValue outputsJson = obj.value(
"outputs_v1");
404 QJsonValue functionsJson = obj.value(
"functions_v1");
405 QJsonValue mixerJson = obj.value(
"mixer_v1");
406 if (outputsJson.isNull() || functionsJson.isNull() || mixerJson.isNull()) {
408 if (outputsJson.isNull()) missing <<
"outputs_v1";
409 if (functionsJson.isNull()) missing <<
"functions_v1";
410 if (mixerJson.isNull()) missing <<
"mixer_v1";
411 if (_initError.isEmpty()) {
412 _initError = QStringLiteral(
"Missing required JSON sections: %1").arg(missing.join(
", "));
414 qCWarning(ActuatorsLog) << _initError;
419 auto makeConditionFromValue = [
this](
const QJsonValue& val,
const char* key) {
423 QJsonArray outputs = outputsJson.toArray();
424 for (
const auto &&outputJson : outputs) {
425 QJsonValue output = outputJson.toObject();
426 QString label = output[
"label"].toString();
428 qCDebug(ActuatorsLog) <<
"Actuator group:" << label;
430 Condition groupVisibilityCondition = makeConditionFromValue(output,
"show-subgroups-if");
431 subscribeFact(groupVisibilityCondition.
fact());
434 _actuatorOutputs->
append(currentActuatorOutput);
436 auto parseParam = [¤tActuatorOutput,
this](
const QJsonValue ¶meter) {
438 param.
parse(parameter);
439 QString functionStr = parameter[
"function"].toString(
"");
440 qCDebug(ActuatorsLog) <<
"param:" << param.name <<
"label:" << param.label <<
"function:" << functionStr;
442 if (functionStr ==
"enable") {
443 function = ConfigParameter::Function::Enable;
444 }
else if (functionStr ==
"primary") {
445 function = ConfigParameter::Function::Primary;
446 }
else if (functionStr !=
"") {
447 qCWarning(ActuatorsLog) <<
"Unknown function " << functionStr <<
"for param" << param.name;
452 QJsonArray parameters = output[
"parameters"].toArray();
453 for (
const auto&& parameterJson : parameters) {
454 currentActuatorOutput->
addConfigParam(parseParam(parameterJson.toObject()));
457 QJsonArray subgroups = output[
"subgroups"].toArray();
458 for (
const auto&& subgroupJson : subgroups) {
459 QJsonValue subgroup = subgroupJson.toObject();
460 QString subgroupLabel = subgroup[
"label"].toString();
462 currentActuatorOutput->
addSubgroup(actuatorSubgroup);
464 QJsonValue supportedActions = subgroup[
"supported-actions"];
465 if (!supportedActions.isNull()) {
466 QJsonObject supportedActionsObj = supportedActions.toObject();
467 for (
const auto& actionName : supportedActionsObj.keys()) {
468 QJsonObject actionObj = supportedActionsObj.value(actionName).toObject();
470 bool knownAction =
true;
471 if (actionName ==
"beep") {
473 }
else if (actionName ==
"3d-mode-on") {
475 }
else if (actionName ==
"3d-mode-off") {
477 }
else if (actionName ==
"set-spin-direction1") {
479 }
else if (actionName ==
"set-spin-direction2") {
483 qCWarning(ActuatorsLog) <<
"Unknown 'supported-actions':" << actionName;
486 QJsonArray actuatorTypesArr = actionObj[
"actuator-types"].toArray();
487 for (
const auto&& type : actuatorTypesArr) {
488 action.actuatorTypes.insert(type.toString());
490 action.condition = makeConditionFromValue(actionObj,
"supported-if");
491 subscribeFact(action.condition.fact());
497 QJsonArray subgroupParameters = subgroup[
"parameters"].toArray();
498 for (
const auto&& parameterJson : subgroupParameters) {
499 actuatorSubgroup->
addConfigParam(parseParam(parameterJson.toObject()));
502 QJsonArray channelParameters = subgroup[
"per-channel-parameters"].toArray();
503 for (
const auto&& channelParametersJson : channelParameters) {
504 QJsonValue channelParameter = channelParametersJson.toObject();
506 param.
parse(channelParameter);
509 QString functionStr = channelParameter[
"function"].toString(
"");
510 if (functionStr ==
"function") {
511 function = ChannelConfig::Function::OutputFunction;
512 }
else if (functionStr ==
"disarmed") {
513 function = ChannelConfig::Function::Disarmed;
514 }
else if (functionStr ==
"min") {
515 function = ChannelConfig::Function::Minimum;
516 }
else if (functionStr ==
"max") {
517 function = ChannelConfig::Function::Maximum;
518 }
else if (functionStr ==
"failsafe") {
519 function = ChannelConfig::Function::Failsafe;
520 }
else if (functionStr !=
"") {
521 qCWarning(ActuatorsLog) <<
"Unknown 'function':" << functionStr;
524 Condition visibilityCondition = makeConditionFromValue(channelParameter,
"show-if");
525 subscribeFact(visibilityCondition.
fact());
527 qCDebug(ActuatorsLog) <<
"per-channel-param:" << param.
label <<
"param:" << param.
name;
531 QJsonArray channels = subgroup[
"channels"].toArray();
532 for (
const auto&& channelJson : channels) {
533 QJsonValue channel = channelJson.toObject();
534 QString channelLabel = channel[
"label"].toString();
535 int paramIndex = channel[
"param-index"].toInt();
536 qCDebug(ActuatorsLog) <<
"channel label:" << channelLabel <<
"param-index" << paramIndex;
544 _showUi = makeConditionFromValue(QJsonValue(obj),
"show-ui-if");
547 QMap<int, Mixer::Mixers::OutputFunction> outputFunctions;
548 QJsonObject functions = functionsJson.toObject();
549 for (
const auto& functionKey : functions.keys()) {
551 int key = functionKey.toInt(&ok);
553 QJsonObject functionObj = functions.value(functionKey).toObject();
554 QString label = functionObj[
"label"].toString();
556 QJsonObject noteObj = functionObj[
"note"].toObject();
557 QString note = noteObj[
"text"].toString();
558 QString condition = noteObj[
"condition"].toString();
559 QString noteCondition = functionObj[
"label"].toString();
560 bool exclude = functionObj[
"exclude-from-actuator-testing"].toBool(
false);
562 subscribeFact(conditionObj.fact());
567 qCDebug(ActuatorsLog) <<
"functions:" << outputFunctions;
571 QJsonObject actuatorTypesJson = mixerJson.toObject().value(
"actuator-types").toObject();
572 for (
const auto& actuatorTypeName : actuatorTypesJson.keys()) {
573 QJsonValue actuatorTypeVal = actuatorTypesJson.value(actuatorTypeName).toObject();
575 actuatorType.
functionMin = actuatorTypeVal[
"function-min"].toInt();
576 actuatorType.functionMax = actuatorTypeVal[
"function-max"].toInt();
577 actuatorType.labelIndexOffset = actuatorTypeVal[
"label-index-offset"].toInt(0);
578 QJsonValue values = actuatorTypeVal[
"values"].toObject();
579 actuatorType.values.min = values[
"min"].toDouble();
580 actuatorType.values.max = values[
"max"].toDouble();
581 actuatorType.values.defaultVal = values[
"default"].toDouble();
582 if (values[
"default-is-nan"].toBool()) {
583 actuatorType.values.defaultVal = NAN;
585 actuatorType.values.reversible = values[
"reversible"].toBool();
587 QJsonArray perItemParametersJson = actuatorTypeVal[
"per-item-parameters"].toArray();
588 for (
const auto&& perItemParameterJson : perItemParametersJson) {
589 QJsonValue perItemParameter = perItemParameterJson.toObject();
591 param.
parse(perItemParameter);
592 actuatorType.perItemParams.append(param);
594 actuatorTypes[actuatorTypeName] = actuatorType;
598 auto actuatorTypeIter = actuatorTypes.constBegin();
599 while (actuatorTypeIter != actuatorTypes.constEnd()) {
600 if (actuatorTypeIter.key() !=
"DEFAULT") {
601 for (
int function = actuatorTypeIter.value().functionMin; function <= actuatorTypeIter.value().functionMax; ++function) {
602 auto functionIter = outputFunctions.find(function);
603 if (functionIter != outputFunctions.end()) {
604 functionIter->actuatorType = actuatorTypeIter.key();
612 QJsonValue mixerConfigJson = mixerJson.toObject().value(
"config");
613 QJsonArray mixerConfigJsonArr = mixerConfigJson.toArray();
614 for (
const auto&& mixerConfigJsonValue : mixerConfigJsonArr) {
615 QJsonValue mixerConfig = mixerConfigJsonValue.toObject();
617 option.
option = mixerConfig[
"option"].toString();
618 option.type = mixerConfig[
"type"].toString();
619 option.title = mixerConfig[
"title"].toString();
620 option.helpUrl = mixerConfig[
"help-url"].toString();
621 QJsonArray actuatorsJson = mixerConfig[
"actuators"].toArray();
622 for (
const auto&& actuatorJson : actuatorsJson) {
623 QJsonValue actuatorJsonVal = actuatorJson.toObject();
625 actuator.
groupLabel = actuatorJsonVal[
"group-label"].toString();
626 if (actuatorJsonVal[
"count"].isString()) {
627 actuator.count = actuatorJsonVal[
"count"].toString();
629 actuator.fixedCount = actuatorJsonVal[
"count"].toInt();
631 actuator.actuatorType = actuatorJsonVal[
"actuator-type"].toString();
632 actuator.required = actuatorJsonVal[
"required"].toBool(
false);
633 QJsonArray parametersJson = actuatorJsonVal[
"parameters"].toArray();
634 for (
const auto&& parameterJson : parametersJson) {
635 QJsonValue parameter = parameterJson.toObject();
637 mixerParameter.
parse(parameter);
638 actuator.parameters.append(mixerParameter);
641 QJsonArray perItemParametersJson = actuatorJsonVal[
"per-item-parameters"].toArray();
642 for (
const auto&& parameterJson : perItemParametersJson) {
643 QJsonValue parameter = parameterJson.toObject();
646 mixerParameter.identifier = parameter[
"identifier"].toString();
647 QString function = parameter[
"function"].toString();
648 if (function ==
"posx") {
650 }
else if (function ==
"posy") {
652 }
else if (function ==
"posz") {
654 }
else if (function ==
"spin-dir") {
656 }
else if (function ==
"axisx") {
658 }
else if (function ==
"axisy") {
660 }
else if (function ==
"axisz") {
662 }
else if (function ==
"type") {
664 }
else if (function !=
"") {
665 qCWarning(ActuatorsLog) <<
"Unknown param function:" << function;
668 bool invalid =
false;
669 if (mixerParameter.param.name ==
"") {
670 QJsonArray valuesJson = parameter[
"value"].toArray();
671 for (
const auto&& valueJson : valuesJson) {
672 mixerParameter.values.append(valueJson.toDouble());
675 if (actuator.fixedCount != mixerParameter.values.size() && mixerParameter.values.size() != 1) {
677 qCWarning(ActuatorsLog) <<
"Invalid mixer param config:" << actuator.fixedCount <<
"," << mixerParameter.values.size();
681 actuator.perItemParameters.append(mixerParameter);
685 if (actuatorJsonVal[
"item-label-prefix"].isString()) {
686 actuator.itemLabelPrefix.append(actuatorJsonVal[
"item-label-prefix"].toString());
688 QJsonArray itemLabelPrefixJson = actuatorJsonVal[
"item-label-prefix"].toArray();
689 for (
const auto&& itemLabelPrefix : itemLabelPrefixJson) {
690 actuator.itemLabelPrefix.append(itemLabelPrefix.toString());
692 if (actuator.fixedCount != actuator.itemLabelPrefix.size() && actuator.itemLabelPrefix.size() > 1) {
693 qCWarning(ActuatorsLog) <<
"Invalid mixer config (item-label-prefix):" << actuator.fixedCount <<
","
694 << actuator.itemLabelPrefix.size();
698 option.actuators.append(actuator);
700 mixerOptions.append(option);
703 QList<Mixer::Rule> rules;
704 QJsonValue mixerRulesJson = mixerJson.toObject().value(
"rules");
705 QJsonArray mixerRulesJsonArr = mixerRulesJson.toArray();
706 for (
const auto&& mixerRuleJson : mixerRulesJsonArr) {
707 QJsonValue mixerRule = mixerRuleJson.toObject();
711 QJsonArray identifiersJson = mixerRule[
"apply-identifiers"].toArray();
712 for (
const auto&& identifierJson : identifiersJson) {
713 rule.applyIdentifiers.append(identifierJson.toString());
716 QJsonObject itemsJson = mixerRule[
"items"].toObject();
717 for (
const auto& itemKey : itemsJson.keys()) {
719 int key = itemKey.toInt(&ok);
721 QJsonArray itemsArr = itemsJson.value(itemKey).toArray();
722 QList<Mixer::Rule::RuleItem> items{};
723 for (
const auto&& itemJson : itemsArr) {
724 QJsonObject itemObj = itemJson.toObject();
727 if (itemObj.contains(
"min")) {
729 item.min = itemObj[
"min"].toDouble();
731 if (itemObj.contains(
"max")) {
733 item.max = itemObj[
"max"].toDouble();
735 if (itemObj.contains(
"default")) {
736 item.hasDefault =
true;
737 item.defaultVal = itemObj[
"default"].toDouble();
739 item.hidden = itemObj[
"hidden"].toBool(
false);
740 item.disabled = itemObj[
"disabled"].toBool(
false);
743 if (items.size() == rule.applyIdentifiers.size()) {
744 rule.items[key] = items;
746 qCWarning(ActuatorsLog) <<
"Rules: unexpected num items in " << itemsArr <<
"expected:" << rule.applyIdentifiers.size();
753 _mixer.
reset(actuatorTypes, mixerOptions, outputFunctions, rules);
758Fact* Actuators::getFact(
const QString& paramName)
761 qCDebug(ActuatorsLog) <<
"Mixer: Param does not exist:" << paramName;
769void Actuators::subscribeFact(
Fact* fact)
771 if (fact && !_subscribedFacts.contains(fact)) {
773 _subscribedFacts.insert(fact);
791 qWarning() <<
"Actuator type 'motor' not found";
793 ret = _motorAssignment.
initAssignment(_selectedActuatorOutput, iter->functionMin, numMotors);
798void Actuators::highlightActuators(
bool highlight)
801 QList<ActuatorGeometry>& actuators = provider->
actuators();
802 for (
auto& actuator : actuators) {
804 actuator.renderOptions.highlight = highlight;
807 updateGeometryImage();
812 highlightActuators(
true);
813 _motorAssignment.
start();
817 _motorAssignment.
abort();
#define QGC_LOGGING_CATEGORY(name, categoryStr)
void addConfigParam(ConfigParameter *param)
void addChannel(ActuatorOutputChannel *channel)
QmlObjectListModel * channelConfigs()
void addChannelConfig(ChannelConfig *channelConfig)
void addAction(const ActuatorActions::Config &action)
QmlObjectListModel * subgroups()
ConfigParameter * enableParam() const
void forEachOutputFunction(std::function< void(ActuatorOutputSubgroup *, ChannelConfigInstance *, Fact *)> callback) const
void getAllChannelFunctions(QList< Fact * > &allFunctions) const
void addConfigParam(ConfigParameter *param)
void addNote(const QString ¬e)
bool hasExistingOutputFunctionParams() const
void addSubgroup(ActuatorOutputSubgroup *subgroup)
Function
Describes the meaning of the parameter.
void updateFunctions(const QList< Actuator * > &actuators)
void actuatorOutputsChanged()
Q_INVOKABLE bool initMotorAssignment()
void hasUnsetRequiredFunctionsChanged()
Q_INVOKABLE void abortMotorAssignment()
bool isMultirotor() const
void imageRefreshFlagChanged()
void motorAssignmentMessageChanged()
Q_INVOKABLE void selectActuatorOutput(int index)
Q_INVOKABLE void startMotorAssignment()
Q_INVOKABLE void imageClicked(QSizeF displaySize, float x, float y)
void motorAssignmentActiveChanged()
ActuatorOutputs::ActuatorOutput * selectedActuatorOutput() const
void motorAssignmentEnabledChanged()
void load(const QString &json_file)
void selectedActuatorOutputChanged()
A Fact is used to hold a single value within the system.
void setEnumInfo(const QStringList &strings, const QVariantList &values)
Generally this is done during parsing. But if you know what you are doing, you can.
void rawValueChanged(const QVariant &value)
QStringList enumStrings() const
QVariant rawValue() const
Value after translation.
static VehicleGeometryImageProvider * instance()
QList< ActuatorGeometry > & actuators()
int getHighlightedMotorIndexAtPos(const QSizeF &displaySize, const QPointF &position)
bool getGeometry(const ActuatorTypes &actuatorTypes, const MixerOption::ActuatorGroup &group, ActuatorGeometry &geometry) const
const MixerOption::ActuatorGroup & group() const
QmlObjectListModel * channels()
const ActuatorTypes & actuatorTypes() const
QString configuredType() const
QmlObjectListModel * groups()
const QMap< int, OutputFunction > & functions() const
void reset(const ActuatorTypes &actuatorTypes, const MixerOptions &mixerOptions, const QMap< int, OutputFunction > &functions, const Rules &rules)
QString getSpecificLabelForFunction(int function) const
void geometryParamChanged()
QSet< int > getFunctions(bool requiredOnly) const
void selectMotor(int motorIndex)
bool initAssignment(int selectedActuatorIdx, int firstMotorsFunction, int numMotors)
bool parameterExists(int componentId, const QString ¶mName) const
Fact * getParameter(int componentId, const QString ¶mName)
bool parametersReady() const
static constexpr int defaultComponentId
void append(QObject *object)
Caller maintains responsibility for object ownership and deletion.
Q_INVOKABLE QObject * get(int index)
QObject * removeAt(int index)
int count() const override final
void clearAndDeleteContents() override final
Clears the list and calls deleteLater on each entry.
ParameterManager * parameterManager()
@ SpinDirection
CCW = true or 1.
QList< MixerOption > MixerOptions
QMap< QString, ActuatorType > ActuatorTypes
key is the group name, where 'DEFAULT' is the default group
@ set3DModeOn
motors: enable 3D mode (reversible)
@ setSpinDirection2
motors: set spin direction 2
@ set3DModeOff
motors: disable 3D mode (reversible)
@ setSpinDirection1
motors: set spin direction 1
static Type typeFromStr(const QString &type)
void parse(const QJsonValue &jsonValue)
QString name
vehicle parameter name, this may have an index in the form '${i}'