Files
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
9.8 KiB
Python

"""Enums del modelo de datos core.
Toda enumeración del producto vive aquí para tener una única fuente de
verdad y poder serializarla consistentemente a `.vmsproj` y `.vmspack`.
Convención de valores: snake_case_minúsculas ASCII para que sean estables
en JSON/YAML/SQLite sin problemas de encoding.
"""
from __future__ import annotations
from enum import StrEnum
# ---------------------------------------------------------------------------
# Buque
# ---------------------------------------------------------------------------
class VesselType(StrEnum):
"""Categoría principal del buque (Parte 2 sec 3, Paso 1)."""
YACHT_MOTOR = "yacht_motor"
YACHT_SAIL = "yacht_sail"
FISHING = "fishing"
PATROL = "patrol"
FERRY = "ferry"
OFFSHORE_SUPPORT = "offshore_support"
class VesselSubtype(StrEnum):
"""Subcategoría que afina el tipo principal."""
# Yacht motor
PLANING = "planing"
SEMI_PLANING = "semi_planing"
DISPLACEMENT = "displacement"
# Fishing
PURSE_SEINER = "purse_seiner" # cerquero
TRAWLER = "trawler" # arrastrero
LONGLINER = "longliner" # palangrero
# Patrol
COASTAL = "coastal"
OCEANIC = "oceanic"
# Ferry
PASSENGER = "passenger"
ROLL_ON_ROLL_OFF = "ro_ro"
# Offshore support
AHTS = "ahts" # Anchor Handling Tug Supply
PSV = "psv" # Platform Supply Vessel
# Catch-all
OTHER = "other"
# ---------------------------------------------------------------------------
# Catálogo maestro de sistemas (Parte 1 sec 7)
# ---------------------------------------------------------------------------
class SystemId(StrEnum):
"""ID estable de cada sistema del catálogo maestro.
Coincide con `vmssailor/library/systems_catalog.json`. El menú lateral del
Runtime se genera a partir de los sistemas que el proyecto tenga
habilitados.
"""
# Propulsión y maquinaria
MAIN_ENGINE = "main_engine"
TRANSMISSION = "transmission"
SHAFT_PROPELLER = "shaft_propeller"
THRUSTER = "thruster"
# Maniobra y trimado
TRIM_STERNDRIVE = "trim_sterndrive"
TRIM_TABS = "trim_tabs"
CPP = "cpp"
GYROSTABILIZER = "gyrostabilizer"
FIN_STABILIZER = "fin_stabilizer"
JOYSTICK_DOCKING = "joystick_docking"
# Generación eléctrica
GENSET = "genset"
SHORE_POWER = "shore_power"
INVERTER_CHARGER = "inverter_charger"
BATTERY_BANK = "battery_bank"
MSB = "msb"
ESB = "esb"
UPS = "ups"
SOLAR = "solar"
SMART_DC_BUSBAR = "smart_dc_busbar"
SMART_PANEL = "smart_panel"
# Aislamiento eléctrico
SECTIONALIZING = "sectionalizing"
EMERGENCY_ISOLATION = "emergency_isolation"
BREAKERS = "breakers"
LOCKOUT_TAGOUT = "lockout_tagout"
# Fluidos
FUEL = "fuel"
LUBE_OIL = "lube_oil"
HYDRAULIC_OIL = "hydraulic_oil"
FW_COOLING = "fw_cooling"
SW_COOLING = "sw_cooling"
STARTING_AIR = "starting_air"
BILGE = "bilge"
BALLAST = "ballast"
GREY_WATER = "grey_water"
BLACK_WATER = "black_water"
POTABLE_WATER = "potable_water"
SW_SERVICE = "sw_service"
WATERMAKER = "watermaker"
# Seguridad
FIRE_DETECTION = "fire_detection"
FIRE_EXTINGUISHING = "fire_extinguishing"
FIFI_EXTERNAL = "fifi_external"
EMERGENCY_BILGE = "emergency_bilge"
GAS_DETECTION = "gas_detection"
MOB = "mob"
# Ambiente
HVAC = "hvac"
ENGINE_VENT = "engine_vent"
HEATING = "heating"
REFRIGERATION = "refrigeration"
# Iluminación
NAV_LIGHTS = "nav_lights"
DECK_LIGHTS = "deck_lights"
INTERIOR_LIGHTS = "interior_lights"
EMERGENCY_LIGHTS = "emergency_lights"
SEARCHLIGHTS = "searchlights"
# Tanques estructurales
FUEL_TANKS = "fuel_tanks"
WATER_TANKS = "water_tanks"
GREY_BLACK_TANKS = "grey_black_tanks"
VOIDS = "voids"
COFFERDAMS = "cofferdams"
# Cubierta y maniobra
WINDLASS = "windlass"
ANCHOR_SYSTEM = "anchor_system"
MOORING = "mooring"
DAVITS = "davits"
GANGWAY = "gangway"
CRANE = "crane"
# Específicos por tipo
FISHING_MACHINERY = "fishing_machinery"
LARGE_FRIDGE_HOLDS = "large_fridge_holds"
ROV = "rov"
DIVING_SYSTEM = "diving_system"
# ---------------------------------------------------------------------------
# Equipos
# ---------------------------------------------------------------------------
class EquipmentCategory(StrEnum):
"""Categoría de un EquipmentModel para clasificar la biblioteca."""
ENGINE_MAIN = "engine_main"
GENSET = "genset"
PUMP = "pump"
VALVE = "valve"
TANK = "tank"
HEAT_EXCHANGER = "heat_exchanger"
FILTER_SEPARATOR = "filter_separator"
COMPRESSOR = "compressor"
SENSOR = "sensor"
INDICATOR = "indicator"
BREAKER = "breaker"
INVERTER = "inverter"
BATTERY = "battery"
THRUSTER = "thruster"
STABILIZER = "stabilizer"
WATERMAKER = "watermaker"
LIGHTING = "lighting"
HVAC_UNIT = "hvac_unit"
OTHER = "other"
# ---------------------------------------------------------------------------
# Señales físicas y canales de tarjeta
# ---------------------------------------------------------------------------
class ChannelType(StrEnum):
"""Tipo de canal físico en la tarjeta AR-NMEA-IO-v1.0."""
AI = "ai" # Analog Input (4 por tarjeta)
DI = "di" # Digital Input (5 por tarjeta)
DO = "do" # Digital Output (10 por tarjeta)
RPM = "rpm" # Frequency input (1 por tarjeta)
class SignalType(StrEnum):
"""Tipo eléctrico de la señal conectada a un canal."""
# Analógicas
SIG_4_20_MA = "4-20ma"
SIG_0_10_V = "0-10v"
SIG_0_5_V = "0-5v"
RTD_PT100 = "rtd_pt100"
RTD_PT1000 = "rtd_pt1000"
THERMOCOUPLE_K = "thermocouple_k"
THERMOCOUPLE_J = "thermocouple_j"
RESISTIVE_SENDER = "resistive_sender" # tank sender, etc.
VOLTAGE_DIVIDER = "voltage_divider" # batería con divisor
# Digitales / discretas
DRY_CONTACT = "dry_contact"
CONTACT_24VDC = "contact_24vdc"
RELAY_NO = "relay_no"
RELAY_NC = "relay_nc"
# Frecuencia
PULSE_MAGNETIC_PICKUP = "pulse_magnetic_pickup"
PULSE_INDUCTIVE = "pulse_inductive"
PULSE_TACHO = "pulse_tacho"
# ---------------------------------------------------------------------------
# Protocolos y buses
# ---------------------------------------------------------------------------
class Protocol(StrEnum):
"""Protocolo por el que se accede a un tag."""
MODBUS_RTU = "modbus_rtu"
MODBUS_TCP = "modbus_tcp"
NMEA2000 = "nmea2000"
J1939 = "j1939"
INTERNAL = "internal" # tags virtuales calculados, no van al bus
class BusRole(StrEnum):
"""Rol de una tarjeta en su bus (Parte 2 sec 3, Paso 7)."""
MODBUS_SLAVE = "modbus_slave"
MODBUS_MASTER = "modbus_master"
NMEA2000_NODE = "nmea2000_node"
DUAL = "dual" # Modbus + NMEA 2000 simultáneo
BRIDGE = "bridge" # Lee NMEA 2000, expone como Modbus
# ---------------------------------------------------------------------------
# Filtros locales (firmware tarjeta)
# ---------------------------------------------------------------------------
class FilterType(StrEnum):
"""Filtro local que aplica la tarjeta antes de reportar (Parte 4 sec 7)."""
NONE = "none"
MOVING_AVG = "moving_avg"
MEDIAN = "median"
DEADBAND = "deadband"
RATE_LIMIT = "rate_limit"
# ---------------------------------------------------------------------------
# Tags: control, autoridad, calidad
# ---------------------------------------------------------------------------
class ControlMode(StrEnum):
"""Estado del tag respecto a control.
Filosofía monitor-now / control-later: todos los tags se definen con
capacidad de control desde el día 1 aunque empiecen como MONITOR.
"""
MONITOR = "monitor"
MANUAL = "manual"
AUTO = "auto"
FUTURE = "future" # actuador físico aún no instalado
class AuthorityRequired(StrEnum):
"""Qué estación debe tener autoridad para ejecutar el control."""
BRIDGE = "bridge"
ENGINE = "engine"
EITHER = "either"
class Quality(StrEnum):
"""Calidad del valor leído (OPC UA estilo)."""
GOOD = "good"
BAD = "bad"
UNCERTAIN = "uncertain"
STALE = "stale" # último conocido pero sensor offline
# ---------------------------------------------------------------------------
# Alarmas
# ---------------------------------------------------------------------------
class AlarmPriority(StrEnum):
"""Prioridad de alarma (Parte 3 sec 3, Parte 1 sec 6)."""
EMERGENCY = "emergency"
HIGH = "high"
LOW = "low"
INFO = "info"
class AlarmState(StrEnum):
"""Estado de una alarma activa."""
ACTIVE = "active" # disparada, no ack
ACK = "ack" # ack pero condición persiste
CLEARED = "cleared" # condición resuelta
# ---------------------------------------------------------------------------
# Unidades SI obligatorias internas
# ---------------------------------------------------------------------------
class UnitSI(StrEnum):
"""Lista cerrada de unidades SI permitidas en `Tag.unit_si` / `Sensor.unit_si`.
Regla de oro: todo internamente en SI. Conversión a imperial solo en UI.
"""
# Sin unidad / boolean
NONE = "none"
BOOL = "bool"
PERCENT = "%"
# Eléctricas
VOLT = "V"
AMPERE = "A"
WATT = "W"
KILOWATT = "kW"
KILOWATT_HOUR = "kWh"
HERTZ = "Hz"
OHM = "ohm"
# Mecánicas
RPM = "rpm"
NEWTON_METER = "Nm"
METER = "m"
METER_PER_SECOND = "m/s"
METER_PER_SECOND_SQ = "m/s2"
DEGREE = "deg"
DEGREE_PER_SECOND = "deg/s"
# Fluidos
PASCAL = "Pa"
KILOPASCAL = "kPa"
BAR = "bar"
LITER = "L"
CUBIC_METER = "m3"
LITER_PER_HOUR = "L/h"
LITER_PER_MINUTE = "L/min"
CUBIC_METER_PER_HOUR = "m3/h"
# Térmicas
DEGREE_CELSIUS = "C"
KELVIN = "K"
# Tiempo
SECOND = "s"
HOUR = "h"
# Masa
KILOGRAM = "kg"
TONNE = "t"