split trainer setup and bind menu
This commit is contained in:
committed by
Raphael Coeffic
parent
209b253064
commit
ea16e82fe7
@@ -50,6 +50,8 @@ set(GUI_SRC
|
||||
# menus.cpp
|
||||
menu_model.cpp
|
||||
model_select.cpp
|
||||
bind_menu_d16.cpp
|
||||
trainer_setup.cpp
|
||||
model_setup.cpp
|
||||
model_flightmodes.cpp
|
||||
model_inputs.cpp
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "bind_menu_d16.h"
|
||||
#include "opentx.h"
|
||||
|
||||
BindChoiceMenu::BindChoiceMenu(Window *parent, uint8_t moduleIdx,
|
||||
std::function<void()> onPress,
|
||||
std::function<void()> onCancel) :
|
||||
Menu(parent), moduleIdx(moduleIdx), onPressHandler(std::move(onPress))
|
||||
{
|
||||
if (isTelemAllowedOnBind(moduleIdx)) {
|
||||
addLine(STR_BINDING_1_8_TELEM_ON, [=]() { onSelect(Bind_1_8_TELEM_ON); });
|
||||
}
|
||||
addLine(STR_BINDING_1_8_TELEM_OFF, [=]() { onSelect(Bind_1_8_TELEM_OFF); });
|
||||
|
||||
if (isBindCh9To16Allowed(moduleIdx)) {
|
||||
if (isTelemAllowedOnBind(moduleIdx)) {
|
||||
addLine(STR_BINDING_9_16_TELEM_ON,
|
||||
[=]() { onSelect(Bind_9_16_TELEM_ON); });
|
||||
}
|
||||
addLine(STR_BINDING_9_16_TELEM_OFF,
|
||||
[=]() { onSelect(Bind_9_16_TELEM_OFF); });
|
||||
}
|
||||
setTitle(STR_SELECT_MODE);
|
||||
setCancelHandler(onCancel);
|
||||
}
|
||||
|
||||
void BindChoiceMenu::onSelect(BindChanMode mode)
|
||||
{
|
||||
bool receiverTelemetry = false;
|
||||
bool receiverHigherChannels = false;
|
||||
|
||||
switch (mode) {
|
||||
case Bind_1_8_TELEM_ON:
|
||||
receiverTelemetry = true;
|
||||
break;
|
||||
case Bind_1_8_TELEM_OFF:
|
||||
break;
|
||||
case Bind_9_16_TELEM_ON:
|
||||
receiverTelemetry = true;
|
||||
receiverHigherChannels = true;
|
||||
break;
|
||||
case Bind_9_16_TELEM_OFF:
|
||||
receiverHigherChannels = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (isModuleMultimodule(moduleIdx)) {
|
||||
g_model.moduleData[moduleIdx].multi.receiverTelemetryOff =
|
||||
!receiverTelemetry;
|
||||
g_model.moduleData[moduleIdx].multi.receiverHigherChannels =
|
||||
receiverHigherChannels;
|
||||
} else {
|
||||
g_model.moduleData[moduleIdx].pxx.receiverTelemetryOff = !receiverTelemetry;
|
||||
g_model.moduleData[moduleIdx].pxx.receiverHigherChannels =
|
||||
receiverHigherChannels;
|
||||
}
|
||||
|
||||
if (onPressHandler) onPressHandler();
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "menu.h"
|
||||
|
||||
class BindChoiceMenu : public Menu
|
||||
{
|
||||
enum BindChanMode {
|
||||
Bind_1_8_TELEM_ON,
|
||||
Bind_1_8_TELEM_OFF,
|
||||
Bind_9_16_TELEM_ON,
|
||||
Bind_9_16_TELEM_OFF,
|
||||
};
|
||||
|
||||
uint8_t moduleIdx;
|
||||
std::function<void()> onPressHandler;
|
||||
|
||||
void onSelect(BindChanMode mode);
|
||||
|
||||
public:
|
||||
BindChoiceMenu(Window *parent, uint8_t moduleIdx,
|
||||
std::function<void()> onPress, std::function<void()> onCancel);
|
||||
};
|
||||
@@ -28,12 +28,15 @@
|
||||
#include "opentx.h"
|
||||
#include "libopenui.h"
|
||||
#include "storage/modelslist.h"
|
||||
#include "algorithm"
|
||||
#include "bind_menu_d16.h"
|
||||
#include "trainer_setup.h"
|
||||
|
||||
#if defined(PXX2)
|
||||
#include "access_settings.h"
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#define SET_DIRTY() storageDirty(EE_MODEL)
|
||||
|
||||
std::string switchWarninglabel(swsrc_t index)
|
||||
@@ -169,352 +172,6 @@ class FailSafePage : public Page {
|
||||
uint8_t moduleIdx;
|
||||
};
|
||||
|
||||
class BindChoiceMenu : public Menu
|
||||
{
|
||||
protected:
|
||||
enum BindChanMode {
|
||||
Bind_1_8_TELEM_ON,
|
||||
Bind_1_8_TELEM_OFF,
|
||||
Bind_9_16_TELEM_ON,
|
||||
Bind_9_16_TELEM_OFF,
|
||||
};
|
||||
|
||||
uint8_t moduleIdx;
|
||||
std::function<void()> onPressHandler;
|
||||
|
||||
void onSelect(BindChanMode mode)
|
||||
{
|
||||
bool receiverTelemetry = false;
|
||||
bool receiverHigherChannels = false;
|
||||
|
||||
switch (mode) {
|
||||
case Bind_1_8_TELEM_ON:
|
||||
receiverTelemetry = true;
|
||||
break;
|
||||
case Bind_1_8_TELEM_OFF:
|
||||
break;
|
||||
case Bind_9_16_TELEM_ON:
|
||||
receiverTelemetry = true;
|
||||
receiverHigherChannels = true;
|
||||
break;
|
||||
case Bind_9_16_TELEM_OFF:
|
||||
receiverHigherChannels = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (isModuleMultimodule(moduleIdx)) {
|
||||
g_model.moduleData[moduleIdx].multi.receiverTelemetryOff =
|
||||
!receiverTelemetry;
|
||||
g_model.moduleData[moduleIdx].multi.receiverHigherChannels =
|
||||
receiverHigherChannels;
|
||||
} else {
|
||||
g_model.moduleData[moduleIdx].pxx.receiverTelemetryOff =
|
||||
!receiverTelemetry;
|
||||
g_model.moduleData[moduleIdx].pxx.receiverHigherChannels =
|
||||
receiverHigherChannels;
|
||||
}
|
||||
|
||||
if (onPressHandler) onPressHandler();
|
||||
}
|
||||
|
||||
public:
|
||||
BindChoiceMenu(Window *parent, uint8_t moduleIdx,
|
||||
std::function<void()> onPress,
|
||||
std::function<void()> onCancel) :
|
||||
Menu(parent), moduleIdx(moduleIdx), onPressHandler(std::move(onPress))
|
||||
{
|
||||
if (isTelemAllowedOnBind(moduleIdx)) {
|
||||
addLine(STR_BINDING_1_8_TELEM_ON, [=]() { onSelect(Bind_1_8_TELEM_ON); });
|
||||
}
|
||||
addLine(STR_BINDING_1_8_TELEM_OFF, [=]() { onSelect(Bind_1_8_TELEM_OFF); });
|
||||
|
||||
if (isBindCh9To16Allowed(moduleIdx)) {
|
||||
if (isTelemAllowedOnBind(moduleIdx)) {
|
||||
addLine(STR_BINDING_9_16_TELEM_ON,
|
||||
[=]() { onSelect(Bind_9_16_TELEM_ON); });
|
||||
}
|
||||
addLine(STR_BINDING_9_16_TELEM_OFF,
|
||||
[=]() { onSelect(Bind_9_16_TELEM_OFF); });
|
||||
}
|
||||
setTitle(STR_SELECT_MODE);
|
||||
setCancelHandler(onCancel);
|
||||
}
|
||||
};
|
||||
|
||||
class TrChoice : public Choice
|
||||
{
|
||||
public:
|
||||
TrChoice(FormGroup *parent, const rect_t &rect, const char **values, int vmin,
|
||||
int vmax, std::function<int()> getValue,
|
||||
std::function<void(int)> setValue = nullptr,
|
||||
bool *menuOpen = nullptr) :
|
||||
Choice(parent, rect, values, vmin, vmax, getValue, setValue),
|
||||
menuOpen(menuOpen)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
void openMenu()
|
||||
{
|
||||
if (menuOpen) *menuOpen = true;
|
||||
Choice::openMenu();
|
||||
}
|
||||
|
||||
private:
|
||||
bool *menuOpen;
|
||||
};
|
||||
|
||||
class TrainerModuleWindow : public FormGroup
|
||||
{
|
||||
public:
|
||||
TrainerModuleWindow(FormWindow *parent, const rect_t &rect) :
|
||||
FormGroup(parent, rect, FORWARD_SCROLL | FORM_FORWARD_FOCUS)
|
||||
{
|
||||
update();
|
||||
}
|
||||
|
||||
void checkEvents() override
|
||||
{
|
||||
#if defined(BLUETOOTH)
|
||||
if (popupopen) {
|
||||
if (bluetooth.state == BLUETOOTH_STATE_DISCOVER_START ||
|
||||
bluetooth.state == BLUETOOTH_STATE_DISCOVER_END) {
|
||||
int cnt = min<uint8_t>(reusableBuffer.moduleSetup.bt.devicesCount,
|
||||
MAX_BLUETOOTH_DISTANT_ADDR);
|
||||
if (devicecount < cnt) {
|
||||
for (int i = 0; i < cnt - devicecount; i++) {
|
||||
int index = devicecount + i;
|
||||
btDiscoverMenuAddItem(reusableBuffer.moduleSetup.bt.devices[index]);
|
||||
}
|
||||
devicecount = cnt;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bluetooth.state != lastbluetoothstate) {
|
||||
if (!popupopen && !trChoiceOpen) update();
|
||||
lastbluetoothstate = bluetooth.state;
|
||||
}
|
||||
#endif
|
||||
FormGroup::checkEvents();
|
||||
}
|
||||
|
||||
void update()
|
||||
{
|
||||
FormGridLayout grid;
|
||||
clear();
|
||||
|
||||
new StaticText(this, grid.getLabelSlot(true), STR_MODE, 0,COLOR_THEME_PRIMARY1);
|
||||
trainerChoice = new TrChoice(
|
||||
this, grid.getFieldSlot(), STR_VTRAINERMODES, 0, TRAINER_MODE_MAX(),
|
||||
GET_DEFAULT(g_model.trainerData.mode),
|
||||
[=](int32_t newValue) {
|
||||
#if defined(BLUETOOTH)
|
||||
memclear(bluetooth.distantAddr, sizeof(bluetooth.distantAddr));
|
||||
bluetooth.state = BLUETOOTH_STATE_OFF;
|
||||
#endif
|
||||
g_model.trainerData.mode = newValue;
|
||||
SET_DIRTY();
|
||||
update();
|
||||
trainerChoice->setFocus(SET_FOCUS_DEFAULT);
|
||||
trChoiceOpen = false;
|
||||
},
|
||||
&trChoiceOpen);
|
||||
trainerChoice->setAvailableHandler(isTrainerModeAvailable);
|
||||
|
||||
grid.nextLine();
|
||||
|
||||
if (g_model.isTrainerTraineeEnable()) {
|
||||
#if defined(BLUETOOTH)
|
||||
if (g_model.trainerData.mode == TRAINER_MODE_MASTER_BLUETOOTH) {
|
||||
btDistAddress = new StaticText(this, grid.getFieldSlot(true), "---", 0,
|
||||
COLOR_THEME_PRIMARY1);
|
||||
if (bluetooth.state == BLUETOOTH_STATE_CONNECTED)
|
||||
new StaticText(this, grid.getLabelSlot(true), STR_CONNECTED, 0,
|
||||
COLOR_THEME_PRIMARY1);
|
||||
else
|
||||
new StaticText(this, grid.getLabelSlot(true), STR_NOT_CONNECTED, 0,
|
||||
COLOR_THEME_PRIMARY1);
|
||||
|
||||
grid.nextLine();
|
||||
btMasterButton =
|
||||
new TextButton(this, grid.getFieldSlot(), "", [=]() -> uint8_t {
|
||||
if (bluetooth.distantAddr[0]) {
|
||||
bluetooth.state = BLUETOOTH_STATE_CLEAR_REQUESTED;
|
||||
memclear(bluetooth.distantAddr, sizeof(bluetooth.distantAddr));
|
||||
} else if (bluetooth.state < BLUETOOTH_STATE_IDLE) {
|
||||
bluetooth.state = BLUETOOTH_STATE_OFF;
|
||||
} else {
|
||||
reusableBuffer.moduleSetup.bt.devicesCount = 0;
|
||||
devicecount = 0;
|
||||
bluetooth.state = BLUETOOTH_STATE_DISCOVER_REQUESTED;
|
||||
btPopUpMenu = new Menu(parent);
|
||||
btPopUpMenu->setTitle(STR_BT_SELECT_DEVICE);
|
||||
popupopen = true;
|
||||
btPopUpMenu->setCloseHandler([=]() { popupopen = false; });
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
if (bluetooth.distantAddr[0]) {
|
||||
btDistAddress->setText(bluetooth.distantAddr);
|
||||
btMasterButton->setText(STR_CLEAR);
|
||||
} else if (bluetooth.state < BLUETOOTH_STATE_IDLE) {
|
||||
btMasterButton->setText(STR_BLUETOOTH_INIT);
|
||||
} else {
|
||||
btMasterButton->setText(STR_DISCOVER);
|
||||
}
|
||||
|
||||
grid.nextLine();
|
||||
|
||||
} else if (g_model.trainerData.mode == TRAINER_MODE_SLAVE_BLUETOOTH) {
|
||||
if (bluetooth.state == BLUETOOTH_STATE_CONNECTED) {
|
||||
new StaticText(this, grid.getLabelSlot(true), STR_CONNECTED, 0,
|
||||
COLOR_THEME_PRIMARY1);
|
||||
new StaticText(this, grid.getFieldSlot(), bluetooth.distantAddr, 0,
|
||||
COLOR_THEME_PRIMARY1);
|
||||
} else
|
||||
new StaticText(this, grid.getLabelSlot(true), STR_NOT_CONNECTED, 0,
|
||||
COLOR_THEME_PRIMARY1);
|
||||
|
||||
grid.nextLine();
|
||||
|
||||
new StaticText(this, grid.getLabelSlot(true), STR_CHANNELRANGE, 0,
|
||||
COLOR_THEME_PRIMARY1);
|
||||
channelStart = new NumberEdit(
|
||||
this, grid.getFieldSlot(2, 0), 1,
|
||||
MAX_OUTPUT_CHANNELS - BLUETOOTH_TRAINER_CHANNELS + 1,
|
||||
GET_DEFAULT(1 + g_model.trainerData.channelsStart));
|
||||
char chend[6];
|
||||
snprintf(chend, sizeof(chend), "%s%d", STR_CH,
|
||||
g_model.trainerData.channelsStart + BLUETOOTH_TRAINER_CHANNELS);
|
||||
|
||||
btChannelEnd = new StaticText(this, grid.getFieldSlot(2, 1), chend, 0,
|
||||
COLOR_THEME_PRIMARY1);
|
||||
channelStart->setPrefix(STR_CH);
|
||||
channelStart->setSetValueHandler([=](int32_t newValue) {
|
||||
g_model.trainerData.channelsStart = newValue - 1;
|
||||
g_model.trainerData.channelsCount = BLUETOOTH_TRAINER_CHANNELS - DEF_TRAINER_CHANNELS;
|
||||
char chend[6];
|
||||
snprintf(chend, sizeof(chend), "%s%d", STR_CH,
|
||||
g_model.trainerData.channelsStart + BLUETOOTH_TRAINER_CHANNELS);
|
||||
SET_DIRTY();
|
||||
btChannelEnd->setText(chend);
|
||||
});
|
||||
|
||||
grid.nextLine();
|
||||
} else
|
||||
#endif
|
||||
if (g_model.trainerData.mode == TRAINER_MODE_SLAVE) {
|
||||
#define PPMCH_START_MIN 1
|
||||
#define PPMCH_START_MAX (MAX_OUTPUT_CHANNELS - g_model.trainerData.channelsCount + 1 - DEF_TRAINER_CHANNELS)
|
||||
#define PPMCH_END_MIN (g_model.trainerData.channelsStart + MIN_TRAINER_CHANNELS)
|
||||
#define PPMCH_END_MAX min<uint8_t>(g_model.trainerData.channelsStart + MAX_TRAINER_CHANNELS, MAX_OUTPUT_CHANNELS)
|
||||
|
||||
new StaticText(this, grid.getLabelSlot(true), STR_CHANNELRANGE, 0,
|
||||
COLOR_THEME_PRIMARY1);
|
||||
channelStart = new NumberEdit(
|
||||
this, grid.getFieldSlot(2, 0), PPMCH_START_MIN, PPMCH_START_MAX,
|
||||
GET_DEFAULT(1 + g_model.trainerData.channelsStart));
|
||||
channelEnd = new NumberEdit(
|
||||
this, grid.getFieldSlot(2, 1), PPMCH_END_MIN, PPMCH_END_MAX,
|
||||
GET_DEFAULT((g_model.trainerData.channelsStart +
|
||||
DEF_TRAINER_CHANNELS +
|
||||
g_model.trainerData.channelsCount)));
|
||||
channelStart->setPrefix(STR_CH);
|
||||
channelEnd->setPrefix(STR_CH);
|
||||
channelStart->setSetValueHandler([=](int32_t newValue) {
|
||||
g_model.trainerData.channelsStart = newValue - 1;
|
||||
SET_DIRTY();
|
||||
channelEnd->setMin(PPMCH_END_MIN);
|
||||
channelEnd->setMax(PPMCH_END_MAX);
|
||||
channelEnd->invalidate();
|
||||
});
|
||||
channelEnd->setSetValueHandler([=](int32_t newValue) {
|
||||
g_model.trainerData.channelsCount =
|
||||
newValue - g_model.trainerData.channelsStart -
|
||||
DEF_TRAINER_CHANNELS;
|
||||
SET_DIRTY();
|
||||
channelStart->setMax(PPMCH_START_MAX);
|
||||
});
|
||||
|
||||
grid.nextLine();
|
||||
|
||||
// PPM frame
|
||||
new StaticText(this, grid.getLabelSlot(true), STR_PPMFRAME, 0,
|
||||
COLOR_THEME_PRIMARY1);
|
||||
|
||||
// PPM frame length
|
||||
auto edit = new NumberEdit(
|
||||
this, grid.getFieldSlot(3, 0), 125, 35 * 5 + 225,
|
||||
GET_DEFAULT(g_model.trainerData.frameLength * 5 + 225),
|
||||
SET_VALUE(g_model.trainerData.frameLength, (newValue - 225) / 5), 0,
|
||||
PREC1);
|
||||
edit->setStep(5);
|
||||
edit->setSuffix(STR_MS);
|
||||
|
||||
// PPM frame delay
|
||||
edit = new NumberEdit(
|
||||
this, grid.getFieldSlot(3, 1), 100, 800,
|
||||
GET_DEFAULT(g_model.trainerData.delay * 50 + 300),
|
||||
SET_VALUE(g_model.trainerData.delay, (newValue - 300) / 50));
|
||||
edit->setStep(50);
|
||||
edit->setSuffix(STR_US);
|
||||
|
||||
// PPM Polarity
|
||||
new Choice(this, grid.getFieldSlot(3, 2), STR_PPM_POL, 0, 1,
|
||||
GET_SET_DEFAULT(g_model.trainerData.pulsePol));
|
||||
grid.nextLine();
|
||||
}
|
||||
}
|
||||
#if defined(HARDWARE_TOUCH)
|
||||
new StaticText(this, grid.getLabelSlot(true));
|
||||
#endif
|
||||
auto par = getParent();
|
||||
par->moveWindowsTop(top() + 1, adjustHeight());
|
||||
par->adjustInnerHeight();
|
||||
}
|
||||
|
||||
protected:
|
||||
TrChoice *trainerChoice = nullptr;
|
||||
NumberEdit *channelStart = nullptr;
|
||||
NumberEdit *channelEnd = nullptr;
|
||||
bool trChoiceOpen = false;
|
||||
#if defined(BLUETOOTH)
|
||||
StaticText *btChannelEnd = nullptr;
|
||||
StaticText *btDistAddress = nullptr;
|
||||
TextButton *btMasterButton = nullptr;
|
||||
Menu *btPopUpMenu = nullptr;
|
||||
bool btCanceled = false;
|
||||
|
||||
private:
|
||||
bool popupopen = false;
|
||||
int devicecount = 0;
|
||||
uint8_t lastbluetoothstate = BLUETOOTH_STATE_OFF;
|
||||
|
||||
void btDiscoverMenuItemChosen()
|
||||
{
|
||||
if (bluetooth.state == BLUETOOTH_STATE_DISCOVER_SENT ||
|
||||
bluetooth.state == BLUETOOTH_STATE_DISCOVER_END) {
|
||||
int index = btPopUpMenu->selection();
|
||||
if (index >= 0 && index < reusableBuffer.moduleSetup.bt.devicesCount) {
|
||||
strncpy(bluetooth.distantAddr,
|
||||
reusableBuffer.moduleSetup.bt.devices[index],
|
||||
LEN_BLUETOOTH_ADDR);
|
||||
bluetooth.state = BLUETOOTH_STATE_BIND_REQUESTED;
|
||||
SET_DIRTY();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void btDiscoverMenuAddItem(const char *itm)
|
||||
{
|
||||
if (btPopUpMenu != nullptr && !btPopUpMenu->deleted())
|
||||
btPopUpMenu->addLine(
|
||||
itm, std::bind(&TrainerModuleWindow::btDiscoverMenuItemChosen, this));
|
||||
}
|
||||
|
||||
#endif
|
||||
};
|
||||
|
||||
class ModuleWindow : public FormGroup {
|
||||
public:
|
||||
|
||||
@@ -0,0 +1,285 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "trainer_setup.h"
|
||||
#include "opentx.h"
|
||||
|
||||
#define SET_DIRTY() storageDirty(EE_MODEL)
|
||||
|
||||
class TrChoice : public Choice
|
||||
{
|
||||
public:
|
||||
TrChoice(FormGroup *parent, const rect_t &rect, const char **values, int vmin,
|
||||
int vmax, std::function<int()> getValue,
|
||||
std::function<void(int)> setValue = nullptr,
|
||||
bool *menuOpen = nullptr) :
|
||||
Choice(parent, rect, values, vmin, vmax, getValue, setValue),
|
||||
menuOpen(menuOpen)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
void openMenu()
|
||||
{
|
||||
if (menuOpen) *menuOpen = true;
|
||||
Choice::openMenu();
|
||||
}
|
||||
|
||||
private:
|
||||
bool *menuOpen;
|
||||
};
|
||||
|
||||
TrainerModuleWindow::TrainerModuleWindow(FormWindow *parent,
|
||||
const rect_t &rect) :
|
||||
FormGroup(parent, rect, FORWARD_SCROLL | FORM_FORWARD_FOCUS)
|
||||
{
|
||||
update();
|
||||
}
|
||||
|
||||
void TrainerModuleWindow::checkEvents()
|
||||
{
|
||||
#if defined(BLUETOOTH)
|
||||
if (popupopen) {
|
||||
if (bluetooth.state == BLUETOOTH_STATE_DISCOVER_START ||
|
||||
bluetooth.state == BLUETOOTH_STATE_DISCOVER_END) {
|
||||
int cnt = min<uint8_t>(reusableBuffer.moduleSetup.bt.devicesCount,
|
||||
MAX_BLUETOOTH_DISTANT_ADDR);
|
||||
if (devicecount < cnt) {
|
||||
for (int i = 0; i < cnt - devicecount; i++) {
|
||||
int index = devicecount + i;
|
||||
btDiscoverMenuAddItem(reusableBuffer.moduleSetup.bt.devices[index]);
|
||||
}
|
||||
devicecount = cnt;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bluetooth.state != lastbluetoothstate) {
|
||||
if (!popupopen && !trChoiceOpen) update();
|
||||
lastbluetoothstate = bluetooth.state;
|
||||
}
|
||||
#endif
|
||||
FormGroup::checkEvents();
|
||||
}
|
||||
|
||||
void TrainerModuleWindow::update()
|
||||
{
|
||||
FormGridLayout grid;
|
||||
clear();
|
||||
|
||||
new StaticText(this, grid.getLabelSlot(true), STR_MODE, 0,
|
||||
COLOR_THEME_PRIMARY1);
|
||||
trainerChoice = new TrChoice(
|
||||
this, grid.getFieldSlot(), STR_VTRAINERMODES, 0, TRAINER_MODE_MAX(),
|
||||
GET_DEFAULT(g_model.trainerData.mode),
|
||||
[=](int32_t newValue) {
|
||||
#if defined(BLUETOOTH)
|
||||
memclear(bluetooth.distantAddr, sizeof(bluetooth.distantAddr));
|
||||
bluetooth.state = BLUETOOTH_STATE_OFF;
|
||||
#endif
|
||||
g_model.trainerData.mode = newValue;
|
||||
SET_DIRTY();
|
||||
update();
|
||||
trainerChoice->setFocus(SET_FOCUS_DEFAULT);
|
||||
trChoiceOpen = false;
|
||||
},
|
||||
&trChoiceOpen);
|
||||
trainerChoice->setAvailableHandler(isTrainerModeAvailable);
|
||||
|
||||
grid.nextLine();
|
||||
|
||||
if (g_model.isTrainerTraineeEnable()) {
|
||||
#if defined(BLUETOOTH)
|
||||
if (g_model.trainerData.mode == TRAINER_MODE_MASTER_BLUETOOTH) {
|
||||
btDistAddress = new StaticText(this, grid.getFieldSlot(true), "---", 0,
|
||||
COLOR_THEME_PRIMARY1);
|
||||
if (bluetooth.state == BLUETOOTH_STATE_CONNECTED)
|
||||
new StaticText(this, grid.getLabelSlot(true), STR_CONNECTED, 0,
|
||||
COLOR_THEME_PRIMARY1);
|
||||
else
|
||||
new StaticText(this, grid.getLabelSlot(true), STR_NOT_CONNECTED, 0,
|
||||
COLOR_THEME_PRIMARY1);
|
||||
|
||||
grid.nextLine();
|
||||
btMasterButton =
|
||||
new TextButton(this, grid.getFieldSlot(), "", [=]() -> uint8_t {
|
||||
if (bluetooth.distantAddr[0]) {
|
||||
bluetooth.state = BLUETOOTH_STATE_CLEAR_REQUESTED;
|
||||
memclear(bluetooth.distantAddr, sizeof(bluetooth.distantAddr));
|
||||
} else if (bluetooth.state < BLUETOOTH_STATE_IDLE) {
|
||||
bluetooth.state = BLUETOOTH_STATE_OFF;
|
||||
} else {
|
||||
reusableBuffer.moduleSetup.bt.devicesCount = 0;
|
||||
devicecount = 0;
|
||||
bluetooth.state = BLUETOOTH_STATE_DISCOVER_REQUESTED;
|
||||
btPopUpMenu = new Menu(parent);
|
||||
btPopUpMenu->setTitle(STR_BT_SELECT_DEVICE);
|
||||
popupopen = true;
|
||||
btPopUpMenu->setCloseHandler([=]() { popupopen = false; });
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
if (bluetooth.distantAddr[0]) {
|
||||
btDistAddress->setText(bluetooth.distantAddr);
|
||||
btMasterButton->setText(STR_CLEAR);
|
||||
} else if (bluetooth.state < BLUETOOTH_STATE_IDLE) {
|
||||
btMasterButton->setText(STR_BLUETOOTH_INIT);
|
||||
} else {
|
||||
btMasterButton->setText(STR_DISCOVER);
|
||||
}
|
||||
|
||||
grid.nextLine();
|
||||
|
||||
} else if (g_model.trainerData.mode == TRAINER_MODE_SLAVE_BLUETOOTH) {
|
||||
if (bluetooth.state == BLUETOOTH_STATE_CONNECTED) {
|
||||
new StaticText(this, grid.getLabelSlot(true), STR_CONNECTED, 0,
|
||||
COLOR_THEME_PRIMARY1);
|
||||
new StaticText(this, grid.getFieldSlot(), bluetooth.distantAddr, 0,
|
||||
COLOR_THEME_PRIMARY1);
|
||||
} else
|
||||
new StaticText(this, grid.getLabelSlot(true), STR_NOT_CONNECTED, 0,
|
||||
COLOR_THEME_PRIMARY1);
|
||||
|
||||
grid.nextLine();
|
||||
|
||||
new StaticText(this, grid.getLabelSlot(true), STR_CHANNELRANGE, 0,
|
||||
COLOR_THEME_PRIMARY1);
|
||||
channelStart =
|
||||
new NumberEdit(this, grid.getFieldSlot(2, 0), 1,
|
||||
MAX_OUTPUT_CHANNELS - BLUETOOTH_TRAINER_CHANNELS + 1,
|
||||
GET_DEFAULT(1 + g_model.trainerData.channelsStart));
|
||||
char chend[6];
|
||||
snprintf(chend, sizeof(chend), "%s%d", STR_CH,
|
||||
g_model.trainerData.channelsStart + BLUETOOTH_TRAINER_CHANNELS);
|
||||
|
||||
btChannelEnd = new StaticText(this, grid.getFieldSlot(2, 1), chend, 0,
|
||||
COLOR_THEME_PRIMARY1);
|
||||
channelStart->setPrefix(STR_CH);
|
||||
channelStart->setSetValueHandler([=](int32_t newValue) {
|
||||
g_model.trainerData.channelsStart = newValue - 1;
|
||||
g_model.trainerData.channelsCount =
|
||||
BLUETOOTH_TRAINER_CHANNELS - DEF_TRAINER_CHANNELS;
|
||||
char chend[6];
|
||||
snprintf(
|
||||
chend, sizeof(chend), "%s%d", STR_CH,
|
||||
g_model.trainerData.channelsStart + BLUETOOTH_TRAINER_CHANNELS);
|
||||
SET_DIRTY();
|
||||
btChannelEnd->setText(chend);
|
||||
});
|
||||
|
||||
grid.nextLine();
|
||||
} else
|
||||
#endif
|
||||
if (g_model.trainerData.mode == TRAINER_MODE_SLAVE) {
|
||||
#define PPMCH_START_MIN 1
|
||||
#define PPMCH_START_MAX \
|
||||
(MAX_OUTPUT_CHANNELS - g_model.trainerData.channelsCount + 1 - \
|
||||
DEF_TRAINER_CHANNELS)
|
||||
#define PPMCH_END_MIN (g_model.trainerData.channelsStart + MIN_TRAINER_CHANNELS)
|
||||
#define PPMCH_END_MAX \
|
||||
min<uint8_t>(g_model.trainerData.channelsStart + MAX_TRAINER_CHANNELS, \
|
||||
MAX_OUTPUT_CHANNELS)
|
||||
|
||||
new StaticText(this, grid.getLabelSlot(true), STR_CHANNELRANGE, 0,
|
||||
COLOR_THEME_PRIMARY1);
|
||||
channelStart = new NumberEdit(
|
||||
this, grid.getFieldSlot(2, 0), PPMCH_START_MIN, PPMCH_START_MAX,
|
||||
GET_DEFAULT(1 + g_model.trainerData.channelsStart));
|
||||
channelEnd = new NumberEdit(
|
||||
this, grid.getFieldSlot(2, 1), PPMCH_END_MIN, PPMCH_END_MAX,
|
||||
GET_DEFAULT((g_model.trainerData.channelsStart +
|
||||
DEF_TRAINER_CHANNELS +
|
||||
g_model.trainerData.channelsCount)));
|
||||
channelStart->setPrefix(STR_CH);
|
||||
channelEnd->setPrefix(STR_CH);
|
||||
channelStart->setSetValueHandler([=](int32_t newValue) {
|
||||
g_model.trainerData.channelsStart = newValue - 1;
|
||||
SET_DIRTY();
|
||||
channelEnd->setMin(PPMCH_END_MIN);
|
||||
channelEnd->setMax(PPMCH_END_MAX);
|
||||
channelEnd->invalidate();
|
||||
});
|
||||
channelEnd->setSetValueHandler([=](int32_t newValue) {
|
||||
g_model.trainerData.channelsCount =
|
||||
newValue - g_model.trainerData.channelsStart - DEF_TRAINER_CHANNELS;
|
||||
SET_DIRTY();
|
||||
channelStart->setMax(PPMCH_START_MAX);
|
||||
});
|
||||
|
||||
grid.nextLine();
|
||||
|
||||
// PPM frame
|
||||
new StaticText(this, grid.getLabelSlot(true), STR_PPMFRAME, 0,
|
||||
COLOR_THEME_PRIMARY1);
|
||||
|
||||
// PPM frame length
|
||||
auto edit = new NumberEdit(
|
||||
this, grid.getFieldSlot(3, 0), 125, 35 * 5 + 225,
|
||||
GET_DEFAULT(g_model.trainerData.frameLength * 5 + 225),
|
||||
SET_VALUE(g_model.trainerData.frameLength, (newValue - 225) / 5), 0,
|
||||
PREC1);
|
||||
edit->setStep(5);
|
||||
edit->setSuffix(STR_MS);
|
||||
|
||||
// PPM frame delay
|
||||
edit = new NumberEdit(
|
||||
this, grid.getFieldSlot(3, 1), 100, 800,
|
||||
GET_DEFAULT(g_model.trainerData.delay * 50 + 300),
|
||||
SET_VALUE(g_model.trainerData.delay, (newValue - 300) / 50));
|
||||
edit->setStep(50);
|
||||
edit->setSuffix(STR_US);
|
||||
|
||||
// PPM Polarity
|
||||
new Choice(this, grid.getFieldSlot(3, 2), STR_PPM_POL, 0, 1,
|
||||
GET_SET_DEFAULT(g_model.trainerData.pulsePol));
|
||||
grid.nextLine();
|
||||
}
|
||||
}
|
||||
#if defined(HARDWARE_TOUCH)
|
||||
new StaticText(this, grid.getLabelSlot(true));
|
||||
#endif
|
||||
auto par = getParent();
|
||||
par->moveWindowsTop(top() + 1, adjustHeight());
|
||||
par->adjustInnerHeight();
|
||||
}
|
||||
|
||||
#if defined(BLUETOOTH)
|
||||
void TrainerModuleWindow::btDiscoverMenuItemChosen()
|
||||
{
|
||||
if (bluetooth.state == BLUETOOTH_STATE_DISCOVER_SENT ||
|
||||
bluetooth.state == BLUETOOTH_STATE_DISCOVER_END) {
|
||||
int index = btPopUpMenu->selection();
|
||||
if (index >= 0 && index < reusableBuffer.moduleSetup.bt.devicesCount) {
|
||||
strncpy(bluetooth.distantAddr,
|
||||
reusableBuffer.moduleSetup.bt.devices[index], LEN_BLUETOOTH_ADDR);
|
||||
bluetooth.state = BLUETOOTH_STATE_BIND_REQUESTED;
|
||||
SET_DIRTY();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TrainerModuleWindow::btDiscoverMenuAddItem(const char *itm)
|
||||
{
|
||||
if (btPopUpMenu != nullptr && !btPopUpMenu->deleted())
|
||||
btPopUpMenu->addLine(
|
||||
itm, std::bind(&TrainerModuleWindow::btDiscoverMenuItemChosen, this));
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "form.h"
|
||||
#include "menu.h"
|
||||
#include "static.h"
|
||||
#include "button.h"
|
||||
#include "numberedit.h"
|
||||
|
||||
class TrChoice;
|
||||
|
||||
class TrainerModuleWindow : public FormGroup
|
||||
{
|
||||
public:
|
||||
TrainerModuleWindow(FormWindow *parent, const rect_t &rect);
|
||||
|
||||
void checkEvents() override;
|
||||
void update();
|
||||
|
||||
protected:
|
||||
TrChoice *trainerChoice = nullptr;
|
||||
NumberEdit *channelStart = nullptr;
|
||||
NumberEdit *channelEnd = nullptr;
|
||||
bool trChoiceOpen = false;
|
||||
#if defined(BLUETOOTH)
|
||||
StaticText *btChannelEnd = nullptr;
|
||||
StaticText *btDistAddress = nullptr;
|
||||
TextButton *btMasterButton = nullptr;
|
||||
Menu *btPopUpMenu = nullptr;
|
||||
bool btCanceled = false;
|
||||
|
||||
private:
|
||||
bool popupopen = false;
|
||||
int devicecount = 0;
|
||||
uint8_t lastbluetoothstate = BLUETOOTH_STATE_OFF;
|
||||
|
||||
void btDiscoverMenuItemChosen();
|
||||
void btDiscoverMenuAddItem(const char *itm);
|
||||
|
||||
#endif
|
||||
};
|
||||
Reference in New Issue
Block a user