GPS: full native re-import on stop + visible progressive import
- stopRecording: replace the dead _lastNativeCount backfill (the variable is never assigned → guard is always false → missed points were never saved) with a full timestamp-based re-import from the native points file, then recompute stats over the complete set. Fixes losing ~90% of a track when recording with the screen off — the JS import poll is frozen by the WebView while the screen is off, so only the native file had all points. - Poll: draw each imported point via a lightweight onImportPoint and yield a frame every ~20 points so the track visibly grows as points transfer on screen-wake catch-up (restores pre-batch behaviour, kept cheap).
This commit is contained in:
parent
2051a604e1
commit
ec5937ea7e
6 changed files with 42 additions and 14 deletions
|
|
@ -5,7 +5,7 @@
|
|||
## Возможности
|
||||
|
||||
- **Дневник питания** — 24400+ продуктов (Магнит, 5ка, Health-Diet.ru, Ашан, Макдоналдс, KFC, Додо Пицца, KDV, ИОН, Гриднев). 37 нутриентов (витамины, минералы, omega-3/6, вода, клетчатка). Рецепты с термообработкой. Формулы порций (4*30, 300+50).
|
||||
- **GPS трекинг** — запись маршрутов на велосипеде/пешком в фоне (foreground service, точки пишутся при выключенном экране; один файл на сессию, переживает START_STICKY-перезапуск ОС). Оффлайн карты (Leaflet + IndexedDB кэш). Импорт GPX/FTB (FitoTrack). Экспорт GPX. Фильтр altitude outlier'ов и GPS-залипаний. Отбор точек по точности (жёсткий порог по настройке «Мин. точность» — рубит грубые сотовые фиксы ~800м, оставляет слабый но точный GPS; где сигнала нет — честный разрыв). Направление (bearing) каждой точки пишется в БД. Вращение карты по компасу (leaflet-rotate, EMA-сглаживание, gimbal lock компенсация). Ландшафтный режим (CSS rotation + панель справа, 3 режима: off/auto/lock). Dead reckoning при потере GPS (гироскоп + акселерометр). Placeholder до GPS (пиксельарт). Карта проезженных дорог (дедупликация 2м, вело/пешие). Импорт тайлов по регионам (SQLite, роутер LAN).
|
||||
- **GPS трекинг** — запись маршрутов на велосипеде/пешком в фоне (foreground service, точки пишутся при выключенном экране; один файл на сессию, переживает START_STICKY-перезапуск ОС; полный до-импорт нативных точек по timestamp при финализации — без потерь, даже если экран был выключен всю запись, а при пробуждении экрана точки видимо нарастают на карте по мере импорта). Оффлайн карты (Leaflet + IndexedDB кэш). Импорт GPX/FTB (FitoTrack). Экспорт GPX. Фильтр altitude outlier'ов и GPS-залипаний. Отбор точек по точности (жёсткий порог по настройке «Мин. точность» — рубит грубые сотовые фиксы ~800м, оставляет слабый но точный GPS; где сигнала нет — честный разрыв). Направление (bearing) каждой точки пишется в БД. Вращение карты по компасу (leaflet-rotate, EMA-сглаживание, gimbal lock компенсация). Ландшафтный режим (CSS rotation + панель справа, 3 режима: off/auto/lock). Dead reckoning при потере GPS (гироскоп + акселерометр). Placeholder до GPS (пиксельарт). Карта проезженных дорог (дедупликация 2м, вело/пешие). Импорт тайлов по регионам (SQLite, роутер LAN).
|
||||
- **BLE пульсометр** — подключение к FitPro браслетам и стандартным HR датчикам (0x180D). ЧСС, SpO2, давление в реальном времени при записи трека.
|
||||
- **Расчёт ккал** — физическая модель для велосипеда (мощность из gradient + Crr + CdA). MET-таблица для ходьбы/бега и тренировок (приседания 6.0, скакалка 12.3, растяжки 2.3, бассейн 8.0). Профили велосипедов. BMR по Mifflin-St Jeor с коэффициентом активности.
|
||||
- **Тренировки** — приседания (подходы x повторения), скакалка, растяжки, бассейн (дистанция). Автоматический расчёт ккал по MET и весу.
|
||||
|
|
|
|||
|
|
@ -787,18 +787,24 @@ GPS.startRecording = async function(activityType, weightKg) {
|
|||
try {
|
||||
const nativePoints = await GPS._readNativePoints();
|
||||
let newCount = 0;
|
||||
// Suppress per-point UI updates during batch — onBatchUpdate handles it
|
||||
// Keep the heavy per-point path off (onPoint runs computeStats over all
|
||||
// points + DB writes → O(n²) on a big backlog). Draw each imported point
|
||||
// cheaply via onImportPoint so the track visibly GROWS as points transfer
|
||||
// from the native file, and yield a frame every ~20 points so Leaflet
|
||||
// repaints — this makes the screen-wake catch-up visible again.
|
||||
const origOnPoint = GPS.recording.onPoint;
|
||||
GPS.recording.onPoint = null;
|
||||
for (const p of nativePoints) {
|
||||
const pTs = new Date(p.ts).getTime();
|
||||
if (pTs <= GPS.recording._lastProcessedTs) continue;
|
||||
GPS.recording.onPoint = null; // suppress addLatLng per point
|
||||
await GPS._processIncomingPoint(p);
|
||||
GPS.recording._lastProcessedTs = pTs;
|
||||
newCount++;
|
||||
if (GPS.recording.onImportPoint) GPS.recording.onImportPoint(p);
|
||||
if (newCount % 20 === 0) await new Promise(r => requestAnimationFrame(r));
|
||||
}
|
||||
GPS.recording.onPoint = origOnPoint;
|
||||
// Batch UI update — rebuild polyline from sorted points
|
||||
// Final sync — marker, pan, course and stats from the full point set.
|
||||
if (newCount > 0 && GPS.recording.onBatchUpdate) {
|
||||
GPS.recording.onBatchUpdate();
|
||||
}
|
||||
|
|
@ -845,14 +851,21 @@ GPS.stopRecording = async function() {
|
|||
// Stop native service — read remaining points but DON'T delete the file yet
|
||||
let nativePointsFile = null;
|
||||
if (GPS.recording.nativeService) {
|
||||
// Import any remaining points from native service
|
||||
// Import EVERY native point not yet in the DB. The live poll is screen-gated
|
||||
// (WebView freezes timers while the screen is off), so a walk with the screen
|
||||
// off leaves most points only in the native file. Idempotent by timestamp —
|
||||
// same pattern as recoverSession. Then recompute stats over the COMPLETE set.
|
||||
const nativePoints = await GPS._readNativePoints();
|
||||
if (nativePoints.length > GPS.recording._lastNativeCount && GPS.recording.sessionId) {
|
||||
for (let i = GPS.recording._lastNativeCount; i < nativePoints.length; i++) {
|
||||
const p = nativePoints[i];
|
||||
GPS.recording.points.push(p);
|
||||
await DB.addGpsPoint(GPS.recording.sessionId, p.ts, p.lat, p.lon, p.altitude, p.speed_ms, null, null);
|
||||
if (GPS.recording.sessionId && nativePoints.length) {
|
||||
const existing = await DB.getGpsPoints(GPS.recording.sessionId);
|
||||
const dbTs = new Set(existing.map(p => new Date(p.ts).getTime()));
|
||||
let seq = existing.length;
|
||||
for (const p of nativePoints) {
|
||||
if (dbTs.has(new Date(p.ts).getTime())) continue;
|
||||
seq++;
|
||||
await DB.addGpsPoint(GPS.recording.sessionId, p.ts, p.lat, p.lon, p.altitude, p.speed_ms, seq, null, p);
|
||||
}
|
||||
GPS.recording.points = await DB.getGpsPoints(GPS.recording.sessionId);
|
||||
}
|
||||
// Remember the file name for cleanup after DB save
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -3296,7 +3296,16 @@ function renderRecordingInline(container) {
|
|||
let stats = GPS.computeStats(GPS.recording.points, GPS.recording.weightKg, GPS.getRecordingOpts());
|
||||
if (distEl) distEl.textContent = GPS.formatDistance(stats.distance);
|
||||
};
|
||||
|
||||
|
||||
// Lightweight per-point draw for progressive import (screen-wake catch-up):
|
||||
// only extends the polyline so the track visibly grows. Heavy work (stats,
|
||||
// marker, course) is done periodically + at the end by onBatchUpdate.
|
||||
GPS.recording.onImportPoint = (p) => {
|
||||
let ll = [p.lat, p.lon];
|
||||
borderLine.addLatLng(ll);
|
||||
trailLine.addLatLng(ll);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
function setupLiveUpdates(container) {
|
||||
|
|
@ -3330,6 +3339,12 @@ function setupLiveUpdates(container) {
|
|||
if (spdEl) spdEl.textContent = stats.avgSpeed + " км/ч";
|
||||
};
|
||||
|
||||
// Lightweight per-point draw for progressive import (see recording screen).
|
||||
GPS.recording.onImportPoint = (p) => {
|
||||
if (!gpsTrailLine) return;
|
||||
gpsTrailLine.addLatLng([p.lat, p.lon]);
|
||||
};
|
||||
|
||||
// Timer for duration display + auto-finish watchdog for a forgotten session
|
||||
timer = setInterval(async () => {
|
||||
if (!GPS.recording.active) { clearInterval(timer); return; }
|
||||
|
|
|
|||
2
src-tauri/Cargo.lock
generated
2
src-tauri/Cargo.lock
generated
|
|
@ -139,7 +139,7 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
|||
|
||||
[[package]]
|
||||
name = "baldav"
|
||||
version = "0.1.361"
|
||||
version = "0.1.363"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"btleplug",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "baldav"
|
||||
version = "0.1.361"
|
||||
version = "0.1.363"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"$schema": "https://raw.githubusercontent.com/nicegui/tauri-v2/refs/heads/main/tooling/cli/schema.json",
|
||||
"productName": "BalDav",
|
||||
"version": "0.1.361",
|
||||
"version": "0.1.363",
|
||||
"identifier": "dev.baldav.app",
|
||||
"build": {
|
||||
"frontendDist": "../frontend"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue