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>
This commit is contained in:
2026-05-17 07:26:06 -04:00
commit deb04c9315
96 changed files with 15335 additions and 0 deletions
+359
View File
@@ -0,0 +1,359 @@
# VMS-Sailor · Brief para Claude Code · Parte 5 de 6
## VMS-Sailor Mobile (app nativa iOS y Android)
> Esta parte detalla la app móvil. Asume que ya leíste Partes 1-4.
---
## 1. Propósito
App nativa que permite al **Admin del buque** (owner), técnicos y tripulación autorizados ver y operar el VMS desde tablets y smartphones. Diseñada para uso a bordo, no remoto desde tierra.
### Casos de uso principales
- **Owner en cubierta o camarote**: revisar estado general del buque sin ir al puente
- **Owner durante navegación**: ajustar trim desde el flybridge mientras conduce
- **Técnico durante mantenimiento**: ver lecturas en tiempo real estando físicamente en sala de máquinas
- **Tripulación**: recibir notificaciones de alarmas en el teléfono cuando no están en estación fija
- **Owner en muelle**: revisar estado del buque amarrado desde su casa cercana (siempre que esté en WiFi local)
---
## 2. Restricciones críticas de seguridad
### Solo WiFi local del buque
La app **JAMÁS** se expone a internet. El servidor del Runtime escucha solo en la subred WiFi local (192.168.x.x típicamente). La app móvil descubre el servidor por mDNS/Bonjour o IP fija configurada al enrolar.
**Implicaciones:**
- No se requiere certificado SSL público (TLS interno con CA propia)
- Sin cuentas en la nube, sin datos saliendo del buque
- Si la WiFi del buque está apagada, la app no funciona
- Si el dueño quiere acceso remoto desde tierra → debe pasar por VPN del armador (instalación separada, no parte de este proyecto)
### Autenticación TOTP + biométrico
Doble factor obligatorio:
1. **Algo que sabes**: usuario + password
2. **Algo que tienes**: el dispositivo enrolado (TOTP de 30s)
3. **Algo que eres**: biométrico del dispositivo (FaceID, TouchID, huella Android) — antes de revelar el TOTP
### Enrollment controlado
Solo el Admin del buque (owner) puede autorizar nuevos dispositivos. Proceso:
1. Owner en su estación principal del Runtime → "Gestión de dispositivos móviles"
2. Crea nuevo dispositivo: nombre (ej: "iPhone Carlos"), usuario asociado
3. Sistema genera QR con secreto TOTP único
4. Usuario escanea con la app VMS-Sailor Mobile en su dispositivo
5. App pide configurar biométrico local
6. Dispositivo queda enrolado
7. Owner puede revocar en cualquier momento desde la estación
### Permissives respetados igual que estación fija
Los permissives se evalúan en el SERVIDOR, no en el cliente. Móvil y desktop son solo UIs. Si un permissive bloquea una acción, bloquea desde cualquier UI. No hay forma de saltarse permissives desde móvil.
---
## 3. Arquitectura técnica
### Stack tecnológico
| Componente | Tecnología |
|---|---|
| Framework | Flutter 3.x |
| Lenguaje | Dart 3.x |
| State management | Riverpod 2.x |
| Comunicación | WebSocket (web_socket_channel) + HTTP/REST (dio) |
| TOTP | `otp` package |
| Biométrico | `local_auth` |
| Almacenamiento seguro | `flutter_secure_storage` (Keychain iOS, Keystore Android) |
| Gráficas | `fl_chart` |
| SVG (mímicos) | `flutter_svg` |
| Notificaciones locales | `flutter_local_notifications` |
| Cliente HTTPS con CA propia | configurar en `dio` con `IOHttpClientAdapter` |
### Por qué Flutter (no React Native ni nativo puro)
- **Performance gráfico superior**: motor Skia/Impeller propio. Para mímicos con muchas lecturas refrescándose en tiempo real, esto es decisivo. RN sufre con UIs densas y actualizaciones rápidas
- **Un solo código para iOS + Android + posible escritorio**: a futuro se puede tener "VMS-Sailor Mobile Lite" como app de escritorio liviana con el mismo código
- **Coherencia visual**: misma UI en iPhone, iPad y Android. Flutter renderiza por sí mismo, no usa widgets nativos
- **Dart bien diseñado**: tipado fuerte, async/await nativo, null safety. Reduce bugs runtime en sistema crítico
- **Soporte enterprise**: Google + BMW + Toyota lo usan en industria
### Estructura de carpetas Flutter
```
mobile/
├── pubspec.yaml
├── README.md
├── android/
├── ios/
└── lib/
├── main.dart
├── app.dart # Material/Cupertino app
├── core/
│ ├── models/
│ │ ├── tag.dart
│ │ ├── alarm.dart
│ │ ├── system.dart
│ │ ├── permissive.dart
│ │ └── user.dart
│ ├── api/
│ │ ├── ws_client.dart # Cliente WebSocket
│ │ ├── rest_client.dart # Cliente REST
│ │ └── auth_client.dart # Auth + TOTP
│ ├── auth/
│ │ ├── totp_manager.dart
│ │ ├── biometric_gate.dart # local_auth wrapper
│ │ └── secure_storage.dart # secretos en Keychain/Keystore
│ ├── discovery/
│ │ └── mdns_finder.dart # Encuentra servidor en WiFi
│ └── theme/
│ ├── light_theme.dart
│ └── dark_theme.dart
├── features/
│ ├── enrollment/
│ │ ├── qr_scanner.dart # Escanea QR del Runtime
│ │ └── enrollment_flow.dart
│ ├── login/
│ │ ├── login_screen.dart
│ │ └── biometric_unlock.dart
│ ├── overview/
│ │ └── overview_screen.dart # Dashboard inicial
│ ├── systems/
│ │ ├── system_list.dart # Lista de sistemas activos
│ │ ├── mimic_view.dart # Mímico de un sistema
│ │ └── tag_detail.dart
│ ├── alarms/
│ │ ├── alarm_list.dart
│ │ ├── alarm_detail.dart
│ │ └── alarm_notifications.dart
│ ├── trends/
│ │ └── trends_screen.dart # Gráficas con fl_chart
│ ├── trim/
│ │ ├── trim_control_screen.dart # Panel destacado de trim
│ │ ├── trim_slider_widget.dart
│ │ ├── stability_indicator.dart # Roll/pitch en vivo
│ │ └── emergency_reset_button.dart
│ ├── logbook/
│ │ ├── logbook_view.dart
│ │ └── manual_entry.dart
│ └── settings/
│ └── settings_screen.dart
└── widgets/ # Widgets reutilizables
├── tag_card.dart
├── alarm_badge.dart
├── gauge_widget.dart
├── lamp_indicator.dart
├── bar_graph.dart
└── permissive_status.dart
```
---
## 4. Pantallas principales
### Login
- Detecta si hay servidor VMS-Sailor en la WiFi local (mDNS)
- Si no, pide IP manual
- Login con usuario + password
- Pide biométrico para revelar TOTP
- Verifica TOTP contra servidor
- Si OK, guarda token de sesión
### Overview (dashboard inicial)
Lo primero que ve el usuario después de loguear:
- Nombre del buque + estado general (verde/amarillo/rojo)
- Resumen: motores estado, gensets estado, tanques niveles, alarmas activas
- Roll/pitch actual (gauge visual)
- Acceso rápido a sistemas más usados
### Lista de sistemas (menú lateral o pestaña)
Generado dinámicamente desde el `.vmspack` activo. Solo muestra los sistemas que el integrador marcó en el wizard.
### Vista de mímico por sistema
Carga el SVG del mímico empaquetado en el `.vmspack`. Se renderiza con `flutter_svg`. Los elementos del mímico se actualizan con WebSocket en tiempo real:
- Cambios de color por estado
- Valores numéricos sobre los símbolos
- Tap en un elemento → muestra detalle del tag
### Panel de Trim y Maniobra (destacado)
Esta es la pantalla estrella para el owner. UI optimizada para uso con una mano mientras conduce:
- Sliders verticales grandes para cada actuador de trim (touch-friendly)
- Indicador horizontal de roll en vivo (animado)
- Indicador de pitch
- Botón grande rojo de "RESET EMERGENCIA"
- Toggle de "Modo Manual del Owner" (requiere biométrico para activar)
- Indicación clara del envelope activo (línea visual donde el sistema empieza a bloquear)
### Panel de alarmas
- Lista cronológica con color por prioridad
- Notificaciones push locales cuando entra alarma crítica (incluso con la app en background)
- Botón ACK por alarma (respeta permisos del rol)
- Filtros y búsqueda
### Trends
- Gráficas con fl_chart
- Múltiples tags simultáneos
- Zoom y pan
- Selector de rango temporal (1h, 24h, 7d)
### Log Book
- Lista de entradas cronológica
- Filtros por categoría (eventos motor, alarmas, manuales)
- Botón "Agregar entrada manual" (Técnico/Admin)
### Settings
- Cambiar password
- Cerrar sesión / desautorizar dispositivo
- Configuración de notificaciones
- Idioma
- Modo oscuro
---
## 5. Permisos por rol en la app
Mismos roles que en estación desktop, pero con restricciones adicionales por ser móvil:
| Acción | Operador | Técnico | Admin (owner) |
|---|---|---|---|
| Ver mímicos | ✓ | ✓ | ✓ |
| Ver alarmas | ✓ | ✓ | ✓ |
| ACK alarmas Info/Low | ✓ | ✓ | ✓ |
| ACK alarmas High/Emergency | ✗ | ✓ | ✓ |
| Ver trends | ✓ | ✓ | ✓ |
| Ver logbook | ✓ | ✓ | ✓ |
| Agregar entrada manual logbook | ✗ | ✓ | ✓ |
| Control de luces, A/A (no críticos) | ✗ | ✓ | ✓ |
| Control trim | ✗ | ✓ | ✓ |
| Activar modo manual del owner | ✗ | ✗ | ✓ |
| Reset emergencia | ✓ | ✓ | ✓ |
| Arrancar motores / thrusters | ✗ | ✗ | ✗ (solo estación fija) |
| Override de permissives WARNING | ✗ | ✗ | ✓ |
| Gestión de usuarios y dispositivos | ✗ | ✗ | ✓ |
**Regla de oro**: acciones de propulsión y maquinaria principal (arranque/parada motores, thrusters, parada de emergencia general) **solo desde estación fija puente o máquinas**, nunca desde móvil. Por seguridad.
---
## 6. Notificaciones push locales
Sin servicios externos (no Firebase, no APNs externos). La app mantiene WebSocket persistente con el Runtime. Cuando recibe evento crítico:
- Si la app está en foreground → notificación dentro de la app
- Si la app está en background → notificación local del sistema operativo (`flutter_local_notifications`)
- Sonido distintivo por prioridad
- Tocar la notificación → abre la app en el evento específico
Limitación: si el usuario fuerza-cierra la app, no llegan notificaciones. Documentar al owner.
---
## 7. Modo offline degradado
Si la conexión WebSocket cae:
- Banner persistente "Sin conexión al VMS"
- Sigue mostrando los últimos valores conocidos (con badge "desactualizado X minutos")
- Permite ver histórico cached
- No permite enviar comandos
- Intenta reconectar automáticamente cada 10s
---
## 8. Empaquetado y distribución
### iOS
- Build con Xcode
- Distribución vía TestFlight para clientes específicos (no App Store público inicialmente)
- Requiere cuenta Apple Developer ($99/año) — gasto del integrador (Álvaro)
- Cada armador recibe invitación TestFlight con el binario
### Android
- Build APK firmado
- Distribución directa por enlace (no Google Play inicialmente)
- O via Play Store con cuenta de desarrollador ($25 una vez) si se decide más adelante
**Por qué no stores públicas inicialmente:**
- Las stores requieren accesibilidad pública (cualquiera puede instalar)
- Eso introduce riesgo de seguridad (cualquiera podría intentar usar la app contra cualquier IP)
- Distribución privada (TestFlight + APK firmado) garantiza que solo clientes autorizados tienen acceso
---
## 9. Sprints relacionados con Mobile
- **Sprint 11**: VMS-Sailor Mobile completo
- Setup Flutter project
- Enrollment flow + QR scan
- Login + TOTP + biométrico
- WebSocket client + REST client
- Pantallas: Overview, Lista sistemas, Mímico simple
- Panel alarmas con notificaciones locales
- Panel Trim (caso destacado)
- Permisos por rol
- Build iOS + Android
- **Sprint posterior**: refinamientos, trends avanzados, modo offline robusto, logbook completo
---
## 10. Consideraciones de UI/UX móvil
### Touch targets
Mínimo 44×44 pt en iOS, 48×48 dp en Android. Los sliders de trim deben ser grandes para uso con guantes o manos mojadas.
### Lecturabilidad bajo sol
- Modo de pantalla "outdoor" con contraste aumentado
- Texto grande por defecto (configurable)
- Evitar colores pastel para datos críticos
### Orientación
- Tablets: soportar landscape y portrait
- Smartphones: portrait principalmente; landscape para mímicos complejos y trends
### Accesibilidad
- Soporte de screen readers (VoiceOver iOS, TalkBack Android)
- Tamaño de fuente respetando configuración del sistema
- Contraste WCAG AA mínimo
---
## 11. Privacidad y datos
La app NO recolecta:
- Datos de telemetría de uso
- Analítica
- Crash reports a servidores externos
La app guarda LOCALMENTE en almacenamiento seguro del dispositivo:
- Secreto TOTP (cifrado en Keychain/Keystore)
- Token de sesión
- Últimos valores cached (para modo offline)
- Configuración del usuario
Si el dispositivo se restablece o se borra la app → todo se pierde. El owner debe enrolar nuevamente.
---
**Fin de Parte 5 de 6.** Próxima: Parte 6 — Plan de Sprints completo.