Files
AR-VMS-Seaman/VMS_Sailor_v2_Parte_05_Mobile.md
T
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

360 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.