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

16 KiB
Raw Permalink Blame History

Требования к модулю: m12-watch-channel

Модуль — WatchChannel телефона Phone.WatchBody (архитектура разд. 1.2): приём live-пульса с часов Fenix 6X по штатной трансляции BLE (Heart Rate Service), статус связи, заряд (если передаётся), подача в экран «Тренировка» (m10), «Допуск» и запись среднего пульса тренировки (avg_hr, цепочка m09). Самодостаточная копия требований (d-dev-6). Источники: Ф-3.1–Ф-3.3 (статусная строка, пульс, таймер), Ф-13.2 (пульс сидя «с часов, если связаны»), Ф-2.4 (вибрация отдыха — errata, см. разд. 1.1), ОВ-3 (tech-req 3 п. 3, прототип 7 п. 1), ui-ux разд. 2.

1. Назначение и границы

Live-пульс на телефоне во время тренировки: часы Fenix 6X транслируют пульс штатной функцией «Broadcast Heart Rate», телефон принимает и показывает; отсутствие связи ничего не ломает (🛡 Ф-2.4, Ф-3.2 — деградация молча, прочерки).

1.1. Транспорт: обоснование (errata ОВ-3)

ОВ-3 (tech-req 3 п. 3) предполагал companion-канал Connect IQ Communications. Исследование 2026-10-19 показало, что на якорных часах он нереализуем для живого пульса:

  1. Поля данных не имеют Toybox.Communications до API 5.0.0, а Fenix 6X навсегда остался на CIQ API ≤ 4.1.7 — поле данных «Силовая» (Ф-2, m13) не может ни передавать, ни принимать.
  2. CIQ-приложение (Communications доступен) не может работать одновременно со штатной активностью «Силовая» — живой пульс во время тренировки из приложения не транслировать; companion SDK (com.garmin.connectiq:ciq-companion-app-sdk) для v1 неприменим.
  3. Штатная трансляция «Broadcast Heart Rate» на fenix 6 серии с прошивки июня 2020 идёт одновременно по ANT+ и BLE (стандартный Heart Rate Service 0x180D; подтверждено на Pro-линии — 6/6S/6X Pro; у базовой «шестёрки» 2022 г. сообщали только ANT+). BLE-приём работает на любом Android без Garmin Connect Mobile и без ANT-железа; канал односторонний (часы → телефон).
  4. Следствие для Ф-2.4 (вибрация конца отдыха «по таймеру, переданному с телефона»): передать секунды отдыха на часы в v1 нечем — таймер отдыха на часах делает m13 автономно (длительность — настройка поля данных в Garmin Connect Mobile, умолчание = Rest.DefaultSec телефона). Таймер телефона (Ф-3.3) работает независимо, как сейчас. Errata НД — разд. 5.

Критерий прототипа ОВ-3 (tech-req 7 п. 1 «пульс и таймер доходят при экране часов активным») исполняется в части «пульс доходит» (проверка на устройстве владельца); часть про таймер переносится в m13 как автономная вибрация (errata 7 п. 1).

