using CtrEditor.ObjetosSim; using LibS7Adv; using Newtonsoft.Json; using System.Collections.ObjectModel; using System.Windows; using CtrEditor.Simulacion; using System.IO; using System.Linq; using System.Windows.Media; 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(); var datosLocalesObjetos = new ObservableCollection(); // Paso 1: Preparar objetos globales y locales foreach (var obj in _mainViewModel.ObjetosSimulables) { // Para objetos globales con datos locales if (obj.Enable_On_All_Pages && obj.Enable_Local_Data) { // Guarda los datos locales independientemente de Show_On_This_Page var datosLocales = obj.ObtenerDatosLocales(); if (datosLocales != null) { datosLocalesObjetos.Add(datosLocales); } // Restaura datos globales para guardar obj.RestaurarDesdeSnapshotGlobal(); } // Prepara para serializaci�n obj.SalvarDatosNoSerializables(); // Separa objetos globales y locales if (!obj.Enable_On_All_Pages) { objetosSimulables.Add(obj); } else { objetosSimulablesAllPages.Add(obj); } } // Paso 2: Guardar datos de la p�gina actual var dataToSerialize = new SimulationData { ObjetosSimulables = objetosSimulables.Count > 0 ? objetosSimulables : null, UnitConverter = PixelToMeter.Instance.calc, PLC_ConnectionData = _mainViewModel.PLCViewModel, DatosLocalesObjetos = datosLocalesObjetos.Count > 0 ? datosLocalesObjetos : null, ImageDataDictionary = _mainViewModel._imageDataDictionary.Count > 0 ? _mainViewModel._imageDataDictionary : null }; var path = _datosDeTrabajo.ObtenerPathImagenConExtension(selectedImage, ".json"); if (path != null) SerializeAndSave(dataToSerialize, path); // Paso 3: Guardar objetos globales path = _datosDeTrabajo.ObtenerPathAllPages(".json"); if (path != null) SerializeAndSave(objetosSimulablesAllPages, path); // Paso 4: Restaurar estado para seguir trabajando foreach (var obj in _mainViewModel.ObjetosSimulables) { obj.RestaurarDatosNoSerializables(); // Para objetos globales, restaura los datos locales if (obj.Enable_On_All_Pages && obj.Enable_Local_Data) { obj.RestaurarDatosLocales(); } } } } public void LoadState(string selectedImage) { try { _mainViewModel.StopSimulation(); _mainViewModel.DisconnectPLC(); _mainViewModel.ObjetosSimulables.Clear(); _simulationManager.Clear(); if (selectedImage != null) { var settings = GetJsonSerializerSettings(); // Paso 1: Cargar objetos globales LoadAllPagesState(settings); // Paso 2: Cargar datos de la p�gina actual var simulationData = LoadCurrentPageState(selectedImage, settings); // Paso 3: Crear snapshots de los objetos globales // Nota: No filtramos por Enable_Local_Data aqu� ya que se establecer� despu�s foreach (var obj in _mainViewModel.ObjetosSimulables) { if (obj.Enable_On_All_Pages) { obj.CrearSnapshotGlobal(); } } // Paso 4: Aplicar datos locales a objetos globales if (simulationData?.DatosLocalesObjetos != null) { // Primero, desactivar Enable_Local_Data en todos los objetos globales foreach (var obj in _mainViewModel.ObjetosSimulables.Where(o => o.Enable_On_All_Pages)) { obj.Enable_Local_Data = false; } // Luego aplicar los datos locales, esto activar� Enable_Local_Data autom�ticamente foreach (var datosLocales in simulationData.DatosLocalesObjetos) { var objetoGlobal = _mainViewModel.ObjetosSimulables.FirstOrDefault( o => o.Enable_On_All_Pages && o.Id.Value == datosLocales.Id_GlobalObject.Value); if (objetoGlobal != null) { objetoGlobal.AplicarDatosLocales(datosLocales); } } } // Paso 5: Crear controles visuales 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 SimulationData LoadCurrentPageState(string selectedImage, JsonSerializerSettings settings) { SimulationData simulationData = null; string jsonPath = _datosDeTrabajo.ObtenerPathImagenConExtension(selectedImage, ".json"); if (File.Exists(jsonPath)) { string jsonString = File.ReadAllText(jsonPath); simulationData = JsonConvert.DeserializeObject(jsonString, settings); if (simulationData != null) { if (simulationData.ObjetosSimulables is not null) { foreach (var obj in simulationData.ObjetosSimulables) _mainViewModel.ObjetosSimulables.Add(obj); } if (simulationData.PLC_ConnectionData is not null) _mainViewModel.PLCViewModel = simulationData.PLC_ConnectionData; else _mainViewModel.PLCViewModel = new PLCViewModel(); // Solo sobrescribir el UnitConverter si existe uno guardado if (simulationData.UnitConverter != null) PixelToMeter.Instance.calc = simulationData.UnitConverter; // Cargar datos de imágenes if (simulationData.ImageDataDictionary != null) { _mainViewModel._imageDataDictionary.Clear(); foreach (var imageDataEntry in simulationData.ImageDataDictionary) { _mainViewModel._imageDataDictionary[imageDataEntry.Key] = imageDataEntry.Value; } } // Compatibilidad con versiones anteriores (ImageCustomNames) #pragma warning disable CS0618 // Type or member is obsolete if (simulationData.ImageCustomNames != null) { foreach (var customName in simulationData.ImageCustomNames) { var imageData = _mainViewModel.GetOrCreateImageData(customName.Key); imageData.CustomName = customName.Value; } } #pragma warning restore CS0618 } } return simulationData; } 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, Error = HandleDeserializationError, Converters = { new SafeImageSourceConverter() } }; } private void HandleDeserializationError(object sender, Newtonsoft.Json.Serialization.ErrorEventArgs e) { // Si es un error relacionado con ImagePath, ImageSource o osCustomImage, simplemente ignorarlo y continuar if (e.ErrorContext.Error.Message.Contains("ImagePath") || e.ErrorContext.Error.Message.Contains("osCustomImage") || e.ErrorContext.Error.Message.Contains("ImageSource") || e.ErrorContext.Error.Message.Contains("lmageSource") || // posible typo en el mensaje de error (e.ErrorContext.Path != null && e.ErrorContext.Path.Contains("ImageSource")) || (e.ErrorContext.Path != null && e.ErrorContext.Path.Contains("imagePath")) || (e.ErrorContext.Member != null && e.ErrorContext.Member.ToString().Contains("Image"))) { e.ErrorContext.Handled = true; // Si el contexto actual es un objeto osCustomImage y hay un error, // intentar establecer valores por defecto if (e.ErrorContext.OriginalObject is osCustomImage customImage) { try { // Establecer valores seguros por defecto if (e.ErrorContext.Member?.ToString() == "ImagePath" || e.ErrorContext.Member?.ToString() == "imagePath") { customImage.ImagePath = ""; // Esto activará el fallback a imagen por defecto } } catch { // Si incluso esto falla, simplemente ignorar } } } } } /// /// Convertidor JSON seguro para ImageSource que nunca lanza excepciones /// public class SafeImageSourceConverter : JsonConverter { public override ImageSource ReadJson(JsonReader reader, Type objectType, ImageSource existingValue, bool hasExistingValue, JsonSerializer serializer) { try { // Si el valor es null o string vacío, devolver null if (reader.Value == null || reader.Value.ToString() == "") return null; // Intentar crear ImageSource desde el valor string value = reader.Value.ToString(); // Limpiar caracteres problemáticos value = value.Trim(); if (value.Contains("•")) value = value.Replace("•", ""); if (value.StartsWith("filet///")) value = value.Replace("filet///", "file:///"); // Si el valor limpio está vacío, devolver null if (string.IsNullOrWhiteSpace(value)) return null; // No intentar crear ImageSource aquí, simplemente devolver null // La lógica de carga de imagen se manejará en el código de la aplicación return null; } catch { // En caso de cualquier error, devolver null silenciosamente return null; } } public override void WriteJson(JsonWriter writer, ImageSource value, JsonSerializer serializer) { // Nunca serializar ImageSource, siempre escribir null writer.WriteNull(); } } }