13 KiB
Архитектура: Феникс 6X v1
Шаг 4 процесса разработки (
std1\_process\process.md). Источники: vision.md (v1.5, изменения 1.1–1.5), requirements.md v1.4 (утверждён; ревью №2 и №3), дизайн LLM-планирования (разд. 8), документ форматов, документы «Метрики…» и «Перерывы…». Схема компонентов: hld.puml (+ рендер hld.svg, hld.png). Версия 1.0 — УТВЕРЖДЕНО (2026-10-17). Версия 1.1 — УТВЕРЖДЕНО (2026-10-17): актуализация под Ф-13 (ревью З-2) и Ф-14 — перерывы (изменение vision 1.4); волна 2 ревью (Р1–Р3). Версия 1.2 — УТВЕРЖДЕНО (2026-10-17): ревью №2, З-29 — переченьLocalDbдополнен (daily_log,breaks); источник — requirements v1.4.
1. Компоненты и размещение
1.1. Часы атлета — FenixDataField (Monkey C, Connect IQ)
Поле данных «Феникс» в штатном профиле «Силовая». Только отображение
(пульс, средний за подход, время, цвет зоны, вибро) — запись .fit
ведёт платформа Garmin (инвариант из requirements, Ф-2.7). Один исходный
код, сборки под целевые модели Garmin — конфигурациями (изменение
vision 1.1); перечень моделей — открытый вопрос 8 требований.
Связь с телефоном — штатная трансляция пульса «Broadcast Heart
Rate», приём на телефоне по BLE (WatchChannel, компонент
телефона; errata ОВ-3 от 2026-10-19: companion-канал Connect IQ
на fenix 6X недоступен для полей данных — CIQ ≤ 4.1.7): live-пульс
на телефоне, канал односторонний (часы → телефон), таймер отдыха
на часах автономный; отсутствие связи ничего не ломает — часы
автономны.
1.2. Телефон атлета — Phone.WatchBody (Android, Kotlin)
TrainingUi— экраны (Jetpack Compose — кандидат, фиксируется шагом 5): активная тренировка, история, программы, профиль, «Сегодня» (дневник) и «Допуск»; тёмная тема, крупные зоны касания.VoiceInput— офлайн-STT (движок — открытый вопрос 2) + грамматика команд (Ф-1.2) + TTS-подтверждения; журнал команд для сопоставления с .fit.WorkoutCore— логика тренировки и программ: подходы, таймеры, план/факт, редактор программ; 🛡 предстартовый допуск (пороги Safety, экран допуска) и дневник самоконтроля (daily_log); автомат перерывов и формула возврата (Ф-14); единственный писательLocalDb.LocalDb— Room/SQLite: текущая тренировка, история, программы, библиотека, профиль, дневник самоконтроля (daily_log), перерывы (breaks), очередь.SyncQueue— исходящие записи сclient_guid, пакеты ≤ 50, идемпотентная отправкаPOST /api/v1/sync; тихие баннеры статуса.WatchChannel— приём live-пульса с часов: BLE Heart Rate Service за интерфейсомWatchTransport(платформо-независимый — любые часы со стандартной трансляцией); статус связи, заряд, средний пульс тренировки.
🛡 Офлайн-first: сеть стоит только на пути SyncQueue; все остальные
компоненты работают без сети (requirements, раздел 5).
1.3. Личный сервер — Server.WatchBody (C# / .NET 10, PostgreSQL)
Проекты в 3-app\:
| Компонент | Проект (3-app\2-cli\) |
Ответственность |
|---|---|---|
RestApi |
Server.WatchBody.Web |
POST /api/v1/sync, GET workouts, GET programs/active; ключ атлета → athlete_id; HTTPS; + admin REST API (/api/admin/*) для консоли |
AdminWeb |
UI — отдельное Flutter Web-приложение (3-app\3-web\admin-web); бэкенд — admin REST API в Server.WatchBody.Web |
Консоль владельца (логин/пароль): атлеты, тренировки + ход, программы, планы, метрики, CSV (решение владельца 2026-10-17: UI как отдельное Flutter-приложение вместо Razor Pages) |
FitFetcher |
Server.WatchBody.Fetch |
Клиент Garmin Connect (неофициальный, опросник 4.3); --mode fetch; дедупликация по fit_file_id |
FitParser |
Server.WatchBody.Import |
Garmin FIT SDK: Session/Lap/Record/Set → workout_facts; --mode import (fallback: .fit/.tcx/.gpx из папки) |
MetricsEngine |
Server.WatchBody.Core |
adherence, тоннаж по группам, 1ПМ, пульс-метрики, красные флаги (АД/пульс/боль — документ метрик) → workout_metrics |
ProgressionRules |
Server.WatchBody.Core |
Правила прогрессии, делоада и координации 1–11 (документ метрик) + 12–17 перерывы (документ перерывов): детекция break по пропускам, формула возврата → draft-версии (author=rules) |
ProgramStore |
Server.WatchBody.Core |
Версионированный JSON программ; неизменяемость версий; active — ровно один на атлета |
| БД | схема src | ref |
PostgreSQL: атлеты, тренировки/факты + session_kind, дневник (daily_log), перерывы (breaks), метрики, программы, журнал LLM (v2) |
Общая библиотека — Kit.Helpers (3-app\1-lib, std1). Взаимодействие
с LLM (v2, LlmPlanner) — по правилам std1 c-llm; в v1 компонент
не реализуется (на схеме — серым).
1.4. Внешние системы (жёлтым на схеме)
- Garmin Connect — облако: источник
.fit(заборFitFetcherпо расписанию — внешний планировщик, открытый вопрос 4). - Garmin (устройства) — запись
.fitна часах, трансляция пульса (BLE/ANT+).
2. Поток цикла обработки
- Допуск и перерывы. Перед стартом — экран допуска (АД/пульс;
красные флаги блокируют или переводят в лёгкое кардио; обход
логируется); автомат перерывов ставит
breakпо пропускам и запрашивает причину у атлета; при возврате — формульная версия (Ф-14). - Тренировка. Атлет запускает «Силовую» на часах (
FenixDataFieldпоказывает пульс/зоны/подходы) и активирует голосовой ввод на телефоне (VoiceInput→ команды →WorkoutCore→LocalDb). Часы и телефон независимы: любой из них работает без другого (🛡). - Фиксация плана.
WorkoutCoreпоказывает позицию активной программы (RestApi → ProgramStore, при наличии сети; локальная копия — офлайн), помечает подходы «по плану/отклонение». - Синхронизация.
SyncQueueдосылает новые записи наRestApiпри появлении сети; сервер пишет их поclient_guid(идемпотентно) от имени атлета ключа. - Трек. Часы синхронизируются с Garmin Connect;
FitFetcherпо расписанию забирает новые.fit,FitParserраскладывает вworkout_facts; сопоставление с тренировкой телефона — по времени начала ± 15 минут (Ф-8.4). - Метрики и правила.
MetricsEngineсчитает adherence/тоннаж/1ПМ;ProgressionRulesформируют draft-версию следующей программы (микроправки ±2,5 кг / −5 %, делоад). - Подтверждение. Draft показывается атлету на телефоне (дифф) или
владельцу в
AdminWeb; после подтверждения версия становитсяactive(🛡 автоматической смены нет). - Наблюдение. Владелец видит в
AdminWebход тренировок, планы/факт, динамику; CSV — выгрузка выборки.
3. Границы и контракты
| Граница | Контракт | Открытые вопросы |
|---|---|---|
| Телефон ↔ сервер | REST/HTTPS, X-Api-Key (атлет), client_guid-идемпотентность, пакеты ≤ 50; формат записей — JSON (документ форматов) |
— (спецификация — шаг 5) |
| Телефон ↔ часы | штатная трансляция пульса «Broadcast Heart Rate» → BLE-приём (WatchTransport): live-пульс, заряд; односторонний канал; деградация без связи |
ОВ-3 (закрыт errata 2026-10-19) |
| Сервер ↔ Garmin Connect | неофициальный клиент, учётка владельца; деградация — папка импорта --mode import |
— |
| Сервер ↔ LLM (v2) | structured output, валидатор, офлайн-fallback на правила (дизайн LLM-планирования) | до v2 |
4. Точки расширения
- Модели часов — новая конфигурация сборки
FenixDataField(разметка под экран), код не меняется (изменение vision 1.1). - LLM-планирование (v2) —
LlmPlannerпотребляет сводкуMetricsEngineи предлагает версии черезProgramStore;ProgressionRulesостаются базовым режимом (офлайн-fallback). - Трековые виды спорта (v3+) — расширение
FitParser(телеметрия) иWorkoutCore(ввод темпа/дистанции); пульс/синхронизация/архив не меняются (опросник 4.7). - Общий справочник упражнений (v2) — надстройка над изолированными библиотеками атлетов (ОВ-11 требований).
5. Архитектурные инварианты (🛡)
- Независимость частей — часы, телефон, сервер работают при отсутствии остальных; ни один путь записи не проходит через сеть.
- Офлайн-first — источник истины на телефоне —
LocalDb; сервер — архив и планирование. - Идемпотентность —
client_guidна телефоне ↔ сервере,fit_file_idна заборе, архивация файлов импорта: повторы не создают дублей. - Изоляция атлетов — ключ →
athlete_id; все выборки API и метрики — в пределах атлета; веб — только владелец. - Неизменяемость истории — версии программ и факт-записи не переписываются; корректировка = новая версия.
- LLM не источник истины — в v1 решений LLM нет; в v2 — только предложения через валидатор и подтверждение (БР-5).