From cbb659aed71c20795094da02968dd6b54ec7c1da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=98=D0=BB=D1=8C=D1=8F=D1=81=20=D0=A1=D1=83=D0=BB=D1=82?= =?UTF-8?q?=D0=B0=D0=BD=D0=BE=D0=B2?= Date: Mon, 31 Aug 2026 15:13:03 +0300 Subject: [PATCH] =?UTF-8?q?feat(gps):=20valid=3Dfalse=20=D0=BE=D1=82=20?= =?UTF-8?q?=D0=BF=D1=80=D0=BE=D1=82=D0=BE=D0=BA=D0=BE=D0=BB=D0=B0=20H02=20?= =?UTF-8?q?=E2=86=92=20=D1=82=D0=BE=D1=87=D0=BA=D0=B0=20=D1=81=D0=BE=D1=85?= =?UTF-8?q?=D1=80=D0=B0=D0=BD=D1=8F=D0=B5=D1=82=D1=81=D1=8F=20=D0=BA=D0=B0?= =?UTF-8?q?=D0=BA=20LBS-=D0=BF=D1=80=D0=B8=D0=BC=D0=B5=D1=80=D0=BD=D0=B0?= =?UTF-8?q?=D1=8F=20(=D0=BD=D0=B5=20=D0=B2=D1=8B=D0=B1=D1=80=D0=B0=D1=81?= =?UTF-8?q?=D1=8B=D0=B2=D0=B0=D0=B5=D1=82=D1=81=D1=8F),=20=D1=81=D0=B8?= =?UTF-8?q?=D0=B3=D0=BD=D0=B0=D0=BB=20=D0=BF=D0=BE=D1=82=D0=B5=D1=80=D0=B8?= =?UTF-8?q?=20GPS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yml | 4 +-- server/gps/geozone.service.ts | 46 +++++++++++++++++++++++++++++++++++ server/gps/routes.ts | 12 +++------ 3 files changed, 52 insertions(+), 10 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index d211e63..cc95568 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -167,9 +167,9 @@ services: image: osrm/osrm-backend:latest container_name: osrm restart: unless-stopped - command: osrm-routed --algorithm mld /data/volga.osrm + command: osrm-routed --algorithm mld /data/region.osrm volumes: - - ./osrm-data:/data:ro + - ./osrm-data-ru:/data:ro volumes: crm_data: diff --git a/server/gps/geozone.service.ts b/server/gps/geozone.service.ts index f00e602..65f8244 100644 --- a/server/gps/geozone.service.ts +++ b/server/gps/geozone.service.ts @@ -51,6 +51,8 @@ export interface ProcessPositionInput { course?: number | null; accuracy?: number | null; recordedAt?: Date; + /** Протокольный признак «GPS невалиден» (Traccar valid=false) — LBS-точка */ + forceLbs?: boolean; } // Вердикт фильтрации точки относительно предыдущей принятой @@ -500,6 +502,50 @@ export async function processPosition( ? { lat: asset.lastLat, lng: asset.lastLng, recordedAt: asset.lastRecordedAt, speed: asset.lastSpeed } : null; + // Протокольный флаг «GPS невалиден» (Traccar valid=false): это точка по вышкам. + // Сохраняем как примерную (is_lbs), маркер двигаем, но walker/геозоны/пробег + // не трогаем. Заодно это сигнал потери GPS — как stale re-send. + if (input.forceLbs) { + assetLastStaleAt.set(assetId, Date.now()); + if (!asset.gpsLost) { + await gpsStorage.setGpsLost(assetId, true); + console.log(`[GPS] gps_lost ON: asset=${assetId} (valid=false от протокола)`); + } + await gpsStorage.insertPosition({ + assetId, + organizationId, + lat: input.lat, + lng: input.lng, + speed: input.speed ?? null, + course: input.course ?? null, + accuracy: input.accuracy ?? null, + isLbs: true, + recordedAt, + }); + await gpsStorage.updateAssetLastPosition(assetId, { + lat: input.lat, + lng: input.lng, + speed: input.speed, + course: input.course, + recordedAt, + isLbs: true, + }); + eventBus.publishEvent({ + type: "gps_asset_moved", + data: { + assetId, + lat: input.lat, + lng: input.lng, + speed: input.speed ?? null, + course: input.course ?? null, + recordedAt: recordedAt.toISOString(), + isLbs: true, + }, + organizationId, + }); + return; + } + // Фильтры stale/jump/echo с гистерезисом подтверждения (устаревшие копии, // LBS-скачки, heartbeat-эхо стоянки) const walker = getAssetWalker(assetId, prev, settings); diff --git a/server/gps/routes.ts b/server/gps/routes.ts index 70aaec7..edcbbbd 100644 --- a/server/gps/routes.ts +++ b/server/gps/routes.ts @@ -261,16 +261,12 @@ async function handleIngest(req: AuthenticatedRequest, res: Response) { continue; } - // Traccar пометил позицию как невалидную (valid=false) — не сохраняем, - // но считаем признаком жизни устройства - if (!point.valid) { - await gpsStorage.touchAssetSeen(asset.id); - processed++; - continue; - } - try { await processPosition(asset.id, asset.organizationId, { + // Traccar пометил позицию как невалидную (valid=false) — протокольный + // признак «нет GPS-фикса»: сохраняем как примерную (LBS), в геозоны + // и пробег не пускаем + forceLbs: !point.valid, lat: point.lat, lng: point.lng, speed: point.speed,