# Fase 2: Enriquecimiento - GTFS Static, Trip Updates y Service Alerts ## Estado: 🚧 EN DESARROLLO La Fase 2 añade enriquecimiento de datos mediante GTFS Static, actualizaciones de viajes en tiempo real (Trip Updates) y alertas de servicio (Service Alerts). --- ## ✨ Características Implementadas ### Backend #### Base de Datos - ✅ Migración V5: Tablas GTFS Static (trips, stop_times, calendar, shapes) - ✅ Tablas para Trip Updates y Stop Time Updates - ✅ Vistas: active_trips_today, delayed_trips - ✅ Funciones: get_trip_schedule, get_next_departures #### Workers - ✅ GTFS Static Syncer: Sincronización diaria de datos estáticos - ✅ Trip Updates Poller: Polling de retrasos y actualizaciones de viajes - ✅ Service Alerts Poller: Polling de alertas e incidencias #### API REST - ✅ Endpoints de Alertas (`/alerts`) - ✅ Endpoints de Trips y Delays (`/trips`) ### Frontend - ⏳ Componente de Alertas (pendiente) - ⏳ Monitor de Puntualidad (pendiente) - ⏳ Timeline Funcional (pendiente) --- ## 📁 Nuevos Archivos Phase 2 ### Base de Datos ``` database/migrations/ └── V5__gtfs_static_tables.sql # Tablas GTFS Static + Trip Updates ``` ### Backend Workers ``` backend/src/worker/ ├── gtfs-static-syncer.js # Sincronización GTFS Static ├── trip-updates-poller.js # Polling Trip Updates └── alerts-poller.js # Polling Service Alerts ``` ### Backend API ``` backend/src/api/routes/ ├── alerts.js # Endpoints de alertas └── trips.js # Endpoints de trips y delays ``` --- ## 🚀 Ejecutar con Fase 2 ### Usando Docker Compose ```bash # 1. Ejecutar migraciones (incluye V5) make migrate # 2. Iniciar todos los servicios (incluye nuevos workers) make start # Los nuevos workers se inician automáticamente: # - gtfs-static-syncer (sincronización diaria a las 3 AM) # - trip-updates-poller (polling cada 30s) # - alerts-poller (polling cada 30s) ``` ### Desarrollo Local ```bash cd backend # Terminal 1: GTFS Static Syncer npm run dev:gtfs-static # Terminal 2: Trip Updates Poller npm run dev:trip-updates # Terminal 3: Service Alerts Poller npm run dev:alerts # Terminal 4: API Server npm run dev ``` --- ## 📡 Nuevos Endpoints API ### Alertas #### GET /alerts Obtener todas las alertas activas con filtros opcionales. **Query Parameters:** - `route_id` (opcional): Filtrar por ruta - `severity` (opcional): Filtrar por severidad (LOW, MEDIUM, HIGH, CRITICAL) - `type` (opcional): Filtrar por tipo (DELAY, CANCELLATION, INCIDENT, etc.) **Ejemplo:** ```bash # Todas las alertas activas curl http://localhost:3000/alerts # Alertas de una ruta específica curl http://localhost:3000/alerts?route_id=AVE-MAD-BCN # Alertas críticas curl http://localhost:3000/alerts?severity=CRITICAL # Alertas de cancelaciones curl http://localhost:3000/alerts?type=CANCELLATION ``` **Respuesta:** ```json [ { "alert_id": 1, "alert_type": "DELAY", "severity": "MEDIUM", "cause": "TECHNICAL_PROBLEM", "effect": "SIGNIFICANT_DELAYS", "header_text": "Retraso en AVE 03055", "description_text": "Retraso de 15 minutos debido a problemas técnicos", "url": null, "route_id": "AVE-MAD-BCN", "trip_id": "trip_12345", "train_id": null, "start_time": "2025-11-27T10:00:00Z", "end_time": null, "created_at": "2025-11-27T10:05:00Z", "updated_at": "2025-11-27T10:05:00Z" } ] ``` #### GET /alerts/:id Obtener una alerta específica. **Ejemplo:** ```bash curl http://localhost:3000/alerts/1 ``` #### GET /alerts/route/:routeId Obtener todas las alertas activas de una ruta. **Ejemplo:** ```bash curl http://localhost:3000/alerts/route/AVE-MAD-BCN ``` #### GET /alerts/train/:trainId Obtener todas las alertas activas de un tren. **Ejemplo:** ```bash curl http://localhost:3000/alerts/train/12345 ``` --- ### Trips y Delays #### GET /trips Obtener todos los viajes activos del día. **Query Parameters:** - `route_id` (opcional): Filtrar por ruta - `service_id` (opcional): Filtrar por servicio **Ejemplo:** ```bash # Todos los viajes activos hoy curl http://localhost:3000/trips # Viajes de una ruta específica curl http://localhost:3000/trips?route_id=AVE-MAD-BCN ``` **Respuesta:** ```json [ { "trip_id": "trip_12345", "route_id": "AVE-MAD-BCN", "service_id": "weekday", "trip_headsign": "Barcelona Sants", "direction_id": 0, "block_id": null, "shape_id": "shape_001" } ] ``` #### GET /trips/:id Obtener detalles completos de un viaje incluyendo su horario. **Ejemplo:** ```bash curl http://localhost:3000/trips/trip_12345 ``` **Respuesta:** ```json { "trip_id": "trip_12345", "route_id": "AVE-MAD-BCN", "service_id": "weekday", "trip_headsign": "Barcelona Sants", "direction_id": 0, "schedule": [ { "stop_id": "MADRID-PUERTA-DE-ATOCHA", "stop_sequence": 1, "arrival_time": "08:00:00", "departure_time": "08:00:00", "stop_headsign": null }, { "stop_id": "ZARAGOZA-DELICIAS", "stop_sequence": 2, "arrival_time": "09:25:00", "departure_time": "09:27:00", "stop_headsign": null }, { "stop_id": "BARCELONA-SANTS", "stop_sequence": 3, "arrival_time": "10:45:00", "departure_time": "10:45:00", "stop_headsign": null } ] } ``` #### GET /trips/:id/updates Obtener actualizaciones en tiempo real de un viaje (retrasos, cancelaciones). **Ejemplo:** ```bash curl http://localhost:3000/trips/trip_12345/updates ``` **Respuesta:** ```json { "trip_id": "trip_12345", "has_updates": true, "update_id": 1, "delay_seconds": 900, "schedule_relationship": "SCHEDULED", "start_date": "20251127", "received_at": "2025-11-27T10:15:00Z", "stop_time_updates": [ { "stop_sequence": 2, "stop_id": "ZARAGOZA-DELICIAS", "arrival_delay": 900, "departure_delay": 900, "schedule_relationship": "SCHEDULED" }, { "stop_sequence": 3, "stop_id": "BARCELONA-SANTS", "arrival_delay": 900, "departure_delay": null, "schedule_relationship": "SCHEDULED" } ] } ``` #### GET /trips/:id/delays Obtener información resumida de retrasos de un viaje. **Ejemplo:** ```bash curl http://localhost:3000/trips/trip_12345/delays ``` **Respuesta:** ```json { "trip_id": "trip_12345", "delay_status": "MODERATE_DELAY", "delay_seconds": 900, "delay_formatted": "15 min 0 s", "schedule_relationship": "SCHEDULED", "received_at": "2025-11-27T10:15:00Z" } ``` **Estados de Delay:** - `NO_DATA`: Sin información de retrasos - `ON_TIME`: Puntual (0 segundos) - `MINOR_DELAY`: Retraso menor (1-5 minutos) - `MODERATE_DELAY`: Retraso moderado (5-15 minutos) - `MAJOR_DELAY`: Retraso mayor (>15 minutos) - `EARLY`: Adelantado #### GET /trips/route/:routeId Obtener todos los viajes de una ruta. **Ejemplo:** ```bash curl http://localhost:3000/trips/route/AVE-MAD-BCN ``` #### GET /trips/delayed/all Obtener todos los viajes actualmente retrasados. **Query Parameters:** - `min_delay` (opcional): Retraso mínimo en segundos (default: 0) **Ejemplo:** ```bash # Todos los viajes retrasados curl http://localhost:3000/trips/delayed/all # Solo retrasos mayores a 5 minutos curl http://localhost:3000/trips/delayed/all?min_delay=300 ``` **Respuesta:** ```json [ { "trip_id": "trip_12345", "route_id": "AVE-MAD-BCN", "trip_headsign": "Barcelona Sants", "delay_seconds": 900, "schedule_relationship": "SCHEDULED", "received_at": "2025-11-27T10:15:00Z" } ] ``` --- ## 🔌 Nuevos WebSocket Events (Planeados) ### Servidor → Cliente ```javascript // Nueva alerta creada socket.on('alert:new', (alert) => { console.log('Nueva alerta:', alert); }); // Alerta actualizada socket.on('alert:update', (alert) => { console.log('Alerta actualizada:', alert); }); // Retraso detectado socket.on('trip:delay', (delayInfo) => { console.log('Retraso en viaje:', delayInfo); }); // Cancelación de viaje socket.on('trip:cancelled', (tripInfo) => { console.log('Viaje cancelado:', tripInfo); }); ``` --- ## 🗄️ Estructura de Datos ### Tablas GTFS Static #### trips Información de viajes planificados. ```sql CREATE TABLE trips ( trip_id VARCHAR(100) PRIMARY KEY, route_id VARCHAR(50), service_id VARCHAR(50), trip_headsign VARCHAR(200), trip_short_name VARCHAR(50), direction_id INTEGER, block_id VARCHAR(50), shape_id VARCHAR(100), wheelchair_accessible INTEGER, bikes_allowed INTEGER ); ``` #### stop_times Horarios de parada de cada viaje. ```sql CREATE TABLE stop_times ( trip_id VARCHAR(100), arrival_time TIME, departure_time TIME, stop_id VARCHAR(100), stop_sequence INTEGER, stop_headsign VARCHAR(200), pickup_type INTEGER, drop_off_type INTEGER, shape_dist_traveled FLOAT, PRIMARY KEY (trip_id, stop_sequence) ); ``` #### calendar Calendario de servicio (días de operación). ```sql CREATE TABLE calendar ( service_id VARCHAR(50) PRIMARY KEY, monday BOOLEAN, tuesday BOOLEAN, wednesday BOOLEAN, thursday BOOLEAN, friday BOOLEAN, saturday BOOLEAN, sunday BOOLEAN, start_date DATE, end_date DATE ); ``` #### shapes Geometría de las rutas (trayectorias). ```sql CREATE TABLE shapes ( shape_id VARCHAR(100), shape_pt_lat DOUBLE PRECISION, shape_pt_lon DOUBLE PRECISION, shape_pt_sequence INTEGER, shape_dist_traveled FLOAT, PRIMARY KEY (shape_id, shape_pt_sequence) ); ``` ### Tablas de Actualizaciones en Tiempo Real #### trip_updates Actualizaciones de viajes (retrasos, cancelaciones). ```sql CREATE TABLE trip_updates ( update_id SERIAL PRIMARY KEY, trip_id VARCHAR(100), route_id VARCHAR(50), start_date VARCHAR(10), schedule_relationship VARCHAR(20), delay_seconds INTEGER, received_at TIMESTAMP DEFAULT NOW() ); ``` #### stop_time_updates Actualizaciones de paradas específicas. ```sql CREATE TABLE stop_time_updates ( update_id INTEGER REFERENCES trip_updates(update_id), stop_sequence INTEGER, stop_id VARCHAR(100), arrival_delay INTEGER, departure_delay INTEGER, schedule_relationship VARCHAR(20), PRIMARY KEY (update_id, stop_sequence) ); ``` --- ## 🧪 Probar Phase 2 ### 1. Verificar GTFS Static Sync ```bash # Ver logs del syncer docker-compose logs -f gtfs-static-syncer # Deberías ver: # "Starting GTFS Static synchronization..." # "GTFS data downloaded successfully" # "Imported X routes, Y trips, Z stops" # Verificar datos en PostgreSQL make psql > SELECT COUNT(*) FROM trips; > SELECT COUNT(*) FROM stop_times; > SELECT * FROM active_trips_today LIMIT 5; ``` ### 2. Verificar Trip Updates ```bash # Ver logs del poller docker-compose logs -f trip-updates-poller # Deberías ver: # "Polling Trip Updates..." # "Processed X trip updates" # Verificar en PostgreSQL make psql > SELECT * FROM delayed_trips; > SELECT * FROM trip_updates ORDER BY received_at DESC LIMIT 5; ``` ### 3. Verificar Service Alerts ```bash # Ver logs del poller docker-compose logs -f alerts-poller # Deberías ver: # "Polling Service Alerts..." # "Processed X alerts" # Probar API curl http://localhost:3000/alerts | jq curl http://localhost:3000/alerts?severity=HIGH | jq ``` ### 4. Verificar Trip Delays API ```bash # Obtener viajes retrasados curl http://localhost:3000/trips/delayed/all | jq # Obtener delay de un viaje específico curl http://localhost:3000/trips/trip_12345/delays | jq # Obtener schedule completo curl http://localhost:3000/trips/trip_12345 | jq ``` --- ## 🐛 Troubleshooting Phase 2 ### No hay datos de GTFS Static **Causa**: El syncer no se ha ejecutado o el ZIP no está disponible. **Solución**: ```bash # Ejecutar sync manual docker-compose exec gtfs-static-syncer node src/worker/gtfs-static-syncer.js # Verificar logs docker-compose logs gtfs-static-syncer # Verificar conectividad curl -I https://data.renfe.com/dataset/horarios-trenes-largo-recorrido-ave/resource/horarios-trenes-largo-recorrido-ave-gtfs.zip ``` ### No se reciben Trip Updates **Causa**: Feed GTFS-RT no disponible o URL incorrecta. **Solución**: ```bash # Verificar logs docker-compose logs trip-updates-poller # Probar feed manualmente curl https://gtfsrt.renfe.com/trip_updates.pb > /tmp/test.pb file /tmp/test.pb # Debe ser "data" (Protocol Buffer) # Verificar variables de entorno docker-compose exec trip-updates-poller env | grep GTFS ``` ### Alerts no aparecen **Causa**: No hay alertas activas o el poller no está corriendo. **Solución**: ```bash # Verificar worker docker-compose ps alerts-poller # Ver logs docker-compose logs alerts-poller # Verificar en BD make psql > SELECT COUNT(*) FROM alerts WHERE end_time IS NULL OR end_time > NOW(); ``` --- ## 📊 Monitorización Phase 2 ### Logs de Workers ```bash # Todos los workers Phase 2 docker-compose logs -f gtfs-static-syncer trip-updates-poller alerts-poller # Worker específico docker-compose logs -f trip-updates-poller ``` ### Estadísticas en Redis ```bash make redis-cli # Viajes retrasados > SMEMBERS trips:delayed # Alertas activas por ruta > SMEMBERS alerts:route:AVE-MAD-BCN # Última sincronización GTFS > GET gtfs:last_sync ``` ### Métricas en PostgreSQL ```sql -- Total de viajes del día SELECT COUNT(*) FROM active_trips_today; -- Viajes retrasados SELECT COUNT(*) FROM delayed_trips; -- Alertas activas SELECT alert_type, COUNT(*) FROM alerts WHERE end_time IS NULL OR end_time > NOW() GROUP BY alert_type; -- Retraso promedio SELECT AVG(delay_seconds) / 60 as avg_delay_minutes FROM trip_updates WHERE received_at > NOW() - INTERVAL '1 hour'; ``` --- ## 🎯 Próximos Pasos Funcionalidades pendientes de Phase 2: - [ ] WebSocket events para alertas y delays en tiempo real - [ ] Frontend: Componente de alertas - [ ] Frontend: Monitor de puntualidad - [ ] Frontend: Timeline funcional con reproducción histórica - [ ] Frontend: Panel de incidencias - [ ] Notificaciones push (opcional) - [ ] Exportar reportes de puntualidad (opcional) --- ## 📝 Notas Técnicas ### Fuentes de Datos Phase 2 - **GTFS Static**: https://data.renfe.com/dataset/horarios-trenes-largo-recorrido-ave/resource/horarios-trenes-largo-recorrido-ave-gtfs.zip - **Trip Updates**: https://gtfsrt.renfe.com/trip_updates.pb - **Service Alerts**: https://gtfsrt.renfe.com/service_alerts.pb ### Frecuencias - **GTFS Static Sync**: Diariamente a las 3 AM (configurable via `SYNC_SCHEDULE`) - **Trip Updates**: Cada 30 segundos - **Service Alerts**: Cada 30 segundos ### Retención de Datos - **Trip Updates**: 7 días (usar función `cleanup_old_trip_updates()`) - **Alerts**: Se mantienen hasta `end_time` + 7 días - **GTFS Static**: Se sobrescribe en cada sync --- ## 📚 Documentación Relacionada - [Arquitectura Completa](arquitectura-sistema-tracking-trenes.md) - [Fase 1 - MVP](FASE1-MVP.md) - [Fuentes de Datos](FUENTES_DATOS.md) - [README Principal](README.md) --- **Estado**: Fase 2 - Backend Completo, Frontend Pendiente **Fecha**: 27 noviembre 2025 **Próxima Fase**: Frontend Phase 2 + Fase 3 (Analytics)