feat: Add detailed status properties and margin converter for tank sections in hydraulic components

This commit is contained in:
Miguel 2025-09-06 23:09:55 +02:00
parent 380fb5ba50
commit 322d335edd
7 changed files with 422 additions and 56 deletions

View File

@ -27,6 +27,7 @@
<local:UnsavedChangesConverter x:Key="UnsavedChangesConverter"/> <local:UnsavedChangesConverter x:Key="UnsavedChangesConverter"/>
<local:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/> <local:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
<converters:TankLevelToHeightConverter x:Key="TankLevelToHeightConverter"/> <converters:TankLevelToHeightConverter x:Key="TankLevelToHeightConverter"/>
<converters:TankSectionMarginConverter x:Key="TankSectionMarginConverter"/>
<!-- DropShadowEffect para efectos de sombra en texto --> <!-- DropShadowEffect para efectos de sombra en texto -->
<DropShadowEffect x:Key="DropShadowEffect" <DropShadowEffect x:Key="DropShadowEffect"

View File

@ -0,0 +1,95 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace CtrEditor.Converters
{
/// <summary>
/// Converter para calcular el margen de las secciones del tanque
/// basado en los porcentajes de las secciones previas
/// </summary>
public class TankSectionMarginConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values == null || values.Length < 2)
return new Thickness(4, 4, 4, 4);
// Convertir valores a double
if (!TryConvertToDouble(values[values.Length - 1], out double tankSize))
return new Thickness(4, 4, 4, 4);
// Calcular la altura total acumulada de las secciones previas
double accumulatedPercentage = 0.0;
for (int i = 0; i < values.Length - 1; i++) // -1 para excluir tankSize
{
if (TryConvertToDouble(values[i], out double percentage))
{
accumulatedPercentage += percentage;
}
}
try
{
// Convertir tankSize a píxeles (100 píxeles por metro aproximadamente)
var tankHeightPixels = tankSize * 100.0;
// Calcular altura acumulada en píxeles
var accumulatedHeightPixels = (accumulatedPercentage / 100.0) * tankHeightPixels;
// Calcular margen bottom basado en la altura acumulada
var marginBottom = 4.0 + accumulatedHeightPixels;
return new Thickness(4, 4, 4, marginBottom);
}
catch (Exception)
{
return new Thickness(4, 4, 4, 4);
}
}
private bool TryConvertToDouble(object value, out double result)
{
result = 0.0;
if (value == null)
return false;
// Intentar conversión directa si ya es double
if (value is double d)
{
result = d;
return true;
}
// Intentar conversión desde float
if (value is float f)
{
result = f;
return true;
}
// Intentar conversión desde int
if (value is int i)
{
result = i;
return true;
}
// Intentar parsing desde string
if (value is string s)
{
if (double.TryParse(s.Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out result))
return true;
}
return false;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException("ConvertBack no está implementado para TankSectionMarginConverter");
}
}
}

View File

