CtrEditor/Documentation/Hidraulic/Patches/osHydTank_OutputModeEnhance...

309 lines
11 KiB
C#

// PARCHE PRIORITARIO 3: Mejoras en osHydTank para agregar selector de tipo de salida
using System;
using System.ComponentModel;
using Newtonsoft.Json;
using CtrEditor.HydraulicSimulator;
namespace CtrEditor.ObjetosSim
{
/// <summary>
/// Tipo de flujo que sale del tanque
/// </summary>
public enum TankOutputMode
{
[Description("Solo fluido primario")]
Primary = 0,
[Description("Solo fluido secundario")]
Secondary = 1,
[Description("Mezcla proporcional")]
Mixed = 2,
[Description("Automático según volúmenes")]
Automatic = 3
}
public partial class osHydTank
{
private TankOutputMode _outputMode = TankOutputMode.Automatic;
[Category("🔄 Control de Salida")]
[DisplayName("Modo de salida")]
[Description("Selecciona qué fluido sale del tanque")]
public TankOutputMode OutputMode
{
get => _outputMode;
set
{
if (SetProperty(ref _outputMode, value))
{
OnPropertyChanged(nameof(CurrentOutputFluid));
OnPropertyChanged(nameof(CurrentFluidDescription));
OnPropertyChanged(nameof(OutputModeDescription));
InvalidateHydraulicNetwork();
}
}
}
[Category("🔄 Control de Salida")]
[DisplayName("Descripción modo")]
[Description("Descripción del modo de salida actual")]
[ReadOnly(true)]
public string OutputModeDescription
{
get
{
return OutputMode switch
{
TankOutputMode.Primary => $"Salida: {PrimaryFluidType} ({PrimaryVolumeL:F1}L)",
TankOutputMode.Secondary => $"Salida: {SecondaryFluidType} ({SecondaryVolumeL:F1}L)",
TankOutputMode.Mixed => $"Salida: Mezcla ({PrimaryVolumeL + SecondaryVolumeL:F1}L total)",
TankOutputMode.Automatic => GetAutomaticModeDescription(),
_ => "Modo desconocido"
};
}
}
[Category("📊 Niveles Específicos")]
[DisplayName("Nivel Primario (m)")]
[Description("Nivel específico del fluido primario")]
[ReadOnly(true)]
public double PrimaryLevelM
{
get
{
if (CrossSectionalArea <= 0) return 0;
return PrimaryVolumeL / 1000.0 / CrossSectionalArea;
}
}
[Category("📊 Niveles Específicos")]
[DisplayName("Nivel Secundario (m)")]
[Description("Nivel específico del fluido secundario")]
[ReadOnly(true)]
public double SecondaryLevelM
{
get
{
if (CrossSectionalArea <= 0) return 0;
return SecondaryVolumeL / 1000.0 / CrossSectionalArea;
}
}
[Category("📊 Niveles Específicos")]
[DisplayName("Proporción Primario")]
[Description("Porcentaje del fluido primario en el total")]
[ReadOnly(true)]
public double PrimaryPercentage
{
get
{
var totalVolume = PrimaryVolumeL + SecondaryVolumeL;
return totalVolume > 0 ? (PrimaryVolumeL / totalVolume) * 100.0 : 0.0;
}
}
[Category("📊 Niveles Específicos")]
[DisplayName("Proporción Secundario")]
[Description("Porcentaje del fluido secundario en el total")]
[ReadOnly(true)]
public double SecondaryPercentage
{
get
{
var totalVolume = PrimaryVolumeL + SecondaryVolumeL;
return totalVolume > 0 ? (SecondaryVolumeL / totalVolume) * 100.0 : 0.0;
}
}
// Propiedades del fluido actual basado en OutputMode
public FluidProperties GetCurrentOutputFluidByMode()
{
return OutputMode switch
{
TankOutputMode.Primary => GetPrimaryOutput(),
TankOutputMode.Secondary => GetSecondaryOutput(),
TankOutputMode.Mixed => GetMixedOutput(),
TankOutputMode.Automatic => GetAutomaticOutput(),
_ => new FluidProperties(FluidType.Air)
};
}
private FluidProperties GetPrimaryOutput()
{
return PrimaryVolumeL > 0 ? _primaryFluid.Clone() : new FluidProperties(FluidType.Air);
}
private FluidProperties GetSecondaryOutput()
{
return SecondaryVolumeL > 0 ? _secondaryFluid.Clone() : new FluidProperties(FluidType.Air);
}
private FluidProperties GetMixedOutput()
{
if (PrimaryVolumeL <= 0 && SecondaryVolumeL <= 0)
return new FluidProperties(FluidType.Air);
if (PrimaryVolumeL <= 0)
return _secondaryFluid.Clone();
if (SecondaryVolumeL <= 0)
return _primaryFluid.Clone();
// Calcular ratio de mezcla basado en volúmenes
var totalVolume = PrimaryVolumeL + SecondaryVolumeL;
var mixRatio = SecondaryVolumeL / totalVolume;
return _primaryFluid.MixWith(_secondaryFluid, mixRatio);
}
private FluidProperties GetAutomaticOutput()
{
// Lógica automática: secundario primero, luego mezcla, luego primario
if (SecondaryVolumeL > 0 && PrimaryVolumeL > 0)
{
// Si hay ambos, usar la lógica del MixingState original
return MixingState switch
{
MixingState.EmptyingSecondary => _secondaryFluid.Clone(),
MixingState.Mixing => _primaryFluid.MixWith(_secondaryFluid, CurrentMixRatio),
MixingState.EmptyingPrimary => _primaryFluid.Clone(),
_ => GetMixedOutput()
};
}
else if (SecondaryVolumeL > 0)
{
return _secondaryFluid.Clone();
}
else if (PrimaryVolumeL > 0)
{
return _primaryFluid.Clone();
}
else
{
return new FluidProperties(FluidType.Air);
}
}
private string GetAutomaticModeDescription()
{
if (SecondaryVolumeL > 0 && PrimaryVolumeL > 0)
{
return $"Auto: {SecondaryFluidType}→Mezcla→{PrimaryFluidType}";
}
else if (SecondaryVolumeL > 0)
{
return $"Auto: {SecondaryFluidType} ({SecondaryVolumeL:F1}L)";
}
else if (PrimaryVolumeL > 0)
{
return $"Auto: {PrimaryFluidType} ({PrimaryVolumeL:F1}L)";
}
else
{
return "Auto: Vacío";
}
}
// Método mejorado para actualizar volúmenes durante el flujo
private void UpdateVolumeFromFlowWithMode(double deltaTimeSec)
{
var flowOutLMin = OutletFlow;
var flowInLMin = InletFlow;
var netFlowLMin = flowInLMin - flowOutLMin;
var deltaVolumeL = netFlowLMin * deltaTimeSec / 60.0;
if (deltaVolumeL < 0) // Salida neta
{
var outVolumeL = Math.Abs(deltaVolumeL);
ReduceVolumesByOutputMode(outVolumeL);
}
else if (deltaVolumeL > 0) // Entrada neta
{
// Por defecto, el fluido que entra se añade al primario
// TODO: Implementar lógica para determinar tipo de fluido entrante basado en la fuente
PrimaryVolumeL += deltaVolumeL;
}
// Actualizar nivel total y otras propiedades
CurrentVolumeL = PrimaryVolumeL + SecondaryVolumeL;
CurrentLevelM = CurrentVolumeL / 1000.0 / Math.Max(0.1, CrossSectionalArea);
// Notificar cambios en las propiedades calculadas
OnPropertyChanged(nameof(PrimaryLevelM));
OnPropertyChanged(nameof(SecondaryLevelM));
OnPropertyChanged(nameof(PrimaryPercentage));
OnPropertyChanged(nameof(SecondaryPercentage));
OnPropertyChanged(nameof(OutputModeDescription));
}
private void ReduceVolumesByOutputMode(double outVolumeL)
{
switch (OutputMode)
{
case TankOutputMode.Primary:
PrimaryVolumeL = Math.Max(0, PrimaryVolumeL - outVolumeL);
break;
case TankOutputMode.Secondary:
SecondaryVolumeL = Math.Max(0, SecondaryVolumeL - outVolumeL);
break;
case TankOutputMode.Mixed:
ReduceVolumeProportionally(outVolumeL);
break;
case TankOutputMode.Automatic:
ReduceVolumeByMixingState(outVolumeL);
break;
}
}
private void ReduceVolumeProportionally(double outVolumeL)
{
var totalVol = PrimaryVolumeL + SecondaryVolumeL;
if (totalVol > 0)
{
var primaryRatio = PrimaryVolumeL / totalVol;
var secondaryRatio = SecondaryVolumeL / totalVol;
PrimaryVolumeL = Math.Max(0, PrimaryVolumeL - (outVolumeL * primaryRatio));
SecondaryVolumeL = Math.Max(0, SecondaryVolumeL - (outVolumeL * secondaryRatio));
}
}
private void ReduceVolumeByMixingState(double outVolumeL)
{
// Usar la lógica original del MixingState
switch (MixingState)
{
case MixingState.EmptyingSecondary:
SecondaryVolumeL = Math.Max(0, SecondaryVolumeL - outVolumeL);
break;
case MixingState.Mixing:
ReduceVolumeProportionally(outVolumeL);
break;
case MixingState.EmptyingPrimary:
PrimaryVolumeL = Math.Max(0, PrimaryVolumeL - outVolumeL);
break;
default:
// Si hay secundario, vaciarlo primero
if (SecondaryVolumeL > 0)
{
SecondaryVolumeL = Math.Max(0, SecondaryVolumeL - outVolumeL);
}
else
{
PrimaryVolumeL = Math.Max(0, PrimaryVolumeL - outVolumeL);
}
break;
}
}
}
}