Files
AR-VMS-Seaman/VMS_Sailor_v2_Parte_03_Runtime.md
alro65 deb04c9315 sprint-0: fundaciones VMS-Sailor
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>
2026-05-17 07:26:06 -04:00

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 → 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:

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)

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):

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.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.