286 lines
7.4 KiB
C++
286 lines
7.4 KiB
C++
/*
|
|
* 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 "edgetx.h"
|
|
#include "storage.h"
|
|
#include "sdcard_common.h"
|
|
#include "modelslist.h"
|
|
#include "model_init.h"
|
|
|
|
#include "hal/abnormal_reboot.h"
|
|
|
|
#if defined(COLORLCD)
|
|
#include "theme_manager.h"
|
|
#endif
|
|
|
|
void getModelPath(char * path, const char * filename, const char* pathName)
|
|
{
|
|
unsigned int len = strlen(pathName);
|
|
strcpy(path, pathName);
|
|
path[len] = '/';
|
|
strcpy(&path[len + 1], filename);
|
|
}
|
|
|
|
void storageEraseAll(bool warn)
|
|
{
|
|
TRACE("storageEraseAll");
|
|
|
|
#if defined(COLORLCD)
|
|
// the theme has not been loaded before
|
|
ThemePersistance::instance()->loadDefaultTheme();
|
|
#endif
|
|
|
|
// Init backlight mode before entering alert screens
|
|
requiredBacklightBright = BACKLIGHT_FORCED_ON;
|
|
|
|
#if defined(PCBHORUS)
|
|
g_eeGeneral.blOffBright = 20;
|
|
#endif
|
|
|
|
if (warn) {
|
|
ALERT(STR_STORAGE_WARNING, STR_BAD_RADIO_DATA, AU_BAD_RADIODATA);
|
|
}
|
|
|
|
RAISE_ALERT(STR_STORAGE_WARNING, STR_STORAGE_FORMAT, STR_PRESS_ANY_KEY_TO_SKIP, AU_NONE);
|
|
|
|
storageFormat();
|
|
storageDirty(EE_GENERAL);
|
|
storageDirty(EE_MODEL);
|
|
storageCheck(true);
|
|
}
|
|
|
|
void storageFormat()
|
|
{
|
|
sdCheckAndCreateDirectory(RADIO_PATH);
|
|
sdCheckAndCreateDirectory(MODELS_PATH);
|
|
generalDefault();
|
|
setModelDefaults();
|
|
}
|
|
|
|
void storageCheck(bool immediately)
|
|
{
|
|
// Don't write anything to SD card if in EM
|
|
if (UNEXPECTED_SHUTDOWN()) return;
|
|
|
|
static constexpr uint8_t retryLimit = 10;
|
|
|
|
static uint8_t retryRadioCount = 0;
|
|
if (storageDirtyMsk & EE_GENERAL) {
|
|
if (retryRadioCount < retryLimit) {
|
|
TRACE("SD card write radio settings");
|
|
const char * error = writeGeneralSettings();
|
|
if (error) {
|
|
TRACE("writeGeneralSettings error=%s", error);
|
|
retryRadioCount += 1;
|
|
} else {
|
|
storageDirtyMsk &= ~EE_GENERAL;
|
|
retryRadioCount = 0;
|
|
}
|
|
} else {
|
|
// Reset timeout to next check
|
|
storageDirtyTime10ms = get_tmr10ms();
|
|
retryRadioCount = retryLimit / 2; // Retry again after timeout; but fewer times
|
|
// TODO: provide some mechanism to alert user that SD card has serious error
|
|
}
|
|
}
|
|
|
|
#if defined(STORAGE_MODELSLIST)
|
|
static uint8_t retryLabelsCount = 0;
|
|
if (storageDirtyMsk & EE_LABELS) {
|
|
if (retryLabelsCount < retryLimit) {
|
|
TRACE("SD card write labels");
|
|
const char * error = modelslist.save();
|
|
if (error) {
|
|
TRACE("writeLabels error=%s", error);
|
|
retryLabelsCount += 1;
|
|
} else {
|
|
storageDirtyMsk &= ~EE_LABELS;
|
|
retryLabelsCount = 0;
|
|
}
|
|
} else {
|
|
// Reset timeout to next check
|
|
storageDirtyTime10ms = get_tmr10ms();
|
|
retryLabelsCount = retryLimit / 2; // Retry again after timeout; but fewer times
|
|
// TODO: provide some mechanism to alert user that SD card has serious error
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static uint8_t retryModelCount = 0;
|
|
if (storageDirtyMsk & EE_MODEL) {
|
|
if (retryModelCount < retryLimit) {
|
|
TRACE("SD card write model settings");
|
|
const char * error = writeModel();
|
|
#if defined(STORAGE_MODELSLIST)
|
|
modelslist.updateCurrentModelCell();
|
|
#endif
|
|
if (error) {
|
|
TRACE("writeModel error=%s", error);
|
|
retryModelCount += 1;
|
|
} else {
|
|
storageDirtyMsk &= ~EE_MODEL;
|
|
retryModelCount = 0;
|
|
}
|
|
} else {
|
|
// Reset timeout to next check
|
|
storageDirtyTime10ms = get_tmr10ms();
|
|
retryModelCount = retryLimit / 2; // Retry again after timeout; but fewer times
|
|
// TODO: provide some mechanism to alert user that SD card has serious error
|
|
}
|
|
}
|
|
}
|
|
|
|
#if defined(STORAGE_MODELSLIST)
|
|
const char * createModel()
|
|
{
|
|
preModelLoad();
|
|
|
|
char filename[LEN_MODEL_FILENAME+1];
|
|
memset(filename, 0, sizeof(filename));
|
|
strcpy(filename, MODEL_FILENAME_PATTERN);
|
|
|
|
int index = findNextFileIndex(filename, LEN_MODEL_FILENAME, MODELS_PATH);
|
|
if (index > 0) {
|
|
setModelDefaults(index);
|
|
memcpy(g_eeGeneral.currModelFilename, filename, sizeof(g_eeGeneral.currModelFilename));
|
|
|
|
storageDirty(EE_GENERAL);
|
|
storageDirty(EE_MODEL);
|
|
storageCheck(true);
|
|
#if defined(COLORLCD)
|
|
// Default layout loaded when setting model defaults - neeed to remove it.
|
|
LayoutFactory::deleteCustomScreens(true);
|
|
#endif
|
|
}
|
|
postModelLoad(false);
|
|
|
|
return g_eeGeneral.currModelFilename;
|
|
}
|
|
#endif
|
|
|
|
const char* loadModel(char* filename, bool alarms)
|
|
{
|
|
preModelLoad();
|
|
|
|
const char* error = readModel(filename, (uint8_t*)&g_model, sizeof(g_model));
|
|
if (error) {
|
|
TRACE("loadModel error=%s", error);
|
|
|
|
// 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);
|
|
postModelLoad(false);
|
|
return error;
|
|
}
|
|
|
|
postModelLoad(alarms);
|
|
return nullptr;
|
|
}
|
|
|
|
const char* loadModelTemplate(const char* fileName, const char* filePath)
|
|
{
|
|
preModelLoad();
|
|
// Assuming that the template is located in current working directory
|
|
const char* error = readModel(fileName, (uint8_t*)&g_model, sizeof(g_model), filePath);
|
|
if (error) {
|
|
TRACE("loadModel error=%s", error);
|
|
// 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);
|
|
postModelLoad(false);
|
|
return error;
|
|
}
|
|
|
|
postModelLoad(false);
|
|
return nullptr;
|
|
}
|
|
|
|
void storageReadAll()
|
|
{
|
|
TRACE("storageReadAll");
|
|
|
|
#if defined(STORAGE_MODELSLIST)
|
|
// Wipe models list in case
|
|
// it's being reloaded after USB connection
|
|
modelslist.clear();
|
|
#endif
|
|
|
|
// Some radio defaults overriden by config loading:
|
|
// - screens disabled by default:
|
|
g_eeGeneral.modelCustomScriptsDisabled = true;
|
|
|
|
if (loadRadioSettings() != nullptr) {
|
|
storageEraseAll(true);
|
|
}
|
|
#if !defined(STORAGE_MODELSLIST)
|
|
else {
|
|
loadModelHeaders();
|
|
}
|
|
#endif
|
|
|
|
for (uint8_t i = 0; languagePacks[i] != nullptr; i++) {
|
|
if (!strncmp(g_eeGeneral.ttsLanguage, languagePacks[i]->id, 2)) {
|
|
currentLanguagePackIdx = i;
|
|
currentLanguagePack = languagePacks[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
#if defined(STORAGE_MODELSLIST)
|
|
// and reload the list
|
|
modelslist.load();
|
|
|
|
// Current model filename is empty...
|
|
// Let's fix it!
|
|
if (strlen(g_eeGeneral.currModelFilename) == 0) {
|
|
|
|
// sizeof(currModelFilename) == LEN_MODEL_FILENAME + 1
|
|
// make sure it is terminated (see doc for strncpy())
|
|
strncpy(g_eeGeneral.currModelFilename, DEFAULT_MODEL_FILENAME, LEN_MODEL_FILENAME);
|
|
g_eeGeneral.currModelFilename[LEN_MODEL_FILENAME] = '\0';
|
|
|
|
storageDirty(EE_GENERAL);
|
|
storageCheck(true);
|
|
}
|
|
|
|
if (loadModel(g_eeGeneral.currModelFilename, false) != nullptr) {
|
|
TRACE("No current model or SD card error");
|
|
}
|
|
#else
|
|
if (loadModel(g_eeGeneral.currModel, false) != nullptr) {
|
|
TRACE("No current model or SD card error");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if !defined(COLORLCD)
|
|
void checkModelIdUnique(uint8_t index, uint8_t module)
|
|
{
|
|
//TODO
|
|
}
|
|
#endif
|
|
|