354 lines
10 KiB
Plaintext
354 lines
10 KiB
Plaintext
|
|
# Sistema de Tracking de Trenes en Tiempo Real - España
|
||
|
|
|
||
|
|
## Descripción del Proyecto
|
||
|
|
|
||
|
|
Este es un sistema web full-stack para visualizar en tiempo real la posición de todos los trenes operados por Renfe en España, con capacidad de consultar histórico mediante un timeline slider.
|
||
|
|
|
||
|
|
## Stack Tecnológico
|
||
|
|
|
||
|
|
### Backend
|
||
|
|
- **Runtime**: Node.js 20+ (ES Modules)
|
||
|
|
- **API**: Express.js con WebSocket (Socket.io)
|
||
|
|
- **Base de Datos**: PostgreSQL 15 + PostGIS (datos geoespaciales)
|
||
|
|
- **Cache**: Redis 7
|
||
|
|
- **Parser GTFS-RT**: gtfs-realtime-bindings
|
||
|
|
- **Logs**: Pino
|
||
|
|
|
||
|
|
### Frontend
|
||
|
|
- **Framework**: React 18 + Vite
|
||
|
|
- **Mapa**: Leaflet.js + React-Leaflet
|
||
|
|
- **WebSocket**: Socket.io-client
|
||
|
|
- **Estilos**: CSS vanilla (sin frameworks CSS)
|
||
|
|
|
||
|
|
### Infraestructura
|
||
|
|
- **Contenedores**: Docker + Docker Compose
|
||
|
|
- **Reverse Proxy**: Nginx
|
||
|
|
- **Migraciones**: Flyway
|
||
|
|
- **Gestión**: Makefile para comandos comunes
|
||
|
|
|
||
|
|
## Arquitectura
|
||
|
|
|
||
|
|
```
|
||
|
|
┌─────────────┐
|
||
|
|
│ Frontend │ ← React + Leaflet + Socket.io
|
||
|
|
│ (Vite) │
|
||
|
|
└──────┬──────┘
|
||
|
|
│
|
||
|
|
┌───▼────┐
|
||
|
|
│ Nginx │ ← Reverse Proxy
|
||
|
|
└───┬────┘
|
||
|
|
│
|
||
|
|
┌──────▼──────┐
|
||
|
|
│ Backend │ ← Express + Socket.io
|
||
|
|
│ API │
|
||
|
|
└─┬─────────┬─┘
|
||
|
|
│ │
|
||
|
|
┌─▼─┐ ┌──▼───┐
|
||
|
|
│DB │ │Redis │
|
||
|
|
└───┘ └──────┘
|
||
|
|
▲
|
||
|
|
│
|
||
|
|
┌─┴──────┐
|
||
|
|
│ Worker │ ← Polling GTFS-RT cada 30seg
|
||
|
|
└────────┘
|
||
|
|
```
|
||
|
|
|
||
|
|
## Fuente de Datos Principal
|
||
|
|
|
||
|
|
- **URL**: https://gtfsrt.renfe.com/vehicle_positions.pb
|
||
|
|
- **Formato**: Protocol Buffer (GTFS Realtime)
|
||
|
|
- **Frecuencia**: Actualización cada 30 segundos
|
||
|
|
- **Contenido**: Posiciones GPS de trenes en tiempo real
|
||
|
|
|
||
|
|
## Estructura del Proyecto
|
||
|
|
|
||
|
|
```
|
||
|
|
trenes/
|
||
|
|
├── backend/ # Backend Node.js
|
||
|
|
│ ├── src/
|
||
|
|
│ │ ├── api/ # API REST + WebSocket
|
||
|
|
│ │ │ ├── routes/ # Endpoints (trains, routes, stations, stats)
|
||
|
|
│ │ │ └── server.js # Servidor principal
|
||
|
|
│ │ ├── worker/ # Workers de fondo
|
||
|
|
│ │ │ └── gtfs-poller.js # Polling GTFS-RT
|
||
|
|
│ │ ├── lib/ # Utilidades (db, redis, logger)
|
||
|
|
│ │ └── config/ # Configuración
|
||
|
|
│ ├── package.json
|
||
|
|
│ └── Dockerfile
|
||
|
|
│
|
||
|
|
├── frontend/ # Frontend React
|
||
|
|
│ ├── src/
|
||
|
|
│ │ ├── components/ # Componentes React
|
||
|
|
│ │ │ ├── TrainMap.jsx # Mapa Leaflet
|
||
|
|
│ │ │ ├── TrainInfo.jsx # Panel de info
|
||
|
|
│ │ │ └── Timeline.jsx # Timeline
|
||
|
|
│ │ ├── hooks/ # Custom hooks
|
||
|
|
│ │ │ └── useTrains.js # Hook WebSocket
|
||
|
|
│ │ ├── styles/ # CSS
|
||
|
|
│ │ ├── App.jsx # Componente principal
|
||
|
|
│ │ └── main.jsx # Entry point
|
||
|
|
│ ├── package.json
|
||
|
|
│ ├── vite.config.js
|
||
|
|
│ └── Dockerfile
|
||
|
|
│
|
||
|
|
├── database/
|
||
|
|
│ ├── init/ # Scripts de inicialización
|
||
|
|
│ └── migrations/ # Migraciones Flyway (V1, V2, V3, V4)
|
||
|
|
│
|
||
|
|
├── nginx/ # Configuración Nginx
|
||
|
|
│ ├── nginx.conf
|
||
|
|
│ └── conf.d/
|
||
|
|
│
|
||
|
|
├── docker-compose.yml # Orquestación de servicios
|
||
|
|
├── Makefile # Comandos simplificados
|
||
|
|
├── .env.example # Variables de entorno
|
||
|
|
└── README.md # Documentación principal
|
||
|
|
```
|
||
|
|
|
||
|
|
## Base de Datos
|
||
|
|
|
||
|
|
### Tablas Principales
|
||
|
|
|
||
|
|
1. **train_positions** (particionada por mes)
|
||
|
|
- Histórico de todas las posiciones GPS
|
||
|
|
- Incluye: lat/lon, velocidad, dirección, estado, timestamp
|
||
|
|
- Particiones: nov 2025 - mar 2027
|
||
|
|
|
||
|
|
2. **trains**
|
||
|
|
- Catálogo de trenes
|
||
|
|
- Estado activo/inactivo
|
||
|
|
|
||
|
|
3. **routes**
|
||
|
|
- Rutas/líneas (AVE, Cercanías, etc.)
|
||
|
|
|
||
|
|
4. **stations**
|
||
|
|
- Estaciones con coordenadas GPS
|
||
|
|
- Datos de accesibilidad y servicios
|
||
|
|
|
||
|
|
5. **alerts**
|
||
|
|
- Alertas e incidencias
|
||
|
|
|
||
|
|
### Vistas
|
||
|
|
|
||
|
|
- **current_train_positions**: Última posición de cada tren
|
||
|
|
- **active_trains**: Trenes activos en últimas 24h
|
||
|
|
|
||
|
|
### Funciones Útiles
|
||
|
|
|
||
|
|
- `get_train_path(train_id, from, to)`: Trayectoria de un tren
|
||
|
|
- `get_trains_in_area(minLat, minLon, maxLat, maxLon, time)`: Trenes en área
|
||
|
|
- `calculate_train_statistics(train_id, from, to)`: Estadísticas de viaje
|
||
|
|
- `cleanup_old_positions(days)`: Limpiar datos antiguos
|
||
|
|
- `create_next_partition()`: Crear siguiente partición
|
||
|
|
|
||
|
|
## Redis Cache
|
||
|
|
|
||
|
|
Estructura de claves:
|
||
|
|
```
|
||
|
|
trains:current:{train_id} → JSON con última posición (TTL 5min)
|
||
|
|
trains:active → SET con IDs de trenes activos
|
||
|
|
stats:last_update → Timestamp última actualización
|
||
|
|
```
|
||
|
|
|
||
|
|
## API Endpoints
|
||
|
|
|
||
|
|
### Trenes
|
||
|
|
- `GET /trains/current` - Todos los trenes activos
|
||
|
|
- `GET /trains/:id` - Info de tren específico
|
||
|
|
- `GET /trains/:id/history` - Histórico de posiciones
|
||
|
|
- `GET /trains/:id/path` - Trayectoria entre fechas
|
||
|
|
- `GET /trains/area` - Trenes en área geográfica
|
||
|
|
|
||
|
|
### Rutas
|
||
|
|
- `GET /routes` - Todas las rutas
|
||
|
|
- `GET /routes/:id` - Ruta específica
|
||
|
|
|
||
|
|
### Estaciones
|
||
|
|
- `GET /stations` - Todas las estaciones
|
||
|
|
- `GET /stations/:id` - Estación específica
|
||
|
|
|
||
|
|
### Estadísticas
|
||
|
|
- `GET /stats` - Estadísticas del sistema
|
||
|
|
- `GET /stats/train/:id` - Estadísticas de tren
|
||
|
|
|
||
|
|
## WebSocket Events
|
||
|
|
|
||
|
|
### Cliente → Servidor
|
||
|
|
- `subscribe:train` - Suscribirse a actualizaciones de un tren
|
||
|
|
- `unsubscribe:train` - Desuscribirse
|
||
|
|
|
||
|
|
### Servidor → Cliente
|
||
|
|
- `trains:update` - Actualización masiva (todos los trenes)
|
||
|
|
- `train:update` - Actualización individual (tren suscrito)
|
||
|
|
|
||
|
|
## Comandos Make Comunes
|
||
|
|
|
||
|
|
```bash
|
||
|
|
make help # Ver todos los comandos disponibles
|
||
|
|
make start # Iniciar servicios en producción
|
||
|
|
make stop # Detener servicios
|
||
|
|
make logs # Ver logs de todos los servicios
|
||
|
|
make logs-api # Ver logs del API
|
||
|
|
make logs-worker # Ver logs del worker
|
||
|
|
make migrate # Ejecutar migraciones
|
||
|
|
make psql # Conectar a PostgreSQL
|
||
|
|
make redis-cli # Conectar a Redis
|
||
|
|
make test-start # Iniciar entorno de testing
|
||
|
|
make backup-db # Crear backup de BD
|
||
|
|
make cleanup-old-data # Limpiar datos antiguos (>90 días)
|
||
|
|
```
|
||
|
|
|
||
|
|
## Flujo de Datos
|
||
|
|
|
||
|
|
1. **Worker** hace polling a GTFS-RT cada 30 segundos
|
||
|
|
2. **Worker** parsea Protocol Buffer y extrae posiciones
|
||
|
|
3. **Worker** almacena en PostgreSQL (histórico) y Redis (cache)
|
||
|
|
4. **API** lee de Redis para requests y WebSocket
|
||
|
|
5. **WebSocket** broadcast a clientes cada 2 segundos
|
||
|
|
6. **Frontend** recibe updates y actualiza mapa en tiempo real
|
||
|
|
|
||
|
|
## Variables de Entorno Importantes
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# API
|
||
|
|
PORT=3000
|
||
|
|
NODE_ENV=development
|
||
|
|
|
||
|
|
# Database
|
||
|
|
DATABASE_URL=postgresql://user:pass@host:5432/db
|
||
|
|
|
||
|
|
# Redis
|
||
|
|
REDIS_URL=redis://:pass@host:6379
|
||
|
|
|
||
|
|
# GTFS-RT
|
||
|
|
GTFS_RT_URL=https://gtfsrt.renfe.com/vehicle_positions.pb
|
||
|
|
POLLING_INTERVAL=30000
|
||
|
|
|
||
|
|
# CORS
|
||
|
|
CORS_ORIGIN=http://localhost:3000,http://localhost:5173
|
||
|
|
|
||
|
|
# Frontend
|
||
|
|
VITE_API_URL=http://localhost/api
|
||
|
|
VITE_WS_URL=ws://localhost/ws
|
||
|
|
```
|
||
|
|
|
||
|
|
## Estado Actual del Proyecto
|
||
|
|
|
||
|
|
### ✅ Fase 1: MVP (COMPLETADA)
|
||
|
|
- [x] Arquitectura Docker completa
|
||
|
|
- [x] Worker GTFS-RT Vehicle Positions
|
||
|
|
- [x] API REST core
|
||
|
|
- [x] WebSocket server
|
||
|
|
- [x] Frontend React con mapa Leaflet
|
||
|
|
- [x] Panel de información de tren
|
||
|
|
- [x] Timeline básico (UI, funcionalidad pendiente)
|
||
|
|
|
||
|
|
### ⬜ Fase 2: Enriquecimiento (SIGUIENTE)
|
||
|
|
- [ ] Integración GTFS Static (rutas, horarios)
|
||
|
|
- [ ] Trip Updates (retrasos, cancelaciones)
|
||
|
|
- [ ] Service Alerts (incidencias)
|
||
|
|
- [ ] Monitor de puntualidad
|
||
|
|
- [ ] Timeline funcional con histórico
|
||
|
|
|
||
|
|
## Documentación Relevante
|
||
|
|
|
||
|
|
- **Arquitectura completa**: [arquitectura-sistema-tracking-trenes.md](arquitectura-sistema-tracking-trenes.md)
|
||
|
|
- **Fuentes de datos**: [FUENTES_DATOS.md](FUENTES_DATOS.md)
|
||
|
|
- **Fase 1 MVP**: [FASE1-MVP.md](FASE1-MVP.md)
|
||
|
|
- **README principal**: [README.md](README.md)
|
||
|
|
|
||
|
|
## Tareas Comunes
|
||
|
|
|
||
|
|
### Añadir un nuevo endpoint
|
||
|
|
1. Crear ruta en `backend/src/api/routes/`
|
||
|
|
2. Importar y usar en `backend/src/api/server.js`
|
||
|
|
3. Documentar en README
|
||
|
|
|
||
|
|
### Añadir una nueva vista en BD
|
||
|
|
1. Crear migración en `database/migrations/V{N}__descripcion.sql`
|
||
|
|
2. Ejecutar `make migrate`
|
||
|
|
3. Documentar en arquitectura
|
||
|
|
|
||
|
|
### Añadir un componente React
|
||
|
|
1. Crear en `frontend/src/components/`
|
||
|
|
2. Importar en `App.jsx` o componente padre
|
||
|
|
3. Añadir estilos en `styles/index.css`
|
||
|
|
|
||
|
|
### Añadir una nueva fuente de datos
|
||
|
|
1. Crear worker en `backend/src/worker/`
|
||
|
|
2. Parsear y almacenar datos
|
||
|
|
3. Actualizar `docker-compose.yml` si es necesario
|
||
|
|
4. Documentar en FUENTES_DATOS.md
|
||
|
|
|
||
|
|
## Debugging
|
||
|
|
|
||
|
|
### Ver logs en tiempo real
|
||
|
|
```bash
|
||
|
|
# Todos los servicios
|
||
|
|
make logs
|
||
|
|
|
||
|
|
# Servicio específico
|
||
|
|
docker-compose logs -f worker
|
||
|
|
docker-compose logs -f api
|
||
|
|
docker-compose logs -f postgres
|
||
|
|
```
|
||
|
|
|
||
|
|
### Inspeccionar base de datos
|
||
|
|
```bash
|
||
|
|
# Conectar a PostgreSQL
|
||
|
|
make psql
|
||
|
|
|
||
|
|
# Ver últimas posiciones
|
||
|
|
SELECT * FROM train_positions ORDER BY recorded_at DESC LIMIT 10;
|
||
|
|
|
||
|
|
# Contar trenes activos
|
||
|
|
SELECT COUNT(*) FROM trains WHERE is_active = true;
|
||
|
|
```
|
||
|
|
|
||
|
|
### Inspeccionar Redis
|
||
|
|
```bash
|
||
|
|
# Conectar a Redis
|
||
|
|
make redis-cli
|
||
|
|
|
||
|
|
# Ver trenes activos
|
||
|
|
SMEMBERS trains:active
|
||
|
|
|
||
|
|
# Ver posición de un tren
|
||
|
|
GET trains:current:TRAIN_ID
|
||
|
|
```
|
||
|
|
|
||
|
|
## Notas de Desarrollo
|
||
|
|
|
||
|
|
### Convenciones de Código
|
||
|
|
|
||
|
|
- **Backend**: ES Modules, camelCase para variables, PascalCase para clases
|
||
|
|
- **Frontend**: React funcional (hooks), componentes PascalCase
|
||
|
|
- **SQL**: snake_case para tablas y columnas, UPPERCASE para SQL keywords
|
||
|
|
- **Git**: Commits descriptivos en español
|
||
|
|
|
||
|
|
### Testing
|
||
|
|
|
||
|
|
- Backend: Tests con Jest (pendiente)
|
||
|
|
- Frontend: Tests con React Testing Library (pendiente)
|
||
|
|
- E2E: Cypress (pendiente)
|
||
|
|
|
||
|
|
### Performance
|
||
|
|
|
||
|
|
- **Particiones**: Crear nuevas particiones mensualmente
|
||
|
|
- **Cleanup**: Ejecutar `cleanup_old_positions()` mensualmente
|
||
|
|
- **Cache**: Redis TTL configurado a 5 minutos
|
||
|
|
- **Pooling**: PostgreSQL pool size 2-10
|
||
|
|
|
||
|
|
## Contacto y Recursos
|
||
|
|
|
||
|
|
- **GTFS Spec**: https://gtfs.org/documentation/realtime/
|
||
|
|
- **Renfe Data**: https://data.renfe.com/
|
||
|
|
- **PostGIS Docs**: https://postgis.net/
|
||
|
|
- **Leaflet Docs**: https://leafletjs.com/
|
||
|
|
- **Socket.io Docs**: https://socket.io/
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
**Última actualización**: 27 noviembre 2025
|
||
|
|
**Versión**: 1.0.0 (Fase 1 MVP)
|