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,