@ -62,7 +62,7 @@ namespace CtrEditor
private double maxSimExecutionTime = 0; private double maxSimExecutionTime = 0;
private int simTimingAdaptationCounter = 0; private int simTimingAdaptationCounter = 0;
private const int SIM_ADAPTATION_SAMPLES = 5; // Evaluar cada 5 muestras para más responsividad private const int SIM_ADAPTATION_SAMPLES = 5; // Evaluar cada 5 muestras para más responsividad
private const double MIN_SIM_INTERVAL = 8; // Mínimo intervalo simulación (ms) private const double MIN_SIM_INTERVAL = 10; // Mínimo intervalo simulación (ms)
private const double MAX_SIM_INTERVAL = 100; // Máximo intervalo simulación (ms) private const double MAX_SIM_INTERVAL = 100; // Máximo intervalo simulación (ms)
private const double SIM_BUFFER_TIME = 2; // Buffer de 2ms extra respecto al tiempo real private const double SIM_BUFFER_TIME = 2; // Buffer de 2ms extra respecto al tiempo real
@ -76,6 +76,7 @@ namespace CtrEditor
private readonly System.Timers.Timer _timerPLCUpdate; // Cambiado a System.Timers.Timer para mejor precisión private readonly System.Timers.Timer _timerPLCUpdate; // Cambiado a System.Timers.Timer para mejor precisión
private readonly DispatcherTimer _timerDisplayUpdate; private readonly DispatcherTimer _timerDisplayUpdate;
private readonly DispatcherTimer _timer3DUpdate; // Nuevo timer para actualización 3D cuando simulación está detenida private readonly DispatcherTimer _timer3DUpdate; // Nuevo timer para actualización 3D cuando simulación está detenida
private readonly System.Timers.Timer _timerDebugFlush; // Timer para flush automático del buffer de debug
public Canvas MainCanvas; public Canvas MainCanvas;
@ -439,6 +440,13 @@ namespace CtrEditor
_timer3DUpdate.Tick += OnTick3DUpdate; _timer3DUpdate.Tick += OnTick3DUpdate;
_timer3DUpdate.Start(); // Iniciar porque la simulación empieza detenida _timer3DUpdate.Start(); // Iniciar porque la simulación empieza detenida
// Timer para flush automático del buffer de debug (cada 5 minutos)
_timerDebugFlush = new System.Timers.Timer();
_timerDebugFlush.Interval = 300000; // 5 minutos = 300,000 ms
_timerDebugFlush.Elapsed += OnDebugFlushTimer;
_timerDebugFlush.AutoReset = true;
_timerDebugFlush.Start();
StartSimulationCommand = new RelayCommand(StartSimulation); StartSimulationCommand = new RelayCommand(StartSimulation);
StopSimulationCommand = new RelayCommand(StopSimulation); StopSimulationCommand = new RelayCommand(StopSimulation);
@ -1270,6 +1278,8 @@ namespace CtrEditor
_timerSimulacion?.Stop(); _timerSimulacion?.Stop();
_timerPLCUpdate?.Stop(); _timerPLCUpdate?.Stop();
_timer3DUpdate?.Stop(); _timer3DUpdate?.Stop();
_timerDisplayUpdate?.Stop();
_timerDebugFlush?.Stop();
// Desconectar PLC si está conectado // Desconectar PLC si está conectado
if (PLCViewModel?.IsConnected == true) if (PLCViewModel?.IsConnected == true)
@ -1333,6 +1343,72 @@ namespace CtrEditor
}); });
} }
/// <summary>
/// Evento del timer que hace flush automático del buffer de debug para evitar acumulación excesiva
/// </summary>
private void OnDebugFlushTimer(object sender, System.Timers.ElapsedEventArgs e)
{
try
{
if (_debugConsoleServer != null)
{
var stats = _debugConsoleServer.GetBufferStats();
// Solo hacer flush si el buffer tiene un número significativo de mensajes
if (stats.messageCount > 500)
{
_debugConsoleServer.FlushMessageBuffer();
Debug.WriteLine($"[Debug Flush Timer] Buffer automático limpiado. Mensajes procesados: {stats.messageCount}");
}
else
{
Debug.WriteLine($"[Debug Flush Timer] Buffer saludable. Mensajes actuales: {stats.messageCount}");
}
}
}
catch (Exception ex)
{
Debug.WriteLine($"[Debug Flush Timer] Error durante flush automático: {ex.Message}");
}
}
/// <summary>
/// Evento del timer para actualización de display (UI)
/// </summary>
private void OnDisplayUpdate(object sender, EventArgs e)
{
try
{
// Actualizar elementos de UI que no requieren alta frecuencia
// Este timer corre a 250ms por lo que es adecuado para actualizaciones de UI menos críticas
// Se puede agregar lógica de actualización de UI aquí si es necesario
}
catch (Exception ex)
{
Debug.WriteLine($"[Display Update Timer] Error durante actualización de display: {ex.Message}");
}
}
/// <summary>
/// Evento del timer para actualización 3D cuando la simulación está detenida
/// </summary>
private void OnTick3DUpdate(object sender, EventArgs e)
{
try
{
// Mantener la visualización 3D actualizada cuando la simulación no está corriendo
if (!IsSimulationRunning && Visualization3DManager != null)
{
Visualization3DManager.SynchronizeWorld();
}
}
catch (Exception ex)
{
Debug.WriteLine($"[3D Update Timer] Error durante actualización 3D: {ex.Message}");
}
}
private void AdaptPlcTiming(double executionTime) private void AdaptPlcTiming(double executionTime)
{ {
lastPlcExecutionTime = executionTime; lastPlcExecutionTime = executionTime;
@ -1350,13 +1426,13 @@ namespace CtrEditor
{ {
// Incrementar intervalo para dar más tiempo // Incrementar intervalo para dar más tiempo
newInterval = Math.Min(maxPlcExecutionTime + SIM_PRIORITY_TIME + 5, MAX_PLC_INTERVAL); newInterval = Math.Min(maxPlcExecutionTime + SIM_PRIORITY_TIME + 5, MAX_PLC_INTERVAL);
Debug.WriteLine($"PLC Timer: Aumentando intervalo a {newInterval:F1}ms (ejecución máxima: {maxPlcExecutionTime:F1}ms)"); // Debug.WriteLine($"PLC Timer: Aumentando intervalo a {newInterval:F1}ms (ejecución máxima: {maxPlcExecutionTime:F1}ms)");
} }
// Si hemos estado por debajo del umbral, podemos reducir el intervalo // Si hemos estado por debajo del umbral, podemos reducir el intervalo
else if (maxPlcExecutionTime + SIM_PRIORITY_TIME < currentInterval * 0.7) else if (maxPlcExecutionTime + SIM_PRIORITY_TIME < currentInterval * 0.7)
{ {
newInterval = Math.Max(maxPlcExecutionTime + SIM_PRIORITY_TIME + 2, MIN_PLC_INTERVAL); newInterval = Math.Max(maxPlcExecutionTime + SIM_PRIORITY_TIME + 2, MIN_PLC_INTERVAL);
Debug.WriteLine($"PLC Timer: Reduciendo intervalo a {newInterval:F1}ms (ejecución máxima: {maxPlcExecutionTime:F1}ms)"); // Debug.WriteLine($"PLC Timer: Reduciendo intervalo a {newInterval:F1}ms (ejecución máxima: {maxPlcExecutionTime:F1}ms)");
} }
// Aplicar el nuevo intervalo si cambió // Aplicar el nuevo intervalo si cambió
@ -1394,20 +1470,20 @@ namespace CtrEditor
{ {
// Incrementar intervalo para evitar que el sistema se vuelva inusable // Incrementar intervalo para evitar que el sistema se vuelva inusable
newInterval = Math.Min(idealInterval + 1, MAX_SIM_INTERVAL); // +1ms adicional de seguridad newInterval = Math.Min(idealInterval + 1, MAX_SIM_INTERVAL); // +1ms adicional de seguridad
Debug.WriteLine($"Simulación Timer: Aumentando intervalo a {newInterval:F1}ms (ejecución máxima: {maxSimExecutionTime:F1}ms)"); //Debug.WriteLine($"Simulación Timer: Aumentando intervalo a {newInterval:F1}ms (ejecución máxima: {maxSimExecutionTime:F1}ms)");
} }
// Si hemos estado muy por debajo del umbral, podemos reducir el intervalo // Si hemos estado muy por debajo del umbral, podemos reducir el intervalo
else if (idealInterval < currentInterval * 0.8) else if (idealInterval < currentInterval * 0.8)
{ {
newInterval = Math.Max(idealInterval, MIN_SIM_INTERVAL); newInterval = Math.Max(idealInterval, MIN_SIM_INTERVAL);
Debug.WriteLine($"Simulación Timer: Reduciendo intervalo a {newInterval:F1}ms (ejecución máxima: {maxSimExecutionTime:F1}ms)"); //Debug.WriteLine($"Simulación Timer: Reduciendo intervalo a {newInterval:F1}ms (ejecución máxima: {maxSimExecutionTime:F1}ms)");
} }
// Aplicar el nuevo intervalo si cambió significativamente // Aplicar el nuevo intervalo si cambió significativamente
if (Math.Abs(newInterval - currentInterval) > 0.5) if (Math.Abs(newInterval - currentInterval) > 0.5)
{ {
_timerSimulacion.Interval = newInterval; _timerSimulacion.Interval = newInterval;
Debug.WriteLine($"Simulación Timer: Intervalo actualizado de {currentInterval:F1}ms a {newInterval:F1}ms"); //Debug.WriteLine($"Simulación Timer: Intervalo actualizado de {currentInterval:F1}ms a {newInterval:F1}ms");
} }
// Reset para el próximo período de evaluación // Reset para el próximo período de evaluación
@ -1607,23 +1683,6 @@ namespace CtrEditor
} }
} }
private void OnDisplayUpdate(object? sender, EventArgs e)
{
if (simSampleCount > 0)
{
SimulationSpeed = accumulatedSimTime / simSampleCount;
accumulatedSimTime = 0;
simSampleCount = 0;
}
if (plcSampleCount > 0)
{
PlcUpdateSpeed = accumulatedPlcTime / plcSampleCount;
accumulatedPlcTime = 0;
plcSampleCount = 0;
}
}
// Reemplazar la propiedad _multiPropertyEditorWindow por un diccionario // Reemplazar la propiedad _multiPropertyEditorWindow por un diccionario
private Dictionary<string, Windows.MultiPropertyEditorWindow> _propertyEditorWindows = new(); private Dictionary<string, Windows.MultiPropertyEditorWindow> _propertyEditorWindows = new();
@ -1758,16 +1817,6 @@ namespace CtrEditor
libraryWindow.Show(); libraryWindow.Show();
} }
private void OnTick3DUpdate(object? sender, EventArgs e)
{
// Solo actualizar Helix3D si la simulación está detenida y la actualización 3D está habilitada
// Cuando la simulación está corriendo, la sincronización se hace en simulationManager.Step()
if (!IsSimulationRunning && Is3DUpdateEnabled && Visualization3DManager != null)
{
Visualization3DManager.SynchronizeWorld();
}
}
#region Hydraulic Simulation Management #region Hydraulic Simulation Management
/// <summary> /// <summary>

