From 2b3ce406734bd5cd696146e84401470d05039c8d Mon Sep 17 00:00:00 2001 From: virus-home Date: Sat, 17 Oct 2026 18:54:37 +0500 Subject: [PATCH] =?UTF-8?q?=D0=A8=D0=B0=D0=B3=204=20=D1=83=D1=82=D0=B2?= =?UTF-8?q?=D0=B5=D1=80=D0=B6=D0=B4=D1=91=D0=BD;=20=D1=88=D0=B0=D0=B3=205:?= =?UTF-8?q?=20tech-requirements.md=20(=D0=B7=D0=B0=D0=BA=D1=80=D1=8B=D1=82?= =?UTF-8?q?=D0=B8=D0=B5=20=D0=9E=D0=92,=20=D1=84=D0=BE=D1=80=D0=BC=D0=B0?= =?UTF-8?q?=D1=82=D1=8B=20=D0=BE=D0=B1=D0=BC=D0=B5=D0=BD=D0=B0,=20=D0=BA?= =?UTF-8?q?=D0=BE=D0=BD=D1=84=D0=B8=D0=B3=D1=83=D1=80=D0=B0=D1=86=D0=B8?= =?UTF-8?q?=D1=8F)=20+=20=D1=81=D0=BF=D0=B5=D1=86=D0=B8=D1=84=D0=B8=D0=BA?= =?UTF-8?q?=D0=B0=D1=86=D0=B8=D1=8F=20Kit.Helpers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 1-docs/0-mng/plan.md | 4 +- 1-docs/2-arch/architecture.md | 2 +- 1-docs/3-tech/kit-helpers-spec.md | 39 ++++++++ 1-docs/3-tech/tech-requirements.md | 137 +++++++++++++++++++++++++++++ 4 files changed, 179 insertions(+), 3 deletions(-) create mode 100644 1-docs/3-tech/kit-helpers-spec.md create mode 100644 1-docs/3-tech/tech-requirements.md diff --git a/1-docs/0-mng/plan.md b/1-docs/0-mng/plan.md index b7eeb1b..c61955c 100644 --- a/1-docs/0-mng/plan.md +++ b/1-docs/0-mng/plan.md @@ -12,8 +12,8 @@ | 1. Инициация разработки | `1-docs\1-reqs\vision.md` (утверждён 2026-10-17, v1.0), `1-docs\0-mng\plan.md` | ✅ | | 2. Создание рабочей области | `readme.md` системы, структура репозитория, `.gitignore` | ✅ | | 3. Разработка функциональных требований | `1-docs\1-reqs\requirements.md` (утверждён 2026-10-17) | ✅ | -| 4. Разработка архитектуры | `1-docs\2-arch\architecture.md`, `hld.puml` (+ рендер) | 🔄 | -| 5. Разработка технических требований | `1-docs\3-tech\tech-requirements.md`, спецификации библиотек | ⬜ | +| 4. Разработка архитектуры | `1-docs\2-arch\architecture.md`, `hld.puml` (+ рендер) (утверждена 2026-10-17) | ✅ | +| 5. Разработка технических требований | `1-docs\3-tech\tech-requirements.md`, спецификации библиотек | 🔄 | | 6. Проектирование баз данных | решение о составе данных (фиксируется здесь) | ⬜ | | 7. Итерационная разработка | `3-app\` (код), `1-docs\4-modules\` (документы модулей) | ⬜ | diff --git a/1-docs/2-arch/architecture.md b/1-docs/2-arch/architecture.md index d47a11d..c6c9d16 100644 --- a/1-docs/2-arch/architecture.md +++ b/1-docs/2-arch/architecture.md @@ -6,7 +6,7 @@ > дизайн LLM-планирования (разд. 8), документ форматов. > Схема компонентов: [hld.puml](hld.puml) (+ рендер [hld.svg](hld.svg), > [hld.png](hld.png)). -> Версия: черновик 1 (2026-10-17). +> Версия 1.0 — **УТВЕРЖДЕНО** (2026-10-17). ## 1. Компоненты и размещение diff --git a/1-docs/3-tech/kit-helpers-spec.md b/1-docs/3-tech/kit-helpers-spec.md new file mode 100644 index 0000000..ff29660 --- /dev/null +++ b/1-docs/3-tech/kit-helpers-spec.md @@ -0,0 +1,39 @@ +# Спецификация потребления: Kit.Helpers + +> Спецификация разделяемой библиотеки в объёме потребления системы +> VEU.WathBody (шаг 5 процесса). Библиотека развивается вне проекта +> (`3-app\1-lib\Kit.Helpers`, net10.0, Npgsql 10) — здесь фиксируется +> только контракт использования; исходники не изменяются. + +## Потребляемые компоненты (v1) + +| Компонент | Использует | Назначение в VEU.WathBody | +|---|---|---| +| `Db.Postgres.SqlServerHelper` | `FitParser`, `MetricsEngine`, `ProgramStore`, `RestApi`, `AdminWeb` | Единственный путь к PostgreSQL: выполнение хранимых функций слоя `src`/`ref` (шаг 6) | +| `SqlServerHelperFluent` | те же | Fluent-обёртка вызовов функций | +| `Log.Logger` | все серверные компоненты | Файловые логи; `Log.Path`, уровень из конфигурации | +| `JsonHelper` | `ProgramStore`, `FitParser` | Сериализация/десериализация версий программ и фактов (`facts_json`) | + +## Контракты + +- **БД:** соединение — из `Db.ConnectionString`; каждый вызов функции БД — + короткое соединение (без долгоживущих транзакций); ошибки — исключения + библиотеки, оборачиваются компонентом в лог + код ответа API. +- **Логи:** уровень из конфигурации; в лог не пишутся ключи, пароли + и хэши (инвариант «Тайны», requirements раздел 5); ротация — средствами + Logger по дням. +- **JSON:** `JsonHelper` — единственная точка сериализации программ + (стабильный порядок полей — диффы версий сравнимы). + +## Не потребляется в v1 + +`Cache` (нет кэшируемых справочников большого объёма), `Http.*` +(Fetcher реализует собственный клиент Garmin — FluentHttpClient может +быть подключён в итерации 2 после прототипа), `Session`, `HttpContext`. + +## Версии и обновления + +Фиксируется прямой ссылкой на проект `3-app\1-lib\Kit.Helpers` (monorepo); +обновления библиотеки — только после прогона тестов сервера +(`c-tst`); Breaking-изменения API библиотеки — повод пересмотреть +настоящую спецификацию. diff --git a/1-docs/3-tech/tech-requirements.md b/1-docs/3-tech/tech-requirements.md new file mode 100644 index 0000000..df8f481 --- /dev/null +++ b/1-docs/3-tech/tech-requirements.md @@ -0,0 +1,137 @@ +# Технические требования: VEU.WathBody («Феникс 6X») v1 + +> Шаг 5 процесса разработки (`std1\_process\process.md`). +> Источники: [vision.md](../1-reqs/vision.md) v1.2, +> [requirements.md](../1-reqs/requirements.md) (утверждён, раздел 7 — +> открытые вопросы), [architecture.md](../2-arch/architecture.md) +> (утверждена), дизайн LLM-планирования, документ форматов. +> Версия: черновик 1 (2026-10-17). + +## 1. Решения открытых вопросов (закрытие раздела 7 requirements) + +| # ОВ | Решение | Обоснование | +|---|---|---| +| 1. Максимальная ЧСС | Формула Танаки: `HRmax = 208 − 0,7 × возраст`; ручной ввод `Hr.MaxBpm` перекрывает расчёт | Точнее для 45+ (профиль пользователя); ручной ввод — окончательная истина | +| 2. Офлайн-STT | **Vosk** (малая русская модель ~50 МБ) + узкая грамматика команд (Ф-1.2: числа и слова «подход/вес/отдых/секунд»); TTS-подтверждения — системный Android TTS | Работа без сети, малый словарь → устойчивость к шуму; sherpa-onnx — запасная кандидатура, если точность < КУ-1 на контрольной тренировке | +| 3. Канал телефон ↔ часы | Connect IQ **Communications SDK** (companion-канал через приложение Garmin Connect Mobile на телефоне). Передаётся: текущий пульс (≈1 Гц), зона, таймер отдыха (секунды до конца), заряд часов. Ограничение: на телефоне должно быть установлено и залогинено Garmin Connect Mobile; канал может не доставлять — обе стороны деградируют молча (Ф-2.4, Ф-3.2) | Единственный штатный канал сторонних приложений Garmin; проверка объёма передачи — прототипом в итерации 1 шага 7 (критерий: пульс и таймер доходят при экране часов активным) | +| 4. Расписание fetch | Внешний планировщик ОС: systemd timer (Linux-сервер) / Планировщик задач (Windows) вызывает `--mode fetch`; внутри сервера планировщика нет | Проще, надёжнее, не дублирует средства ОС | +| 5. Формула 1ПМ | **Эпли**: `1ПМ = w × (1 + reps/30)` | Стандарт индустрии; при reps ≤ 10 различие с Бжицки < 2 % | +| 6. UI телефона | **Jetpack Compose** + Material 3 (тёмная тема по умолчанию) | Актуальный стек Kotlin; крупные зоны касания — Min touch target 48 dp (Ф-3.6) | +| 7. Модели часов v1 | Таргет `fenix6` (Fenix 6/6S/6X, включая Pro/Sapphire — одно разрешение 260×260); добавление моделей — новыми таргетами по фактическому парку (шаг 7) | Якорное устройство ТЗ; 6S/6X отличаются размером корпуса, не экраном | +| 8. Сопоставление телефон ↔ .fit | Окно ± 15 минут по времени начала (Ф-8.4); при нескольких кандидатах — ближайший по времени | ✅ закрыт в requirements | + +## 2. Форматы обмена + +### 2.1. Конверт синхронизации (телефон → сервер) + +`POST /api/v1/sync`, JSON-массив записей (пакет ≤ 50), каждая: + +```json +{ + "client_guid": "uuid-v4", + "entity": "workout | exercise | program_version | profile | workout_set | set_note", + "op": "upsert", + "data": { ...схема сущности... } +} +``` + +Ответ: `{ "accepted": ["guid1", ...] }` (все или ничего — пакет +атомарен; 409 при конфликте версии программы — перезапись на телефоне +после перечитывания). Повтор пакета безопасен: сервер отвечает тем же +`accepted` по уже принятым GUID (идемпотентность, Ф-7.4). + +### 2.2. Сущности `data` (ключевые поля) + +- `exercise`: `exercise_id` (uuid), `name`, `group` (push|pull|legs|core|…), + `is_base` (bool), `is_active` (bool); +- `workout`: `workout_id` (uuid), `started_at`/`ended_at` (UTC ISO 8601), + `status` (active|finished|cancelled), `program_version_id` (nullable), + `avg_hr`; +- `workout_set`: `workout_id`, `exercise_id`, `seq`, `weight_kg`, `reps`, + `done_at`, `source` (voice|watch|edit), `in_plan` (bool); +- `profile`: `sex`, `birth_year`, `weight_kg`, `hr_max` (nullable); +- `program_version`: JSON-схема программы из дизайна LLM-планирования + (разд. 2) + `athlete_id` (выводится из ключа, в теле не передаётся). + +### 2.3. Канонический JSON тренировки (сервер, из .fit) + +Из `FitParser` → `workout_facts.facts_json`: `session` (тип, старт, +длительность), `laps[]` (индекс, старт, длительность), `records[]` +(`t_offset_s`, `hr`), `sets[]` (упражнение/повторы/вес — если писались), +`fit_file_id`, `device`. Схема версионная: `"schema": "workout-facts/1"`. + +### 2.4. CSV-выгрузка (AdminWeb) + +Разделитель — `;`, кодировка UTF-8 c BOM (Excel), заголовок первой +строкой: `athlete;date;duration_min;avg_hr;sets;exercises;adherence_pct`. +Строка — тренировка; выборка = текущий фильтр таблицы. + +## 3. Спецификация потребления Kit.Helpers + +Библиотека — `3-app\1-lib\Kit.Helpers` (std1, net10.0, Npgsql 10); +потребление без изменений исходников (полная спецификация — +[kit-helpers-spec.md](kit-helpers-spec.md)): + +- `Db.Postgres.SqlServerHelper` — доступ к PostgreSQL (единственный + путь к БД из кода сервера; строка подключения — `Db.ConnectionString`); +- `Log.Logger` — файловые логи сервера (`Log.Path`, уровень `Info`); +- `JsonHelper` — сериализация фактов/программ; +- остальное (Cache, Http, Session) в v1 не потребляется. + +## 4. Схема конфигурации и правила валидации + +### 4.1. Телефон (Preferences) + +| Параметр | Тип | Валидация | +|---|---|---| +| `Server.BaseUrl` | string | абсолютный https:// URL — иначе экран настроек не даёт сохранить | +| `Server.ApiKey` | string | непустой | +| `Sync.WifiOnly` | bool | — | +| `Hr.MaxBpm` | int? | 120–240 или пусто (расчёт по Танаке) | +| `Rest.DefaultSec` | int | 30–600 | + +### 4.2. Сервер (`appsettings.json`) + +| Параметр | Тип | Валидация (startup, `--mode *`) | +|---|---|---| +| `Api.Key` | string | непустой; уникальность среди `Api.Athletes[].apiKey` | +| `Api.Athletes` | list | `{name}` непусто, `{apiKey}` непустой; дубликаты ключей — фатально | +| `Admin.Login / Admin.PasswordHash` | string | непустые; хэш — PBKDF2 (100k итераций), префикс `pbkdf2$` | +| `Db.ConnectionString` | string | тест подключения при старте — фатально при неудаче | +| `Garmin.Login/Password` | string | обязательны для `--mode fetch` (иначе код возврата 1) | +| `Garmin.DownloadDir / Import.Dir / Import.ArchiveDir` | path | создание при старте; недоступность — фатально | +| `Progression.BaseStepKg` | double | 0,5–10 | +| `Progression.BackoffPercent` | int | 1–20 | +| `Deload.MaxWeeks` | int | 3–12 | +| `Log.Path / Log.Level` | — | уровень: Debug/Info/Error | + +Валидация конфигурации выполняется при старте любого режима; ошибки — +в лог и stderr, код возврата 1 (раздел 2.3 requirements). + +## 5. Технические решения по компонентам + +- **`FitFetcher`**: реализация логина Garmin Connect (SSO-последовательность) + на C# — по контракту семьи библиотек `garminconnect` (подписи запросов, + OAuth1-заголовок); при блокировке — решение ОВ шага 7 (fallback: папка + импорта уже работает независимо). Пометка прототипа итерации 1. +- **`FitParser`**: официальный **Garmin FIT SDK for C#**; парсинг — + потоковый, файл читается один раз; сбой одного файла не останавливает + пакет (код возврата 2). +- **`SyncQueue`**: таблица Room `sync_pending(guid, entity, payload, + created_at)`; отправка — WorkManager (сетевое ограничение учитывает + `Sync.WifiOnly`); ретрай — экспоненциально, 1 мин → 64 мин. +- **`VoiceInput`**: Vosk-API (Kaldi) как service в процессе приложения; + грамматика — JSON-словарь `[«подход», «вес», «отдых», «на», «секунд», + 0–999]`; числа — постобработка («сто двадцать» → 120). +- **`AdminWeb`**: Razor Pages, сессия по cookie после логина; + пароль — PBKDF2 (см. 4.2); графики — Chart.js (локальный ассет, + без CDN). +- **HTTPS**: обратный прокси (nginx/Caddy) терминирует TLS; Kestrel — + только localhost — вопрос развёртывания, вне кода. + +## 6. Чек-лист шага 7 (проверки прототипами) + +1. Companion-канал: пульс/таймер доходят на телефон (ОВ-3) — итерация 1. +2. Логин Garmin Connect из C# (FitFetcher) — итерация 2. +3. Vosk: точность команд в шумном зале ≥ КУ-1 — итерация 2. +4. Сопоставление телефон ↔ .fit на реальной тренировке — итерация 3.