- Updated docs and inventory files - Added new architecture docs Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
324 lines
6.9 KiB
Markdown
324 lines
6.9 KiB
Markdown
# Mapa de Tracking en Tiempo Real - Documentacion Tecnica
|
|
|
|
**Feature:** tracking
|
|
**Sprint:** S7
|
|
**Estado:** Funcional (mapas pendientes)
|
|
**Fecha:** 2026-01-28
|
|
|
|
---
|
|
|
|
## 1. Vision General
|
|
|
|
El sistema de tracking en tiempo real permite:
|
|
- Visualizar posicion actual de unidades en mapa
|
|
- Ver historial de ruta recorrida
|
|
- Monitorear eventos y alertas
|
|
- Calcular y mostrar ETA dinamico
|
|
- Recibir actualizaciones via WebSocket
|
|
|
|
---
|
|
|
|
## 2. Arquitectura de Componentes
|
|
|
|
```
|
|
ViajeTrackingView
|
|
├── MapSection (mapa con ruta)
|
|
│ ├── RoutePolyline (ruta planificada)
|
|
│ ├── HistoryPolyline (ruta recorrida)
|
|
│ ├── CurrentPositionMarker
|
|
│ ├── OriginMarker
|
|
│ └── DestinationMarker
|
|
├── ETAProgressBar
|
|
│ ├── MilestoneIndicators
|
|
│ └── ProgressLine
|
|
└── EventTimeline
|
|
├── EventCards (17 tipos)
|
|
└── TimelineConnector
|
|
```
|
|
|
|
---
|
|
|
|
## 3. Tipos Principales
|
|
|
|
### 3.1 ENUMs
|
|
|
|
```typescript
|
|
enum TipoEventoTracking {
|
|
// GPS
|
|
POSICION = 'POSICION',
|
|
GPS_POSICION = 'GPS_POSICION',
|
|
|
|
// Geocercas
|
|
GEOCERCA_ENTRADA = 'GEOCERCA_ENTRADA',
|
|
GEOCERCA_SALIDA = 'GEOCERCA_SALIDA',
|
|
|
|
// Flujo de viaje
|
|
INICIO_VIAJE = 'INICIO_VIAJE',
|
|
ARRIBO_ORIGEN = 'ARRIBO_ORIGEN',
|
|
INICIO_CARGA = 'INICIO_CARGA',
|
|
FIN_CARGA = 'FIN_CARGA',
|
|
SALIDA = 'SALIDA',
|
|
ARRIBO_DESTINO = 'ARRIBO_DESTINO',
|
|
INICIO_DESCARGA = 'INICIO_DESCARGA',
|
|
FIN_DESCARGA = 'FIN_DESCARGA',
|
|
ENTREGA_POD = 'ENTREGA_POD',
|
|
FIN_VIAJE = 'FIN_VIAJE',
|
|
|
|
// Incidentes
|
|
PARADA = 'PARADA',
|
|
DESVIO = 'DESVIO',
|
|
INCIDENTE = 'INCIDENTE'
|
|
}
|
|
|
|
enum FuenteEvento {
|
|
GPS = 'GPS',
|
|
APP_OPERADOR = 'APP_OPERADOR',
|
|
SISTEMA = 'SISTEMA',
|
|
MANUAL = 'MANUAL',
|
|
GEOCERCA = 'GEOCERCA'
|
|
}
|
|
```
|
|
|
|
### 3.2 Interfaces
|
|
|
|
```typescript
|
|
interface PosicionActual {
|
|
lat: number;
|
|
lng: number;
|
|
velocidad: number;
|
|
rumbo: number;
|
|
timestamp: string;
|
|
precision: number;
|
|
}
|
|
|
|
interface EventoTracking {
|
|
id: string;
|
|
viajeId: string;
|
|
tipo: TipoEventoTracking;
|
|
fuente: FuenteEvento;
|
|
timestampEvento: string;
|
|
ubicacion: { lat: number; lng: number } | null;
|
|
descripcion: string;
|
|
metadata: Record<string, unknown>;
|
|
}
|
|
|
|
interface TrackingData {
|
|
viajeId: string;
|
|
posicionActual: PosicionActual;
|
|
rutaPlanificada: [number, number][];
|
|
rutaRecorrida: [number, number][];
|
|
eventos: EventoTracking[];
|
|
eta: {
|
|
llegadaEstimada: string;
|
|
distanciaRestante: number;
|
|
tiempoRestante: string;
|
|
};
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 4. WebSocket Integration
|
|
|
|
### 4.1 Hook useTrackingWebSocket
|
|
|
|
```typescript
|
|
// hooks/useTrackingWebSocket.ts
|
|
export function useTrackingWebSocket(viajeId: string) {
|
|
const [isConnected, setIsConnected] = useState(false);
|
|
const [lastPosition, setLastPosition] = useState<PosicionActual | null>(null);
|
|
const [eventos, setEventos] = useState<EventoTracking[]>([]);
|
|
|
|
useEffect(() => {
|
|
const ws = new WebSocket(`${WS_URL}/tracking/${viajeId}`);
|
|
|
|
ws.onmessage = (event) => {
|
|
const data = JSON.parse(event.data);
|
|
if (data.type === 'position') {
|
|
setLastPosition(data.payload);
|
|
} else if (data.type === 'event') {
|
|
setEventos(prev => [...prev, data.payload]);
|
|
}
|
|
};
|
|
|
|
// Auto-reconnect logic included
|
|
}, [viajeId]);
|
|
|
|
return { isConnected, lastPosition, eventos };
|
|
}
|
|
```
|
|
|
|
### 4.2 Mensajes WebSocket
|
|
|
|
```typescript
|
|
// Posicion
|
|
{
|
|
type: 'position',
|
|
payload: {
|
|
lat: 19.4326,
|
|
lng: -99.1332,
|
|
velocidad: 85,
|
|
rumbo: 45,
|
|
timestamp: '2026-01-28T10:30:00Z'
|
|
}
|
|
}
|
|
|
|
// Evento
|
|
{
|
|
type: 'event',
|
|
payload: {
|
|
id: 'evt-123',
|
|
tipo: 'ARRIBO_DESTINO',
|
|
fuente: 'GPS',
|
|
timestamp: '2026-01-28T10:30:00Z',
|
|
descripcion: 'Unidad arribo a destino'
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 5. ETAProgressBar Component
|
|
|
|
### 5.1 Milestones
|
|
|
|
```typescript
|
|
const milestones = [
|
|
{ id: 'origin', label: 'Origen', icon: 'MapPin' },
|
|
{ id: 'loaded', label: 'Cargado', icon: 'Package' },
|
|
{ id: 'transit', label: 'En Transito', icon: 'Truck' },
|
|
{ id: 'destination', label: 'Destino', icon: 'Flag' },
|
|
{ id: 'delivered', label: 'Entregado', icon: 'CheckCircle' }
|
|
];
|
|
```
|
|
|
|
### 5.2 Progress Calculation
|
|
|
|
```typescript
|
|
function calculateProgress(eventos: EventoTracking[]): number {
|
|
const weights = {
|
|
INICIO_VIAJE: 0,
|
|
ARRIBO_ORIGEN: 10,
|
|
FIN_CARGA: 25,
|
|
SALIDA: 30,
|
|
EN_TRANSITO: 50, // Interpolado por distancia
|
|
ARRIBO_DESTINO: 80,
|
|
FIN_DESCARGA: 90,
|
|
ENTREGA_POD: 100
|
|
};
|
|
|
|
const lastEvent = eventos[eventos.length - 1];
|
|
return weights[lastEvent?.tipo] || 0;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 6. EventTimeline Component
|
|
|
|
### 6.1 Iconos por Tipo
|
|
|
|
```typescript
|
|
const eventIcons: Record<TipoEventoTracking, React.ComponentType> = {
|
|
POSICION: MapPin,
|
|
GPS_POSICION: Satellite,
|
|
GEOCERCA_ENTRADA: LogIn,
|
|
GEOCERCA_SALIDA: LogOut,
|
|
INICIO_VIAJE: PlayCircle,
|
|
ARRIBO_ORIGEN: MapPin,
|
|
INICIO_CARGA: Package,
|
|
FIN_CARGA: PackageCheck,
|
|
SALIDA: Truck,
|
|
ARRIBO_DESTINO: Flag,
|
|
INICIO_DESCARGA: PackageOpen,
|
|
FIN_DESCARGA: PackageCheck,
|
|
ENTREGA_POD: FileCheck,
|
|
FIN_VIAJE: CheckCircle,
|
|
PARADA: PauseCircle,
|
|
DESVIO: AlertTriangle,
|
|
INCIDENTE: AlertOctagon
|
|
};
|
|
```
|
|
|
|
### 6.2 Colores por Tipo
|
|
|
|
```typescript
|
|
const eventColors: Record<TipoEventoTracking, string> = {
|
|
INICIO_VIAJE: 'bg-green-500',
|
|
FIN_VIAJE: 'bg-green-600',
|
|
INCIDENTE: 'bg-red-500',
|
|
PARADA: 'bg-yellow-500',
|
|
DESVIO: 'bg-orange-500',
|
|
// ... otros
|
|
default: 'bg-blue-500'
|
|
};
|
|
```
|
|
|
|
---
|
|
|
|
## 7. API Endpoints
|
|
|
|
| Endpoint | Metodo | Descripcion |
|
|
|----------|--------|-------------|
|
|
| `/api/v1/tracking/viajes/:id` | GET | Datos de tracking de viaje |
|
|
| `/api/v1/tracking/viajes/:id/eventos` | GET | Historial de eventos |
|
|
| `/api/v1/tracking/viajes/:id/ruta` | GET | Ruta planificada y recorrida |
|
|
| `/api/v1/gps/posiciones/ultima/:dispositivoId` | GET | Ultima posicion |
|
|
| `WS /tracking/:viajeId` | WS | Streaming de posiciones |
|
|
|
|
---
|
|
|
|
## 8. Integracion con Mapas (Pendiente)
|
|
|
|
### 8.1 Estado Actual
|
|
|
|
- Mapa placeholder con CSS gradient
|
|
- Polylines simplificados con SVG
|
|
- Marcadores posicionados con % relativo
|
|
|
|
### 8.2 Implementacion con Leaflet
|
|
|
|
```typescript
|
|
import { MapContainer, Polyline, Marker, Popup } from 'react-leaflet';
|
|
|
|
const TrackingMap = ({ data }: { data: TrackingData }) => (
|
|
<MapContainer center={data.posicionActual} zoom={12}>
|
|
<TileLayer url="..." />
|
|
|
|
{/* Ruta planificada */}
|
|
<Polyline
|
|
positions={data.rutaPlanificada}
|
|
color="blue"
|
|
dashArray="5,10"
|
|
/>
|
|
|
|
{/* Ruta recorrida */}
|
|
<Polyline
|
|
positions={data.rutaRecorrida}
|
|
color="green"
|
|
weight={4}
|
|
/>
|
|
|
|
{/* Posicion actual */}
|
|
<Marker position={[data.posicionActual.lat, data.posicionActual.lng]}>
|
|
<Popup>
|
|
<span>Velocidad: {data.posicionActual.velocidad} km/h</span>
|
|
</Popup>
|
|
</Marker>
|
|
</MapContainer>
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
## 9. Performance Considerations
|
|
|
|
1. **Throttle WebSocket updates**: Maximo 1 update por segundo en UI
|
|
2. **Virtualized EventTimeline**: Para viajes con 100+ eventos
|
|
3. **Route simplification**: Usar Douglas-Peucker para rutas largas
|
|
4. **Map tile caching**: Service worker para tiles frecuentes
|
|
|
|
---
|
|
|
|
*Sprint S7 - TASK-007 | Sistema SIMCO v4.0.0*
|