from sqlalchemy import Column, String, Float, Boolean, DateTime, Enum, Text, Integer from sqlalchemy.sql import func from database import Base import enum class AidCategory(str, enum.Enum): FLOTANTE = "FLOTANTE" FIJA = "FIJA" class AidType(str, enum.Enum): BOYA_LATERAL = "BOYA_LATERAL" BOYA_CARDINAL = "BOYA_CARDINAL" BOYA_PELIGRO = "BOYA_PELIGRO" BOYA_AGUAS_SEGURAS = "BOYA_AGUAS_SEGURAS" BOYA_ESPECIAL = "BOYA_ESPECIAL" FARO = "FARO" FAROL = "FAROL" BALIZA = "BALIZA" ENFILACION = "ENFILACION" class AisType(str, enum.Enum): ATON_21 = "ATON_21" AIS_NORMAL = "AIS_NORMAL" SIN_AIS = "SIN_AIS" class Aid(Base): __tablename__ = "aids" id = Column(String, primary_key=True) nombre = Column(String, nullable=False) numero_interno = Column(String) categoria = Column(String, nullable=False) # FLOTANTE | FIJA tipo = Column(String, nullable=False) tipo_ais = Column(String, default="SIN_AIS") # Posición oficial (fuente de verdad) lat_nominal = Column(Float, nullable=False) lon_nominal = Column(Float, nullable=False) fuente_posicion = Column(String, default="MANUAL") # S57 | MANUAL | AIS # Link estable al feature S-57 (cuando el Aid fue creado desde una carta). # cell_id + chart_feature_id permite reencontrar el feature aunque el ENC # se reedite. Si no hay LNAM en el feature original, chart_feature_id es # un fingerprint: tipo_obj + round(lat,6) + round(lon,6). source_chart = Column(String, nullable=True) # 'S57' | 'MANUAL' | 'AIS' cell_id = Column(String, nullable=True, index=True) chart_feature_id = Column(String, nullable=True, index=True) # Solo flotantes radio_borneo_m = Column(Float, default=10.0) # Per-aid displacement alert thresholds (override global config when set). # Different buoys have different anchor chain lengths / acceptable drift. displacement_warn_m = Column(Float, nullable=True) # None → use global setting displacement_alarm_m = Column(Float, nullable=True) # None → use global setting # Minutes without AIS signal before PERDIDA_SENAL alert fires. # None = no signal-loss monitoring for this aid. signal_loss_min = Column(Integer, nullable=True) # Si tiene AIS mmsi = Column(String, unique=True, nullable=True) # Digital input function mapping (what each IN means for THIS buoy) # Values: NULL | 'WATER_INGRESS_WARN' | 'WATER_INGRESS_CRITICAL' | 'LISTING' din3_function = Column(String, nullable=True) din4_function = Column(String, nullable=True) # Posición actual (actualizada por AIS) lat_actual = Column(Float, nullable=True) lon_actual = Column(Float, nullable=True) desplazamiento_m = Column(Float, default=0.0) ultima_senal = Column(DateTime, nullable=True) # Estado en_posicion = Column(Boolean, default=True) en_movimiento = Column(Boolean, default=False) activa = Column(Boolean, default=True) # Características luminosas caracteristica_luz = Column(String, nullable=True) alcance_nm = Column(Float, nullable=True) lamp_id = Column(String, nullable=True) # FK → lamps.id (no enforced constraint to allow nullable) # Responsabilidad puerto_responsable = Column(String, nullable=True) # ej: "Barranquilla", "Cartagena" empresa_responsable = Column(String, nullable=True) # empresa de señalización observaciones = Column(Text, nullable=True) # Auditoría creado_en = Column(DateTime, server_default=func.now()) modificado_en = Column(DateTime, onupdate=func.now()) modificado_por = Column(String, nullable=True) motivo_cambio = Column(Text, nullable=True)