155 lines
13 KiB
Markdown
155 lines
13 KiB
Markdown
# Архитектура: Феникс 6X v1
|
||
|
||
> Шаг 4 процесса разработки (`std1\_process\process.md`).
|
||
> Источники: [vision.md](../1-reqs/vision.md) (v1.5, изменения 1.1–1.5),
|
||
> [requirements.md](../1-reqs/requirements.md) v1.4 (утверждён; ревью
|
||
> №2 и №3),
|
||
> дизайн LLM-планирования (разд. 8), документ форматов, документы
|
||
> «Метрики…» и «Перерывы…».
|
||
> Схема компонентов: [hld.puml](hld.puml) (+ рендер [hld.svg](hld.svg),
|
||
> [hld.png](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. Поток цикла обработки
|
||
|
||
0. **Допуск и перерывы.** Перед стартом — экран допуска (АД/пульс;
|
||
красные флаги блокируют или переводят в лёгкое кардио; обход
|
||
логируется); автомат перерывов ставит `break` по пропускам и
|
||
запрашивает причину у атлета; при возврате — формульная версия
|
||
(Ф-14).
|
||
1. **Тренировка.** Атлет запускает «Силовую» на часах (`FenixDataField`
|
||
показывает пульс/зоны/подходы) и активирует голосовой ввод на телефоне
|
||
(`VoiceInput` → команды → `WorkoutCore` → `LocalDb`). Часы и телефон
|
||
независимы: любой из них работает без другого (🛡).
|
||
2. **Фиксация плана.** `WorkoutCore` показывает позицию активной
|
||
программы (`RestApi → ProgramStore`, при наличии сети; локальная
|
||
копия — офлайн), помечает подходы «по плану/отклонение».
|
||
3. **Синхронизация.** `SyncQueue` досылает новые записи на `RestApi`
|
||
при появлении сети; сервер пишет их по `client_guid` (идемпотентно)
|
||
от имени атлета ключа.
|
||
4. **Трек.** Часы синхронизируются с Garmin Connect; `FitFetcher`
|
||
по расписанию забирает новые `.fit`, `FitParser` раскладывает
|
||
в `workout_facts`; сопоставление с тренировкой телефона — по времени
|
||
начала ± 15 минут (Ф-8.4).
|
||
5. **Метрики и правила.** `MetricsEngine` считает adherence/тоннаж/1ПМ;
|
||
`ProgressionRules` формируют draft-версию следующей программы
|
||
(микроправки ±2,5 кг / −5 %, делоад).
|
||
6. **Подтверждение.** Draft показывается атлету на телефоне (дифф) или
|
||
владельцу в `AdminWeb`; после подтверждения версия становится
|
||
`active` (🛡 автоматической смены нет).
|
||
7. **Наблюдение.** Владелец видит в `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. Архитектурные инварианты (🛡)
|
||
|
||
1. **Независимость частей** — часы, телефон, сервер работают при
|
||
отсутствии остальных; ни один путь записи не проходит через сеть.
|
||
2. **Офлайн-first** — источник истины на телефоне — `LocalDb`; сервер
|
||
— архив и планирование.
|
||
3. **Идемпотентность** — `client_guid` на телефоне ↔ сервере,
|
||
`fit_file_id` на заборе, архивация файлов импорта: повторы не создают
|
||
дублей.
|
||
4. **Изоляция атлетов** — ключ → `athlete_id`; все выборки API и метрики
|
||
— в пределах атлета; веб — только владелец.
|
||
5. **Неизменяемость истории** — версии программ и факт-записи не
|
||
переписываются; корректировка = новая версия.
|
||
6. **LLM не источник истины** — в v1 решений LLM нет; в v2 — только
|
||
предложения через валидатор и подтверждение (БР-5).
|