Mejorado la descripcion de las extracciones de Tag con Patrones

This commit is contained in:
Miguel 2025-02-17 13:04:21 +01:00
parent 8573d942c4
commit 633cd71d00
21 changed files with 779 additions and 305 deletions

View File

View File

@ -44,6 +44,8 @@
<None Remove="Icons\duplicate.png" /> <None Remove="Icons\duplicate.png" />
<None Remove="Icons\extract.png" /> <None Remove="Icons\extract.png" />
<None Remove="Icons\fotocelula.png" /> <None Remove="Icons\fotocelula.png" />
<None Remove="Icons\match.png" />
<None Remove="Icons\ocr.png" />
<None Remove="Icons\rotation.cur" /> <None Remove="Icons\rotation.cur" />
<None Remove="Icons\rotation32x32.cur" /> <None Remove="Icons\rotation32x32.cur" />
<None Remove="Icons\rotationRx.cur" /> <None Remove="Icons\rotationRx.cur" />
@ -121,6 +123,8 @@
<CopyToOutputDirectory></CopyToOutputDirectory> <CopyToOutputDirectory></CopyToOutputDirectory>
</Resource> </Resource>
<Resource Include="Icons\fotocelula.png" /> <Resource Include="Icons\fotocelula.png" />
<Resource Include="Icons\match.png" />
<Resource Include="Icons\ocr.png" />
<Resource Include="Icons\rotationRx.cur" /> <Resource Include="Icons\rotationRx.cur" />
<Resource Include="Icons\rotationSx.cur" /> <Resource Include="Icons\rotationSx.cur" />
<Resource Include="Icons\save.png" /> <Resource Include="Icons\save.png" />

383
DataStates/StateManager.cs Normal file
View File

@ -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
{
/// <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 { }
}

68
FuncionesBase/Idiomas.cs Normal file
View File