В границах модуля:

  • транспорт-интерфейс WatchTransport + первая реализация — BLE-клиент HrBleClient (требование владельца при утверждении гейта 1.3, 2026-10-19: инфраструктура должна поддерживать и другие умные часы): скан по UUID Heart Rate Service (0x180D), подключение, подписка на изменения характеристики Measurement (0x2A37, indications), разбор пакета (флаги: разрядность 8/16 бит, поля Energy/RR пропускаются), автопереподключение при потере, чтение Battery Service (0x180F) при его наличии — иначе заряд не показывается (Ф-3.1 «если передаётся»); интерфейс — для unit-тестов на фейках и для будущих транспортов (CIQ 5+, ANT+) без правок фасада и UI; только стандартные профили GATT, платформо-специфичных UUID/имён/логики в коде нет — любые часы, транслирующие стандартный Heart Rate Service, работают без изменений кода; платформо-специчное — только тексты подсказок включения трансляции в «Настройках» (расширяемый справочник по производителям, v1 — Garmin);
  • буфер пульса HrSampleStore (чистый, в памяти): сэмплы (bpm, t_offset_s от старта тренировки), текущее значение, среднее за тренировку, серия «последний подход» (сэмплы от последнего записанного подхода до текущего момента — Ф-3.2 «линия за последний подход»), устаревание (> 10 с без сэмплов — «нет данных», прочерк);
  • фасад WatchChannel: состояние канала — StateFlow со статусом (не выбраны / скан / связь есть / потеряна / Bluetooth выключен / нет разрешения 🛡 текстом), текущим пульсом, зарядом, серией последнего подхода и средним; жизненный цикл: канал активен, пока открыты экраны «Тренировка» или «Допуск» (v1 — без foreground-сервиса; пульс считается за время открытого экрана — ограничение решения 5 гейта 1.3);
  • выбор устройства: в «Настройках» блок «Часы» — скан, список найденных пульс-датчиков (имя + адрес), выбор сохраняется (Watch.HrDeviceAddress в DataStore); не задано — статус «часы не выбраны», канал не сканирует; сброс выбора;
  • экран «Тренировка» (интеграция m10, замена заглушек WorkoutScreen: статус-строка): связь часов — зелёный/красный/ серый, дублируется текстом («часы: связь есть (заряд 80 %)» / «часы: нет связи» / «часы: не выбраны»); пульс — крупное текущее значение, окраска по зонам Танака (ZoneCalculator m10, границы из Hr.MaxBpm), под значением — линия последнего подхода (Canvas, серия буфера); при отсутствии/устаревании — прочерк, голос и ручной ввод работают (Ф-1.1, Ф-3.2);
  • финиш тренировки: WorkoutCore.finishWorkout(avgHr = среднее буфера) — готовая цепочка m09 (workout.avg_hr → sync); канал сам в БД не пишет;
  • экран «Допуск» (Ф-13.2): если канал активен и есть свежий сэмпл (≤ 30 с) — кнопка «взять с часов» подставляет пульс сидя в поле допуска; иначе — ручной ввод пикером (как в m10);
  • разрешения Bluetooth: Android 12+ — BLUETOOTH_SCAN (neverForLocation) + BLUETOOTH_CONNECT (runtime); Android 8–11 — ACCESS_FINE_LOCATION (runtime) + BLUETOOTH/ BLUETOOTH_ADMIN (манифест); отказ в разрешении — статус «нет разрешения», экран не блокируется (🛡);
  • unit-тесты (pure JVM): разбор 0x2A37 (фикстуры байтов: 8/16 бит, с полями Energy/RR), буфер (среднее, серия последнего подхода, устаревание), статусные переходы фасада на фейках клиента.

Вне границ модуля: m13 поле данных (Monkey C — отображение, зоны, автономный таймер отдыха с настройкой длительности; мультитаргет-сборка по моделям — там же), дополнительные реализации WatchTransport — ANT+-приём (плагин Garmin, нужен ANT-транспорт в телефоне) и CIQ companion (часы с CIQ 5+) — кандидаты v2 по мере появления устройств, foreground-фон пульса при погашенном экране (v2), запись live-сэмплов в LocalDb/sync (история пульса — из .fit, m07/m08), пульсограмма всей тренировки на телефоне (Ф-5 — карточка факт из .fit на сервере m08), wake-слова, смена устройства голосом.

2. Решения интерфейса (ui-ux разд. 2, tech-req 6)

  • Статусная строка часов не конкурирует с пульсом: статус — мелкой строкой сверху, пульс — крупным значением (≥ 40 sp) в зоне пульса;
  • статус и доступность связи дублируются текстом (🛡 цвет — только дублирование, не единственный носитель);
  • линия последнего подхода — без сеток и подписей, минимум чернил; значение пульса — цифрой всегда;
  • «взять с часов» на допуске — вторичная кнопка рядом с полем «Пульс сидя», после нажатия значение видно в поле (правка пикером сохраняется);
  • блок «Часы» в «Настройках» — по образцу блока «Распознавание речи» (m11): статус, действие, инструкция текстом («включите Broadcast Heart Rate на часах»).