View File

@ -239,6 +239,47 @@ namespace CtrEditor.ObjetosSim
} }
} }
[Category("📊 Estado Actual")]
[DisplayName("Estado Cavitación")]
[Description("Estado actual de cavitación de la bomba")]
[JsonIgnore]
public string CavitationStatus
{
get
{
if (!IsRunning) return "Bomba detenida";
var factor = CavitationFactor;
var canOperate = CanOperateWithoutCavitation;
var npshAvailable = NPSHAvailable;
if (canOperate && factor >= 0.9)
return "✅ Operación normal";
else if (factor >= 0.5)
return $"⚠️ Riesgo cavitación (Factor: {factor:F2})";
else
return $"❌ NPSH insuficiente ({npshAvailable:F2}m)";
}
}
[Category("📊 Estado Actual")]
[DisplayName("Estado Detallado")]
[Description("Información detallada del estado operacional")]
[JsonIgnore]
public string DetailedStatus
{
get
{
if (!IsRunning) return "Bomba detenida";
var factor = CavitationFactor;
var npshAvailable = NPSHAvailable;
var flow = CurrentFlowLMin;
return $"NPSH: {npshAvailable:F2}m | Factor: {factor:F2} | Flujo: {flow:F1} L/min";
}
}
// Propiedades de fluido actual // Propiedades de fluido actual
private FluidProperties _currentFluid = new FluidProperties(FluidType.Air); private FluidProperties _currentFluid = new FluidProperties(FluidType.Air);
@ -629,22 +670,8 @@ namespace CtrEditor.ObjetosSim
if (SpeedRatio < 0.0) SpeedRatio = 0.0; if (SpeedRatio < 0.0) SpeedRatio = 0.0;
if (SpeedRatio > 1.0) SpeedRatio = 1.0; if (SpeedRatio > 1.0) SpeedRatio = 1.0;
// Verificar condiciones de NPSH si está habilitada la verificación // Las verificaciones de NPSH ahora se muestran a través de propiedades
if (hydraulicSimulationManager.EnableNPSHVerification && IsRunning) // Se eliminaron los Debug.WriteLine para evitar saturación del sistema
{
var npshAvailable = NPSHAvailable;
var cavitationFactor = CavitationFactor;
if (cavitationFactor < 0.5)
{
Debug.WriteLine($"⚠️ Bomba {Nombre}: Riesgo de cavitación - Factor={cavitationFactor:F2}, NPSH_disponible={npshAvailable:F2}m");
}
if (!CanOperateWithoutCavitation)
{
Debug.WriteLine($"❌ Bomba {Nombre}: NO puede operar sin cavitación - NPSH insuficiente");
}
}
//Debug.WriteLine($"Bomba {Nombre}: Velocidad={SpeedRatio:F2}, Funcionando={IsRunning}"); //Debug.WriteLine($"Bomba {Nombre}: Velocidad={SpeedRatio:F2}, Funcionando={IsRunning}");
} }

