From 633cd71d00e563904f6858466a749b070e890995 Mon Sep 17 00:00:00 2001 From: Miguel Date: Mon, 17 Feb 2025 13:04:21 +0100 Subject: [PATCH] Mejorado la descripcion de las extracciones de Tag con Patrones --- Converters/ConnectionConverters.cs | 0 CtrEditor.csproj | 4 + DataStates/StateManager.cs | 383 ++++++++++++++++++ FuncionesBase/Idiomas.cs | 68 ++++ FuncionesBase/TagPattern.cs | 99 +++++ IA/gtpask.cs | 23 +- Icons/match.png | Bin 0 -> 43494 bytes Icons/ocr.png | Bin 0 -> 19322 bytes MainViewModel.cs | 270 ++---------- MainWindow.xaml | 45 +- MainWindow.xaml.cs | 13 +- ObjetosSim/Estaticos/ucVMmotorSim.xaml.cs | 5 - .../ucBuscarCoincidencias.xaml.cs | 36 +- .../Extraccion Datos/ucExtraccionTag.xaml.cs | 43 +- ObjetosSim/osBase.cs | 2 +- PopUps/AssignImagesWindow.xaml.cs | 2 +- PopUps/ColumnSelectionDialog.xaml.cs | 11 +- PopUps/MatrixPreviewWindow.xaml | 12 +- Services/LLMService.cs | 4 +- ViewModels/MatrixPreviewViewModel.cs | 37 +- XAMLhelpers.cs | 27 ++ 21 files changed, 779 insertions(+), 305 deletions(-) create mode 100644 Converters/ConnectionConverters.cs create mode 100644 DataStates/StateManager.cs create mode 100644 FuncionesBase/Idiomas.cs create mode 100644 FuncionesBase/TagPattern.cs create mode 100644 Icons/match.png create mode 100644 Icons/ocr.png diff --git a/Converters/ConnectionConverters.cs b/Converters/ConnectionConverters.cs new file mode 100644 index 0000000..e69de29 diff --git a/CtrEditor.csproj b/CtrEditor.csproj index 787b3d7..81cb2ab 100644 --- a/CtrEditor.csproj +++ b/CtrEditor.csproj @@ -44,6 +44,8 @@ + + @@ -121,6 +123,8 @@ + + diff --git a/DataStates/StateManager.cs b/DataStates/StateManager.cs new file mode 100644 index 0000000..7387186 --- /dev/null +++ b/DataStates/StateManager.cs @@ -0,0 +1,383 @@ +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 +{ + /// + /// Metadata de una página, incluyendo información sobre la imagen y configuración + /// + 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 CustomMetadata { get; set; } = new(); + } + + /// + /// Estado de un objeto dentro de una página específica + /// + 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 CustomProperties { get; set; } = new(); + } + + /// + /// Estado de una página específica + /// + public class PageState + { + public string PageId { get; set; } + public string PageName { get; set; } + public List LocalObjects { get; set; } = new(); + public Dictionary GlobalObjectStates { get; set; } = new(); + public Dictionary PageMetadata { get; set; } = new(); + } + + /// + /// Estado global de la aplicación + /// + public class GlobalState + { + public List SharedObjects { get; set; } = new(); + public Dictionary PagesMetadata { get; set; } = new(); + public PLCViewModel PLCConfiguration { get; set; } + public UnitConverter UnitConverter { get; set; } + public int LastUsedId { get; set; } + } + + /// + /// Gestor principal de estados de la aplicación + /// + public class StateManager : IDisposable + { + private readonly string _basePath; + private GlobalState _globalState; + private Dictionary _loadedPages; + private string _currentPageId; + private readonly object _lockObject = new(); + private bool _hasUnsavedChanges; + private MainViewModel _mainViewModel; + + public event EventHandler PageStateChanged; + public event EventHandler GlobalStateChanged; + + public StateManager(string basePath, MainViewModel mainViewModel) + { + _basePath = basePath; + _mainViewModel = mainViewModel; + _loadedPages = new Dictionary(); + 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(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 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(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 CaptureCustomProperties(osBase obj) + { + var customProps = new Dictionary(); + 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(); + } + } + } + + /// + /// Atributo para marcar propiedades que deben ser serializadas como propiedades personalizadas + /// + [AttributeUsage(AttributeTargets.Property)] + public class SerializeAttribute : Attribute { } +} \ No newline at end of file diff --git a/FuncionesBase/Idiomas.cs b/FuncionesBase/Idiomas.cs new file mode 100644 index 0000000..8d2756a --- /dev/null +++ b/FuncionesBase/Idiomas.cs @@ -0,0 +1,68 @@ +using LanguageDetection; +using System.Collections.Generic; +using System.ComponentModel; +using Xceed.Wpf.Toolkit.PropertyGrid.Attributes; + +namespace CtrEditor.FuncionesBase +{ + public class Idiomas + { + public const string DEFAULT_LANGUAGE = "English"; + + private static readonly Dictionary _languageMap = new Dictionary + { + { "en", "English" }, + { "es", "Spanish" }, + { "it", "Italian" }, + { "pt", "Portuguese" }, + { "fr", "French" }, + { "de", "German" } + }; + + private static readonly LanguageDetector _languageDetector; + + static Idiomas() + { + _languageDetector = new LanguageDetector(); + _languageDetector.AddLanguages("en", "es", "it", "pt", "fr", "de"); + } + + public static List GetLanguageList() + { + return new List(_languageMap.Values); + } + + public static string DetectarIdioma(string texto) + { + if (string.IsNullOrEmpty(texto)) return DEFAULT_LANGUAGE; + + try + { + string detectedLanguageCode = _languageDetector.Detect(texto); + return _languageMap.GetValueOrDefault(detectedLanguageCode, DEFAULT_LANGUAGE); + } + catch + { + return DEFAULT_LANGUAGE; + } + } + + public static string GetLanguageCode(string languageName) + { + return _languageMap.FirstOrDefault(x => x.Value == languageName).Key ?? "en"; + } + } + + public class IdiomasItemsSource : IItemsSource + { + public ItemCollection GetValues() + { + ItemCollection items = new ItemCollection(); + foreach (string language in Idiomas.GetLanguageList()) + { + items.Add(language); + } + return items; + } + } +} diff --git a/FuncionesBase/TagPattern.cs b/FuncionesBase/TagPattern.cs new file mode 100644 index 0000000..649defa --- /dev/null +++ b/FuncionesBase/TagPattern.cs @@ -0,0 +1,99 @@ +using System; +using System.Text.RegularExpressions; +using System.Collections.Generic; +using Xceed.Wpf.Toolkit.PropertyGrid.Attributes; + +namespace CtrEditor.FuncionesBase +{ + public class TagPattern + { + public const string DEFAULT_PATTERN = "DESCRIPCION"; + + private static readonly Dictionary _patternDescriptions = new Dictionary + { + { "Descripcion", "First letter capitalized" }, + { "DESCRIPCION", "All uppercase text" }, + { "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)" }, + { "Numero", "Numeric value" } + }; + + public static List GetPatternList() + { + return new List(_patternDescriptions.Keys); + } + + public static string ApplyPattern(string text, string pattern) + { + if (string.IsNullOrEmpty(text)) return text; + + return pattern switch + { + "Descripcion" => ApplyDescripcionPattern(text), + "DESCRIPCION" => text.ToUpper(), + "Siemens IO Input" => ApplySiemensPattern(text, "E"), + "Siemens IO Output" => ApplySiemensPattern(text, "A"), + "LETRASNUMEROS" => ApplyLetrasNumerosPattern(text), + "Numero" => ApplyNumberPattern(text), + _ => text + }; + } + + private static string ApplyDescripcionPattern(string text) + { + if (string.IsNullOrEmpty(text)) return text; + return char.ToUpper(text[0]) + (text.Length > 1 ? text.Substring(1).ToLower() : ""); + } + + private static string ApplySiemensPattern(string text, string prefix) + { + var match = Regex.Match(text, $"{prefix}?(\\d{{1,5}})\\.?(\\d)?", RegexOptions.IgnoreCase); + if (match.Success) + { + int address = Math.Min(int.Parse(match.Groups[1].Value), 65535); + int bit = match.Groups[2].Success ? + Math.Min(int.Parse(match.Groups[2].Value), 7) : 0; + return $"{prefix}{address}.{bit}"; + } + return $"{prefix}0.0"; // Default value if pattern doesn't match + } + + private static string ApplyLetrasNumerosPattern(string text) + { + // Extract letters and numbers from the text + var letters = new string(text.Where(c => char.IsLetter(c)).Take(10).ToArray()); + var numbers = new string(text.Where(c => char.IsDigit(c)).ToArray()); + + // If no letters found, return "A" as default + if (string.IsNullOrEmpty(letters)) + letters = "A"; + + // If no numbers found, return "0" as default + if (string.IsNullOrEmpty(numbers)) + numbers = "0"; + + // Combine letters (uppercase) and numbers + return $"{letters.ToUpper()}{numbers}"; + } + + private static string ApplyNumberPattern(string text) + { + var match = Regex.Match(text, @"-?\d+\.?\d*"); + return match.Success ? match.Value : "0"; + } + } + + public class TagPatternItemsSource : IItemsSource + { + public ItemCollection GetValues() + { + ItemCollection items = new ItemCollection(); + foreach (string pattern in TagPattern.GetPatternList()) + { + items.Add(pattern); + } + return items; + } + } +} diff --git a/IA/gtpask.cs b/IA/gtpask.cs index 3afbaf2..c08a982 100644 --- a/IA/gtpask.cs +++ b/IA/gtpask.cs @@ -9,6 +9,7 @@ using LanguageDetection; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System.Diagnostics; +using CtrEditor.FuncionesBase; namespace GTPCorrgir { @@ -70,15 +71,6 @@ namespace GTPCorrgir private string _grokApiKey; private readonly HttpClient _httpClient; private bool _disposed; - private readonly LanguageDetector _languageDetector; - - private readonly Dictionary _languageMap = new Dictionary - { - { "en", "English" }, - { "es", "Spanish" }, - { "it", "Italian" }, - { "pt", "Portuguese" } - }; public string IdiomaDetectado { get; private set; } public string TextoACorregir { get; set; } @@ -90,8 +82,6 @@ namespace GTPCorrgir try { _httpClient = new HttpClient(); - _languageDetector = new LanguageDetector(); - _languageDetector.AddLanguages("en", "es", "it", "pt"); LoadApiKeys(); InitializeHttpClient(); @@ -153,20 +143,15 @@ namespace GTPCorrgir { try { - - string detectedLanguageCode = _languageDetector.Detect(TextoACorregir); - IdiomaDetectado = _languageMap.GetValueOrDefault(detectedLanguageCode, "Desconocido"); - - return IdiomaDetectado != "Desconocido"; + IdiomaDetectado = Idiomas.DetectarIdioma(TextoACorregir); + return IdiomaDetectado != "Unknown"; } - catch (Exception ex) + catch { return false; } } - - private async Task ProcesarTextoConLLM( Opciones Modelo) { try diff --git a/Icons/match.png b/Icons/match.png new file mode 100644 index 0000000000000000000000000000000000000000..358ddf4e9c6ec216c5145b97c41c5f3ceada1dc0 GIT binary patch literal 43494 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

v+}(-o{<@Xh>p6A_@Px2ppZtKd^q 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 IM6N<$f;9Swm;e9( literal 0 HcmV?d00001 diff --git a/MainViewModel.cs b/MainViewModel.cs index 65f49a0..285a2f6 100644 --- a/MainViewModel.cs +++ b/MainViewModel.cs @@ -43,9 +43,24 @@ namespace CtrEditor public Canvas MainCanvas; - public bool IsConnected + [ObservableProperty] + private bool isConnected; + + partial void OnPLCViewModelChanged(PLCViewModel value) { - get => PLCViewModel.IsConnected; + if (value != null) + { + value.PropertyChanged += PLCViewModel_PropertyChanged; + IsConnected = value.IsConnected; + } + } + + private void PLCViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(PLCViewModel.IsConnected)) + { + IsConnected = PLCViewModel.IsConnected; + } } [ObservableProperty] @@ -64,8 +79,6 @@ namespace CtrEditor public ICommand TBStartSimulationCommand { get; } public ICommand TBStopSimulationCommand { get; } public ICommand TBSaveCommand { get; } - public ICommand TBConnectPLCCommand { get; } - public ICommand TBDisconnectPLCCommand { get; } public ICommand TBExtractTagsCommand { get; } public ICommand TBEliminarUserControlCommand { get; } @@ -79,9 +92,17 @@ namespace CtrEditor public ICommand TBAssingPagesCommand { get; } public ICommand TBMultiPageExtractTagsCommand { get; } public ICommand TBMultiPageAnalizeCommand { get; } - public ICommand TBAnalyzeMatrixCommand { get; } public ICommand TBMultiPageMatrixCommand { get; } + + public ICommand TBTogglePLCConnectionCommand => new RelayCommand(() => + { + if (IsConnected) + DisconnectPLC(); + else + ConnectPLC(); + }); + // Evento que se dispara cuando se selecciona una nueva imagen public event EventHandler ImageSelected; public event EventHandler TickSimulacion; @@ -202,27 +223,24 @@ namespace CtrEditor [ObservableProperty] private string selectedImage; + partial void OnSelectedImageChanging(string? oldValue, string newValue) + { + if (HasUnsavedChanges && !inhibitSaveChangesControl) + { + var result = MessageBox.Show("There are unsaved changes. Do you want to save them?", + "Save Changes", + MessageBoxButton.YesNo); + + if (result == MessageBoxResult.Yes) + { + SaveStateObjetosSimulables(); + } + } + } partial void OnSelectedImageChanged(string value) { if (value != null) { - if (HasUnsavedChanges && !inhibitSaveChangesControl) - { - var result = MessageBox.Show("There are unsaved changes. Do you want to save them?", - "Save Changes", - MessageBoxButton.YesNoCancel); - - if (result == MessageBoxResult.Cancel) - { - OnPropertyChanged(nameof(SelectedImage)); // Restore previous selection - return; - } - else if (result == MessageBoxResult.Yes) - { - SaveStateObjetosSimulables(); - } - } - StopSimulation(); ImageSelected?.Invoke(this, datosDeTrabajo.Imagenes[value]); LoadStateObjetosSimulables(); @@ -271,6 +289,8 @@ namespace CtrEditor // Constructor // + private StateManager _stateManager; + public MainViewModel() { OpenWorkDirectoryCommand = new RelayCommand(OpenWorkDirectory); @@ -289,7 +309,7 @@ namespace CtrEditor PLCViewModel = new PLCViewModel(); _timerPLCUpdate = new DispatcherTimer(); - _timerPLCUpdate.Interval = TimeSpan.FromMilliseconds(50); // ajusta el intervalo según sea necesario + _timerPLCUpdate.Interval = TimeSpan.FromMilliseconds(10); // ajusta el intervalo según sea necesario _timerPLCUpdate.Tick += OnRefreshEvent; InitializeTipoSimulableList(); @@ -306,21 +326,15 @@ namespace CtrEditor TBStartSimulationCommand = new RelayCommand(StartSimulation, () => !IsSimulationRunning); TBStopSimulationCommand = new RelayCommand(StopSimulation, () => IsSimulationRunning); TBSaveCommand = new RelayCommand(Save); - TBConnectPLCCommand = new RelayCommand(ConnectPLC, () => !PLCViewModel.IsConnected); - TBDisconnectPLCCommand = new RelayCommand(DisconnectPLC, () => PLCViewModel.IsConnected); TBEliminarUserControlCommand = new RelayCommand(EliminarUserControl, () => habilitarEliminarUserControl); TBDuplicarUserControlCommand = new RelayCommand(DuplicarUserControl, () => habilitarEliminarUserControl); - TBExtractTagsCommand = new RelayCommand(ExtraerTags); - TBEliminarTodosCommand = new RelayCommand(EliminarTodosCommand); TBEliminarAutoCreatedCommand = new RelayCommand(EliminarAutoCreatedCommand); TBEliminarClonedCommand = new RelayCommand(EliminarClonedCommand); TBAssingPagesCommand = new RelayCommand(AssingPagesCommand); - TBMultiPageExtractTagsCommand = new RelayCommand(MultiPageExtractTagsCommand); TBMultiPageAnalizeCommand = new RelayCommand(MultiPageAnalizeCommand); - TBAnalyzeMatrixCommand = new RelayCommand(AnalyzeMatrixCommand); TBMultiPageMatrixCommand = new RelayCommand(MultiPageMatrixCommand); stopwatch_Sim = new Stopwatch(); @@ -334,6 +348,8 @@ namespace CtrEditor recentDirectories = new ObservableCollection(EstadoPersistente.Instance.RecentDirectories); OpenRecentDirectoryCommand = new RelayCommand(OpenRecentDirectory); + + _stateManager = new StateManager(EstadoPersistente.Instance.directorio, this); } private void OsListFilter_PropertyChanged(object? sender, PropertyChangedEventArgs e) @@ -544,51 +560,6 @@ namespace CtrEditor Application.Current.Dispatcher.Invoke(() => { }, DispatcherPriority.ApplicationIdle); } - - private async void MultiPageExtractTagsCommand() - { - if (HasUnsavedChanges) - { - var result = MessageBox.Show("There are unsaved changes. Do you want to save them?", - "Save Changes", - MessageBoxButton.YesNoCancel); - - if (result == MessageBoxResult.Cancel) - return; - else if (result == MessageBoxResult.Yes) - SaveStateObjetosSimulables(); - } - - var ImagenesSeleccionadas = new ObservableCollection - { - SelectedImage - }; - - StopSimulation(); - - var selectPagesWindow = new SelectPages(); - var selectPagesViewModel = new SelectPagesViewModel(); - selectPagesViewModel.Initialize(this, selectPagesWindow, ref ImagenesSeleccionadas); - selectPagesWindow.DataContext = selectPagesViewModel; - selectPagesWindow.ShowDialog(); - - inhibitSaveChangesControl = true; - try - { - if (selectPagesWindow.DataContext is SelectPagesViewModel dialog && dialog.CloseOK) - foreach (var page in ImagenesSeleccionadas) - { - SelectedImage = page; - await WaitForUIUpdateAsync(); // Espera a que la UI se actualice - ExtraerTags(); - } - } - finally - { - inhibitSaveChangesControl = false; - } - } - private async void MultiPageAnalizeCommand() { var ImagenesSeleccionadas = new ObservableCollection @@ -641,148 +612,6 @@ namespace CtrEditor } - - ///

- /// Extrae y formatea las etiquetas de los objetos simulables y las guarda en un archivo Excel. - /// - private void ExtraerTags() - { - // Obtiene la ruta del archivo Excel donde se guardarán los datos. - var filePath = DatosDeTrabajo.ObtenerPathAllPages(".xlsx"); - - try - { - // Crea o abre un libro de Excel. - XLWorkbook workbook = File.Exists(filePath) ? new XLWorkbook(filePath) : new XLWorkbook(); - var sheetName = "TagsExtracted"; - // Obtiene o crea la hoja de trabajo "TagsExtracted". - var worksheet = workbook.Worksheets.Contains(sheetName) ? workbook.Worksheet(sheetName) : workbook.Worksheets.Add(sheetName); - var lastRowUsed = worksheet.LastRowUsed(); - // Determina la fila en la que se empezarán a escribir los datos. - int rowOffset = lastRowUsed == null ? 2 : lastRowUsed.RowNumber() + 1; - - // Determina la columna fija más alta. - List columnasOcupadas = new List(); - int actualMaxCol = 0; - int col = 0; - - // Filtrar los objetos de tipo osExtraccionTag y crear una nueva lista - var osBuscarCoincidencias_List = ObjetosSimulables - .OfType() - .Where(tag => tag.Show_On_This_Page) - .ToList(); - var osExtraccionTagBaseGrouped_List = ObjetosSimulables - .OfType() - .Where(tag => tag.Show_On_This_Page && !tag.Cloned && tag.Id_Search_Templates != null && tag.Id_Search_Templates != "") - .ToList(); - var osExtraccionTagBaseFix_List = ObjetosSimulables - .OfType() - .Where(tag => tag.Show_On_This_Page && !tag.Cloned && (tag.Id_Search_Templates == null || tag.Id_Search_Templates == "")) - .ToList(); - - var osExtraccionTagCloned_List = ObjetosSimulables - .OfType() - .Where(tag => tag.Show_On_This_Page && tag.Cloned) - .ToList(); - - // Columnas Fijas para los Tags no agrupados que no son clonados - foreach (var objExtraccionTag in osExtraccionTagBaseFix_List) - if ((string.IsNullOrEmpty(objExtraccionTag.Id_Search_Templates)) && !objExtraccionTag.Cloned) - { - col = objExtraccionTag.Collumn_number; - if (col == 0 || columnasOcupadas.Contains(col)) - col = ++actualMaxCol; - else - actualMaxCol = Math.Max(actualMaxCol, col); - - columnasOcupadas.Add(col); - objExtraccionTag.Collumn_number = col; - } - - // Tags Agrupados no Clonados - foreach (var objBC in osBuscarCoincidencias_List) - foreach (var objExtraccionTag in osExtraccionTagBaseGrouped_List) - if (objExtraccionTag.Id_Search_Templates == objBC.Nombre && !objExtraccionTag.Cloned) - { - col = objExtraccionTag.Collumn_number; - if (col == 0 || columnasOcupadas.Contains(col)) - col = ++actualMaxCol; - else - actualMaxCol = Math.Max(actualMaxCol, col); - - columnasOcupadas.Add(col); - objExtraccionTag.Collumn_number = col; - } - - int RowToRender = 0; - // Cloned Tag - Asignar las mismas columnas - foreach (var oFrom in osExtraccionTagBaseGrouped_List) - foreach (var oCloned in osExtraccionTagCloned_List) - { - if (oCloned.Cloned_from == oFrom.Id) - oCloned.Collumn_number = oFrom.Collumn_number; - RowToRender = Math.Max(RowToRender, oCloned.Copy_Number); - } - - // Render Rows - for (int row = 0; row < RowToRender; row++) - { - // Render Fix tags - foreach (var TagFixs in osExtraccionTagBaseFix_List) - { - col = TagFixs.Collumn_number; - if (worksheet.Cell(1, col).IsEmpty()) - worksheet.Cell(1, col).Value = TagFixs.Collumn_name; - TagFixs.CaptureImageAreaAndDoOCR(); - worksheet.Cell(row + rowOffset, col).Value = TagFixs.Tag_extract; - } - // Render Cloned tags - foreach (var TagCloned in osExtraccionTagCloned_List) - { - if (TagCloned.Copy_Number == row) // Estamos en la fila correcta - { - col = TagCloned.Collumn_number; - if (worksheet.Cell(1, col).IsEmpty()) - worksheet.Cell(1, col).Value = TagCloned.Collumn_name; - - TagCloned.CaptureImageAreaAndDoOCR(); - worksheet.Cell(row + rowOffset, col).Value = TagCloned.Tag_extract; - } - } - } - - - // Formatear los títulos en la fila 1 - var titleRow = worksheet.Row(1); - titleRow.Style.Font.Bold = true; - titleRow.Style.Fill.BackgroundColor = XLColor.LightGray; - titleRow.Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center; - - // Auto dimensionado de las columnas utilizadas - worksheet.Columns().AdjustToContents(); - - // Guarda el libro de Excel. - workbook.SaveAs(filePath); - HasUnsavedChanges = false; - } - catch (IOException ex) - { - // Muestra un diálogo de error si ocurre una excepción de IO. - var dialog = new TaskDialog - { - WindowTitle = "IOException", - MainInstruction = "Error al acceder al archivo", - Content = $"El proceso no puede acceder al archivo '{filePath}' porque está siendo utilizado por otro proceso.", - ExpandedInformation = ex.ToString(), - MainIcon = TaskDialogIcon.Error, - ButtonStyle = TaskDialogButtonStyle.Standard - }; - dialog.Buttons.Add(new TaskDialogButton(ButtonType.Ok)); - dialog.ShowDialog(); - } - } - - private void InitializeTipoSimulableList() { var baseType = typeof(osBase); @@ -1142,15 +971,6 @@ namespace CtrEditor Application.Current.Shutdown(); } - private void AnalyzeMatrixCommand() - { - var matrixPreviewWindow = new MatrixPreviewWindow(); - var matrixPreviewViewModel = new MatrixPreviewViewModel(); - matrixPreviewViewModel.Initialize(this, matrixPreviewWindow); - matrixPreviewWindow.DataContext = matrixPreviewViewModel; - matrixPreviewWindow.ShowDialog(); - } - private async void MultiPageMatrixCommand() { if (HasUnsavedChanges) diff --git a/MainWindow.xaml b/MainWindow.xaml index 761712c..921e08e 100644 --- a/MainWindow.xaml +++ b/MainWindow.xaml @@ -27,13 +27,21 @@ + + + + + + @@ -116,42 +124,29 @@ - - - - + diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs index fc4802f..a1719f3 100644 --- a/MainWindow.xaml.cs +++ b/MainWindow.xaml.cs @@ -288,6 +288,15 @@ namespace CtrEditor new Tuple(new Point(rectBox.Right, rectBox.Top + rectBox.Height / 2), "CenterRight") }; + // Add validation before setting Canvas position + void SetCanvasPosition(UIElement element, double left, double top) + { + if (!double.IsInfinity(left) && !double.IsNaN(left)) + Canvas.SetLeft(element, left); + if (!double.IsInfinity(top) && !double.IsNaN(top)) + Canvas.SetTop(element, top); + } + foreach (var position in positions) { Rectangle rect = new Rectangle @@ -337,8 +346,8 @@ namespace CtrEditor break; } - Canvas.SetLeft(rect, position.Item1.X - rectSize / 2); - Canvas.SetTop(rect, position.Item1.Y - rectSize / 2); + // Replace direct Canvas.Set calls with the validation method + SetCanvasPosition(rect, position.Item1.X - rectSize / 2, position.Item1.Y - rectSize / 2); rect.MouseLeftButtonDown += UserControl_MouseLeftButtonDown; rect.MouseMove += UserControl_MouseMove; diff --git a/ObjetosSim/Estaticos/ucVMmotorSim.xaml.cs b/ObjetosSim/Estaticos/ucVMmotorSim.xaml.cs index 7521d7c..6616a41 100644 --- a/ObjetosSim/Estaticos/ucVMmotorSim.xaml.cs +++ b/ObjetosSim/Estaticos/ucVMmotorSim.xaml.cs @@ -123,8 +123,6 @@ namespace CtrEditor.ObjetosSim public override void UpdatePLC(PLCViewModel plc, int TotalMilliseconds) { - Stopwatch stopwatch = new Stopwatch(); - stopwatch.Start(); elapsedTimeAccumulator += TotalMilliseconds; float randomFactor = (float)(new Random().NextDouble() * 0.1); // 10% random factor @@ -135,12 +133,9 @@ namespace CtrEditor.ObjetosSim motState.UpdatePLC(plc, this, TotalMilliseconds); elapsedTimeAccumulator = 0; } - Velocidad = (Proporcional_Speed / 100) * (motState.STATUS_VFD_ACT_Speed_Hz / 10); Sentido_contrario = motState.OUT_Reversal; - stopwatch.Stop(); - Debug.WriteLine($" osVMmotorSim : {Nombre} : {stopwatch.Elapsed.TotalMilliseconds} ms"); } public override void UpdateControl(int TotalMilliseconds) diff --git a/ObjetosSim/Extraccion Datos/ucBuscarCoincidencias.xaml.cs b/ObjetosSim/Extraccion Datos/ucBuscarCoincidencias.xaml.cs index 590b00f..d1424ae 100644 --- a/ObjetosSim/Extraccion Datos/ucBuscarCoincidencias.xaml.cs +++ b/ObjetosSim/Extraccion Datos/ucBuscarCoincidencias.xaml.cs @@ -27,9 +27,40 @@ using Emgu.CV.Structure; namespace CtrEditor.ObjetosSim.Extraccion_Datos { /// - /// Interaction logic for ucBuscarCoincidencias.xaml + /// Represents a template search control that identifies similar patterns in images and creates tag extraction clones. + /// This class is designed to work with OCR extraction by finding visual patterns and creating copies of extraction tags + /// at each matching location. /// + /// + /// Key functionalities: + /// - Template matching using OpenCV + /// - Automatic tag cloning at found locations + /// - OCR text extraction from matched regions + /// - Export capabilities to Excel /// + /// Workflow: + /// 1. User creates a search template by positioning and sizing the control over a pattern + /// 2. Links extraction tags to this template using Id_Search_Templates + /// 3. Activates search_templates to find similar patterns + /// 4. The system automatically: + /// - Searches for visual matches in the image + /// - Creates clones of linked extraction tags at each match + /// - Assigns incremental copy_Number to organize rows in exports + /// - Performs OCR on each cloned tag location + /// + /// Properties: + /// - search_templates: Triggers the pattern search process + /// - threshold: Minimum similarity threshold for pattern matching + /// - coincidencias: Number of matches found (readonly) + /// - show_debug_ocr: Shows debug windows during OCR process + /// - export_ocr: Triggers OCR text export for all matches + /// + /// Usage example: + /// 1. Position the search template over a repeating pattern + /// 2. Create extraction tags and link them to this template + /// 3. Set threshold value (default usually works well) + /// 4. Activate search_templates to find matches and create clones + /// public partial class osBuscarCoincidencias : osBase, IosBase { @@ -362,9 +393,6 @@ namespace CtrEditor.ObjetosSim.Extraccion_Datos } } Row++; - if (newObj != null) - newObj.New_Row = true; - } } diff --git a/ObjetosSim/Extraccion Datos/ucExtraccionTag.xaml.cs b/ObjetosSim/Extraccion Datos/ucExtraccionTag.xaml.cs index 2631580..13654cc 100644 --- a/ObjetosSim/Extraccion Datos/ucExtraccionTag.xaml.cs +++ b/ObjetosSim/Extraccion Datos/ucExtraccionTag.xaml.cs @@ -36,7 +36,18 @@ namespace CtrEditor.ObjetosSim.Extraccion_Datos bool extraer; [ObservableProperty] - bool new_Row; + [property: Category("Tag Extraction:")] + bool eliminar_enters; + + [ObservableProperty] + [property: Category("Tag Extraction:")] + [property: ItemsSource(typeof(IdiomasItemsSource))] + string idioma_Extraccion; + + [ObservableProperty] + [property: Category("Tag Extraction:")] + [property: ItemsSource(typeof(TagPatternItemsSource))] + string pattern_Type; public override void TopChanged(float value) { @@ -147,11 +158,39 @@ namespace CtrEditor.ObjetosSim.Extraccion_Datos Alto = 1; Angulo = 0; Opacity_oculto = 0.1f; + Idioma_Extraccion = Idiomas.DEFAULT_LANGUAGE; + Pattern_Type = TagPattern.DEFAULT_PATTERN; } public void CaptureImageAreaAndDoOCR() { - Tag_extract = CaptureImageAreaAndDoOCR(Left, Top, Ancho, Alto, Angulo, Show_Debug_Window); + string extractedText = CaptureImageAreaAndDoOCR(Left, Top, Ancho, Alto, Angulo, Show_Debug_Window); + + // Clean up the extracted text if eliminar_enters is true + if (Eliminar_enters && !string.IsNullOrEmpty(extractedText)) + { + // Replace all types of line endings with spaces + extractedText = extractedText.Replace("\r\n", " ") + .Replace("\n", " ") + .Replace("\r", " "); + + // Replace multiple consecutive spaces with a single space + extractedText = System.Text.RegularExpressions.Regex.Replace(extractedText, @"\s+", " "); + + // Trim spaces at the beginning and end + extractedText = extractedText.Trim(); + } + + // Apply the selected pattern + extractedText = TagPattern.ApplyPattern(extractedText, Pattern_Type); + + Tag_extract = extractedText; + + // Set default language to English if not set + if (string.IsNullOrEmpty(Idioma_Extraccion)) + { + Idioma_Extraccion = Idiomas.DEFAULT_LANGUAGE; + } } public int ExportToExcel(IXLWorksheet worksheet, int row, int colBase) diff --git a/ObjetosSim/osBase.cs b/ObjetosSim/osBase.cs index e26f732..df4cdce 100644 --- a/ObjetosSim/osBase.cs +++ b/ObjetosSim/osBase.cs @@ -312,7 +312,7 @@ namespace CtrEditor.ObjetosSim if (e.PropertyName == nameof(osFramePlate.Left)) { Left += ((osFramePlate)sender).offsetX; - OnMoveResizeRotate(); + OnMoveResizeRotate(); } if (e.PropertyName == nameof(osFramePlate.Zindex_FramePlate)) diff --git a/PopUps/AssignImagesWindow.xaml.cs b/PopUps/AssignImagesWindow.xaml.cs index aec6359..a2e485f 100644 --- a/PopUps/AssignImagesWindow.xaml.cs +++ b/PopUps/AssignImagesWindow.xaml.cs @@ -12,7 +12,7 @@ namespace CtrEditor.PopUps /// Interaction logic for AssignImagesWindow.xaml /// public partial class AssignImagesWindow : Window - { + { public AssignImagesWindow() { InitializeComponent(); diff --git a/PopUps/ColumnSelectionDialog.xaml.cs b/PopUps/ColumnSelectionDialog.xaml.cs index a7cc3d0..a34de8d 100644 --- a/PopUps/ColumnSelectionDialog.xaml.cs +++ b/PopUps/ColumnSelectionDialog.xaml.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using System.Windows; +using CtrEditor.FuncionesBase; namespace CtrEditor.PopUps { @@ -12,15 +13,7 @@ namespace CtrEditor.PopUps public string SelectedSourceLanguage { get; private set; } public string SelectedTargetLanguage { get; private set; } - private static readonly List SupportedLanguages = new List - { - "English", - "Spanish", - "Italian", - "French", - "German", - "Portuguese" - }; + private static readonly List SupportedLanguages = Idiomas.GetLanguageList(); private string _sourceColumnLabel = "Idioma 1"; private string _targetColumnLabel = "Idioma 2"; diff --git a/PopUps/MatrixPreviewWindow.xaml b/PopUps/MatrixPreviewWindow.xaml index bf7a0be..c3db2b4 100644 --- a/PopUps/MatrixPreviewWindow.xaml +++ b/PopUps/MatrixPreviewWindow.xaml @@ -1,7 +1,7 @@ + Title="Exportar Tags Extraidos" Height="600" Width="800"> @@ -70,23 +70,23 @@ HorizontalAlignment="Right" Margin="5" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"> -