@ -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<string, string> _languageMap = new Dictionary<string, string>
{
{ "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<string> GetLanguageList()
{
return new List<string>(_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<T> : IItemsSource
{
public ItemCollection GetValues()
{
ItemCollection items = new ItemCollection();
foreach (string language in Idiomas.GetLanguageList())
{
items.Add(language);
}
return items;
}
}
}

View File

@ -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<string, string> _patternDescriptions = new Dictionary<string, string>
{
{ "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<string> GetPatternList()
{
return new List<string>(_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<T> : IItemsSource
{
public ItemCollection GetValues()
{
ItemCollection items = new ItemCollection();
foreach (string pattern in TagPattern.GetPatternList())
{
items.Add(pattern);
}
return items;
}
}
}

View File

@ -9,6 +9,7 @@ using LanguageDetection;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using System.Diagnostics; using System.Diagnostics;
using CtrEditor.FuncionesBase;
namespace GTPCorrgir namespace GTPCorrgir
{ {
@ -70,15 +71,6 @@ namespace GTPCorrgir
private string _grokApiKey; private string _grokApiKey;
private readonly HttpClient _httpClient; private readonly HttpClient _httpClient;
private bool _disposed; private bool _disposed;
private readonly LanguageDetector _languageDetector;
private readonly Dictionary<string, string> _languageMap = new Dictionary<string, string>
{
{ "en", "English" },
{ "es", "Spanish" },
{ "it", "Italian" },
{ "pt", "Portuguese" }
};
public string IdiomaDetectado { get; private set; } public string IdiomaDetectado { get; private set; }
public string TextoACorregir { get; set; } public string TextoACorregir { get; set; }
@ -90,8 +82,6 @@ namespace GTPCorrgir
try try
{ {
_httpClient = new HttpClient(); _httpClient = new HttpClient();
_languageDetector = new LanguageDetector();
_languageDetector.AddLanguages("en", "es", "it", "pt");
LoadApiKeys(); LoadApiKeys();
InitializeHttpClient(); InitializeHttpClient();
@ -153,20 +143,15 @@ namespace GTPCorrgir
{ {
try try
{ {
IdiomaDetectado = Idiomas.DetectarIdioma(TextoACorregir);
string detectedLanguageCode = _languageDetector.Detect(TextoACorregir); return IdiomaDetectado != "Unknown";
IdiomaDetectado = _languageMap.GetValueOrDefault(detectedLanguageCode, "Desconocido");
return IdiomaDetectado != "Desconocido";
} }
catch (Exception ex) catch
{ {
return false; return false;
} }
} }
private async Task ProcesarTextoConLLM( Opciones Modelo) private async Task ProcesarTextoConLLM( Opciones Modelo)
{ {
try try

BIN
Icons/match.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

BIN
Icons/ocr.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -43,9 +43,24 @@ namespace CtrEditor
public Canvas MainCanvas; 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] [ObservableProperty]
@ -64,8 +79,6 @@ namespace CtrEditor
public ICommand TBStartSimulationCommand { get; } public ICommand TBStartSimulationCommand { get; }
public ICommand TBStopSimulationCommand { get; } public ICommand TBStopSimulationCommand { get; }
public ICommand TBSaveCommand { get; } public ICommand TBSaveCommand { get; }
public ICommand TBConnectPLCCommand { get; }
public ICommand TBDisconnectPLCCommand { get; }
public ICommand TBExtractTagsCommand { get; } public ICommand TBExtractTagsCommand { get; }
public ICommand TBEliminarUserControlCommand { get; } public ICommand TBEliminarUserControlCommand { get; }
@ -79,9 +92,17 @@ namespace CtrEditor
public ICommand TBAssingPagesCommand { get; } public ICommand TBAssingPagesCommand { get; }
public ICommand TBMultiPageExtractTagsCommand { get; } public ICommand TBMultiPageExtractTagsCommand { get; }
public ICommand TBMultiPageAnalizeCommand { get; } public ICommand TBMultiPageAnalizeCommand { get; }
public ICommand TBAnalyzeMatrixCommand { get; }
public ICommand TBMultiPageMatrixCommand { 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 // Evento que se dispara cuando se selecciona una nueva imagen
public event EventHandler<string> ImageSelected; public event EventHandler<string> ImageSelected;
public event EventHandler<TickSimulacionEventArgs> TickSimulacion; public event EventHandler<TickSimulacionEventArgs> TickSimulacion;
@ -202,27 +223,24 @@ namespace CtrEditor
[ObservableProperty] [ObservableProperty]
private string selectedImage; 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) partial void OnSelectedImageChanged(string value)
{ {
if (value != null) 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(); StopSimulation();
ImageSelected?.Invoke(this, datosDeTrabajo.Imagenes[value]); ImageSelected?.Invoke(this, datosDeTrabajo.Imagenes[value]);
LoadStateObjetosSimulables(); LoadStateObjetosSimulables();
@ -271,6 +289,8 @@ namespace CtrEditor
// Constructor // Constructor
// //
private StateManager _stateManager;
public MainViewModel() public MainViewModel()
{ {
OpenWorkDirectoryCommand = new RelayCommand(OpenWorkDirectory); OpenWorkDirectoryCommand = new RelayCommand(OpenWorkDirectory);
@ -289,7 +309,7 @@ namespace CtrEditor
PLCViewModel = new PLCViewModel(); PLCViewModel = new PLCViewModel();
_timerPLCUpdate = new DispatcherTimer(); _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; _timerPLCUpdate.Tick += OnRefreshEvent;
InitializeTipoSimulableList(); InitializeTipoSimulableList();
@ -306,21 +326,15 @@ namespace CtrEditor
TBStartSimulationCommand = new RelayCommand(StartSimulation, () => !IsSimulationRunning); TBStartSimulationCommand = new RelayCommand(StartSimulation, () => !IsSimulationRunning);
TBStopSimulationCommand = new RelayCommand(StopSimulation, () => IsSimulationRunning); TBStopSimulationCommand = new RelayCommand(StopSimulation, () => IsSimulationRunning);
TBSaveCommand = new RelayCommand(Save); TBSaveCommand = new RelayCommand(Save);
TBConnectPLCCommand = new RelayCommand(ConnectPLC, () => !PLCViewModel.IsConnected);
TBDisconnectPLCCommand = new RelayCommand(DisconnectPLC, () => PLCViewModel.IsConnected);
TBEliminarUserControlCommand = new RelayCommand(EliminarUserControl, () => habilitarEliminarUserControl); TBEliminarUserControlCommand = new RelayCommand(EliminarUserControl, () => habilitarEliminarUserControl);
TBDuplicarUserControlCommand = new RelayCommand(DuplicarUserControl, () => habilitarEliminarUserControl); TBDuplicarUserControlCommand = new RelayCommand(DuplicarUserControl, () => habilitarEliminarUserControl);
TBExtractTagsCommand = new RelayCommand(ExtraerTags);
TBEliminarTodosCommand = new RelayCommand(EliminarTodosCommand); TBEliminarTodosCommand = new RelayCommand(EliminarTodosCommand);
TBEliminarAutoCreatedCommand = new RelayCommand(EliminarAutoCreatedCommand); TBEliminarAutoCreatedCommand = new RelayCommand(EliminarAutoCreatedCommand);
TBEliminarClonedCommand = new RelayCommand(EliminarClonedCommand); TBEliminarClonedCommand = new RelayCommand(EliminarClonedCommand);
TBAssingPagesCommand = new RelayCommand(AssingPagesCommand); TBAssingPagesCommand = new RelayCommand(AssingPagesCommand);
TBMultiPageExtractTagsCommand = new RelayCommand(MultiPageExtractTagsCommand);
TBMultiPageAnalizeCommand = new RelayCommand(MultiPageAnalizeCommand); TBMultiPageAnalizeCommand = new RelayCommand(MultiPageAnalizeCommand);
TBAnalyzeMatrixCommand = new RelayCommand(AnalyzeMatrixCommand);
TBMultiPageMatrixCommand = new RelayCommand(MultiPageMatrixCommand); TBMultiPageMatrixCommand = new RelayCommand(MultiPageMatrixCommand);
stopwatch_Sim = new Stopwatch(); stopwatch_Sim = new Stopwatch();
@ -334,6 +348,8 @@ namespace CtrEditor
recentDirectories = new ObservableCollection<string>(EstadoPersistente.Instance.RecentDirectories); recentDirectories = new ObservableCollection<string>(EstadoPersistente.Instance.RecentDirectories);
OpenRecentDirectoryCommand = new RelayCommand<string>(OpenRecentDirectory); OpenRecentDirectoryCommand = new RelayCommand<string>(OpenRecentDirectory);
_stateManager = new StateManager(EstadoPersistente.Instance.directorio, this);
} }
private void OsListFilter_PropertyChanged(object? sender, PropertyChangedEventArgs e) private void OsListFilter_PropertyChanged(object? sender, PropertyChangedEventArgs e)
@ -544,51 +560,6 @@ namespace CtrEditor
Application.Current.Dispatcher.Invoke(() => { }, DispatcherPriority.ApplicationIdle); 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<string>
{
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() private async void MultiPageAnalizeCommand()
{ {
var ImagenesSeleccionadas = new ObservableCollection<string> var ImagenesSeleccionadas = new ObservableCollection<string>
@ -641,148 +612,6 @@ namespace CtrEditor
} }
/// <summary>
/// Extrae y formatea las etiquetas de los objetos simulables y las guarda en un archivo Excel.
/// </summary>
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<int> columnasOcupadas = new List<int>();
int actualMaxCol = 0;
int col = 0;
// Filtrar los objetos de tipo osExtraccionTag y crear una nueva lista
var osBuscarCoincidencias_List = ObjetosSimulables
.OfType<osBuscarCoincidencias>()
.Where(tag => tag.Show_On_This_Page)
.ToList();
var osExtraccionTagBaseGrouped_List = ObjetosSimulables
.OfType<osExtraccionTag>()
.Where(tag => tag.Show_On_This_Page && !tag.Cloned && tag.Id_Search_Templates != null && tag.Id_Search_Templates != "")
.ToList();
var osExtraccionTagBaseFix_List = ObjetosSimulables
.OfType<osExtraccionTag>()
.Where(tag => tag.Show_On_This_Page && !tag.Cloned && (tag.Id_Search_Templates == null || tag.Id_Search_Templates == ""))
.ToList();
var osExtraccionTagCloned_List = ObjetosSimulables
.OfType<osExtraccionTag>()
.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() private void InitializeTipoSimulableList()
{ {
var baseType = typeof(osBase); var baseType = typeof(osBase);
@ -1142,15 +971,6 @@ namespace CtrEditor
Application.Current.Shutdown(); 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() private async void MultiPageMatrixCommand()
{ {
if (HasUnsavedChanges) if (HasUnsavedChanges)

View File

@ -27,13 +27,21 @@
<!-- Style for Connect/Disconnect Button --> <!-- Style for Connect/Disconnect Button -->
<Style x:Key="ConnectDisconnectButtonStyle" TargetType="Button"> <Style x:Key="ConnectDisconnectButtonStyle" TargetType="Button">
<Setter Property="Background" Value="Transparent" /> <Setter Property="Background" Value="Transparent" />
<Setter Property="Padding" Value="5" />
<Setter Property="MinWidth" Value="80" />
<Style.Triggers> <Style.Triggers>
<DataTrigger Binding="{Binding IsConnected}" Value="True"> <DataTrigger Binding="{Binding IsConnected}" Value="True">
<Setter Property="Background" Value="LightGreen" /> <Setter Property="Background" Value="#90EE90" />
</DataTrigger> </DataTrigger>
</Style.Triggers> </Style.Triggers>
</Style> </Style>
<!-- Converter for PLC Connect/Disconnect button text -->
<local:ConnectStateToBtnTextConverter x:Key="ConnectStateToBtnTextConverter" />
<!-- Converter for PLC Connect/Disconnect image -->
<local:ConnectStateToImageConverter x:Key="ConnectStateToImageConverter" />
</Window.Resources> </Window.Resources>
<Grid> <Grid>
@ -116,42 +124,29 @@
<TextBlock Text="Guardar" /> <TextBlock Text="Guardar" />
</StackPanel> </StackPanel>
</Button> </Button>
<Button Command="{Binding TBConnectPLCCommand}" ToolTip="Conectar PLC" <Button Command="{Binding TBTogglePLCConnectionCommand}"
ToolTip="{Binding IsConnected, Converter={StaticResource ConnectStateToBtnTextConverter}}"
Style="{StaticResource ConnectDisconnectButtonStyle}"> Style="{StaticResource ConnectDisconnectButtonStyle}">
<StackPanel> <StackPanel>
<Image Source="Icons/connect.png" Width="24" Height="24" /> <Image
<TextBlock Text="Conectar" /> Source="{Binding IsConnected, Converter={StaticResource ConnectStateToImageConverter}}"
</StackPanel> Width="24" Height="24" />
</Button> <TextBlock
<Button Command="{Binding TBDisconnectPLCCommand}" ToolTip="Desconectar PLC"> Text="{Binding IsConnected, Converter={StaticResource ConnectStateToBtnTextConverter}}" />
<StackPanel>
<Image Source="Icons/disconnect.png" Width="24" Height="24" />
<TextBlock Text="Desconectar" />
</StackPanel>
</Button>
<Button Command="{Binding TBExtractTagsCommand}" ToolTip="Extraer Tags">
<StackPanel>
<Image Source="Icons/extract.png" Width="24" Height="24" />
<TextBlock Text="Extraer Tags" />
</StackPanel> </StackPanel>
</Button> </Button>
<Button Command="{Binding TBMultiPageAnalizeCommand}" <Button Command="{Binding TBMultiPageAnalizeCommand}"
ToolTip="Analyze Tags in multiple pages."> ToolTip="Analyze Tags in multiple pages.">
<StackPanel> <StackPanel>
<Image Source="Icons/analyze.png" Width="24" Height="24" /> <Image Source="Icons/match.png" Width="24" Height="24" />
<TextBlock Text="Multi Page Analyze" /> <TextBlock Text="Multi Page Analyze" />
</StackPanel> </StackPanel>
</Button> </Button>
<Button Command="{Binding TBAnalyzeMatrixCommand}" ToolTip="Analyze Export Matrix">
<StackPanel>
<Image Source="Icons/analyze.png" Width="24" Height="24" />
<TextBlock Text="Analyze Matrix" />
</StackPanel>
</Button>
<Button Command="{Binding TBMultiPageMatrixCommand}" ToolTip="Analyze Matrix (Multiple Pages)"> <Button Command="{Binding TBMultiPageMatrixCommand}" ToolTip="Analyze Matrix (Multiple Pages)">
<StackPanel> <StackPanel>
<Image Source="Icons/analyze.png" Width="24" Height="24" /> <Image Source="Icons/ocr.png" Width="24" Height="24" />
<TextBlock Text="Multi Page Matrix" /> <TextBlock Text="Exportar Tags a Excel" />
</StackPanel> </StackPanel>
</Button> </Button>

View File

@ -288,6 +288,15 @@ namespace CtrEditor
new Tuple<Point, string>(new Point(rectBox.Right, rectBox.Top + rectBox.Height / 2), "CenterRight") new Tuple<Point, string>(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) foreach (var position in positions)
{ {
Rectangle rect = new Rectangle Rectangle rect = new Rectangle
@ -337,8 +346,8 @@ namespace CtrEditor
break; break;
} }
Canvas.SetLeft(rect, position.Item1.X - rectSize / 2); // Replace direct Canvas.Set calls with the validation method
Canvas.SetTop(rect, position.Item1.Y - rectSize / 2); SetCanvasPosition(rect, position.Item1.X - rectSize / 2, position.Item1.Y - rectSize / 2);
rect.MouseLeftButtonDown += UserControl_MouseLeftButtonDown; rect.MouseLeftButtonDown += UserControl_MouseLeftButtonDown;
rect.MouseMove += UserControl_MouseMove; rect.MouseMove += UserControl_MouseMove;

View File

@ -123,8 +123,6 @@ namespace CtrEditor.ObjetosSim
public override void UpdatePLC(PLCViewModel plc, int TotalMilliseconds) public override void UpdatePLC(PLCViewModel plc, int TotalMilliseconds)
{ {
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
elapsedTimeAccumulator += TotalMilliseconds; elapsedTimeAccumulator += TotalMilliseconds;
float randomFactor = (float)(new Random().NextDouble() * 0.1); // 10% random factor float randomFactor = (float)(new Random().NextDouble() * 0.1); // 10% random factor
@ -135,12 +133,9 @@ namespace CtrEditor.ObjetosSim
motState.UpdatePLC(plc, this, TotalMilliseconds); motState.UpdatePLC(plc, this, TotalMilliseconds);
elapsedTimeAccumulator = 0; elapsedTimeAccumulator = 0;
} }
Velocidad = (Proporcional_Speed / 100) * (motState.STATUS_VFD_ACT_Speed_Hz / 10); Velocidad = (Proporcional_Speed / 100) * (motState.STATUS_VFD_ACT_Speed_Hz / 10);
Sentido_contrario = motState.OUT_Reversal; Sentido_contrario = motState.OUT_Reversal;
stopwatch.Stop();
Debug.WriteLine($" osVMmotorSim : {Nombre} : {stopwatch.Elapsed.TotalMilliseconds} ms");
} }
public override void UpdateControl(int TotalMilliseconds) public override void UpdateControl(int TotalMilliseconds)

View File

@ -27,9 +27,40 @@ using Emgu.CV.Structure;
namespace CtrEditor.ObjetosSim.Extraccion_Datos namespace CtrEditor.ObjetosSim.Extraccion_Datos
{ {
/// <summary> /// <summary>
/// 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.
/// </summary> /// </summary>
/// <remarks>
/// 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
/// </remarks>
public partial class osBuscarCoincidencias : osBase, IosBase public partial class osBuscarCoincidencias : osBase, IosBase
{ {
@ -362,9 +393,6 @@ namespace CtrEditor.ObjetosSim.Extraccion_Datos
} }
} }
Row++; Row++;
if (newObj != null)
newObj.New_Row = true;
} }
} }

View File

@ -36,7 +36,18 @@ namespace CtrEditor.ObjetosSim.Extraccion_Datos
bool extraer; bool extraer;
[ObservableProperty] [ObservableProperty]
bool new_Row; [property: Category("Tag Extraction:")]
bool eliminar_enters;
[ObservableProperty]
[property: Category("Tag Extraction:")]
[property: ItemsSource(typeof(IdiomasItemsSource<Idiomas>))]
string idioma_Extraccion;
[ObservableProperty]
[property: Category("Tag Extraction:")]
[property: ItemsSource(typeof(TagPatternItemsSource<TagPattern>))]
string pattern_Type;
public override void TopChanged(float value) public override void TopChanged(float value)
{ {
@ -147,11 +158,39 @@ namespace CtrEditor.ObjetosSim.Extraccion_Datos
Alto = 1; Alto = 1;
Angulo = 0; Angulo = 0;
Opacity_oculto = 0.1f; Opacity_oculto = 0.1f;
Idioma_Extraccion = Idiomas.DEFAULT_LANGUAGE;
Pattern_Type = TagPattern.DEFAULT_PATTERN;
} }
public void CaptureImageAreaAndDoOCR() 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) public int ExportToExcel(IXLWorksheet worksheet, int row, int colBase)

View File

@ -312,7 +312,7 @@ namespace CtrEditor.ObjetosSim
if (e.PropertyName == nameof(osFramePlate.Left)) if (e.PropertyName == nameof(osFramePlate.Left))
{ {
Left += ((osFramePlate)sender).offsetX; Left += ((osFramePlate)sender).offsetX;
OnMoveResizeRotate(); OnMoveResizeRotate();
} }
if (e.PropertyName == nameof(osFramePlate.Zindex_FramePlate)) if (e.PropertyName == nameof(osFramePlate.Zindex_FramePlate))

View File

@ -12,7 +12,7 @@ namespace CtrEditor.PopUps
/// Interaction logic for AssignImagesWindow.xaml /// Interaction logic for AssignImagesWindow.xaml
/// </summary> /// </summary>
public partial class AssignImagesWindow : Window public partial class AssignImagesWindow : Window
{ {
public AssignImagesWindow() public AssignImagesWindow()
{ {
InitializeComponent(); InitializeComponent();

View File

@ -1,6 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Windows; using System.Windows;
using CtrEditor.FuncionesBase;
namespace CtrEditor.PopUps namespace CtrEditor.PopUps
{ {
@ -12,15 +13,7 @@ namespace CtrEditor.PopUps
public string SelectedSourceLanguage { get; private set; } public string SelectedSourceLanguage { get; private set; }
public string SelectedTargetLanguage { get; private set; } public string SelectedTargetLanguage { get; private set; }
private static readonly List<string> SupportedLanguages = new List<string> private static readonly List<string> SupportedLanguages = Idiomas.GetLanguageList();
{
"English",
"Spanish",
"Italian",
"French",
"German",
"Portuguese"
};
private string _sourceColumnLabel = "Idioma 1"; private string _sourceColumnLabel = "Idioma 1";
private string _targetColumnLabel = "Idioma 2"; private string _targetColumnLabel = "Idioma 2";

View File

@ -1,7 +1,7 @@
<Window x:Class="CtrEditor.PopUps.MatrixPreviewWindow" <Window x:Class="CtrEditor.PopUps.MatrixPreviewWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Export Matrix Preview" Height="600" Width="800"> Title="Exportar Tags Extraidos" Height="600" Width="800">
<Grid> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="*"/> <RowDefinition Height="*"/>
@ -70,23 +70,23 @@
HorizontalAlignment="Right" HorizontalAlignment="Right"
Margin="5" Margin="5"
Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"> Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}">
<Button Content="AI Correction" <Button Content="Correccion de Descripcion"
Command="{Binding AICorrectMatrixCommand}" Command="{Binding AICorrectMatrixCommand}"
Margin="5" Margin="5"
Padding="10,5"/> Padding="10,5"/>
<Button Content="Export to Excel" <Button Content="Exportar a Excel"
Command="{Binding ExportToExcelCommand}" Command="{Binding ExportToExcelCommand}"
Margin="5" Margin="5"
Padding="10,5"/> Padding="10,5"/>
<Button Content="Regenerate Matrix" <Button Content="Regenerar Matriz"
Command="{Binding RegenerateMatrixCommand}" Command="{Binding RegenerateMatrixCommand}"
Margin="5" Margin="5"
Padding="10,5"/> Padding="10,5"/>
<Button Content="Apply Changes" <Button Content="Salvar Cambios"
Command="{Binding ApplyChangesCommand}" Command="{Binding ApplyChangesCommand}"
Margin="5" Margin="5"
Padding="10,5"/> Padding="10,5"/>
<Button Content="Close" <Button Content="Cerrar"
Command="{Binding CloseCommand}" Command="{Binding CloseCommand}"
Margin="5" Margin="5"
Padding="10,5"/> Padding="10,5"/>

View File

@ -41,8 +41,8 @@ namespace CtrEditor.Services
public async Task<List<(string Source, string Target)>> ProcessTextBatch( public async Task<List<(string Source, string Target)>> ProcessTextBatch(
List<(string Source, string Target)> textPairs, List<(string Source, string Target)> textPairs,
string sourceLanguage = "Unknown", string sourceLanguage = "English",
string targetLanguage = "Unknown") string targetLanguage = "English")
{ {
try try
{ {

View File

@ -213,31 +213,60 @@ namespace CtrEditor.PopUps
UpdateMatrixPreview(); UpdateMatrixPreview();
} }
/// <summary>
/// Analyzes the current page and collects all extraction tags data for matrix preview.
/// </summary>
/// <remarks>
/// The method processes three types of extraction tags:
/// 1. Fixed Tags: Tags without Search Templates association
/// 2. Grouped Tags: Tags linked to Search Templates but not cloned
/// 3. Cloned Tags: Tags created by Search Templates pattern matching
///
/// Workflow:
/// 1. Filters objects by visibility on current page
/// 2. Groups tags by their type (fixed, grouped, cloned)
/// 3. Performs OCR extraction on each tag
/// 4. Creates MatrixItems with extracted data
///
/// Data Organization:
/// - Fixed tags maintain their original position and values
/// - Grouped tags are associated with their Search Templates
/// - Cloned tags inherit Copy_Number from their creation order in pattern matching
///
/// Related Classes:
/// - osExtraccionTag: Contains OCR extraction logic and tag properties
/// - osBuscarCoincidencias: Handles pattern matching and tag cloning
/// </remarks>
/// <returns>A list of MatrixItems containing all processed tag data for the current page</returns>
private List<MatrixItem> AnalyzePage() private List<MatrixItem> AnalyzePage()
{ {
var items = new List<MatrixItem>(); var items = new List<MatrixItem>();
// Filter visible objects by type
var osBuscarCoincidencias_List = _mainViewModel.ObjetosSimulables var osBuscarCoincidencias_List = _mainViewModel.ObjetosSimulables
.OfType<osBuscarCoincidencias>() .OfType<osBuscarCoincidencias>()
.Where(tag => tag.Show_On_This_Page) .Where(tag => tag.Show_On_This_Page)
.ToList(); .ToList();
// Get base tags that are linked to Search Templates
var osExtraccionTagBaseGrouped_List = _mainViewModel.ObjetosSimulables var osExtraccionTagBaseGrouped_List = _mainViewModel.ObjetosSimulables
.OfType<osExtraccionTag>() .OfType<osExtraccionTag>()
.Where(tag => tag.Show_On_This_Page && !tag.Cloned && tag.Id_Search_Templates != null && tag.Id_Search_Templates != "") .Where(tag => tag.Show_On_This_Page && !tag.Cloned && tag.Id_Search_Templates != null && tag.Id_Search_Templates != "")
.ToList(); .ToList();
// Get fixed tags (not linked to Search Templates)
var osExtraccionTagBaseFix_List = _mainViewModel.ObjetosSimulables var osExtraccionTagBaseFix_List = _mainViewModel.ObjetosSimulables
.OfType<osExtraccionTag>() .OfType<osExtraccionTag>()
.Where(tag => tag.Show_On_This_Page && !tag.Cloned && (tag.Id_Search_Templates == null || tag.Id_Search_Templates == "")) .Where(tag => tag.Show_On_This_Page && !tag.Cloned && (tag.Id_Search_Templates == null || tag.Id_Search_Templates == ""))
.ToList(); .ToList();
// Get cloned tags (created by Search Templates)
var osExtraccionTagCloned_List = _mainViewModel.ObjetosSimulables var osExtraccionTagCloned_List = _mainViewModel.ObjetosSimulables
.OfType<osExtraccionTag>() .OfType<osExtraccionTag>()
.Where(tag => tag.Show_On_This_Page && tag.Cloned) .Where(tag => tag.Show_On_This_Page && tag.Cloned)
.ToList(); .ToList();
// Add fixed tags // Process fixed tags
foreach (var tag in osExtraccionTagBaseFix_List) foreach (var tag in osExtraccionTagBaseFix_List)
{ {
tag.CaptureImageAreaAndDoOCR(); tag.CaptureImageAreaAndDoOCR();
@ -253,7 +282,7 @@ namespace CtrEditor.PopUps
}); });
} }
// Add grouped tags // Process grouped tags
foreach (var tag in osExtraccionTagBaseGrouped_List) foreach (var tag in osExtraccionTagBaseGrouped_List)
{ {
tag.CaptureImageAreaAndDoOCR(); tag.CaptureImageAreaAndDoOCR();
@ -269,7 +298,7 @@ namespace CtrEditor.PopUps
}); });
} }
// Add cloned tags // Process cloned tags
foreach (var tag in osExtraccionTagCloned_List) foreach (var tag in osExtraccionTagCloned_List)
{ {
tag.CaptureImageAreaAndDoOCR(); tag.CaptureImageAreaAndDoOCR();
@ -282,7 +311,7 @@ namespace CtrEditor.PopUps
Type = "Cloned", Type = "Cloned",
IsCloned = true, IsCloned = true,
Id = tag.Id, Id = tag.Id,
Copy_Number = tag.Copy_Number // Add this line Copy_Number = tag.Copy_Number
}); });
} }

View File

@ -30,6 +30,33 @@ namespace CtrEditor
} }
public class ConnectStateToBtnTextConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (bool)value ? "Desconectar PLC" : "Conectar PLC";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class ConnectStateToImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (bool)value ? "Icons/disconnect.png" : "Icons/connect.png";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class ListItemPropertyDescriptor<T> : PropertyDescriptor public class ListItemPropertyDescriptor<T> : PropertyDescriptor
{ {
private readonly IList<T> owner; private readonly IList<T> owner;