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:
@@ -0,0 +1,359 @@
|
||||
"""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"
|
||||
Reference in New Issue
Block a user