VEU.WathBody/1-docs/4-modules/m06-fit-import/api_specs/01-facts-schema.md

3.2 KiB
Raw Permalink Blame History

Спецификация 01: FitParser — канонический факт workout-facts/1

Источники: tech-req 2.3, Ф-8.2, Ф-13.4. SDK — Garmin.FIT.Sdk (NuGet 21.218.0), namespace Dynastream.Fit, событийный Decode.MesgEvent.

1. Сигнатура

public sealed record FitFacts(
    string FactsJson,       // workout-facts/1, разд. 2
    DateTime StartedAt,     // session start (UTC)
    int? AvgHr,             // среднее records.hr (округление AwayFromZero)
    int? HrRecoveryS,       // разд. 3
    string FitFileId,       // file_idmesg.time_created (UTC ISO) + серийник
    string Device);         // device_infomesg (продукт/версия) или ""

Parse(string path) — файл читается один раз (потоково, tech-req 5); Decode.CheckIntegrity перед разбором; кривой файл → FitException (обрабатывает ImportService — spec 04).

2. Схема JSON

{"schema": "workout-facts/1",
 "session": {"type": "strength_training", "start": "2026-10-18T18:00:00Z",
             "duration_s": 3600},
 "laps": [{"index": 0, "start": "...", "duration_s": 120}],
 "records": [{"t_offset_s": 0, "hr": 95}],
 "sets": [{"index": 0, "reps": 12, "weight_kg": 100.0}],
 "fit_file_id": "...", "device": "..."}
Сообщение Что берём
FileIdMesg TimeCreated (UTC) + SerialNumber → fit_file_id ("{serial}-{yyyyMMddTHHmmssZ}"); оба пусты → имя файла
SessionMesg Sport/SubSport (строкой), StartTime, TotalTimerTime → duration_s (сек, округление)
LapMesg index по порядку, StartTime, TotalTimerTime
RecordMesg Timestamp → t_offset_s (от session start), HeartRate (есть и > 0)
SetMesg index по порядку, NumReps, Weight (кг; 0xFFFF/0 — нет)
DeviceInfoMesg последний (свежий) Product/GarminProduct → device (строкой)
  • Session нет → FitException (файл не активность).
  • sets/laps/records пустые → пустые массивы (не null).
  • Тип сессии — как есть строкой (strength_training, cardio, …); фильтра по типу импорт не делает (решает сопоставление).

3. hr_recovery_s (Ф-13.4)

Конец последнего Lap (start + duration) → первый Record строго после него с hr < 90 (порог Safety.RestingHrSitting — конфиг, Р3-А): hr_recovery_s = t_offset(record) − конец последнего lap в offset. Нет записей после последнего Lap или не опустился → null.

4. Тест-фикстуры

Генерируются в тестах энкодером SDK (Encode, Mesg-объекты в прямом порядке FileId → DeviceInfo → Record* → Lap* → Set* → Session): полная активность (3 рекорда до/1 после Lap с hr < 90, Set с reps/weight), активность без Set, битый файл (усечённый байтовый массив).