erp-transportistas-v2/docs/10-arquitectura/ARQUITECTURA-GPS.md
Adrian Flores Cortes 6ed7f9e2ec [BACKUP] Pre-restructure workspace backup 2026-01-29
- Updated docs and inventory files
- Added new architecture docs

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 17:35:54 -06:00

12 KiB

Arquitectura del Modulo GPS - ERP Transportistas

Sistema: SIMCO v4.0.0 Modulo: GPS (backend/src/modules/gps) Sprint: S1 Version: 1.0.0 Fecha: 2026-01-28


1. Vision General

El modulo GPS proporciona capacidades de rastreo vehicular en tiempo real para la flota de transporte, incluyendo:

  • Gestion de dispositivos GPS
  • Captura y almacenamiento de posiciones
  • Definicion y monitoreo de geocercas
  • Generacion de eventos por entrada/salida de geocercas
  • Segmentacion de rutas para analisis

2. Arquitectura de Componentes

GPS Module
├── Entities
│   ├── DispositivoGps        # Dispositivos de rastreo
│   ├── PosicionGps           # Posiciones historicas
│   ├── Geocerca              # Zonas geograficas
│   ├── EventoGeocerca        # Eventos entrada/salida
│   └── SegmentoRuta          # Segmentos de viaje
├── Services
│   ├── DispositivoGpsService # CRUD dispositivos
│   ├── PosicionGpsService    # Captura posiciones
│   ├── GeocercaService       # CRUD geocercas
│   └── SegmentoRutaService   # Analisis de rutas
└── Controllers
    ├── DispositivoGpsController
    ├── PosicionGpsController
    ├── GeocercaController
    └── SegmentoRutaController

3. Modelo de Datos

3.1 DispositivoGps

@Entity('dispositivos_gps', { schema: 'tracking' })
class DispositivoGps {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @Column() tenantId: string;
  @Column() imei: string;           // Identificador unico
  @Column() marca: string;
  @Column() modelo: string;
  @Column({ enum: TipoDispositivo })
  tipo: TipoDispositivo;            // FIJO | PORTATIL | OBD
  @Column({ enum: EstadoDispositivo })
  estado: EstadoDispositivo;        // ACTIVO | INACTIVO | MANTENIMIENTO

  @ManyToOne(() => Unidad)
  unidad: Unidad;                   // Unidad asignada

  @Column() ultimaPosicionLat: number;
  @Column() ultimaPosicionLng: number;
  @Column() ultimoReporte: Date;
}

3.2 PosicionGps

@Entity('posiciones_gps', { schema: 'tracking' })
class PosicionGps {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @Column() tenantId: string;
  @ManyToOne(() => DispositivoGps)
  dispositivo: DispositivoGps;

  @Column('decimal') latitud: number;
  @Column('decimal') longitud: number;
  @Column('decimal') velocidad: number;    // km/h
  @Column('decimal') rumbo: number;        // 0-360 grados
  @Column('decimal') altitud: number;      // metros
  @Column('decimal') precision: number;    // metros

  @Column() timestampDispositivo: Date;    // Hora del GPS
  @Column() timestampServidor: Date;       // Hora de recepcion
}

3.3 Geocerca

@Entity('geocercas', { schema: 'tracking' })
class Geocerca {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @Column() tenantId: string;
  @Column() nombre: string;
  @Column({ enum: TipoGeocerca })
  tipo: TipoGeocerca;               // CLIENTE | PROVEEDOR | ZONA_RIESGO | etc.

  @Column('decimal') centroLat: number;
  @Column('decimal') centroLng: number;
  @Column('decimal') radioMetros: number;  // Para geocercas circulares
  @Column('jsonb') geometria: GeoJSON;     // Para geocercas poligonales

  @Column() alertaEntrada: boolean;
  @Column() alertaSalida: boolean;
  @Column() activa: boolean;
}

4. Flujo de Datos

4.1 Captura de Posiciones

┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│ Dispositivo │────>│   Gateway   │────>│   Backend   │
│    GPS      │     │ (protocolo) │     │  API REST   │
└─────────────┘     └─────────────┘     └─────────────┘
                                               │
                    ┌──────────────────────────┼──────────────────────────┐
                    │                          │                          │
                    ▼                          ▼                          ▼
            ┌─────────────┐           ┌─────────────┐           ┌─────────────┐
            │  Validar    │           │  Guardar    │           │  Verificar  │
            │  Posicion   │           │  en BD      │           │  Geocercas  │
            └─────────────┘           └─────────────┘           └─────────────┘
                                                                       │
                                                                       ▼
                                                               ┌─────────────┐
                                                               │  Generar    │
                                                               │  Eventos    │
                                                               └─────────────┘

4.2 Procesamiento Batch

// PosicionGpsService.createBatch()
async createBatch(posiciones: CreatePosicionDto[]): Promise<void> {
  // 1. Validar formato de datos
  const valid = posiciones.filter(p => this.isValidPosition(p));

  // 2. Insertar en batch
  await this.posicionRepo.insert(valid);

  // 3. Actualizar ultima posicion del dispositivo
  const byDevice = groupBy(valid, 'dispositivoId');
  for (const [deviceId, positions] of Object.entries(byDevice)) {
    const latest = maxBy(positions, 'timestampDispositivo');
    await this.dispositivoService.updateLastPosition(deviceId, latest);
  }

  // 4. Verificar geocercas para cada posicion
  for (const pos of valid) {
    await this.geocercaService.checkGeofences(pos);
  }
}