View File

@ -834,6 +834,91 @@ namespace CtrEditor.ObjetosSim
[JsonIgnore] [JsonIgnore]
public double CurrentFluidViscosity => CurrentOutputFluid.Viscosity; public double CurrentFluidViscosity => CurrentOutputFluid.Viscosity;
/// <summary>
/// Porcentaje de altura para la sección secundaria (abajo) - para display visual
/// </summary>
[JsonIgnore]
public double SecondaryDisplayPercentage
{
get
{
var totalVolume = _primaryVolumeL + _secondaryVolumeL + _mixingVolumeL;
if (totalVolume == 0) return 0;
return (_secondaryVolumeL / totalVolume) * FillPercentage / 100.0;
}
}
/// <summary>
/// Porcentaje de altura para la sección de mezcla (medio) - para display visual
/// </summary>
[JsonIgnore]
public double MixDisplayPercentage
{
get
{
var totalVolume = _primaryVolumeL + _secondaryVolumeL + _mixingVolumeL;
if (totalVolume == 0) return 0;
return (_mixingVolumeL / totalVolume) * FillPercentage / 100.0;
}
}
/// <summary>
/// Porcentaje de altura para la sección primaria (arriba) - para display visual
/// </summary>
[JsonIgnore]
public double PrimaryDisplayPercentage
{
get
{
var totalVolume = _primaryVolumeL + _secondaryVolumeL + _mixingVolumeL;
if (totalVolume == 0) return 0;
return (_primaryVolumeL / totalVolume) * FillPercentage / 100.0;
}
}
/// <summary>
/// Color para la sección secundaria
/// </summary>
[JsonIgnore]
public SolidColorBrush SecondaryLevelColor
{
get
{
var colorHex = _secondaryFluid.Color;
var color = (Color)System.Windows.Media.ColorConverter.ConvertFromString(colorHex);
return new SolidColorBrush(color);
}
}
/// <summary>
/// Color para la sección de mezcla
/// </summary>
[JsonIgnore]
public SolidColorBrush MixLevelColor
{
get
{
var mixedFluid = _primaryFluid.MixWith(_secondaryFluid, 0.5); // Mezcla 50-50
var colorHex = mixedFluid.Color;
var color = (Color)System.Windows.Media.ColorConverter.ConvertFromString(colorHex);
return new SolidColorBrush(color);
}
}
/// <summary>
/// Color para la sección primaria
/// </summary>
[JsonIgnore]
public SolidColorBrush PrimaryLevelColor
{
get
{
var colorHex = _primaryFluid.Color;
var color = (Color)System.Windows.Media.ColorConverter.ConvertFromString(colorHex);
return new SolidColorBrush(color);
}
}
// Private Methods // Private Methods

