343 lines
14 KiB
C#
343 lines
14 KiB
C#
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<osBase>();
|
||
var objetosSimulablesAllPages = new ObservableCollection<osBase>();
|
||
var datosLocalesObjetos = new ObservableCollection<DatosLocales>();
|
||
|
||
// 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<63>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<71> ya que se establecer<65> despu<70>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<61> Enable_Local_Data autom<6F>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<SimulationData>(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<ObservableCollection<osBase>>(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
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Convertidor JSON seguro para ImageSource que nunca lanza excepciones
|
||
/// </summary>
|
||
public class SafeImageSourceConverter : JsonConverter<ImageSource>
|
||
{
|
||
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();
|
||
}
|
||
}
|
||
} |