# VMS-Sailor · Brief para Claude Code · Parte 3 de 6 ## VMS-Sailor Runtime (a bordo del buque) > Esta parte detalla el Runtime. Asume que ya leíste Partes 1 y 2. --- ## 1. Propósito El Runtime es lo que se instala en el PC industrial del buque del cliente. Opera 24/7. Es lo que el armador, capitán, jefe de máquinas y tripulación usan día a día. Dos componentes principales: - **Servidor**: servicio Windows con motor de tiempo real, drivers, alarm engine, etc. - **Cliente desktop**: aplicación PySide6 en cada estación de operación (puente, máquinas, eventualmente camarote del owner) --- ## 2. Estructura de carpetas del Runtime ``` vmssailor/runtime/ ├── __init__.py │ ├── server/ # SERVIDOR (servicio Windows) │ ├── __init__.py │ ├── service.py # Servicio Windows (pywin32) │ ├── main_loop.py # Loop asíncrono principal │ │ │ ├── drivers/ # Drivers de protocolo │ │ ├── __init__.py │ │ ├── base_driver.py │ │ ├── modbus_tcp.py │ │ ├── modbus_rtu.py # Bus principal AR-NMEA-IO │ │ ├── nmea2000.py # CAN/NMEA 2000 (publicar y suscribir) │ │ ├── j1939.py # Para motores que hablan J1939 nativo │ │ └── card_discovery.py # Auto-descubrimiento plug-and-produce │ │ │ ├── tag_db/ # Base de tags │ │ ├── __init__.py │ │ ├── tag_store.py # En memoria + persistencia periódica │ │ ├── tag_resolver.py # Resuelve id → valor, calidad, timestamp │ │ └── historian.py # DuckDB con series temporales │ │ │ ├── alarm_engine/ │ │ ├── __init__.py │ │ ├── alarm_evaluator.py # Evalúa límites contra tags │ │ ├── alarm_priorities.py # Emergency / High / Low / Info │ │ ├── alarm_state.py # Active / Ack / Cleared │ │ ├── acknowledgement.py │ │ ├── escalation.py # Escala si no se ack en X tiempo │ │ └── notification_dispatcher.py # A clientes desktop y móvil │ │ │ ├── permissive_engine/ # Pre-condiciones para acciones │ │ ├── __init__.py │ │ ├── permissive_evaluator.py │ │ ├── condition_parser.py # Lee reglas YAML del paquete │ │ └── override_handler.py # Override consciente del Admin del buque │ │ │ ├── authority/ # Command Authority │ │ ├── __init__.py │ │ ├── authority_manager.py │ │ ├── transfer.py # Bilateral handshake puente↔máquinas │ │ └── override.py # Override de emergencia │ │ │ ├── stability/ # Protección de escora (Parte 1 sec 8) │ │ ├── __init__.py │ │ ├── attitude_reader.py # Lee PGN 127257 del backbone NMEA 2000 │ │ ├── roll_monitor.py # Filtros + detección sostenida │ │ ├── safety_levels.py # Lógica niveles 1/2/3 │ │ ├── envelope_predictor.py # Predice efecto de comando antes de ejecutar │ │ ├── auto_corrector.py # Corrección suave hacia neutral │ │ ├── emergency_reset.py # Reset físico + virtual │ │ └── owner_override.py # Modo manual con safety envelope │ │ │ ├── config/ # Layer Config Engine (capas) │ │ ├── __init__.py │ │ ├── layer_loader.py # Carga capas en orden │ │ ├── delta_applier.py # Aplica deltas firmados │ │ ├── snapshot_manager.py # Snapshots automáticos │ │ ├── rollback_engine.py # Rollback granular │ │ ├── schema_validator.py │ │ └── signature_verifier.py # Verifica firma de Álvaro │ │ │ ├── logbook/ # Log Book naval (Parte 1 sec 6) │ │ ├── __init__.py │ │ ├── engine_log_writer.py # Auto-detección arranque/parada motores │ │ ├── alarm_log_writer.py │ │ ├── manual_entry_handler.py │ │ ├── snapshot_periodic.py # Snapshots cada 15 min con motor corriendo │ │ ├── log_signer.py # Firma digital inmutable │ │ └── log_exporter.py # Exporta a PDF formato oficial │ │ │ ├── licensing/ # Activación HWID │ │ ├── __init__.py │ │ ├── hwid_generator.py # MAC + Disk Serial + UUID │ │ ├── activator.py # Activación online inicial │ │ ├── verifier.py # Verifica firma del paquete vs HWID │ │ └── license_file.py │ │ │ ├── vpn/ # Soporte VPN administrativa │ │ ├── __init__.py │ │ └── wg_helper.py # Helpers para WireGuard externo │ │ │ ├── audit/ # Auditoría │ │ ├── __init__.py │ │ ├── audit_log.py # Eventos auditados inmutables │ │ └── vpn_session_tracker.py # Registra cada sesión VPN de Álvaro │ │ │ ├── telemetry/ # Telemetría de salud técnica │ │ ├── __init__.py │ │ ├── health_reporter.py │ │ └── client_transparency.py # Lo que se envía es visible al owner │ │ │ └── api/ │ ├── __init__.py │ ├── fastapi_app.py │ ├── ws_endpoints.py # WebSockets tiempo real │ ├── rest_endpoints.py # REST configuración + históricos │ ├── auth.py # JWT local + TOTP para móvil │ └── mobile_endpoints.py # Endpoints específicos Mobile │ ├── client/ # CLIENTE DESKTOP (PySide6) │ ├── __init__.py │ ├── app.py │ ├── login.py # Login Operador/Técnico/Admin │ ├── main_window.py │ ├── ws_client.py # Cliente WebSocket │ │ │ ├── views/ # Vistas principales │ │ ├── overview.py # Panel general del buque │ │ ├── mimic_view.py # Mímico de un sistema │ │ ├── alarm_panel.py # Lista alarmas viva con ack │ │ ├── trends_panel.py # Gráficas tiempo real │ │ ├── logbook_view.py # Log book │ │ ├── audit_view.py # Auditoría (Admin solo) │ │ ├── support_view.py # Pestaña "Soporte y Auditoría" del Admin │ │ ├── trim_panel.py # Control trim y maniobra │ │ └── settings.py # Settings limitados del cliente │ │ │ └── widgets/ # Widgets reutilizables │ ├── system_sidebar.py # Menú lateral dinámico │ ├── tag_widget.py # Display individual con color/alarma │ ├── gauge.py │ ├── lamp.py # Indicador on/off │ ├── bargraph.py │ ├── valve.py # Símbolo válvula con estado │ ├── motor.py # Símbolo motor │ ├── pump.py │ ├── tank.py │ └── alarm_badge.py ``` --- ## 3. Motor de tiempo real (Server) ### Loop principal Servicio Windows arranca al boot. Main loop asíncrono con `asyncio`. Múltiples tareas concurrentes: - **Driver Modbus RTU** poolea las tarjetas AR-NMEA-IO en el bus (cada 100ms por defecto, configurable por tag) - **Driver NMEA 2000** suscribe a PGNs configurados, lee del bus CAN - **Tag store** recibe updates de drivers, mantiene en memoria valores actuales con timestamp y calidad - **Historian** guarda en DuckDB cada N segundos (configurable por tag, default 1s para críticos, 60s para no-críticos) - **Alarm engine** evalúa cambios de tags contra límites - **Permissive engine** evalúa cuando se solicita acción - **Stability monitor** lee PGN 127257 cada 100ms, monitorea roll/pitch - **API server** atiende WebSockets de clientes desktop y Mobile, REST para configuración - **VPN listener** atiende endpoints administrativos solo desde IP del túnel - **Health reporter** envía telemetría técnica si está habilitada ### Tag store Estructura en memoria optimizada: - Dict por `tag_id` → objeto `Tag` con: valor, calidad, timestamp, unidad SI, rango normal, alarmas configuradas, `controllable`, `control_mode`, `authority_required`, protocolo, dirección, escalado - Pub/Sub interno: cuando un valor cambia, se notifica a suscriptores (alarm engine, historian, API clients) - Calidad: GOOD, BAD, UNCERTAIN. Si un sensor da timeout 3 veces seguidas → calidad BAD, valor último conocido marcado como stale. ### Historian (DuckDB) DuckDB embebido en archivo `%PROGRAMDATA%/VMS-Sailor/historian.duckdb`. Esquema simple: ``` tag_id (string), timestamp (datetime), value (double), quality (enum) ``` Retención configurable por tipo de tag: - Tags críticos: alta resolución (1 muestra/s) durante 30 días, downsampled a 1/min después de 30 días, retenidos 5 años - Tags no críticos: 1 muestra/min durante 90 días, downsampled a 1/hora después - Eventos discretos (alarmas, comandos): retención completa, todos guardados ### Alarm engine Cada tag con alarmas configuradas se evalúa cuando cambia su valor: - Compara contra límites: `low_low`, `low`, `high`, `high_high` - Cada límite tiene prioridad: Emergency, High, Low, Info - Histéresis: tag no sale de alarma hasta cruzar el límite +/- N (configurable) - Retraso: tag no entra en alarma hasta que la condición persiste X segundos (anti-flicker) - Mensaje configurable por alarma - Estados: ACTIVE (no ack), ACK (ack pero condición persiste), CLEARED (condición resuelta) - Escalación: si una alarma Emergency lleva X minutos sin ack, escala (sonido más fuerte, notificación móvil push) ### Authority Manager (puente ↔ máquinas) Estado: cuál estación tiene autoridad actualmente (BRIDGE, ENGINE, NONE en blackout). Transferencia bilateral: 1. Estación A solicita autoridad 2. Estación B recibe notificación, debe confirmar o rechazar 3. Si confirma, autoridad cambia 4. Timeout: si B no responde en 30s, autoridad permanece en A Override de emergencia: botón E-stop físico cablea directo a tarjeta cerca del motor (permissive local). Anula cualquier autoridad. Detiene equipo críticamente. Registra evento crítico en logbook. ### Stability Monitor (detalle Parte 1 sec 8) Suscribe a PGN 127257 del backbone NMEA 2000 (que publica el AR-ECDIS). Lee roll/pitch cada 100ms. Aplica filtros (mediana 5 muestras + promedio 1s). Evalúa tres niveles: ```python async def stability_loop(): while running: attitude = await read_pgn_127257() # del backbone roll = filter(attitude.roll) pitch = filter(attitude.pitch) await level_1_check(roll, pitch) # WARNING await level_2_check(roll, pitch) # AUTO-OFFER await level_3_check(roll, pitch) # AUTO-RESET FORZADO await asyncio.sleep(0.1) ``` --- ## 4. Cliente desktop (PySide6) ### Login Tres perfiles fijos. Login con usuario + password (gestionados por el Admin del buque). En sprint posterior se puede agregar SSO o LDAP. ### Layout principal - Barra superior: nombre del buque, estado conexión servidor, alarmas activas (badge), autoridad actual, usuario logueado - Barra lateral: menú dinámico de sistemas (generado a partir del `.vmspack` activo). Solo aparecen los sistemas marcados en el wizard del Studio - Vista central: mímico del sistema seleccionado, o vista de overview por defecto - Barra inferior: ticker de alarmas, hora del sistema, estado VPN ### Vistas por sistema Cada sistema marcado tiene su propio mímico (SVG/JSON empaquetado en `.vmspack`). El mímico se renderiza con PySide6 + QGraphicsView. Los símbolos (motor, válvula, bomba, etc.) muestran: - Estado en color (verde = OK, amarillo = warning, rojo = alarma, gris = offline) - Valores en displays digitales sobre el símbolo - Click → menú contextual con acciones disponibles según permissives + autoridad ### Panel de alarmas Lista cronológica de alarmas activas y recientes: - Color por prioridad - Botón ACK por alarma - Filtros por sistema, prioridad, estado - Histórico completo accesible (DuckDB) ### Panel de trends Gráficas tiempo real (último 1 hora, 24 horas, 7 días). Hasta 8 tags simultáneos en mismo eje. Zoom y pan. Exportable a CSV. ### Panel de Trim y maniobra (caso de uso destacado) - Sliders visuales para cada actuador de trim (2 motores + 2 trim tabs típicamente) - Indicador en tiempo real de roll y pitch (desde PGN 127257) - Botón "Reset Emergencia" muy visible - Toggle "Modo Manual del Owner" (con PIN/biométrico) - Indicador visual del envelope de seguridad activo ### Vista de Soporte y Auditoría (solo Admin del buque) - Log de mis conexiones VPN: fecha, duración, endpoints accedidos, mensaje mío sobre qué hice - Configuración de telemetría: qué se envía, pausar/desactivar - Gestión de usuarios del Runtime - Gestión de dispositivos móviles enrolados (revocar si se pierde) - Historial de snapshots con botón rollback --- ## 5. API del servidor ### WebSockets (tiempo real) `ws://localhost:8765/realtime` — todos los clientes desktop y Mobile conectan aquí. Mensajes que envía el servidor: - `tag_update`: id, value, quality, timestamp - `alarm_event`: alarm_id, state, priority - `authority_change`: new_authority - `stability_event`: level, roll, pitch - `permissive_check_result`: action_id, status, reasons Mensajes que recibe del cliente: - `subscribe_tags`: lista de IDs para suscribirse - `request_action`: solicita ejecutar acción de control (genera permissive check) - `ack_alarm`: ack de alarma - `request_authority`: solicita autoridad - `commit_logbook_entry`: entrada manual al logbook ### REST (configuración y consultas) - `GET /tags` — lista de tags con configuración - `GET /tags/{id}/history?from=X&to=Y` — históricos - `GET /alarms?state=active|all` — alarmas - `GET /logbook?from=X&to=Y` — entradas logbook - `POST /logbook` — entrada manual (Técnico/Admin) - `GET /audit/vpn_sessions` — sesiones VPN históricas (Admin solo) - `POST /telemetry/pause` — owner pausa telemetría ### Endpoints administrativos VPN (acceso solo desde IP del túnel) - `POST /admin/upload_delta` — Álvaro sube un .vmsdelta firmado - `POST /admin/apply_delta/{id}` — aplica delta (owner debe haber aprobado en su UI antes) - `GET /admin/diagnostics` — diagnóstico del sistema - `POST /admin/rollback/{snapshot_id}` — rollback (registrado en auditoría) --- ## 6. Layer Config Engine Detalle de cómo se cargan y aplican las capas (Parte 1 sec 5): ```python async def boot_runtime(): config = empty_config() # Capa 1 base = load_vmspack(path_base) verify_signature(base, alvaro_pubkey) verify_hwid(base, current_hwid) config.apply(base) # Capa 2 if exists(commissioning_delta): config.apply(load_vmsdelta(commissioning_delta)) # Capa 3 if exists(owner_prefs_delta): config.apply(load_vmsdelta(owner_prefs_delta)) # Capa 4 — expansiones en orden cronológico for delta in sorted(list_expansion_deltas()): verify_signature(delta, alvaro_pubkey) config.apply(delta) # Configuración efectiva lista start_services(config) ``` Snapshots automáticos ANTES de aplicar cualquier delta. Rollback granular permite volver a cualquier snapshot (visible al Admin del buque). --- ## 7. Telemetría de salud (transparente al cliente) Solo se envía estado técnico, NO contenido operativo: ```json { "timestamp": "2027-03-15T14:32:00Z", "runtime_version": "1.4.2", "package_version": "v2.0", "cpu_pct": 23, "ram_mb": 458, "disk_free_gb": 145, "uptime_hours": 1284, "drivers": { "modbus_rtu": {"status": "ok", "errors_24h": 12}, "nmea2000": {"status": "ok", "errors_24h": 0} }, "tags_offline": ["TANK_FUEL_2.LEVEL"], "alarms_unack_critical": 0, "vpn_status": "available" } ``` El owner ve en su UI **exactamente** lo que se está enviando en tiempo real, puede pausar o desactivar. Si desactiva, yo solo doy soporte reactivo cuando él me llame. --- ## 8. Log Book naval (módulo básico para Sprint 5/6) Por decisión del Sprint, arrancamos con módulo básico — regulatorio completo viene después. **Lo que SÍ implementamos en Sprint 5/6:** - Auto-detección arranque/parada motores: cuando `Card_02.DO1` se energiza y luego `Card_02.RPM1 > 600` por 10s → entrada automática - Snapshots periódicos cada 15 min con motor corriendo: RPM, temp aceite, temp agua, presión aceite, voltaje batería, horas acumuladas - Auto-registro de alarmas con timestamp + ack - Auto-registro de eventos de seguridad (autoridad, override, escora, trim reset) - Entradas manuales del Técnico/Admin - Firma digital inmutable - Export CSV básico **Lo que dejamos para Sprint 13+:** - Formato PDF oficial según regulación IMO MARPOL - Oil Record Book separado - Garbage Record Book - Cumplimiento estricto MARPOL Annex I/V - Firma con certificado X.509 del capitán --- ## 9. Sprints relacionados con Runtime - **Sprint 4**: drivers Modbus RTU/TCP + tag_db + historian + alarm engine básico + API base - **Sprint 5**: driver NMEA 2000 + log book básico + simulador para test bench - **Sprint 6**: cliente desktop completo (estaciones puente y máquinas) + panel alarmas + trends - **Sprint 8**: Permissive engine + Authority transfer + Stability monitor (protección escora) - **Sprint 9-10**: Layer Config Engine + deltas + rollback + activación HWID + telemetría - **Sprint 11** (parte): endpoints específicos para Mobile Detalle completo en Parte 6. --- **Fin de Parte 3 de 6.** Próxima: Parte 4 — Hardware AR-NMEA-IO + Firmware ESP32.