View File

@ -34,23 +34,70 @@
RadiusX="5" RadiusX="5"
RadiusY="5"/> RadiusY="5"/>
<!-- Nivel del líquido --> <!-- Nivel del líquido - 3 secciones -->
<Rectangle x:Name="rectLevel"
Fill="{Binding LevelColor}" <!-- Sección secundaria (abajo) -->
Stroke="{Binding LevelBorderColor}" <Rectangle x:Name="rectSecondaryLevel"
StrokeThickness="1" Fill="{Binding SecondaryLevelColor}"
Stroke="DarkBlue"
StrokeThickness="0.5"
RadiusX="3" RadiusX="3"
RadiusY="3" RadiusY="3"
VerticalAlignment="Bottom" VerticalAlignment="Bottom"
Margin="4,4,4,4"> Margin="4,4,4,4">
<Rectangle.Height> <Rectangle.Height>
<MultiBinding Converter="{StaticResource TankLevelToHeightConverter}"> <MultiBinding Converter="{StaticResource TankLevelToHeightConverter}">
<Binding Path="FillPercentage"/> <Binding Path="SecondaryDisplayPercentage"/>
<Binding Path="Tamano"/> <Binding Path="Tamano"/>
</MultiBinding> </MultiBinding>
</Rectangle.Height> </Rectangle.Height>
</Rectangle> </Rectangle>
<!-- Sección de mezcla (medio) -->
<Rectangle x:Name="rectMixLevel"
Fill="{Binding MixLevelColor}"
Stroke="Purple"
StrokeThickness="0.5"
RadiusX="3"
RadiusY="3"
VerticalAlignment="Bottom">
<Rectangle.Height>
<MultiBinding Converter="{StaticResource TankLevelToHeightConverter}">
<Binding Path="MixDisplayPercentage"/>
<Binding Path="Tamano"/>
</MultiBinding>
</Rectangle.Height>
<Rectangle.Margin>
<MultiBinding Converter="{StaticResource TankSectionMarginConverter}">
<Binding Path="SecondaryDisplayPercentage"/>
<Binding Path="Tamano"/>
</MultiBinding>
</Rectangle.Margin>
</Rectangle>
<!-- Sección primaria (arriba) -->
<Rectangle x:Name="rectPrimaryLevel"
Fill="{Binding PrimaryLevelColor}"
Stroke="DarkGreen"
StrokeThickness="0.5"
RadiusX="3"
RadiusY="3"
VerticalAlignment="Bottom">
<Rectangle.Height>
<MultiBinding Converter="{StaticResource TankLevelToHeightConverter}">
<Binding Path="PrimaryDisplayPercentage"/>
<Binding Path="Tamano"/>
</MultiBinding>
</Rectangle.Height>
<Rectangle.Margin>
<MultiBinding Converter="{StaticResource TankSectionMarginConverter}">
<Binding Path="SecondaryDisplayPercentage"/>
<Binding Path="MixDisplayPercentage"/>
<Binding Path="Tamano"/>
</MultiBinding>
</Rectangle.Margin>
</Rectangle>
<!-- Líneas de nivel (marcas visuales) --> <!-- Líneas de nivel (marcas visuales) -->
<Canvas> <Canvas>
<!-- Línea de nivel máximo --> <!-- Línea de nivel máximo -->