3. Инварианты (🛡 — нарушать запрещено)

  1. Офлайн-first: приём пульса — локальный BLE, сеть и облако не используются; сервер не участвует (БЦ-3, Ф-1.1).
  2. Деградация молча (Ф-2.4, Ф-3.2): отсутствие часов, потери связи, отказ разрешения, выключенный Bluetooth — только прочерки и статус; тренировка, голос, ручной ввод и sync не блокируются.
  3. Канал не пишет в БД: единственная точка записи — WorkoutCore.finishWorkout(avgHr) (единственный писатель LocalDb — инвариант m09); live-сэмплы никуда не сохраняются.
  4. Часы не зависят от телефона (Ф-2.4 «не зависимость»): телефон ничего не «требует» от часов; трансляцию включает сам атлет на часах (инструкция в «Настройках»).
  5. Тайны: BLE-адрес устройства хранится только в DataStore телефона; в лог/журнал команд адреса не пишутся (З-6).
  6. Платформо-независимость канала (указание владельца 2026-10-19): любые часы, транслирующие стандартный Heart Rate Service, работают без изменений кода; Garmin/fenix-специфика в коде запрещена; новые транспорты подключаются реализацией WatchTransport без правок фасада, UI и БД; то же требование наследуется m13 — мультитаргет-сборка поля данных по моделям (vision 1.1, Ф-2.8).

4. Критерии приёмки модуля

  1. assembleDebug собирается; unit-тесты (все, включая m09–m11) зелёные.
  2. Приёмка на устройстве владельца (Fenix 6X, см. инструкцию в readme): включить «Broadcast Heart Rate» → «Настройки → Часы» → скан → выбрать часы → экран «Тренировка»: статус «связь есть», пульс обновляется ≈1 Гц при активном экране часов, линия последнего подхода рисуется; финиш → avg_hr в записи (История на телефоне, карточка в консоли m08 после sync); «Допуск» → «взять с часов» подставляет пульс.
  3. Отрицательные исходы: часы не выбраны / трансляция выключена / Bluetooth выключен / отказ разрешения — прочерки и статус текстом, приложение работает (ручной смок владельца).
  4. Прототип ОВ-3 (tech-req 7 п. 1) — в части «пульс доходит при экране часов активным»: подтверждается пунктом 2; фиксируется в журнале приёмки владельца; часть «таймер» — errata, m13.

5. Errata НД (по утверждению гейта 1.3, решение 2; ВНЕСЕНА 2026-10-19)

  • requirements Ф-2.4: «вибрация — конец таймера отдыха, переданного с телефона» → «вибрация — конец автономного таймера отдыха поля; длительность — настройка поля в Garmin Connect (умолчание Rest.DefaultSec); передача с телефона — v2 (кандидат при смене часов на CIQ 5+)»;
  • requirements Ф-3.2: источник пульса — «штатная трансляция пульса часов (BLE Heart Rate Service; ОВ-3 закрыт: companion- канал Connect IQ на fenix 6X недоступен для полей данных)»;
  • tech-requirements 3 п. 3 (ОВ-3): описание канала фактически = односторонняя штатная трансляция «Broadcast Heart Rate» (BLE/ANT+), Garmin Connect Mobile для пульса не нужен;
  • tech-requirements 7 п. 1: критерий прототипа — «пульс доходит на телефон при активном экране часов» (таймер — автономный, проверяется в m13);
  • architecture 1.1/1.2/3 (телефон↔часы): канал = BLE-приём штатной трансляции, односторонний.

Волна правок НД вносится после «утверждаю» по гейту 1.3 одним пакетом (без подъёма версий документов — errata в телах разделов, версии поднимутся на ближайшем ревью).