188 lines
18 KiB
Markdown
188 lines
18 KiB
Markdown
# m13-fenix-data-field — план работ (d-dev)
|
||
|
||
> Модуль: FenixDataField — поле данных Connect IQ (Monkey C)
|
||
> «Феникс» в штатном профиле «Силовая»: пульс + зона, метрики подхода
|
||
> (Lap→Lap — errata Ф-2.3, см. [module_reqs.md](module_reqs.md)
|
||
> разд. 1.1/5), автономный таймер отдыха с вибрацией. Мультитаргет:
|
||
> якорь fenix6x 280×280, добавление моделей — строкой таргета.
|
||
> Алгоритм: `std1\_rules\d\d-dev.md` (адаптация: домен/функции/БД →
|
||
> единицы Monkey C, гейты те же).
|
||
> Зависимости: m12 (DONE — канал телефона не затрагивается; errata
|
||
> Ф-2.4 об автономном таймере — исполнение здесь). Сервер/телефон —
|
||
> без изменений.
|
||
|
||
| Шаг | Что делаем | Статус |
|
||
|---|---|---|
|
||
| 0 | Модульные документы: module_reqs.md / readme.md / SKILL.md | ✅ 2026-10-19 |
|
||
| 1 | Список единиц (манифест/ядро×3/экран/вибрации/настройки/тесты/доставка) | ✅ 2026-10-19 |
|
||
| 1.3 | **Пользователь!** утверждение списка (+ errata Ф-2.3, toolchain, настройки-на-устройстве, таргеты) | ✅ 2026-10-19 (все 5 решений утверждены) |
|
||
| 2 | Спецификации: api_specs (манифест/таргеты, ядро×3, экран, настройки/вибрации) | ✅ 2026-10-19 (01-проект/манифест/сборка, 02-ядро SetTracker/RestTimer/ZoneCalc, 03-экран, 04-настройки/вибрации) |
|
||
| 2.4 | **Пользователь!** утверждение спецификаций | ✅ 2026-10-19 (утверждены 4 api_specs) |
|
||
| 3 | Реализация + тесты + сборка таргетов + симулятор | ✅ 2026-10-19 (сборки 3 таргетов; тесты 54/54; смок-в-симуляторе ограничен средой — примечания 5–6) |
|
||
| 3.4 | **Пользователь!** утверждение реализации (закрытие модуля) | ✅ 2026-10-19 («заккомить» — утверждение и команда коммита) |
|
||
| 4 | Завершение: план, примечания, волна errata НД | ✅ 2026-10-19 (errata внесена после гейта 1.3 — примечание 2; план/примечания обновлены) |
|
||
|
||
Статусы: ⬜ не начат · 🔄 в работе · ✅ готов.
|
||
|
||
## Список единиц (шаг 1)
|
||
|
||
| # | Единица | Что делает |
|
||
|---|---|---|
|
||
| 1 | Проект + манифест + таргеты | `3-app\5-watch\FenixDataField`: тип datafield, UUID «Феникс», minApiVersion ≤ 2.1.0; monkey.jungle — матрица таргетов (якорь fenix6x), новая модель — строка |
|
||
| 2 | `SetTracker` | метрики подхода: время с границы (Lap), средний пульс интервала (сэмплы из `compute`), обнуление по `onTimerLap`; null-пульс пропускается |
|
||
| 3 | `RestTimer` | автономный отдых: старт по Lap, длительность `restSec` (умолчание 120 = `Rest.DefaultSec`), обратный отсчёт, конец → однократная лёгкая вибрация; пауза активности останавливает |
|
||
| 4 | `ZoneCalc` | зоны 50/70/85 % от `maxBpm`: `hrMaxBpm` или Танака 208−0,7×`age`; цвет MIP-палитры + текстовая подпись (те же названия, что на «Профиле» m10) |
|
||
| 5 | Экран поля | параметрический макет семейства квадратных MIP (якоря, dc-размеры): пульс крупно (`FONT_NUMBER_*`), плашка зоны цветом + подпись, строка «ПОДХОД мм:сс · ср N» / «ОТДЫХ мм:сс», прочерк без пульса |
|
||
| 6 | Вибрации | `Attention.vibrate` + `VibeProfile`: конец отдыха — однократная лёгкая; красная зона — двойная, гистерезис (повтор ≥ 60 с, сброс ниже жёлтой) |
|
||
| 7 | Настройки поля | properties: `restSec` (30–600, умолчание 120), `hrMaxBpm` (0–250, 0 = формула), `age` (14–100, 45); подписи ru/en; умолчания работают без магазина |
|
||
| 8 | Тесты Toybox.Test | ZoneCalc / RestTimer / SetTracker: asserts (компилируются out в release), прогон инструментарием SDK |
|
||
| 9 | Сборка и доставка | `monkeyc` по всем таргетам; developer-ключ; инструкция sideload (developer mode + GCM) и приёмки на Fenix 6X — в этом readme |
|
||
|
||
## Решения на гейт 1.3 (для владельца)
|
||
|
||
1. **Errata Ф-2.3 / Ф-2.1 — границы подхода.** «Штатного события
|
||
Strength Set» в Connect IQ API нет (проверено по актуальному API:
|
||
классов повторений не существует ни в одной версии платформы —
|
||
родная детекция повторений «Силовой» стороннему коду недоступна).
|
||
Рекомендация: границы подхода = между нажатиями **Lap** (штатная
|
||
семантика «Силовой»: Lap — конец подхода, метки в `.fit`
|
||
сохраняются, КУ-2 жив). «Время подхода» — интервал с последнего
|
||
Lap (включает предшествующий отдых; точно «под нагрузкой» не
|
||
измерить), «средний пульс подхода» — среднее за интервал.
|
||
Отвергнутая альтернатива: эвристика «начало подхода = рост
|
||
пульса» — ненадёжна (зал, интервалы отдыха разные). Пакет errata
|
||
НД — module_reqs разд. 5, вносится после «утверждаю».
|
||
2. **Toolchain.** Агент устанавливает Connect IQ SDK для Windows
|
||
(актуальная 9.2; SDK Manager — zip с developer.garmin.com) в
|
||
`C:\tools` по прецеденту m09 (JDK 21 уже стоит). Сборка — CLI
|
||
`monkeyc`; смок — симулятор SDK; приёмка — реальный Fenix 6X
|
||
(ui-ux: симулятор только для разработки).
|
||
3. **Настройки поля на устройстве.** Стандартные properties поля
|
||
(`restSec`, `hrMaxBpm`, `age`) на реальном устройстве
|
||
редактируются только через Garmin Connect Mobile/Express **для
|
||
приложений из магазина Connect IQ**; sideload-дев-сборка живёт на
|
||
зашитых умолчаниях (120 с и т.д.). Смена поведения в v1 =
|
||
пересборка агентом. Опция владельца: публикация поля приватной
|
||
бетой в магазине (бесплатный Garmin-аккаунт разработчика) — даёт
|
||
правку настроек в GCM; в границы v1 не входит.
|
||
4. **Таргетная матрица v1.** Собираем только `fenix6x` (якорь
|
||
владельца); макет параметрический на семейство квадратных MIP —
|
||
fenix 6 (260×260) / 6S (240×240) добавляются строкой таргета без
|
||
правок кода (tech-req ОВ-7 «по фактическому парку», указание
|
||
2026-10-19 о лёгкой сборке под другие часы).
|
||
5. **Красная зона на часах — процентная** (Ф-2.2: > 85 % от
|
||
`maxBpm` поля), а не абсолютный `Safety.HrStrengthMax` = 120
|
||
телефона: часам абсолютный порог неизвестен (передать нельзя —
|
||
канал односторонний), по НД у часов процентная норма. Зоны
|
||
настраиваются вручную в поле (`hrMaxBpm`/`age`), синхронизация с
|
||
профилем телефона — открытый вопрос 3 (v2).
|
||
|
||
## Примечания по ходу работ
|
||
|
||
0. **Исследование API (2026-10-19, веб)** — обоснование errata
|
||
Ф-2.3, см. module_reqs разд. 1.1: `Toybox.Activity` — 6 классов,
|
||
повторений нет нигде (вплоть до актуального API платформы);
|
||
у DataField есть `onTimerLap()` (API 1.3.0), `compute()` раз/с
|
||
(`currentHeartRate` с 1.0.0), `Attention.vibrate` (1.0.0);
|
||
`Toybox.Test` — с API 2.1.0, asserts компилируются out в release.
|
||
SDK: актуальная 9.2, Windows SDK Manager
|
||
`developer.garmin.com/downloads/connect-iq/sdk-manager/connectiq-sdk-manager-windows.zip`.
|
||
1. Расположение проекта — `3-app\5-watch\FenixDataField` (продолжение
|
||
нумерации 1-lib/2-cli/3-web/4-phone); «5-watch» — место будущих
|
||
полей/приложений под другие часы (мультичасовая инфраструктура).
|
||
2. **Волна errata НД внесена (2026-10-19, после «утверждаю» гейта
|
||
1.3):** requirements Ф-2.1 («время с последнего Lap — точное
|
||
„под нагрузкой“ не измеряется») и Ф-2.3 (границы подхода =
|
||
Lap→Lap, события Strength Set в CIQ API нет); Ф-2.2 — правка не
|
||
потребовалась (норма уже содержит «настройки поля», открытый
|
||
вопрос 3); tech-req 7 п. 1 — дополнено «вибрация конца отдыха и
|
||
метрики Lap→Lap — приёмка m13». Версии документов не поднимались
|
||
(ближайшее ревью). UUID поля зафиксирован в спеке 01:
|
||
`34f470ff-b5d1-4141-ad06-c483fa79a1f8`.
|
||
3. **Тулчейн фактически (2026-10-19).** Рабочая связка: **SDK 4.2.2**
|
||
(Wayback-архив zip; актуальная 9.2 скачана, но её симулятор
|
||
отвергает dev-подписи нашего пака — примечание 7) + JDK 21 +
|
||
девайс-паки fenix6xpro/fenix6/fenix6s из docker-образа
|
||
`piccobit/docker-connectiq:4.1.7` → `%APPDATA%\Garmin\ConnectIQ\Devices`
|
||
(путь виндового стора найден декомпиляцией Monkeybrains).
|
||
Идентификатор таргета — **fenix6xpro** (6X существует только в
|
||
Pro-линии). Ключ разработчика: **RSA 4096**, PKCS#8 DER
|
||
(`C:\Users\virus-home\.watchbody\ciq\developer_key.der`; PEM и
|
||
2048-битные отвергаются — см. примечание 4).
|
||
4. **Developer-ключ обязан быть RSA 4096.** Симуляционная прошивка
|
||
отвергала 2048-битную подпись «Signature check failed» (проверено
|
||
на штатном сэмпле Garmin — падает одинаково; JDK ни при чём:
|
||
сборка под Java 11 в docker даёт то же). В `DeveloperSignature`
|
||
(monkeybrains.jar) модуль = 512 байт = 4096 бит. Ключ
|
||
перегенерирован 2026-10-19; старый 2048 сохранён как
|
||
`developer_key2048.obsolete.der` — подписи им больше не собираются.
|
||
5. **Тесты: собственный раннер вместо `monkeydo /t`.** Штатный
|
||
(:test)-harness Toybox.Test не стартует в связке «симулятор 4.2.2 +
|
||
девайс-пак docker»: ЛЮБОЙ тестовый prg (включая минимальный проект
|
||
из 15 строк) падает на старте «Unexpected Type Error: Failed to
|
||
start CIQ Application»; поле данных, к тому же, нельзя запускать
|
||
как приложение. Решение: `test-source/` — watch-app-вход
|
||
`TestEntry` (test-manifest.xml, тип watch-app) + `CoreChecks`
|
||
(счётчик/печать) + те же 13 тестовых функций из спеки 02;
|
||
тестовая сборка `monkey-test.jungle` (без --unit-test), прогон —
|
||
`monkeydo` без /t, вывод println → консоль. Итог: **54/54
|
||
проверок** (зоны 12, метрики подхода 6, отдых 10, вибрации 7,
|
||
формат 10, клампы 9 — состав уточнён против спеки 02 числом
|
||
проверок, случаи те же). Раннер отработал как положено: нашёл
|
||
3 реальных бага ядра (шкала порогов ZoneCalc ×10, лишнее слагаемое
|
||
в округлении avgBpm, сборка строки mmss не со строки) — исправлены,
|
||
повторный прогон зелёный. Поле данных (release, monkey.jungle) в
|
||
тестовые сборки не входит. Хвостовой «app crash» после RESULT —
|
||
штатный выход watch-app без view, на результаты не влияет.
|
||
6. **Смок поля в симуляторе — ограничение среды.** Симулятор 4.2.2 не
|
||
хостит поля данных в симулируемой активности (поле нельзя
|
||
«открыть» как приложение), поэтому интерактивные проверки спеки 03
|
||
§5 (проводка пульса по зонам, Lap, пауза, вибрации глазами) —
|
||
приёмка владельца на реальном Fenix 6X (прецедент m12: «на
|
||
устройстве — приёмка владельца»). Поведенческое ядро этих проверок
|
||
закрыто юнит-тестами: пороги всех 4 зон (границы 89/90, 125/126,
|
||
152/153 при max 180), прочерк без пульса (ZONE_NONE), обнуление по
|
||
Lap + отсчёт отдыха + однократность сигнала, гистерезис красной
|
||
60 с, целочисленный mmss. Отрисовка — параметрическая, собирается
|
||
под 280×280/260×260/240×240 без правок кода.
|
||
7. **Мультитаргет-инвариант подтверждён:** fenix6 (260×260) и fenix6s
|
||
(240×240) — release и тестовые prg собираются только заменой
|
||
`<iq:product>` в манифесте (правки кода — нулевые; проверочные
|
||
манифесты во временной папке, в репо не попадают). Симулятор 9.2 в
|
||
контуре не используется (отвергает подписи нашего пака); если
|
||
понадобится — отдельное исследование (sdk-settings.json симулятора).
|
||
8. **Monkey C-ловушки, пойманные тестами:** `==` для строк — по ссылке
|
||
(литерал ≠ собранная строка; сверять `Lang.String.equals`);
|
||
`число + строка` не склеивается — конкатенация только со строки
|
||
(`"" + x + …`); дефолтная джунгла компилирует `.\**.mc` рекурсивно
|
||
— джунглы проекта обязаны задавать `base.sourcePath` явно (иначе
|
||
test-source попадает в release).
|
||
9. **Сборка/прогон (команды).** Сборка: `monkeyc.bat -d fenix6xpro -w
|
||
-f monkey.jungle -y <ключ> -o bin\FenixDataField.prg` (тест —
|
||
`-f monkey-test.jungle … FenixDataField-test.prg`; JAVA_HOME =
|
||
JDK 21). Симулятор: `bin\connectiq.bat` (запуск вне песочницы),
|
||
затем `monkeydo.bat <prg> fenix6xpro` (без /t) — юнит-вывод в
|
||
консоль; лог прошивки —
|
||
`%TEMP%\com.garmin.connectiq\GARMIN\APPS\LOGS\CIQ_LOG.YML`.
|
||
|
||
## Sideload на Fenix 6X (приёмка владельца)
|
||
|
||
1. На часах включить режим разработчика: **Settings → System →
|
||
Developer Mode → On** (перезагрузка по запросу).
|
||
2. Подключить часы по USB (режим MTP), открыть диск **GARMIN**.
|
||
3. Скопировать `FenixDataField.prg` в **`GARMIN\APPS\DataField\`**
|
||
(если папки нет — создать; вариант совместимости —
|
||
`GARMIN\APPS\WatchApp\`).
|
||
4. Отсоединить USB. Профиль **Strength (Силовая)** → настройки
|
||
тренировки → Data Screens → страница → Data Field 1 → **Феникс**
|
||
(Phoenix).
|
||
5. Проверки: пульс крупно + плашка зоны с подписью; Lap (кнопка
|
||
BACK/LAP на 6X) — строка «ОТДЫХ 2:00» тикает вниз, по нулю —
|
||
короткая вибрация; пульс > 85 % maxBpm (умолчание Танака 45 лет =
|
||
176) — двойная вибрация не чаще раза в минуту; пауза активности —
|
||
отсчёты стоят; без нагрудного датчика/оптического пульса — «—»,
|
||
поле не падает. Настройки `restSec`/`hrMaxBpm`/`age` в dev-сборке —
|
||
зашитые умолчания (решение 3 гейта 1.3).
|
||
6. Удаление: часы → Settings → System → Storage → Connect IQ /
|
||
`GARMIN\APPS\DataField\FenixDataField.prg` вручную.
|