using System.ComponentModel; using System.Runtime.CompilerServices; using System.Windows.Controls; using System.Windows.Input; using Ookii.Dialogs.Wpf; using System.Collections.ObjectModel; using System.Windows.Threading; using CtrEditor.ObjetosSim; using CtrEditor.Siemens; using System.IO; using Newtonsoft.Json; using System.Windows; using CtrEditor.Simulacion; using System.Diagnostics; using System.Reflection; using CommunityToolkit.Mvvm.ComponentModel; using Xceed.Wpf.Toolkit.PropertyGrid; namespace CtrEditor { public partial class MainViewModel : ObservableObject { public Stopwatch stopwatch_Sim; private double stopwatch_SimPLC_last; private double stopwatch_SimModel_last; private float TiempoDesdeStartSimulacion; private bool Debug_SimulacionCreado = false; public SimulationManagerFP simulationManager = new SimulationManagerFP(); private readonly DispatcherTimer _timerSimulacion; public Canvas MainCanvas; [ObservableProperty] private DatosDeTrabajo datosDeTrabajo; [ObservableProperty] private ObservableCollection listaImagenes; // Publicación de las claves del diccionario [ObservableProperty] public ObservableCollection listaOsBase; public ICommand StartSimulationCommand { get; } public ICommand StopSimulationCommand { get; } public ICommand ItemDoubleClickCommand { get; private set; } public ICommand TBStartSimulationCommand { get; } public ICommand TBStopSimulationCommand { get; } public ICommand TBSaveCommand { get; } public ICommand TBConnectPLCCommand { get; } public ICommand TBDisconnectPLCCommand { get; } public ICommand TBEliminarUserControlCommand { get; } public ICommand TBDuplicarUserControlCommand { get; } public ICommand OpenWorkDirectoryCommand { get; } // Evento que se dispara cuando se selecciona una nueva imagen public event EventHandler ImageSelected; public event EventHandler TickSimulacion; // Propiedades private bool habilitarEliminarUserControl; private MainWindow mainWindow; public MainWindow MainWindow { get => mainWindow; set => mainWindow = value; } [ObservableProperty] private float canvasLeft; [ObservableProperty] private float canvasTop; [ObservableProperty] private bool isSimulationRunning; partial void OnIsSimulationRunningChanged(bool value) { CommandManager.InvalidateRequerySuggested(); // Notificar que el estado de los comandos ha cambiado } [ObservableProperty] private bool isConnected; partial void OnIsConnectedChanged(bool value) { CommandManager.InvalidateRequerySuggested(); } public string directorioTrabajo { get => EstadoPersistente.Instance.directorio; set { if (value != null) { EstadoPersistente.Instance.directorio = value; // Actualizar el estado persistente EstadoPersistente.Instance.GuardarEstado(); // Guardar el estado actualizado DatosDeTrabajo.CargarImagenes(); ListaImagenes = new ObservableCollection(DatosDeTrabajo.Imagenes.Keys); // Actualizar claves SelectedImage = null; if (ListaImagenes.FirstOrDefault() != null) SelectedImage = ListaImagenes.FirstOrDefault(); OnPropertyChanged(nameof(directorioTrabajo)); // Notificar el cambio de propiedad OnPropertyChanged(nameof(ListaImagenes)); // Notificar que la lista de imágenes ha cambiado } } } [ObservableProperty] private PLCViewModel pLCViewModel; [ObservableProperty] private string selectedImage; partial void OnSelectedImageChanged(string value) { if (value != null) { StopSimulation(); // SaveStateObjetosSimulables(); // Guarda el estado antes de cambiar la imagen ImageSelected?.Invoke(this, datosDeTrabajo.Imagenes[value]); // Dispara el evento con la nueva ruta de imagen LoadStateObjetosSimulables(); } } [ObservableProperty] private osBase selectedItemOsList; partial void OnSelectedItemOsListChanged(osBase value) { if (value != null) habilitarEliminarUserControl = true; else habilitarEliminarUserControl = false; } [ObservableProperty] private TipoSimulable selectedItem; [ObservableProperty] public ObservableCollection objetosSimulables; // // Constructor // public MainViewModel() { OpenWorkDirectoryCommand = new RelayCommand(OpenWorkDirectory); datosDeTrabajo = new DatosDeTrabajo(); ObjetosSimulables = new ObservableCollection(); ListaOsBase = new ObservableCollection(); // Inicializa el PLCViewModel PLCViewModel = new PLCViewModel(); PLCViewModel.RefreshEvent += OnRefreshEvent; InitializeTipoSimulableList(); ItemDoubleClickCommand = new ParameterizedRelayCommand(ExecuteDoubleClick); _timerSimulacion = new DispatcherTimer(); _timerSimulacion.Interval = TimeSpan.FromMilliseconds(1); // ajusta el intervalo según sea necesario _timerSimulacion.Tick += OnTickSimulacion; StartSimulationCommand = new RelayCommand(StartSimulation); StopSimulationCommand = new RelayCommand(StopSimulation); TBStartSimulationCommand = new RelayCommand(StartSimulation, () => !IsSimulationRunning); TBStopSimulationCommand = new RelayCommand(StopSimulation, () => IsSimulationRunning); TBSaveCommand = new RelayCommand(Save); TBConnectPLCCommand = new RelayCommand(ConnectPLC, () => !IsConnected); TBDisconnectPLCCommand = new RelayCommand(DisconnectPLC, () => IsConnected); TBEliminarUserControlCommand = new RelayCommand(EliminarUserControl, () => habilitarEliminarUserControl); TBDuplicarUserControlCommand = new RelayCommand(DuplicarUserControl, () => habilitarEliminarUserControl); stopwatch_Sim = new Stopwatch(); stopwatch_Sim.Start(); } public void LoadInitialData() { // Suponiendo que "SelectedImage" es una propiedad que al establecerse dispara "ImageSelected" directorioTrabajo = EstadoPersistente.Instance.directorio; } // Crear un nuevo Objeto private void ExecuteDoubleClick(object parameter) { if (parameter is TipoSimulable tipoSimulable) { CrearObjetoSimulableEnCentroCanvas(tipoSimulable.Tipo); } } public void CrearObjetoSimulableEnCentroCanvas(Type tipoSimulable) { var CentroCanvas = MainWindow.ObtenerCentroCanvasMeters(); CrearObjetoSimulable(tipoSimulable, CentroCanvas.X, CentroCanvas.Y); } public osBase CrearObjetoSimulable(Type tipoSimulable, float Left, float Top) { // Crear una nueva instancia del osBase correspondiente osBase? NuevoOsBase = UserControlFactory.GetInstanceForType(tipoSimulable); NuevoOsBase.Left = Left; NuevoOsBase.Top = Top; if (NuevoOsBase != null) { if (CrearUserControlDesdeObjetoSimulable(NuevoOsBase)) // Añadir el nuevo osBase a la colección de objetos simulables ObjetosSimulables.Add(NuevoOsBase); } return NuevoOsBase; } // Crear UserControl desde osBase : Nuevo o desde Deserealizacion private bool CrearUserControlDesdeObjetoSimulable(osBase osObjeto) { Type tipoObjeto = osObjeto.GetType(); // Obtén el UserControl correspondiente para el tipo de objeto UserControl? userControl = UserControlFactory.GetControlForType(tipoObjeto); if (userControl != null) { // Asignar los datos al UserControl UserControlFactory.AssignDatos(userControl, osObjeto, simulationManager); osObjeto._mainViewModel = this; MainWindow.AgregarRegistrarUserControlCanvas(userControl); return true; } return false; } public void RemoverObjetoSimulable(osBase osObjeto) { if (osObjeto != null && ObjetosSimulables.Contains(osObjeto)) { ObjetosSimulables.Remove(osObjeto); if (osObjeto.VisualRepresentation != null) MainWindow.EliminarUserControlDelCanvas(osObjeto.VisualRepresentation); } } private void DuplicarUserControl() { if (SelectedItemOsList is osBase objDuplicar) { StopSimulation(); DisconnectPLC(); objDuplicar.SalvarDatosNoSerializables(); var settings = new JsonSerializerSettings { Formatting = Formatting.Indented, NullValueHandling = NullValueHandling.Ignore, TypeNameHandling = TypeNameHandling.All }; try { // Serializar var serializedData = JsonConvert.SerializeObject(objDuplicar, settings); // Duplicar var NuevoObjetoDuplicado = JsonConvert.DeserializeObject(serializedData, settings); if (NuevoObjetoDuplicado != null) { NuevoObjetoDuplicado.Nombre += "_Duplicado"; NuevoObjetoDuplicado.Left += 0.5f; NuevoObjetoDuplicado.Top += 0.5f; ObjetosSimulables.Add(NuevoObjetoDuplicado); CrearUserControlDesdeObjetoSimulable(NuevoObjetoDuplicado); } } catch { // Log error or handle it accordingly } finally { objDuplicar.RestaurarDatosNoSerializables(); } } } private void EliminarUserControl() { if (SelectedItemOsList is osBase objEliminar) RemoverObjetoSimulable(objEliminar); } private void InitializeTipoSimulableList() { var baseType = typeof(osBase); var types = AppDomain.CurrentDomain.GetAssemblies() .SelectMany(assembly => assembly.GetTypes()) .Where(type => type.IsSubclassOf(baseType) && !type.IsAbstract && typeof(IosBase).IsAssignableFrom(type)); foreach (var type in types) { var methodInfo = type.GetMethod("NombreClase", BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy); string nombre = methodInfo != null ? methodInfo.Invoke(null, null)?.ToString() : "Nombre no encontrado"; ListaOsBase.Add(new TipoSimulable { Nombre = nombre, Tipo = type }); } } private void StartSimulation() { IsSimulationRunning = true; foreach (var objetoSimulable in ObjetosSimulables) objetoSimulable.UpdateGeometryStart(); simulationManager.Debug_DrawInitialBodies(); TiempoDesdeStartSimulacion = 0; Debug_SimulacionCreado = true; _timerSimulacion.Start(); } private void StopSimulation() { IsSimulationRunning = false; foreach (var objetoSimulable in ObjetosSimulables) objetoSimulable.SimulationStop(); if (Debug_SimulacionCreado) { simulationManager.Debug_ClearSimulationShapes(); Debug_SimulacionCreado = false; } _timerSimulacion.Stop(); } private void OnTickSimulacion(object sender, EventArgs e) { // 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; // Eliminar el diseño de Debug luego de 2 segundos if (TiempoDesdeStartSimulacion > 12000) simulationManager.Debug_ClearSimulationShapes(); else TiempoDesdeStartSimulacion += (float)elapsedMilliseconds; foreach (var objetoSimulable in ObjetosSimulables) objetoSimulable.UpdateGeometryStep(); simulationManager.Step(); var objetosSimulablesCopy = new List(ObjetosSimulables); foreach (var objetoSimulable in objetosSimulablesCopy) { if (!objetoSimulable.RemoverDesdeSimulacion) objetoSimulable.UpdateControl((int)elapsedMilliseconds); else RemoverObjetoSimulable(objetoSimulable); } } private void ConnectPLC() { PLCViewModel.Connect(); } private void DisconnectPLC() { PLCViewModel.Disconnect(); IsConnected = false; foreach (var objetoSimulable in ObjetosSimulables) objetoSimulable.SetPLC(null); } private void OnRefreshEvent(object sender, EventArgs e) { if (PLCViewModel.IsConnected) { if (!isConnected) { IsConnected = true; foreach (var objetoSimulable in ObjetosSimulables) objetoSimulable.SetPLC(PLCViewModel.PLCInterface); } // 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; // Reiniciar el cronómetro para la próxima medición foreach (var objetoSimulable in ObjetosSimulables) objetoSimulable.UpdatePLC(PLCViewModel.PLCInterface, (int)elapsedMilliseconds); } } private void OpenWorkDirectory() { var dialog = new VistaFolderBrowserDialog(); if (dialog.ShowDialog() == true) // Mostrar el diálogo y comprobar si el resultado es positivo { directorioTrabajo = dialog.SelectedPath; // Actualizar la propiedad que también actualiza el estado persistente } } // // Lista de osBase // public void Save() { SaveStateObjetosSimulables(); } public void SaveStateObjetosSimulables() { if (SelectedImage != null) { StopSimulation(); DisconnectPLC(); // Ruta del archivo a ser guardado var path = DatosDeTrabajo.ObtenerPathImagenConExtension(SelectedImage, ".json"); // Verificar si el archivo ya existe y crear un respaldo if (File.Exists(path)) { var backupPath = Path.ChangeExtension(path, ".bak"); File.Copy(path, backupPath, true); // Copia el archivo existente a un nuevo archivo .bak, sobrescribiendo si es necesario } foreach (var obj in ObjetosSimulables) // Guardar referencias temporales obj.SalvarDatosNoSerializables(); var settings = new JsonSerializerSettings { Formatting = Formatting.Indented, NullValueHandling = NullValueHandling.Ignore, TypeNameHandling = TypeNameHandling.Auto }; // Crear un objeto que incluya tanto los ObjetosSimulables como el UnitConverter y PLC_ConnectionData var dataToSerialize = new SimulationData { ObjetosSimulables = ObjetosSimulables, UnitConverter = PixelToMeter.Instance.calc, PLC_ConnectionData = PLCViewModel }; // Serializar var serializedData = JsonConvert.SerializeObject(dataToSerialize, settings); File.WriteAllText(path, serializedData); // Escribir el nuevo archivo JSON // Restaurar las propiedades originales de los objetos foreach (var obj in ObjetosSimulables) obj.RestaurarDatosNoSerializables(); } } public void LoadStateObjetosSimulables() { try { StopSimulation(); DisconnectPLC(); ObjetosSimulables.Clear(); simulationManager.Clear(); if (SelectedImage != null) { string jsonPath = datosDeTrabajo.ObtenerPathImagenConExtension(SelectedImage, ".json"); if (File.Exists(jsonPath)) { string jsonString = File.ReadAllText(jsonPath); var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto, ObjectCreationHandling = ObjectCreationHandling.Replace, PreserveReferencesHandling = PreserveReferencesHandling.Objects, ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor }; var simulationData = JsonConvert.DeserializeObject(jsonString, settings); if (simulationData != null) { if (simulationData.ObjetosSimulables is not null) ObjetosSimulables = simulationData.ObjetosSimulables; if (simulationData.PLC_ConnectionData is not null) PLCViewModel = simulationData.PLC_ConnectionData; else PLCViewModel = new PLCViewModel(); PixelToMeter.Instance.calc = simulationData.UnitConverter; // Re-register to the events PLCViewModel.RefreshEvent += OnRefreshEvent; // Recorrer la colección de objetos simulables foreach (var objetoSimulable in ObjetosSimulables) CrearUserControlDesdeObjetoSimulable(objetoSimulable); } } } } catch { /* Consider logging the error or handling it appropriately */ } } // Se cargan los datos de cada UserControl en el StackPanel public void CargarPropiedadesosDatos(osBase selectedObject, PropertyGrid PanelEdicion, ResourceDictionary Resources) { UserControlFactory.CargarPropiedadesosDatos(selectedObject, PanelEdicion); } private RelayCommand saveCommand; public ICommand SaveCommand => saveCommand ??= new RelayCommand(Save); private void Save(object commandParameter) { } private RelayCommand exitCommand; public ICommand ExitCommand => exitCommand ??= new RelayCommand(Exit); private void Exit() { Save(); Application.Current.Shutdown(); } } public class SimulationData { public ObservableCollection? ObjetosSimulables { get; set; } public UnitConverter? UnitConverter { get; set; } public PLCViewModel? PLC_ConnectionData { get; set; } } public class TipoSimulable { public string? Nombre { get; set; } public Type? Tipo { get; set; } } public class TickSimulacionEventArgs : EventArgs { // Aquí puedes agregar propiedades o campos para pasar información adicional // en el evento TickSimulacion } }