Unverified Commit 124f9ec9 authored by Gus Grubba's avatar Gus Grubba Committed by GitHub

Merge pull request #7622 from mavlink/joystickButtons

Joystick Work
parents e71da845 4d3620dc
......@@ -6,6 +6,7 @@ Note: This file only contains high level features or important fixes.
### 3.6.0 - Daily Build
* Added ability to set a joystick button to be single action or repeated action while the button is held down.
* Rework joysticks. Fixed several issues and updated setup UI.
* Adding support for UDP RTP h.265 video streams
* For text to speech engine on Linux to English (all messages are in English)
......
......@@ -23,14 +23,16 @@ QGC_LOGGING_CATEGORY(JoystickLog, "JoystickLog")
QGC_LOGGING_CATEGORY(JoystickValuesLog, "JoystickValuesLog")
const char* Joystick::_settingsGroup = "Joysticks";
const char* Joystick::_calibratedSettingsKey = "Calibrated3"; // Increment number to force recalibration
const char* Joystick::_buttonActionSettingsKey = "ButtonActionName%1";
const char* Joystick::_calibratedSettingsKey = "Calibrated4"; // Increment number to force recalibration
const char* Joystick::_buttonActionNameKey = "ButtonActionName%1";
const char* Joystick::_buttonActionRepeatKey = "ButtonActionRepeat%1";
const char* Joystick::_throttleModeSettingsKey = "ThrottleMode";
const char* Joystick::_exponentialSettingsKey = "Exponential";
const char* Joystick::_accumulatorSettingsKey = "Accumulator";
const char* Joystick::_deadbandSettingsKey = "Deadband";
const char* Joystick::_circleCorrectionSettingsKey = "Circle_Correction";
const char* Joystick::_frequencySettingsKey = "Frequency";
const char* Joystick::_axisFrequencySettingsKey = "AxisFrequency";
const char* Joystick::_buttonFrequencySettingsKey = "ButtonFrequency";
const char* Joystick::_txModeSettingsKey = nullptr;
const char* Joystick::_fixedWingTXModeSettingsKey = "TXMode_FixedWing";
const char* Joystick::_multiRotorTXModeSettingsKey = "TXMode_MultiRotor";
......@@ -39,13 +41,16 @@ const char* Joystick::_vtolTXModeSettingsKey = "TXMode_VTOL";
const char* Joystick::_submarineTXModeSettingsKey = "TXMode_Submarine";
const char* Joystick::_gimbalSettingsKey = "GimbalEnabled";
const char* Joystick::_buttonActionNone = QT_TR_NOOP("No Action");
const char* Joystick::_buttonActionArm = QT_TR_NOOP("Arm");
const char* Joystick::_buttonActionDisarm = QT_TR_NOOP("Disarm");
const char* Joystick::_buttonActionToggleArm = QT_TR_NOOP("Toggle Arm");
const char* Joystick::_buttonActionVTOLFixedWing = QT_TR_NOOP("VTOL: Fixed Wing");
const char* Joystick::_buttonActionVTOLMultiRotor = QT_TR_NOOP("VTOL: Multi-Rotor");
const char* Joystick::_buttonActionZoomIn = QT_TR_NOOP("Zoom In");
const char* Joystick::_buttonActionZoomOut = QT_TR_NOOP("Zoom Out");
const char* Joystick::_buttonActionContinuousZoomIn = QT_TR_NOOP("Continuous Zoom In");
const char* Joystick::_buttonActionContinuousZoomOut = QT_TR_NOOP("Continuous Zoom Out");
const char* Joystick::_buttonActionStepZoomIn = QT_TR_NOOP("Step Zoom In");
const char* Joystick::_buttonActionStepZoomOut = QT_TR_NOOP("Step Zoom Out");
const char* Joystick::_buttonActionNextStream = QT_TR_NOOP("Next Video Stream");
const char* Joystick::_buttonActionPreviousStream = QT_TR_NOOP("Previous Video Stream");
const char* Joystick::_buttonActionNextCamera = QT_TR_NOOP("Next Camera");
......@@ -71,6 +76,19 @@ const char* Joystick::_rgFunctionSettingsKey[Joystick::maxFunction] = {
int Joystick::_transmitterMode = 2;
AssignedButtonAction::AssignedButtonAction(QObject* parent, const QString name)
: QObject(parent)
, action(name)
{
}
AssignableButtonAction::AssignableButtonAction(QObject* parent, QString action_, bool canRepeat_)
: QObject(parent)
, _action(action_)
, _repeat(canRepeat_)
{
}
Joystick::Joystick(const QString& name, int axisCount, int buttonCount, int hatCount, MultiVehicleManager* multiVehicleManager)
: _name(name)
, _axisCount(axisCount)
......@@ -83,14 +101,47 @@ Joystick::Joystick(const QString& name, int axisCount, int buttonCount, int hatC
_rgAxisValues = new int[static_cast<size_t>(_axisCount)];
_rgCalibration = new Calibration_t[static_cast<size_t>(_axisCount)];
_rgButtonValues = new uint8_t[static_cast<size_t>(_totalButtonCount)];
for (int i = 0; i < _axisCount; i++) {
_rgAxisValues[i] = 0;
}
for (int i = 0; i < _totalButtonCount; i++) {
_rgButtonValues[i] = BUTTON_UP;
_buttonActionArray.append(nullptr);
}
//-- Available Actions
_assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionNone));
_assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionArm));
_assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionDisarm));
_assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionToggleArm));
if (_activeVehicle) {
QStringList list = _activeVehicle->flightModes();
foreach(auto mode, list) {
_assignableButtonActions.append(new AssignableButtonAction(this, mode));
}
}
_assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionVTOLFixedWing));
_assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionVTOLMultiRotor));
_assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionContinuousZoomIn, true));
_assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionContinuousZoomOut, true));
_assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionStepZoomIn, true));
_assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionStepZoomOut, true));
_assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionNextStream));
_assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionPreviousStream));
_assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionNextCamera));
_assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionPreviousCamera));
_assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionTriggerCamera));
_assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionStartVideoRecord));
_assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionStopVideoRecord));
_assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionToggleVideoRecord));
_assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionGimbalDown, true));
_assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionGimbalUp, true));
_assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionGimbalLeft, true));
_assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionGimbalRight, true));
_assignableButtonActions.append(new AssignableButtonAction(this, _buttonActionGimbalCenter));
for(int i = 0; i < _assignableButtonActions.count(); i++) {
AssignableButtonAction* p = qobject_cast<AssignableButtonAction*>(_assignableButtonActions[i]);
_availableActionTitles << p->action();
}
_updateTXModeSettingsKey(_multiVehicleManager->activeVehicle());
_loadSettings();
connect(_multiVehicleManager, &MultiVehicleManager::activeVehicleChanged, this, &Joystick::_activeVehicleChanged);
......@@ -101,10 +152,15 @@ Joystick::~Joystick()
// Crash out of the thread if it is still running
terminate();
wait();
delete[] _rgAxisValues;
delete[] _rgCalibration;
delete[] _rgButtonValues;
_assignableButtonActions.deleteListAndContents();
for (int button = 0; button < _totalButtonCount; button++) {
if(_buttonActionArray[button]) {
_buttonActionArray[button]->deleteLater();
}
}
}
void Joystick::_setDefaultCalibration(void) {
......@@ -136,7 +192,8 @@ void Joystick::_setDefaultCalibration(void) {
_exponential = 0;
_accumulator = false;
_deadband = false;
_frequency = 25.0f;
_axisFrequency = 25.0f;
_buttonFrequency= 5.0f;
_throttleMode = ThrottleModeDownZero;
_calibrated = true;
_circleCorrection = false;
......@@ -198,7 +255,8 @@ void Joystick::_loadSettings()
_exponential = settings.value(_exponentialSettingsKey, 0).toFloat();
_accumulator = settings.value(_accumulatorSettingsKey, false).toBool();
_deadband = settings.value(_deadbandSettingsKey, false).toBool();
_frequency = settings.value(_frequencySettingsKey, 25.0f).toFloat();
_axisFrequency = settings.value(_axisFrequencySettingsKey, 25.0f).toFloat();
_buttonFrequency= settings.value(_buttonFrequencySettingsKey, 5.0f).toFloat();
_circleCorrection = settings.value(_circleCorrectionSettingsKey, false).toBool();
_gimbalEnabled = settings.value(_gimbalSettingsKey, false).toBool();
......@@ -233,23 +291,37 @@ void Joystick::_loadSettings()
qCDebug(JoystickLog) << "_loadSettings axis:min:max:trim:reversed:deadband:badsettings" << axis << calibration->min << calibration->max << calibration->center << calibration->reversed << calibration->deadband << badSettings;
}
int workingAxis = 0;
for (int function = 0; function < maxFunction; function++) {
int functionAxis;
functionAxis = settings.value(_rgFunctionSettingsKey[function], -1).toInt(&convertOk);
badSettings |= !convertOk || (functionAxis == -1) || (functionAxis >= _axisCount);
badSettings |= !convertOk || (functionAxis >= _axisCount);
if(functionAxis >= 0) {
workingAxis++;
}
if(functionAxis < _axisCount) {
_rgFunctionAxis[function] = functionAxis;
}
qCDebug(JoystickLog) << "_loadSettings function:axis:badsettings" << function << functionAxis << badSettings;
}
badSettings |= workingAxis < 4;
// FunctionAxis mappings are always stored in TX mode 2
// Remap to stored TX mode in settings
_remapAxes(2, _transmitterMode, _rgFunctionAxis);
for (int button=0; button<_totalButtonCount; button++) {
_rgButtonActions << settings.value(QString(_buttonActionSettingsKey).arg(button), QString()).toString();
qCDebug(JoystickLog) << "_loadSettings button:action" << button << _rgButtonActions[button];
for (int button = 0; button < _totalButtonCount; button++) {
QString a = settings.value(QString(_buttonActionNameKey).arg(button), QString()).toString();
if(!a.isEmpty() && _findAssignableButtonAction(a) >= 0 && a != _buttonActionNone) {
if(_buttonActionArray[button]) {
_buttonActionArray[button]->deleteLater();
}
AssignedButtonAction* ap = new AssignedButtonAction(this, a);
ap->repeat = settings.value(QString(_buttonActionRepeatKey).arg(button), false).toBool();
_buttonActionArray[button] = ap;
_buttonActionArray[button]->buttonTime.start();
qCDebug(JoystickLog) << "_loadSettings button:action" << button << _buttonActionArray[button]->action << _buttonActionArray[button]->repeat;
}
}
if (badSettings) {
......@@ -258,10 +330,23 @@ void Joystick::_loadSettings()
}
}
void Joystick::_saveSettings()
void Joystick::_saveButtonSettings()
{
QSettings settings;
settings.beginGroup(_settingsGroup);
settings.beginGroup(_name);
for (int button = 0; button < _totalButtonCount; button++) {
if(_buttonActionArray[button]) {
settings.setValue(QString(_buttonActionNameKey).arg(button), _buttonActionArray[button]->action);
settings.setValue(QString(_buttonActionRepeatKey).arg(button), _buttonActionArray[button]->repeat);
qCDebug(JoystickLog) << "_saveButtonSettings button:action" << button << _buttonActionArray[button]->action << _buttonActionArray[button]->repeat;
}
}
}
void Joystick::_saveSettings()
{
QSettings settings;
settings.beginGroup(_settingsGroup);
// Transmitter mode is static
......@@ -270,12 +355,12 @@ void Joystick::_saveSettings()
settings.setValue(_txModeSettingsKey, _transmitterMode);
settings.beginGroup(_name);
settings.setValue(_calibratedSettingsKey, _calibrated);
settings.setValue(_exponentialSettingsKey, _exponential);
settings.setValue(_accumulatorSettingsKey, _accumulator);
settings.setValue(_deadbandSettingsKey, _deadband);
settings.setValue(_frequencySettingsKey, _frequency);
settings.setValue(_axisFrequencySettingsKey, _axisFrequency);
settings.setValue(_buttonFrequencySettingsKey, _buttonFrequency);
settings.setValue(_throttleModeSettingsKey, _throttleMode);
settings.setValue(_gimbalSettingsKey, _gimbalEnabled);
settings.setValue(_circleCorrectionSettingsKey, _circleCorrection);
......@@ -309,16 +394,11 @@ void Joystick::_saveSettings()
// Write mode 2 mappings without changing mapping currently in use
int temp[maxFunction];
_remapAxes(_transmitterMode, 2, temp);
for (int function = 0; function < maxFunction; function++) {
settings.setValue(_rgFunctionSettingsKey[function], temp[function]);
qCDebug(JoystickLog) << "_saveSettings name:function:axis" << _name << function << _rgFunctionSettingsKey[function];
}
for (int button = 0; button < _totalButtonCount; button++) {
settings.setValue(QString(_buttonActionSettingsKey).arg(button), _rgButtonActions[button]);
qCDebug(JoystickLog) << "_saveSettings button:action" << button << _rgButtonActions[button];
}
_saveButtonSettings();
}
// Relative mappings of axis functions between different TX modes
......@@ -334,15 +414,12 @@ int Joystick::_mapFunctionMode(int mode, int function) {
// Remap current axis functions from current TX mode to new TX mode
void Joystick::_remapAxes(int currentMode, int newMode, int (&newMapping)[maxFunction]) {
int temp[maxFunction];
for(int function = 0; function < maxFunction; function++) {
temp[_mapFunctionMode(newMode, function)] = _rgFunctionAxis[_mapFunctionMode(currentMode, function)];
}
for(int function = 0; function < maxFunction; function++) {
newMapping[function] = temp[function];
}
}
void Joystick::setTXMode(int mode) {
......@@ -413,20 +490,27 @@ float Joystick::_adjustRange(int value, Calibration_t calibration, bool withDead
void Joystick::run()
{
//-- Joystick thread
_open();
//-- Reset timers
_axisTime.start();
for (int buttonIndex = 0; buttonIndex < _totalButtonCount; buttonIndex++) {
if(_buttonActionArray[buttonIndex]) {
_buttonActionArray[buttonIndex]->buttonTime.start();
}
}
while (!_exitThread) {
_update();
// Update axes
for (int axisIndex = 0; axisIndex < _axisCount; axisIndex++) {
int newAxisValue = _getAxis(axisIndex);
// Calibration code requires signal to be emitted even if value hasn't changed
_rgAxisValues[axisIndex] = newAxisValue;
emit rawAxisValueChanged(axisIndex, newAxisValue);
_handleButtons();
_handleAxis();
QGC::SLEEP::msleep(20);
}
_close();
}
// Update buttons
void Joystick::_handleButtons()
{
//-- Update button states
for (int buttonIndex = 0; buttonIndex < _buttonCount; buttonIndex++) {
bool newButtonValue = _getButton(buttonIndex);
if (newButtonValue && _rgButtonValues[buttonIndex] == BUTTON_UP) {
......@@ -437,8 +521,7 @@ void Joystick::run()
emit rawButtonPressedChanged(buttonIndex, newButtonValue);
}
}
// Update hat - append hat buttons to the end of the normal button list
//-- Update hat - append hat buttons to the end of the normal button list
int numHatButtons = 4;
for (int hatIndex = 0; hatIndex < _hatCount; hatIndex++) {
for (int hatButtonIndex = 0; hatButtonIndex<numHatButtons; hatButtonIndex++) {
......@@ -455,7 +538,50 @@ void Joystick::run()
}
}
}
//-- Process button press
for (int buttonIndex = 0; buttonIndex < _totalButtonCount; buttonIndex++) {
if(_rgButtonValues[buttonIndex] == BUTTON_DOWN || _rgButtonValues[buttonIndex] == BUTTON_REPEAT) {
if(_buttonActionArray[buttonIndex]) {
QString buttonAction = _buttonActionArray[buttonIndex]->action;
if(buttonAction.isEmpty() || buttonAction == _buttonActionNone)
continue;
//-- Process single button
if(!_buttonActionArray[buttonIndex]->repeat) {
//-- This button just went down
if(_rgButtonValues[buttonIndex] == BUTTON_DOWN) {
qCDebug(JoystickLog) << "Single button triggered" << buttonIndex << buttonAction;
_executeButtonAction(buttonAction);
}
} else {
//-- Process repeat buttons
int buttonDelay = static_cast<int>(1000.0f / _buttonFrequency);
if(_buttonActionArray[buttonIndex]->buttonTime.elapsed() > buttonDelay) {
_buttonActionArray[buttonIndex]->buttonTime.start();
qCDebug(JoystickLog) << "Repeat button triggered" << buttonIndex << buttonAction;
_executeButtonAction(buttonAction);
}
}
}
//-- Flag it as processed
_rgButtonValues[buttonIndex] = BUTTON_REPEAT;
}
}
}
void Joystick::_handleAxis()
{
//-- Get frequency
int axisDelay = static_cast<int>(1000.0f / _axisFrequency);
//-- Check elapsed time since last run
if(_axisTime.elapsed() > axisDelay) {
_axisTime.start();
//-- Update axis
for (int axisIndex = 0; axisIndex < _axisCount; axisIndex++) {
int newAxisValue = _getAxis(axisIndex);
// Calibration code requires signal to be emitted even if value hasn't changed
_rgAxisValues[axisIndex] = newAxisValue;
emit rawAxisValueChanged(axisIndex, newAxisValue);
}
if (_activeVehicle->joystickEnabled() && !_calibrationMode && _calibrated) {
int axis = _rgFunctionAxis[rollFunction];
float roll = _adjustRange(_rgAxisValues[axis], _rgCalibration[axis], _deadband);
......@@ -519,22 +645,8 @@ void Joystick::run()
} else {
throttle = (throttle + 1.0f) / 2.0f;
}
//-- Process button press
for (int buttonIndex = 0; buttonIndex < _totalButtonCount; buttonIndex++) {
//-- This button just went down
if(_rgButtonValues[buttonIndex] == BUTTON_DOWN) {
//-- Flag it as processed
_rgButtonValues[buttonIndex] = BUTTON_REPEAT;
//-- Process button
QString buttonAction =_rgButtonActions[buttonIndex];
qCDebug(JoystickLog) << "button triggered" << buttonIndex << buttonAction;
if (!buttonAction.isEmpty()) {
_buttonAction(buttonAction);
}
}
}
qCDebug(JoystickValuesLog) << "name:roll:pitch:yaw:throttle:gimbalPitch:gimbalYaw" << name() << roll << -pitch << yaw << throttle << gimbalPitch << gimbalYaw;
// NOTE: The buttonPressedBits going to MANUAL_CONTROL are currently used by ArduSub (and it only handles 16 bits)
// Set up button bitmap
quint64 buttonPressedBits = 0; // Buttons pressed for manualControl signal
for (int buttonIndex = 0; buttonIndex < _totalButtonCount; buttonIndex++) {
......@@ -544,9 +656,6 @@ void Joystick::run()
buttonPressedBits |= buttonBit;
}
}
qCDebug(JoystickValuesLog) << "name:roll:pitch:yaw:throttle:gimbalPitch:gimbalYaw" << name() << roll << -pitch << yaw << throttle << gimbalPitch << gimbalYaw;
// NOTE: The buttonPressedBits going to MANUAL_CONTROL are currently used by ArduSub (and it only handles 16 bits)
uint16_t shortButtons = static_cast<uint16_t>(buttonPressedBits & 0xFFFF);
emit manualControl(roll, -pitch, yaw, throttle, shortButtons, _activeVehicle->joystickMode());
if(_activeVehicle && _axisCount > 4 && _gimbalEnabled) {
......@@ -555,13 +664,7 @@ void Joystick::run()
emit manualControlGimbal((gimbalPitch + 1.0f) / 2.0f * 90.0f, gimbalYaw * 180.0f);
}
}
// Sleep. Update rate of joystick is by default 25 Hz
unsigned long mswait = static_cast<unsigned long>(1000.0f / _frequency);
QGC::SLEEP::msleep(mswait);
}
_close();
}
void Joystick::startPolling(Vehicle* vehicle)
......@@ -631,7 +734,6 @@ void Joystick::stopPolling(void)
void Joystick::setCalibration(int axis, Calibration_t& calibration)
{
if (!_validAxis(axis)) {
qCWarning(JoystickLog) << "Invalid axis index" << axis;
return;
}
_calibrated = true;
......@@ -643,16 +745,14 @@ void Joystick::setCalibration(int axis, Calibration_t& calibration)
Joystick::Calibration_t Joystick::getCalibration(int axis)
{
if (!_validAxis(axis)) {
qCWarning(JoystickLog) << "Invalid axis index" << axis;
return Calibration_t();
}
return _rgCalibration[axis];
}
void Joystick::setFunctionAxis(AxisFunction_t function, int axis)
{
if (!_validAxis(axis)) {
qCWarning(JoystickLog) << "Invalid axis index" << axis;
return;
}
_calibrated = true;
......@@ -669,62 +769,86 @@ int Joystick::getFunctionAxis(AxisFunction_t function)
return _rgFunctionAxis[function];
}
QStringList Joystick::actions()
void Joystick::setButtonRepeat(int button, bool repeat)
{
QStringList list;
list << _buttonActionArm << _buttonActionDisarm << _buttonActionToggleArm;
if (_activeVehicle) {
list << _activeVehicle->flightModes();
}
list << _buttonActionVTOLFixedWing << _buttonActionVTOLMultiRotor;
list << _buttonActionZoomIn << _buttonActionZoomOut;
list << _buttonActionNextStream << _buttonActionPreviousStream;
list << _buttonActionNextCamera << _buttonActionPreviousCamera;
list << _buttonActionTriggerCamera;
list << _buttonActionStartVideoRecord;
list << _buttonActionStopVideoRecord;
list << _buttonActionToggleVideoRecord;
list << _buttonActionGimbalDown;
list << _buttonActionGimbalUp;
list << _buttonActionGimbalLeft;
list << _buttonActionGimbalRight;
list << _buttonActionGimbalCenter;
return list;
if (!_validButton(button) || !_buttonActionArray[button]) {
return;
}
_buttonActionArray[button]->repeat = repeat;
_buttonActionArray[button]->buttonTime.start();
//-- Save to settings
QSettings settings;
settings.beginGroup(_settingsGroup);
settings.beginGroup(_name);
settings.setValue(QString(_buttonActionRepeatKey).arg(button), _buttonActionArray[button]->repeat);
}
bool Joystick::getButtonRepeat(int button)
{
if (!_validButton(button) || !_buttonActionArray[button]) {
return false;
}
return _buttonActionArray[button]->repeat;
}
void Joystick::setButtonAction(int button, const QString& action)
{
if (!_validButton(button)) {
qCWarning(JoystickLog) << "Invalid button index" << button;
return;
}
qDebug() << "setButtonAction" << action;
_rgButtonActions[button] = action;
_saveSettings();
emit buttonActionsChanged(buttonActions());
qCWarning(JoystickLog) << "setButtonAction:" << button << action;
QSettings settings;
settings.beginGroup(_settingsGroup);
settings.beginGroup(_name);
if(action.isEmpty() || action == _buttonActionNone) {
if(_buttonActionArray[button]) {
_buttonActionArray[button]->deleteLater();
_buttonActionArray[button] = nullptr;
//-- Clear from settings
settings.remove(QString(_buttonActionNameKey).arg(button));
settings.remove(QString(_buttonActionRepeatKey).arg(button));
}
} else {
if(!_buttonActionArray[button]) {
_buttonActionArray[button] = new AssignedButtonAction(this, action);
} else {
_buttonActionArray[button]->action = action;
//-- Make sure repeat is off if this action doesn't support repeats
int idx = _findAssignableButtonAction(action);
if(idx >= 0) {
AssignableButtonAction* p = qobject_cast<AssignableButtonAction*>(_assignableButtonActions[idx]);
if(!p->canRepeat()) {
_buttonActionArray[button]->repeat = false;
}
}
}
//-- Save to settings
settings.setValue(QString(_buttonActionNameKey).arg(button), _buttonActionArray[button]->action);
settings.setValue(QString(_buttonActionRepeatKey).arg(button), _buttonActionArray[button]->repeat);
}
emit buttonActionsChanged();
}
QString Joystick::getButtonAction(int button)
{
if (!_validButton(button)) {
qCWarning(JoystickLog) << "Invalid button index" << button;
if (_validButton(button)) {
if(_buttonActionArray[button]) {
return _buttonActionArray[button]->action;
}
return _rgButtonActions[button];
}
return QString(_buttonActionNone);
}
QVariantList Joystick::buttonActions()
QStringList Joystick::buttonActions()
{
QVariantList list;
for (int button=0; button<_totalButtonCount; button++) {
list += QVariant::fromValue(_rgButtonActions[button]);
QStringList list;
for (int button = 0; button < _totalButtonCount; button++) {
list << getButtonAction(button);
}
return list;
}
int Joystick::throttleMode(void)
int Joystick::throttleMode()
{
return _throttleMode;
}
......@@ -743,7 +867,7 @@ void Joystick::setThrottleMode(int mode)
emit throttleModeChanged(_throttleMode);
}
bool Joystick::negativeThrust(void)
bool Joystick::negativeThrust()
{
return _negativeThrust;
}
......@@ -758,7 +882,7 @@ void Joystick::setNegativeThrust(bool allowNegative)
emit negativeThrustChanged(_negativeThrust);
}
float Joystick::exponential(void)
float Joystick::exponential()
{
return _exponential;
}
......@@ -770,7 +894,7 @@ void Joystick::setExponential(float expo)
emit exponentialChanged(_exponential);
}
bool Joystick::accumulator(void)
bool Joystick::accumulator()
{
return _accumulator;
}
......@@ -782,7 +906,7 @@ void Joystick::setAccumulator(bool accu)
emit accumulatorChanged(_accumulator);
}
bool Joystick::deadband(void)
bool Joystick::deadband()
{
return _deadband;
}
......@@ -793,7 +917,7 @@ void Joystick::setDeadband(bool deadband)
_saveSettings();
}
bool Joystick::circleCorrection(void)
bool Joystick::circleCorrection()
{
return _circleCorrection;
}
......@@ -805,11 +929,6 @@ void Joystick::setCircleCorrection(bool circleCorrection)
emit circleCorrectionChanged(_circleCorrection);
}
float Joystick::frequency()
{
return _frequency;
}
void Joystick::setGimbalEnabled(bool set)
{
_gimbalEnabled = set;
......@@ -817,14 +936,24 @@ void Joystick::setGimbalEnabled(bool set)
emit gimbalEnabledChanged();
}
void Joystick::setFrequency(float val)
void Joystick::setAxisFrequency(float val)
{
//-- Arbitrary limits
if(val < 0.25f) val = 0.25f;
if(val > 100.0f) val = 100.0f;
_frequency = val;
if(val > 50.0f) val = 50.0f;
_axisFrequency = val;
_saveSettings();
emit frequencyChanged();
emit axisFrequencyChanged();
}
void Joystick::setButtonFrequency(float val)
{
//-- Arbitrary limits
if(val < 0.25f) val = 0.25f;
if(val > 50.0f) val = 50.0f;
_buttonFrequency = val;
_saveSettings();
emit buttonFrequencyChanged();
}
void Joystick::setCalibrationMode(bool calibrating)
......@@ -840,9 +969,9 @@ void Joystick::setCalibrationMode(bool calibrating)
}
void Joystick::_buttonAction(const QString& action)
void Joystick::_executeButtonAction(const QString& action)
{
if (!_activeVehicle || !_activeVehicle->joystickEnabled()) {
if (!_activeVehicle || !_activeVehicle->joystickEnabled() || action == _buttonActionNone) {
return;
}
if (action == _buttonActionArm) {
......@@ -857,8 +986,10 @@ void Joystick::_buttonAction(const QString& action)
emit setVtolInFwdFlight(false);
} else if (_activeVehicle->flightModes().contains(action)) {
emit setFlightMode(action);
} else if(action == _buttonActionZoomIn || action == _buttonActionZoomOut) {
emit stepZoom(action == _buttonActionZoomIn ? 1 : -1);
} else if(action == _buttonActionContinuousZoomIn || action == _buttonActionContinuousZoomOut) {
emit startContinuousZoom(action == _buttonActionStepZoomIn ? 1 : -1);
} else if(action == _buttonActionStepZoomIn || action == _buttonActionStepZoomOut) {
emit stepZoom(action == _buttonActionStepZoomIn ? 1 : -1);
} else if(action == _buttonActionNextStream || action == _buttonActionPreviousStream) {
emit stepStream(action == _buttonActionNextStream ? 1 : -1);
} else if(action == _buttonActionNextCamera || action == _buttonActionPreviousCamera) {
......@@ -888,11 +1019,28 @@ void Joystick::_buttonAction(const QString& action)
bool Joystick::_validAxis(int axis)
{
return axis >= 0 && axis < _axisCount;
if(axis >= 0 && axis < _axisCount) {
return true;
}
qCWarning(JoystickLog) << "Invalid axis index" << axis;
return false;
}
bool Joystick::_validButton(int button)
{
return button >= 0 && button < _totalButtonCount;
if(button >= 0 && button < _totalButtonCount)
return true;
qCWarning(JoystickLog) << "Invalid button index" << button;
return false;
}
int Joystick::_findAssignableButtonAction(const QString& action)
{
for(int i = 0; i < _assignableButtonActions.count(); i++) {
AssignableButtonAction* p = qobject_cast<AssignableButtonAction*>(_assignableButtonActions[i]);
if(p->action() == action)
return i;
}
return -1;
}
......@@ -21,10 +21,33 @@
Q_DECLARE_LOGGING_CATEGORY(JoystickLog)
Q_DECLARE_LOGGING_CATEGORY(JoystickValuesLog)
//-- Action assigned to button
class AssignedButtonAction : public QObject {
Q_OBJECT
public:
AssignedButtonAction(QObject* parent, const QString name);
QString action;
QTime buttonTime;
bool repeat = false;
};
//-- Assignable Button Action
class AssignableButtonAction : public QObject {
Q_OBJECT
public:
AssignableButtonAction(QObject* parent, QString action_, bool canRepeat_ = false);
Q_PROPERTY(QString action READ action CONSTANT)
Q_PROPERTY(bool canRepeat READ canRepeat CONSTANT)
QString action () { return _action; }
bool canRepeat () { return _repeat; }
private:
QString _action;
bool _repeat = false;
};
class Joystick : public QThread
{
Q_OBJECT
public:
Joystick(const QString& name, int axisCount, int buttonCount, int hatCount, MultiVehicleManager* multiVehicleManager);
......@@ -65,17 +88,26 @@ public:
Q_PROPERTY(int totalButtonCount READ totalButtonCount CONSTANT)
Q_PROPERTY(int axisCount READ axisCount CONSTANT)
Q_PROPERTY(bool requiresCalibration READ requiresCalibration CONSTANT)
Q_PROPERTY(QStringList actions READ actions CONSTANT)
Q_PROPERTY(QVariantList buttonActions READ buttonActions NOTIFY buttonActionsChanged)
//-- Actions assigned to buttons
Q_PROPERTY(QStringList buttonActions READ buttonActions NOTIFY buttonActionsChanged)
//-- Actions that can be assigned to buttons
Q_PROPERTY(QmlObjectListModel* assignableActions READ assignableActions CONSTANT)
Q_PROPERTY(QStringList assignableActionTitles READ assignableActionTitles CONSTANT)
Q_PROPERTY(QString disabledActionName READ disabledActionName CONSTANT)
Q_PROPERTY(bool gimbalEnabled READ gimbalEnabled WRITE setGimbalEnabled NOTIFY gimbalEnabledChanged)
Q_PROPERTY(int throttleMode READ throttleMode WRITE setThrottleMode NOTIFY throttleModeChanged)
Q_PROPERTY(float frequency READ frequency WRITE setFrequency NOTIFY frequencyChanged)
Q_PROPERTY(float axisFrequency READ axisFrequency WRITE setAxisFrequency NOTIFY axisFrequencyChanged)
Q_PROPERTY(float buttonFrequency READ buttonFrequency WRITE setButtonFrequency NOTIFY buttonFrequencyChanged)
Q_PROPERTY(bool negativeThrust READ negativeThrust WRITE setNegativeThrust NOTIFY negativeThrustChanged)
Q_PROPERTY(float exponential READ exponential WRITE setExponential NOTIFY exponentialChanged)
Q_PROPERTY(bool accumulator READ accumulator WRITE setAccumulator NOTIFY accumulatorChanged)
Q_PROPERTY(bool circleCorrection READ circleCorrection WRITE setCircleCorrection NOTIFY circleCorrectionChanged)
Q_INVOKABLE void setButtonRepeat (int button, bool repeat);
Q_INVOKABLE bool getButtonRepeat (int button);
Q_INVOKABLE void setButtonAction (int button, const QString& action);
Q_INVOKABLE QString getButtonAction (int button);
......@@ -85,8 +117,11 @@ public:
int totalButtonCount () { return _totalButtonCount; }
int axisCount () { return _axisCount; }
bool gimbalEnabled () { return _gimbalEnabled; }
QStringList actions ();
QVariantList buttonActions ();
QStringList buttonActions ();
QmlObjectListModel* assignableActions () { return &_assignableButtonActions; }
QStringList assignableActionTitles () { return _availableActionTitles; }
QString disabledActionName () { return _buttonActionNone; }
void setGimbalEnabled (bool set);
......@@ -109,53 +144,48 @@ public:
*/
virtual bool requiresCalibration(void) { return true; }
int throttleMode(void);
void setThrottleMode(int mode);
int throttleMode ();
void setThrottleMode (int mode);
bool negativeThrust(void);
void setNegativeThrust(bool allowNegative);
bool negativeThrust ();
void setNegativeThrust (bool allowNegative);
float exponential(void);
void setExponential(float expo);
float exponential ();
void setExponential (float expo);
bool accumulator(void);
void setAccumulator(bool accu);
bool accumulator ();
void setAccumulator (bool accu);
bool deadband(void);
void setDeadband(bool accu);
bool deadband ();
void setDeadband (bool accu);
bool circleCorrection(void);
bool circleCorrection ();
void setCircleCorrection(bool circleCorrection);
void setTXMode(int mode);
int getTXMode(void) { return _transmitterMode; }
void setTXMode (int mode);
int getTXMode () { return _transmitterMode; }
/// Set the current calibration mode
void setCalibrationMode(bool calibrating);
void setCalibrationMode (bool calibrating);
float frequency();
void setFrequency(float val);
float axisFrequency () { return _axisFrequency; }
void setAxisFrequency (float val);
signals:
void calibratedChanged(bool calibrated);
float buttonFrequency () { return _buttonFrequency; }
void setButtonFrequency(float val);
signals:
// The raw signals are only meant for use by calibration
void rawAxisValueChanged(int index, int value);
void rawButtonPressedChanged(int index, int pressed);
void buttonActionsChanged(QVariantList actions);
void throttleModeChanged(int mode);
void negativeThrustChanged(bool allowNegative);
void exponentialChanged(float exponential);
void accumulatorChanged(bool accumulator);
void enabledChanged(bool enabled);
void circleCorrectionChanged(bool circleCorrection);
void rawAxisValueChanged (int index, int value);
void rawButtonPressedChanged (int index, int pressed);
void calibratedChanged (bool calibrated);
void buttonActionsChanged ();
void throttleModeChanged (int mode);
void negativeThrustChanged (bool allowNegative);
void exponentialChanged (float exponential);
void accumulatorChanged (bool accumulator);
void enabledChanged (bool enabled);
void circleCorrectionChanged (bool circleCorrection);
/// Signal containing new joystick information
/// @param roll Range is -1:1, negative meaning roll left, positive meaning roll right
......@@ -166,10 +196,13 @@ signals:
void manualControl (float roll, float pitch, float yaw, float throttle, quint16 buttons, int joystickMmode);
void manualControlGimbal (float gimbalPitch, float gimbalYaw);
void buttonActionTriggered(int action);
void buttonActionTriggered (int action);
void gimbalEnabledChanged ();
void frequencyChanged ();
void axisFrequencyChanged ();
void buttonFrequencyChanged ();
void startContinuousZoom (int direction);
void stopContinuousZoom ();
void stepZoom (int direction);
void stepCamera (int direction);
void stepStream (int direction);
......@@ -187,11 +220,15 @@ signals:
protected:
void _setDefaultCalibration ();
void _saveSettings ();
void _saveButtonSettings ();
void _loadSettings ();
float _adjustRange (int value, Calibration_t calibration, bool withDeadbands);
void _buttonAction (const QString& action);
void _executeButtonAction (const QString& action);
int _findAssignableButtonAction(const QString& action);
bool _validAxis (int axis);
bool _validButton (int button);
void _handleAxis ();
void _handleButtons ();
private:
virtual bool _open () = 0;
......@@ -229,7 +266,8 @@ protected:
bool _accumulator = false;
bool _deadband = false;
bool _circleCorrection = true;
float _frequency = 25.0f;
float _axisFrequency = 25.0f;
float _buttonFrequency = 5.0f;
Vehicle* _activeVehicle = nullptr;
bool _gimbalEnabled = false;
......@@ -244,11 +282,12 @@ protected:
int _totalButtonCount;
static int _transmitterMode;
int _rgFunctionAxis[maxFunction] = {};
QTime _axisTime;
QStringList _rgButtonActions;
QmlObjectListModel _assignableButtonActions;
QList<AssignedButtonAction*> _buttonActionArray;
QStringList _availableActionTitles;
MultiVehicleManager* _multiVehicleManager = nullptr;
private:
......@@ -256,13 +295,15 @@ private:
static const char* _settingsGroup;
static const char* _calibratedSettingsKey;
static const char* _buttonActionSettingsKey;
static const char* _buttonActionNameKey;
static const char* _buttonActionRepeatKey;
static const char* _throttleModeSettingsKey;
static const char* _exponentialSettingsKey;
static const char* _accumulatorSettingsKey;
static const char* _deadbandSettingsKey;
static const char* _circleCorrectionSettingsKey;
static const char* _frequencySettingsKey;
static const char* _axisFrequencySettingsKey;
static const char* _buttonFrequencySettingsKey;
static const char* _txModeSettingsKey;
static const char* _fixedWingTXModeSettingsKey;
static const char* _multiRotorTXModeSettingsKey;
......@@ -271,13 +312,16 @@ private:
static const char* _submarineTXModeSettingsKey;
static const char* _gimbalSettingsKey;
static const char* _buttonActionNone;
static const char* _buttonActionArm;
static const char* _buttonActionDisarm;
static const char* _buttonActionToggleArm;
static const char* _buttonActionVTOLFixedWing;
static const char* _buttonActionVTOLMultiRotor;
static const char* _buttonActionZoomIn;
static const char* _buttonActionZoomOut;
static const char* _buttonActionStepZoomIn;
static const char* _buttonActionStepZoomOut;
static const char* _buttonActionContinuousZoomIn;
static const char* _buttonActionContinuousZoomOut;
static const char* _buttonActionNextStream;
static const char* _buttonActionPreviousStream;
static const char* _buttonActionNextCamera;
......
......@@ -32,6 +32,14 @@ QmlObjectListModel::~QmlObjectListModel()
}
QObject* QmlObjectListModel::get(int index)
{
if (index < 0 || index >= _objectList.count()) {
return nullptr;
}
return _objectList[index];
}
int QmlObjectListModel::rowCount(const QModelIndex& parent) const
{
Q_UNUSED(parent);
......@@ -160,19 +168,17 @@ void QmlObjectListModel::insert(int i, QObject* object)
if (i < 0 || i > _objectList.count()) {
qWarning() << "Invalid index index:count" << i << _objectList.count();
}
if(object) {
QQmlEngine::setObjectOwnership(object, QQmlEngine::CppOwnership);
// Look for a dirtyChanged signal on the object
if (object->metaObject()->indexOfSignal(QMetaObject::normalizedSignature("dirtyChanged(bool)")) != -1) {
if (!_skipDirtyFirstItem || i != 0) {
QObject::connect(object, SIGNAL(dirtyChanged(bool)), this, SLOT(_childDirtyChanged(bool)));
}
}
}
_objectList.insert(i, object);
insertRows(i, 1);
setDirty(true);
}
......
......@@ -27,7 +27,7 @@ public:
/// a dirty property and dirtyChanged signal.
Q_PROPERTY(bool dirty READ dirty WRITE setDirty NOTIFY dirtyChanged)
Q_INVOKABLE QObject* get(int index) { return _objectList[index]; }
Q_INVOKABLE QObject* get(int index);
// Property accessors
......
......@@ -61,6 +61,9 @@ SetupPage {
QGCTabBar {
id: bar
width: parent.width
Component.onCompleted: {
currentIndex = _activeJoystick && _activeJoystick.calibrated ? 0 : 2
}
anchors.top: parent.top
QGCTabButton {
text: qsTr("General")
......
......@@ -144,20 +144,38 @@ Item {
visible: advancedSettings.checked
}
//-----------------------------------------------------------------
//-- Message Frequency
//-- Axis Message Frequency
QGCLabel {
text: qsTr("Message frequency (Hz):")
text: qsTr("Axis frequency (Hz):")
Layout.alignment: Qt.AlignVCenter
visible: advancedSettings.checked
}
QGCTextField {
text: _activeJoystick.frequency
text: _activeJoystick.axisFrequency
enabled: advancedSettings.checked
validator: DoubleValidator { bottom: 0.25; top: 100.0; }
validator: DoubleValidator { bottom: 0.25; top: 50.0; }
inputMethodHints: Qt.ImhFormattedNumbersOnly
Layout.alignment: Qt.AlignVCenter
onEditingFinished: {
_activeJoystick.frequency = parseFloat(text)
_activeJoystick.axisFrequency = parseFloat(text)
}
visible: advancedSettings.checked
}
//-----------------------------------------------------------------
//-- Button Repeat Frequency
QGCLabel {
text: qsTr("Button repeat frequency (Hz):")
Layout.alignment: Qt.AlignVCenter
visible: advancedSettings.checked
}
QGCTextField {
text: _activeJoystick.buttonFrequency
enabled: advancedSettings.checked
validator: DoubleValidator { bottom: 0.25; top: 50.0; }
inputMethodHints: Qt.ImhFormattedNumbersOnly
Layout.alignment: Qt.AlignVCenter
onEditingFinished: {
_activeJoystick.buttonFrequency = parseFloat(text)
}
visible: advancedSettings.checked
}
......
......@@ -46,11 +46,7 @@ Item {
Row {
spacing: ScreenTools.defaultFontPixelWidth
property bool pressed
QGCCheckBox {
anchors.verticalCenter: parent.verticalCenter
checked: _activeJoystick ? _activeJoystick.buttonActions[modelData] !== "" : false
onClicked: _activeJoystick.setButtonAction(modelData, checked ? buttonActionCombo.textAt(buttonActionCombo.currentIndex) : "")
}
property var currentAssignableAction: _activeJoystick ? _activeJoystick.assignableActions.get(buttonActionCombo.currentIndex) : null
Rectangle {
anchors.verticalCenter: parent.verticalCenter
width: ScreenTools.defaultFontPixelHeight * 1.5
......@@ -69,9 +65,35 @@ Item {
QGCComboBox {
id: buttonActionCombo
width: ScreenTools.defaultFontPixelWidth * 26
model: _activeJoystick ? _activeJoystick.actions : 0
onActivated: _activeJoystick.setButtonAction(modelData, textAt(index))
Component.onCompleted: currentIndex = find(_activeJoystick.buttonActions[modelData])
model: _activeJoystick ? _activeJoystick.assignableActionTitles : []
onActivated: {
_activeJoystick.setButtonAction(modelData, textAt(index))
}
Component.onCompleted: {
if(_activeJoystick) {
var i = find(_activeJoystick.buttonActions[modelData])
if(i < 0) i = 0
currentIndex = i
}
}
}
QGCCheckBox {
id: repeatCheck
text: qsTr("Repeat")
enabled: currentAssignableAction && _activeJoystick.calibrated && currentAssignableAction.canRepeat
onClicked: {
_activeJoystick.setButtonRepeat(modelData, checked)
}
Component.onCompleted: {
if(_activeJoystick) {
checked = _activeJoystick.getButtonRepeat(modelData)
}
}
anchors.verticalCenter: parent.verticalCenter
}
Item {
width: ScreenTools.defaultFontPixelWidth * 2
height: 1
}
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment