383 lines
13 KiB
C#
383 lines
13 KiB
C#
using System;
|
|
using System.Collections.ObjectModel;
|
|
using System.IO;
|
|
using System.Threading.Tasks;
|
|
using Newtonsoft.Json;
|
|
using System.Linq;
|
|
using System.Windows.Controls;
|
|
using System.Windows;
|
|
using CtrEditor.ObjetosSim;
|
|
using LibS7Adv;
|
|
|
|
namespace CtrEditor
|
|
{
|
|
/// <summary>
|
|
/// Metadata de una página, incluyendo información sobre la imagen y configuración
|
|
/// </summary>
|
|
public class PageMetadata
|
|
{
|
|
public string PageId { get; set; }
|
|
public string PageName { get; set; }
|
|
public string ImagePath { get; set; }
|
|
public DateTime LastModified { get; set; }
|
|
public Dictionary<string, object> CustomMetadata { get; set; } = new();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Estado de un objeto dentro de una página específica
|
|
/// </summary>
|
|
public class PageObjectState
|
|
{
|
|
public string GlobalObjectId { get; set; }
|
|
public bool IsVisible { get; set; } = true;
|
|
public float Left { get; set; }
|
|
public float Top { get; set; }
|
|
public float Rotation { get; set; }
|
|
public Dictionary<string, object> CustomProperties { get; set; } = new();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Estado de una página específica
|
|
/// </summary>
|
|
public class PageState
|
|
{
|
|
public string PageId { get; set; }
|
|
public string PageName { get; set; }
|
|
public List<osBase> LocalObjects { get; set; } = new();
|
|
public Dictionary<string, PageObjectState> GlobalObjectStates { get; set; } = new();
|
|
public Dictionary<string, object> PageMetadata { get; set; } = new();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Estado global de la aplicación
|
|
/// </summary>
|
|
public class GlobalState
|
|
{
|
|
public List<osBase> SharedObjects { get; set; } = new();
|
|
public Dictionary<string, PageMetadata> PagesMetadata { get; set; } = new();
|
|
public PLCViewModel PLCConfiguration { get; set; }
|
|
public UnitConverter UnitConverter { get; set; }
|
|
public int LastUsedId { get; set; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gestor principal de estados de la aplicación
|
|
/// </summary>
|
|
public class StateManager : IDisposable
|
|
{
|
|
private readonly string _basePath;
|
|
private GlobalState _globalState;
|
|
private Dictionary<string, PageState> _loadedPages;
|
|
private string _currentPageId;
|
|
private readonly object _lockObject = new();
|
|
private bool _hasUnsavedChanges;
|
|
private MainViewModel _mainViewModel;
|
|
|
|
public event EventHandler<string> PageStateChanged;
|
|
public event EventHandler GlobalStateChanged;
|
|
|
|
public StateManager(string basePath, MainViewModel mainViewModel)
|
|
{
|
|
_basePath = basePath;
|
|
_mainViewModel = mainViewModel;
|
|
_loadedPages = new Dictionary<string, PageState>();
|
|
Directory.CreateDirectory(basePath);
|
|
}
|
|
|
|
public async Task InitializeAsync()
|
|
{
|
|
await LoadGlobalStateAsync();
|
|
if (_mainViewModel.SelectedImage != null)
|
|
{
|
|
await LoadPageStateAsync(_mainViewModel.SelectedImage);
|
|
}
|
|
}
|
|
|
|
private JsonSerializerSettings GetSerializerSettings()
|
|
{
|
|
return new JsonSerializerSettings
|
|
{
|
|
TypeNameHandling = TypeNameHandling.Auto,
|
|
ObjectCreationHandling = ObjectCreationHandling.Replace,
|
|
Formatting = Formatting.Indented,
|
|
NullValueHandling = NullValueHandling.Ignore
|
|
};
|
|
}
|
|
|
|
public async Task LoadGlobalStateAsync()
|
|
{
|
|
var path = Path.Combine(_basePath, "global.json");
|
|
if (File.Exists(path))
|
|
{
|
|
try
|
|
{
|
|
var json = await File.ReadAllTextAsync(path);
|
|
_globalState = JsonConvert.DeserializeObject<GlobalState>(json, GetSerializerSettings());
|
|
|
|
// Restaurar configuración global
|
|
_mainViewModel.PLCViewModel = _globalState.PLCConfiguration ?? new PLCViewModel();
|
|
if (_globalState.UnitConverter != null)
|
|
PixelToMeter.Instance.calc = _globalState.UnitConverter;
|
|
else
|
|
PixelToMeter.Instance.calc = new UnitConverter(1.0f); // Valor por defecto
|
|
|
|
// Restaurar objetos globales
|
|
foreach (var obj in _globalState.SharedObjects)
|
|
{
|
|
await RestoreObjectAsync(obj);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// Log error y crear nuevo estado global
|
|
_globalState = new GlobalState();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_globalState = new GlobalState();
|
|
}
|
|
}
|
|
|
|
public async Task SaveGlobalStateAsync()
|
|
{
|
|
var path = Path.Combine(_basePath, "global.json");
|
|
var backupPath = Path.ChangeExtension(path, ".bak");
|
|
|
|
// Actualizar estado global
|
|
_globalState.PLCConfiguration = _mainViewModel.PLCViewModel;
|
|
_globalState.UnitConverter = PixelToMeter.Instance.calc;
|
|
|
|
// Crear backup
|
|
if (File.Exists(path))
|
|
{
|
|
File.Copy(path, backupPath, true);
|
|
}
|
|
|
|
try
|
|
{
|
|
// Preparar objetos para serialización
|
|
foreach (var obj in _globalState.SharedObjects)
|
|
{
|
|
obj.SalvarDatosNoSerializables();
|
|
}
|
|
|
|
var json = JsonConvert.SerializeObject(_globalState, GetSerializerSettings());
|
|
await File.WriteAllTextAsync(path, json);
|
|
_hasUnsavedChanges = false;
|
|
GlobalStateChanged?.Invoke(this, EventArgs.Empty);
|
|
}
|
|
finally
|
|
{
|
|
// Restaurar estado de objetos
|
|
foreach (var obj in _globalState.SharedObjects)
|
|
{
|
|
obj.RestaurarDatosNoSerializables();
|
|
}
|
|
}
|
|
}
|
|
|
|
public async Task<PageState> LoadPageStateAsync(string pageId)
|
|
{
|
|
// Retornar página cacheada si existe
|
|
if (_loadedPages.TryGetValue(pageId, out var cachedState))
|
|
{
|
|
return cachedState;
|
|
}
|
|
|
|
var path = Path.Combine(_basePath, $"page_{pageId}.json");
|
|
try
|
|
{
|
|
PageState pageState;
|
|
if (File.Exists(path))
|
|
{
|
|
var json = await File.ReadAllTextAsync(path);
|
|
pageState = JsonConvert.DeserializeObject<PageState>(json, GetSerializerSettings());
|
|
}
|
|
else
|
|
{
|
|
pageState = new PageState
|
|
{
|
|
PageId = pageId,
|
|
PageName = _globalState.PagesMetadata.GetValueOrDefault(pageId)?.PageName ?? pageId
|
|
};
|
|
}
|
|
|
|
_loadedPages[pageId] = pageState;
|
|
|
|
// Restaurar objetos locales
|
|
foreach (var obj in pageState.LocalObjects)
|
|
{
|
|
await RestoreObjectAsync(obj);
|
|
}
|
|
|
|
// Aplicar estados de objetos globales
|
|
ApplyGlobalObjectStates(pageState);
|
|
|
|
return pageState;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// Log error
|
|
throw;
|
|
}
|
|
}
|
|
|
|
private void ApplyGlobalObjectStates(PageState pageState)
|
|
{
|
|
foreach (var kvp in pageState.GlobalObjectStates)
|
|
{
|
|
var globalObj = _globalState.SharedObjects.FirstOrDefault(o => o.Id.Value.ToString() == kvp.Key);
|
|
if (globalObj != null)
|
|
{
|
|
var state = kvp.Value;
|
|
globalObj.Left = state.Left;
|
|
globalObj.Top = state.Top;
|
|
globalObj.Angulo = state.Rotation;
|
|
|
|
// Actualizar Show_On_This_Page que ahora maneja internamente showOnThisPagesList
|
|
if (state.IsVisible && !globalObj.Show_On_This_Page)
|
|
globalObj.Show_On_This_Page = true;
|
|
else if (!state.IsVisible && globalObj.Show_On_This_Page)
|
|
globalObj.Show_On_This_Page = false;
|
|
|
|
// Aplicar propiedades personalizadas
|
|
foreach (var prop in state.CustomProperties)
|
|
{
|
|
var property = globalObj.GetType().GetProperty(prop.Key);
|
|
if (property != null && property.CanWrite)
|
|
{
|
|
property.SetValue(globalObj, prop.Value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private async Task RestoreObjectAsync(osBase obj)
|
|
{
|
|
if (obj != null)
|
|
{
|
|
obj.CheckData();
|
|
await Task.Run(() =>
|
|
{
|
|
Application.Current.Dispatcher.Invoke(() =>
|
|
{
|
|
CrearUserControlDesdeObjetoSimulable(obj);
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
private bool CrearUserControlDesdeObjetoSimulable(osBase osObjeto)
|
|
{
|
|
Type tipoObjeto = osObjeto.GetType();
|
|
UserControl userControl = UserControlFactory.GetControlForType(tipoObjeto);
|
|
|
|
if (userControl != null)
|
|
{
|
|
UserControlFactory.AssignDatos(userControl, osObjeto, _mainViewModel.simulationManager);
|
|
osObjeto._mainViewModel = _mainViewModel;
|
|
|
|
if (osObjeto.Id == null)
|
|
{
|
|
osObjeto.Id = new UniqueId().ObtenerNuevaID();
|
|
}
|
|
|
|
_mainViewModel.MainWindow.AgregarRegistrarUserControlCanvas(userControl);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public async Task SavePageStateAsync(string pageId)
|
|
{
|
|
if (!_loadedPages.TryGetValue(pageId, out var pageState))
|
|
return;
|
|
|
|
var path = Path.Combine(_basePath, $"page_{pageId}.json");
|
|
var backupPath = Path.ChangeExtension(path, ".bak");
|
|
|
|
// Crear backup
|
|
if (File.Exists(path))
|
|
{
|
|
File.Copy(path, backupPath, true);
|
|
}
|
|
|
|
// Actualizar estado de objetos globales
|
|
pageState.GlobalObjectStates.Clear();
|
|
foreach (var obj in _mainViewModel.ObjetosSimulables.Where(o => o.Enable_On_All_Pages))
|
|
{
|
|
var currentPageId = _mainViewModel.SelectedImage;
|
|
pageState.GlobalObjectStates[obj.Id.Value.ToString()] = new PageObjectState
|
|
{
|
|
GlobalObjectId = obj.Id.Value.ToString(),
|
|
IsVisible = obj.Show_On_This_Page,
|
|
Left = obj.Left,
|
|
Top = obj.Top,
|
|
Rotation = obj.Angulo,
|
|
CustomProperties = CaptureCustomProperties(obj)
|
|
};
|
|
}
|
|
|
|
try
|
|
{
|
|
// Preparar objetos para serialización
|
|
foreach (var obj in pageState.LocalObjects)
|
|
{
|
|
obj.SalvarDatosNoSerializables();
|
|
}
|
|
|
|
var json = JsonConvert.SerializeObject(pageState, GetSerializerSettings());
|
|
await File.WriteAllTextAsync(path, json);
|
|
_hasUnsavedChanges = false;
|
|
PageStateChanged?.Invoke(this, pageId);
|
|
}
|
|
finally
|
|
{
|
|
// Restaurar estado de objetos
|
|
foreach (var obj in pageState.LocalObjects)
|
|
{
|
|
obj.RestaurarDatosNoSerializables();
|
|
}
|
|
}
|
|
}
|
|
|
|
private Dictionary<string, object> CaptureCustomProperties(osBase obj)
|
|
{
|
|
var customProps = new Dictionary<string, object>();
|
|
var properties = obj.GetType().GetProperties()
|
|
.Where(p => p.GetCustomAttributes(typeof(SerializeAttribute), true).Any());
|
|
|
|
foreach (var prop in properties)
|
|
{
|
|
customProps[prop.Name] = prop.GetValue(obj);
|
|
}
|
|
|
|
return customProps;
|
|
}
|
|
|
|
public async Task SaveAllAsync()
|
|
{
|
|
foreach (var pageId in _loadedPages.Keys)
|
|
{
|
|
await SavePageStateAsync(pageId);
|
|
}
|
|
await SaveGlobalStateAsync();
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
if (_hasUnsavedChanges)
|
|
{
|
|
SaveAllAsync().Wait();
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Atributo para marcar propiedades que deben ser serializadas como propiedades personalizadas
|
|
/// </summary>
|
|
[AttributeUsage(AttributeTargets.Property)]
|
|
public class SerializeAttribute : Attribute { }
|
|
} |