309 lines
11 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|
|
}
|