133 lines
12 KiB
Markdown
133 lines
12 KiB
Markdown
# 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 приёмки на
|
||
стороне владельца; логика покрыта фейками и байтовыми фикстурами.
|