Implementar temporizadores más precisos utilizando System.Timers en MainViewModel y optimizar la lógica de simulación en BEPU para evitar cálculos innecesarios cuando no hay objetos simulables.
This commit is contained in:
parent
091170b70d
commit
1e6ad6377e
249
MainViewModel.cs
249
MainViewModel.cs
|
@ -18,6 +18,7 @@ using CommunityToolkit.Mvvm.ComponentModel;
|
|||
using Xceed.Wpf.Toolkit.PropertyGrid;
|
||||
using CtrEditor.ObjetosSim.Extraccion_Datos;
|
||||
using ClosedXML.Excel;
|
||||
using System.Timers; // Para el nuevo timer de simulación más preciso
|
||||
using CtrEditor.PopUps;
|
||||
using System.Windows.Data;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
|
@ -44,13 +45,22 @@ namespace CtrEditor
|
|||
private int simSampleCount;
|
||||
private int plcSampleCount;
|
||||
|
||||
// Sistema adaptativo para timing de PLC
|
||||
private double lastPlcExecutionTime = 0;
|
||||
private double maxPlcExecutionTime = 0;
|
||||
private int plcTimingAdaptationCounter = 0;
|
||||
private const int PLC_ADAPTATION_SAMPLES = 10; // Evaluar cada 10 muestras
|
||||
private const double MIN_PLC_INTERVAL = 10; // Mínimo intervalo PLC (ms)
|
||||
private const double MAX_PLC_INTERVAL = 100; // Máximo intervalo PLC (ms)
|
||||
private const double SIM_PRIORITY_TIME = 10; // Tiempo reservado para simulación (ms)
|
||||
|
||||
private float TiempoDesdeStartSimulacion;
|
||||
private bool Debug_SimulacionCreado = false;
|
||||
|
||||
public SimulationManagerBEPU simulationManager = new SimulationManagerBEPU();
|
||||
|
||||
private readonly DispatcherTimer _timerSimulacion;
|
||||
private readonly DispatcherTimer _timerPLCUpdate;
|
||||
private readonly System.Timers.Timer _timerSimulacion; // 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 _timer3DUpdate; // Nuevo timer para actualización 3D cuando simulación está detenida
|
||||
|
||||
|
@ -380,17 +390,21 @@ namespace CtrEditor
|
|||
// Inicializa el PLCViewModel
|
||||
PLCViewModel = new PLCViewModel();
|
||||
|
||||
_timerPLCUpdate = new DispatcherTimer();
|
||||
_timerPLCUpdate.Interval = TimeSpan.FromMilliseconds(10); // Restaurado a 10ms
|
||||
_timerPLCUpdate.Tick += OnRefreshEvent;
|
||||
_timerPLCUpdate = new System.Timers.Timer();
|
||||
_timerPLCUpdate.Interval = 10; // 10ms - más preciso que DispatcherTimer
|
||||
_timerPLCUpdate.Elapsed += OnRefreshEvent;
|
||||
_timerPLCUpdate.AutoReset = true; // Reinicio automático
|
||||
_timerPLCUpdate.SynchronizingObject = null; // No sincronizar automáticamente con UI
|
||||
|
||||
InitializeTipoSimulableList();
|
||||
|
||||
ItemDoubleClickCommand = new ParameterizedRelayCommand(ExecuteDoubleClick);
|
||||
|
||||
_timerSimulacion = new DispatcherTimer();
|
||||
_timerSimulacion.Interval = TimeSpan.FromMilliseconds(10); // Restaurado a 10ms
|
||||
_timerSimulacion.Tick += OnTickSimulacion;
|
||||
_timerSimulacion = new System.Timers.Timer();
|
||||
_timerSimulacion.Interval = 10; // 10ms - más preciso que DispatcherTimer
|
||||
_timerSimulacion.Elapsed += OnTickSimulacion;
|
||||
_timerSimulacion.AutoReset = true; // Reinicio automático
|
||||
_timerSimulacion.SynchronizingObject = null; // No sincronizar automáticamente con UI
|
||||
|
||||
// Nuevo timer para actualización de display
|
||||
_timerDisplayUpdate = new DispatcherTimer();
|
||||
|
@ -1104,6 +1118,7 @@ namespace CtrEditor
|
|||
Debug_SimulacionCreado = false;
|
||||
}
|
||||
_timerSimulacion.Stop();
|
||||
_timerPLCUpdate.Stop(); // También detener el timer PLC
|
||||
|
||||
// Restaurar los rectángulos de selección si hay objetos seleccionados
|
||||
_objectManager.UpdateSelectionVisuals();
|
||||
|
@ -1114,39 +1129,55 @@ namespace CtrEditor
|
|||
|
||||
|
||||
|
||||
private void OnTickSimulacion(object sender, EventArgs e)
|
||||
private void OnTickSimulacion(object sender, System.Timers.ElapsedEventArgs e)
|
||||
{
|
||||
var stopwatch = Stopwatch.StartNew(); // Start measuring time
|
||||
|
||||
// Detener el cronómetro y obtener el tiempo transcurrido en milisegundos
|
||||
var elapsedMilliseconds = stopwatch_Sim.Elapsed.TotalMilliseconds - stopwatch_SimModel_last;
|
||||
stopwatch_SimModel_last = stopwatch_Sim.Elapsed.TotalMilliseconds;
|
||||
|
||||
// Acumular tiempo para el promedio
|
||||
accumulatedSimTime += elapsedMilliseconds;
|
||||
simSampleCount++;
|
||||
|
||||
// Contador de tiempo desde el inicio de la simulación
|
||||
if (TiempoDesdeStartSimulacion <= 1200)
|
||||
TiempoDesdeStartSimulacion += (float)elapsedMilliseconds;
|
||||
|
||||
foreach (var objetoSimulable in ObjetosSimulables)
|
||||
objetoSimulable.UpdateGeometryStep();
|
||||
|
||||
simulationManager.Step();
|
||||
|
||||
var objetosSimulablesCopy = new List<osBase>(ObjetosSimulables);
|
||||
|
||||
foreach (var objetoSimulable in objetosSimulablesCopy)
|
||||
// Verificar si la aplicación todavía está disponible
|
||||
if (Application.Current == null) return;
|
||||
|
||||
// Ejecutar en el thread de UI para acceso a controles
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
if (!objetoSimulable.RemoverDesdeSimulacion)
|
||||
objetoSimulable.UpdateControl((int)elapsedMilliseconds);
|
||||
else
|
||||
RemoverObjetoSimulable(objetoSimulable);
|
||||
}
|
||||
var executionStopwatch = Stopwatch.StartNew(); // ✅ NUEVO: Medir tiempo de ejecución del método
|
||||
|
||||
stopwatch.Stop(); // Stop measuring time
|
||||
//Debug.WriteLine($"OnTickSimulacion execution time: {stopwatch.TotalMilliseconds} ms");
|
||||
// ✅ MANTENER: Tiempo entre llamadas del timer para lógica interna
|
||||
var timeBetweenCalls = stopwatch_Sim.Elapsed.TotalMilliseconds - stopwatch_SimModel_last;
|
||||
stopwatch_SimModel_last = stopwatch_Sim.Elapsed.TotalMilliseconds;
|
||||
|
||||
// Acumular tiempo para el promedio (usando tiempo real del timer)
|
||||
accumulatedSimTime += timeBetweenCalls;
|
||||
simSampleCount++;
|
||||
|
||||
// Contador de tiempo desde el inicio de la simulación
|
||||
if (TiempoDesdeStartSimulacion <= 1200)
|
||||
TiempoDesdeStartSimulacion += (float)timeBetweenCalls;
|
||||
|
||||
// ✅ NUEVO: Optimización temprana para escenarios sin objetos
|
||||
if (ObjetosSimulables?.Count > 0)
|
||||
{
|
||||
foreach (var objetoSimulable in ObjetosSimulables)
|
||||
objetoSimulable.UpdateGeometryStep();
|
||||
}
|
||||
|
||||
simulationManager.Step();
|
||||
|
||||
// ✅ NUEVO: Solo crear la copia si hay objetos para procesar
|
||||
if (ObjetosSimulables?.Count > 0)
|
||||
{
|
||||
var objetosSimulablesCopy = new List<osBase>(ObjetosSimulables);
|
||||
|
||||
foreach (var objetoSimulable in objetosSimulablesCopy)
|
||||
{
|
||||
if (!objetoSimulable.RemoverDesdeSimulacion)
|
||||
objetoSimulable.UpdateControl((int)timeBetweenCalls);
|
||||
else
|
||||
RemoverObjetoSimulable(objetoSimulable);
|
||||
}
|
||||
}
|
||||
|
||||
executionStopwatch.Stop(); // ✅ CORREGIDO: Detener cronómetro de ejecución
|
||||
//Debug.WriteLine($"OnTickSimulacion execution time: {stopwatch.ElapsedMilliseconds} ms");
|
||||
//Debug.WriteLine($"OnTickSimulacion execution time: {executionStopwatch.Elapsed.TotalMilliseconds:F2}ms | Timer interval: {timeBetweenCalls:F2}ms | Objects: {ObjetosSimulables?.Count ?? 0}");
|
||||
});
|
||||
}
|
||||
|
||||
private void ConnectPLC()
|
||||
|
@ -1168,45 +1199,117 @@ namespace CtrEditor
|
|||
MainWindow?.ClearUndoHistory();
|
||||
}
|
||||
|
||||
private List<osBase> objetosSimulablesLlamados = new List<osBase>();
|
||||
|
||||
private void OnRefreshEvent(object sender, EventArgs e)
|
||||
/// <summary>
|
||||
/// Limpia todos los recursos y detiene todos los timers antes del cierre de la aplicación
|
||||
/// </summary>
|
||||
public void CleanupResources()
|
||||
{
|
||||
var stopwatch = Stopwatch.StartNew(); // Start measuring time
|
||||
|
||||
if (PLCViewModel.IsConnected)
|
||||
// Detener simulación si está corriendo
|
||||
if (IsSimulationRunning)
|
||||
{
|
||||
// Detener el cronómetro y obtener el tiempo transcurrido en milisegundos
|
||||
var elapsedMilliseconds = stopwatch_Sim.Elapsed.TotalMilliseconds - stopwatch_SimPLC_last;
|
||||
stopwatch_SimPLC_last = stopwatch_Sim.Elapsed.TotalMilliseconds;
|
||||
|
||||
// Acumular tiempo para el promedio
|
||||
accumulatedPlcTime += elapsedMilliseconds;
|
||||
plcSampleCount++;
|
||||
|
||||
// Reiniciar el cronómetro para la próxima medición
|
||||
var remainingObjetosSimulables = ObjetosSimulables.Except(objetosSimulablesLlamados).ToList();
|
||||
|
||||
foreach (var objetoSimulable in remainingObjetosSimulables)
|
||||
{
|
||||
var objStopwatch = Stopwatch.StartNew();
|
||||
objetoSimulable.UpdatePLC(PLCViewModel, (int)elapsedMilliseconds);
|
||||
objStopwatch.Stop();
|
||||
|
||||
objetosSimulablesLlamados.Add(objetoSimulable);
|
||||
|
||||
if (stopwatch.Elapsed.TotalMilliseconds >= 10)
|
||||
break;
|
||||
}
|
||||
|
||||
if (remainingObjetosSimulables.Count == 0)
|
||||
{
|
||||
objetosSimulablesLlamados.Clear();
|
||||
}
|
||||
StopSimulation();
|
||||
}
|
||||
|
||||
stopwatch.Stop(); // Stop measuring time
|
||||
// Debug.WriteLine($"OnRefreshEvent: {stopwatch.Elapsed.TotalMilliseconds} ms");
|
||||
// Detener todos los timers
|
||||
_timerSimulacion?.Stop();
|
||||
_timerPLCUpdate?.Stop();
|
||||
_timer3DUpdate?.Stop();
|
||||
|
||||
// Desconectar PLC si está conectado
|
||||
if (PLCViewModel?.IsConnected == true)
|
||||
{
|
||||
PLCViewModel.Disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
private List<osBase> objetosSimulablesLlamados = new List<osBase>();
|
||||
|
||||
private void OnRefreshEvent(object sender, System.Timers.ElapsedEventArgs e)
|
||||
{
|
||||
// Verificar si la aplicación todavía está disponible
|
||||
if (Application.Current == null) return;
|
||||
|
||||
// Ejecutar en el thread de UI para acceso a controles
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
var stopwatch = Stopwatch.StartNew(); // Start measuring time
|
||||
|
||||
if (PLCViewModel.IsConnected)
|
||||
{
|
||||
// Detener el cronómetro y obtener el tiempo transcurrido en milisegundos
|
||||
var elapsedMilliseconds = stopwatch_Sim.Elapsed.TotalMilliseconds - stopwatch_SimPLC_last;
|
||||
stopwatch_SimPLC_last = stopwatch_Sim.Elapsed.TotalMilliseconds;
|
||||
|
||||
// Acumular tiempo para el promedio
|
||||
accumulatedPlcTime += elapsedMilliseconds;
|
||||
plcSampleCount++;
|
||||
|
||||
// Reiniciar el cronómetro para la próxima medición
|
||||
var remainingObjetosSimulables = ObjetosSimulables.Except(objetosSimulablesLlamados).ToList();
|
||||
|
||||
foreach (var objetoSimulable in remainingObjetosSimulables)
|
||||
{
|
||||
var objStopwatch = Stopwatch.StartNew();
|
||||
objetoSimulable.UpdatePLC(PLCViewModel, (int)elapsedMilliseconds);
|
||||
objStopwatch.Stop();
|
||||
|
||||
objetosSimulablesLlamados.Add(objetoSimulable);
|
||||
|
||||
if (stopwatch.Elapsed.TotalMilliseconds >= 10)
|
||||
break;
|
||||
}
|
||||
|
||||
if (remainingObjetosSimulables.Count == 0)
|
||||
{
|
||||
objetosSimulablesLlamados.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
stopwatch.Stop(); // Stop measuring time
|
||||
|
||||
// Sistema adaptativo de timing PLC
|
||||
AdaptPlcTiming(stopwatch.Elapsed.TotalMilliseconds);
|
||||
|
||||
// Debug.WriteLine($"OnRefreshEvent: {stopwatch.Elapsed.TotalMilliseconds} ms, Interval: {_timerPLCUpdate.Interval}ms");
|
||||
});
|
||||
}
|
||||
|
||||
private void AdaptPlcTiming(double executionTime)
|
||||
{
|
||||
lastPlcExecutionTime = executionTime;
|
||||
maxPlcExecutionTime = Math.Max(maxPlcExecutionTime, executionTime);
|
||||
plcTimingAdaptationCounter++;
|
||||
|
||||
// Evaluar cada PLC_ADAPTATION_SAMPLES ejecuciones
|
||||
if (plcTimingAdaptationCounter >= PLC_ADAPTATION_SAMPLES)
|
||||
{
|
||||
double currentInterval = _timerPLCUpdate.Interval;
|
||||
double newInterval = currentInterval;
|
||||
|
||||
// Si el tiempo máximo de ejecución + tiempo para simulación > intervalo actual
|
||||
if (maxPlcExecutionTime + SIM_PRIORITY_TIME > currentInterval)
|
||||
{
|
||||
// Incrementar intervalo para dar más tiempo
|
||||
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)");
|
||||
}
|
||||
// Si hemos estado por debajo del umbral, podemos reducir el intervalo
|
||||
else if (maxPlcExecutionTime + SIM_PRIORITY_TIME < currentInterval * 0.7)
|
||||
{
|
||||
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)");
|
||||
}
|
||||
|
||||
// Aplicar el nuevo intervalo si cambió
|
||||
if (Math.Abs(newInterval - currentInterval) > 1)
|
||||
{
|
||||
_timerPLCUpdate.Interval = newInterval;
|
||||
}
|
||||
|
||||
// Reset para el próximo período de evaluación
|
||||
maxPlcExecutionTime = 0;
|
||||
plcTimingAdaptationCounter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void OpenWorkDirectory()
|
||||
|
|
|
@ -491,6 +491,9 @@ namespace CtrEditor
|
|||
if (DataContext is MainViewModel viewModel)
|
||||
{
|
||||
viewModel.ImageSelected -= ViewModel_ImageSelected;
|
||||
|
||||
// Limpiar todos los recursos antes del cierre
|
||||
viewModel.CleanupResources();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1013,6 +1013,17 @@ namespace CtrEditor.Simulacion
|
|||
{
|
||||
_frameCount++;
|
||||
|
||||
// ✅ NUEVO: Verificación temprana para cuando no hay objetos simulables
|
||||
// Esto evita cálculos de tiempo irregulares cuando no hay trabajo que hacer
|
||||
if (Cuerpos == null || Cuerpos.Count == 0)
|
||||
{
|
||||
// Mantener el cronómetro actualizado pero usar deltaTime fijo
|
||||
var currentTimeEmpty = stopwatch.Elapsed.TotalMilliseconds;
|
||||
stopwatch_last = currentTimeEmpty;
|
||||
GlobalTime += 1f / 60f; // Incremento fijo de 60 FPS
|
||||
return;
|
||||
}
|
||||
|
||||
// ✅ NUEVO: Resetear el estado de contacto para el sistema de presión
|
||||
foreach (var botella in Cuerpos.OfType<simBotella>())
|
||||
{
|
||||
|
@ -1109,6 +1120,9 @@ namespace CtrEditor.Simulacion
|
|||
_descarteContacts.Clear();
|
||||
}
|
||||
|
||||
// ✅ NUEVO: Actualizar el tiempo global de simulación
|
||||
GlobalTime += deltaTime;
|
||||
|
||||
// ✅ CONSERVAR - sincronización 3D
|
||||
if (Is3DUpdateEnabled)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue