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
+29
View File
@@ -0,0 +1,29 @@
"""vmssailor.library — Biblioteca curada (Sprint 0).
Contiene:
- `systems_catalog.json` — catálogo maestro de sistemas instalables
- `vessels/` — plantillas de buques (Sunseeker 76, Ferretti 850, ...)
- `equipment/` — modelos de equipos (motores, gensets, bombas, ...)
- `rules/` — reglas heurísticas YAML por tipo de buque
- `loader.py` — carga y validación de toda la biblioteca
**REGLA DE ORO #7:** La biblioteca curada es ORO. Cambios de formato
requieren migración para los proyectos existentes.
**REGLA DE ORO #5:** Esta biblioteca pertenece a Álvaro (propiedad
intelectual). El cliente final nunca la ve cruda — sólo el resultado de
aplicarla en el .vmspack.
"""
from pathlib import Path
LIBRARY_ROOT: Path = Path(__file__).parent
"""Raíz del directorio de biblioteca curada (paquete instalado o source)."""
from vmssailor.library.loader import ( # noqa: E402
LibraryLoadResult,
load_library,
load_systems_catalog,
)
__all__ = ["LIBRARY_ROOT", "LibraryLoadResult", "load_library", "load_systems_catalog"]
@@ -0,0 +1,130 @@
{
"id": "mtu_12v_2000_m96",
"manufacturer": "MTU",
"model_name": "12V 2000 M96",
"category": "engine_main",
"typical_systems": ["main_engine"],
"specs": {
"power_kw": 1432,
"rpm_nominal": 2450,
"weight_kg": 2600,
"length_m": 2.13,
"width_m": 1.18,
"height_m": 1.27,
"fuel_consumption_lph": 320
},
"description": "MTU Series 2000, 12 cilindros en V, 24.0 L, 2-stage turbo. Aplicación yates rápidos / patrulleros / fast ferries. Common Rail. Habla J1939 nativo en su MCU.",
"data_source": "seed_estimate",
"default_sensors": [
{
"id": "rpm",
"name": "RPM",
"unit_si": "rpm",
"range_normal_min": 0,
"range_normal_max": 2600,
"alarm_high_value": 2550,
"alarm_high_priority": "high",
"default_signal_type": "pulse_magnetic_pickup",
"description": "Régimen del cigüeñal."
},
{
"id": "oil_press",
"name": "Presión de aceite",
"unit_si": "bar",
"range_normal_min": 3.5,
"range_normal_max": 6.5,
"alarm_low_value": 1.5,
"alarm_low_priority": "emergency",
"default_signal_type": "4-20ma",
"description": "Presión aceite lubricante en galería principal."
},
{
"id": "oil_temp",
"name": "Temperatura de aceite",
"unit_si": "C",
"range_normal_min": 60,
"range_normal_max": 110,
"alarm_high_value": 120,
"alarm_high_priority": "high",
"default_signal_type": "rtd_pt100"
},
{
"id": "coolant_temp",
"name": "Temperatura refrigerante",
"unit_si": "C",
"range_normal_min": 65,
"range_normal_max": 95,
"alarm_high_value": 100,
"alarm_high_priority": "emergency",
"default_signal_type": "rtd_pt100"
},
{
"id": "boost_press",
"name": "Presión de sobrealimentación",
"unit_si": "bar",
"range_normal_min": 0.0,
"range_normal_max": 2.5,
"default_signal_type": "4-20ma"
},
{
"id": "fuel_press",
"name": "Presión de combustible",
"unit_si": "bar",
"range_normal_min": 3.0,
"range_normal_max": 6.0,
"alarm_low_value": 2.0,
"alarm_low_priority": "high",
"default_signal_type": "4-20ma"
},
{
"id": "alternator_v",
"name": "Voltaje alternador",
"unit_si": "V",
"range_normal_min": 27.0,
"range_normal_max": 29.0,
"alarm_low_value": 24.0,
"alarm_low_priority": "high",
"default_signal_type": "voltage_divider"
},
{
"id": "load_pct",
"name": "Carga del motor",
"unit_si": "%",
"range_normal_min": 0,
"range_normal_max": 100,
"default_signal_type": "4-20ma"
},
{
"id": "running_hours",
"name": "Horas totales",
"unit_si": "h",
"range_normal_min": 0,
"range_normal_max": 80000,
"default_signal_type": "4-20ma"
},
{
"id": "start_cmd",
"name": "Comando arranque",
"unit_si": "bool",
"default_signal_type": "relay_no"
},
{
"id": "stop_cmd",
"name": "Comando parada",
"unit_si": "bool",
"default_signal_type": "relay_no"
},
{
"id": "running_state",
"name": "Estado motor en marcha",
"unit_si": "bool",
"default_signal_type": "dry_contact"
},
{
"id": "estop_active",
"name": "E-stop activado",
"unit_si": "bool",
"default_signal_type": "dry_contact"
}
]
}
@@ -0,0 +1,112 @@
{
"id": "volvo_d13_900hp",
"manufacturer": "Volvo Penta",
"model_name": "D13-900",
"category": "engine_main",
"typical_systems": ["main_engine"],
"specs": {
"power_kw": 662,
"rpm_nominal": 2300,
"weight_kg": 1430,
"length_m": 1.69,
"width_m": 0.99,
"height_m": 1.16,
"fuel_consumption_lph": 160
},
"description": "Volvo Penta D13 inline-6, 12.8 L, common rail, twin-entry turbo. Aplicación yates motor 60-90 pies. Habla J1939 nativo.",
"data_source": "seed_estimate",
"default_sensors": [
{
"id": "rpm",
"name": "RPM",
"unit_si": "rpm",
"range_normal_min": 0,
"range_normal_max": 2400,
"alarm_high_value": 2350,
"alarm_high_priority": "high",
"default_signal_type": "pulse_magnetic_pickup"
},
{
"id": "oil_press",
"name": "Presión de aceite",
"unit_si": "bar",
"range_normal_min": 3.0,
"range_normal_max": 5.5,
"alarm_low_value": 1.5,
"alarm_low_priority": "emergency",
"default_signal_type": "4-20ma"
},
{
"id": "oil_temp",
"name": "Temperatura de aceite",
"unit_si": "C",
"range_normal_min": 60,
"range_normal_max": 110,
"alarm_high_value": 120,
"alarm_high_priority": "high",
"default_signal_type": "rtd_pt100"
},
{
"id": "coolant_temp",
"name": "Temperatura refrigerante",
"unit_si": "C",
"range_normal_min": 70,
"range_normal_max": 92,
"alarm_high_value": 98,
"alarm_high_priority": "emergency",
"default_signal_type": "rtd_pt100"
},
{
"id": "boost_press",
"name": "Presión de sobrealimentación",
"unit_si": "bar",
"range_normal_min": 0.0,
"range_normal_max": 2.2,
"default_signal_type": "4-20ma"
},
{
"id": "alternator_v",
"name": "Voltaje alternador",
"unit_si": "V",
"range_normal_min": 13.5,
"range_normal_max": 14.5,
"alarm_low_value": 12.0,
"alarm_low_priority": "high",
"default_signal_type": "voltage_divider"
},
{
"id": "load_pct",
"name": "Carga del motor",
"unit_si": "%",
"range_normal_min": 0,
"range_normal_max": 100,
"default_signal_type": "4-20ma"
},
{
"id": "running_hours",
"name": "Horas totales",
"unit_si": "h",
"range_normal_min": 0,
"range_normal_max": 80000,
"default_signal_type": "4-20ma"
},
{
"id": "start_cmd",
"name": "Comando arranque",
"unit_si": "bool",
"default_signal_type": "relay_no"
},
{
"id": "stop_cmd",
"name": "Comando parada",
"unit_si": "bool",
"default_signal_type": "relay_no"
},
{
"id": "running_state",
"name": "Estado motor en marcha",
"unit_si": "bool",
"default_signal_type": "dry_contact"
}
]
}
@@ -0,0 +1,114 @@
{
"id": "northern_lights_m65c13",
"manufacturer": "Northern Lights",
"model_name": "M65C13",
"category": "genset",
"typical_systems": ["genset"],
"specs": {
"power_kw": 52,
"rpm_nominal": 1800,
"weight_kg": 880,
"length_m": 1.55,
"width_m": 0.74,
"height_m": 0.96,
"voltage_v": 230,
"current_a": 226,
"fuel_consumption_lph": 16.3
},
"description": "Genset marino diésel Northern Lights M65C13 (Lugger), 65 kVA / 52 kW @ 1800 rpm 60 Hz (válido también a 50 Hz con curva diferente). Motor John Deere 4045 base. Aplicación yates motor 70-90 pies y patrulleros medianos.",
"data_source": "seed_estimate",
"default_sensors": [
{
"id": "rpm",
"name": "RPM",
"unit_si": "rpm",
"range_normal_min": 0,
"range_normal_max": 1850,
"default_signal_type": "pulse_magnetic_pickup"
},
{
"id": "oil_press",
"name": "Presión de aceite",
"unit_si": "bar",
"range_normal_min": 2.5,
"range_normal_max": 4.5,
"alarm_low_value": 1.0,
"alarm_low_priority": "emergency",
"default_signal_type": "4-20ma"
},
{
"id": "coolant_temp",
"name": "Temperatura refrigerante",
"unit_si": "C",
"range_normal_min": 70,
"range_normal_max": 95,
"alarm_high_value": 102,
"alarm_high_priority": "emergency",
"default_signal_type": "rtd_pt100"
},
{
"id": "voltage_l1",
"name": "Tensión L1",
"unit_si": "V",
"range_normal_min": 220,
"range_normal_max": 240,
"alarm_low_value": 200,
"alarm_low_priority": "high",
"alarm_high_value": 250,
"alarm_high_priority": "high",
"default_signal_type": "voltage_divider"
},
{
"id": "current_l1",
"name": "Corriente L1",
"unit_si": "A",
"range_normal_min": 0,
"range_normal_max": 230,
"alarm_high_value": 250,
"alarm_high_priority": "high",
"default_signal_type": "4-20ma"
},
{
"id": "freq",
"name": "Frecuencia",
"unit_si": "Hz",
"range_normal_min": 49.5,
"range_normal_max": 60.5,
"default_signal_type": "pulse_inductive"
},
{
"id": "load_pct",
"name": "Carga",
"unit_si": "%",
"range_normal_min": 0,
"range_normal_max": 100,
"default_signal_type": "4-20ma"
},
{
"id": "running_hours",
"name": "Horas totales",
"unit_si": "h",
"range_normal_min": 0,
"range_normal_max": 80000,
"default_signal_type": "4-20ma"
},
{
"id": "start_cmd",
"name": "Comando arranque",
"unit_si": "bool",
"default_signal_type": "relay_no"
},
{
"id": "stop_cmd",
"name": "Comando parada",
"unit_si": "bool",
"default_signal_type": "relay_no"
},
{
"id": "breaker_status",
"name": "Estado breaker principal",
"unit_si": "bool",
"default_signal_type": "dry_contact"
}
]
}
+214
View File
@@ -0,0 +1,214 @@
"""Carga y validación de la biblioteca curada.
Funciones públicas:
- `load_systems_catalog()` — devuelve el dict del catálogo maestro
- `load_library(root=None)` — carga TODA la biblioteca y devuelve un
`LibraryLoadResult` con vessels, equipment,
rules y la lista de issues.
El loader es defensivo: NO levanta excepciones por archivos individuales
malos. Acumula issues y deja al caller decidir si abortar.
"""
from __future__ import annotations
import json
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any
import yaml
from pydantic import ValidationError
from vmssailor.core.equipment import EquipmentModel
from vmssailor.core.vessel import Vessel
@dataclass(slots=True)
class LibraryIssue:
"""Un problema encontrado al cargar la biblioteca."""
severity: str # "error" | "warning" | "info"
path: str
message: str
def __str__(self) -> str:
return f"{self.severity.upper():7s} {self.path:60s} {self.message}"
@dataclass(slots=True)
class LibraryLoadResult:
"""Resultado de cargar la biblioteca completa."""
vessels: list[Vessel] = field(default_factory=list)
equipment_models: list[EquipmentModel] = field(default_factory=list)
rules: dict[str, dict[str, Any]] = field(default_factory=dict)
systems_catalog: dict[str, Any] = field(default_factory=dict)
issues: list[LibraryIssue] = field(default_factory=list)
@property
def errors(self) -> list[LibraryIssue]:
return [i for i in self.issues if i.severity == "error"]
@property
def warnings(self) -> list[LibraryIssue]:
return [i for i in self.issues if i.severity == "warning"]
def ok(self) -> bool:
return len(self.errors) == 0
def format(self) -> str:
lines: list[str] = []
lines.append("Library load summary:")
lines.append(f" Vessels : {len(self.vessels)}")
lines.append(f" EquipmentModels: {len(self.equipment_models)}")
lines.append(f" Rules : {len(self.rules)}")
lines.append(f" Issues : {len(self.errors)} errors, {len(self.warnings)} warnings")
if self.issues:
lines.append("")
for i in self.issues:
lines.append(f" {i}")
return "\n".join(lines)
def load_systems_catalog(root: Path | None = None) -> dict[str, Any]:
"""Carga el catálogo maestro de sistemas."""
base = root or _default_root()
catalog_path = base / "systems_catalog.json"
if not catalog_path.exists():
raise FileNotFoundError(f"systems_catalog.json no encontrado en {base}")
return json.loads(catalog_path.read_text(encoding="utf-8"))
def load_library(root: Path | None = None) -> LibraryLoadResult:
"""Carga la biblioteca completa y devuelve un `LibraryLoadResult`."""
base = root or _default_root()
result = LibraryLoadResult()
# systems_catalog
try:
result.systems_catalog = load_systems_catalog(base)
except Exception as exc:
result.issues.append(
LibraryIssue("error", "systems_catalog.json", f"{type(exc).__name__}: {exc}")
)
# vessels/
vessels_dir = base / "vessels"
if vessels_dir.exists():
for f in sorted(vessels_dir.glob("*.json")):
try:
data = json.loads(f.read_text(encoding="utf-8"))
v = Vessel(**data)
result.vessels.append(v)
if v.data_source == "seed_estimate":
result.issues.append(
LibraryIssue(
"info",
str(f.relative_to(base)),
"data_source=seed_estimate — requiere validación de Álvaro.",
)
)
except ValidationError as exc:
result.issues.append(
LibraryIssue("error", str(f.relative_to(base)), f"Pydantic: {exc.errors()}")
)
except Exception as exc:
result.issues.append(
LibraryIssue(
"error", str(f.relative_to(base)), f"{type(exc).__name__}: {exc}"
)
)
# equipment/**/*.json
eq_dir = base / "equipment"
if eq_dir.exists():
for f in sorted(eq_dir.glob("**/*.json")):
try:
data = json.loads(f.read_text(encoding="utf-8"))
em = EquipmentModel(**data)
result.equipment_models.append(em)
if em.data_source == "seed_estimate":
result.issues.append(
LibraryIssue(
"info",
str(f.relative_to(base)),
"data_source=seed_estimate — requiere validación.",
)
)
except ValidationError as exc:
result.issues.append(
LibraryIssue("error", str(f.relative_to(base)), f"Pydantic: {exc.errors()}")
)
except Exception as exc:
result.issues.append(
LibraryIssue(
"error", str(f.relative_to(base)), f"{type(exc).__name__}: {exc}"
)
)
# rules/*.yaml
rules_dir = base / "rules"
if rules_dir.exists():
for f in sorted(rules_dir.glob("*.yaml")):
try:
data = yaml.safe_load(f.read_text(encoding="utf-8")) or {}
meta = data.get("meta") or {}
rule_id = meta.get("rule_id") or f.stem
result.rules[rule_id] = data
except Exception as exc:
result.issues.append(
LibraryIssue(
"error", str(f.relative_to(base)), f"{type(exc).__name__}: {exc}"
)
)
# Validación cross-references
_validate_cross_refs(result)
return result
def _validate_cross_refs(result: LibraryLoadResult) -> None:
"""Chequea referencias entre rules → equipment models → systems."""
em_ids = {em.id for em in result.equipment_models}
valid_systems = set()
for cat in result.systems_catalog.get("categories", []):
for s in cat.get("systems", []):
valid_systems.add(s["id"])
# Chequear que equipment.typical_systems referencien sistemas válidos
for em in result.equipment_models:
for sys_id in em.typical_systems:
if sys_id.value not in valid_systems:
result.issues.append(
LibraryIssue(
"error",
f"equipment/.../{em.id}.json",
f"typical_systems contiene '{sys_id.value}' que no existe en "
"systems_catalog.json.",
)
)
# Chequear que rules referencien EquipmentModel.id existentes
for rule_id, rule_data in result.rules.items():
proposals = rule_data.get("equipment_proposals") or {}
for _sys_id, sys_proposals in proposals.items():
if not isinstance(sys_proposals, dict):
continue
for cand in sys_proposals.get("candidates", []) or []:
model_ref = cand.get("model_ref")
if model_ref and model_ref not in em_ids:
result.issues.append(
LibraryIssue(
"warning",
f"rules/{rule_id}.yaml",
f"Rule referencia model_ref='{model_ref}' que no existe en "
"equipment_models cargados.",
)
)
def _default_root() -> Path:
return Path(__file__).parent
@@ -0,0 +1,161 @@
# Reglas heurísticas para yates motor planeo 20-30 m.
#
# Este archivo captura el conocimiento de Álvaro sobre qué sistemas y qué
# equipos lleva típicamente un yate motor planeo del segmento objetivo.
# El motor de reglas del Studio (Sprint 2) consulta este archivo en el
# Paso 5 del wizard para proponer equipos al integrador.
#
# Filosofía: PROPONE, no impone. El integrador siempre puede ajustar.
# data_source de cada propuesta es "seed_estimate" y queda en
# docs/seed_data_notes.md hasta que Álvaro lo valide contra proyectos reales.
meta:
version: 1
rule_id: yacht_motor_planeo
applies_to:
vessel_types: ["yacht_motor"]
vessel_subtypes: ["planing", "semi_planing"]
length_overall_m:
min: 18.0
max: 32.0
data_source: seed_estimate
# ----- Sistemas que típicamente se incluyen -------------------------------
systems_default_enabled:
- main_engine
- transmission
- shaft_propeller
- thruster
- trim_tabs
- genset
- shore_power
- msb
- solar
- fuel
- lube_oil
- fw_cooling
- sw_cooling
- bilge
- potable_water
- watermaker
- fire_detection
- fire_extinguishing
- hvac
- engine_vent
- nav_lights
- deck_lights
- interior_lights
- emergency_lights
- fuel_tanks
- water_tanks
- grey_black_tanks
- windlass
- anchor_system
systems_optional:
- gyrostabilizer # Seakeeper se vuelve muy común en este rango
- joystick_docking
- inverter_charger
- battery_bank
- searchlights
- davits
- gangway
# ----- Equipos propuestos por sistema --------------------------------------
equipment_proposals:
main_engine:
# Para yates de 20-25 m, MTU o Volvo en pares. Para 25-32 m, MTU.
count: 2
candidates:
- model_ref: mtu_12v_2000_m96
when:
length_overall_m: { min: 22.0, max: 32.0 }
rationale: "Estándar de oro en este rango. Buena disponibilidad de partes y servicio."
- model_ref: volvo_d13_900hp
when:
length_overall_m: { min: 18.0, max: 26.0 }
rationale: "Más liviano y económico que MTU 2000. Servicio mundial Volvo Penta."
location_template:
port: { x_pp_pct: 0.25, y_cl: -0.9, z_bl: 1.2 }
starboard: { x_pp_pct: 0.25, y_cl: 0.9, z_bl: 1.2 }
tag_prefix_template: "ME_{side}"
sides: ["PORT", "STBD"]
genset:
count: 1
candidates:
- model_ref: northern_lights_m65c13
when:
length_overall_m: { min: 18.0, max: 30.0 }
rationale: "Confiabilidad probada. Aceptado por clase RINA/Lloyd's con poco trámite."
location_template:
default: { x_pp_pct: 0.20, y_cl: 0.0, z_bl: 1.0 }
tag_prefix_template: "GEN_{idx}"
fuel:
# Sin modelo concreto — el integrador definirá tanques estructurales en Paso 6.
sensors_per_tank:
- level
- temperature
typical_tank_count: 2
tag_prefix_template: "TANK_FUEL_{idx}"
bilge:
typical_pump_count: 3
tag_prefix_template: "BILGE_{location}"
locations_template: ["FWD", "MID", "AFT"]
# ----- Permissives típicos a sugerir ---------------------------------------
permissives_template:
- id: start_main_engine
action_id_template: "START_{tag_prefix}"
apply_to: ["main_engine"]
conditions:
- tag_ref_template: "{tag_prefix}.OIL_PRESS"
operator: ">"
threshold: 0.3
message_on_fail: "Presión aceite previa al arranque demasiado baja (lubricación insuficiente)."
- tag_ref_template: "{tag_prefix}.COOLANT_TEMP"
operator: ">"
threshold: 5.0
message_on_fail: "Refrigerante por debajo de 5°C — pre-calentar antes de arrancar."
- tag_ref_template: "{tag_prefix}.ESTOP_ACTIVE"
operator: "is_false"
message_on_fail: "Pulsador E-stop activado — desbloquear antes de arrancar."
on_fail_message: "Pre-condiciones de arranque del motor principal no cumplidas."
- id: start_genset
action_id_template: "START_{tag_prefix}"
apply_to: ["genset"]
conditions:
- tag_ref_template: "{tag_prefix}.OIL_PRESS"
operator: ">"
threshold: 0.3
message_on_fail: "Presión aceite previa baja."
- tag_ref_template: "{tag_prefix}.COOLANT_TEMP"
operator: ">"
threshold: 0.0
message_on_fail: "Refrigerante demasiado frío para arranque seguro."
on_fail_message: "Pre-condiciones de arranque del genset no cumplidas."
# ----- Topología sugerida de tarjetas AR-NMEA-IO --------------------------
topology_template:
# Patrón típico para yate planeo con 2 motores + 1 genset + tanques + auxiliares:
# 5-7 tarjetas distribuidas. Una maestra Modbus en el PC industrial.
cards_estimate:
min: 5
typical: 6
max: 8
buses:
- id: bus_main
protocol: modbus_rtu
role: "Maestra en PC industrial central. Esclavas distribuidas."
- id: bus_n2k
protocol: nmea2000
role: "Backbone NMEA 2000 del buque. Motores y gensets en modo dual."
+151
View File
@@ -0,0 +1,151 @@
{
"_meta": {
"version": 1,
"source": "VMS_Sailor_v2_Parte_01.md section 7",
"notes": "Catálogo maestro completo. Sirve de checklist para el wizard del Studio (paso 4). Lo que el integrador marca define el menú lateral del Runtime. SystemId values deben coincidir con vmssailor.core.enums.SystemId."
},
"categories": [
{
"id": "propulsion",
"name": "Propulsión y maquinaria",
"systems": [
{ "id": "main_engine", "name": "Máquina principal", "name_en": "Main engine", "default_for": ["yacht_motor", "fishing", "patrol", "ferry", "offshore_support"] },
{ "id": "transmission", "name": "Transmisiones / reductoras", "name_en": "Transmissions" },
{ "id": "shaft_propeller", "name": "Ejes y hélices", "name_en": "Shafts & propellers" },
{ "id": "thruster", "name": "Hélices de proa/popa", "name_en": "Bow/stern thrusters" }
]
},
{
"id": "maneuvering",
"name": "Maniobra y trimado",
"systems": [
{ "id": "trim_sterndrive", "name": "Trim de motores / sterndrives", "name_en": "Engine trim" },
{ "id": "trim_tabs", "name": "Trim tabs", "name_en": "Trim tabs (Bennett, Lenco)" },
{ "id": "cpp", "name": "Hélices de paso variable", "name_en": "Controllable Pitch Propellers" },
{ "id": "gyrostabilizer", "name": "Estabilizadores girostáticos", "name_en": "Gyrostabilizers (Seakeeper, Quick MC²)" },
{ "id": "fin_stabilizer", "name": "Estabilizadores de aletas activas", "name_en": "Active fin stabilizers" },
{ "id": "joystick_docking", "name": "Joystick docking", "name_en": "Joystick docking" }
]
},
{
"id": "electrical_generation",
"name": "Generación eléctrica",
"systems": [
{ "id": "genset", "name": "Gensets diésel", "name_en": "Diesel gensets", "default_for": ["yacht_motor", "fishing", "patrol", "ferry", "offshore_support"] },
{ "id": "shore_power", "name": "Shore power con transferencia", "name_en": "Shore power with ATS" },
{ "id": "inverter_charger", "name": "Inversores/cargadores combinados", "name_en": "Inverter/chargers (Victron, Mastervolt)" },
{ "id": "battery_bank", "name": "Bancos de baterías litio con BMS", "name_en": "Lithium battery banks with BMS" },
{ "id": "msb", "name": "Cuadros principales (MSB)", "name_en": "Main switchboards", "default_for": ["yacht_motor", "fishing", "patrol", "ferry"] },
{ "id": "esb", "name": "Cuadros de emergencia (ESB)", "name_en": "Emergency switchboards" },
{ "id": "ups", "name": "UPS", "name_en": "UPS" },
{ "id": "solar", "name": "Paneles solares + MPPT", "name_en": "Solar + MPPT" },
{ "id": "smart_dc_busbar", "name": "Smart busbars DC", "name_en": "Smart DC busbars (Lynx Smart BMS)" },
{ "id": "smart_panel", "name": "Tableros inteligentes con monitoreo", "name_en": "Smart panels with embedded monitoring" }
]
},
{
"id": "electrical_isolation",
"name": "Aislamiento eléctrico",
"systems": [
{ "id": "sectionalizing", "name": "Aislamiento por sectores", "name_en": "Sectionalizing" },
{ "id": "emergency_isolation", "name": "Aislamiento total de emergencia", "name_en": "Total emergency isolation" },
{ "id": "breakers", "name": "Breakers configurables", "name_en": "Configurable breakers" },
{ "id": "lockout_tagout", "name": "Lockout-tagout digital", "name_en": "Digital lockout-tagout" }
]
},
{
"id": "fluids",
"name": "Fluidos del buque",
"systems": [
{ "id": "fuel", "name": "Combustible (DO/MDO)", "name_en": "Fuel oil", "default_for": ["yacht_motor", "fishing", "patrol", "ferry", "offshore_support"] },
{ "id": "lube_oil", "name": "Aceite lubricante", "name_en": "Lube oil" },
{ "id": "hydraulic_oil", "name": "Aceite hidráulico", "name_en": "Hydraulic oil" },
{ "id": "fw_cooling", "name": "Refrigeración agua dulce", "name_en": "Fresh water cooling" },
{ "id": "sw_cooling", "name": "Refrigeración agua salada", "name_en": "Sea water cooling" },
{ "id": "starting_air", "name": "Aire de arranque / aire comprimido", "name_en": "Starting / compressed air" },
{ "id": "bilge", "name": "Sentinas", "name_en": "Bilge", "default_for": ["yacht_motor", "fishing", "patrol", "ferry", "offshore_support"] },
{ "id": "ballast", "name": "Lastre", "name_en": "Ballast" },
{ "id": "grey_water", "name": "Aguas grises", "name_en": "Grey water" },
{ "id": "black_water", "name": "Aguas negras", "name_en": "Black water" },
{ "id": "potable_water", "name": "Agua potable", "name_en": "Potable water", "default_for": ["yacht_motor", "ferry"] },
{ "id": "sw_service", "name": "Agua salada de servicio", "name_en": "Sea water service" },
{ "id": "watermaker", "name": "Watermaker", "name_en": "Watermaker (desalinator)" }
]
},
{
"id": "safety",
"name": "Seguridad",
"systems": [
{ "id": "fire_detection", "name": "Detección de incendio", "name_en": "Fire detection", "default_for": ["yacht_motor", "fishing", "patrol", "ferry", "offshore_support"] },
{ "id": "fire_extinguishing", "name": "Extinción (CO₂, HiFog, espuma)", "name_en": "Fire extinguishing" },
{ "id": "fifi_external", "name": "FiFi externo", "name_en": "External FiFi monitors" },
{ "id": "emergency_bilge", "name": "Achique de emergencia", "name_en": "Emergency bilge" },
{ "id": "gas_detection", "name": "Detección de gases", "name_en": "Gas detection" },
{ "id": "mob", "name": "Hombre al agua (MOB)", "name_en": "Man overboard" }
]
},
{
"id": "environment",
"name": "Ambiente y confort",
"systems": [
{ "id": "hvac", "name": "HVAC / aire acondicionado", "name_en": "HVAC", "default_for": ["yacht_motor", "ferry"] },
{ "id": "engine_vent", "name": "Ventilación de máquinas", "name_en": "Engine room ventilation", "default_for": ["yacht_motor", "fishing", "patrol", "ferry", "offshore_support"] },
{ "id": "heating", "name": "Calefacción", "name_en": "Heating" },
{ "id": "refrigeration", "name": "Refrigeración (cámaras, neveras)", "name_en": "Refrigeration" }
]
},
{
"id": "lighting",
"name": "Iluminación",
"systems": [
{ "id": "nav_lights", "name": "Luces de navegación", "name_en": "Navigation lights", "default_for": ["yacht_motor", "fishing", "patrol", "ferry", "offshore_support"] },
{ "id": "deck_lights", "name": "Luces de cubierta", "name_en": "Deck lights" },
{ "id": "interior_lights", "name": "Luces interiores por sector", "name_en": "Interior lights" },
{ "id": "emergency_lights", "name": "Luces de emergencia", "name_en": "Emergency lights" },
{ "id": "searchlights", "name": "Reflectores", "name_en": "Searchlights" }
]
},
{
"id": "structural_tanks",
"name": "Tanques estructurales",
"systems": [
{ "id": "fuel_tanks", "name": "Tanques de combustible", "name_en": "Fuel tanks", "default_for": ["yacht_motor", "fishing", "patrol", "ferry", "offshore_support"] },
{ "id": "water_tanks", "name": "Tanques de agua", "name_en": "Water tanks" },
{ "id": "grey_black_tanks", "name": "Tanques de aguas grises/negras", "name_en": "Grey/black water tanks" },
{ "id": "voids", "name": "Voids", "name_en": "Voids" },
{ "id": "cofferdams", "name": "Cofferdams", "name_en": "Cofferdams" }
]
},
{
"id": "deck_maneuvering",
"name": "Cubierta y maniobra",
"systems": [
{ "id": "windlass", "name": "Cabrestantes / molinetes", "name_en": "Windlasses" },
{ "id": "anchor_system", "name": "Sistema de anclas", "name_en": "Anchor system" },
{ "id": "mooring", "name": "Sistema de amarre", "name_en": "Mooring system" },
{ "id": "davits", "name": "Davits / pescantes", "name_en": "Davits" },
{ "id": "gangway", "name": "Pasarelas / portalones", "name_en": "Gangways" },
{ "id": "crane", "name": "Grúas", "name_en": "Cranes" }
]
},
{
"id": "vessel_specific",
"name": "Específicos por tipo de buque",
"systems": [
{ "id": "fishing_machinery", "name": "Maquinaria de pesca", "name_en": "Fishing machinery", "default_for": ["fishing"] },
{ "id": "large_fridge_holds", "name": "Cámaras frigoríficas grandes", "name_en": "Large refrigerated holds", "default_for": ["fishing"] },
{ "id": "rov", "name": "ROV / equipos sumergibles", "name_en": "ROV / submersibles", "default_for": ["offshore_support"] },
{ "id": "diving_system", "name": "Sistema de buceo", "name_en": "Diving system" }
]
}
],
"_excluded_from_vms_sailor": {
"note": "Estos pertenecen al AR-ECDIS, NO se incluyen en el VMS-Sailor.",
"items": [
"ECDIS / radar / AIS",
"Piloto automático",
"Comunicaciones VHF/HF/SatCom",
"GPS y sensores de actitud (vienen del AR-ECDIS por NMEA 2000)"
]
}
}
@@ -0,0 +1,22 @@
{
"id": "ferretti_850",
"name": "Ferretti 850",
"type": "yacht_motor",
"subtype": "planing",
"length_overall_m": 26.04,
"beam_max_m": 6.28,
"draft_m": 1.98,
"displacement_kg": 68000,
"description": "Yate motor planeo italiano de 26 m, casco semi-V con propulsión convencional, 3 cubiertas, 4 cabinas + tripulación. Motores en V centrales, 2 gensets, A/A multizona. Tope típico ~30 nudos.",
"data_source": "seed_estimate",
"decks": [
{ "id": "lower", "name": "Cubierta inferior", "z_bl_bottom": 0.5, "z_bl_top": 2.7, "polygon_xy": [] },
{ "id": "main", "name": "Cubierta principal", "z_bl_bottom": 2.7, "z_bl_top": 4.9, "polygon_xy": [] },
{ "id": "flybridge", "name": "Flybridge", "z_bl_bottom": 4.9, "z_bl_top": 6.6, "polygon_xy": [] }
],
"bulkheads": [
{ "id": "collision", "name": "Mamparo de colisión", "x_pp": 23.5, "description": "" },
{ "id": "er_fwd", "name": "Mamparo proa SM", "x_pp": 8.0, "description": "" },
{ "id": "er_aft", "name": "Mamparo popa SM", "x_pp": 4.0, "description": "" }
]
}
@@ -0,0 +1,40 @@
{
"id": "sunseeker_76",
"name": "Sunseeker 76 Yacht",
"type": "yacht_motor",
"subtype": "planing",
"length_overall_m": 23.45,
"beam_max_m": 5.65,
"draft_m": 1.85,
"displacement_kg": 55000,
"description": "Yate motor planeo británico de 23.4 m, casco semi-V profundo, 3 cubiertas (lower, main, flybridge), 4 cabinas. Sala de máquinas central con 2 motores principales en V, 1-2 gensets, sistemas de A/A y refrigeración. Tope típico ~32 nudos.",
"data_source": "seed_estimate",
"decks": [
{
"id": "lower",
"name": "Cubierta inferior (Lower Deck)",
"z_bl_bottom": 0.5,
"z_bl_top": 2.6,
"polygon_xy": []
},
{
"id": "main",
"name": "Cubierta principal (Main Deck)",
"z_bl_bottom": 2.6,
"z_bl_top": 4.8,
"polygon_xy": []
},
{
"id": "flybridge",
"name": "Flybridge",
"z_bl_bottom": 4.8,
"z_bl_top": 6.4,
"polygon_xy": []
}
],
"bulkheads": [
{ "id": "collision", "name": "Mamparo de colisión", "x_pp": 21.0, "description": "Mamparo de colisión (proa)" },
{ "id": "er_fwd", "name": "Mamparo proa sala de máquinas", "x_pp": 7.0, "description": "" },
{ "id": "er_aft", "name": "Mamparo popa sala de máquinas", "x_pp": 3.5, "description": "" }
]
}