Merge pull request #705 from EdgeTX/mpm-proto-list2

[color] Dynamic MPM proto list
This commit is contained in:
Raphael Coeffic
2021-09-13 08:35:47 +02:00
committed by GitHub
17 changed files with 812 additions and 165 deletions
+4
View File
@@ -93,6 +93,10 @@ if(PXX2 OR MULTIMODULE)
add_gui_src(radio_spectrum_analyser.cpp)
endif()
if(MULTIMODULE)
add_gui_src(multi_rfprotos.cpp)
endif()
if(GHOST)
add_gui_src(radio_ghost_module_config.cpp)
endif()
+49 -140
View File
@@ -19,6 +19,9 @@
*/
#include "model_setup.h"
#include "multi_rfprotos.h"
#include "io/multi_protolist.h"
#include "opentx.h"
#include "libopenui.h"
#include "storage/modelslist.h"
@@ -26,87 +29,6 @@
#define SET_DIRTY() storageDirty(EE_MODEL)
class RFProtocols
{
private:
const char * protocols;
int maxProtocols;
std::vector<std::string> labels;
uint8_t getProtoStringLength();
public:
RFProtocols(const char * rfProtocols, int max);
int findProtocolIndex(int rfProtcolIndex);
int lookupProtocolIndex(int selectedIndex);
std::vector<std::string> &getLabels();
};
uint8_t RFProtocols::getProtoStringLength()
{
return protocols[0];
}
RFProtocols::RFProtocols(const char *protocols, int max)
{
this->protocols = protocols;
maxProtocols = max;
uint8_t len = protocols[0];
const char * value = &protocols[1];
for (int i = 0; i <= max; i++)
{
std::string label = std::string(value, min<uint8_t>(len, strlen(value)));
labels.emplace_back(label);
value += len;
}
std::sort(labels.begin(), labels.end());
}
std::vector<std::string> &RFProtocols::getLabels()
{
return labels;
}
int RFProtocols::lookupProtocolIndex(int selectedIndex)
{
uint8_t len = getProtoStringLength();
const char *p = &protocols[1];
const char *protocolLabel = labels.at(selectedIndex).c_str();
int protocolIndex = 0;
for (; protocolIndex < maxProtocols; protocolIndex++)
{
if (strncmp(p, protocolLabel, min((int)strlen(protocolLabel), (int)len)) == 0)
break;
p += len;
}
assert(protocolIndex < maxProtocols);
return protocolIndex;
}
int RFProtocols::findProtocolIndex(int rfProtocolIndex)
{
uint8_t len = getProtoStringLength();
const char *protocolLabel = &protocols[1] + len * rfProtocolIndex;
auto result = std::find_if(labels.begin(), labels.end(), [protocolLabel, len](std::string &a) {
auto theSame = strncmp(a.c_str(), protocolLabel, max((int)a.length(), (int)len)) == 0;
return theSame;
});
assert(result != labels.end());
return result - labels.begin();
}
// global pointer intialized once on the first call to update()
RFProtocols *rfProtocols = nullptr;
std::string switchWarninglabel(swsrc_t index)
{
static const char switchPositions[] = {
@@ -910,70 +832,65 @@ class ModuleWindow : public FormGroup {
}
#if defined(MULTIMODULE)
else if (isModuleMultimodule(moduleIdx)) {
Choice * mmSubProtocol = nullptr;
Choice * mmSubProto = nullptr;
grid.nextLine();
new StaticText(this, grid.getLabelSlot(true), STR_RF_PROTOCOL, 0, COLOR_THEME_PRIMARY1);
new StaticText(this, grid.getLabelSlot(true), STR_RF_PROTOCOL, 0,
COLOR_THEME_PRIMARY1);
// Multi type (CUSTOM, brand A, brand B,...)
int multiRfProto = g_model.moduleData[moduleIdx].getMultiProtocol();
// Grid count for narrow/wide screen
int count =
LCD_W < LCD_H
? 1
: (g_model.moduleData[moduleIdx].multi.customProto ? 3 : 2);
// initialize rfProtocols if it is not initialized already. This is a hack
// because i dont know how to construct it at the exact right time after the module
// is loaded
if (rfProtocols == nullptr)
rfProtocols = new RFProtocols(STR_MULTI_PROTOCOLS, MODULE_SUBTYPE_MULTI_LAST);
multiRfProto = rfProtocols->findProtocolIndex(multiRfProto);
// Grid count for narrow/wide screen
int count = LCD_W < LCD_H ? 1 : (g_model.moduleData[moduleIdx].multi.customProto ? 3 : 2);
rfChoice = new Choice(
this,
grid.getFieldSlot(count, 0),
rfProtocols->getLabels(), MODULE_SUBTYPE_MULTI_FIRST,
MODULE_SUBTYPE_MULTI_LAST, GET_DEFAULT(multiRfProto),
rfChoice = new MultiProtoChoice(
this, grid.getFieldSlot(count, 0), moduleIdx,
[=](int32_t newValue) {
newValue = rfProtocols->lookupProtocolIndex(newValue);
g_model.moduleData[moduleIdx].setMultiProtocol(newValue);
g_model.moduleData[moduleIdx].subType = 0;
if (mmSubProtocol != nullptr) mmSubProtocol->invalidate(); // always null ???
resetMultiProtocolsOptions(moduleIdx);
MultiModuleStatus& status = getMultiModuleStatus(moduleIdx);
status.invalidate();
uint32_t startUpdate = RTOS_GET_MS();
while(!status.isValid() && (RTOS_GET_MS() - startUpdate < 250));
SET_DIRTY();
update();
rfChoice->setFocus(SET_FOCUS_DEFAULT);// really valid ???
});
if (MULTIMODULE_HAS_SUBTYPE(moduleIdx)) {
if (rfChoice)
rfChoice->setFocus(SET_FOCUS_DEFAULT);
},
[=]() { update(); });
auto *rfProto =
MultiRfProtocols::instance(moduleIdx)->getProto(
g_model.moduleData[moduleIdx].getMultiProtocol());
if (rfProto && !rfProto->subProtos.empty()) {
// Subtype (D16, DSMX,...)
// Grid count for narrow/wide screen
count = LCD_W < LCD_H ? 1 : 2;
int index = 1;
if (count == 1)
{
grid.nextLine();
index = 0;
}
const mm_protocol_definition *pdef = getMultiProtocolDefinition(
g_model.moduleData[moduleIdx].getMultiProtocol());
if (pdef->maxSubtype > 0) {
mmSubProtocol = new Choice(
this, grid.getFieldSlot(count, index), pdef->subTypeString, 0,
pdef->maxSubtype,
// Grid count for narrow/wide screen
count = LCD_W < LCD_H ? 1 : 2;
int index = 1;
if (count == 1) {
grid.nextLine();
index = 0;
}
mmSubProto = new Choice(
this, grid.getFieldSlot(count, index),
rfProto->subProtos, 0, rfProto->subProtos.size() - 1,
[=]() { return g_model.moduleData[moduleIdx].subType; },
[=](int16_t newValue) {
g_model.moduleData[moduleIdx].subType = newValue;
resetMultiProtocolsOptions(moduleIdx);
SET_DIRTY();
// update(); ??? how to set focus properly ???
update();
//mmSubProtocol->setFocus(SET_FOCUS_DEFAULT);
if (mmSubProto != nullptr) mmSubProto->setFocus(SET_FOCUS_DEFAULT);
});
}
}
grid.nextLine();
@@ -985,24 +902,14 @@ class ModuleWindow : public FormGroup {
return std::string(msg);
}, COLOR_THEME_PRIMARY1);
// Multimodule sync
/*if (multiSyncStatus.isValid()) {
grid.nextLine();
new StaticText(this, grid.getLabelSlot(true), STR_MODULE_SYNC, 0, COLOR_THEME_PRIMARY1);
multiSyncStatus.getRefreshString(statusText);
new StaticText(this, grid.getFieldSlot(), statusText, 0, COLOR_THEME_PRIMARY1);
}*/
const uint8_t multi_proto = g_model.moduleData[moduleIdx].getMultiProtocol();
if (MULTIMODULE_PROTOCOL_KNOWN(moduleIdx)) {
if (rfProto) {
// Multi optional feature row
const char *title = getMultiOptionTitleStatic(moduleIdx);
const char *title = rfProto->getOptionStr();
if (title != nullptr) {
grid.nextLine();
new StaticText(this, grid.getLabelSlot(true), title, 0, COLOR_THEME_PRIMARY1);
// int optionValue =
// g_model.moduleData[moduleIdx].multi.optionValue;
int8_t min, max;
getMultiOptionValues(multi_proto, min, max);
@@ -1102,7 +1009,7 @@ class ModuleWindow : public FormGroup {
GET_SET_DEFAULT(
g_model.moduleData[moduleIdx].multi.disableTelemetry));
if (MULTI_DISABLE_CHAN_MAP_ROW_STATIC(moduleIdx) != HIDDEN_ROW) {
if (rfProto && rfProto->supportsDisableMapping()) {
// Disable channel mapping
grid.nextLine();
new StaticText(this, grid.getLabelSlot(true), STR_DISABLE_CH_MAP, 0, COLOR_THEME_PRIMARY1);
@@ -1382,7 +1289,9 @@ class ModuleWindow : public FormGroup {
void checkEvents() override
{
if (isModuleFailsafeAvailable(moduleIdx) != hasFailsafe) {
if (isModuleFailsafeAvailable(moduleIdx) != hasFailsafe
&& rfChoice && !rfChoice->isEditMode()) {
hasFailsafe = isModuleFailsafeAvailable(moduleIdx);
update();
}
+130
View File
@@ -0,0 +1,130 @@
/*
* 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 "io/multi_protolist.h"
#include "multi_rfprotos.h"
#include "strhelpers.h"
#include "progress.h"
// TODO: format for LCD_WIDTH > LCD_HEIGHT
constexpr rect_t RFSCAN_DIALOG_RECT = {
50, 100, LCD_W - 100, LCD_H - 200
};
// TODO: translation
const char* RFSCAN_TITLE = "MPM: Scanning protocols...";
class RfScanDialog : public Dialog
{
MultiRfProtocols* protos;
uint32_t lastUpdate = 0;
Progress* progress;
std::function<void()> onClose;
public:
RfScanDialog(Window* parent, MultiRfProtocols* protos,
std::function<void()> onClose) :
Dialog(parent, RFSCAN_TITLE, RFSCAN_DIALOG_RECT),
protos(protos),
progress(new Progress(&content->form,
{PAGE_PADDING, PAGE_PADDING,
content->form.width() - 2 * PAGE_PADDING,
content->form.height() - 2 * PAGE_PADDING})),
onClose(std::move(onClose))
{
// disable canceling dialog
setCloseWhenClickOutside(false);
setFocus();
}
void showProgress()
{
progress->setValue((int)(protos->getProgress() * 100.0));
}
// disable keys
void onEvent(event_t) override { return; }
void checkEvents() override
{
if (!protos->isScanning()) {
deleteLater();
onClose();
} else if (RTOS_GET_MS() - lastUpdate >= 200) {
showProgress();
lastUpdate = RTOS_GET_MS();
}
Dialog::checkEvents();
}
};
MultiProtoChoice::MultiProtoChoice(FormGroup* parent, const rect_t& rect,
unsigned int moduleIdx,
std::function<void(int)> setValue,
std::function<void()> updateForm) :
Choice(
parent, rect, 0, 0,
[=] { return g_model.moduleData[moduleIdx].getMultiProtocol(); },
setValue),
moduleIdx(moduleIdx)
{
TRACE("MultiProtoChoice::MultiProtoChoice(%p)", this);
protos = MultiRfProtocols::instance(moduleIdx);
protos->triggerScan();
if (protos->isScanning()) {
new RfScanDialog(parent, protos, std::move(updateForm));
} else {
TRACE("!protos->isScanning()");
}
setTextHandler([=](int value) {
return protos->getProtoLabel(value);
});
}
void MultiProtoChoice::openMenu()
{
menu = new Menu(this);
if (!menuTitle.empty()) menu->setTitle(menuTitle);
menu->setCloseHandler([=]() { setEditMode(false); });
setEditMode(true);
invalidate();
protos->fillList([=](const MultiRfProtocols::RfProto& p){
addProto(p.proto, p.label.c_str());
});
int idx = protos->getIndex(g_model.moduleData[moduleIdx].getMultiProtocol());
if (idx >= 0) menu->select(idx);
}
void MultiProtoChoice::addProto(unsigned int proto, const char* protocolName)
{
addValue(protocolName);
menu->addLine(protocolName, [=]() { setValue(proto); });
}
+42
View File
@@ -0,0 +1,42 @@
/*
* 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 "libopenui.h"
class MultiRfProtocols;
class MultiProtoChoice : public Choice
{
unsigned int moduleIdx;
MultiRfProtocols* protos = nullptr;
Menu* menu = nullptr;
public:
MultiProtoChoice(FormGroup *parent, const rect_t &rect, unsigned int moduleIdx,
std::function<void(int)> setValue, std::function<void()> updateForm);
void openMenu() override;
void addProto(unsigned int proto, const char* protocolName);
};
+2 -2
View File
@@ -952,7 +952,7 @@ const char STR_SUBTYPE_HUBSAN[] = "\004""H107""H301""H501";
const char STR_SUBTYPE_FRSKY[] = "\011""D16\0 ""D8\0 ""D16 8ch\0 ""V8\0 ""LBT(EU)\0 ""LBT 8ch\0 ""D8Cloned\0""D16Cloned";
const char STR_SUBTYPE_HISKY[] = "\005""Std\0 ""HK310";
const char STR_SUBTYPE_V2X2[] = "\006""Std\0 ""JXD506""MR101\0";
const char STR_SUBTYPE_DSM[] = "\004""2 1F""2 2F""X 1F""X 2F";
const char STR_SUBTYPE_DSM[] = "\004""2 1F""2 2F""X 1F""X 2F""Auto""R 1F";
const char STR_SUBTYPE_DEVO[] = "\004""8ch\0""10ch""12ch""6ch\0""7ch\0";
const char STR_SUBTYPE_YD717[] = "\007""Std\0 ""SkyWlkr""Syma X4""XINXUN\0""NIHUI\0 ";
const char STR_SUBTYPE_KN[] = "\006""WLtoys""FeiLun";
@@ -1031,7 +1031,7 @@ const mm_protocol_definition multi_protocols[] = {
{MODULE_SUBTYPE_MULTI_FRSKY, 7, false, false, STR_SUBTYPE_FRSKY, STR_MULTI_RFTUNE},
{MODULE_SUBTYPE_MULTI_HISKY, 1, false, true, STR_SUBTYPE_HISKY, nullptr},
{MODULE_SUBTYPE_MULTI_V2X2, 2, false, false, STR_SUBTYPE_V2X2, nullptr},
{MODULE_SUBTYPE_MULTI_DSM2, 3, false, true, STR_SUBTYPE_DSM, STR_MULTI_MAX_THROW},
{MODULE_SUBTYPE_MULTI_DSM2, 5, false, true, STR_SUBTYPE_DSM, STR_MULTI_MAX_THROW},
{MODULE_SUBTYPE_MULTI_DEVO, 4, false, true, STR_SUBTYPE_DEVO, STR_MULTI_FIXEDID},
{MODULE_SUBTYPE_MULTI_YD717, 4, false, false, STR_SUBTYPE_YD717, nullptr},
{MODULE_SUBTYPE_MULTI_KN, 1, false, false, STR_SUBTYPE_KN, nullptr},
+6 -3
View File
@@ -274,21 +274,24 @@ inline bool MULTIMODULE_PROTOCOL_KNOWN(uint8_t moduleIdx)
inline bool MULTIMODULE_HAS_SUBTYPE(uint8_t moduleIdx)
{
MultiModuleStatus &status = getMultiModuleStatus(moduleIdx);
int proto = g_model.moduleData[moduleIdx].getMultiProtocol();
if (g_model.moduleData[moduleIdx].getMultiProtocol() == MODULE_SUBTYPE_MULTI_FRSKY) {
if (proto == MODULE_SUBTYPE_MULTI_FRSKY) {
return true;
}
if (status.isValid()) {
TRACE("(%d) status.protocolSubNbr = %d", proto, status.protocolSubNbr);
return status.protocolSubNbr > 0;
}
else
{
if (g_model.moduleData[moduleIdx].getMultiProtocol() > MODULE_SUBTYPE_MULTI_LAST) {
if (proto > MODULE_SUBTYPE_MULTI_LAST) {
return true;
}
else {
return getMultiProtocolDefinition(g_model.moduleData[moduleIdx].getMultiProtocol())->subTypeString != nullptr;
auto subProto = getMultiProtocolDefinition(proto);
return subProto->subTypeString != nullptr;
}
}
}