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

334 lines
12 KiB
Markdown

# 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
```typescript
@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
```typescript
@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
```typescript
@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
```typescript
// 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
```typescript
// 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](../30-integraciones/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*