12 KiB
m12-watch-channel — план работ (d-dev)
Модуль: WatchChannel — приём live-пульса с Fenix 6X по штатной трансляции BLE (Heart Rate Service), статус связи/заряд, подача в «Тренировку»/«Допуск»,
avg_hrпри финише. Транспорт-обоснование и errata ОВ-3/Ф-2.4 — 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 (для владельца)
- Транспорт 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-приложение не работает во время штатной активности.
- Errata Ф-2.4 / ОВ-3 — таймер отдыха на часах автономный
(длительность — настройка поля в Garmin Connect, умолчание
Rest.DefaultSec); с телефона в v1 не передаётся (канал односторонний). Пакет правок НД — module_reqs разд. 5, вносится после утверждения. - Заряд часов — из Battery Service, если трансляция его exposing; иначе прочерк (Ф-3.1 «если передаётся» допускает).
- Хранение live-пульса — только в памяти: буфер линии
последнего подхода + среднее в
workout.avg_hrпри финише; сэмплы в LocalDb/sync не пишутся (история пульса — из .fit m07/m08), схема БД и sync-контракт не меняются. - Жизненный цикл канала — активен при открытых экранах «Тренировка»/«Допуск» (foreground-сервис — кандидат v2); среднее считается за время работы канала (ограничение фиксируется в readme приёмки).
- Мультичасовая инфраструктура (указание владельца при
утверждении гейта 1.3, 2026-10-19) — транспорт скрывается
за интерфейсом
WatchTransport(BLE-реализация — первая; CIQ 5+/ANT+ — реализациями того же интерфейса, без правок фасада/UI/БД); в коде только стандартные профили GATT, без Garmin/fenix-специфики — любые часы со стандартной BLE-трансляцией пульса работают без изменений; платформо- специфичные тексты подсказок — расширяемый справочник (v1 — Garmin). Требование наследуется m13: мультитаргет- сборка по моделям/разрешениям (vision 1.1, Ф-2.8).
Примечания по ходу работ
-
Волна 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-интерфейс (мультичасовая инфраструктура). Версии документов не поднимались (ближайшее ревью). -
Исследование канала (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). -
Инструкция включения трансляции (для приёмки, по руководству Garmin): на часах — виджет пульса → удержание MENU → «Heart Rate Options» → «Broadcast During Activity» = Вкл (или разовый «Broadcast Heart Rate» перед тренировкой).
-
BLE-адрес часов при «Broadcast Heart Rate» виден как обычный пульс-датчик; в зале возможны чужие HRM — выбор устройства в «Настройках» обязателен (не авто-first-found).
-
Состав реализации (
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).
- ReconnectPolicy 1/5/30 c + BleProfiles — только стандартные
UUID),
-
Сборка/тесты: BUILD SUCCESSFUL; unit 89/89 (m12 +26: парсер 7, буфер 6, фасад 9 — включая ReconnectPolicy, формат 4); APK 57,3 МБ (рост на ~0 — доминируют JNI Vosk из m11).
-
Уточнения по ходу (спеки не меняют наблюдаемое поведение): NO_PERMISSION вычисляет фасад, в TransportStatus не входит (спека 01 ставила его в транспорт); добавлен
WatchChannel.dispose()— полная остановка для тестов (в проде канал живёт с контейнером). -
Не проверено в среде (нет телефона/часов в контуре агента): живой BLE-приём с Fenix 6X, батарея через Battery Service, реальный скан, runtime-разрешения — критерии 2–4 приёмки на стороне владельца; логика покрыта фейками и байтовыми фикстурами.