VEU.WathBody/1-docs/4-modules/m12-watch-channel/readme.md

133 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# m12-watch-channel — план работ (d-dev)
> Модуль: WatchChannel — приём live-пульса с Fenix 6X по штатной
> трансляции BLE (Heart Rate Service), статус связи/заряд, подача
> в «Тренировку»/«Допуск», `avg_hr` при финише. Транспорт-обоснование
> и errata ОВ-3/Ф-2.4 — [module_reqs.md](module_reqs.md) разд. 1.1/5.
> Алгоритм: `std1\_rules\d\d-dev.md`.
> Зависимости: m09 (WorkoutCore/`finishWorkout(avgHr)`, DataStore —
> DONE), m10 (экраны «Тренировка»/«Допуск»/«Настройки»,
> ZoneCalculator — DONE), m11 (DONE; независимо). Часы: Fenix 6X
> (якорь, владелец, 2026-10-19).
| Шаг | Что делаем | Статус |
|---|---|---|
| 0 | Модульные документы: module_reqs.md / readme.md / SKILL.md | ✅ 2026-10-19 |
| 1 | Список единиц (BLE-клиент/буфер/фасад/UI×3/разрешения/тесты) | ✅ 2026-10-19 |
| 1.3 | **Пользователь!** утверждение списка (+ решения по транспорту и errata Ф-2.4) | ✅ 2026-10-19 (+ указание: инфраструктура мультичасовых сборок) |
| 2 | Спецификации: api_specs (BLE-клиент/буфер/фасад, UI×3, payload — без изменений контракта) | ✅ 2026-10-19 (01-transport+BLE, 02-store, 03-facade, 04-UI) |
| 2.4 | **Пользователь!** утверждение спецификаций | ✅ 2026-10-19 |
| 3 | Реализация + тесты + сборка | ✅ 2026-10-19 |
| 3.4 | **Пользователь!** утверждение реализации (закрытие модуля) | ✅ 2026-10-19 |
| 4 | Завершение: план, примечания, волна errata НД | ✅ 2026-10-19 |
Статусы: ⬜ не начат · 🔄 в работе · ✅ готов.
## Список единиц (шаг 1)
| # | Единица | Что делает |
|---|---|---|
| 1 | `WatchTransport` + `HrBleClient` | транспорт-интерфейс (события/статусы, мультичасовая инфраструктура — решение 6) + BLE-реализация: скан по UUID 0x180D, подключение, indications 0x2A37, разбор пакета (8/16 бит, Energy/RR пропуск), автопереподключение, Battery Service (0x180F) при наличии |
| 2 | `HrSampleStore` | чистый буфер в памяти: сэмплы (bpm, `t_offset_s`), текущее/среднее/серия «последний подход», устаревание >10 с |
| 3 | `WatchChannel` (фасад) | `StateFlow` статуса (не выбраны/скан/связь/потеря/BT-выкл/нет разрешения 🛡 текстом) + пульс + заряд + серия; активен при открытых экранах «Тренировка»/«Допуск» |
| 4 | UI «Тренировка» | статус-строка часов (зелёный/красный/серый + текст, заряд), крупный пульс с окраской по зонам Танака, линия последнего подхода (Canvas), прочерки при отсутствии; `finishWorkout(avgHr=среднее)` |
| 5 | UI «Допуск» | кнопка «взять с часов» при свежем сэмпле ≤30 с → поле «Пульс сидя» (Ф-13.2) |
| 6 | UI «Настройки» | блок «Часы»: скан/список/выбор (`Watch.HrDeviceAddress`), статус, сброс, инструкция «включите Broadcast Heart Rate» |
| 7 | Разрешения | `BLUETOOTH_SCAN`(neverForLocation)+`BLUETOOTH_CONNECT` (12+, runtime), `ACCESS_FINE_LOCATION`+`BLUETOOTH/BLUETOOTH_ADMIN` (8–11); отказ — статус, не блок |
| 8 | `WorkoutCore.finishWorkout(avgHr)` | подстановка среднего из канала при финише (цепочка m09 готова: `workout.avg_hr` → sync `avg_hr`) |
| 9 | unit-тесты | парсер 0x2A37 (байтовые фикстуры), буфер (среднее/серия/устаревание), фасад на фейках (переходы статусов) |
## Решения на гейт 1.3 (для владельца)
1. **Транспорт live-пульса** — рекомендуем (а): штатная трансляция
«Broadcast Heart Rate» Fenix 6X, приём по **BLE** (стандартный
Heart Rate Service; работает на любом Android, Garmin Connect
Mobile не нужен; трансляция ANT++BLE на Pro-линии fenix 6 —
прошивка июня 2020). Альтернативы: (б) ANT+-плагин Garmin —
нужен ANT-транспорт в телефоне (Samsung до S20 или USB-стик),
в v1 не реализуем (кандидат v2); (в) CIQ companion SDK — живой
пульс не даёт: поле данных на fenix 6X без Communications
(CIQ ≤ 4.1.7 < 5.0), CIQ-приложение не работает во время
штатной активности.
2. **Errata Ф-2.4 / ОВ-3** — таймер отдыха на часах автономный
(длительность — настройка поля в Garmin Connect, умолчание
`Rest.DefaultSec`); с телефона в v1 не передаётся (канал
односторонний). Пакет правок НД — module_reqs разд. 5,
вносится после утверждения.
3. **Заряд часов** — из Battery Service, если трансляция его
exposing; иначе прочерк (Ф-3.1 «если передаётся» допускает).
4. **Хранение live-пульса** — только в памяти: буфер линии
последнего подхода + среднее в `workout.avg_hr` при финише;
сэмплы в LocalDb/sync не пишутся (история пульса — из .fit
m07/m08), схема БД и sync-контракт не меняются.
5. **Жизненный цикл канала** — активен при открытых экранах
«Тренировка»/«Допуск» (foreground-сервис — кандидат v2);
среднее считается за время работы канала (ограничение
фиксируется в readme приёмки).
6. **Мультичасовая инфраструктура** (указание владельца при
утверждении гейта 1.3, 2026-10-19) — транспорт скрывается
за интерфейсом `WatchTransport` (BLE-реализация — первая;
CIQ 5+/ANT+ — реализациями того же интерфейса, без правок
фасада/UI/БД); в коде только стандартные профили GATT,
без Garmin/fenix-специфики — любые часы со стандартной
BLE-трансляцией пульса работают без изменений; платформо-
специфичные тексты подсказок — расширяемый справочник
(v1 — Garmin). Требование наследуется m13: мультитаргет-
сборка по моделям/разрешениям (vision 1.1, Ф-2.8).
## Примечания по ходу работ
0. **Волна errata НД внесена (2026-10-19, после «утверждаю» гейта
1.3):** requirements Ф-2.4/Ф-3.2 + реестр открытых вопросов № 3,
tech-req 3 п. 3 (ОВ-3) + 7 п. 1, architecture 1.1/1.2/3/1.4 —
транспорт = штатная BLE-трансляция «Broadcast Heart Rate»,
канал односторонний, таймер отдыха на часах автономный,
`WatchTransport`-интерфейс (мультичасовая инфраструктура).
Версии документов не поднимались (ближайшее ревью).
1. **Исследование канала (2026-10-19, веб)**: fenix 6X — CIQ API
max 4.1.7 (Communications в полях данных — с API 5.0.0, только
foreground); BLE HR-броадкаст fenix 6 серии — firmware 2020
(DCRainmaker, проверено на 6 Pro — Pro-линия 6X); Android
companion SDK жив (`com.garmin.connectiq:ciq-companion-app-sdk`,
Maven Central) — для v1 неприменим (см. решение 1).
2. Инструкция включения трансляции (для приёмки, по руководству
Garmin): на часах — виджет пульса → удержание MENU →
«Heart Rate Options» → «Broadcast During Activity» = Вкл
(или разовый «Broadcast Heart Rate» перед тренировкой).
3. BLE-адрес часов при «Broadcast Heart Rate» виден как обычный
пульс-датчик; в зале возможны чужие HRM — выбор устройства
в «Настройках» обязателен (не авто-first-found).
4. **Состав реализации** (`app/src/main/java/com/watchbody/phone/watch/`,
7 файлов): `WatchTransport.kt` (интерфейс + TransportEvent/Status
+ ReconnectPolicy 1/5/30 c + BleProfiles — только стандартные
UUID), `HrPacketParser.kt` (0x2A37, 8/16 бит, Energy/RR пропуск,
диапазон 30–250), `HrSampleStore.kt`, `HrBleClient.kt` (скан
по 0x180D → connectGatt → indications 0x2A37; оба поколения
GATT-колбэков — 33+ и <33; батарея 0x180F опционально; приёмник
ACTION_STATE_CHANGED; автопереподключение), `WatchChannel.kt`
(фасад: StateFlow статусов, bind/unbind/**dispose**, avgHr/
freshHr/series/clearSamples, тикер устаревания 2 с),
`WatchFormat.kt` (тексты/зоны/подписи — чистые), `WatchPermissions.kt`
(+ AndroidChannelEnvironment), `DeviceScanner.kt` (скан 15 с
для «Настроек»). Интеграция: `AppSettings.watchHrDeviceAddress`
(DataStore), контейнер (watchChannel + deviceScanner),
WorkoutScreen (статус-строка m12, строка NO_PERMISSION+«Разрешить»,
HrBlock 44 sp с зоной и Canvas-линией, finish→avgHr+clear, bind/
unbind/ребинд при возврате разрешения), AdmissionScreen («Взять
с часов» + подпись причины), SettingsScreen (блок «Часы»: скан/
список/тап-выбор/«Забыть»/инструкция), манифест — 5 разрешений
(SCAN neverForLocation, CONNECT, BLUETOOTH/ADMIN ≤30,
ACCESS_FINE_LOCATION).
5. **Сборка/тесты**: BUILD SUCCESSFUL; unit **89/89** (m12 +26:
парсер 7, буфер 6, фасад 9 — включая ReconnectPolicy, формат 4);
APK 57,3 МБ (рост на ~0 — доминируют JNI Vosk из m11).
6. **Уточнения по ходу** (спеки не меняют наблюдаемое поведение):
NO_PERMISSION вычисляет фасад, в TransportStatus не входит
(спека 01 ставила его в транспорт); добавлен `WatchChannel.dispose()`
— полная остановка для тестов (в проде канал живёт с контейнером).
7. **Не проверено в среде** (нет телефона/часов в контуре агента):
живой BLE-приём с Fenix 6X, батарея через Battery Service,
реальный скан, runtime-разрешения — критерии 2–4 приёмки на
стороне владельца; логика покрыта фейками и байтовыми фикстурами.