View File

@ -27,6 +27,11 @@ namespace CtrEditor.Services
// Custom TraceListener para capturar Debug.WriteLine // Custom TraceListener para capturar Debug.WriteLine
private DebugTraceListener _traceListener; private DebugTraceListener _traceListener;
// Control de buffer para evitar acumulación excesiva
private const int MAX_BUFFER_SIZE = 1000;
private int _messageCount = 0;
private readonly object _bufferLock = new object();
public DebugConsoleServer(int port = 5007) public DebugConsoleServer(int port = 5007)
{ {
_port = port; _port = port;
@ -175,6 +180,10 @@ namespace CtrEditor.Services
{ {
if (_messageQueue.TryDequeue(out string message)) if (_messageQueue.TryDequeue(out string message))
{ {
lock (_bufferLock)
{
_messageCount = Math.Max(0, _messageCount - 1);
}
await BroadcastMessageAsync(message); await BroadcastMessageAsync(message);
} }
else else
@ -243,8 +252,61 @@ namespace CtrEditor.Services
public void QueueMessage(string message) public void QueueMessage(string message)
{ {
if (_isRunning) if (_isRunning)
{
lock (_bufferLock)
{ {
_messageQueue.Enqueue(message); _messageQueue.Enqueue(message);
_messageCount++;
// Si el buffer se está llenando demasiado, hacer un flush automático
if (_messageCount > MAX_BUFFER_SIZE)
{
FlushMessageBuffer();
}
}
}
}
/// <summary>
/// Limpia el buffer de mensajes para evitar acumulación excesiva
/// </summary>
public void FlushMessageBuffer()
{
lock (_bufferLock)
{
// Mantener solo los últimos 100 mensajes
var messagesToKeep = new List<string>();
var count = 0;
while (_messageQueue.TryDequeue(out string message) && count < 100)
{
messagesToKeep.Insert(0, message); // Insertar al inicio para mantener orden
count++;
}
// Limpiar completamente la cola
while (_messageQueue.TryDequeue(out _)) { }
// Volver a agregar los mensajes que queremos mantener
foreach (var message in messagesToKeep)
{
_messageQueue.Enqueue(message);
}
_messageCount = messagesToKeep.Count;
Debug.WriteLine($"[Debug Console Server] Buffer flushed. Messages kept: {_messageCount}");
}
}
/// <summary>
/// Obtiene estadísticas del buffer de mensajes
/// </summary>
public (int messageCount, int maxBufferSize) GetBufferStats()
{
lock (_bufferLock)
{
return (_messageCount, MAX_BUFFER_SIZE);
} }
} }