Sprint 0 completo del producto VMS-Sailor (Vessel Management System integrado para buques 30-40m). Brief de referencia en VMS_Sailor_v2_Parte_*.md (intacto). Core (vmssailor.core, 95.17% coverage, 99 tests verde): - ShipCoord: sistema naval x_pp/y_cl/z_bl frozen - Vessel, Deck, Bulkhead - Equipment, EquipmentModel, Sensor, EquipmentSpec - Tag, AlarmConfig, TagBinding, Scaling - CardInstance, Bus, Topology con validacion 21 puntos I/O AR-NMEA-IO-v1.0 - Alarm, PermissiveRule, Condition - Project agregado raiz con validacion cross-entity - Persistencia portable .vmsproj (SQLite) con roundtrip verificable Biblioteca curada seed (vmssailor.library): - systems_catalog.json completo (catalogo maestro Parte 1 sec 7) - 2 vessels: Sunseeker 76, Ferretti 850 - 2 motores: MTU 12V 2000 M96, Volvo D13-900 - 1 genset: Northern Lights M65C13 - yacht_motor_planeo.yaml (reglas heuristicas) - TODO marcado data_source=seed_estimate - requiere validacion datasheets Tools: - vms-validate-library: CLI valida biblioteca completa - vms-generate-test-project: CLI demo + verificacion roundtrip persistencia Design System + 8 mockups HTML estaticos: - docs/design_system.md (paleta Deep Ocean, gradientes, typography, motion) - docs/brand/ (logo + variantes SVG) - docs/mockups/splash, studio_main, runtime_overview, runtime_mimic_fuel (P&ID animado), runtime_alarms, runtime_trim (panel estrella con horizonte artificial), mobile_overview, mobile_trim - docs/mockups/index.html (galeria) Firmware (Sprint 12+ implementacion): - firmware/ar_nmea_io_v1/src/config/pinout.h con macros GPIO Decisiones autonomas documentadas en docs/decisions_sprint0.md. Stack: Python 3.11 + uv + Pydantic v2 + SQLite stdlib + hatchling + pytest 9 + ruff + mypy. Sin PySide6, FastAPI, Flutter ni firmware funcional (entran en sprints siguientes). Criterio de aceptacion Sprint 0: cumplido. - uv sync: OK - pytest: 99/99 verde - cov vmssailor.core: 95.17% (objetivo >=80%) - ruff: clean - vms-validate-library: OK - vms-generate-test-project: INTEGRIDAD OK Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
18 KiB
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→ objetoTagcon: 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:
- Estación A solicita autoridad
- Estación B recibe notificación, debe confirmar o rechazar
- Si confirma, autoridad cambia
- 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:
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
.vmspackactivo). 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, timestampalarm_event: alarm_id, state, priorityauthority_change: new_authoritystability_event: level, roll, pitchpermissive_check_result: action_id, status, reasons
Mensajes que recibe del cliente:
subscribe_tags: lista de IDs para suscribirserequest_action: solicita ejecutar acción de control (genera permissive check)ack_alarm: ack de alarmarequest_authority: solicita autoridadcommit_logbook_entry: entrada manual al logbook
REST (configuración y consultas)
GET /tags— lista de tags con configuraciónGET /tags/{id}/history?from=X&to=Y— históricosGET /alarms?state=active|all— alarmasGET /logbook?from=X&to=Y— entradas logbookPOST /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 firmadoPOST /admin/apply_delta/{id}— aplica delta (owner debe haber aprobado en su UI antes)GET /admin/diagnostics— diagnóstico del sistemaPOST /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):
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:
{
"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.DO1se energiza y luegoCard_02.RPM1 > 600por 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.