Compare commits
7 Commits
9b710fcb00
...
75c507be4e
Author | SHA1 | Date |
---|---|---|
|
75c507be4e | |
|
fefc0a700d | |
|
58781c13a3 | |
|
b48dbeb76e | |
|
ca70f66ff1 | |
|
354b4a8acf | |
|
909e438f5b |
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
// Use IntelliSense to find out which attributes exist for C# debugging
|
||||
// Use hover for the description of the existing attributes
|
||||
// For further information visit https://github.com/dotnet/vscode-csharp/blob/main/debugger-launchjson.md
|
||||
"name": ".NET Core Launch (console)",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
// If you have changed target frameworks, make sure to update the program path.
|
||||
"program": "${workspaceFolder}/bin/Debug/net8.0-windows8.0/CtrEditor.dll",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}",
|
||||
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
|
||||
"console": "internalConsole",
|
||||
"stopAtEntry": false
|
||||
},
|
||||
{
|
||||
"name": ".NET Core Attach",
|
||||
"type": "coreclr",
|
||||
"request": "attach"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "build",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"build",
|
||||
"${workspaceFolder}/CtrEditor.sln",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary;ForceNoAlign"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "publish",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"publish",
|
||||
"${workspaceFolder}/CtrEditor.sln",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary;ForceNoAlign"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "watch",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"watch",
|
||||
"run",
|
||||
"--project",
|
||||
"${workspaceFolder}/CtrEditor.sln"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace CtrEditor.Converters
|
||||
{
|
||||
public class ImageDisplayNameConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is string imageName)
|
||||
{
|
||||
// Buscar el MainViewModel en el Application.Current.MainWindow.DataContext
|
||||
if (Application.Current?.MainWindow?.DataContext is MainViewModel viewModel)
|
||||
{
|
||||
return viewModel.GetImageDisplayNameWithTags(imageName);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace CtrEditor.Converters
|
||||
{
|
||||
public class RegionalFloatConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is float floatValue)
|
||||
{
|
||||
// Usar la cultura actual para mostrar el número con el separador decimal correcto
|
||||
return floatValue.ToString("N4", CultureInfo.CurrentCulture);
|
||||
}
|
||||
return value?.ToString() ?? string.Empty;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is string stringValue)
|
||||
{
|
||||
// Intentar parsing con la cultura actual primero
|
||||
if (float.TryParse(stringValue, NumberStyles.Float, CultureInfo.CurrentCulture, out float result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
// Si falla, intentar con cultura invariante (punto como separador)
|
||||
if (float.TryParse(stringValue, NumberStyles.Float, CultureInfo.InvariantCulture, out result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
// Si ambos fallan, intentar reemplazar punto por coma o viceversa
|
||||
string adjustedString = stringValue.Replace(',', '.').Replace(".", CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator);
|
||||
if (float.TryParse(adjustedString, NumberStyles.Float, CultureInfo.CurrentCulture, out result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return 0.0f; // Valor por defecto si no se puede parsear
|
||||
}
|
||||
}
|
||||
}
|
|
@ -118,8 +118,7 @@ namespace CtrEditor
|
|||
_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
|
||||
// No crear un nuevo UnitConverter si no hay uno guardado, mantener el actual
|
||||
|
||||
// Restaurar objetos globales
|
||||
foreach (var obj in _globalState.SharedObjects)
|
||||
|
|
|
@ -5,12 +5,14 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace CtrEditor
|
||||
{
|
||||
public class DatosDeTrabajo
|
||||
{
|
||||
public Dictionary<string, string> Imagenes { get; private set; }
|
||||
private MainViewModel _mainViewModel;
|
||||
|
||||
public DatosDeTrabajo()
|
||||
{
|
||||
|
@ -18,6 +20,11 @@ namespace CtrEditor
|
|||
CargarImagenes(); // Inicializar la carga de imágenes basada en el directorio persistente
|
||||
}
|
||||
|
||||
public void SetMainViewModel(MainViewModel mainViewModel)
|
||||
{
|
||||
_mainViewModel = mainViewModel;
|
||||
}
|
||||
|
||||
public string ObtenerPathImagenConExtension(string key, string extension)
|
||||
{
|
||||
if (Imagenes.TryGetValue(key, out string imagePath))
|
||||
|
@ -51,7 +58,85 @@ namespace CtrEditor
|
|||
Imagenes[nombreArchivo] = archivo;
|
||||
}
|
||||
}
|
||||
|
||||
// Cargar datos de imágenes existentes desde archivos JSON
|
||||
if (_mainViewModel != null)
|
||||
{
|
||||
CargarDatosImagenesExistentes();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CargarDatosImagenesExistentes()
|
||||
{
|
||||
var jsonSerializerSettings = GetJsonSerializerSettings();
|
||||
|
||||
foreach (var imageName in Imagenes.Keys)
|
||||
{
|
||||
string jsonPath = ObtenerPathImagenConExtension(imageName, ".json");
|
||||
if (!string.IsNullOrEmpty(jsonPath) && File.Exists(jsonPath))
|
||||
{
|
||||
try
|
||||
{
|
||||
// Cargar datos completos del archivo JSON
|
||||
string jsonString = File.ReadAllText(jsonPath);
|
||||
var simulationData = Newtonsoft.Json.JsonConvert.DeserializeObject<SimulationData>(jsonString, jsonSerializerSettings);
|
||||
|
||||
// Cargar datos de imágenes si existen en el archivo
|
||||
if (simulationData?.ImageDataDictionary != null)
|
||||
{
|
||||
foreach (var imageDataEntry in simulationData.ImageDataDictionary)
|
||||
{
|
||||
// Solo cargar si no existe ya en el diccionario principal
|
||||
if (!_mainViewModel._imageDataDictionary.ContainsKey(imageDataEntry.Key))
|
||||
{
|
||||
_mainViewModel._imageDataDictionary[imageDataEntry.Key] = imageDataEntry.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compatibilidad con versiones anteriores (ImageCustomNames)
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
if (simulationData?.ImageCustomNames != null)
|
||||
{
|
||||
foreach (var customName in simulationData.ImageCustomNames)
|
||||
{
|
||||
var imageData = _mainViewModel.GetOrCreateImageData(customName.Key);
|
||||
if (string.IsNullOrEmpty(imageData.CustomName)) // Solo actualizar si no tiene nombre personalizado
|
||||
{
|
||||
imageData.CustomName = customName.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma warning restore CS0618
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Log del error pero no fallar la carga completa
|
||||
System.Diagnostics.Debug.WriteLine($"Error al cargar datos de imagen desde {jsonPath}: {ex.Message}");
|
||||
|
||||
// Como fallback, crear una instancia vacía
|
||||
_mainViewModel.GetOrCreateImageData(imageName);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Si no existe archivo JSON, crear instancia vacía
|
||||
_mainViewModel.GetOrCreateImageData(imageName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Newtonsoft.Json.JsonSerializerSettings GetJsonSerializerSettings()
|
||||
{
|
||||
return new Newtonsoft.Json.JsonSerializerSettings
|
||||
{
|
||||
Formatting = Newtonsoft.Json.Formatting.Indented,
|
||||
NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore,
|
||||
TypeNameHandling = Newtonsoft.Json.TypeNameHandling.Auto,
|
||||
ObjectCreationHandling = Newtonsoft.Json.ObjectCreationHandling.Replace,
|
||||
ConstructorHandling = Newtonsoft.Json.ConstructorHandling.AllowNonPublicDefaultConstructor
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
152
MainViewModel.cs
152
MainViewModel.cs
|
@ -81,6 +81,12 @@ namespace CtrEditor
|
|||
[ObservableProperty]
|
||||
public ObservableCollection<TipoSimulable> listaOsBase;
|
||||
|
||||
// Diccionario para almacenar datos expandidos de imágenes
|
||||
public Dictionary<string, Models.ImageData> _imageDataDictionary = new Dictionary<string, Models.ImageData>();
|
||||
|
||||
// Comando para renombrar imagen
|
||||
public ICommand RenameImageCommand { get; private set; }
|
||||
|
||||
public ICommand StartSimulationCommand { get; }
|
||||
public ICommand StopSimulationCommand { get; }
|
||||
public ICommand ItemDoubleClickCommand { get; private set; }
|
||||
|
@ -383,6 +389,7 @@ namespace CtrEditor
|
|||
TBMultiPageAnalizeCommand = new RelayCommand(MultiPageAnalizeCommand);
|
||||
TBMultiPageMatrixCommand = new RelayCommand(MultiPageMatrixCommand);
|
||||
TBLibraryManagerCommand = new RelayCommand(ShowLibraryManager);
|
||||
RenameImageCommand = new RelayCommand<string>(RenameImage);
|
||||
|
||||
stopwatch_Sim = new Stopwatch();
|
||||
stopwatch_Sim.Start();
|
||||
|
@ -398,6 +405,73 @@ namespace CtrEditor
|
|||
|
||||
_stateManager = new StateManager(EstadoPersistente.Instance.directorio, this);
|
||||
_stateSerializer = new StateSerializer(this, datosDeTrabajo, simulationManager);
|
||||
|
||||
// Conectar DatosDeTrabajo con este ViewModel para el escaneo de imágenes
|
||||
datosDeTrabajo.SetMainViewModel(this);
|
||||
}
|
||||
|
||||
// Métodos para manejo de datos de imágenes
|
||||
private void RenameImage(string imageName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(imageName)) return;
|
||||
|
||||
var imageData = GetOrCreateImageData(imageName);
|
||||
|
||||
var dialog = new PopUps.RenameImageWindow(imageName, imageData);
|
||||
dialog.Owner = MainWindow;
|
||||
|
||||
if (dialog.ShowDialog() == true)
|
||||
{
|
||||
// El dialog ya ha modificado directamente el imageData
|
||||
// Solo necesitamos verificar si debemos remover la entrada si está vacía
|
||||
if (string.IsNullOrEmpty(imageData.CustomName) &&
|
||||
imageData.Tags.Count == 0 &&
|
||||
string.IsNullOrEmpty(imageData.Etiquetas))
|
||||
{
|
||||
_imageDataDictionary.Remove(imageName);
|
||||
}
|
||||
|
||||
// Forzar actualización del UI usando CollectionViewSource
|
||||
var collectionView = System.Windows.Data.CollectionViewSource.GetDefaultView(ListaImagenes);
|
||||
collectionView?.Refresh();
|
||||
|
||||
HasUnsavedChanges = true;
|
||||
}
|
||||
}
|
||||
|
||||
public string GetImageDisplayName(string imageName)
|
||||
{
|
||||
if (_imageDataDictionary.TryGetValue(imageName, out var imageData))
|
||||
{
|
||||
return imageData.DisplayName;
|
||||
}
|
||||
return imageName;
|
||||
}
|
||||
|
||||
public string GetImageDisplayNameWithTags(string imageName)
|
||||
{
|
||||
if (_imageDataDictionary.TryGetValue(imageName, out var imageData))
|
||||
{
|
||||
var displayName = imageData.DisplayName;
|
||||
var tags = imageData.Etiquetas;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(tags))
|
||||
{
|
||||
return $"{displayName} {tags}";
|
||||
}
|
||||
return displayName;
|
||||
}
|
||||
return imageName;
|
||||
}
|
||||
|
||||
public Models.ImageData GetOrCreateImageData(string imageName)
|
||||
{
|
||||
if (!_imageDataDictionary.TryGetValue(imageName, out var imageData))
|
||||
{
|
||||
imageData = new Models.ImageData(imageName);
|
||||
_imageDataDictionary[imageName] = imageData;
|
||||
}
|
||||
return imageData;
|
||||
}
|
||||
|
||||
private void OnVisFilterChanged(osVisFilterViewModel filter) // Changed signature to accept viewmodel directly
|
||||
|
@ -1162,6 +1236,77 @@ namespace CtrEditor
|
|||
OnPropertyChanged(nameof(SelectedItemOsList));
|
||||
}
|
||||
|
||||
// Diccionario para manejar la ventana de configuración de escala
|
||||
private PopUps.ScaleConfigWindow? _scaleConfigWindow;
|
||||
|
||||
// Método para configurar la escala desde el menú contextual
|
||||
public void ConfigureScale()
|
||||
{
|
||||
// Verificar si ya existe una ventana de configuración de escala
|
||||
if (_scaleConfigWindow != null && _scaleConfigWindow.IsVisible)
|
||||
{
|
||||
_scaleConfigWindow.Activate();
|
||||
return;
|
||||
}
|
||||
|
||||
var currentScale = PixelToMeter.Instance.calc.Scale;
|
||||
_scaleConfigWindow = new PopUps.ScaleConfigWindow(currentScale, this);
|
||||
_scaleConfigWindow.Owner = MainWindow;
|
||||
|
||||
// Manejar el cierre de la ventana
|
||||
_scaleConfigWindow.Closed += (s, e) => _scaleConfigWindow = null;
|
||||
|
||||
// Mostrar como modeless (no modal)
|
||||
_scaleConfigWindow.Show();
|
||||
}
|
||||
|
||||
// Método público para aplicar la escala desde la ventana modeless
|
||||
public void ApplyScale(float newScale)
|
||||
{
|
||||
// Detener simulaciones antes de cambiar la escala
|
||||
StopSimulation();
|
||||
StopFluidSimulation();
|
||||
DisconnectPLC();
|
||||
|
||||
// Actualizar la escala en el UnitConverter
|
||||
PixelToMeter.Instance.calc.SetScale(newScale);
|
||||
|
||||
// Forzar redibujo completo del canvas y todos los objetos
|
||||
ForceCanvasRedraw();
|
||||
|
||||
// Marcar como cambios no guardados ya que esto afecta la configuración de la imagen
|
||||
HasUnsavedChanges = true;
|
||||
|
||||
// Limpiar historial de undo después de cambiar la escala
|
||||
MainWindow?.ClearUndoHistory();
|
||||
}
|
||||
|
||||
// Método para forzar el redibujo completo del canvas
|
||||
private void ForceCanvasRedraw()
|
||||
{
|
||||
// Forzar actualización de todos los objetos simulables mediante PropertyChanged
|
||||
foreach (var obj in ObjetosSimulables)
|
||||
{
|
||||
// Forzar la notificación de PropertyChanged para las propiedades de posición y tamaño
|
||||
// Esto hará que los bindings y converters se recalculen automáticamente
|
||||
obj.ForceUpdatePositionBindings();
|
||||
}
|
||||
|
||||
// Forzar actualización del layout del canvas principal
|
||||
MainWindow?.ImagenEnTrabajoCanvas?.InvalidateVisual();
|
||||
MainWindow?.ImagenEnTrabajoCanvas?.UpdateLayout();
|
||||
|
||||
// Actualizar selecciones visuales si existen
|
||||
_objectManager?.UpdateSelectionVisuals();
|
||||
|
||||
// Forzar actualización global del layout con prioridad de renderizado
|
||||
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Render, new Action(() =>
|
||||
{
|
||||
MainWindow?.ImagenEnTrabajoCanvas?.InvalidateVisual();
|
||||
MainWindow?.ImagenEnTrabajoCanvas?.UpdateLayout();
|
||||
}));
|
||||
}
|
||||
|
||||
// Diccionario para manejar ventanas de biblioteca múltiples
|
||||
private Dictionary<string, PopUps.LibraryWindow> _libraryWindows = new();
|
||||
|
||||
|
@ -1202,6 +1347,13 @@ namespace CtrEditor
|
|||
|
||||
// Nueva propiedad para almacenar los datos locales de objetos globales
|
||||
public ObservableCollection<DatosLocales>? DatosLocalesObjetos { get; set; }
|
||||
|
||||
// Diccionario para almacenar datos expandidos de imágenes
|
||||
public Dictionary<string, Models.ImageData>? ImageDataDictionary { get; set; }
|
||||
|
||||
// Compatibilidad con versiones anteriores - OBSOLETO
|
||||
[System.Obsolete("Use ImageDataDictionary instead")]
|
||||
public Dictionary<string, string>? ImageCustomNames { get; set; }
|
||||
}
|
||||
|
||||
public class TipoSimulable
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
xmlns:local="clr-namespace:CtrEditor"
|
||||
xmlns:controls="clr-namespace:CtrEditor.Controls"
|
||||
xmlns:uc="clr-namespace:CtrEditor.ObjetosSim.UserControls"
|
||||
xmlns:converters="clr-namespace:CtrEditor.Converters"
|
||||
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
|
||||
xmlns:sys="clr-namespace:System;assembly=mscorlib"
|
||||
xmlns:ObjetosSim="clr-namespace:CtrEditor.ObjetosSim"
|
||||
|
@ -48,6 +49,9 @@
|
|||
<!-- Converter for PLC Connect/Disconnect image -->
|
||||
<local:ConnectStateToImageConverter x:Key="ConnectStateToImageConverter" />
|
||||
|
||||
<!-- Converter for Image Display Names -->
|
||||
<converters:ImageDisplayNameConverter x:Key="ImageDisplayNameConverter" />
|
||||
|
||||
</Window.Resources>
|
||||
|
||||
<Grid>
|
||||
|
@ -87,7 +91,18 @@
|
|||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<ListBox x:Name="ListaImagenes" Grid.Row="0" Margin="5" ItemsSource="{Binding ListaImagenes}"
|
||||
SelectedItem="{Binding SelectedImage}" />
|
||||
SelectedItem="{Binding SelectedImage}">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Converter={StaticResource ImageDisplayNameConverter}}"/>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
<ListBox.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem Header="Propiedades" Command="{Binding RenameImageCommand}" CommandParameter="{Binding PlacementTarget.SelectedItem, RelativeSource={RelativeSource AncestorType=ContextMenu}}"/>
|
||||
</ContextMenu>
|
||||
</ListBox.ContextMenu>
|
||||
</ListBox>
|
||||
<ListBox x:Name="ListaFunciones" Grid.Row="1" Margin="5" ItemsSource="{Binding ListaOsBase}"
|
||||
DisplayMemberPath="Nombre" SelectedItem="{Binding SelectedItem}">
|
||||
<i:Interaction.Triggers>
|
||||
|
|
|
@ -713,6 +713,13 @@ namespace CtrEditor
|
|||
contextMenu.Items.Add(new Separator());
|
||||
}
|
||||
|
||||
// Agregar opción de configurar escala
|
||||
var scaleConfigItem = new MenuItem { Header = "Configurar Escala..." };
|
||||
scaleConfigItem.Click += (s, e) => viewModel.ConfigureScale();
|
||||
contextMenu.Items.Add(scaleConfigItem);
|
||||
|
||||
contextMenu.Items.Add(new Separator());
|
||||
|
||||
// Agregar información del sistema de undo
|
||||
var undoHistoryCount = _objectManager.GetUndoHistoryCount();
|
||||
var canUndo = _objectManager.CanUndo();
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
using CtrEditor.ObjetosSim;
|
||||
using Xceed.Wpf.Toolkit.PropertyGrid.Editors;
|
||||
using System.Linq;
|
||||
|
||||
namespace CtrEditor.Models
|
||||
{
|
||||
public class ImageData : INotifyPropertyChanged
|
||||
{
|
||||
private string _customName;
|
||||
private List<string> _tags;
|
||||
private string _etiquetas;
|
||||
|
||||
public string FileName { get; set; }
|
||||
|
||||
[DisplayName("Nombre personalizado")]
|
||||
public string CustomName
|
||||
{
|
||||
get => _customName;
|
||||
set
|
||||
{
|
||||
_customName = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
[Browsable(false)]
|
||||
public List<string> Tags
|
||||
{
|
||||
get => _tags ?? new List<string>();
|
||||
set
|
||||
{
|
||||
_tags = value;
|
||||
_etiquetas = string.Join(" ", value.Select(tag => tag.StartsWith("#") ? tag : "#" + tag));
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(Etiquetas));
|
||||
}
|
||||
}
|
||||
|
||||
[DisplayName("Etiquetas")]
|
||||
[property: Editor(typeof(TagPropertyEditor), typeof(TagPropertyEditor))]
|
||||
public string Etiquetas
|
||||
{
|
||||
get => _etiquetas ?? string.Empty;
|
||||
set
|
||||
{
|
||||
_etiquetas = value ?? string.Empty;
|
||||
// Convertir string de etiquetas a List<string>
|
||||
if (string.IsNullOrWhiteSpace(_etiquetas))
|
||||
{
|
||||
_tags = new List<string>();
|
||||
}
|
||||
else
|
||||
{
|
||||
_tags = _etiquetas.Split(' ', StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(tag => tag.StartsWith("#") ? tag.Substring(1) : tag)
|
||||
.Where(tag => !string.IsNullOrWhiteSpace(tag))
|
||||
.ToList();
|
||||
}
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(Tags));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lista de etiquetas sin el prefijo #, compatible con osBase
|
||||
/// </summary>
|
||||
[Browsable(false)]
|
||||
public List<string> ListaEtiquetas
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(Etiquetas))
|
||||
return new List<string>();
|
||||
|
||||
return Etiquetas.Split(' ', StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(tag => tag.StartsWith("#") ? tag.Substring(1) : tag)
|
||||
.Where(tag => !string.IsNullOrWhiteSpace(tag))
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
public string DisplayName => !string.IsNullOrEmpty(CustomName) ? CustomName : FileName;
|
||||
|
||||
public ImageData()
|
||||
{
|
||||
FileName = string.Empty;
|
||||
_customName = string.Empty;
|
||||
_tags = new List<string>();
|
||||
_etiquetas = string.Empty;
|
||||
}
|
||||
|
||||
public ImageData(string fileName, string customName = null, List<string> tags = null)
|
||||
{
|
||||
FileName = fileName;
|
||||
_customName = customName ?? string.Empty;
|
||||
_tags = tags ?? new List<string>();
|
||||
_etiquetas = string.Join(" ", _tags.Select(tag => tag.StartsWith("#") ? tag : "#" + tag));
|
||||
}
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -169,8 +169,54 @@ namespace CtrEditor.ObjetosSim
|
|||
|
||||
if (_visualRepresentation is ucTransporteTTop uc)
|
||||
{
|
||||
SimGeometria = AddRectangle(simulationManager, uc.Transporte, Alto, Ancho, Angulo);
|
||||
CrearAnimacionStoryBoardTrasnporte(uc.Transporte, InvertirDireccion);
|
||||
try
|
||||
{
|
||||
// Asegurar que el layout esté actualizado antes de crear la geometría
|
||||
uc.UpdateLayout();
|
||||
|
||||
// Validar que el rectángulo esté disponible y tenga dimensiones válidas
|
||||
if (uc.Transporte != null &&
|
||||
(!double.IsNaN(uc.Transporte.ActualWidth) && uc.Transporte.ActualWidth > 0) ||
|
||||
(!double.IsNaN(uc.Transporte.Width) && uc.Transporte.Width > 0))
|
||||
{
|
||||
SimGeometria = AddRectangle(simulationManager, uc.Transporte, Alto, Ancho, Angulo);
|
||||
CrearAnimacionStoryBoardTrasnporte(uc.Transporte, InvertirDireccion);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Si el rectángulo no está listo, intentar después del próximo layout
|
||||
uc.Dispatcher.BeginInvoke(new Action(() =>
|
||||
{
|
||||
if (uc.Transporte != null && simulationManager != null)
|
||||
{
|
||||
SimGeometria = AddRectangle(simulationManager, uc.Transporte, Alto, Ancho, Angulo);
|
||||
CrearAnimacionStoryBoardTrasnporte(uc.Transporte, InvertirDireccion);
|
||||
}
|
||||
}), System.Windows.Threading.DispatcherPriority.Loaded);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Log del error para diagnóstico pero continuar sin fallar
|
||||
System.Diagnostics.Debug.WriteLine($"Error al crear geometría de simulación: {ex.Message}");
|
||||
|
||||
// Intentar crear la geometría más tarde
|
||||
uc.Dispatcher.BeginInvoke(new Action(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (uc.Transporte != null && simulationManager != null)
|
||||
{
|
||||
SimGeometria = AddRectangle(simulationManager, uc.Transporte, Alto, Ancho, Angulo);
|
||||
CrearAnimacionStoryBoardTrasnporte(uc.Transporte, InvertirDireccion);
|
||||
}
|
||||
}
|
||||
catch (Exception ex2)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"Segundo intento falló: {ex2.Message}");
|
||||
}
|
||||
}), System.Windows.Threading.DispatcherPriority.ApplicationIdle);
|
||||
}
|
||||
}
|
||||
}
|
||||
public override void ucUnLoaded()
|
||||
|
|
|
@ -2,11 +2,13 @@
|
|||
using CtrEditor.FuncionesBase;
|
||||
using LibS7Adv;
|
||||
using Newtonsoft.Json;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
|
||||
|
||||
namespace CtrEditor.ObjetosSim
|
||||
{
|
||||
/// <summary>
|
||||
|
@ -59,6 +61,16 @@ namespace CtrEditor.ObjetosSim
|
|||
[ObservableProperty]
|
||||
public float tiempoRampa;
|
||||
|
||||
[ObservableProperty]
|
||||
[property: Description("Enable read of the Motor encoder simulated on PLC.")]
|
||||
[property: Category("Encoder:")]
|
||||
bool motor_With_Encoder;
|
||||
|
||||
[ObservableProperty]
|
||||
[property: Description("Actual Value of the encoder position.")]
|
||||
[property: Category("Encoder:")]
|
||||
public float actual_Position;
|
||||
|
||||
partial void OnTiempoRampaChanged(float value)
|
||||
{
|
||||
if (value < 0.1f)
|
||||
|
@ -205,8 +217,13 @@ namespace CtrEditor.ObjetosSim
|
|||
// Add timestamp to trace when the read occurs
|
||||
var timestamp = DateTime.Now;
|
||||
|
||||
// Read ControlWord and track the raw response
|
||||
var rawResponse = plc.LeerTagDInt($"\"DB MotorSimulate\".Motors[{DB_Motor}].ControlWord");
|
||||
if (Data.Motor_With_Encoder)
|
||||
Data.Actual_Position = (float)plc.LeerTagDInt($"\"DB MotorSimulate\".Motors[{DB_Motor}].ActualPosition");
|
||||
else
|
||||
Data.Actual_Position = 0;
|
||||
|
||||
// Read ControlWord and track the raw response
|
||||
var rawResponse = plc.LeerTagDInt($"\"DB MotorSimulate\".Motors[{DB_Motor}].ControlWord");
|
||||
int controlWord = rawResponse ?? 0;
|
||||
var control = VMMotorBitPacker.UnpackControlWord(controlWord);
|
||||
|
||||
|
|
|
@ -14,8 +14,6 @@ namespace CtrEditor.ObjetosSim
|
|||
public partial class osEncoderMotor : osBase, IosBase
|
||||
{
|
||||
private osBase Motor = null;
|
||||
private Stopwatch Stopwatch = new Stopwatch();
|
||||
private double stopwatch_last = 0;
|
||||
|
||||
public static string NombreClase()
|
||||
{
|
||||
|
@ -86,7 +84,6 @@ namespace CtrEditor.ObjetosSim
|
|||
Pulsos_Por_Vuelta = 360; // Por defecto, un pulso por grado
|
||||
Ratio_Giros_50hz = 1; // Por defecto, 1 giro por cada 50Hz
|
||||
Color_oculto = Brushes.Gray;
|
||||
Stopwatch.Start();
|
||||
}
|
||||
|
||||
public override void UpdatePLC(PLCViewModel plc, int elapsedMilliseconds)
|
||||
|
|
|
@ -14,8 +14,6 @@ namespace CtrEditor.ObjetosSim
|
|||
public partial class osEncoderMotorLineal : osBase, IosBase
|
||||
{
|
||||
private osBase Motor = null;
|
||||
private Stopwatch Stopwatch = new Stopwatch();
|
||||
private double stopwatch_last = 0;
|
||||
|
||||
public static string NombreClase()
|
||||
{
|
||||
|
@ -86,7 +84,6 @@ namespace CtrEditor.ObjetosSim
|
|||
{
|
||||
Pulsos_Por_Hz = 3000; // Por defecto, un pulso por grado
|
||||
Color_oculto = Brushes.Gray;
|
||||
Stopwatch.Start();
|
||||
}
|
||||
|
||||
public override void UpdatePLC(PLCViewModel plc, int elapsedMilliseconds)
|
||||
|
@ -100,7 +97,8 @@ namespace CtrEditor.ObjetosSim
|
|||
Valor_Actual = (int)value;
|
||||
return;
|
||||
}
|
||||
} else if (Motor != null && Motor is osVMmotorSim motor)
|
||||
}
|
||||
else if (Motor != null && Motor is osVMmotorSim motor)
|
||||
{
|
||||
VelocidadActual = motor.Velocidad;
|
||||
|
||||
|
@ -116,7 +114,10 @@ namespace CtrEditor.ObjetosSim
|
|||
float incrementoPulsos = pulsosPorHz * segundosTranscurridos;
|
||||
|
||||
// Actualizar valor del encoder
|
||||
Valor_Actual = (Valor_Actual + incrementoPulsos);
|
||||
if (motor.Motor_With_Encoder)
|
||||
Valor_Actual = motor.Actual_Position;
|
||||
else
|
||||
Valor_Actual = (Valor_Actual + incrementoPulsos);
|
||||
|
||||
// Actualizar color basado en si está girando
|
||||
Color_oculto = Math.Abs(pulsosPorHz) > 0.01f ? Brushes.LightGreen : Brushes.Gray;
|
||||
|
|
|
@ -5,6 +5,7 @@ using System.Windows.Data;
|
|||
using CtrEditor.PopUps;
|
||||
using Xceed.Wpf.Toolkit.PropertyGrid;
|
||||
using Xceed.Wpf.Toolkit.PropertyGrid.Editors;
|
||||
using CtrEditor.Models;
|
||||
|
||||
namespace CtrEditor.ObjetosSim
|
||||
{
|
||||
|
@ -50,18 +51,18 @@ namespace CtrEditor.ObjetosSim
|
|||
{
|
||||
try
|
||||
{
|
||||
// Obtener el objeto que se está editando
|
||||
if (propertyItem.Instance is osBase osObject)
|
||||
{
|
||||
// Obtener el MainWindow para acceder a todos los objetos
|
||||
var mainWindow = Application.Current.Windows
|
||||
.OfType<MainWindow>()
|
||||
.FirstOrDefault();
|
||||
// Obtener el MainWindow para acceder a todos los objetos
|
||||
var mainWindow = Application.Current.Windows
|
||||
.OfType<MainWindow>()
|
||||
.FirstOrDefault();
|
||||
|
||||
if (mainWindow?.DataContext is MainViewModel mainViewModel)
|
||||
if (mainWindow?.DataContext is MainViewModel mainViewModel)
|
||||
{
|
||||
// Determinar si el objeto es osBase o ImageData
|
||||
if (propertyItem.Instance is osBase osObject)
|
||||
{
|
||||
// Abrir el editor de etiquetas
|
||||
var tagEditor = new TagEditorWindow(osObject, mainViewModel.ObjetosSimulables);
|
||||
// Abrir el editor de etiquetas para osBase
|
||||
var tagEditor = new TagEditorWindow(osObject, mainViewModel.ObjetosSimulables, mainViewModel._imageDataDictionary);
|
||||
tagEditor.Owner = mainWindow;
|
||||
|
||||
if (tagEditor.ShowDialog() == true)
|
||||
|
@ -71,6 +72,22 @@ namespace CtrEditor.ObjetosSim
|
|||
textBox.GetBindingExpression(TextBox.TextProperty)?.UpdateTarget();
|
||||
}
|
||||
}
|
||||
else if (propertyItem.Instance is ImageData imageData)
|
||||
{
|
||||
// Para ImageData, crear un osBase temporal para usar el editor
|
||||
var tempOsBase = new osTextPlate();
|
||||
tempOsBase.Etiquetas = imageData.Etiquetas;
|
||||
|
||||
var tagEditor = new TagEditorWindow(tempOsBase, mainViewModel.ObjetosSimulables, mainViewModel._imageDataDictionary);
|
||||
tagEditor.Owner = mainWindow;
|
||||
|
||||
if (tagEditor.ShowDialog() == true)
|
||||
{
|
||||
// Actualizar ImageData con las nuevas etiquetas
|
||||
imageData.Etiquetas = tempOsBase.Etiquetas;
|
||||
textBox.GetBindingExpression(TextBox.TextProperty)?.UpdateTarget();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
|
@ -1,17 +1,14 @@
|
|||
<UserControl x:Class="CtrEditor.ObjetosSim.ucBoolTag"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
|
||||
xmlns:ei="http://schemas.microsoft.com/xaml/behaviors"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim"
|
||||
mc:Ignorable="d">
|
||||
<UserControl x:Class="CtrEditor.ObjetosSim.ucBoolTag" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
|
||||
xmlns:ei="http://schemas.microsoft.com/xaml/behaviors"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:vm="clr-namespace:CtrEditor.ObjetosSim"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<UserControl.DataContext>
|
||||
<vm:osBoolTag/>
|
||||
<vm:osBoolTag Show_Description="True" />
|
||||
</UserControl.DataContext>
|
||||
<Canvas RenderTransformOrigin="0.5,0.5">
|
||||
<Canvas RenderTransformOrigin="0.5,0.5" ToolTip="{Binding Descripcion}">
|
||||
<Canvas.RenderTransform>
|
||||
<TransformGroup>
|
||||
<ScaleTransform />
|
||||
|
@ -26,18 +23,14 @@
|
|||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Border x:Name="BackgroundRectangle"
|
||||
BorderBrush="Black"
|
||||
BorderThickness="2"
|
||||
CornerRadius="10,0,0,10"
|
||||
Width="30"
|
||||
Height="40"
|
||||
VerticalAlignment="Top"
|
||||
HorizontalAlignment="Left"
|
||||
Background="{Binding Color, Converter={StaticResource ColorToBrushConverter}}"/>
|
||||
<Label Content="{Binding Descripcion}" Grid.Column="1" VerticalAlignment="Center" Background="{Binding Color}" />
|
||||
<Border x:Name="BackgroundRectangle" BorderBrush="Black" BorderThickness="2" CornerRadius="10,0,0,10"
|
||||
Width="16" Height="25" VerticalAlignment="Center" HorizontalAlignment="Center"
|
||||
Background="{Binding Color, Converter={StaticResource ColorToBrushConverter}}" />
|
||||
<Label Content="{Binding Descripcion}" Grid.Column="1" VerticalAlignment="Center"
|
||||
Background="{Binding Color}"
|
||||
Visibility="{Binding Show_Description, Converter={StaticResource BooleanToVisibilityConverter}}" />
|
||||
<CheckBox Grid.Column="2" VerticalAlignment="Center" IsChecked="{Binding Estado}"
|
||||
Background="{Binding Color_oculto}" />
|
||||
Background="{Binding Color_oculto}" />
|
||||
</Grid>
|
||||
</Canvas>
|
||||
</UserControl>
|
||||
|
|
|
@ -39,6 +39,9 @@ namespace CtrEditor.ObjetosSim
|
|||
[ObservableProperty]
|
||||
public bool estado;
|
||||
|
||||
[ObservableProperty]
|
||||
public bool show_Description;
|
||||
|
||||
partial void OnEstadoChanged(bool value)
|
||||
{
|
||||
EscribirBitTag(Tag, value);
|
||||
|
@ -69,6 +72,7 @@ namespace CtrEditor.ObjetosSim
|
|||
tag = "%M50.0";
|
||||
Descripcion = "Nombre del Tag";
|
||||
Color = Colors.LightBlue;
|
||||
Show_Description = true;
|
||||
}
|
||||
|
||||
public override void UpdatePLCPrimerCiclo()
|
||||
|
|
|
@ -7,7 +7,7 @@ using LibS7Adv;
|
|||
using nkast.Aether.Physics2D.Common;
|
||||
using PaddleOCRSharp;
|
||||
using Siemens.Simatic.Simulation.Runtime;
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel; // Para poder usar [property: Category ...
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
|
@ -1195,6 +1195,21 @@ namespace CtrEditor.ObjetosSim
|
|||
CanvasSetTopinMeter(Top);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fuerza la actualización de todos los bindings de posición y tamaño
|
||||
/// disparando PropertyChanged para Left, Top, Ancho, Alto
|
||||
/// </summary>
|
||||
public void ForceUpdatePositionBindings()
|
||||
{
|
||||
// Para Left y Top, actualizar directamente el Canvas ya que tienen lógica especial en OnChanged
|
||||
CanvasSetLeftinMeter(Left);
|
||||
CanvasSetTopinMeter(Top);
|
||||
|
||||
// Para Ancho y Alto, disparar PropertyChanged para que los bindings se actualicen
|
||||
OnPropertyChanged(nameof(Ancho));
|
||||
OnPropertyChanged(nameof(Alto));
|
||||
}
|
||||
|
||||
|
||||
|
||||
public bool LeerBitTag(string Tag)
|
||||
|
@ -1349,13 +1364,65 @@ namespace CtrEditor.ObjetosSim
|
|||
return transformGroup;
|
||||
}
|
||||
|
||||
private static bool IsVisualChild(Visual child, Visual parent)
|
||||
{
|
||||
if (child == null || parent == null)
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
// Intentar obtener el ancestro común
|
||||
var transform = child.TransformToAncestor(parent);
|
||||
return transform != null;
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public (Vector2 TopLeft, Vector2 BottomRight) GetRectangleCoordinatesInMeter(Rectangle rect)
|
||||
{
|
||||
if (rect != null)
|
||||
if (rect == null)
|
||||
return (new Vector2(0, 0), new Vector2(0, 0));
|
||||
|
||||
var _canvasLeft = CanvasGetLeftinMeter();
|
||||
var _canvasTop = CanvasGetTopinMeter();
|
||||
|
||||
try
|
||||
{
|
||||
var _canvasLeft = CanvasGetLeftinMeter();
|
||||
var _canvasTop = CanvasGetTopinMeter();
|
||||
// Verificar que tanto el rectángulo como la representación visual estén cargados
|
||||
if (!rect.IsLoaded || _visualRepresentation == null || !_visualRepresentation.IsLoaded)
|
||||
{
|
||||
// Fallback: usar las propiedades básicas de posición si la transformación no está disponible
|
||||
float rectWidth = (float)rect.Width;
|
||||
float rectHeight = (float)rect.Height;
|
||||
|
||||
if (double.IsNaN(rectWidth) || rectWidth == 0)
|
||||
rectWidth = Ancho * PixelToMeter.Instance.calc.MetersToPixels(1);
|
||||
if (double.IsNaN(rectHeight) || rectHeight == 0)
|
||||
rectHeight = Alto * PixelToMeter.Instance.calc.MetersToPixels(1);
|
||||
|
||||
return (new Vector2(_canvasLeft, _canvasTop),
|
||||
new Vector2(_canvasLeft + PixelToMeter.Instance.calc.PixelsToMeters(rectWidth),
|
||||
_canvasTop + PixelToMeter.Instance.calc.PixelsToMeters(rectHeight)));
|
||||
}
|
||||
|
||||
// Forzar actualización del layout
|
||||
_visualRepresentation.UpdateLayout();
|
||||
|
||||
// Verificar que el rectángulo sea un descendiente visual de _visualRepresentation
|
||||
if (!IsVisualChild(rect, _visualRepresentation))
|
||||
{
|
||||
// Fallback: usar las dimensiones básicas
|
||||
float rectWidth = (float)rect.ActualWidth;
|
||||
float rectHeight = (float)rect.ActualHeight;
|
||||
|
||||
return (new Vector2(_canvasLeft, _canvasTop),
|
||||
new Vector2(_canvasLeft + PixelToMeter.Instance.calc.PixelsToMeters(rectWidth),
|
||||
_canvasTop + PixelToMeter.Instance.calc.PixelsToMeters(rectHeight)));
|
||||
}
|
||||
|
||||
// Obtiene la transformada del objeto visual
|
||||
GeneralTransform transform = rect.TransformToAncestor(_visualRepresentation);
|
||||
|
@ -1364,10 +1431,26 @@ namespace CtrEditor.ObjetosSim
|
|||
Point topLeft = transform.Transform(new Point(0, 0));
|
||||
Point bottomRight = transform.Transform(new Point(rect.ActualWidth, rect.ActualHeight));
|
||||
|
||||
return (new Vector2(PixelToMeter.Instance.calc.PixelsToMeters((float)topLeft.X) + _canvasLeft, PixelToMeter.Instance.calc.PixelsToMeters((float)topLeft.Y) + _canvasTop),
|
||||
new Vector2(PixelToMeter.Instance.calc.PixelsToMeters((float)bottomRight.X) + _canvasLeft, PixelToMeter.Instance.calc.PixelsToMeters((float)bottomRight.Y) + _canvasTop));
|
||||
return (new Vector2(PixelToMeter.Instance.calc.PixelsToMeters((float)topLeft.X) + _canvasLeft,
|
||||
PixelToMeter.Instance.calc.PixelsToMeters((float)topLeft.Y) + _canvasTop),
|
||||
new Vector2(PixelToMeter.Instance.calc.PixelsToMeters((float)bottomRight.X) + _canvasLeft,
|
||||
PixelToMeter.Instance.calc.PixelsToMeters((float)bottomRight.Y) + _canvasTop));
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
// Fallback en caso de error de transformación
|
||||
float rectWidth = (float)rect.ActualWidth;
|
||||
float rectHeight = (float)rect.ActualHeight;
|
||||
|
||||
if (double.IsNaN(rectWidth) || rectWidth == 0)
|
||||
rectWidth = Ancho * PixelToMeter.Instance.calc.MetersToPixels(1);
|
||||
if (double.IsNaN(rectHeight) || rectHeight == 0)
|
||||
rectHeight = Alto * PixelToMeter.Instance.calc.MetersToPixels(1);
|
||||
|
||||
return (new Vector2(_canvasLeft, _canvasTop),
|
||||
new Vector2(_canvasLeft + PixelToMeter.Instance.calc.PixelsToMeters(rectWidth),
|
||||
_canvasTop + PixelToMeter.Instance.calc.PixelsToMeters(rectHeight)));
|
||||
}
|
||||
else return (new Vector2(0, 0), new Vector2(0, 0));
|
||||
}
|
||||
|
||||
public (Vector2 Start, Vector2 End) GetCenterLineVectors(Rectangle rect)
|
||||
|
@ -1381,27 +1464,56 @@ namespace CtrEditor.ObjetosSim
|
|||
// Usar Dispatcher para asegurar la ejecución en el hilo correcto
|
||||
_visualRepresentation?.Dispatcher.Invoke(() =>
|
||||
{
|
||||
// Asegúrate de que el control está en el árbol visual y actualizado
|
||||
if (_visualRepresentation.IsLoaded && rect.IsLoaded)
|
||||
try
|
||||
{
|
||||
_visualRepresentation.UpdateLayout();
|
||||
// Asegúrate de que el control está en el árbol visual y actualizado
|
||||
if (_visualRepresentation.IsLoaded && rect.IsLoaded && IsVisualChild(rect, _visualRepresentation))
|
||||
{
|
||||
_visualRepresentation.UpdateLayout();
|
||||
|
||||
var _canvasLeft = CanvasGetLeftinMeter();
|
||||
var _canvasTop = CanvasGetTopinMeter();
|
||||
|
||||
var transform = rect.TransformToAncestor(_visualRepresentation);
|
||||
|
||||
// Puntos en coordenadas locales del rectángulo no rotado
|
||||
Point startLocal = new Point(0, rect.ActualHeight / 2);
|
||||
Point endLocal = new Point(rect.ActualWidth, rect.ActualHeight / 2);
|
||||
|
||||
// Transformar estos puntos al sistema de coordenadas del ancestro
|
||||
Point transformedStart = transform.Transform(startLocal);
|
||||
Point transformedEnd = transform.Transform(endLocal);
|
||||
|
||||
// Convierte a unidades de Farseer (metros en este caso)
|
||||
start = new Vector2(PixelToMeter.Instance.calc.PixelsToMeters((float)transformedStart.X) + _canvasLeft, PixelToMeter.Instance.calc.PixelsToMeters((float)transformedStart.Y) + _canvasTop);
|
||||
end = new Vector2(PixelToMeter.Instance.calc.PixelsToMeters((float)transformedEnd.X) + _canvasLeft, PixelToMeter.Instance.calc.PixelsToMeters((float)transformedEnd.Y) + _canvasTop);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fallback: usar coordenadas básicas
|
||||
var _canvasLeft = CanvasGetLeftinMeter();
|
||||
var _canvasTop = CanvasGetTopinMeter();
|
||||
|
||||
float rectWidth = (float)rect.ActualWidth;
|
||||
if (double.IsNaN(rectWidth) || rectWidth == 0)
|
||||
rectWidth = Ancho * PixelToMeter.Instance.calc.MetersToPixels(1);
|
||||
|
||||
start = new Vector2(_canvasLeft, _canvasTop + PixelToMeter.Instance.calc.PixelsToMeters(rectWidth / 2));
|
||||
end = new Vector2(_canvasLeft + PixelToMeter.Instance.calc.PixelsToMeters(rectWidth), _canvasTop + PixelToMeter.Instance.calc.PixelsToMeters(rectWidth / 2));
|
||||
}
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
// Fallback en caso de error de transformación
|
||||
var _canvasLeft = CanvasGetLeftinMeter();
|
||||
var _canvasTop = CanvasGetTopinMeter();
|
||||
|
||||
var transform = rect.TransformToAncestor(_visualRepresentation);
|
||||
float rectWidth = (float)rect.ActualWidth;
|
||||
if (double.IsNaN(rectWidth) || rectWidth == 0)
|
||||
rectWidth = Ancho * PixelToMeter.Instance.calc.MetersToPixels(1);
|
||||
|
||||
// Puntos en coordenadas locales del rectángulo no rotado
|
||||
Point startLocal = new Point(0, rect.ActualHeight / 2);
|
||||
Point endLocal = new Point(rect.ActualWidth, rect.ActualHeight / 2);
|
||||
|
||||
// Transformar estos puntos al sistema de coordenadas del ancestro
|
||||
Point transformedStart = transform.Transform(startLocal);
|
||||
Point transformedEnd = transform.Transform(endLocal);
|
||||
|
||||
// Convierte a unidades de Farseer (metros en este caso)
|
||||
start = new Vector2(PixelToMeter.Instance.calc.PixelsToMeters((float)transformedStart.X) + _canvasLeft, PixelToMeter.Instance.calc.PixelsToMeters((float)transformedStart.Y) + _canvasTop);
|
||||
end = new Vector2(PixelToMeter.Instance.calc.PixelsToMeters((float)transformedEnd.X) + _canvasLeft, PixelToMeter.Instance.calc.PixelsToMeters((float)transformedEnd.Y) + _canvasTop);
|
||||
start = new Vector2(_canvasLeft, _canvasTop + PixelToMeter.Instance.calc.PixelsToMeters(rectWidth / 2));
|
||||
end = new Vector2(_canvasLeft + PixelToMeter.Instance.calc.PixelsToMeters(rectWidth), _canvasTop + PixelToMeter.Instance.calc.PixelsToMeters(rectWidth / 2));
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
<Window x:Class="CtrEditor.PopUps.RenameImageWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
|
||||
Title="Editar Información de Imagen" Height="400" Width="500"
|
||||
WindowStartupLocation="CenterOwner" ResizeMode="CanResize">
|
||||
<Grid Margin="20">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Grid.Row="0" Text="Archivo original:" FontWeight="Bold" Margin="0,0,0,5"/>
|
||||
<TextBlock Grid.Row="1" x:Name="OriginalNameTextBlock" Margin="0,0,0,15" Foreground="Gray"/>
|
||||
|
||||
<GroupBox Grid.Row="2" Header="Propiedades de la Imagen" Margin="0,0,0,15">
|
||||
<xctk:PropertyGrid x:Name="PropertyGridControl"
|
||||
ShowSortOptions="False"
|
||||
ShowSearchBox="False"
|
||||
ShowSummary="True"
|
||||
ShowAdvancedOptions="False"
|
||||
ShowTitle="False"
|
||||
Margin="5">
|
||||
<xctk:PropertyGrid.EditorDefinitions>
|
||||
<xctk:EditorTemplateDefinition TargetProperties="Etiquetas">
|
||||
<xctk:EditorTemplateDefinition.EditingTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBox Grid.Column="0"
|
||||
Text="{Binding Value, UpdateSourceTrigger=PropertyChanged}"
|
||||
VerticalAlignment="Stretch"/>
|
||||
<Button Grid.Column="1"
|
||||
Content="..."
|
||||
Width="25"
|
||||
Margin="2,0,0,0"
|
||||
ToolTip="Abrir editor de etiquetas"
|
||||
Click="TagEditorButton_Click"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</xctk:EditorTemplateDefinition.EditingTemplate>
|
||||
</xctk:EditorTemplateDefinition>
|
||||
</xctk:PropertyGrid.EditorDefinitions>
|
||||
</xctk:PropertyGrid>
|
||||
</GroupBox>
|
||||
|
||||
<StackPanel Grid.Row="3" Orientation="Horizontal" HorizontalAlignment="Right">
|
||||
<Button x:Name="OkButton" Content="Aceptar" Width="80" Margin="0,0,10,0" IsDefault="True" Click="OkButton_Click"/>
|
||||
<Button x:Name="CancelButton" Content="Cancelar" Width="80" IsCancel="True" Click="CancelButton_Click"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Window>
|
|
@ -0,0 +1,82 @@
|
|||
using System.Windows;
|
||||
using CtrEditor.Models;
|
||||
using CtrEditor.ObjetosSim;
|
||||
using Xceed.Wpf.Toolkit.PropertyGrid;
|
||||
|
||||
namespace CtrEditor.PopUps
|
||||
{
|
||||
public partial class RenameImageWindow : Window
|
||||
{
|
||||
public ImageData ImageData { get; private set; }
|
||||
public bool IsOkClicked { get; private set; }
|
||||
|
||||
public RenameImageWindow(string originalName, ImageData imageData = null)
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
OriginalNameTextBlock.Text = originalName;
|
||||
|
||||
// Crear o usar ImageData existente
|
||||
ImageData = imageData ?? new ImageData
|
||||
{
|
||||
FileName = originalName,
|
||||
CustomName = string.Empty,
|
||||
Etiquetas = string.Empty
|
||||
};
|
||||
|
||||
// Configurar PropertyGrid
|
||||
PropertyGridControl.SelectedObject = ImageData;
|
||||
|
||||
Focus();
|
||||
}
|
||||
|
||||
private void TagEditorButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Obtener el MainWindow para acceder a todos los objetos
|
||||
var mainWindow = Application.Current.Windows
|
||||
.OfType<MainWindow>()
|
||||
.FirstOrDefault();
|
||||
|
||||
if (mainWindow?.DataContext is MainViewModel mainViewModel)
|
||||
{
|
||||
// Para ImageData, crear un osBase temporal para usar el editor
|
||||
var tempOsBase = new osTextPlate();
|
||||
tempOsBase.Etiquetas = ImageData.Etiquetas;
|
||||
|
||||
var tagEditor = new TagEditorWindow(tempOsBase, mainViewModel.ObjetosSimulables, mainViewModel._imageDataDictionary);
|
||||
tagEditor.Owner = this;
|
||||
|
||||
if (tagEditor.ShowDialog() == true)
|
||||
{
|
||||
// Actualizar ImageData con las nuevas etiquetas
|
||||
ImageData.Etiquetas = tempOsBase.Etiquetas;
|
||||
|
||||
// Forzar actualización del PropertyGrid
|
||||
PropertyGridControl.Update();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"Error al abrir el editor de etiquetas: {ex.Message}",
|
||||
"Error", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void OkButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
IsOkClicked = true;
|
||||
DialogResult = true;
|
||||
Close();
|
||||
}
|
||||
|
||||
private void CancelButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
IsOkClicked = false;
|
||||
DialogResult = false;
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
<Window x:Class="CtrEditor.PopUps.ScaleConfigWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:converters="clr-namespace:CtrEditor.Converters"
|
||||
Title="Configurar Escala"
|
||||
Height="280"
|
||||
Width="450"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
ResizeMode="NoResize">
|
||||
<Window.Resources>
|
||||
<converters:RegionalFloatConverter x:Key="RegionalFloatConverter"/>
|
||||
</Window.Resources>
|
||||
<Grid Margin="20">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Título -->
|
||||
<TextBlock Grid.Row="0"
|
||||
Text="Configuración de Escala de Conversión"
|
||||
FontWeight="Bold"
|
||||
FontSize="14"
|
||||
Margin="0,0,0,15"/>
|
||||
|
||||
<!-- Escala actual -->
|
||||
<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="0,0,0,10">
|
||||
<TextBlock Text="Escala actual: " VerticalAlignment="Center" Width="120"/>
|
||||
<TextBlock VerticalAlignment="Center" FontWeight="Bold">
|
||||
<TextBlock.Text>
|
||||
<MultiBinding StringFormat="{}{0} m/pixel">
|
||||
<Binding Path="CurrentScale" Converter="{StaticResource RegionalFloatConverter}"/>
|
||||
</MultiBinding>
|
||||
</TextBlock.Text>
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Nueva escala -->
|
||||
<StackPanel Grid.Row="2" Orientation="Horizontal" Margin="0,0,0,10">
|
||||
<TextBlock Text="Nueva escala: " VerticalAlignment="Center" Width="120"/>
|
||||
<TextBox x:Name="ScaleTextBox"
|
||||
Text="{Binding NewScale, Converter={StaticResource RegionalFloatConverter}, UpdateSourceTrigger=PropertyChanged}"
|
||||
Width="100"
|
||||
VerticalAlignment="Center"
|
||||
Margin="0,0,5,0"/>
|
||||
<TextBlock Text="m/pixel" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Presets -->
|
||||
<StackPanel Grid.Row="3" Orientation="Vertical" Margin="0,0,0,15">
|
||||
<TextBlock Text="Presets comunes:" FontWeight="SemiBold" Margin="0,0,0,5"/>
|
||||
<WrapPanel>
|
||||
<Button Content="1:1 (0.01)" Command="{Binding SetPresetCommand}" CommandParameter="0.01" Margin="0,0,5,5"/>
|
||||
<Button Content="1:10 (0.001)" Command="{Binding SetPresetCommand}" CommandParameter="0.001" Margin="0,0,5,5"/>
|
||||
<Button Content="1:100 (0.0001)" Command="{Binding SetPresetCommand}" CommandParameter="0.0001" Margin="0,0,5,5"/>
|
||||
<Button Content="1 px = 1 cm (0.01)" Command="{Binding SetPresetCommand}" CommandParameter="0.01" Margin="0,0,5,5"/>
|
||||
<Button Content="1 px = 1 mm (0.001)" Command="{Binding SetPresetCommand}" CommandParameter="0.001" Margin="0,0,5,5"/>
|
||||
</WrapPanel>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Información de ayuda -->
|
||||
<Border Grid.Row="5"
|
||||
Background="LightYellow"
|
||||
BorderBrush="Orange"
|
||||
BorderThickness="1"
|
||||
Padding="10"
|
||||
Margin="0,0,0,15">
|
||||
<StackPanel>
|
||||
<TextBlock Text="Información:" FontWeight="Bold" Margin="0,0,0,5"/>
|
||||
<TextBlock TextWrapping="Wrap">
|
||||
<Run Text="La escala define cuántos metros representa cada píxel en el canvas."/>
|
||||
<LineBreak/>
|
||||
<Run Text="• Valores menores = objetos más pequeños en pantalla"/>
|
||||
<LineBreak/>
|
||||
<Run Text="• Valores mayores = objetos más grandes en pantalla"/>
|
||||
<LineBreak/>
|
||||
<Run Text="• Ejemplo: 0.01 significa que 1 píxel = 1 centímetro"/>
|
||||
<LineBreak/>
|
||||
<Run Text="• Los cambios se aplican automáticamente después de 0.5 segundos"/>
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- Botones -->
|
||||
<StackPanel Grid.Row="6"
|
||||
Orientation="Horizontal"
|
||||
HorizontalAlignment="Right">
|
||||
<Button Content="Aplicar"
|
||||
Command="{Binding ApplyCommand}"
|
||||
Width="80"
|
||||
Height="30"
|
||||
Margin="0,0,10,0"/>
|
||||
<Button Content="Aceptar"
|
||||
Command="{Binding AcceptCommand}"
|
||||
Width="80"
|
||||
Height="30"
|
||||
Margin="0,0,10,0"
|
||||
IsDefault="True"/>
|
||||
<Button Content="Cancelar"
|
||||
Command="{Binding CancelCommand}"
|
||||
Width="80"
|
||||
Height="30"
|
||||
IsCancel="True"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Window>
|
|
@ -0,0 +1,146 @@
|
|||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Threading;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
|
||||
namespace CtrEditor.PopUps
|
||||
{
|
||||
public partial class ScaleConfigWindow : Window
|
||||
{
|
||||
public ScaleConfigWindow(float currentScale, MainViewModel mainViewModel)
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = new ScaleConfigViewModel(currentScale, this, mainViewModel);
|
||||
}
|
||||
|
||||
public float NewScale => ((ScaleConfigViewModel)DataContext).NewScale;
|
||||
}
|
||||
|
||||
public partial class ScaleConfigViewModel : ObservableObject
|
||||
{
|
||||
private readonly ScaleConfigWindow _window;
|
||||
private readonly MainViewModel _mainViewModel;
|
||||
private float _originalScale;
|
||||
private DispatcherTimer _autoApplyTimer;
|
||||
|
||||
[ObservableProperty]
|
||||
private float currentScale;
|
||||
|
||||
[ObservableProperty]
|
||||
private float newScale;
|
||||
|
||||
public ICommand SetPresetCommand { get; }
|
||||
public ICommand AcceptCommand { get; }
|
||||
public ICommand CancelCommand { get; }
|
||||
public ICommand ApplyCommand { get; }
|
||||
|
||||
public ScaleConfigViewModel(float currentScale, ScaleConfigWindow window, MainViewModel mainViewModel)
|
||||
{
|
||||
_window = window;
|
||||
_mainViewModel = mainViewModel;
|
||||
_originalScale = currentScale;
|
||||
CurrentScale = currentScale;
|
||||
NewScale = currentScale;
|
||||
|
||||
// Inicializar timer para aplicación automática
|
||||
_autoApplyTimer = new DispatcherTimer
|
||||
{
|
||||
Interval = TimeSpan.FromMilliseconds(500) // 500ms de delay
|
||||
};
|
||||
_autoApplyTimer.Tick += (s, e) =>
|
||||
{
|
||||
_autoApplyTimer.Stop();
|
||||
if (NewScale > 0 && NewScale != CurrentScale)
|
||||
{
|
||||
Apply();
|
||||
}
|
||||
};
|
||||
|
||||
SetPresetCommand = new RelayCommand<object>(SetPreset);
|
||||
AcceptCommand = new RelayCommand(Accept, CanAccept);
|
||||
CancelCommand = new RelayCommand(Cancel);
|
||||
ApplyCommand = new RelayCommand(Apply, CanAccept);
|
||||
}
|
||||
|
||||
private void SetPreset(object parameter)
|
||||
{
|
||||
if (parameter != null && float.TryParse(parameter.ToString(),
|
||||
System.Globalization.NumberStyles.Float,
|
||||
System.Globalization.CultureInfo.InvariantCulture,
|
||||
out float presetValue))
|
||||
{
|
||||
NewScale = presetValue;
|
||||
}
|
||||
}
|
||||
|
||||
private bool CanAccept()
|
||||
{
|
||||
return NewScale > 0; // Solo verificar que sea positivo
|
||||
}
|
||||
|
||||
private void Accept()
|
||||
{
|
||||
if (NewScale <= 0)
|
||||
{
|
||||
MessageBox.Show("La escala debe ser un valor positivo mayor que cero.",
|
||||
"Error de Validación",
|
||||
MessageBoxButton.OK,
|
||||
MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
// Aplicar la escala una última vez antes de cerrar
|
||||
Apply();
|
||||
|
||||
// Cerrar la ventana
|
||||
_window.Close();
|
||||
}
|
||||
|
||||
private void Apply()
|
||||
{
|
||||
if (NewScale <= 0)
|
||||
{
|
||||
MessageBox.Show("La escala debe ser un valor positivo mayor que cero.",
|
||||
"Error de Validación",
|
||||
MessageBoxButton.OK,
|
||||
MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
// Aplicar la escala inmediatamente
|
||||
_mainViewModel.ApplyScale(NewScale);
|
||||
|
||||
// Actualizar la escala actual para reflejar el cambio
|
||||
CurrentScale = NewScale;
|
||||
}
|
||||
|
||||
private void Cancel()
|
||||
{
|
||||
// Restaurar la escala original
|
||||
_mainViewModel.ApplyScale(_originalScale);
|
||||
|
||||
// Cerrar la ventana
|
||||
_window.Close();
|
||||
}
|
||||
|
||||
partial void OnNewScaleChanged(float value)
|
||||
{
|
||||
// Forzar la re-evaluación del comando Accept cuando el valor cambia
|
||||
CommandManager.InvalidateRequerySuggested();
|
||||
|
||||
// Reiniciar el timer para aplicación automática (verificar que esté inicializado)
|
||||
if (_autoApplyTimer != null)
|
||||
{
|
||||
_autoApplyTimer.Stop();
|
||||
if (value > 0 && value != CurrentScale)
|
||||
{
|
||||
_autoApplyTimer.Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ using System.Windows.Input;
|
|||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using CtrEditor.ObjetosSim;
|
||||
using CtrEditor.Models;
|
||||
|
||||
namespace CtrEditor.PopUps
|
||||
{
|
||||
|
@ -13,10 +14,10 @@ namespace CtrEditor.PopUps
|
|||
/// </summary>
|
||||
public partial class TagEditorWindow : Window
|
||||
{
|
||||
public TagEditorWindow(osBase objeto, IEnumerable<osBase> todosLosObjetos)
|
||||
public TagEditorWindow(osBase objeto, IEnumerable<osBase> todosLosObjetos, Dictionary<string, ImageData> imageDataDictionary = null)
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = new TagEditorViewModel(objeto, todosLosObjetos, this);
|
||||
DataContext = new TagEditorViewModel(objeto, todosLosObjetos, this, imageDataDictionary);
|
||||
}
|
||||
|
||||
private void TxtNuevaEtiqueta_KeyDown(object sender, KeyEventArgs e)
|
||||
|
@ -46,6 +47,7 @@ namespace CtrEditor.PopUps
|
|||
{
|
||||
private readonly osBase _objeto;
|
||||
private readonly IEnumerable<osBase> _todosLosObjetos;
|
||||
private readonly Dictionary<string, ImageData> _imageDataDictionary;
|
||||
private readonly TagEditorWindow _window;
|
||||
private readonly List<string> _etiquetasOriginales;
|
||||
|
||||
|
@ -64,10 +66,11 @@ namespace CtrEditor.PopUps
|
|||
public ICommand AplicarCommand { get; }
|
||||
public ICommand CancelarCommand { get; }
|
||||
|
||||
public TagEditorViewModel(osBase objeto, IEnumerable<osBase> todosLosObjetos, TagEditorWindow window)
|
||||
public TagEditorViewModel(osBase objeto, IEnumerable<osBase> todosLosObjetos, TagEditorWindow window, Dictionary<string, ImageData> imageDataDictionary = null)
|
||||
{
|
||||
_objeto = objeto;
|
||||
_todosLosObjetos = todosLosObjetos;
|
||||
_imageDataDictionary = imageDataDictionary;
|
||||
_window = window;
|
||||
|
||||
// Guardar las etiquetas originales para poder cancelar
|
||||
|
@ -111,9 +114,22 @@ namespace CtrEditor.PopUps
|
|||
var todasLasEtiquetas = _todosLosObjetos
|
||||
.SelectMany(obj => obj.ListaEtiquetas)
|
||||
.Distinct()
|
||||
.OrderBy(tag => tag)
|
||||
.ToList();
|
||||
|
||||
// Agregar etiquetas de las imágenes si está disponible el diccionario
|
||||
if (_imageDataDictionary != null)
|
||||
{
|
||||
var etiquetasImagenes = _imageDataDictionary.Values
|
||||
.SelectMany(imageData => imageData.ListaEtiquetas)
|
||||
.Distinct();
|
||||
|
||||
todasLasEtiquetas.AddRange(etiquetasImagenes);
|
||||
todasLasEtiquetas = todasLasEtiquetas.Distinct().ToList();
|
||||
}
|
||||
|
||||
// Ordenar todas las etiquetas
|
||||
todasLasEtiquetas = todasLasEtiquetas.OrderBy(tag => tag).ToList();
|
||||
|
||||
// Mostrar solo las que no están en el objeto actual
|
||||
var etiquetasObjetoActual = _objeto.ListaEtiquetas;
|
||||
foreach (var tag in todasLasEtiquetas.Except(etiquetasObjetoActual))
|
||||
|
|
|
@ -50,7 +50,7 @@ namespace CtrEditor.Serialization
|
|||
obj.RestaurarDesdeSnapshotGlobal();
|
||||
}
|
||||
|
||||
// Prepara para serialización
|
||||
// Prepara para serializaci<EFBFBD>n
|
||||
obj.SalvarDatosNoSerializables();
|
||||
|
||||
// Separa objetos globales y locales
|
||||
|
@ -64,13 +64,14 @@ namespace CtrEditor.Serialization
|
|||
}
|
||||
}
|
||||
|
||||
// Paso 2: Guardar datos de la página actual
|
||||
// Paso 2: Guardar datos de la p<EFBFBD>gina actual
|
||||
var dataToSerialize = new SimulationData
|
||||
{
|
||||
ObjetosSimulables = objetosSimulables.Count > 0 ? objetosSimulables : null,
|
||||
UnitConverter = PixelToMeter.Instance.calc,
|
||||
PLC_ConnectionData = _mainViewModel.PLCViewModel,
|
||||
DatosLocalesObjetos = datosLocalesObjetos.Count > 0 ? datosLocalesObjetos : null
|
||||
DatosLocalesObjetos = datosLocalesObjetos.Count > 0 ? datosLocalesObjetos : null,
|
||||
ImageDataDictionary = _mainViewModel._imageDataDictionary.Count > 0 ? _mainViewModel._imageDataDictionary : null
|
||||
};
|
||||
|
||||
var path = _datosDeTrabajo.ObtenerPathImagenConExtension(selectedImage, ".json");
|
||||
|
@ -112,11 +113,11 @@ namespace CtrEditor.Serialization
|
|||
// Paso 1: Cargar objetos globales
|
||||
LoadAllPagesState(settings);
|
||||
|
||||
// Paso 2: Cargar datos de la página actual
|
||||
// Paso 2: Cargar datos de la p<EFBFBD>gina actual
|
||||
var simulationData = LoadCurrentPageState(selectedImage, settings);
|
||||
|
||||
// Paso 3: Crear snapshots de los objetos globales
|
||||
// Nota: No filtramos por Enable_Local_Data aquí ya que se establecerá después
|
||||
// Nota: No filtramos por Enable_Local_Data aqu<EFBFBD> ya que se establecer<65> despu<70>s
|
||||
foreach (var obj in _mainViewModel.ObjetosSimulables)
|
||||
{
|
||||
if (obj.Enable_On_All_Pages)
|
||||
|
@ -134,7 +135,7 @@ namespace CtrEditor.Serialization
|
|||
obj.Enable_Local_Data = false;
|
||||
}
|
||||
|
||||
// Luego aplicar los datos locales, esto activará Enable_Local_Data automáticamente
|
||||
// Luego aplicar los datos locales, esto activar<EFBFBD> Enable_Local_Data autom<6F>ticamente
|
||||
foreach (var datosLocales in simulationData.DatosLocalesObjetos)
|
||||
{
|
||||
var objetoGlobal = _mainViewModel.ObjetosSimulables.FirstOrDefault(
|
||||
|
@ -186,7 +187,31 @@ namespace CtrEditor.Serialization
|
|||
else
|
||||
_mainViewModel.PLCViewModel = new PLCViewModel();
|
||||
|
||||
PixelToMeter.Instance.calc = simulationData.UnitConverter;
|
||||
// Solo sobrescribir el UnitConverter si existe uno guardado
|
||||
if (simulationData.UnitConverter != null)
|
||||
PixelToMeter.Instance.calc = simulationData.UnitConverter;
|
||||
|
||||
// Cargar datos de imágenes
|
||||
if (simulationData.ImageDataDictionary != null)
|
||||
{
|
||||
_mainViewModel._imageDataDictionary.Clear();
|
||||
foreach (var imageDataEntry in simulationData.ImageDataDictionary)
|
||||
{
|
||||
_mainViewModel._imageDataDictionary[imageDataEntry.Key] = imageDataEntry.Value;
|
||||
}
|
||||
}
|
||||
|
||||
// Compatibilidad con versiones anteriores (ImageCustomNames)
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
if (simulationData.ImageCustomNames != null)
|
||||
{
|
||||
foreach (var customName in simulationData.ImageCustomNames)
|
||||
{
|
||||
var imageData = _mainViewModel.GetOrCreateImageData(customName.Key);
|
||||
imageData.CustomName = customName.Value;
|
||||
}
|
||||
}
|
||||
#pragma warning restore CS0618
|
||||
}
|
||||
}
|
||||
return simulationData;
|
||||
|
|
|
@ -314,7 +314,7 @@ namespace CtrEditor
|
|||
{
|
||||
if (values.Length == 2 && values[0] is float value1 && values[1] is float value2)
|
||||
{
|
||||
return (double) (value1 + value2);
|
||||
return (double)(value1 + value2);
|
||||
}
|
||||
return DependencyProperty.UnsetValue;
|
||||
}
|
||||
|
@ -435,7 +435,14 @@ namespace CtrEditor
|
|||
{
|
||||
// Instancia privada estática, parte del patrón Singleton
|
||||
private static PixelToMeter? _instance;
|
||||
public UnitConverter calc = new UnitConverter(0.01f);
|
||||
public UnitConverter calc;
|
||||
|
||||
// Constructor privado para el patrón Singleton
|
||||
private PixelToMeter()
|
||||
{
|
||||
// Solo inicializar con valor por defecto si no se ha configurado antes
|
||||
calc = new UnitConverter(0.01f);
|
||||
}
|
||||
|
||||
// Propiedad pública estática para acceder a la instancia
|
||||
public static PixelToMeter Instance
|
||||
|
|
Loading…
Reference in New Issue