From 061007158d53d5711f557546d9e4c89ab0d2521c Mon Sep 17 00:00:00 2001 From: Miguel Date: Fri, 21 Feb 2025 15:12:10 +0100 Subject: [PATCH] Agregada clase de Serializacion para separar la logica del MainViewModel --- CtrEditor.csproj | 4 +- DatosDeTrabajo.cs | 9 +- FuncionesBase/TagPattern.cs | 11 +++ MainViewModel.cs | 155 ++--------------------------- MainWindow.xaml | 8 +- MainWindow.xaml.cs | 24 +++-- ObjectManipulationManager.cs | 51 +++++++++- Serialization/StateSerializer.cs | 162 +++++++++++++++++++++++++++++++ 8 files changed, 260 insertions(+), 164 deletions(-) create mode 100644 Serialization/StateSerializer.cs diff --git a/CtrEditor.csproj b/CtrEditor.csproj index 81cb2ab..5fd72e8 100644 --- a/CtrEditor.csproj +++ b/CtrEditor.csproj @@ -10,11 +10,11 @@ - False + True - False + True diff --git a/DatosDeTrabajo.cs b/DatosDeTrabajo.cs index 277aced..385d8c3 100644 --- a/DatosDeTrabajo.cs +++ b/DatosDeTrabajo.cs @@ -30,14 +30,11 @@ namespace CtrEditor public string? ObtenerPathAllPages(string extension) { - string folderPath = EstadoPersistente.Instance.directorio; // Usar directamente desde el Singleton - - if (Directory.Exists(folderPath)) - return Path.ChangeExtension(folderPath + "\\allpages", extension); - else return null; + if (!string.IsNullOrEmpty(EstadoPersistente.Instance.directorio)) + return Path.Combine(EstadoPersistente.Instance.directorio, "AllPages" + extension); + return null; } - public void CargarImagenes() { Imagenes.Clear(); diff --git a/FuncionesBase/TagPattern.cs b/FuncionesBase/TagPattern.cs index 649defa..893e648 100644 --- a/FuncionesBase/TagPattern.cs +++ b/FuncionesBase/TagPattern.cs @@ -16,6 +16,7 @@ namespace CtrEditor.FuncionesBase { "Siemens IO Input", "Format: Exxxxx.y (x: 0-65535, y: 0-7)" }, { "Siemens IO Output", "Format: Axxxxx.y (x: 0-65535, y: 0-7)" }, { "LETRASNUMEROS", "Format: ABC...123... (1-10 letters + numbers)" }, + { "LETRASNUMEROSESPACIOS", "Format: ABC...123... (letters, numbers and spaces allowed)" }, { "Numero", "Numeric value" } }; @@ -35,6 +36,7 @@ namespace CtrEditor.FuncionesBase "Siemens IO Input" => ApplySiemensPattern(text, "E"), "Siemens IO Output" => ApplySiemensPattern(text, "A"), "LETRASNUMEROS" => ApplyLetrasNumerosPattern(text), + "LETRASNUMEROSESPACIOS" => ApplyLetrasNumerosEspaciosPattern(text), "Numero" => ApplyNumberPattern(text), _ => text }; @@ -77,6 +79,15 @@ namespace CtrEditor.FuncionesBase return $"{letters.ToUpper()}{numbers}"; } + private static string ApplyLetrasNumerosEspaciosPattern(string text) + { + // Keep only letters, numbers and spaces + var cleanedText = new string(text.Where(c => char.IsLetterOrDigit(c) || char.IsWhiteSpace(c)).ToArray()); + + // Convert to uppercase + return cleanedText.ToUpper(); + } + private static string ApplyNumberPattern(string text) { var match = Regex.Match(text, @"-?\d+\.?\d*"); diff --git a/MainViewModel.cs b/MainViewModel.cs index 84e0b19..f9b6018 100644 --- a/MainViewModel.cs +++ b/MainViewModel.cs @@ -21,13 +21,14 @@ using System.Windows.Data; using CommunityToolkit.Mvvm.Input; using System.Text.RegularExpressions; using System.Collections.Specialized; - +using CtrEditor.Serialization; // Add this line namespace CtrEditor { public partial class MainViewModel : ObservableObject { + private readonly StateSerializer _stateSerializer; public Stopwatch stopwatch_Sim; private double stopwatch_SimPLC_last; @@ -367,6 +368,7 @@ namespace CtrEditor OpenRecentDirectoryCommand = new RelayCommand(OpenRecentDirectory); _stateManager = new StateManager(EstadoPersistente.Instance.directorio, this); + _stateSerializer = new StateSerializer(this, datosDeTrabajo, simulationManager); } private void OsListFilter_PropertyChanged(object? sender, PropertyChangedEventArgs e) @@ -425,7 +427,7 @@ namespace CtrEditor } // Crear UserControl desde osBase : Nuevo o desde Deserealizacion - private bool CrearUserControlDesdeObjetoSimulable(osBase osObjeto) + public bool CrearUserControlDesdeObjetoSimulable(osBase osObjeto) { Type tipoObjeto = osObjeto.GetType(); @@ -513,25 +515,9 @@ namespace CtrEditor } - public void EliminarObjetoSeleccionado() - { - if (SelectedItemOsList is osBase objEliminar) - { - var result = MessageBox.Show($"¿Está seguro que desea eliminar el objeto '{objEliminar.Nombre}'?", - "Confirmar eliminación", - MessageBoxButton.YesNo, - MessageBoxImage.Question); - - if (result == MessageBoxResult.Yes) - { - RemoverObjetoSimulable(objEliminar); - } - } - } - private void EliminarUserControl() { - EliminarObjetoSeleccionado(); + _objectManager.EliminarObjetosSeleccionados(); } @@ -661,7 +647,7 @@ namespace CtrEditor simulationManager.Start(); } - private void StopSimulation() + public void StopSimulation() { IsSimulationRunning = false; @@ -717,7 +703,7 @@ namespace CtrEditor objetoSimulable.SetPLC(PLCViewModel); } - private void DisconnectPLC() + public void DisconnectPLC() { PLCViewModel.Disconnect(); _timerPLCUpdate.Stop(); @@ -822,138 +808,17 @@ namespace CtrEditor { if (SelectedImage != null) { - StopSimulation(); - DisconnectPLC(); - - ObservableCollection _objetosSimulables = new ObservableCollection(); - ObservableCollection _objetosSimulablesAllPages = new ObservableCollection(); - - foreach (var obj in ObjetosSimulables) - { - // Guardar referencias temporales - obj.SalvarDatosNoSerializables(); - if (!obj.Enable_On_All_Pages) - _objetosSimulables.Add(obj); - else - _objetosSimulablesAllPages.Add(obj); - } - - // Salvar los objetos de la pagina actual - - // 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 - }; - - // Ruta del archivo a ser guardado - var path = DatosDeTrabajo.ObtenerPathImagenConExtension(SelectedImage, ".json"); - if (path != null) - SerializarYSalvar(dataToSerialize, path); - - // Salvar los objetos de todas las paginas - - // Ruta del archivo a ser guardado - path = DatosDeTrabajo.ObtenerPathAllPages(".json"); - if (path != null) - SerializarYSalvar(_objetosSimulablesAllPages, path); - - // Restaurar las propiedades originales de los objetos - foreach (var obj in ObjetosSimulables) - obj.RestaurarDatosNoSerializables(); - + _stateSerializer.SaveState(SelectedImage); HasUnsavedChanges = false; } } - private void SerializarYSalvar(object listaObjetos, string path) - { - - // 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 - } - - - var settings = new JsonSerializerSettings - { - Formatting = Formatting.Indented, - // PreserveReferencesHandling = PreserveReferencesHandling.Objects, - NullValueHandling = NullValueHandling.Ignore, - TypeNameHandling = TypeNameHandling.Auto - }; - - // Serializar - var serializedData = JsonConvert.SerializeObject(listaObjetos, settings); - File.WriteAllText(path, serializedData); // Escribir el nuevo archivo JSON - } - public void LoadStateObjetosSimulables() { - try + if (SelectedImage != null) { - StopSimulation(); - DisconnectPLC(); - ObjetosSimulables.Clear(); - simulationManager.Clear(); - if (SelectedImage != null) - { - var settings = new JsonSerializerSettings - { - TypeNameHandling = TypeNameHandling.Auto, - ObjectCreationHandling = ObjectCreationHandling.Replace, - // PreserveReferencesHandling = PreserveReferencesHandling.Objects, - ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor - }; - - string jsonPath = datosDeTrabajo.ObtenerPathImagenConExtension(SelectedImage, ".json"); - if (File.Exists(jsonPath)) - { - string jsonString = File.ReadAllText(jsonPath); - - - 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; - - } - } - - jsonPath = DatosDeTrabajo.ObtenerPathAllPages(".json"); - if (File.Exists(jsonPath)) - { - string jsonString = File.ReadAllText(jsonPath); - - ObservableCollection _objetosSimulablesAllPages = new ObservableCollection(); - - _objetosSimulablesAllPages = JsonConvert.DeserializeObject>(jsonString, settings); - if (_objetosSimulablesAllPages != null) - foreach (var obj in _objetosSimulablesAllPages) - ObjetosSimulables.Add(obj); - } - // Recorrer la colección de objetos simulables - foreach (var objetoSimulable in ObjetosSimulables) - if (objetoSimulable != null) - { - objetoSimulable.CheckData(); - CrearUserControlDesdeObjetoSimulable(objetoSimulable); - } - } + _stateSerializer.LoadState(SelectedImage); } - catch { /* Consider logging the error or handling it appropriately */ } } // Se cargan los datos de cada UserControl en el StackPanel diff --git a/MainWindow.xaml b/MainWindow.xaml index 8f12648..14f4428 100644 --- a/MainWindow.xaml +++ b/MainWindow.xaml @@ -256,8 +256,12 @@ ResizeDirection="Rows" VerticalAlignment="Center" /> - + diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs index 157d689..911eafe 100644 --- a/MainWindow.xaml.cs +++ b/MainWindow.xaml.cs @@ -287,17 +287,20 @@ namespace CtrEditor private void MainWindow_KeyDown(object sender, KeyEventArgs e) { - // Forzar el foco al canvas si no lo tiene - if (!ImagenEnTrabajoCanvas.IsFocused) + // Only force canvas focus if PanelEdicion doesn't have focus + if (!PanelEdicion.IsKeyboardFocusWithin) { - ImagenEnTrabajoCanvas.Focus(); + if (!ImagenEnTrabajoCanvas.IsFocused) + { + ImagenEnTrabajoCanvas.Focus(); + } } if (DataContext is MainViewModel viewModel) { if (e.Key == Key.Delete) { - viewModel.EliminarObjetoSeleccionado(); + _objectManager.EliminarObjetosSeleccionados(); e.Handled = true; } else if (e.Key == Key.Escape) @@ -491,13 +494,18 @@ namespace CtrEditor private void Canvas_KeyDown(object sender, KeyEventArgs e) { - HandleKeyDown(e); + // Only handle if PanelEdicion doesn't have focus + if (!PanelEdicion.IsKeyboardFocusWithin) + { + HandleKeyDown(e); + } } private void ScrollViewer_PreviewKeyDown(object sender, KeyEventArgs e) { - // Prevenir que el ScrollViewer maneje las teclas de flecha - if (e.Key == Key.Left || e.Key == Key.Right || e.Key == Key.Up || e.Key == Key.Down) + // Only handle if PanelEdicion doesn't have focus + if (!PanelEdicion.IsKeyboardFocusWithin && + (e.Key == Key.Left || e.Key == Key.Right || e.Key == Key.Up || e.Key == Key.Down)) { HandleKeyDown(e); e.Handled = true; @@ -510,7 +518,7 @@ namespace CtrEditor { if (e.Key == Key.Delete) { - viewModel.EliminarObjetoSeleccionado(); + _objectManager.EliminarObjetosSeleccionados(); // Cambiar aquí e.Handled = true; } else if (e.Key == Key.Escape) diff --git a/ObjectManipulationManager.cs b/ObjectManipulationManager.cs index 3360ef3..32620e8 100644 --- a/ObjectManipulationManager.cs +++ b/ObjectManipulationManager.cs @@ -75,9 +75,25 @@ namespace CtrEditor _canvas = canvas; } + private void PurgeDeletedObjects() + { + var deletedObjects = _selectedObjects.Where(obj => + obj.VisualRepresentation == null || + !_canvas.Children.Contains(obj.VisualRepresentation)).ToList(); + + foreach (var obj in deletedObjects) + { + DeselectObject(obj); + } + } + public ObservableCollection SelectedObjects { - get => _selectedObjects; + get + { + PurgeDeletedObjects(); + return _selectedObjects; + } private set { _selectedObjects = value; @@ -87,6 +103,7 @@ namespace CtrEditor public void UpdateSelectionVisuals() { + PurgeDeletedObjects(); // Asegurarse de que el canvas haya actualizado su layout _canvas.UpdateLayout(); @@ -308,6 +325,7 @@ namespace CtrEditor private void HandleObjectSelection(UserControl userControl, osBase datos) { + PurgeDeletedObjects(); var viewModel = _mainWindow.DataContext as MainViewModel; if (viewModel == null) return; @@ -545,6 +563,7 @@ namespace CtrEditor private void HandleDrag(Point currentPosition) { + PurgeDeletedObjects(); var dx = currentPosition.X - _startPointUserControl.X; var dy = currentPosition.Y - _startPointUserControl.Y; @@ -695,6 +714,7 @@ namespace CtrEditor private void HandleResize(Point currentPosition, HandleMode mode) { + PurgeDeletedObjects(); RemoveAllSelectionHighlights(); // Remover antes de redimensionar foreach (var selectedObject in _selectedObjects) @@ -718,6 +738,7 @@ namespace CtrEditor private void HandleRotation(Point currentPosition) { + PurgeDeletedObjects(); RemoveAllSelectionHighlights(); // Remover antes de rotar // Calcular el ángulo respecto al centro del bounding box que contiene todos los objetos seleccionados @@ -744,6 +765,7 @@ namespace CtrEditor public void AlignObjects(AlignmentType alignmentType) { + PurgeDeletedObjects(); if (_selectedObjects.Count <= 1) return; var alignment = new ObjectAlignment(_selectedObjects, _selectedObjects.FirstOrDefault()); @@ -794,5 +816,32 @@ namespace CtrEditor // Update the selection visuals after alignment UpdateSelectionVisuals(); } + + public void EliminarObjetosSeleccionados() + { + if (_selectedObjects.Count == 0) return; + + var viewModel = _mainWindow.DataContext as MainViewModel; + if (viewModel == null) return; + + // Crear una copia de la lista para evitar modificaciones durante la iteración + var objectsToRemove = _selectedObjects.ToList(); + + foreach (var obj in objectsToRemove) + { + viewModel.RemoverObjetoSimulable(obj); + } + + // Limpiar la selección y actualizar la interfaz + ClearSelection(); + RemoveResizeRectangles(); + RemoveAllSelectionHighlights(); + + // Actualizar el estado de cambios sin guardar + if (viewModel != null) + { + viewModel.HasUnsavedChanges = true; + } + } } } \ No newline at end of file diff --git a/Serialization/StateSerializer.cs b/Serialization/StateSerializer.cs new file mode 100644 index 0000000..ad83bc3 --- /dev/null +++ b/Serialization/StateSerializer.cs @@ -0,0 +1,162 @@ +using CtrEditor.ObjetosSim; +using LibS7Adv; +using Newtonsoft.Json; +using System.Collections.ObjectModel; +using System.Windows; +using CtrEditor.Simulacion; +using System.IO; + +namespace CtrEditor.Serialization +{ + public class StateSerializer + { + private readonly DatosDeTrabajo _datosDeTrabajo; + private readonly MainViewModel _mainViewModel; + private readonly SimulationManagerFP _simulationManager; + + public StateSerializer(MainViewModel mainViewModel, DatosDeTrabajo datosDeTrabajo, SimulationManagerFP simulationManager) + { + _mainViewModel = mainViewModel; + _datosDeTrabajo = datosDeTrabajo; + _simulationManager = simulationManager; + } + + public void SaveState(string selectedImage) + { + if (selectedImage != null) + { + _mainViewModel.StopSimulation(); + _mainViewModel.DisconnectPLC(); + + var objetosSimulables = new ObservableCollection(); + var objetosSimulablesAllPages = new ObservableCollection(); + + foreach (var obj in _mainViewModel.ObjetosSimulables) + { + obj.SalvarDatosNoSerializables(); + if (!obj.Enable_On_All_Pages) + objetosSimulables.Add(obj); + else + objetosSimulablesAllPages.Add(obj); + } + + // Save current page objects + var dataToSerialize = new SimulationData + { + ObjetosSimulables = objetosSimulables, + UnitConverter = PixelToMeter.Instance.calc, + PLC_ConnectionData = _mainViewModel.PLCViewModel + }; + + var path = _datosDeTrabajo.ObtenerPathImagenConExtension(selectedImage, ".json"); + if (path != null) + SerializeAndSave(dataToSerialize, path); + + // Save all pages objects + path = _datosDeTrabajo.ObtenerPathAllPages(".json"); + if (path != null) + SerializeAndSave(objetosSimulablesAllPages, path); + + // Restore original properties + foreach (var obj in _mainViewModel.ObjetosSimulables) + obj.RestaurarDatosNoSerializables(); + } + } + + public void LoadState(string selectedImage) + { + try + { + _mainViewModel.StopSimulation(); + _mainViewModel.DisconnectPLC(); + _mainViewModel.ObjetosSimulables.Clear(); + _simulationManager.Clear(); + + if (selectedImage != null) + { + var settings = GetJsonSerializerSettings(); + LoadCurrentPageState(selectedImage, settings); + LoadAllPagesState(settings); + + // Create UserControls for all loaded objects + foreach (var objetoSimulable in _mainViewModel.ObjetosSimulables) + { + if (objetoSimulable != null) + { + objetoSimulable.CheckData(); + _mainViewModel.CrearUserControlDesdeObjetoSimulable(objetoSimulable); + } + } + } + } + catch (Exception ex) + { + MessageBox.Show($"Error loading state: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error); + } + } + + private void LoadCurrentPageState(string selectedImage, JsonSerializerSettings settings) + { + string jsonPath = _datosDeTrabajo.ObtenerPathImagenConExtension(selectedImage, ".json"); + if (File.Exists(jsonPath)) + { + string jsonString = File.ReadAllText(jsonPath); + var simulationData = JsonConvert.DeserializeObject(jsonString, settings); + if (simulationData != null) + { + if (simulationData.ObjetosSimulables is not null) + _mainViewModel.ObjetosSimulables = simulationData.ObjetosSimulables; + + if (simulationData.PLC_ConnectionData is not null) + _mainViewModel.PLCViewModel = simulationData.PLC_ConnectionData; + else + _mainViewModel.PLCViewModel = new PLCViewModel(); + + PixelToMeter.Instance.calc = simulationData.UnitConverter; + } + } + } + + private void LoadAllPagesState(JsonSerializerSettings settings) + { + string jsonPath = _datosDeTrabajo.ObtenerPathAllPages(".json"); + if (File.Exists(jsonPath)) + { + string jsonString = File.ReadAllText(jsonPath); + var objetosSimulablesAllPages = JsonConvert.DeserializeObject>(jsonString, settings); + if (objetosSimulablesAllPages != null) + { + foreach (var obj in objetosSimulablesAllPages) + _mainViewModel.ObjetosSimulables.Add(obj); + } + } + } + + private void SerializeAndSave(object objectToSerialize, string path) + { + // Create backup if file exists + if (File.Exists(path)) + { + var backupPath = Path.ChangeExtension(path, ".bak"); + File.Copy(path, backupPath, true); + } + + var settings = GetJsonSerializerSettings(); + var serializedData = JsonConvert.SerializeObject(objectToSerialize, settings); + File.WriteAllText(path, serializedData); + } + + private JsonSerializerSettings GetJsonSerializerSettings() + { + return new JsonSerializerSettings + { + Formatting = Formatting.Indented, + NullValueHandling = NullValueHandling.Ignore, + TypeNameHandling = TypeNameHandling.Auto, + ObjectCreationHandling = ObjectCreationHandling.Replace, + ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor + }; + } + } +} +