17#include <QtCore/QCoreApplication>
21#include <QtCore/QSettings>
22#include <QtCore/QThread>
27static QDebug operator<<(QDebug debug,
Joystick::ButtonEvent_t event)
35 return debug << static_cast<int>(event);
40static constexpr const char* kButtonActionArrayGroup =
"JoystickButtonActionSettingsArray";
41static constexpr const char* kButtonActionNameKey =
"actionName";
42static constexpr const char* kButtonRepeatKey =
"repeat";
43static constexpr const char* kAxisSettingsArrayGroup =
"JoystickAxisSettingsArray";
44static constexpr const char* kAxisFunctionKey =
"function";
45static constexpr const char* kAxisMinKey =
"min";
46static constexpr const char* kAxisMaxKey =
"max";
47static constexpr const char* kAxisCenterKey =
"center";
48static constexpr const char* kAxisDeadbandKey =
"deadband";
49static constexpr const char* kAxisReversedKey =
"reversed";
55 : actionName(actionName_)
58 qCDebug(JoystickVerboseLog) <<
this;
63 , _actionName(actionName_)
64 , _onDown(std::move(onDown_))
65 , _onRepeat(std::move(onRepeat_))
66 , _onUp(std::move(onUp_))
68 qCDebug(JoystickVerboseLog) <<
this;
73Joystick::Joystick(
const QString &name,
int axisCount,
int buttonCount,
int hatCount, QObject *parent)
76 , _axisCount(axisCount)
77 , _buttonCount(buttonCount)
79 , _hatButtonCount(4 * hatCount)
80 , _totalButtonCount(_buttonCount + _hatButtonCount)
81 , _rgCalibration(_axisCount)
82 , _buttonEventStates(_totalButtonCount)
83 , _assignedButtonActions(_totalButtonCount, nullptr)
87 , _joystickSettings(name, _axisCount, _totalButtonCount)
93 <<
"hatCount:" << hatCount
96 if (QCoreApplication *
const app = QCoreApplication::instance()) {
97 QThread *
const guiThread = app->thread();
98 if (_joystickSettings.thread() != guiThread) {
99 _joystickSettings.moveToThread(guiThread);
102 const auto ensureFactThread = [guiThread](
Fact *fact) {
103 if (fact && fact->thread() != guiThread) {
104 fact->moveToThread(guiThread);
108 ensureFactThread(_joystickSettings.calibrated());
109 ensureFactThread(_joystickSettings.circleCorrection());
110 ensureFactThread(_joystickSettings.useDeadband());
111 ensureFactThread(_joystickSettings.negativeThrust());
112 ensureFactThread(_joystickSettings.throttleSmoothing());
113 ensureFactThread(_joystickSettings.axisFrequencyHz());
114 ensureFactThread(_joystickSettings.buttonFrequencyHz());
115 ensureFactThread(_joystickSettings.throttleModeCenterZero());
116 ensureFactThread(_joystickSettings.transmitterMode());
117 ensureFactThread(_joystickSettings.exponentialPct());
118 ensureFactThread(_joystickSettings.enableManualControlPitchExtension());
119 ensureFactThread(_joystickSettings.enableManualControlRollExtension());
120 ensureFactThread(_joystickSettings.enableAdditionalAxis1());
121 ensureFactThread(_joystickSettings.enableAdditionalAxis2());
122 ensureFactThread(_joystickSettings.enableAdditionalAxis3());
123 ensureFactThread(_joystickSettings.enableAdditionalAxis4());
124 ensureFactThread(_joystickSettings.enableAdditionalAxis5());
125 ensureFactThread(_joystickSettings.enableAdditionalAxis6());
126 ensureFactThread(_joystickSettings.additionalAxesFunction());
130 connect(_joystickSettings.enableManualControlPitchExtension(), &
Fact::rawValueChanged,
this, [
this]() {
131 _joystickSettings.calibrated()->setRawValue(false);
133 connect(_joystickSettings.enableManualControlRollExtension(), &
Fact::rawValueChanged,
this, [
this]() {
134 _joystickSettings.calibrated()->setRawValue(false);
137 _joystickSettings.calibrated()->setRawValue(false);
140 _joystickSettings.calibrated()->setRawValue(false);
143 _joystickSettings.calibrated()->setRawValue(false);
146 _joystickSettings.calibrated()->setRawValue(false);
149 _joystickSettings.calibrated()->setRawValue(false);
152 _joystickSettings.calibrated()->setRawValue(false);
155 _resetFunctionToAxisMap();
156 _resetAxisCalibrationData();
157 _resetButtonActionData();
158 _resetButtonEventStates();
162 _loadFromSettingsIntoCalibrationData();
167 _exitPollingThread =
true;
169 if (QThread::currentThread() ==
this) {
170 qCWarning(JoystickLog) <<
"Skipping wait() on joystick thread";
176 _resetButtonActionData();
180void Joystick::_resetFunctionToAxisMap()
182 _axisFunctionToJoystickAxisMap.clear();
184 _axisFunctionToJoystickAxisMap[
static_cast<AxisFunction_t>(i)] = kJoystickAxisNotAssigned;
188void Joystick::_resetAxisCalibrationData()
190 _resetFunctionToAxisMap();
191 for (
int axis = 0; axis <
_axisCount; axis++) {
192 auto& calibration = _rgCalibration[axis];
197void Joystick::_resetButtonActionData()
199 for (
int i = 0; i < _assignedButtonActions.count(); i++) {
200 delete _assignedButtonActions[i];
201 _assignedButtonActions[i] =
nullptr;
205void Joystick::_resetButtonEventStates()
207 for (
int i = 0; i < _buttonEventStates.count(); i++) {
212void Joystick::_loadButtonSettings()
214 const QString actionNameKey = QString::fromLatin1(kButtonActionNameKey);
215 const QString repeatKey = QString::fromLatin1(kButtonRepeatKey);
217 QSettings buttonSettings;
218 buttonSettings.beginGroup(_joystickSettings.
settingsGroup());
220 if (buttonSettings.childGroups().contains(QString::fromLatin1(kButtonActionArrayGroup))) {
221 buttonSettings.beginGroup(QString::fromLatin1(kButtonActionArrayGroup));
223 qCDebug(JoystickLog) <<
"Button Actions:";
225 bool foundButton =
false;
226 for (
int buttonIndex = 0; buttonIndex < _totalButtonCount; buttonIndex++) {
227 if (!buttonSettings.childGroups().contains(QString::number(buttonIndex))) {
231 buttonSettings.beginGroup(QString::number(buttonIndex));
232 const QString actionName = buttonSettings.value(actionNameKey).toString();
233 const bool repeat = buttonSettings.value(repeatKey,
false).toBool();
234 buttonSettings.endGroup();
236 if (actionName.isEmpty()) {
237 qCWarning(JoystickLog)
239 <<
"button:" << buttonIndex
240 <<
"has empty action name, clearing action from data";
241 buttonSettings.remove(QString::number(buttonIndex));
244 if (_findAvailableButtonActionIndex(actionName) == -1) {
252 <<
"button:" << buttonIndex
253 <<
"action name not currently available:" << actionName
254 <<
", keeping assignment";
255 _addAvailableButtonActionIfMissing(actionName);
257 if (actionName == _buttonActionNone) {
258 buttonSettings.remove(QString::number(buttonIndex));
263 _assignedButtonActions[buttonIndex] = buttonAction;
264 _assignedButtonActions[buttonIndex]->buttonElapsedTimer.start();
269 <<
"button:" << buttonIndex
270 <<
"actionName:" << actionName
271 <<
"repeat:" << repeat;
274 buttonSettings.endGroup();
277 buttonSettings.remove(QString::fromLatin1(kButtonActionArrayGroup));
282void Joystick::_foundInvalidAxisSettingsCleanup()
284 _clearAxisSettings();
285 _resetAxisCalibrationData();
286 _joystickSettings.calibrated()->setRawValue(
false);
289void Joystick::_loadAxisSettings(
bool joystickCalibrated,
int transmitterMode)
291 QSettings axisSettings;
296 if (!joystickCalibrated) {
297 _clearAxisSettings();
298 _resetAxisCalibrationData();
302 const QString functionKey = QString::fromLatin1(kAxisFunctionKey);
303 const QString minKey = QString::fromLatin1(kAxisMinKey);
304 const QString maxKey = QString::fromLatin1(kAxisMaxKey);
305 const QString centerKey = QString::fromLatin1(kAxisCenterKey);
306 const QString deadbandKey = QString::fromLatin1(kAxisDeadbandKey);
307 const QString reversedKey = QString::fromLatin1(kAxisReversedKey);
309 qCDebug(JoystickLog) <<
"Axis Settings:";
311 axisSettings.beginGroup(QString::fromLatin1(kAxisSettingsArrayGroup));
312 for (
int axis = 0; axis <
_axisCount; axis++) {
313 if (!axisSettings.childGroups().contains(QString::number(axis))) {
317 <<
"no settings found, skipping";
321 axisSettings.beginGroup(QString::number(axis));
323 auto& axisCalibration = _rgCalibration[axis];
325 const int axisFunction = axisSettings.value(functionKey,
maxAxisFunction).toInt();
327 qCWarning(JoystickLog) <<
"Invalid function" << axisFunction <<
"for axis" << axis;
328 axisSettings.endGroup();
333 axisSettings.endGroup();
336 _setJoystickAxisForAxisFunction(
static_cast<AxisFunction_t>(axisFunction), axis);
338 axisCalibration.center = axisSettings.value(centerKey, axisCalibration.center).toInt();
339 axisCalibration.min = axisSettings.value(minKey, axisCalibration.min).toInt();
340 axisCalibration.max = axisSettings.value(maxKey, axisCalibration.max).toInt();
341 axisCalibration.deadband = axisSettings.value(deadbandKey, axisCalibration.deadband).toInt();
342 axisCalibration.reversed = axisSettings.value(reversedKey, axisCalibration.reversed).toBool();
347 <<
"min:" << axisCalibration.min
348 <<
"max:" << axisCalibration.max
349 <<
"center:" << axisCalibration.center
350 <<
"reversed:" << axisCalibration.reversed
351 <<
"deadband:" << axisCalibration.deadband
354 axisSettings.endGroup();
356 axisSettings.endGroup();
359 bool rollFunctionNotAssigned = _axisFunctionToJoystickAxisMap[
rollFunction] == kJoystickAxisNotAssigned;
360 if (rollFunctionNotAssigned) {
361 qCWarning(JoystickLog) <<
"Roll axis function not assigned";
363 bool pitchFunctionNotAssigned = _axisFunctionToJoystickAxisMap[
pitchFunction] == kJoystickAxisNotAssigned;
364 if (pitchFunctionNotAssigned) {
365 qCWarning(JoystickLog) <<
"Pitch axis function not assigned";
367 bool yawFunctionNotAssigned = _axisFunctionToJoystickAxisMap[
yawFunction] == kJoystickAxisNotAssigned;
368 if (yawFunctionNotAssigned) {
369 qCWarning(JoystickLog) <<
"Yaw axis function not assigned";
371 bool throttleFunctionNotAssigned = _axisFunctionToJoystickAxisMap[
throttleFunction] == kJoystickAxisNotAssigned;
372 if (throttleFunctionNotAssigned) {
373 qCWarning(JoystickLog) <<
"Throttle axis function not assigned";
375 bool pitchExtensionFunctionRequiredButNotAssigned =
false;
376 if (_joystickSettings.enableManualControlPitchExtension()->rawValue().toBool()) {
377 pitchExtensionFunctionRequiredButNotAssigned = _getJoystickAxisForAxisFunction(
pitchExtensionFunction) == kJoystickAxisNotAssigned;
378 if (pitchExtensionFunctionRequiredButNotAssigned) {
379 qCWarning(JoystickLog) <<
"Internal Error: Missing pitch extension axis function mapping!";
382 bool rollExtensionFunctionRequiredButNotAssigned =
false;
383 if (_joystickSettings.enableManualControlRollExtension()->rawValue().toBool()) {
384 rollExtensionFunctionRequiredButNotAssigned = _getJoystickAxisForAxisFunction(
rollExtensionFunction) == kJoystickAxisNotAssigned;
385 if (rollExtensionFunctionRequiredButNotAssigned) {
386 qCWarning(JoystickLog) <<
"Internal Error: Missing roll extension axis function mapping!";
389 bool additionalAxis1FunctionRequiredButNotAssigned =
false;
390 if (_joystickSettings.enableAdditionalAxis1()->rawValue().toBool()) {
391 additionalAxis1FunctionRequiredButNotAssigned = _getJoystickAxisForAxisFunction(
additionalAxis1Function) == kJoystickAxisNotAssigned;
392 if (additionalAxis1FunctionRequiredButNotAssigned) {
393 qCWarning(JoystickLog) <<
"Internal Error: Missing additional axis 1 function mapping!";
396 bool additionalAxis2FunctionRequiredButNotAssigned =
false;
397 if (_joystickSettings.enableAdditionalAxis2()->rawValue().toBool()) {
398 additionalAxis2FunctionRequiredButNotAssigned = _getJoystickAxisForAxisFunction(
additionalAxis2Function) == kJoystickAxisNotAssigned;
399 if (additionalAxis2FunctionRequiredButNotAssigned) {
400 qCWarning(JoystickLog) <<
"Internal Error: Missing additional axis 2 function mapping!";
403 bool additionalAxis3FunctionRequiredButNotAssigned =
false;
404 if (_joystickSettings.enableAdditionalAxis3()->rawValue().toBool()) {
405 additionalAxis3FunctionRequiredButNotAssigned = _getJoystickAxisForAxisFunction(
additionalAxis3Function) == kJoystickAxisNotAssigned;
406 if (additionalAxis3FunctionRequiredButNotAssigned) {
407 qCWarning(JoystickLog) <<
"Internal Error: Missing additional axis 3 function mapping!";
410 bool additionalAxis4FunctionRequiredButNotAssigned =
false;
411 if (_joystickSettings.enableAdditionalAxis4()->rawValue().toBool()) {
412 additionalAxis4FunctionRequiredButNotAssigned = _getJoystickAxisForAxisFunction(
additionalAxis4Function) == kJoystickAxisNotAssigned;
413 if (additionalAxis4FunctionRequiredButNotAssigned) {
414 qCWarning(JoystickLog) <<
"Internal Error: Missing additional axis 4 function mapping!";
417 bool additionalAxis5FunctionRequiredButNotAssigned =
false;
418 if (_joystickSettings.enableAdditionalAxis5()->rawValue().toBool()) {
419 additionalAxis5FunctionRequiredButNotAssigned = _getJoystickAxisForAxisFunction(
additionalAxis5Function) == kJoystickAxisNotAssigned;
420 if (additionalAxis5FunctionRequiredButNotAssigned) {
421 qCWarning(JoystickLog) <<
"Internal Error: Missing additional axis 5 function mapping!";
424 bool additionalAxis6FunctionRequiredButNotAssigned =
false;
425 if (_joystickSettings.enableAdditionalAxis6()->rawValue().toBool()) {
426 additionalAxis6FunctionRequiredButNotAssigned = _getJoystickAxisForAxisFunction(
additionalAxis6Function) == kJoystickAxisNotAssigned;
427 if (additionalAxis6FunctionRequiredButNotAssigned) {
428 qCWarning(JoystickLog) <<
"Internal Error: Missing additional axis 6 function mapping!";
431 if (rollFunctionNotAssigned || pitchFunctionNotAssigned || yawFunctionNotAssigned || throttleFunctionNotAssigned ||
432 pitchExtensionFunctionRequiredButNotAssigned || rollExtensionFunctionRequiredButNotAssigned ||
433 additionalAxis1FunctionRequiredButNotAssigned || additionalAxis2FunctionRequiredButNotAssigned || additionalAxis3FunctionRequiredButNotAssigned ||
434 additionalAxis4FunctionRequiredButNotAssigned || additionalAxis5FunctionRequiredButNotAssigned || additionalAxis6FunctionRequiredButNotAssigned) {
435 qCWarning(JoystickLog) <<
"Missing control axis function(s), resetting all axis settings, marking joystick as uncalibrated and disabled";
436 _resetAxisCalibrationData();
437 _clearAxisSettings();
438 _joystickSettings.calibrated()->setRawValue(
false);
445 _remapFunctionsInFunctionMapToNewTransmittedMode(2, transmitterMode);
448void Joystick::_loadFromSettingsIntoCalibrationData()
450 _resetAxisCalibrationData();
451 _resetButtonActionData();
455 bool calibrated = _joystickSettings.calibrated()->rawValue().toBool();
456 int transmitterMode = _joystickSettings.transmitterMode()->rawValue().toInt();
458 qCDebug(JoystickLog) <<
name();
459 qCDebug(JoystickLog) <<
" calibrated:" << _joystickSettings.calibrated()->rawValue().toBool();
460 qCDebug(JoystickLog) <<
" throttleSmoothing:" << _joystickSettings.throttleSmoothing()->rawValue().toBool();
461 qCDebug(JoystickLog) <<
" axisFrequencyHz:" << _joystickSettings.axisFrequencyHz()->rawValue().toDouble();
462 qCDebug(JoystickLog) <<
" buttonFrequencyHz:" << _joystickSettings.buttonFrequencyHz()->rawValue().toDouble();
463 qCDebug(JoystickLog) <<
" throttleModeCenterZero:" << _joystickSettings.throttleModeCenterZero()->rawValue().toBool();
464 qCDebug(JoystickLog) <<
" negativeThrust:" << _joystickSettings.negativeThrust()->rawValue().toBool();
465 qCDebug(JoystickLog) <<
" circleCorrection:" << _joystickSettings.circleCorrection()->rawValue().toBool();
466 qCDebug(JoystickLog) <<
" exponentialPct:" << _joystickSettings.exponentialPct()->rawValue().toDouble();
467 qCDebug(JoystickLog) <<
" useDeadband:" << _joystickSettings.useDeadband()->rawValue().toBool();
468 qCDebug(JoystickLog) <<
" transmitterMode:" << transmitterMode;
469 qCDebug(JoystickLog) <<
" enableManualControlPitchExtension:" << _joystickSettings.enableManualControlPitchExtension()->rawValue().toBool();
470 qCDebug(JoystickLog) <<
" enableManualControlRollExtension:" << _joystickSettings.enableManualControlRollExtension()->rawValue().toBool();
471 qCDebug(JoystickLog) <<
" additionalAxesFunction:" << (_joystickSettings.additionalAxesFunction()->rawValue().toUInt() == 1 ?
"RC_CHANNELS_OVERRIDE" :
"MANUAL_CONTROL");
472 qCDebug(JoystickLog) <<
" enableAdditionalAxis1:" << _joystickSettings.enableAdditionalAxis1()->rawValue().toBool();
473 qCDebug(JoystickLog) <<
" enableAdditionalAxis2:" << _joystickSettings.enableAdditionalAxis2()->rawValue().toBool();
474 qCDebug(JoystickLog) <<
" enableAdditionalAxis3:" << _joystickSettings.enableAdditionalAxis3()->rawValue().toBool();
475 qCDebug(JoystickLog) <<
" enableAdditionalAxis4:" << _joystickSettings.enableAdditionalAxis4()->rawValue().toBool();
476 qCDebug(JoystickLog) <<
" enableAdditionalAxis5:" << _joystickSettings.enableAdditionalAxis5()->rawValue().toBool();
477 qCDebug(JoystickLog) <<
" enableAdditionalAxis6:" << _joystickSettings.enableAdditionalAxis6()->rawValue().toBool();
479 _loadAxisSettings(calibrated, transmitterMode);
480 _loadButtonSettings();
483void Joystick::_saveAxisSettings(
int transmitterMode)
485 qCDebug(JoystickLog) <<
name();
489 _remapFunctionsInFunctionMapToNewTransmittedMode(transmitterMode, 2);
491 QSettings axisSettings;
493 axisSettings.beginGroup(QString::fromLatin1(kAxisSettingsArrayGroup));
495 const QString functionKey = QString::fromLatin1(kAxisFunctionKey);
496 const QString minKey = QString::fromLatin1(kAxisMinKey);
497 const QString maxKey = QString::fromLatin1(kAxisMaxKey);
498 const QString centerKey = QString::fromLatin1(kAxisCenterKey);
499 const QString deadbandKey = QString::fromLatin1(kAxisDeadbandKey);
500 const QString reversedKey = QString::fromLatin1(kAxisReversedKey);
502 for (
int axis = 0; axis <
_axisCount; axis++) {
508 AxisCalibration_t *
const calibration = &_rgCalibration[axis];
509 axisSettings.beginGroup(QString::number(axis));
511 axisSettings.setValue(centerKey, calibration->center);
512 axisSettings.setValue(minKey, calibration->min);
513 axisSettings.setValue(maxKey, calibration->max);
514 axisSettings.setValue(deadbandKey, calibration->deadband);
515 axisSettings.setValue(reversedKey, calibration->reversed);
516 axisSettings.setValue(functionKey,
static_cast<int>(function));
521 <<
"min:" << calibration->min
522 <<
"max:" << calibration->max
523 <<
"center:" << calibration->center
524 <<
"reversed:" << calibration->reversed
525 <<
"deadband:" << calibration->deadband
528 axisSettings.endGroup();
531 axisSettings.endGroup();
532 axisSettings.endGroup();
535 _remapFunctionsInFunctionMapToNewTransmittedMode(2, transmitterMode);
538void Joystick::_saveButtonSettings()
540 qCDebug(JoystickLog) <<
name();
542 const QString actionNameKey = QString::fromLatin1(kButtonActionNameKey);
543 const QString repeatKey = QString::fromLatin1(kButtonRepeatKey);
545 QSettings buttonSettings;
546 buttonSettings.beginGroup(_joystickSettings.
settingsGroup());
547 buttonSettings.beginGroup(QString::fromLatin1(kButtonActionArrayGroup));
549 bool anyButtonsSaved =
false;
550 for (
int buttonIndex = 0; buttonIndex < _totalButtonCount; buttonIndex++) {
554 buttonSettings.beginGroup(QString::number(buttonIndex));
555 buttonSettings.setValue(actionNameKey, buttonAction->
actionName);
556 buttonSettings.setValue(repeatKey, buttonAction->
repeat);
557 buttonSettings.endGroup();
559 anyButtonsSaved =
true;
563 <<
"button:" << buttonIndex
565 <<
"repeat:" << buttonAction->
repeat;
568 buttonSettings.remove(QString::number(buttonIndex));
572 buttonSettings.endGroup();
573 buttonSettings.endGroup();
575 if (!anyButtonsSaved) {
576 qCDebug(JoystickLog) <<
" No button actions to save, cleared button action settings.";
577 _clearButtonSettings();
581void Joystick::_clearAxisSettings()
583 QSettings axisSettings;
585 axisSettings.remove(QString::fromLatin1(kAxisSettingsArrayGroup));
586 axisSettings.endGroup();
589void Joystick::_clearButtonSettings()
591 QSettings buttonSettings;
592 buttonSettings.beginGroup(_joystickSettings.
settingsGroup());
593 buttonSettings.remove(QString::fromLatin1(kButtonActionArrayGroup));
594 buttonSettings.endGroup();
597void Joystick::_saveFromCalibrationDataIntoSettings()
599 bool calibrated = _joystickSettings.calibrated()->rawValue().toBool();
600 int transmitterMode = _joystickSettings.transmitterMode()->rawValue().toInt();
602 qCDebug(JoystickLog) <<
name();
603 qCDebug(JoystickLog) <<
" calibrated:" << calibrated;
604 qCDebug(JoystickLog) <<
" throttleSmoothing:" << _joystickSettings.throttleSmoothing()->rawValue().toBool();
605 qCDebug(JoystickLog) <<
" axisFrequencyHz:" << _joystickSettings.axisFrequencyHz()->rawValue().toDouble();
606 qCDebug(JoystickLog) <<
" buttonFrequencyHz:" << _joystickSettings.buttonFrequencyHz()->rawValue().toDouble();
607 qCDebug(JoystickLog) <<
" throttleModeCenterZero:" << _joystickSettings.throttleModeCenterZero()->rawValue().toBool();
608 qCDebug(JoystickLog) <<
" negativeThrust:" << _joystickSettings.negativeThrust()->rawValue().toBool();
609 qCDebug(JoystickLog) <<
" circleCorrection:" << _joystickSettings.circleCorrection()->rawValue().toBool();
610 qCDebug(JoystickLog) <<
" exponentialPct:" << _joystickSettings.exponentialPct()->rawValue().toDouble();
611 qCDebug(JoystickLog) <<
" useDeadband:" << _joystickSettings.useDeadband()->rawValue().toBool();
612 qCDebug(JoystickLog) <<
" enableManualControlPitchExtension:" << _joystickSettings.enableManualControlPitchExtension()->rawValue().toBool();
613 qCDebug(JoystickLog) <<
" enableManualControlRollExtension:" << _joystickSettings.enableManualControlRollExtension()->rawValue().toBool();
614 qCDebug(JoystickLog) <<
" additionalAxesFunction:" << (_joystickSettings.additionalAxesFunction()->rawValue().toUInt() == 1 ?
"RC_CHANNELS_OVERRIDE" :
"MANUAL_CONTROL");
615 qCDebug(JoystickLog) <<
" enableAdditionalAxis1:" << _joystickSettings.enableAdditionalAxis1()->rawValue().toBool();
616 qCDebug(JoystickLog) <<
" enableAdditionalAxis2:" << _joystickSettings.enableAdditionalAxis2()->rawValue().toBool();
617 qCDebug(JoystickLog) <<
" enableAdditionalAxis3:" << _joystickSettings.enableAdditionalAxis3()->rawValue().toBool();
618 qCDebug(JoystickLog) <<
" enableAdditionalAxis4:" << _joystickSettings.enableAdditionalAxis4()->rawValue().toBool();
619 qCDebug(JoystickLog) <<
" enableAdditionalAxis5:" << _joystickSettings.enableAdditionalAxis5()->rawValue().toBool();
620 qCDebug(JoystickLog) <<
" enableAdditionalAxis6:" << _joystickSettings.enableAdditionalAxis6()->rawValue().toBool();
621 qCDebug(JoystickLog) <<
" transmitterMode:" << transmitterMode;
623 _clearAxisSettings();
625 _saveAxisSettings(transmitterMode);
628 _clearButtonSettings();
629 _saveButtonSettings();
632void Joystick::_remapFunctionsInFunctionMapToNewTransmittedMode(
int fromMode,
int toMode)
634 static constexpr const int modeCount = 4;
635 static constexpr const int attitudeControlCount = 4;
636 static constexpr const AxisFunction_t mapping[modeCount][attitudeControlCount] = {
644 AxisFunctionMap_t tempMap = _axisFunctionToJoystickAxisMap;
646 for (
int i=0; i<attitudeControlCount; i++) {
647 auto fromAxisFunction = mapping[fromMode - 1][i];
648 auto toAxisFunction = mapping[toMode - 1][i];
650 tempMap[toAxisFunction] = _axisFunctionToJoystickAxisMap[fromAxisFunction];
655 _axisFunctionToJoystickAxisMap[axisFunction] = tempMap[axisFunction];
661 bool openFailed =
false;
662 bool updateFailed =
false;
665 qCWarning(JoystickLog) <<
"Failed to open joystick:" <<
_name;
670 _axisElapsedTimer.start();
672 for (
int buttonIndex = 0; buttonIndex < _totalButtonCount; buttonIndex++) {
673 if (_assignedButtonActions[buttonIndex]) {
674 _assignedButtonActions[buttonIndex]->buttonElapsedTimer.start();
678 while (!_exitPollingThread) {
680 qCWarning(JoystickLog) <<
"Joystick disconnected or update failed:" <<
_name;
691 const double axisFrequencyHz = _joystickSettings.axisFrequencyHz()->rawValue().toDouble();
692 const double buttonFrequencyHz = _joystickSettings.buttonFrequencyHz()->rawValue().toDouble();
694 const int sleep = qMin(
static_cast<int>(1000.0 / axisFrequencyHz),
static_cast<int>(1000.0 / buttonFrequencyHz)) / 2;
695 QThread::msleep(sleep);
701 if ((openFailed || updateFailed) && !_exitPollingThread) {
702 qCDebug(JoystickLog) <<
"Triggering joystick rescan after failure";
707void Joystick::_updateButtonEventState(
int buttonIndex,
const bool buttonPressed, ButtonEvent_t &buttonEventState)
711 qCDebug(JoystickLog) <<
"Button" << buttonIndex <<
"down transition";
714 qCDebug(JoystickLog) <<
"Button" << buttonIndex <<
"repeat";
719 qCDebug(JoystickLog) <<
"Button" << buttonIndex <<
"up transition";
722 qCDebug(JoystickLog) <<
"Button" << buttonIndex <<
"none";
728void Joystick::_updateButtonEventStates(QVector<ButtonEvent_t> &buttonEventStates)
730 if (buttonEventStates.size() < _totalButtonCount) {
731 qCWarning(JoystickLog) <<
"Internal Error: buttonEventStates size incorrect!";
736 for (
int buttonIndex = 0; buttonIndex <
_buttonCount; buttonIndex++) {
737 const bool buttonIsPressed = _getButton(buttonIndex);
738 _updateButtonEventState(buttonIndex, buttonIsPressed, buttonEventStates[buttonIndex]);
742 const int numHatButtons = 4;
744 for (
int hatIndex = 0; hatIndex <
_hatCount; hatIndex++) {
745 for (
int hatButtonIndex = 0; hatButtonIndex < numHatButtons; hatButtonIndex++) {
746 const bool buttonIsPressed = _getHat(hatIndex, hatButtonIndex);
747 const int currentIndex = nextIndex;
748 _updateButtonEventState(currentIndex, buttonIsPressed, buttonEventStates[currentIndex]);
754void Joystick::_handleButtons()
756 if (_pollingFlags == PollingNone) {
757 qCWarning(JoystickLog) <<
"Internal Error: Joystick not polling!";
761 _updateButtonEventStates(_buttonEventStates);
763 if (_pollingFlags.testFlag(PollingForConfiguration)) {
764 for (
int buttonIndex = 0; buttonIndex < _totalButtonCount; buttonIndex++) {
766 qCDebug(JoystickLog) <<
"Button pressed - button" << buttonIndex;
769 qCDebug(JoystickLog) <<
"Button released - button" << buttonIndex;
773 }
else if (_pollingFlags.testFlag(PollingForVehicle)) {
774 Vehicle *
const vehicle = _pollingVehicle;
776 qCWarning(JoystickLog) <<
"Internal Error: No vehicle for joystick!";
780 qCWarning(JoystickLog) <<
"Internal Error: Joystick not enabled for vehicle!";
783 if (!_joystickSettings.calibrated()->rawValue().toBool()) {
788 const int buttonDelay =
static_cast<int>(1000.0 / _joystickSettings.buttonFrequencyHz()->rawValue().toDouble());
789 QSet<QString> executedActions;
790 for (
int buttonIndex = 0; buttonIndex < _totalButtonCount; buttonIndex++) {
791 if (!_assignedButtonActions[buttonIndex]) {
794 auto assignedAction = _assignedButtonActions[buttonIndex];
796 const QString buttonAction = assignedAction->
actionName;
797 if (buttonAction.isEmpty() || (buttonAction == _buttonActionNone)) {
801 auto buttonEventState = _buttonEventStates[buttonIndex];
803 if (assignedAction->repeat) {
804 if (assignedAction->buttonElapsedTimer.elapsed() > buttonDelay) {
805 assignedAction->buttonElapsedTimer.start();
806 qCDebug(JoystickLog) <<
"Repeat - button:action" << buttonIndex << buttonAction;
808 QMetaObject::invokeMethod(
this, [
this, buttonAction]() {
810 }, Qt::QueuedConnection);
815 QList<int> multiActionButtons = { buttonIndex };
816 bool allActionButtonsPressed =
true;
817 for (
int multiIndex = 0; multiIndex < _totalButtonCount; multiIndex++) {
818 if (multiIndex == buttonIndex) {
821 if (_assignedButtonActions[multiIndex] && (_assignedButtonActions[multiIndex]->actionName == buttonAction)) {
825 multiActionButtons.append(multiIndex);
828 allActionButtonsPressed =
false;
834 if (!allActionButtonsPressed) {
838 if (multiActionButtons.size() > 1) {
839 qCDebug(JoystickLog) <<
"Multi-button action - buttons:action" << multiActionButtons << buttonAction;
841 qCDebug(JoystickLog) <<
"Action triggered - button:Action" << buttonIndex << buttonAction;
846 if (!executedActions.contains(buttonAction)) {
848 QMetaObject::invokeMethod(
this, [
this, buttonAction]() {
850 }, Qt::QueuedConnection);
851 executedActions.insert(buttonAction);
858 if (!executedActions.contains(buttonAction)) {
860 QMetaObject::invokeMethod(
this, [
this, buttonAction]() {
862 }, Qt::QueuedConnection);
863 executedActions.insert(buttonAction);
871float Joystick::_adjustRange(
int value,
const AxisCalibration_t &calibration,
bool withDeadbands)
873 float valueNormalized;
877 if (calibration.center == calibration.min) {
879 valueNormalized =
static_cast<float>(std::max(0, value - calibration.center));
880 axisLength = calibration.max - calibration.center;
881 }
else if (calibration.center == calibration.max) {
883 valueNormalized =
static_cast<float>(std::max(0, calibration.center - value));
884 axisLength = calibration.center - calibration.min;
885 }
else if (value > calibration.center) {
887 valueNormalized = value - calibration.center;
888 axisLength = calibration.max - calibration.center;
891 valueNormalized = calibration.center - value;
892 axisLength = calibration.center - calibration.min;
895 if (axisLength <= 0.0f) {
901 if (valueNormalized > calibration.deadband) {
902 axisPercent = (valueNormalized - calibration.deadband) / (axisLength - calibration.deadband);
903 }
else if (valueNormalized<-calibration.deadband) {
904 axisPercent = (valueNormalized + calibration.deadband) / (axisLength - calibration.deadband);
909 axisPercent = valueNormalized / axisLength;
912 float correctedValue = axisBasis * axisPercent;
913 if (calibration.reversed) {
914 correctedValue *= -1.0f;
917 return std::max(-1.0f, std::min(correctedValue, 1.0f));
920uint16_t Joystick::_adjustRangeToRcOverridePwm(
int value,
const AxisCalibration_t &calibration,
bool withDeadbands)
922 const float normalizedValue = _adjustRange(value, calibration, withDeadbands);
923 const bool oneSidedAxis = (calibration.center == calibration.min) || (calibration.center == calibration.max);
927 pwmValue = 1000.0f + (std::clamp(normalizedValue, 0.0f, 1.0f) * 1000.0f);
929 pwmValue = 1500.0f + (std::clamp(normalizedValue, -1.0f, 1.0f) * 500.0f);
932 return static_cast<uint16_t
>(std::lround(std::clamp(pwmValue, 1000.0f, 2000.0f)));
935void Joystick::_handleAxis()
937 const int axisDelay =
static_cast<int>(1000.0 / _joystickSettings.axisFrequencyHz()->rawValue().toDouble());
938 if (_axisElapsedTimer.elapsed() <= axisDelay) {
942 _axisElapsedTimer.start();
944 if (_pollingFlags == PollingNone) {
945 qCWarning(JoystickLog) <<
"Internal Error: Joystick not polling!";
949 if (_pollingFlags.testFlag(PollingForConfiguration)) {
953 for (
int axisIndex = 0; axisIndex <
_axisCount; axisIndex++) {
954 channelValues[axisIndex] = _getAxisValue(axisIndex);
957 }
else if (_pollingFlags.testFlag(PollingForVehicle)) {
958 Vehicle *
const vehicle = _pollingVehicle;
960 qCWarning(JoystickLog) <<
"Internal Error: No vehicle for joystick!";
964 qCWarning(JoystickLog) <<
"Internal Error: Joystick not enabled for vehicle!";
967 if (!_joystickSettings.calibrated()->rawValue().toBool()) {
971 bool useDeadband = _joystickSettings.useDeadband()->rawValue().toBool();
972 bool throttleModeCenterZero = _joystickSettings.throttleModeCenterZero()->rawValue().toBool();
973 bool negativeThrust = _joystickSettings.negativeThrust()->rawValue().toBool();
974 bool circleCorrection = _joystickSettings.circleCorrection()->rawValue().toBool();
975 bool throttleSmoothing = _joystickSettings.throttleSmoothing()->rawValue().toBool();
976 bool additionalAxesFunctionIsManualControl = _joystickSettings.additionalAxesFunction()->rawValue().toUInt() == 0;
977 double exponentialPercent = _joystickSettings.exponentialPct()->rawValue().toDouble();
979 if (_getJoystickAxisForAxisFunction(
rollFunction) == kJoystickAxisNotAssigned ||
980 _getJoystickAxisForAxisFunction(
pitchFunction) == kJoystickAxisNotAssigned ||
981 _getJoystickAxisForAxisFunction(
yawFunction) == kJoystickAxisNotAssigned ||
982 _getJoystickAxisForAxisFunction(
throttleFunction) == kJoystickAxisNotAssigned) {
983 qCWarning(JoystickLog) <<
"Internal Error: Missing attitude control axis function mapping!";
986 int axisIndex = _getJoystickAxisForAxisFunction(
rollFunction);
987 float roll = _adjustRange(_getAxisValue(axisIndex), _rgCalibration[axisIndex], useDeadband);
989 float pitch = _adjustRange(_getAxisValue(axisIndex), _rgCalibration[axisIndex], useDeadband);
990 axisIndex = _getJoystickAxisForAxisFunction(
yawFunction);
991 float yaw = _adjustRange(_getAxisValue(axisIndex), _rgCalibration[axisIndex],useDeadband);
993 float throttle = _adjustRange(_getAxisValue(axisIndex), _rgCalibration[axisIndex], throttleModeCenterZero ? useDeadband : false);
995 float pitchExtension = qQNaN();
996 if (_joystickSettings.enableManualControlPitchExtension()->rawValue().toBool()) {
998 qCWarning(JoystickLog) <<
"Internal Error: Missing pitch extension axis function mapping!";
1002 pitchExtension = _adjustRange(_getAxisValue(axisIndex), _rgCalibration[axisIndex], useDeadband);
1004 float rollExtension = qQNaN();
1005 if (_joystickSettings.enableManualControlRollExtension()->rawValue().toBool()) {
1007 qCWarning(JoystickLog) <<
"Internal Error: Missing roll extension axis function mapping!";
1011 rollExtension = _adjustRange(_getAxisValue(axisIndex), _rgCalibration[axisIndex], useDeadband);
1013 std::array<uint16_t, 6> auxRcOverridePwm{};
1014 std::array<bool, 6> auxRcOverrideEnabled{};
1016 float auxManualControl1 = qQNaN();
1017 if (_joystickSettings.enableAdditionalAxis1()->rawValue().toBool()) {
1019 qCWarning(JoystickLog) <<
"Internal Error: Missing additional axis 1 function mapping!";
1023 if (additionalAxesFunctionIsManualControl) {
1024 auxManualControl1 = _adjustRange(_getAxisValue(axisIndex), _rgCalibration[axisIndex], useDeadband);
1026 auxRcOverridePwm[0] = _adjustRangeToRcOverridePwm(_getAxisValue(axisIndex), _rgCalibration[axisIndex], useDeadband);
1027 auxRcOverrideEnabled[0] =
true;
1030 float auxManualControl2 = qQNaN();
1031 if (_joystickSettings.enableAdditionalAxis2()->rawValue().toBool()) {
1033 qCWarning(JoystickLog) <<
"Internal Error: Missing additional axis 2 function mapping!";
1037 if (additionalAxesFunctionIsManualControl) {
1038 auxManualControl2 = _adjustRange(_getAxisValue(axisIndex), _rgCalibration[axisIndex], useDeadband);
1040 auxRcOverridePwm[1] = _adjustRangeToRcOverridePwm(_getAxisValue(axisIndex), _rgCalibration[axisIndex], useDeadband);
1041 auxRcOverrideEnabled[1] =
true;
1044 float auxManualControl3 = qQNaN();
1045 if (_joystickSettings.enableAdditionalAxis3()->rawValue().toBool()) {
1047 qCWarning(JoystickLog) <<
"Internal Error: Missing additional axis 3 function mapping!";
1051 if (additionalAxesFunctionIsManualControl) {
1052 auxManualControl3 = _adjustRange(_getAxisValue(axisIndex), _rgCalibration[axisIndex], useDeadband);
1054 auxRcOverridePwm[2] = _adjustRangeToRcOverridePwm(_getAxisValue(axisIndex), _rgCalibration[axisIndex], useDeadband);
1055 auxRcOverrideEnabled[2] =
true;
1058 float auxManualControl4 = qQNaN();
1059 if (_joystickSettings.enableAdditionalAxis4()->rawValue().toBool()) {
1061 qCWarning(JoystickLog) <<
"Internal Error: Missing additional axis 4 function mapping!";
1065 if (additionalAxesFunctionIsManualControl) {
1066 auxManualControl4 = _adjustRange(_getAxisValue(axisIndex), _rgCalibration[axisIndex], useDeadband);
1068 auxRcOverridePwm[3] = _adjustRangeToRcOverridePwm(_getAxisValue(axisIndex), _rgCalibration[axisIndex], useDeadband);
1069 auxRcOverrideEnabled[3] =
true;
1072 float auxManualControl5 = qQNaN();
1073 if (_joystickSettings.enableAdditionalAxis5()->rawValue().toBool()) {
1075 qCWarning(JoystickLog) <<
"Internal Error: Missing additional axis 5 function mapping!";
1079 if (additionalAxesFunctionIsManualControl) {
1080 auxManualControl5 = _adjustRange(_getAxisValue(axisIndex), _rgCalibration[axisIndex], useDeadband);
1082 auxRcOverridePwm[4] = _adjustRangeToRcOverridePwm(_getAxisValue(axisIndex), _rgCalibration[axisIndex], useDeadband);
1083 auxRcOverrideEnabled[4] =
true;
1086 float auxManualControl6 = qQNaN();
1087 if (_joystickSettings.enableAdditionalAxis6()->rawValue().toBool()) {
1089 qCWarning(JoystickLog) <<
"Internal Error: Missing additional axis 6 function mapping!";
1093 if (additionalAxesFunctionIsManualControl) {
1094 auxManualControl6 = _adjustRange(_getAxisValue(axisIndex), _rgCalibration[axisIndex], useDeadband);
1096 auxRcOverridePwm[5] = _adjustRangeToRcOverridePwm(_getAxisValue(axisIndex), _rgCalibration[axisIndex], useDeadband);
1097 auxRcOverrideEnabled[5] =
true;
1101 if (throttleSmoothing) {
1102 static float throttleSmoothingValue = 0.f;
1103 throttleSmoothingValue += (throttle * (40 / 1000.f));
1104 throttleSmoothingValue = std::max(
static_cast<float>(-1.f), std::min(throttleSmoothingValue,
static_cast<float>(1.f)));
1105 throttle = throttleSmoothingValue;
1108 if (circleCorrection) {
1109 const float roll_limited = std::max(
static_cast<float>(-M_PI_4), std::min(roll,
static_cast<float>(M_PI_4)));
1110 const float pitch_limited = std::max(
static_cast<float>(-M_PI_4), std::min(pitch,
static_cast<float>(M_PI_4)));
1111 const float yaw_limited = std::max(
static_cast<float>(-M_PI_4), std::min(yaw,
static_cast<float>(M_PI_4)));
1112 const float throttle_limited = std::max(
static_cast<float>(-M_PI_4), std::min(throttle,
static_cast<float>(M_PI_4)));
1115 roll = std::max(-1.0f, std::min(tanf(asinf(roll_limited)), 1.0f));
1116 pitch = std::max(-1.0f, std::min(tanf(asinf(pitch_limited)), 1.0f));
1117 yaw = std::max(-1.0f, std::min(tanf(asinf(yaw_limited)), 1.0f));
1118 throttle = std::max(-1.0f, std::min(tanf(asinf(throttle_limited)), 1.0f));
1121 if ( exponentialPercent > 0.0 ) {
1122 const float exponential = -
static_cast<float>(exponentialPercent / 100.0);
1123 roll = -exponential * powf(roll, 3) + ((1 + exponential) * roll);
1124 pitch = -exponential * powf(pitch,3) + ((1 + exponential) * pitch);
1125 yaw = -exponential * powf(yaw, 3) + ((1 + exponential) * yaw);
1131 throttle = std::max(0.0f, throttle);
1134 throttle = (throttle + 1.0f) / 2.0f;
1137 if (additionalAxesFunctionIsManualControl) {
1138 qCDebug(JoystickVerboseLog)
1141 <<
"pitch:" << -pitch
1143 <<
"throttle:" << throttle
1144 <<
"pitchExtension:" << pitchExtension
1145 <<
"rollExtension:" << rollExtension
1146 <<
"additionalAxesFunction: MANUAL_CONTROL"
1147 <<
"aux1:" << auxManualControl1
1148 <<
"aux2:" << auxManualControl2
1149 <<
"aux3:" << auxManualControl3
1150 <<
"aux4:" << auxManualControl4
1151 <<
"aux5:" << auxManualControl5
1152 <<
"aux6:" << auxManualControl6;
1154 qCDebug(JoystickVerboseLog)
1157 <<
"pitch:" << -pitch
1159 <<
"throttle:" << throttle
1160 <<
"pitchExtension:" << pitchExtension
1161 <<
"rollExtension:" << rollExtension
1162 <<
"additionalAxesFunction: RC_CHANNELS_OVERRIDE"
1163 <<
"rcOverridePwm[0]:" << auxRcOverridePwm[0] <<
"enabled:" << auxRcOverrideEnabled[0]
1164 <<
"rcOverridePwm[1]:" << auxRcOverridePwm[1] <<
"enabled:" << auxRcOverrideEnabled[1]
1165 <<
"rcOverridePwm[2]:" << auxRcOverridePwm[2] <<
"enabled:" << auxRcOverrideEnabled[2]
1166 <<
"rcOverridePwm[3]:" << auxRcOverridePwm[3] <<
"enabled:" << auxRcOverrideEnabled[3]
1167 <<
"rcOverridePwm[4]:" << auxRcOverridePwm[4] <<
"enabled:" << auxRcOverrideEnabled[4]
1168 <<
"rcOverridePwm[5]:" << auxRcOverridePwm[5] <<
"enabled:" << auxRcOverrideEnabled[5];
1173 quint64 buttonPressedBits = 0;
1174 for (
int buttonIndex = 0; buttonIndex < _totalButtonCount; buttonIndex++) {
1175 const quint64 buttonBit =
static_cast<quint64
>(1LL << buttonIndex);
1177 buttonPressedBits |= buttonBit;
1183 const uint16_t lowButtons =
static_cast<uint16_t
>(buttonPressedBits & 0xFFFF);
1184 const uint16_t highButtons =
static_cast<uint16_t
>((buttonPressedBits >> 16) & 0xFFFF);
1187 vehicle->
sendJoystickDataThreadSafe(roll, pitch, yaw, throttle, lowButtons, highButtons, pitchExtension, rollExtension, auxManualControl1, auxManualControl2, auxManualControl3, auxManualControl4, auxManualControl5, auxManualControl6);
1192void Joystick::_startPollingForActiveVehicle()
1195 if (!activeVehicle) {
1196 qCWarning(JoystickLog) <<
"Internal Error: No active vehicle to poll for";
1199 _startPollingForVehicle(*activeVehicle);
1202void Joystick::_startPollingForVehicle(
Vehicle &vehicle)
1204 qCDebug(JoystickLog) <<
"Starting joystick polling for vehicle. Vehicle id:" << vehicle.
id() <<
"Current flags:" << _pollingFlagsToString(_pollingFlags);
1206 if (_pollingFlags.testFlag(PollingForVehicle)) {
1207 qCWarning(JoystickLog) <<
"Internal Error: Joystick already polling for vehicle!";
1211 _pollingVehicle = &vehicle;
1212 qCDebug(JoystickLog) <<
"Started joystick polling for vehicle. Vehicle id:" << _pollingVehicle->
id();
1214 _buildAvailableButtonsActionList(_pollingVehicle);
1236 _pollingFlags |= PollingForVehicle;
1237 _startPollingThread();
1240void Joystick::_startPollingForConfiguration()
1242 qCDebug(JoystickLog) <<
"Starting joystick polling for configuration. Current flags:" << _pollingFlagsToString(_pollingFlags);
1244 if (_pollingFlags.testFlag(PollingForConfiguration)) {
1245 qCWarning(JoystickLog) <<
"Internal Error: Joystick already polling for configuration!";
1249 _pollingFlags |= PollingForConfiguration;
1250 _startPollingThread();
1253void Joystick::_stopPollingForConfiguration()
1255 qCDebug(JoystickLog) <<
"Stopping joystick polling for configuration. Current flags:" << _pollingFlagsToString(_pollingFlags);
1257 if (!_pollingFlags.testFlag(PollingForConfiguration)) {
1258 qCWarning(JoystickLog) <<
"Internal Error: Joystick not polling for configuration!";
1263 qCWarning(JoystickLog) <<
"Internal Error: Joystick polling thread not running!";
1266 const PollingFlags remainingFlags = _pollingFlags & ~PollingFlags(PollingForConfiguration);
1271 if (remainingFlags == PollingNone) {
1272 _stopPollingThread();
1275 _pollingFlags = remainingFlags;
1276 qCDebug(JoystickLog) <<
"Remaining flags:" << _pollingFlagsToString(_pollingFlags);
1278 if (remainingFlags.testFlag(PollingForVehicle) && !isRunning()) {
1279 qCWarning(JoystickLog) <<
"Internal Error: Joystick polling not running! Forcing start of polling thread. Continuing polling for vehicle.";
1280 _startPollingThread();
1284void Joystick::_stopAllPollingForVehicle()
1286 qCDebug(JoystickLog) <<
"Stopping all joystick polling for vehicle. Current flags:" << _pollingFlagsToString(_pollingFlags);
1288 if (_pollingVehicle) {
1290 (void) disconnect(
this,
nullptr, _pollingVehicle,
nullptr);
1293 (void) disconnect(
this,
nullptr, gimbal,
nullptr);
1296 qCWarning(JoystickLog) <<
"Joystick polling thread not running even though _pollingVehicle was set.";
1300 const PollingFlags remainingFlags = _pollingFlags & ~PollingFlags(PollingForVehicle);
1305 if (remainingFlags == PollingNone) {
1306 _stopPollingThread();
1312 _pollingVehicle =
nullptr;
1313 _pollingFlags = remainingFlags;
1314 qCDebug(JoystickLog) <<
"Remaining flags:" << _pollingFlagsToString(_pollingFlags);
1316 if (remainingFlags.testFlag(PollingForConfiguration) && !isRunning()) {
1317 qCWarning(JoystickLog) <<
"Joystick polling thread not running but configuration polling flag set. Forcing start.";
1318 _startPollingThread();
1322void Joystick::_startPollingThread()
1325 qCDebug(JoystickLog) <<
"Polling thread already running. Flags:" << _pollingFlagsToString(_pollingFlags);
1327 qCDebug(JoystickLog) <<
"Starting polling thread. Flags:" << _pollingFlagsToString(_pollingFlags);
1328 _exitPollingThread =
false;
1333void Joystick::_stopPollingThread()
1336 qCDebug(JoystickLog) <<
"Stopping polling thread. Flags:" << _pollingFlagsToString(_pollingFlags);
1337 _exitPollingThread =
true;
1338 if (QThread::currentThread() ==
this) {
1339 qCWarning(JoystickLog) <<
"Skipping wait() on joystick thread to avoid deadlock";
1345 qCDebug(JoystickLog) <<
"Polling thread already stopped. Flags:" << _pollingFlagsToString(_pollingFlags);
1349QString Joystick::_pollingFlagsToString(PollingFlags flags)
const
1351 if (flags == PollingNone) {
1352 return QStringLiteral(
"None");
1355 if (flags.testFlag(PollingForVehicle)) {
1356 parts << QStringLiteral(
"PollingForVehicle");
1358 if (flags.testFlag(PollingForConfiguration)) {
1359 parts << QStringLiteral(
"PollingForConfiguration");
1361 return parts.join(QStringLiteral(
"|"));
1366 if (!_validAxis(axis)) {
1370 _rgCalibration[axis] = calibration;
1375 if (!_validAxis(axis)) {
1379 return _rgCalibration[axis];
1382void Joystick::_setJoystickAxisForAxisFunction(AxisFunction_t axisFunction,
int joystickAxis)
1385 qCWarning(JoystickLog) <<
"Internal Error: maxAxisFunction passed to _setJoystickAxisForAxisFunction";
1388 if (joystickAxis == kJoystickAxisNotAssigned) {
1389 qCWarning(JoystickLog) <<
"Internal Error: kJoystickAxisNotAssigned passed to _setJoystickAxisForAxisFunction";
1393 if (!_validAxis(joystickAxis)) {
1397 _axisFunctionToJoystickAxisMap[axisFunction] = joystickAxis;
1400int Joystick::_getJoystickAxisForAxisFunction(AxisFunction_t axisFunction)
const
1403 qCWarning(JoystickLog) <<
"Internal Error: maxAxisFunction passed to _getJoystickAxisForAxisFunction";
1404 return kJoystickAxisNotAssigned;
1407 return _axisFunctionToJoystickAxisMap.value(axisFunction, kJoystickAxisNotAssigned);
1412 switch (axisFunction) {
1445 switch (stickFunction) {
1488 if (!_validButton(button) || !_assignedButtonActions[button]) {
1492 _assignedButtonActions[button]->repeat = repeat;
1493 _assignedButtonActions[button]->buttonElapsedTimer.start();
1497 settings.beginGroup(QString::fromLatin1(kButtonActionArrayGroup));
1498 settings.beginGroup(QString::number(button));
1499 const QString repeatKey = QString::fromLatin1(kButtonRepeatKey);
1500 settings.setValue(repeatKey, repeat);
1508 if (!_validButton(button) || !_assignedButtonActions[button]) {
1512 return _assignedButtonActions[button]->repeat;
1517 if (!_validButton(button)) {
1521 qCDebug(JoystickLog) <<
"button:actionName" << button << actionName;
1525 settings.beginGroup(QString::fromLatin1(kButtonActionArrayGroup));
1526 settings.beginGroup(QString::number(button));
1527 const QString actionNameKey = QString::fromLatin1(kButtonActionNameKey);
1528 const QString repeatKey = QString::fromLatin1(kButtonRepeatKey);
1530 if (actionName.isEmpty() || actionName == _buttonActionNone) {
1531 if (_assignedButtonActions[button]) {
1532 delete _assignedButtonActions[button];
1533 _assignedButtonActions[button] =
nullptr;
1535 settings.setValue(actionNameKey, _buttonActionNone);
1536 settings.setValue(repeatKey,
false);
1538 if (!_assignedButtonActions[button]) {
1541 _assignedButtonActions[button]->actionName = actionName;
1543 const int idx = _findAvailableButtonActionIndex(actionName);
1545 const AvailableButtonAction *
const buttonAction = qobject_cast<const AvailableButtonAction*>(_availableButtonActions->
get(idx));
1547 _assignedButtonActions[button]->repeat =
false;
1552 settings.setValue(actionNameKey, _assignedButtonActions[button]->actionName);
1553 settings.setValue(repeatKey, _assignedButtonActions[button]->repeat);
1565 if (_validButton(button)) {
1566 if (_assignedButtonActions[button]) {
1567 return _assignedButtonActions[button]->actionName;
1571 return QString(_buttonActionNone);
1578 list.reserve(_totalButtonCount);
1580 for (
int button = 0; button < _totalButtonCount; button++) {
1581 if (_assignedButtonActions[button]) {
1582 list << _assignedButtonActions[button]->actionName;
1584 list << _buttonActionNone;
1591void Joystick::_executeButtonAction(
const QString &action,
const ButtonEvent_t buttonEvent)
1593 Vehicle *
const vehicle = _pollingVehicle;
1595 qCWarning(JoystickLog) <<
"Internal Error: No vehicle for joystick!";
1599 qCWarning(JoystickLog) <<
"Internal Error: Joystick not enabled for vehicle!";
1602 if (action == _buttonActionNone) {
1606 const int idx = _findAvailableButtonActionIndex(action);
1608 const AvailableButtonAction *
const availAction = qobject_cast<const AvailableButtonAction*>(_availableButtonActions->
get(idx));
1609 std::function<void()> handler;
1610 switch (buttonEvent) {
1612 handler = availAction->
onDown();
1618 handler = availAction->
onUp();
1624 qCDebug(JoystickLog) <<
"Button Action:" << action << buttonEvent;
1640 qCDebug(JoystickLog) <<
"Button Action: Switching flight mode to" << action;
1647 for (
int i = 0; i < _mavlinkActionManager->
actions()->count(); i++) {
1649 if (action == mavlinkAction->
label()) {
1650 qCDebug(JoystickLog) <<
"Button Action: Sending MAVLink action" << action;
1651 mavlinkAction->
sendTo(vehicle);
1657bool Joystick::_validAxis(
int axis)
const
1663 qCWarning(JoystickLog) <<
"Invalid axis index" << axis;
1667bool Joystick::_validButton(
int button)
const
1669 if ((button >= 0) && (button < _totalButtonCount)) {
1673 qCWarning(JoystickLog) <<
"Invalid button index" << button;
1677int Joystick::_findAvailableButtonActionIndex(
const QString &action)
1679 for (
int i = 0; i < _availableButtonActions->
count(); i++) {
1680 const AvailableButtonAction *
const buttonAction = qobject_cast<const AvailableButtonAction*>(_availableButtonActions->
get(i));
1681 if (buttonAction->
action() == action) {
1689void Joystick::_addAvailableButtonActionIfMissing(
const QString &action)
1691 if (_findAvailableButtonActionIndex(action) != -1) {
1698 _availableActionTitles << action;
1702void Joystick::_buildAvailableButtonsActionList(
Vehicle *vehicle)
1704 if (_availableButtonActions->
count()) {
1707 _availableActionTitles.clear();
1711 [
this]() { emit
setArmed(
true); }));
1713 [
this]() { emit
setArmed(
false); }));
1718 for (
const QString &mode : list) {
1807 for (
const auto &action : customActions) {
1811 std::function<void()> repeatFn = action.canRepeat ? std::function<void()>([]{}) : nullptr;
1815 for (
int i = 0; i < _mavlinkActionManager->
actions()->count(); i++) {
1824 for (
int buttonIndex = 0; buttonIndex < _assignedButtonActions.count(); buttonIndex++) {
1826 if (assigned && (_findAvailableButtonActionIndex(assigned->
actionName) == -1)) {
1831 for (
int i = 0; i < _availableButtonActions->
count(); i++) {
1832 const AvailableButtonAction *
const buttonAction = qobject_cast<const AvailableButtonAction*>(_availableButtonActions->
get(i));
1833 _availableActionTitles << buttonAction->
action();
1843 return QStringLiteral(
"Roll");
1845 return QStringLiteral(
"Pitch");
1847 return QStringLiteral(
"Yaw");
1849 return QStringLiteral(
"Throttle");
1851 return QStringLiteral(
"Additional Axis 1");
1853 return QStringLiteral(
"Additional Axis 2");
1855 return QStringLiteral(
"Additional Axis 3");
1857 return QStringLiteral(
"Additional Axis 4");
1859 return QStringLiteral(
"Additional Axis 5");
1861 return QStringLiteral(
"Additional Axis 6");
1863 return QStringLiteral(
"Pitch Extension");
1865 return QStringLiteral(
"Roll Extension");
1867 return QStringLiteral(
"Unassigned");
1874 _startPollingForConfiguration();
1879 _stopPollingForConfiguration();
1884 if (!_validAxis(joystickAxis)) {
1890 if (_axisFunctionToJoystickAxisMap.value(axisFunction, kJoystickAxisNotAssigned) == joystickAxis) {
1891 return axisFunction;
1900 _exitPollingThread =
true;
1902 if (QThread::currentThread() ==
this) {
1903 qCWarning(JoystickLog) <<
"Skipping wait() on joystick thread";
1912 if (_linkedGroupId != groupId) {
1913 _linkedGroupId = groupId;
1917 settings.beginGroup(QStringLiteral(
"Joystick"));
1919 if (groupId.isEmpty()) {
1920 settings.remove(QStringLiteral(
"LinkedGroupId"));
1922 settings.setValue(QStringLiteral(
"LinkedGroupId"), groupId);
1933 if (_linkedGroupRole != role) {
1934 _linkedGroupRole = role;
1938 settings.beginGroup(QStringLiteral(
"Joystick"));
1940 if (role.isEmpty()) {
1941 settings.remove(QStringLiteral(
"LinkedGroupRole"));
1943 settings.setValue(QStringLiteral(
"LinkedGroupRole"), role);
#define QGC_LOGGING_CATEGORY(name, categoryStr)
A Fact is used to hold a single value within the system.
void rawValueChanged(const QVariant &value)
Q_INVOKABLE void centerGimbal()
void gimbalYawLock(bool yawLock)
void gimbalYawStart(int direction)
void gimbalPitchStart(int direction)
bool activeJoystickEnabledForActiveVehicle() const
static JoystickManager * instance()
void setActiveJoystickEnabledForActiveVehicle(bool enabled)
void setLinkedGroupId(const QString &groupId)
Q_INVOKABLE void stopConfiguration()
Tells the joystick that the configuration UI is being closed so it can do any special processing requ...
void rawButtonPressedChanged(int index, bool pressed)
Signalled during PollingForConfiguration.
void gimbalYawLock(bool lock)
@ additionalAxis2Function
@ additionalAxis6Function
@ additionalAxis3Function
@ additionalAxis4Function
@ additionalAxis1Function
@ additionalAxis5Function
Joystick(const QString &name, int axisCount, int buttonCount, int hatCount, QObject *parent=nullptr)
void stopContinuousFocus()
RemoteControlCalibrationController::StickFunction mapAxisFunctionToRCCStickFunction(AxisFunction_t axisFunction) const
void setLinkedGroupRole(const QString &role)
void setFlightMode(const QString &flightMode)
void motorInterlock(bool enable)
static QString axisFunctionToString(AxisFunction_t function)
void setFunctionForChannel(RemoteControlCalibrationController::StickFunction stickFunction, int channel)
void axisValues(float roll, float pitch, float yaw, float throttle)
void buttonActionsChanged()
@ ButtonEventDownTransition
@ ButtonEventUpTransition
void assignableActionsChanged()
void unknownAction(const QString &action)
int getChannelForFunction(RemoteControlCalibrationController::StickFunction stickFunction) const
Q_INVOKABLE QString getButtonAction(int button) const
void stepZoom(int direction)
QStringList buttonActions() const
AxisFunction_t mapRCCStickFunctionToAxisFunction(RemoteControlCalibrationController::StickFunction stickFunction) const
Q_INVOKABLE void startConfiguration()
Tells the joystick that the configuration UI is being displayed so it can do any special processing r...
Joystick::AxisCalibration_t getAxisCalibration(int axis) const
void setAxisCalibration(int axis, const AxisCalibration_t &calibration)
void startContinuousZoom(int direction)
JoystickSettings * settings()
void stopContinuousZoom()
Q_INVOKABLE bool getButtonRepeat(int button)
Q_INVOKABLE void setButtonAction(int button, const QString &action)
void stepCamera(int direction)
void setVtolInFwdFlight(bool set)
void gripperAction(GRIPPER_ACTIONS gripperAction)
void stepStream(int direction)
void landingGearRetract()
void stepFocus(int direction)
void gimbalPitchStart(int direction)
void linkedGroupChanged()
void rawChannelValuesChanged(QVector< int > channelValues)
Signalled during PollingForConfiguration.
void startContinuousFocus(int direction)
void gimbalYawStart(int direction)
Q_INVOKABLE void setButtonRepeat(int button, bool repeat)
Loads the specified action file and provides access to the actions it contains.
QmlObjectListModel * actions()
const QString & label() const
Q_INVOKABLE void sendTo(Vehicle *vehicle)
static MultiVehicleManager * instance()
Vehicle * activeVehicle() const
virtual QList< JoystickAction > joystickActions()
static QGCCorePlugin * instance()
void append(QObject *object)
Caller maintains responsibility for object ownership and deletion.
Q_INVOKABLE QObject * get(int index)
int count() const override final
void clearAndDeleteContents() override final
Clears the list and calls deleteLater on each entry.
StickFunction
These identify the various controls functions. They are also used as indices into the _rgFunctioInfo ...
@ stickFunctionAdditionalAxis6
@ stickFunctionRollExtension
@ stickFunctionAdditionalAxis1
@ stickFunctionAdditionalAxis4
@ stickFunctionAdditionalAxis2
@ stickFunctionAdditionalAxis3
@ stickFunctionAdditionalAxis5
@ stickFunctionPitchExtension
QString settingsGroup() const
Provides access to all app settings.
bool negativeThrust() const
bool throttleModeCenterZero() const
Q_INVOKABLE void motorInterlock(bool enable)
Command vehicle to Enable/Disable Motor Interlock.
Q_INVOKABLE void sendGripperAction(GRIPPER_ACTIONS gripperOption)
void sendJoystickAuxRcOverrideThreadSafe(const std::array< uint16_t, kAuxRcOverrideChannelCount > &channelValues, const std::array< bool, kAuxRcOverrideChannelCount > &channelEnabled, bool useRcOverride)
Q_INVOKABLE void emergencyStop()
Command vehicle to kill all motors no matter what state.
void setFlightMode(const QString &flightMode)
Q_INVOKABLE void landingGearDeploy()
Command vichecle to deploy landing gear.
void flightModesChanged()
GimbalController * gimbalController()
void setVtolInFwdFlight(bool vtolInFwdFlight)
void sendJoystickDataThreadSafe(float roll, float pitch, float yaw, float thrust, quint16 buttons, quint16 buttons2, float pitchExtension, float rollExtension, float aux1, float aux2, float aux3, float aux4, float aux5, float aux6)
Q_INVOKABLE void landingGearRetract()
Command vichecle to retract landing gear.
VehicleSupports * supports()
QStringList flightModes()
void setArmedShowError(bool armed)