fix: [companion] Show cause of error if unable to read from radio, scan MODELS dir if models.yml missing
Fallback to model file scanning if models.yml cannot be read
This commit is contained in:
@@ -51,34 +51,27 @@ bool loadModelsListFromYaml(std::vector<CategoryData>& categories,
|
||||
if (data.size() == 0)
|
||||
return true;
|
||||
|
||||
try {
|
||||
YAML::Node node = loadYamlFromByteArray(data);
|
||||
if (!node.IsSequence()) return false;
|
||||
YAML::Node node = loadYamlFromByteArray(data);
|
||||
if (!node.IsSequence()) return false;
|
||||
|
||||
int modelIdx = 0;
|
||||
for (const auto& cat : node) {
|
||||
int modelIdx = 0;
|
||||
for (const auto& cat : node) {
|
||||
if (!cat.IsMap()) continue;
|
||||
|
||||
if (!cat.IsMap()) continue;
|
||||
for (const auto& cat_map : cat) {
|
||||
categories.push_back(cat_map.first.Scalar().c_str());
|
||||
|
||||
for (const auto& cat_map : cat) {
|
||||
categories.push_back(cat_map.first.Scalar().c_str());
|
||||
const auto& models = cat_map.second;
|
||||
if (!models.IsSequence()) continue;
|
||||
|
||||
const auto& models = cat_map.second;
|
||||
if (!models.IsSequence()) continue;
|
||||
|
||||
for (const auto& model : models) {
|
||||
std::string filename, name;
|
||||
model["filename"] >> filename;
|
||||
model["name"] >> name;
|
||||
modelFiles.push_back(
|
||||
{filename, name, (int)categories.size() - 1, modelIdx++});
|
||||
}
|
||||
for (const auto& model : models) {
|
||||
std::string filename, name;
|
||||
model["filename"] >> filename;
|
||||
model["name"] >> name;
|
||||
modelFiles.push_back(
|
||||
{filename, name, (int)categories.size() - 1, modelIdx++});
|
||||
}
|
||||
}
|
||||
|
||||
} catch (const std::runtime_error& e) {
|
||||
qDebug() << "YAML::ParserException: " << e.what();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -117,18 +110,35 @@ bool writeModelsListToYaml(const std::vector<CategoryData>& categories,
|
||||
QByteArray& data)
|
||||
{
|
||||
YAML::Node node;
|
||||
std::vector<CategoryData> cats = categories;
|
||||
std::vector<EtxModelMetadata> files = { modelFiles.begin(), modelFiles.end() };
|
||||
|
||||
std::stable_sort(files.begin(), files.end(),
|
||||
[](const EtxModelMetadata &a, const EtxModelMetadata &b) {
|
||||
return a.category < b.category;
|
||||
});
|
||||
|
||||
int catIdx = 0;
|
||||
for (const auto& cat : cats) {
|
||||
node[catIdx++][cat.name] = YAML::Node();
|
||||
}
|
||||
|
||||
for (const auto& modelFile: modelFiles) {
|
||||
|
||||
YAML::Node cat_attrs;
|
||||
cat_attrs["filename"] = modelFile.filename;
|
||||
cat_attrs["name"] = modelFile.name;
|
||||
|
||||
if (modelFile.category >= (int)categories.size()) {
|
||||
return false;
|
||||
catIdx = modelFile.category;
|
||||
if (catIdx >= (int)cats.size()) {
|
||||
catIdx = 0;
|
||||
if (cats.size() == 0) {
|
||||
cats.push_back("Models");
|
||||
}
|
||||
}
|
||||
|
||||
const std::string cat_name = categories[modelFile.category].name;
|
||||
node[modelFile.category][cat_name].push_back(cat_attrs);
|
||||
const std::string cat_name = cats[catIdx].name;
|
||||
node[catIdx][cat_name].push_back(cat_attrs);
|
||||
}
|
||||
|
||||
writeYamlToByteArray(node, data);
|
||||
|
||||
@@ -671,6 +671,7 @@ void TreeModel::refresh()
|
||||
defaultCategoryItem = rootItem->appendChild(0, -1);
|
||||
/*: Translators do NOT use accent for this, this is the default category name on Horus. */
|
||||
defaultCategoryItem->setData(0, tr("Models"));
|
||||
radioData->categories.push_back(qPrintable(tr("Models")));
|
||||
}
|
||||
categoryItem = defaultCategoryItem;
|
||||
}
|
||||
|
||||
@@ -211,12 +211,16 @@ bool readSettingsSDCard(const QString & filename, ProgressWidget * progress)
|
||||
RadioData radioData;
|
||||
Storage inputStorage(radioPath);
|
||||
if (!inputStorage.load(radioData)) {
|
||||
QMessageBox::critical(progress, CPN_STR_TTL_ERROR, QCoreApplication::translate("RadioInterface", "Failed to read Models and Settings from") % radioPath);
|
||||
QString errorMsg = inputStorage.error();
|
||||
if (errorMsg.isEmpty()) {
|
||||
errorMsg = QCoreApplication::translate("RadioInterface", "Failed to read Models and Settings from") % radioPath;
|
||||
}
|
||||
QMessageBox::critical(progress, CPN_STR_TTL_ERROR, errorMsg);
|
||||
return false;
|
||||
}
|
||||
Storage outputStorage(filename);
|
||||
if (!outputStorage.write(radioData)) {
|
||||
QMessageBox::critical(progress, CPN_STR_TTL_ERROR, QCoreApplication::translate("RadioInterface", "Failed to write Models and Setting file") % filename);
|
||||
QMessageBox::critical(progress, CPN_STR_TTL_ERROR, QCoreApplication::translate("RadioInterface", "Failed to write Models and Setting file") % " " % filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,8 @@ bool CategorizedStorageFormat::load(RadioData & radioData)
|
||||
if (st == STORAGE_TYPE_UNKNOWN) {
|
||||
st = probeFormat();
|
||||
}
|
||||
if (st == STORAGE_TYPE_ETX || st == STORAGE_TYPE_YML) {
|
||||
if (st == STORAGE_TYPE_ETX || st == STORAGE_TYPE_YML
|
||||
|| st == STORAGE_TYPE_UNKNOWN) {
|
||||
return loadYaml(radioData);
|
||||
} else {
|
||||
return loadBin(radioData);
|
||||
@@ -254,9 +255,35 @@ bool CategorizedStorageFormat::loadYaml(RadioData & radioData)
|
||||
|
||||
QByteArray modelslistBuffer;
|
||||
if (loadFile(modelslistBuffer, "MODELS/models.yml")) {
|
||||
if (!loadModelsListFromYaml(radioData.categories, modelFiles, modelslistBuffer)) {
|
||||
setError(tr("Can't load MODELS/models.yml"));
|
||||
return false;
|
||||
try {
|
||||
if (!loadModelsListFromYaml(radioData.categories, modelFiles, modelslistBuffer)) {
|
||||
setError(tr("Can't load MODELS/models.yml"));
|
||||
return false;
|
||||
}
|
||||
} catch(const std::runtime_error& e) {
|
||||
setError(tr("Can't load MODELS/models.yml") + ":\n" + QString(e.what()));
|
||||
//return false;
|
||||
|
||||
//TODO: fall back to directory scan
|
||||
std::list<std::string> filelist;
|
||||
if (!getFileList(filelist)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// push default category
|
||||
radioData.categories.push_back("Models");
|
||||
|
||||
const std::regex yml_regex("MODELS/(model([0-9]+)\\.yml)");
|
||||
for(const auto& f : filelist) {
|
||||
std::smatch match;
|
||||
if (std::regex_match(f, match, yml_regex)) {
|
||||
if (match.size() == 3) {
|
||||
std::ssub_match modelFile = match[1];
|
||||
std::ssub_match modelIdx = match[2];
|
||||
modelFiles.push_back({ modelFile.str(), "", 0, std::stoi(modelIdx.str()) });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// fetch "MODELS/modelXX.yml"
|
||||
@@ -279,6 +306,9 @@ bool CategorizedStorageFormat::loadYaml(RadioData & radioData)
|
||||
}
|
||||
}
|
||||
|
||||
int modelIdx = 0;
|
||||
bool hasCategories = getCurrentFirmware()->getCapability(HasModelCategories);
|
||||
|
||||
radioData.models.resize(modelFiles.size());
|
||||
for (const auto& mc : modelFiles) {
|
||||
qDebug() << "Filename: " << mc.filename.c_str() << " / Category: " << mc.category;
|
||||
@@ -293,7 +323,7 @@ bool CategorizedStorageFormat::loadYaml(RadioData & radioData)
|
||||
// Please note:
|
||||
// ModelData() use memset to clear everything to 0
|
||||
//
|
||||
auto& model = radioData.models[mc.modelIdx];
|
||||
auto& model = radioData.models[modelIdx];
|
||||
|
||||
try {
|
||||
if (!loadModelFromYaml(model,modelBuffer)) {
|
||||
@@ -306,10 +336,17 @@ bool CategorizedStorageFormat::loadYaml(RadioData & radioData)
|
||||
}
|
||||
|
||||
model.category = mc.category;
|
||||
model.modelIndex = mc.modelIdx;
|
||||
model.modelIndex = modelIdx;
|
||||
strncpy(model.filename, mc.filename.c_str(), sizeof(model.filename)-1);
|
||||
|
||||
if (hasCategories && !strncmp(radioData.generalSettings.currModelFilename,
|
||||
model.filename, sizeof(model.filename))) {
|
||||
radioData.generalSettings.currModelIndex = modelIdx;
|
||||
qDebug() << "currModelIndex =" << modelIdx;
|
||||
}
|
||||
|
||||
model.used = true;
|
||||
modelIdx++;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
Reference in New Issue
Block a user