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
247
MainViewModel.cs
247
MainViewModel.cs
|
@ -18,6 +18,7 @@ using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using Xceed.Wpf.Toolkit.PropertyGrid;
|
using Xceed.Wpf.Toolkit.PropertyGrid;
|
||||||
using CtrEditor.ObjetosSim.Extraccion_Datos;
|
using CtrEditor.ObjetosSim.Extraccion_Datos;
|
||||||
using ClosedXML.Excel;
|
using ClosedXML.Excel;
|
||||||
|
using System.Timers; // Para el nuevo timer de simulación más preciso
|
||||||
using CtrEditor.PopUps;
|
using CtrEditor.PopUps;
|
||||||
using System.Windows.Data;
|
using System.Windows.Data;
|
||||||
using CommunityToolkit.Mvvm.Input;
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
@ -44,13 +45,22 @@ namespace CtrEditor
|
||||||
private int simSampleCount;
|
private int simSampleCount;
|
||||||
private int plcSampleCount;
|
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 float TiempoDesdeStartSimulacion;
|
||||||
private bool Debug_SimulacionCreado = false;
|
private bool Debug_SimulacionCreado = false;
|
||||||
|
|
||||||
public SimulationManagerBEPU simulationManager = new SimulationManagerBEPU();
|
public SimulationManagerBEPU simulationManager = new SimulationManagerBEPU();
|
||||||
|
|
||||||
private readonly DispatcherTimer _timerSimulacion;
|
private readonly System.Timers.Timer _timerSimulacion; // Cambiado a System.Timers.Timer para mejor precisión
|
||||||
private readonly DispatcherTimer _timerPLCUpdate;
|
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
|
||||||
|
|
||||||
|
@ -380,17 +390,21 @@ namespace CtrEditor
|
||||||
// Inicializa el PLCViewModel
|
// Inicializa el PLCViewModel
|
||||||
PLCViewModel = new PLCViewModel();
|
PLCViewModel = new PLCViewModel();
|
||||||
|
|
||||||
_timerPLCUpdate = new DispatcherTimer();
|
_timerPLCUpdate = new System.Timers.Timer();
|
||||||
_timerPLCUpdate.Interval = TimeSpan.FromMilliseconds(10); // Restaurado a 10ms
|
_timerPLCUpdate.Interval = 10; // 10ms - más preciso que DispatcherTimer
|
||||||
_timerPLCUpdate.Tick += OnRefreshEvent;
|
_timerPLCUpdate.Elapsed += OnRefreshEvent;
|
||||||
|
_timerPLCUpdate.AutoReset = true; // Reinicio automático
|
||||||
|
_timerPLCUpdate.SynchronizingObject = null; // No sincronizar automáticamente con UI
|
||||||
|
|
||||||
InitializeTipoSimulableList();
|
InitializeTipoSimulableList();
|
||||||
|
|
||||||
ItemDoubleClickCommand = new ParameterizedRelayCommand(ExecuteDoubleClick);
|
ItemDoubleClickCommand = new ParameterizedRelayCommand(ExecuteDoubleClick);
|
||||||
|
|
||||||
_timerSimulacion = new DispatcherTimer();
|
_timerSimulacion = new System.Timers.Timer();
|
||||||
_timerSimulacion.Interval = TimeSpan.FromMilliseconds(10); // Restaurado a 10ms
|
_timerSimulacion.Interval = 10; // 10ms - más preciso que DispatcherTimer
|
||||||
_timerSimulacion.Tick += OnTickSimulacion;
|
_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
|
// Nuevo timer para actualización de display
|
||||||
_timerDisplayUpdate = new DispatcherTimer();
|
_timerDisplayUpdate = new DispatcherTimer();
|
||||||
|
@ -1104,6 +1118,7 @@ namespace CtrEditor
|
||||||
Debug_SimulacionCreado = false;
|
Debug_SimulacionCreado = false;
|
||||||
}
|
}
|
||||||
_timerSimulacion.Stop();
|
_timerSimulacion.Stop();
|
||||||
|
_timerPLCUpdate.Stop(); // También detener el timer PLC
|
||||||
|
|
||||||
// Restaurar los rectángulos de selección si hay objetos seleccionados
|
// Restaurar los rectángulos de selección si hay objetos seleccionados
|
||||||
_objectManager.UpdateSelectionVisuals();
|
_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
|
// Verificar si la aplicación todavía está disponible
|
||||||
|
if (Application.Current == null) return;
|
||||||
|
|
||||||
// Detener el cronómetro y obtener el tiempo transcurrido en milisegundos
|
// Ejecutar en el thread de UI para acceso a controles
|
||||||
var elapsedMilliseconds = stopwatch_Sim.Elapsed.TotalMilliseconds - stopwatch_SimModel_last;
|
Application.Current.Dispatcher.Invoke(() =>
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
if (!objetoSimulable.RemoverDesdeSimulacion)
|
var executionStopwatch = Stopwatch.StartNew(); // ✅ NUEVO: Medir tiempo de ejecución del método
|
||||||
objetoSimulable.UpdateControl((int)elapsedMilliseconds);
|
|
||||||
else
|
|
||||||
RemoverObjetoSimulable(objetoSimulable);
|
|
||||||
}
|
|
||||||
|
|
||||||
stopwatch.Stop(); // Stop measuring time
|
// ✅ MANTENER: Tiempo entre llamadas del timer para lógica interna
|
||||||
//Debug.WriteLine($"OnTickSimulacion execution time: {stopwatch.TotalMilliseconds} ms");
|
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()
|
private void ConnectPLC()
|
||||||
|
@ -1168,45 +1199,117 @@ namespace CtrEditor
|
||||||
MainWindow?.ClearUndoHistory();
|
MainWindow?.ClearUndoHistory();
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<osBase> objetosSimulablesLlamados = new List<osBase>();
|
/// <summary>
|
||||||
|
/// Limpia todos los recursos y detiene todos los timers antes del cierre de la aplicación
|
||||||
private void OnRefreshEvent(object sender, EventArgs e)
|
/// </summary>
|
||||||
|
public void CleanupResources()
|
||||||
{
|
{
|
||||||
var stopwatch = Stopwatch.StartNew(); // Start measuring time
|
// Detener simulación si está corriendo
|
||||||
|
if (IsSimulationRunning)
|
||||||
if (PLCViewModel.IsConnected)
|
|
||||||
{
|
{
|
||||||
// Detener el cronómetro y obtener el tiempo transcurrido en milisegundos
|
StopSimulation();
|
||||||
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
|
// Detener todos los timers
|
||||||
// Debug.WriteLine($"OnRefreshEvent: {stopwatch.Elapsed.TotalMilliseconds} ms");
|
_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()
|
private void OpenWorkDirectory()
|
||||||
|
|
|
@ -491,6 +491,9 @@ namespace CtrEditor
|
||||||
if (DataContext is MainViewModel viewModel)
|
if (DataContext is MainViewModel viewModel)
|
||||||
{
|
{
|
||||||
viewModel.ImageSelected -= ViewModel_ImageSelected;
|
viewModel.ImageSelected -= ViewModel_ImageSelected;
|
||||||
|
|
||||||
|
// Limpiar todos los recursos antes del cierre
|
||||||
|
viewModel.CleanupResources();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1013,6 +1013,17 @@ namespace CtrEditor.Simulacion
|
||||||
{
|
{
|
||||||
_frameCount++;
|
_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
|
// ✅ NUEVO: Resetear el estado de contacto para el sistema de presión
|
||||||
foreach (var botella in Cuerpos.OfType<simBotella>())
|
foreach (var botella in Cuerpos.OfType<simBotella>())
|
||||||
{
|
{
|
||||||
|
@ -1109,6 +1120,9 @@ namespace CtrEditor.Simulacion
|
||||||
_descarteContacts.Clear();
|
_descarteContacts.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ✅ NUEVO: Actualizar el tiempo global de simulación
|
||||||
|
GlobalTime += deltaTime;
|
||||||
|
|
||||||
// ✅ CONSERVAR - sincronización 3D
|
// ✅ CONSERVAR - sincronización 3D
|
||||||
if (Is3DUpdateEnabled)
|
if (Is3DUpdateEnabled)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue