deb04c9315
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>
360 lines
13 KiB
Markdown
360 lines
13 KiB
Markdown
# 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.
|