/
DemienMedich
/
BodyweightBase
Обзор
Документация
Войти
/
DemienMedich
/
BodyweightBase
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
develop
core/src/exercise_catalog.cpp
116 строк
5 KB
DemienMedich
Add configurable exercise frame playback
18 июл 2026, 04:25
18 июл 2026, 04:25
da929ca
Код
Авторство
О чём код?
#include "bodyweight/exercise_catalog.h" #include <algorithm> #include <QFile> #include <QJsonArray> #include <QJsonDocument> #include <QJsonObject> #include <QJsonParseError> #include <QSet> namespace bodyweight { ExerciseCatalogResult importExerciseCatalog(const QByteArray &json) { ExerciseCatalogResult result; QJsonParseError parseError; const QJsonDocument document = QJsonDocument::fromJson(json, &parseError); if (parseError.error != QJsonParseError::NoError) { result.error = QStringLiteral("Invalid exercise catalog JSON: %1").arg(parseError.errorString()); return result; } if (!document.isObject()) { result.error = QStringLiteral("Exercise catalog root must be an object."); return result; } const QJsonObject root = document.object(); if (root.value("schemaVersion").toInt() != 1) { result.error = QStringLiteral("Unsupported exercise catalog schema."); return result; } result.libraryVersion = root.value("libraryVersion").toString(); if (!root.value("exercises").isArray()) { result.error = QStringLiteral("Exercises must be an array."); return result; } QSet<QString> ids; for (const QJsonValue &value : root.value("exercises").toArray()) { if (!value.isObject()) { result.warnings.append(QStringLiteral("Exercise catalog contains a non-object entry.")); continue; } const QJsonObject object = value.toObject(); ExerciseDefinition exercise; exercise.id = object.value("id").toString(); exercise.name = object.value("name").toString(); exercise.category = object.value("category").toString(); exercise.equipment = object.value("equipment").toString(); exercise.description = object.value("description").toString(); exercise.primaryCue = object.value("primaryCue").toString(); exercise.secondaryCue = object.value("secondaryCue").toString(); exercise.loadRecommendation = object.value("loadRecommendation").toString(); exercise.availabilityNote = object.value("availabilityNote").toString(); exercise.metric = object.value("metric").toString() == "seconds" ? ExerciseMetric::seconds : ExerciseMetric::repetitions; exercise.defaultTarget = object.value("defaultTarget").toInt(); exercise.defaultRestSeconds = object.value("defaultRestSeconds").toInt(); exercise.frameDurationMs = std::clamp(object.value("frameDurationMs").toInt(1100), 350, 10000); const QString playbackValue = object.value("framePlayback").toString(QStringLiteral("loop")).trimmed().toLower(); if (playbackValue == QStringLiteral("pingpong")) { exercise.framePlayback = QStringLiteral("pingPong"); } else if (playbackValue == QStringLiteral("loop") || playbackValue == QStringLiteral("steps") || playbackValue == QStringLiteral("static")) { exercise.framePlayback = playbackValue; } else { exercise.framePlayback = QStringLiteral("loop"); result.warnings.append( QStringLiteral("Exercise '%1' has unsupported framePlayback '%2'; using loop.") .arg(exercise.id, playbackValue)); } if (object.value("frameDurationsMs").isArray()) { for (const QJsonValue &durationValue : object.value("frameDurationsMs").toArray()) { if (!durationValue.isDouble()) { result.warnings.append( QStringLiteral("Exercise '%1' has a non-numeric frame duration; entry skipped.") .arg(exercise.id)); continue; } exercise.frameDurationsMs.append(std::clamp(durationValue.toInt(), 350, 10000)); } } exercise.alternatingSides = object.value("alternatingSides").toBool(); const QString normalizedId = exercise.id.toLower(); if (exercise.id.isEmpty() || ids.contains(normalizedId)) { result.warnings.append(QStringLiteral("Exercise has an empty or duplicate Id: %1").arg(exercise.id)); continue; } ids.insert(normalizedId); result.exercises.append(std::move(exercise)); } if (result.exercises.isEmpty()) { result.error = QStringLiteral("Exercise catalog contains no usable exercises."); return result; } result.ok = true; return result; } ExerciseCatalogResult importExerciseCatalogFile(const QString &path) { QFile file(path); if (!file.open(QIODevice::ReadOnly)) { ExerciseCatalogResult result; result.error = QStringLiteral("Could not open '%1': %2").arg(path, file.errorString()); return result; } return importExerciseCatalog(file.readAll()); } } // namespace bodyweight