Agregada funcion de analizar la matriz de exportacion de tags. Creada un submenu para cargar los ultimos directorios ustilizados. Cambiado intercambio de datos para los Motores simulados a DINT
This commit is contained in:
parent
dc164e96ef
commit
3a2e87cd75
|
@ -79,6 +79,7 @@ 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; }
|
||||||
|
|
||||||
|
|
||||||
// Evento que se dispara cuando se selecciona una nueva imagen
|
// Evento que se dispara cuando se selecciona una nueva imagen
|
||||||
|
@ -108,6 +109,11 @@ namespace CtrEditor
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private bool hasUnsavedChanges;
|
private bool hasUnsavedChanges;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private ObservableCollection<string> recentDirectories;
|
||||||
|
|
||||||
|
public ICommand OpenRecentDirectoryCommand { get; private set; }
|
||||||
|
|
||||||
partial void OnIsSimulationRunningChanged(bool value)
|
partial void OnIsSimulationRunningChanged(bool value)
|
||||||
{
|
{
|
||||||
CommandManager.InvalidateRequerySuggested(); // Notificar que el estado de los comandos ha cambiado
|
CommandManager.InvalidateRequerySuggested(); // Notificar que el estado de los comandos ha cambiado
|
||||||
|
@ -148,6 +154,8 @@ namespace CtrEditor
|
||||||
|
|
||||||
OnPropertyChanged(nameof(directorioTrabajo)); // Notificar el cambio de propiedad
|
OnPropertyChanged(nameof(directorioTrabajo)); // Notificar el cambio de propiedad
|
||||||
OnPropertyChanged(nameof(ListaImagenes)); // Notificar que la lista de imágenes ha cambiado
|
OnPropertyChanged(nameof(ListaImagenes)); // Notificar que la lista de imágenes ha cambiado
|
||||||
|
|
||||||
|
AddToRecentDirectories(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -305,6 +313,7 @@ namespace CtrEditor
|
||||||
TBAssingPagesCommand = new RelayCommand(AssingPagesCommand);
|
TBAssingPagesCommand = new RelayCommand(AssingPagesCommand);
|
||||||
TBMultiPageExtractTagsCommand = new RelayCommand(MultiPageExtractTagsCommand);
|
TBMultiPageExtractTagsCommand = new RelayCommand(MultiPageExtractTagsCommand);
|
||||||
TBMultiPageAnalizeCommand = new RelayCommand(MultiPageAnalizeCommand);
|
TBMultiPageAnalizeCommand = new RelayCommand(MultiPageAnalizeCommand);
|
||||||
|
TBAnalyzeMatrixCommand = new RelayCommand(AnalyzeMatrixCommand);
|
||||||
|
|
||||||
stopwatch_Sim = new Stopwatch();
|
stopwatch_Sim = new Stopwatch();
|
||||||
stopwatch_Sim.Start();
|
stopwatch_Sim.Start();
|
||||||
|
@ -314,6 +323,9 @@ namespace CtrEditor
|
||||||
if (e.Action != NotifyCollectionChangedAction.Move)
|
if (e.Action != NotifyCollectionChangedAction.Move)
|
||||||
HasUnsavedChanges = true;
|
HasUnsavedChanges = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
recentDirectories = new ObservableCollection<string>(EstadoPersistente.Instance.RecentDirectories);
|
||||||
|
OpenRecentDirectoryCommand = new RelayCommand<string>(OpenRecentDirectory);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OsListFilter_PropertyChanged(object? sender, PropertyChangedEventArgs e)
|
private void OsListFilter_PropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||||
|
@ -873,6 +885,43 @@ namespace CtrEditor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OpenRecentDirectory(string path)
|
||||||
|
{
|
||||||
|
if (Directory.Exists(path))
|
||||||
|
{
|
||||||
|
directorioTrabajo = path;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MessageBox.Show($"Directory not found: {path}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
RecentDirectories.Remove(path);
|
||||||
|
UpdateRecentDirectories();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateRecentDirectories()
|
||||||
|
{
|
||||||
|
EstadoPersistente.Instance.RecentDirectories = RecentDirectories.ToList();
|
||||||
|
EstadoPersistente.Instance.GuardarEstado();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddToRecentDirectories(string path)
|
||||||
|
{
|
||||||
|
// Remove the path if it already exists
|
||||||
|
RecentDirectories.Remove(path);
|
||||||
|
|
||||||
|
// Add the new path at the beginning
|
||||||
|
RecentDirectories.Insert(0, path);
|
||||||
|
|
||||||
|
// Keep only the last 10 entries
|
||||||
|
while (RecentDirectories.Count > 10)
|
||||||
|
{
|
||||||
|
RecentDirectories.RemoveAt(RecentDirectories.Count - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateRecentDirectories();
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Lista de osBase
|
// Lista de osBase
|
||||||
//
|
//
|
||||||
|
@ -1051,6 +1100,15 @@ 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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
public class SimulationData
|
public class SimulationData
|
||||||
{
|
{
|
||||||
|
|
|
@ -41,6 +41,14 @@
|
||||||
<Menu VerticalAlignment="Top" HorizontalAlignment="Stretch">
|
<Menu VerticalAlignment="Top" HorizontalAlignment="Stretch">
|
||||||
<MenuItem Header="Projecto">
|
<MenuItem Header="Projecto">
|
||||||
<MenuItem Header="Abrir Directorio de trabajo" Command="{Binding OpenWorkDirectoryCommand}" />
|
<MenuItem Header="Abrir Directorio de trabajo" Command="{Binding OpenWorkDirectoryCommand}" />
|
||||||
|
<MenuItem Header="Ultimos Directorios Utilizados" ItemsSource="{Binding RecentDirectories}">
|
||||||
|
<MenuItem.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<MenuItem Header="{Binding}" Command="{Binding DataContext.OpenRecentDirectoryCommand, RelativeSource={RelativeSource AncestorType=MenuItem, AncestorLevel=2}}"
|
||||||
|
CommandParameter="{Binding}"/>
|
||||||
|
</DataTemplate>
|
||||||
|
</MenuItem.ItemTemplate>
|
||||||
|
</MenuItem>
|
||||||
<MenuItem Header="Iniciar Simulacion" Command="{Binding StartSimulationCommand}" />
|
<MenuItem Header="Iniciar Simulacion" Command="{Binding StartSimulationCommand}" />
|
||||||
<MenuItem Header="Detenet Simulacion" Command="{Binding StopSimulationCommand}" />
|
<MenuItem Header="Detenet Simulacion" Command="{Binding StopSimulationCommand}" />
|
||||||
<MenuItem Header="Debug Window" Command="{Binding DebugWindowCommand}" />
|
<MenuItem Header="Debug Window" Command="{Binding DebugWindowCommand}" />
|
||||||
|
@ -132,7 +140,7 @@
|
||||||
<Image Source="Icons/extract.png" Width="24" Height="24" />
|
<Image Source="Icons/extract.png" Width="24" Height="24" />
|
||||||
<TextBlock Text="Multi Page Extract" />
|
<TextBlock Text="Multi Page Extract" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Button>a
|
</Button>
|
||||||
<Button Command="{Binding TBMultiPageAnalizeCommand}"
|
<Button Command="{Binding TBMultiPageAnalizeCommand}"
|
||||||
ToolTip="Analyze Tags in multiple pages.">
|
ToolTip="Analyze Tags in multiple pages.">
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
|
@ -140,6 +148,12 @@
|
||||||
<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>
|
||||||
|
|
||||||
</ToolBar>
|
</ToolBar>
|
||||||
</ToolBarTray>
|
</ToolBarTray>
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
|
||||||
using CtrEditor.FuncionesBase;
|
using CtrEditor.FuncionesBase;
|
||||||
using LibS7Adv;
|
using LibS7Adv;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
@ -208,17 +207,21 @@ namespace CtrEditor.ObjetosSim
|
||||||
if (DB_Motor == 0)
|
if (DB_Motor == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// Read ControlWord in one operation
|
||||||
|
int controlWord = plc.LeerTagDInt($"\"DB MotorSimulate\".Motors[{DB_Motor}].ControlWord") ?? 0;
|
||||||
|
var control = VMMotorBitPacker.UnpackControlWord(controlWord);
|
||||||
|
|
||||||
OUT_Run = plc.LeerTagBool($"\"DB MotorSimulate\".Motors[{DB_Motor}].OUT.Run");
|
// Update local state from ControlWord
|
||||||
OUT_Reversal = plc.LeerTagBool($"\"DB MotorSimulate\".Motors[{DB_Motor}].OUT.\"Reversal Direction\"");
|
OUT_Run = control.run;
|
||||||
OUT_OUT_VFD_REQ_Speed_Hz = (float)plc.LeerTagInt16($"\"DB MotorSimulate\".Motors[{DB_Motor}].OUT.OUT_VFD_REQ_Speed_Hz");
|
OUT_Stop = control.stop;
|
||||||
|
OUT_Reversal = control.reversal;
|
||||||
|
OUT_OUT_VFD_REQ_Speed_Hz = control.reqSpeedHz;
|
||||||
|
|
||||||
|
// Update motor state based on enable status
|
||||||
if (Data.Encendido)
|
if (Data.Encendido)
|
||||||
{
|
{
|
||||||
_STATUS_VFD_Ready = true;
|
_STATUS_VFD_Ready = true;
|
||||||
Motor_Running = true;
|
Motor_Running = OUT_Run && !OUT_Stop;
|
||||||
STATUS_VFD_Trip = false;
|
STATUS_VFD_Trip = false;
|
||||||
STATUS_VFD_Warning = false;
|
STATUS_VFD_Warning = false;
|
||||||
STATUS_VFD_Coasting = false;
|
STATUS_VFD_Coasting = false;
|
||||||
|
@ -235,14 +238,18 @@ namespace CtrEditor.ObjetosSim
|
||||||
if (Data.VFD_Trip_NC)
|
if (Data.VFD_Trip_NC)
|
||||||
STATUS_VFD_Trip = !STATUS_VFD_Trip;
|
STATUS_VFD_Trip = !STATUS_VFD_Trip;
|
||||||
|
|
||||||
plc.EscribirTagBool($"\"DB MotorSimulate\".Motors[{DB_Motor}].STATUS_VFD_Ready", _STATUS_VFD_Ready);
|
// Pack all status bits and speed into StatusWord
|
||||||
plc.EscribirTagBool($"\"DB MotorSimulate\".Motors[{DB_Motor}].Motor_Running", Motor_Running);
|
int statusWord = VMMotorBitPacker.PackStatusWord(
|
||||||
plc.EscribirTagBool($"\"DB MotorSimulate\".Motors[{DB_Motor}].STATUS_VFD_Trip", STATUS_VFD_Trip);
|
_STATUS_VFD_Ready,
|
||||||
plc.EscribirTagBool($"\"DB MotorSimulate\".Motors[{DB_Motor}].STATUS_VFD_Warning", STATUS_VFD_Warning);
|
Motor_Running,
|
||||||
plc.EscribirTagBool($"\"DB MotorSimulate\".Motors[{DB_Motor}].STATUS_VFD_Coasting", STATUS_VFD_Coasting);
|
STATUS_VFD_Trip,
|
||||||
|
STATUS_VFD_Warning,
|
||||||
plc.EscribirTagInt16($"\"DB MotorSimulate\".Motors[{DB_Motor}].STATUS_VFD_ACT_Speed_Hz", (int)STATUS_VFD_ACT_Speed_Hz);
|
STATUS_VFD_Coasting,
|
||||||
|
(short)STATUS_VFD_ACT_Speed_Hz
|
||||||
|
);
|
||||||
|
|
||||||
|
// Write StatusWord in one operation
|
||||||
|
plc.EscribirTagDInt($"\"DB MotorSimulate\".Motors[{DB_Motor}].StatusWord", statusWord);
|
||||||
}
|
}
|
||||||
|
|
||||||
private float CalcSpeedRamp(float max_Speed_for_Ramp, float TiempoRampa, float actual, float expected, int TotalMilliseconds)
|
private float CalcSpeedRamp(float max_Speed_for_Ramp, float TiempoRampa, float actual, float expected, int TotalMilliseconds)
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
namespace CtrEditor.ObjetosSim
|
||||||
|
{
|
||||||
|
public static class VMMotorBitPacker
|
||||||
|
{
|
||||||
|
// Bit positions for StatusWord (bits 16-31)
|
||||||
|
private const int STATUS_VFD_READY_BIT = 16;
|
||||||
|
private const int MOTOR_RUNNING_BIT = 17;
|
||||||
|
private const int STATUS_VFD_TRIP_BIT = 18;
|
||||||
|
private const int STATUS_VFD_WARNING_BIT = 19;
|
||||||
|
private const int STATUS_VFD_COASTING_BIT = 20;
|
||||||
|
// Bits 21-31 reserved for future use
|
||||||
|
// Bits 0-15 used for STATUS_VFD_ACT_Speed_Hz (16 bits)
|
||||||
|
|
||||||
|
// Bit positions for ControlWord (bits 16-31)
|
||||||
|
private const int OUT_RUN_BIT = 16;
|
||||||
|
private const int OUT_STOP_BIT = 17;
|
||||||
|
private const int OUT_RESET_ALARM_BIT = 18;
|
||||||
|
private const int OUT_REVERSAL_BIT = 19;
|
||||||
|
// Bits 20-31 reserved for future use
|
||||||
|
// Bits 0-15 used for OUT_VFD_REQ_Speed_Hz (16 bits)
|
||||||
|
|
||||||
|
public static int PackStatusWord(bool vfdReady, bool motorRunning, bool vfdTrip,
|
||||||
|
bool vfdWarning, bool vfdCoasting, short actSpeedHz)
|
||||||
|
{
|
||||||
|
int result = actSpeedHz & 0xFFFF; // Speed in lower 16 bits
|
||||||
|
result |= (vfdReady ? 1 : 0) << STATUS_VFD_READY_BIT;
|
||||||
|
result |= (motorRunning ? 1 : 0) << MOTOR_RUNNING_BIT;
|
||||||
|
result |= (vfdTrip ? 1 : 0) << STATUS_VFD_TRIP_BIT;
|
||||||
|
result |= (vfdWarning ? 1 : 0) << STATUS_VFD_WARNING_BIT;
|
||||||
|
result |= (vfdCoasting ? 1 : 0) << STATUS_VFD_COASTING_BIT;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int PackControlWord(bool run, bool stop, bool resetAlarm,
|
||||||
|
bool reversal, short reqSpeedHz)
|
||||||
|
{
|
||||||
|
int result = reqSpeedHz & 0xFFFF; // Speed in lower 16 bits
|
||||||
|
result |= (run ? 1 : 0) << OUT_RUN_BIT;
|
||||||
|
result |= (stop ? 1 : 0) << OUT_STOP_BIT;
|
||||||
|
result |= (resetAlarm ? 1 : 0) << OUT_RESET_ALARM_BIT;
|
||||||
|
result |= (reversal ? 1 : 0) << OUT_REVERSAL_BIT;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static (bool vfdReady, bool motorRunning, bool vfdTrip, bool vfdWarning,
|
||||||
|
bool vfdCoasting, short actSpeedHz) UnpackStatusWord(int statusWord)
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
((statusWord >> STATUS_VFD_READY_BIT) & 1) == 1,
|
||||||
|
((statusWord >> MOTOR_RUNNING_BIT) & 1) == 1,
|
||||||
|
((statusWord >> STATUS_VFD_TRIP_BIT) & 1) == 1,
|
||||||
|
((statusWord >> STATUS_VFD_WARNING_BIT) & 1) == 1,
|
||||||
|
((statusWord >> STATUS_VFD_COASTING_BIT) & 1) == 1,
|
||||||
|
(short)(statusWord & 0xFFFF)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static (bool run, bool stop, bool resetAlarm, bool reversal, short reqSpeedHz)
|
||||||
|
UnpackControlWord(int controlWord)
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
((controlWord >> OUT_RUN_BIT) & 1) == 1,
|
||||||
|
((controlWord >> OUT_STOP_BIT) & 1) == 1,
|
||||||
|
((controlWord >> OUT_RESET_ALARM_BIT) & 1) == 1,
|
||||||
|
((controlWord >> OUT_REVERSAL_BIT) & 1) == 1,
|
||||||
|
(short)(controlWord & 0xFFFF)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
<Window x:Class="CtrEditor.PopUps.MatrixPreviewWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Title="Export Matrix Preview" Height="600" Width="800">
|
||||||
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<!-- Matrix Preview -->
|
||||||
|
<DataGrid x:Name="MatrixPreview"
|
||||||
|
Grid.Row="0"
|
||||||
|
ItemsSource="{Binding MatrixRows}"
|
||||||
|
AutoGenerateColumns="False"
|
||||||
|
CanUserReorderColumns="True"
|
||||||
|
CanUserAddRows="False"
|
||||||
|
HeadersVisibility="Column"
|
||||||
|
Background="White"
|
||||||
|
GridLinesVisibility="All"
|
||||||
|
Margin="5"
|
||||||
|
ColumnReordered="DataGrid_ColumnReordered">
|
||||||
|
<DataGrid.Resources>
|
||||||
|
<Style TargetType="DataGridColumnHeader">
|
||||||
|
<Setter Property="Background" Value="LightGray"/>
|
||||||
|
<Setter Property="FontWeight" Value="Bold"/>
|
||||||
|
<Setter Property="Padding" Value="5"/>
|
||||||
|
</Style>
|
||||||
|
</DataGrid.Resources>
|
||||||
|
</DataGrid>
|
||||||
|
|
||||||
|
<!-- GridSplitter between tables -->
|
||||||
|
<GridSplitter Grid.Row="1"
|
||||||
|
Height="5"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
Background="LightGray"
|
||||||
|
ResizeDirection="Rows"/>
|
||||||
|
|
||||||
|
<!-- Column Details -->
|
||||||
|
<DataGrid Grid.Row="2"
|
||||||
|
ItemsSource="{Binding MatrixItems}"
|
||||||
|
AutoGenerateColumns="False"
|
||||||
|
CanUserAddRows="False"
|
||||||
|
Margin="5">
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTextColumn Header="Column Number"
|
||||||
|
Binding="{Binding ColumnNumber, UpdateSourceTrigger=PropertyChanged}"
|
||||||
|
Width="100"/>
|
||||||
|
<DataGridTextColumn Header="Column Name"
|
||||||
|
Binding="{Binding ColumnName, UpdateSourceTrigger=PropertyChanged}"
|
||||||
|
Width="150"/>
|
||||||
|
<DataGridTextColumn Header="Tag Name"
|
||||||
|
Binding="{Binding TagName}"
|
||||||
|
Width="150"
|
||||||
|
IsReadOnly="True"/>
|
||||||
|
<DataGridTextColumn Header="Type"
|
||||||
|
Binding="{Binding Type}"
|
||||||
|
Width="100"
|
||||||
|
IsReadOnly="True"/>
|
||||||
|
</DataGrid.Columns>
|
||||||
|
</DataGrid>
|
||||||
|
|
||||||
|
<!-- Bottom Panel with Buttons -->
|
||||||
|
<StackPanel Grid.Row="3"
|
||||||
|
Orientation="Horizontal"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
Margin="5"
|
||||||
|
Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}">
|
||||||
|
<Button Content="Regenerate Matrix"
|
||||||
|
Command="{Binding RegenerateMatrixCommand}"
|
||||||
|
Margin="5"
|
||||||
|
Padding="10,5"/>
|
||||||
|
<Button Content="Apply Changes"
|
||||||
|
Command="{Binding ApplyChangesCommand}"
|
||||||
|
Margin="5"
|
||||||
|
Padding="10,5"/>
|
||||||
|
<Button Content="Close"
|
||||||
|
Command="{Binding CloseCommand}"
|
||||||
|
Margin="5"
|
||||||
|
Padding="10,5"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
|
@ -0,0 +1,46 @@
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Threading;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace CtrEditor.PopUps
|
||||||
|
{
|
||||||
|
public partial class MatrixPreviewWindow : Window
|
||||||
|
{
|
||||||
|
public MatrixPreviewWindow()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DataGrid_ColumnReordered(object sender, DataGridColumnEventArgs e)
|
||||||
|
{
|
||||||
|
if (DataContext is MatrixPreviewViewModel viewModel)
|
||||||
|
{
|
||||||
|
// Obtener el nuevo orden basado en DisplayIndex
|
||||||
|
var orderedColumns = MatrixPreview.Columns
|
||||||
|
.OrderBy(c => c.DisplayIndex)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
// Actualizar los números de columna basado en DisplayIndex
|
||||||
|
for (int i = 0; i < orderedColumns.Count; i++)
|
||||||
|
{
|
||||||
|
var column = orderedColumns[i];
|
||||||
|
var headerParts = column.Header?.ToString().Split(':');
|
||||||
|
if (headerParts?.Length == 2)
|
||||||
|
{
|
||||||
|
var columnName = headerParts[1];
|
||||||
|
// Actualizar todos los items que tengan el mismo nombre de columna
|
||||||
|
var itemsToUpdate = viewModel.MatrixItems.Where(x => x.ColumnName == columnName);
|
||||||
|
foreach (var item in itemsToUpdate)
|
||||||
|
{
|
||||||
|
item.ColumnNumber = i + 1;
|
||||||
|
}
|
||||||
|
// Actualizar el header con el nuevo número
|
||||||
|
column.Header = $"{i + 1}:{columnName}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,363 @@
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
using CtrEditor.ObjetosSim;
|
||||||
|
using CtrEditor.ObjetosSim.Extraccion_Datos;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Data;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace CtrEditor.PopUps
|
||||||
|
{
|
||||||
|
public class MatrixItem : ObservableObject
|
||||||
|
{
|
||||||
|
public string TagName { get; set; }
|
||||||
|
private string columnName;
|
||||||
|
public string ColumnName
|
||||||
|
{
|
||||||
|
get => columnName;
|
||||||
|
set => SetProperty(ref columnName, value);
|
||||||
|
}
|
||||||
|
private int columnNumber;
|
||||||
|
public int ColumnNumber
|
||||||
|
{
|
||||||
|
get => columnNumber;
|
||||||
|
set => SetProperty(ref columnNumber, value);
|
||||||
|
}
|
||||||
|
public string Value { get; set; }
|
||||||
|
public string Type { get; set; }
|
||||||
|
public bool IsCloned { get; set; }
|
||||||
|
public osBase SourceObject { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public partial class MatrixPreviewViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
private Window _window;
|
||||||
|
private MainViewModel _mainViewModel;
|
||||||
|
private DataGrid _dataGrid;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private ObservableCollection<MatrixItem> matrixItems;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private ObservableCollection<Dictionary<string, string>> matrixRows;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private GridLength detailsHeight = new GridLength(150);
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private double detailsHeightValue = 150;
|
||||||
|
|
||||||
|
partial void OnDetailsHeightValueChanged(double value)
|
||||||
|
{
|
||||||
|
DetailsHeight = new GridLength(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Initialize(MainViewModel mainViewModel, Window window)
|
||||||
|
{
|
||||||
|
_mainViewModel = mainViewModel;
|
||||||
|
_window = window;
|
||||||
|
_dataGrid = (_window as MatrixPreviewWindow)?.MatrixPreview;
|
||||||
|
|
||||||
|
MatrixItems = new ObservableCollection<MatrixItem>();
|
||||||
|
MatrixRows = new ObservableCollection<Dictionary<string, string>>();
|
||||||
|
|
||||||
|
AnalyzeMatrix();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateMatrixPreview()
|
||||||
|
{
|
||||||
|
if (_dataGrid == null) return;
|
||||||
|
if (MatrixRows == null) MatrixRows = new ObservableCollection<Dictionary<string, string>>();
|
||||||
|
|
||||||
|
MatrixRows.Clear();
|
||||||
|
_dataGrid.Columns.Clear();
|
||||||
|
|
||||||
|
if (!MatrixItems.Any()) return;
|
||||||
|
|
||||||
|
// Ordenar items por número de columna
|
||||||
|
var orderedItems = MatrixItems
|
||||||
|
.GroupBy(x => x.ColumnNumber)
|
||||||
|
.OrderBy(g => g.Key)
|
||||||
|
.Select(g => g.First())
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
// Crear columnas
|
||||||
|
foreach (var item in orderedItems)
|
||||||
|
{
|
||||||
|
var column = new DataGridTextColumn
|
||||||
|
{
|
||||||
|
Header = $"{item.ColumnNumber}:{item.ColumnName}",
|
||||||
|
Binding = new Binding($"[{item.ColumnNumber}]"),
|
||||||
|
Width = DataGridLength.Auto
|
||||||
|
};
|
||||||
|
_dataGrid.Columns.Add(column);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calcular número de filas necesarias
|
||||||
|
int maxRows = 1;
|
||||||
|
var clonedItems = MatrixItems.Where(x => x.IsCloned).ToList();
|
||||||
|
if (clonedItems.Any())
|
||||||
|
{
|
||||||
|
maxRows = clonedItems.Max(x => x.SourceObject is osExtraccionTag tag ? tag.Copy_Number : 0) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Crear filas con valores
|
||||||
|
for (int row = 0; row < maxRows; row++)
|
||||||
|
{
|
||||||
|
var rowData = new Dictionary<string, string>();
|
||||||
|
|
||||||
|
// Agregar valores fijos (no clonados) en todas las filas
|
||||||
|
foreach (var item in MatrixItems.Where(x => !x.IsCloned))
|
||||||
|
{
|
||||||
|
if (item.ColumnNumber > 0)
|
||||||
|
{
|
||||||
|
rowData[item.ColumnNumber.ToString()] = item.Value ?? string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Agregar valores clonados solo en su fila correspondiente
|
||||||
|
foreach (var item in MatrixItems.Where(x => x.IsCloned))
|
||||||
|
{
|
||||||
|
if (item.ColumnNumber > 0 && item.SourceObject is osExtraccionTag tag && tag.Copy_Number == row)
|
||||||
|
{
|
||||||
|
rowData[item.ColumnNumber.ToString()] = item.Value ?? string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MatrixRows.Add(rowData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateColumnHeaders()
|
||||||
|
{
|
||||||
|
if (_dataGrid == null) return;
|
||||||
|
|
||||||
|
foreach (var col in _dataGrid.Columns.Cast<DataGridColumn>())
|
||||||
|
{
|
||||||
|
if (col is DataGridTextColumn textCol)
|
||||||
|
{
|
||||||
|
var item = MatrixItems.FirstOrDefault(x => x.ColumnName == (textCol.Header?.ToString() ?? "").Split(':').Last());
|
||||||
|
if (item != null)
|
||||||
|
{
|
||||||
|
textCol.Header = $"{item.ColumnNumber}:{item.ColumnName}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AnalyzeMatrix()
|
||||||
|
{
|
||||||
|
var osBuscarCoincidencias_List = _mainViewModel.ObjetosSimulables
|
||||||
|
.OfType<osBuscarCoincidencias>()
|
||||||
|
.Where(tag => tag.Show_On_This_Page)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var osExtraccionTagBaseGrouped_List = _mainViewModel.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 = _mainViewModel.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 = _mainViewModel.ObjetosSimulables
|
||||||
|
.OfType<osExtraccionTag>()
|
||||||
|
.Where(tag => tag.Show_On_This_Page && tag.Cloned)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
// Add fixed tags
|
||||||
|
foreach (var tag in osExtraccionTagBaseFix_List)
|
||||||
|
{
|
||||||
|
tag.CaptureImageAreaAndDoOCR();
|
||||||
|
MatrixItems.Add(new MatrixItem
|
||||||
|
{
|
||||||
|
TagName = tag.Nombre,
|
||||||
|
ColumnName = tag.Collumn_name,
|
||||||
|
ColumnNumber = tag.Collumn_number,
|
||||||
|
Value = tag.Tag_extract,
|
||||||
|
Type = "Fixed",
|
||||||
|
IsCloned = false,
|
||||||
|
SourceObject = tag
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add grouped tags
|
||||||
|
foreach (var tag in osExtraccionTagBaseGrouped_List)
|
||||||
|
{
|
||||||
|
tag.CaptureImageAreaAndDoOCR();
|
||||||
|
MatrixItems.Add(new MatrixItem
|
||||||
|
{
|
||||||
|
TagName = tag.Nombre,
|
||||||
|
ColumnName = tag.Collumn_name,
|
||||||
|
ColumnNumber = tag.Collumn_number,
|
||||||
|
Value = tag.Tag_extract,
|
||||||
|
Type = $"Grouped ({tag.Id_Search_Templates})",
|
||||||
|
IsCloned = false,
|
||||||
|
SourceObject = tag
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add cloned tags
|
||||||
|
foreach (var tag in osExtraccionTagCloned_List)
|
||||||
|
{
|
||||||
|
tag.CaptureImageAreaAndDoOCR();
|
||||||
|
MatrixItems.Add(new MatrixItem
|
||||||
|
{
|
||||||
|
TagName = tag.Nombre,
|
||||||
|
ColumnName = tag.Collumn_name,
|
||||||
|
ColumnNumber = tag.Collumn_number,
|
||||||
|
Value = tag.Tag_extract,
|
||||||
|
Type = "Cloned",
|
||||||
|
IsCloned = true,
|
||||||
|
SourceObject = tag
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ordenar los items por número de columna
|
||||||
|
var items = MatrixItems.OrderBy(x => x.ColumnNumber).ToList();
|
||||||
|
MatrixItems.Clear();
|
||||||
|
foreach (var item in items)
|
||||||
|
{
|
||||||
|
MatrixItems.Add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
ResolveColumnConflicts(); // Analizar y resolver conflictos al cargar
|
||||||
|
UpdateMatrixPreview();
|
||||||
|
}
|
||||||
|
|
||||||
|
partial void OnMatrixItemsChanged(ObservableCollection<MatrixItem> value)
|
||||||
|
{
|
||||||
|
if (value != null)
|
||||||
|
{
|
||||||
|
foreach (var item in value)
|
||||||
|
{
|
||||||
|
item.PropertyChanged += (s, e) =>
|
||||||
|
{
|
||||||
|
if (e.PropertyName == nameof(MatrixItem.ColumnNumber) ||
|
||||||
|
e.PropertyName == nameof(MatrixItem.ColumnName))
|
||||||
|
{
|
||||||
|
UpdateColumnHeaders();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UpdateMatrixPreview();
|
||||||
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private void ApplyChanges()
|
||||||
|
{
|
||||||
|
// Resolver solo los conflictos reales, mantener el orden existente
|
||||||
|
var conflicts = MatrixItems
|
||||||
|
.GroupBy(x => x.ColumnNumber)
|
||||||
|
.Where(g => g.Select(x => x.ColumnName).Distinct().Count() > 1)
|
||||||
|
.Any();
|
||||||
|
|
||||||
|
if (conflicts)
|
||||||
|
{
|
||||||
|
ResolveColumnConflicts();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validar números de columna
|
||||||
|
var columnNumbers = MatrixItems.Select(x => x.ColumnNumber).OrderBy(x => x).ToList();
|
||||||
|
for (int i = 0; i < columnNumbers.Count; i++)
|
||||||
|
{
|
||||||
|
if (columnNumbers[i] <= 0)
|
||||||
|
{
|
||||||
|
MessageBox.Show("Column numbers must be greater than 0", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aplicar cambios a los objetos
|
||||||
|
foreach (var item in MatrixItems)
|
||||||
|
{
|
||||||
|
if (item.SourceObject is osExtraccionTag tag)
|
||||||
|
{
|
||||||
|
tag.Collumn_name = item.ColumnName;
|
||||||
|
tag.Collumn_number = item.ColumnNumber;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_mainViewModel.HasUnsavedChanges = true;
|
||||||
|
_window.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private void Close()
|
||||||
|
{
|
||||||
|
_window.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private void RegenerateMatrix()
|
||||||
|
{
|
||||||
|
ResolveColumnConflicts();
|
||||||
|
UpdateMatrixPreview();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ResolveColumnConflicts()
|
||||||
|
{
|
||||||
|
// Paso 1: Mantener el orden actual y resolver solo los conflictos reales
|
||||||
|
var columnGroups = MatrixItems
|
||||||
|
.GroupBy(x => x.ColumnNumber)
|
||||||
|
.Where(g => g.Select(x => x.ColumnName).Distinct().Count() > 1)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
foreach (var group in columnGroups)
|
||||||
|
{
|
||||||
|
var nameGroups = group
|
||||||
|
.GroupBy(x => x.ColumnName)
|
||||||
|
.OrderByDescending(g => g.Count())
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
// El grupo más grande mantiene su número
|
||||||
|
var largestGroup = nameGroups.First();
|
||||||
|
|
||||||
|
// Solo reasignar números para grupos que tengan conflicto
|
||||||
|
for (int i = 1; i < nameGroups.Count; i++)
|
||||||
|
{
|
||||||
|
var newColumnNumber = GetNextAvailableColumnNumber();
|
||||||
|
foreach (var item in nameGroups[i])
|
||||||
|
{
|
||||||
|
item.ColumnNumber = newColumnNumber;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Paso 2: Asegurarse de que los números son consecutivos sin alterar el orden relativo
|
||||||
|
var orderedItems = MatrixItems
|
||||||
|
.OrderBy(x => x.ColumnNumber)
|
||||||
|
.GroupBy(x => x.ColumnName)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
int currentNumber = 1;
|
||||||
|
foreach (var group in orderedItems)
|
||||||
|
{
|
||||||
|
foreach (var item in group)
|
||||||
|
{
|
||||||
|
item.ColumnNumber = currentNumber;
|
||||||
|
}
|
||||||
|
currentNumber++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetNextAvailableColumnNumber()
|
||||||
|
{
|
||||||
|
var usedNumbers = MatrixItems.Select(x => x.ColumnNumber).Distinct().OrderBy(x => x).ToList();
|
||||||
|
int nextNumber = 1;
|
||||||
|
|
||||||
|
foreach (var number in usedNumbers)
|
||||||
|
{
|
||||||
|
if (number > nextNumber)
|
||||||
|
return nextNumber;
|
||||||
|
nextNumber = number + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nextNumber;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,6 +26,8 @@ namespace CtrEditor
|
||||||
|
|
||||||
public ObjetosSimulablesFilterTypes osListFilter;
|
public ObjetosSimulablesFilterTypes osListFilter;
|
||||||
|
|
||||||
|
public List<string> RecentDirectories { get; set; } = new List<string>();
|
||||||
|
|
||||||
// Propiedad pública con get y set para controlar el acceso a _strDirectorioTrabajo
|
// Propiedad pública con get y set para controlar el acceso a _strDirectorioTrabajo
|
||||||
public string directorio
|
public string directorio
|
||||||
{
|
{
|
||||||
|
@ -55,6 +57,7 @@ namespace CtrEditor
|
||||||
private EstadoPersistente Inizializar()
|
private EstadoPersistente Inizializar()
|
||||||
{
|
{
|
||||||
_strDirectorioTrabajo = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
|
_strDirectorioTrabajo = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
|
||||||
|
RecentDirectories = new List<string>();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue