- Updated docs and inventory files - Added new architecture docs Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
334 lines
12 KiB
Markdown
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*
|