diff --git a/radio/src/CMakeLists.txt b/radio/src/CMakeLists.txt index b8c302f44..d36706293 100644 --- a/radio/src/CMakeLists.txt +++ b/radio/src/CMakeLists.txt @@ -419,6 +419,7 @@ set(SRC stamp.cpp timers.cpp trainer.cpp + model_init.cpp ) if(GUI) diff --git a/radio/src/datastructs.h b/radio/src/datastructs.h index 0c29eb50a..ae07276b3 100644 --- a/radio/src/datastructs.h +++ b/radio/src/datastructs.h @@ -550,9 +550,9 @@ typedef uint8_t swarnenable_t; #elif defined(COLORLCD) #include "gui/colorlcd/layout.h" #include "gui/colorlcd/topbar.h" -#define LAYOUT_NAME_LEN 10 +#define LAYOUT_ID_LEN 10 PACK(struct CustomScreenData { - char layoutName[LAYOUT_NAME_LEN]; + char LayoutId[LAYOUT_ID_LEN]; Layout::PersistentData layoutData; }); #define CUSTOM_SCREENS_DATA \ diff --git a/radio/src/gui/colorlcd/layout.cpp b/radio/src/gui/colorlcd/layout.cpp index a84c397ca..c5bd2f3f9 100644 --- a/radio/src/gui/colorlcd/layout.cpp +++ b/radio/src/gui/colorlcd/layout.cpp @@ -75,7 +75,7 @@ void loadDefaultLayout() auto& screenData = g_model.screenData[0]; if (screen == nullptr && defaultLayout != nullptr) { - strcpy(screenData.layoutName, defaultLayout->getName()); + strcpy(screenData.LayoutId, defaultLayout->getId()); screen = defaultLayout->create(&screenData.layoutData); if (screen) { screen->attach(ViewMain::instance()); @@ -88,7 +88,7 @@ void loadCustomScreens() for (unsigned int i = 0; i < MAX_CUSTOM_SCREENS; i++) { auto& screen = customScreens[i]; - screen = loadLayout(g_model.screenData[i].layoutName, + screen = loadLayout(g_model.screenData[i].LayoutId, &g_model.screenData[i].layoutData); if (screen) { diff --git a/radio/src/gui/colorlcd/model_select.cpp b/radio/src/gui/colorlcd/model_select.cpp index 63ff7c993..53017aef8 100644 --- a/radio/src/gui/colorlcd/model_select.cpp +++ b/radio/src/gui/colorlcd/model_select.cpp @@ -286,14 +286,7 @@ class ModelCategoryPageBody: public FormWindow { update(); // modelslist.getModelIndex(modelCell)); }); } - menu->addLine(STR_CREATE_MODEL, [=]() { - storageCheck(true); - modelslist.setCurrentModel(modelslist.addModel(category, createModel())); -#if defined(LUA) - // chainMenu(menuModelWizard); -#endif - update(category->size() - 1); - }); + menu->addLine(STR_CREATE_MODEL, getCreateModelAction()); menu->addLine(STR_DUPLICATE_MODEL, [=]() { char duplicatedFilename[LEN_MODEL_FILENAME + 1]; memcpy(duplicatedFilename, model->modelFilename, sizeof(duplicatedFilename)); @@ -342,11 +335,55 @@ class ModelCategoryPageBody: public FormWindow { } setInnerHeight(y); + + if (category->empty()) { + setFocus(); + } } +#if defined(HARDWARE_KEYS) + void onEvent(event_t event) override + { + if (event == EVT_KEY_BREAK(KEY_ENTER)) { + Menu * menu = new Menu(this); + menu->addLine(STR_CREATE_MODEL, getCreateModelAction()); + //TODO: create category? + } + else { + FormWindow::onEvent(event); + } + } +#endif + + void setFocus(uint8_t flag = SET_FOCUS_DEFAULT, Window * from = nullptr) override + { + if (category->empty()) { + // use Window::setFocus() to avoid forwarding focus to nowhere + // this crashes currently in libopenui + Window::setFocus(flag, from); + } + else { + FormWindow::setFocus(flag, from); + } + } + + protected: ModelsCategory * category; ModelSelectFooter * footer; + + std::function getCreateModelAction() + { + return [=]() { + storageCheck(true); + modelslist.setCurrentModel(modelslist.addModel(category, createModel())); +#if defined(LUA) + // chainMenu(menuModelWizard); +#endif + update(category->size() - 1); + }; + } + }; class ModelCategoryPage: public PageTab { diff --git a/radio/src/gui/colorlcd/radio_calibration.cpp b/radio/src/gui/colorlcd/radio_calibration.cpp index 13158f1d6..527a040f2 100644 --- a/radio/src/gui/colorlcd/radio_calibration.cpp +++ b/radio/src/gui/colorlcd/radio_calibration.cpp @@ -276,8 +276,11 @@ void RadioCalibrationPage::nextStep() g_eeGeneral.chkSum = evalChkSum(); storageDirty(EE_GENERAL); menuCalibrationState = CALIB_FINISHED; + + // initial calibration completed + // -> exit if (initial) - text->getParent()->getParent()->deleteLater(); + deleteLater(); break; default: @@ -289,5 +292,5 @@ void RadioCalibrationPage::nextStep() void startCalibration() { - new RadioCalibrationPage(); + new RadioCalibrationPage(true); } diff --git a/radio/src/gui/colorlcd/screen_setup.cpp b/radio/src/gui/colorlcd/screen_setup.cpp index 9a586003a..48c6e99a4 100644 --- a/radio/src/gui/colorlcd/screen_setup.cpp +++ b/radio/src/gui/colorlcd/screen_setup.cpp @@ -69,7 +69,7 @@ void ScreenAddPage::build(FormWindow * window) const LayoutFactory * factory = getRegisteredLayouts().front(); screen = factory->create(&screenData.layoutData); - strncpy(screenData.layoutName, factory->getId(), sizeof(screenData.layoutName)); + strncpy(screenData.LayoutId, factory->getId(), sizeof(screenData.LayoutId)); auto tab = new ScreenSetupPage(menu, screen, screenData); std::string title(STR_MAIN_VIEW_X); @@ -257,8 +257,10 @@ class SetupWidgetsPage: public FormWindow #if defined(HARDWARE_TOUCH) Keyboard::hide(); #endif - // give it back to its old parent before it gets deleted - screen->attach(oldParent); + if (oldParent) { + // give it back to its old parent before it gets deleted + screen->attach(oldParent); + } FormWindow::deleteLater(detach, trash); } @@ -299,7 +301,7 @@ void ScreenSetupPage::build(FormWindow * window) layoutChoice = new LayoutChoice(window, layoutSlot, GET_VALUE(screen->getFactory()), [=](const LayoutFactory *factory) { delete screen; screen = factory->create(&screenData.layoutData); - strncpy(screenData.layoutName, factory->getId(), sizeof(CustomScreenData::layoutName)); + strncpy(screenData.LayoutId, factory->getId(), sizeof(CustomScreenData::LayoutId)); SET_DIRTY(); updateLayoutOptions(); }); diff --git a/radio/src/lua/api_model.cpp b/radio/src/lua/api_model.cpp index 90c19f4d9..76800d842 100644 --- a/radio/src/lua/api_model.cpp +++ b/radio/src/lua/api_model.cpp @@ -23,6 +23,7 @@ #include "opentx.h" #include "lua_api.h" #include "timers.h" +#include "model_init.h" /*luadoc @function model.getInfo() @@ -664,7 +665,8 @@ Set all inputs to defaults */ static int luaModelDefaultInputs(lua_State *L) { - defaultInputs(); + clearInputs(); + setDefaultInputs(); return 0; } diff --git a/radio/src/model_init.cpp b/radio/src/model_init.cpp new file mode 100644 index 000000000..1a4b8ce2e --- /dev/null +++ b/radio/src/model_init.cpp @@ -0,0 +1,145 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "opentx.h" + +void clearInputs() +{ + memset(g_model.expoData, 0, sizeof(g_model.expoData)); +} + +void setDefaultInputs() +{ + for (int i=0; isrcRaw = MIXSRC_Rud - 1 + stick_index; + expo->curve.type = CURVE_REF_EXPO; + expo->chn = i; + expo->weight = 100; + expo->mode = 3; // TODO constant + for (int c = 0; c < 3; c++) { + g_model.inputNames[i][c] = STR_VSRCRAW[2 + 4 * stick_index + c]; + } +#if LEN_INPUT_NAME > 3 + g_model.inputNames[i][3] = '\0'; +#endif + } + storageDirty(EE_MODEL); +} + +void clearMixes() +{ + memset(g_model.mixData, 0, sizeof(g_model.mixData)); +} + +void setDefaultMixes() +{ + for (int i=0; idestCh = i; + mix->weight = 100; + mix->srcRaw = i+1; + } + storageDirty(EE_MODEL); +} + +void setDefaultModelRegistrationID() +{ + memcpy(g_model.modelRegistrationID, g_eeGeneral.ownerRegistrationID, PXX2_LEN_REGISTRATION_ID); +} + +void setDefaultGVars() +{ +#if defined(FLIGHT_MODES) && defined(GVARS) + for (int fmIdx = 1; fmIdx < MAX_FLIGHT_MODES; fmIdx++) { + for (int gvarIdx = 0; gvarIdx < MAX_GVARS; gvarIdx++) { + g_model.flightModeData[fmIdx].gvars[gvarIdx] = GVAR_MAX + 1; + } + } +#endif +} + +void setVendorSpecificModelDefaults() +{ +#if defined(FRSKY_RELEASE) + g_model.moduleData[INTERNAL_MODULE].type = IS_PXX2_INTERNAL_ENABLED() ? MODULE_TYPE_ISRM_PXX2 : MODULE_TYPE_XJT_PXX1; + g_model.moduleData[INTERNAL_MODULE].channelsCount = defaultModuleChannels_M8(INTERNAL_MODULE); + #if defined(EEPROM) + g_model.header.modelId[INTERNAL_MODULE] = findNextUnusedModelId(id, INTERNAL_MODULE); + modelHeaders[id].modelId[INTERNAL_MODULE] = g_model.header.modelId[INTERNAL_MODULE]; + #endif +#endif + + // TODO: we should probably have some default trainer mode + // per radio, depending on what is supported + // +#if defined(PCBXLITE) + g_model.trainerData.mode = TRAINER_MODE_MASTER_BLUETOOTH; +#endif + +#if defined(RADIOMASTER_RTF_RELEASE) + // Those settings are for headless radio + g_model.trainerData.mode = TRAINER_MODE_SLAVE; + g_model.moduleData[INTERNAL_MODULE].type = MODULE_TYPE_MULTIMODULE; + g_model.moduleData[INTERNAL_MODULE].setMultiProtocol(MODULE_SUBTYPE_MULTI_FRSKY); + g_model.moduleData[INTERNAL_MODULE].subType = MM_RF_FRSKY_SUBTYPE_D8; + g_model.moduleData[INTERNAL_MODULE].failsafeMode = FAILSAFE_NOPULSES; +#endif +} + +void applyDefaultTemplate() +{ + setDefaultInputs(); + setDefaultMixes(); + setDefaultGVars(); + + setDefaultModelRegistrationID(); + +#if defined(COLORLCD) + //TODO: not sure yet we need it here + loadDefaultLayout(); + + // enable switch warnings + for (int i = 0; i < NUM_SWITCHES; i++) { + g_model.switchWarningState |= (1 << (3*i)); + } +#endif + + // TODO: what about switch warnings in non-color LCD radios? +} + + +void setModelDefaults(uint8_t id) +{ + memset(&g_model, 0, sizeof(g_model)); + applyDefaultTemplate(); + + setVendorSpecificModelDefaults(); + + strAppendUnsigned(strAppend(g_model.header.name, STR_MODEL), id + 1, 2); + +#if defined(LUA) && defined(PCBTARANIS) // Horus uses menuModelWizard() for wizard + if (isFileAvailable(WIZARD_PATH "/" WIZARD_NAME)) { + f_chdir(WIZARD_PATH); + luaExec(WIZARD_NAME); + } +#endif +} diff --git a/radio/src/opentx.cpp b/radio/src/opentx.cpp index 8fcce8cc1..4ad05a801 100644 --- a/radio/src/opentx.cpp +++ b/radio/src/opentx.cpp @@ -23,7 +23,7 @@ #if defined(LIBOPENUI) // #include "shutdown_animation.h" -// #include "radio_calibration.h" +#include "radio_calibration.h" #include "view_main.h" #endif @@ -364,178 +364,6 @@ uint16_t evalChkSum() return sum; } -void clearInputs() -{ - memset(g_model.expoData, 0, sizeof(g_model.expoData)); // clear all expos -} - -void defaultInputs() -{ - clearInputs(); - - for (int i=0; isrcRaw = MIXSRC_Rud - 1 + stick_index; - expo->curve.type = CURVE_REF_EXPO; - expo->chn = i; - expo->weight = 100; - expo->mode = 3; // TODO constant - for (int c = 0; c < 3; c++) { - g_model.inputNames[i][c] = STR_VSRCRAW[2 + 4 * stick_index + c]; - } -#if LEN_INPUT_NAME > 3 - g_model.inputNames[i][3] = '\0'; -#endif - } - storageDirty(EE_MODEL); -} - -void applyDefaultTemplate() -{ - defaultInputs(); // calls storageDirty internally - - for (int i=0; idestCh = i; - mix->weight = 100; - mix->srcRaw = i+1; - } -} - -#if defined(EEPROM) -void checkModelIdUnique(uint8_t index, uint8_t module) -{ - if (isModuleXJTD8(module)) - return; - - uint8_t modelId = g_model.header.modelId[module]; - uint8_t additionalOnes = 0; - char * name = reusableBuffer.moduleSetup.msg; - - memset(reusableBuffer.moduleSetup.msg, 0, sizeof(reusableBuffer.moduleSetup.msg)); - - if (modelId != 0) { - for (uint8_t i = 0; i < MAX_MODELS; i++) { - if (i != index) { - if (modelId == modelHeaders[i].modelId[module]) { - if ((WARNING_LINE_LEN - 4 - (name - reusableBuffer.moduleSetup.msg)) > (signed)(modelHeaders[i].name[0] ? zlen(modelHeaders[i].name, LEN_MODEL_NAME) : sizeof(TR_MODEL) + 2)) { // you cannot rely exactly on WARNING_LINE_LEN so using WARNING_LINE_LEN-2 (-2 for the ",") - if (reusableBuffer.moduleSetup.msg[0] != '\0') { - name = strAppend(name, ", "); - } - if (modelHeaders[i].name[0] == 0) { - name = strAppend(name, STR_MODEL); - name = strAppendUnsigned(name+strlen(name), i + 1, 2); - } - else { - name += zchar2str(name, modelHeaders[i].name, LEN_MODEL_NAME); - } - } - else { - additionalOnes++; - } - } - } - } - } - - if (additionalOnes) { - name = strAppend(name, " (+"); - name = strAppendUnsigned(name, additionalOnes); - strAppend(name, ")"); - } - - if (reusableBuffer.moduleSetup.msg[0]) { - POPUP_WARNING(STR_MODELIDUSED, reusableBuffer.moduleSetup.msg); - } -} - -uint8_t findNextUnusedModelId(uint8_t index, uint8_t module) -{ - uint8_t usedModelIds[(MAX_RXNUM + 7) / 8]; - memset(usedModelIds, 0, sizeof(usedModelIds)); - - for (uint8_t modelIndex = 0; modelIndex < MAX_MODELS; modelIndex++) { - if (modelIndex == index) - continue; - - uint8_t id = modelHeaders[modelIndex].modelId[module]; - if (id == 0) - continue; - - uint8_t mask = 1u << (id & 7u); - usedModelIds[id >> 3u] |= mask; - } - - for (uint8_t id = 1; id <= getMaxRxNum(module); id++) { - uint8_t mask = 1u << (id & 7u); - if (!(usedModelIds[id >> 3u] & mask)) { - // found free ID - return id; - } - } - - // failed finding something... - return 0; -} -#endif - -void modelDefault(uint8_t id) -{ - memset(&g_model, 0, sizeof(g_model)); - - applyDefaultTemplate(); - - memcpy(g_model.modelRegistrationID, g_eeGeneral.ownerRegistrationID, PXX2_LEN_REGISTRATION_ID); - -#if defined(LUA) && defined(PCBTARANIS) // Horus uses menuModelWizard() for wizard - if (isFileAvailable(WIZARD_PATH "/" WIZARD_NAME)) { - f_chdir(WIZARD_PATH); - luaExec(WIZARD_NAME); - } -#endif - -#if defined(FRSKY_RELEASE) - g_model.moduleData[INTERNAL_MODULE].type = IS_PXX2_INTERNAL_ENABLED() ? MODULE_TYPE_ISRM_PXX2 : MODULE_TYPE_XJT_PXX1; - g_model.moduleData[INTERNAL_MODULE].channelsCount = defaultModuleChannels_M8(INTERNAL_MODULE); - #if defined(EEPROM) - g_model.header.modelId[INTERNAL_MODULE] = findNextUnusedModelId(id, INTERNAL_MODULE); - modelHeaders[id].modelId[INTERNAL_MODULE] = g_model.header.modelId[INTERNAL_MODULE]; - #endif -#endif - -#if defined(PCBXLITE) - g_model.trainerData.mode = TRAINER_MODE_MASTER_BLUETOOTH; -#endif - -#if defined(FLIGHT_MODES) && defined(GVARS) - for (int fmIdx = 1; fmIdx < MAX_FLIGHT_MODES; fmIdx++) { - for (int gvarIdx = 0; gvarIdx < MAX_GVARS; gvarIdx++) { - g_model.flightModeData[fmIdx].gvars[gvarIdx] = GVAR_MAX + 1; - } - } -#endif - - strAppendUnsigned(strAppend(g_model.header.name, STR_MODEL), id + 1, 2); - -#if defined(COLORLCD) - loadDefaultLayout(); - - // enable switch warnings - for (int i = 0; i < NUM_SWITCHES; i++) { - g_model.switchWarningState |= (1 << (3*i)); - } -#endif - -#if defined(RADIOMASTER_RTF_RELEASE) - // Those settings are for headless radio - g_model.trainerData.mode = TRAINER_MODE_SLAVE; - g_model.moduleData[INTERNAL_MODULE].type = MODULE_TYPE_MULTIMODULE; - g_model.moduleData[INTERNAL_MODULE].setMultiProtocol(MODULE_SUBTYPE_MULTI_FRSKY); - g_model.moduleData[INTERNAL_MODULE].subType = MM_RF_FRSKY_SUBTYPE_D8; - g_model.moduleData[INTERNAL_MODULE].failsafeMode = FAILSAFE_NOPULSES; -#endif -} bool isInputRecursive(int index) { @@ -1632,8 +1460,7 @@ void opentxStart(const uint8_t startOptions = OPENTX_START_DEFAULT_ARGS) #if defined(GUI) if (calibration_needed) { #if defined(LIBOPENUI) - #warning "TODO add a startCalibration function" - // startCalibration(); + startCalibration(); #else chainMenu(menuFirstCalib); #endif @@ -1927,8 +1754,10 @@ void moveTrimsToOffsets() // copy state of 3 primary to subtrim void opentxInit() { TRACE("opentxInit"); - -#if defined(GUI) && !defined(LIBOPENUI) +#if defined(LIBOPENUI) + // create ViewMain + ViewMain::instance(); +#elif defined(GUI) // TODO add a function for this (duplicated) menuHandlers[0] = menuMainView; #if MENUS_LOCK != 2/*no menus*/ diff --git a/radio/src/opentx.h b/radio/src/opentx.h index 9e25810c5..dbbd9eec8 100644 --- a/radio/src/opentx.h +++ b/radio/src/opentx.h @@ -665,12 +665,6 @@ inline void resumeMixerCalculations() void setDefaultOwnerId(); void generalDefault(); -void modelDefault(uint8_t id); - -#if defined(EEPROM) -void checkModelIdUnique(uint8_t index, uint8_t module); -uint8_t findNextUnusedModelId(uint8_t index, uint8_t module); -#endif uint32_t hash(const void * ptr, uint32_t size); @@ -786,12 +780,6 @@ inline void getGVarIncDecRange(int16_t & valMin, int16_t & valMax) } #endif - - - -void clearInputs(); -void defaultInputs(); - void applyExpos(int16_t * anas, uint8_t mode, uint8_t ovwrIdx=0, int16_t ovwrValue=0); int16_t applyLimits(uint8_t channel, int32_t value); diff --git a/radio/src/storage/conversions/conversions_218_219.cpp b/radio/src/storage/conversions/conversions_218_219.cpp index 14854fe59..412ef97bd 100644 --- a/radio/src/storage/conversions/conversions_218_219.cpp +++ b/radio/src/storage/conversions/conversions_218_219.cpp @@ -323,7 +323,7 @@ void convertModelData_218_to_219(ModelData &model) for (int screen=0; screen #include "opentx.h" #include "timers.h" +#include "model_init.h" void eeLoadModel(uint8_t index) { @@ -45,7 +46,7 @@ void eeLoadModel(uint8_t index) bool alarms = true; if (size < EEPROM_MIN_MODEL_SIZE) { // if not loaded a fair amount - modelDefault(index) ; + setModelDefaults(index); storageCheck(true); alarms = false; } @@ -131,7 +132,7 @@ void storageEraseAll(bool warn) TRACE("storageEraseAll"); generalDefault(); - modelDefault(0); + setModelDefault(0); if (warn) { ALERT(STR_STORAGE_WARNING, STR_BAD_RADIO_DATA, AU_BAD_RADIODATA); @@ -143,3 +144,78 @@ void storageEraseAll(bool warn) storageDirty(EE_GENERAL|EE_MODEL); storageCheck(true); } + +void checkModelIdUnique(uint8_t index, uint8_t module) +{ + if (isModuleXJTD8(module)) + return; + + uint8_t modelId = g_model.header.modelId[module]; + uint8_t additionalOnes = 0; + char * name = reusableBuffer.moduleSetup.msg; + + memset(reusableBuffer.moduleSetup.msg, 0, sizeof(reusableBuffer.moduleSetup.msg)); + + if (modelId != 0) { + for (uint8_t i = 0; i < MAX_MODELS; i++) { + if (i != index) { + if (modelId == modelHeaders[i].modelId[module]) { + if ((WARNING_LINE_LEN - 4 - (name - reusableBuffer.moduleSetup.msg)) > (signed)(modelHeaders[i].name[0] ? zlen(modelHeaders[i].name, LEN_MODEL_NAME) : sizeof(TR_MODEL) + 2)) { // you cannot rely exactly on WARNING_LINE_LEN so using WARNING_LINE_LEN-2 (-2 for the ",") + if (reusableBuffer.moduleSetup.msg[0] != '\0') { + name = strAppend(name, ", "); + } + if (modelHeaders[i].name[0] == 0) { + name = strAppend(name, STR_MODEL); + name = strAppendUnsigned(name+strlen(name), i + 1, 2); + } + else { + name += zchar2str(name, modelHeaders[i].name, LEN_MODEL_NAME); + } + } + else { + additionalOnes++; + } + } + } + } + } + + if (additionalOnes) { + name = strAppend(name, " (+"); + name = strAppendUnsigned(name, additionalOnes); + strAppend(name, ")"); + } + + if (reusableBuffer.moduleSetup.msg[0]) { + POPUP_WARNING(STR_MODELIDUSED, reusableBuffer.moduleSetup.msg); + } +} + +uint8_t findNextUnusedModelId(uint8_t index, uint8_t module) +{ + uint8_t usedModelIds[(MAX_RXNUM + 7) / 8]; + memset(usedModelIds, 0, sizeof(usedModelIds)); + + for (uint8_t modelIndex = 0; modelIndex < MAX_MODELS; modelIndex++) { + if (modelIndex == index) + continue; + + uint8_t id = modelHeaders[modelIndex].modelId[module]; + if (id == 0) + continue; + + uint8_t mask = 1u << (id & 7u); + usedModelIds[id >> 3u] |= mask; + } + + for (uint8_t id = 1; id <= getMaxRxNum(module); id++) { + uint8_t mask = 1u << (id & 7u); + if (!(usedModelIds[id >> 3u] & mask)) { + // found free ID + return id; + } + } + + // failed finding something... + return 0; +} diff --git a/radio/src/storage/eeprom_common.h b/radio/src/storage/eeprom_common.h index a4d33bc55..78171d3b9 100644 --- a/radio/src/storage/eeprom_common.h +++ b/radio/src/storage/eeprom_common.h @@ -39,6 +39,9 @@ void storageClearRadioSettings(); bool storageReadRadioSettings(bool allowFixes = true); void storageReadCurrentModel(); +void checkModelIdUnique(uint8_t index, uint8_t module); +uint8_t findNextUnusedModelId(uint8_t index, uint8_t module); + #if defined(EEPROM_RLC) #include "eeprom_rlc.h" #else diff --git a/radio/src/storage/modelslist.cpp b/radio/src/storage/modelslist.cpp index 54762d76a..0b5c20b81 100644 --- a/radio/src/storage/modelslist.cpp +++ b/radio/src/storage/modelslist.cpp @@ -441,14 +441,12 @@ bool ModelsList::load() if (!currentModel) { if (model) { currentModel = model; - currentCategory = category; } else { - model = new ModelCell(line); category = new ModelsCategory("Models"); categories.push_back(category); - category->push_back(model); } + currentCategory = category; } loaded = true; diff --git a/radio/src/storage/sdcard_common.cpp b/radio/src/storage/sdcard_common.cpp index 7be087043..7abdfdded 100644 --- a/radio/src/storage/sdcard_common.cpp +++ b/radio/src/storage/sdcard_common.cpp @@ -23,6 +23,7 @@ #include "sdcard_common.h" #include "modelslist.h" #include "conversions/conversions.h" +#include "model_init.h" // defined either in sdcard_raw.cpp or sdcard_yaml.cpp void storageCreateModelsList(); @@ -43,9 +44,6 @@ void storageEraseAll(bool warn) static_cast(theme)->load(); #endif - generalDefault(); - modelDefault(1); - if (warn) { ALERT(STR_STORAGE_WARNING, STR_BAD_RADIO_DATA, AU_BAD_RADIODATA); } @@ -53,7 +51,7 @@ void storageEraseAll(bool warn) RAISE_ALERT(STR_STORAGE_WARNING, STR_STORAGE_FORMAT, NULL, AU_NONE); storageFormat(); - storageDirty(EE_GENERAL|EE_MODEL); + storageDirty(EE_GENERAL); storageCheck(true); } @@ -68,7 +66,7 @@ void storageCheck(bool immediately) { if (storageDirtyMsk & EE_GENERAL) { TRACE("eeprom write general"); - storageDirtyMsk -= EE_GENERAL; + storageDirtyMsk &= ~EE_GENERAL; const char * error = writeGeneralSettings(); if (error) { TRACE("writeGeneralSettings error=%s", error); @@ -77,7 +75,7 @@ void storageCheck(bool immediately) if (storageDirtyMsk & EE_MODEL) { TRACE("eeprom write model"); - storageDirtyMsk -= EE_MODEL; + storageDirtyMsk &= ~EE_MODEL; const char * error = writeModel(); if (error) { TRACE("writeModel error=%s", error); @@ -95,7 +93,7 @@ const char * createModel() int index = findNextFileIndex(filename, LEN_MODEL_FILENAME, MODELS_PATH); if (index > 0) { - modelDefault(index); + setModelDefaults(index); memcpy(g_eeGeneral.currModelFilename, filename, sizeof(g_eeGeneral.currModelFilename)); storageDirty(EE_GENERAL); storageDirty(EE_MODEL); @@ -109,18 +107,20 @@ const char * createModel() const char * loadModel(const char * filename, bool alarms) { uint8_t version; - preModelLoad(); const char * error = readModel(filename, (uint8_t *)&g_model, sizeof(g_model), &version); if (error) { TRACE("loadModel error=%s", error); - } - - if (error) { - modelDefault(0) ; + + // just get some clean memory state in "g_model" + // so the mixer can run safely + memset(&g_model, 0, sizeof(g_model)); + applyDefaultTemplate(); + storageCheck(true); - alarms = false; + postModelLoad(false); + return error; } #if defined(STORAGE_CONVERSIONS) @@ -130,8 +130,7 @@ const char * loadModel(const char * filename, bool alarms) #endif postModelLoad(alarms); - - return error; + return nullptr; } void storageReadAll() @@ -150,8 +149,7 @@ void storageReadAll() } if (loadModel(g_eeGeneral.currModelFilename, false) != nullptr) { - sdCheckAndCreateDirectory(MODELS_PATH); - createModel(); + TRACE("No current model or SD card error"); } // Wipe models list in case diff --git a/radio/src/storage/sdcard_raw.cpp b/radio/src/storage/sdcard_raw.cpp index 710fcc44a..2bb2f924d 100644 --- a/radio/src/storage/sdcard_raw.cpp +++ b/radio/src/storage/sdcard_raw.cpp @@ -60,6 +60,8 @@ const char * writeModel() { char path[256]; getModelPath(path, g_eeGeneral.currModelFilename); + + sdCheckAndCreateDirectory(MODELS_PATH); return writeFile(path, (uint8_t *)&g_model, sizeof(g_model)); } diff --git a/radio/src/storage/yaml/yaml_datastructs_x10.cpp b/radio/src/storage/yaml/yaml_datastructs_x10.cpp index c0d4be228..acea7b1d7 100644 --- a/radio/src/storage/yaml/yaml_datastructs_x10.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_x10.cpp @@ -697,7 +697,7 @@ static const struct YamlNode struct_Layout__PersistentData[] = { }; static const struct YamlNode struct_CustomScreenData[] = { YAML_IDX, - YAML_STRING("layoutName", 10), + YAML_STRING("LayoutId", 10), YAML_STRUCT("layoutData", 6720, struct_Layout__PersistentData, NULL), YAML_END }; diff --git a/radio/src/storage/yaml/yaml_datastructs_x12s.cpp b/radio/src/storage/yaml/yaml_datastructs_x12s.cpp index 5da72c704..8cb54df1f 100644 --- a/radio/src/storage/yaml/yaml_datastructs_x12s.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_x12s.cpp @@ -698,7 +698,7 @@ static const struct YamlNode struct_Layout__PersistentData[] = { }; static const struct YamlNode struct_CustomScreenData[] = { YAML_IDX, - YAML_STRING("layoutName", 10), + YAML_STRING("LayoutId", 10), YAML_STRUCT("layoutData", 6720, struct_Layout__PersistentData, NULL), YAML_END }; diff --git a/radio/src/tasks.cpp b/radio/src/tasks.cpp index a051cc509..a1df13c73 100644 --- a/radio/src/tasks.cpp +++ b/radio/src/tasks.cpp @@ -293,6 +293,9 @@ void tasksStart() { RTOS_INIT(); + RTOS_CREATE_MUTEX(audioMutex); + RTOS_CREATE_MUTEX(mixerMutex); + #if defined(CLI) cliStart(); #endif @@ -303,9 +306,5 @@ void tasksStart() #if !defined(SIMU) RTOS_CREATE_TASK(audioTaskId, audioTask, "audio", audioStack, AUDIO_STACK_SIZE, AUDIO_TASK_PRIO); #endif - - RTOS_CREATE_MUTEX(audioMutex); - RTOS_CREATE_MUTEX(mixerMutex); - RTOS_START(); } diff --git a/radio/src/tasks.h b/radio/src/tasks.h index 856aaf038..6f761d843 100644 --- a/radio/src/tasks.h +++ b/radio/src/tasks.h @@ -48,7 +48,6 @@ extern RTOS_TASK_HANDLE audioTaskId; extern RTOS_DEFINE_STACK(audioStack, AUDIO_STACK_SIZE); extern RTOS_MUTEX_HANDLE mixerMutex; -extern RTOS_FLAG_HANDLE openTxInitCompleteFlag; void stackPaint(); void tasksStart(); diff --git a/radio/src/tests/conversions.cpp b/radio/src/tests/conversions.cpp index 2eec8cc63..2d9d5f5e3 100644 --- a/radio/src/tests/conversions.cpp +++ b/radio/src/tests/conversions.cpp @@ -291,7 +291,7 @@ TEST(Conversions, ConversionX10From22) EXPECT_EQ(9, g_model.telemetrySensors[0].frskyInstance.physID); EXPECT_EQ((NUM_POTS + NUM_SLIDERS + 3), g_model.thrTraceSrc); // CH3 - EXPECT_STREQ("Layout2P1", g_model.screenData[0].layoutName); + EXPECT_STREQ("Layout2P1", g_model.screenData[0].LayoutId); EXPECT_STREQ("ModelBmp", g_model.screenData[0].layoutData.zones[0].widgetName); EXPECT_STREQ("Value", g_model.topbarData.zones[0].widgetName); EXPECT_EQ(MIXSRC_FIRST_TELEM, g_model.topbarData.zones[0].widgetData.options[0].value.unsignedValue); @@ -358,7 +358,7 @@ TEST(Conversions, ConversionX12SFrom22) EXPECT_EQ(9, g_model.telemetrySensors[0].frskyInstance.physID); EXPECT_EQ((NUM_POTS + NUM_SLIDERS + 3), g_model.thrTraceSrc); // CH3 - EXPECT_STREQ("Layout2P1", g_model.screenData[0].layoutName); + EXPECT_STREQ("Layout2P1", g_model.screenData[0].LayoutId); EXPECT_STREQ("ModelBmp", g_model.screenData[0].layoutData.zones[0].widgetName); EXPECT_STREQ("Value", g_model.topbarData.zones[0].widgetName); EXPECT_EQ(MIXSRC_FIRST_TELEM, g_model.topbarData.zones[0].widgetData.options[0].unsignedValue); diff --git a/radio/src/tests/gtests.h b/radio/src/tests/gtests.h index 55218bbc5..7a95d6688 100644 --- a/radio/src/tests/gtests.h +++ b/radio/src/tests/gtests.h @@ -104,7 +104,7 @@ class OpenTxTest : public testing::Test SYSTEM_RESET(); MODEL_RESET(); MIXER_RESET(); - modelDefault(0); + setModelDefaults(0); RADIO_RESET(); } }; diff --git a/radio/src/tests/mixer.cpp b/radio/src/tests/mixer.cpp index 080155d80..5bb972dc9 100644 --- a/radio/src/tests/mixer.cpp +++ b/radio/src/tests/mixer.cpp @@ -466,7 +466,7 @@ TEST(Curves, LinearIntpol) SYSTEM_RESET(); MODEL_RESET(); MIXER_RESET(); - modelDefault(0); + setModelDefaults(0); for (int8_t i=-2; i<=2; i++) { g_model.points[2+i] = 50*i; } @@ -672,7 +672,7 @@ TEST_F(TrimsTest, throttleTrimEle) { SYSTEM_RESET(); MODEL_RESET(); MIXER_RESET(); - modelDefault(0); + setModelDefaults(0); g_eeGeneral.templateSetup = 17; applyDefaultTemplate(); g_model.thrTrim = 1; @@ -685,7 +685,7 @@ TEST_F(TrimsTest, throttleTrimEle) { SYSTEM_RESET(); MODEL_RESET(); MIXER_RESET(); - modelDefault(0); + setModelDefaults(0); g_eeGeneral.templateSetup = 0; applyDefaultTemplate(); } @@ -696,7 +696,7 @@ TEST(Heli, BasicTest) SYSTEM_RESET(); MODEL_RESET(); MIXER_RESET(); - modelDefault(0); + setModelDefaults(0); g_model.swashR.collectiveSource = MIXSRC_Thr; g_model.swashR.elevatorSource = MIXSRC_Ele; g_model.swashR.aileronSource = MIXSRC_Ail; @@ -728,7 +728,7 @@ TEST(Heli, Mode2Test) SYSTEM_RESET(); MODEL_RESET(); MIXER_RESET(); - modelDefault(0); + setModelDefaults(0); g_eeGeneral.templateSetup = 2; applyDefaultTemplate(); g_model.swashR.collectiveSource = MIXSRC_Thr; @@ -764,7 +764,7 @@ TEST(Trainer, UnpluggedTest) SYSTEM_RESET(); MODEL_RESET(); MIXER_RESET(); - modelDefault(0); + setModelDefaults(0); g_model.mixData[0].destCh = 0; g_model.mixData[0].mltpx = MLTPX_ADD; g_model.mixData[0].srcRaw = MIXSRC_FIRST_TRAINER; @@ -781,7 +781,7 @@ TEST_F(MixerTest, flightModeTransition) SYSTEM_RESET(); MODEL_RESET(); MIXER_RESET(); - modelDefault(0); + setModelDefaults(0); g_model.flightModeData[1].swtch = TR(SWSRC_ID2, SWSRC_SA2); g_model.flightModeData[0].fadeIn = 100; g_model.flightModeData[0].fadeOut = 100; @@ -807,7 +807,7 @@ TEST_F(MixerTest, flightModeOverflow) SYSTEM_RESET(); MODEL_RESET(); MIXER_RESET(); - modelDefault(0); + setModelDefaults(0); g_model.flightModeData[1].swtch = TR(SWSRC_ID2, SWSRC_SA2); g_model.flightModeData[0].fadeIn = 100; g_model.flightModeData[0].fadeOut = 100; diff --git a/radio/src/tests/switches.cpp b/radio/src/tests/switches.cpp index 56be91390..fe7779847 100644 --- a/radio/src/tests/switches.cpp +++ b/radio/src/tests/switches.cpp @@ -77,7 +77,7 @@ TEST(getSwitch, nullSW) TEST(getSwitch, inputWithTrim) { MODEL_RESET(); - modelDefault(0); + setModelDefaults(0); MIXER_RESET(); // g_model.logicalSw[0] = { LS_FUNC_VPOS, MIXSRC_FIRST_INPUT, 0, 0 }; @@ -99,7 +99,7 @@ TEST(evalLogicalSwitches, playFile) { SYSTEM_RESET(); MODEL_RESET(); - modelDefault(0); + setModelDefaults(0); MIXER_RESET(); extern BitField<(MAX_LOGICAL_SWITCHES * 2/*on, off*/)> sdAvailableLogicalSwitchAudioFiles;