5. Geocercas

5.1 Tipos de Geocerca

Tipo Uso Alertas Tipicas
CLIENTE Ubicacion de entrega Arribo, Salida
PROVEEDOR Punto de carga Arribo, Demora
ZONA_RIESGO Area peligrosa Entrada
CASETA Peaje Paso registrado
GASOLINERA Combustible Parada larga
PATIO Base de operaciones Entrada/Salida
PUNTO_CONTROL Inspeccion Paso registrado

5.2 Algoritmo de Deteccion

// GeocercaService.checkGeofences()
async checkGeofences(posicion: PosicionGps): Promise<EventoGeocerca[]> {
  const eventos: EventoGeocerca[] = [];

  // 1. Obtener geocercas activas del tenant
  const geocercas = await this.geocercaRepo.find({
    where: { tenantId: posicion.tenantId, activa: true }
  });

  // 2. Obtener ultima posicion previa
  const anterior = await this.getLastPosition(posicion.dispositivoId);

  // 3. Verificar cada geocerca
  for (const geo of geocercas) {
    const dentroAhora = this.isInside(posicion, geo);
    const dentroAntes = anterior ? this.isInside(anterior, geo) : false;

    // Entrada: estaba fuera, ahora dentro
    if (!dentroAntes && dentroAhora && geo.alertaEntrada) {
      eventos.push(this.createEvento('ENTRADA', posicion, geo));
    }

    // Salida: estaba dentro, ahora fuera
    if (dentroAntes && !dentroAhora && geo.alertaSalida) {
      eventos.push(this.createEvento('SALIDA', posicion, geo));
    }
  }

  return eventos;
}

// Deteccion punto-en-circulo (Haversine)
isInsideCircle(punto: Coordenada, centro: Coordenada, radioMetros: number): boolean {
  const distancia = this.haversineDistance(punto, centro);
  return distancia <= radioMetros;
}

6. API Endpoints

6.1 Dispositivos

Metodo Endpoint Descripcion
GET /api/v1/gps/dispositivos Listar dispositivos
POST /api/v1/gps/dispositivos Crear dispositivo
GET /api/v1/gps/dispositivos/:id Obtener dispositivo
PATCH /api/v1/gps/dispositivos/:id Actualizar dispositivo
DELETE /api/v1/gps/dispositivos/:id Eliminar dispositivo
POST /api/v1/gps/dispositivos/:id/asignar Asignar a unidad

6.2 Posiciones

Metodo Endpoint Descripcion
POST /api/v1/gps/posiciones Registrar posicion
POST /api/v1/gps/posiciones/batch Registrar batch
GET /api/v1/gps/posiciones Historial (paginado)
GET /api/v1/gps/posiciones/ultima/:dispositivoId Ultima posicion
POST /api/v1/gps/posiciones/ultimas Multiples ultimas
GET /api/v1/gps/posiciones/track/:dispositivoId Track completo

6.3 Geocercas

Metodo Endpoint Descripcion
GET /api/v1/gps/geocercas Listar geocercas
POST /api/v1/gps/geocercas Crear geocerca
GET /api/v1/gps/geocercas/:id Obtener geocerca
PATCH /api/v1/gps/geocercas/:id Actualizar geocerca
DELETE /api/v1/gps/geocercas/:id Eliminar geocerca
POST /api/v1/gps/geocercas/verificar Verificar punto en geocercas

7. Integracion con Providers

7.1 Providers Soportados

Provider Protocolo Estado
Plataforma propia REST API Implementado
Queclink TCP/IP Planificado
Teltonika TCP/IP Planificado
CalAmp TCP/IP Planificado

7.2 Arquitectura Multi-Provider

┌─────────────────────────────────────────────────────────────┐
│                    GPS Gateway Service                       │
├─────────────┬─────────────┬─────────────┬─────────────────┤
│  Queclink   │  Teltonika  │   CalAmp    │  REST API       │
│  Adapter    │  Adapter    │   Adapter   │  (nativo)       │
└──────┬──────┴──────┬──────┴──────┬──────┴────────┬────────┘
       │             │             │               │
       └─────────────┴─────────────┴───────────────┘
                           │
                           ▼
               ┌─────────────────────┐
               │ Normalized Position │
               │    { lat, lng,      │
               │      speed, ...}    │
               └─────────────────────┘

Ver: INTEGRACION-GPS-PROVIDERS.md


8. Consideraciones de Performance

8.1 Volumen de Datos

Metrica Valor Tipico Maximo
Posiciones/dispositivo/dia 1,440 (1/min) 86,400 (1/seg)
Dispositivos activos 100-500 5,000
Posiciones/dia total 144,000-720,000 432M

8.2 Estrategias de Optimizacion

  1. Batch inserts: Agrupar posiciones en lotes de 100-1000
  2. TimescaleDB: Particionamiento por tiempo para historico
  3. Redis cache: Ultimas posiciones por dispositivo
  4. Compresion: Eliminar posiciones redundantes (sin movimiento)
  5. Indices: tenant_id, dispositivo_id, timestamp

9. DDL Relacionado

  • database/ddl/03-tracking-schema-ddl.sql - Tablas base
  • database/ddl/03a-gps-devices-ddl.sql - Extension dispositivos

Sprint S1 - TASK-007 | Sistema SIMCO v4.0.0