Compare commits

...

3 Commits

9 changed files with 475 additions and 222 deletions

View File

@ -10,11 +10,11 @@
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<Optimize>False</Optimize> <Optimize>True</Optimize>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<Optimize>False</Optimize> <Optimize>True</Optimize>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@ -30,14 +30,11 @@ namespace CtrEditor
public string? ObtenerPathAllPages(string extension) public string? ObtenerPathAllPages(string extension)
{ {
string folderPath = EstadoPersistente.Instance.directorio; // Usar directamente desde el Singleton if (!string.IsNullOrEmpty(EstadoPersistente.Instance.directorio))
return Path.Combine(EstadoPersistente.Instance.directorio, "AllPages" + extension);
if (Directory.Exists(folderPath)) return null;
return Path.ChangeExtension(folderPath + "\\allpages", extension);
else return null;
} }
public void CargarImagenes() public void CargarImagenes()
{ {
Imagenes.Clear(); Imagenes.Clear();

View File

@ -16,6 +16,7 @@ namespace CtrEditor.FuncionesBase
{ "Siemens IO Input", "Format: Exxxxx.y (x: 0-65535, y: 0-7)" }, { "Siemens IO Input", "Format: Exxxxx.y (x: 0-65535, y: 0-7)" },
{ "Siemens IO Output", "Format: Axxxxx.y (x: 0-65535, y: 0-7)" }, { "Siemens IO Output", "Format: Axxxxx.y (x: 0-65535, y: 0-7)" },
{ "LETRASNUMEROS", "Format: ABC...123... (1-10 letters + numbers)" }, { "LETRASNUMEROS", "Format: ABC...123... (1-10 letters + numbers)" },
{ "LETRASNUMEROSESPACIOS", "Format: ABC...123... (letters, numbers and spaces allowed)" },
{ "Numero", "Numeric value" } { "Numero", "Numeric value" }
}; };
@ -35,6 +36,7 @@ namespace CtrEditor.FuncionesBase
"Siemens IO Input" => ApplySiemensPattern(text, "E"), "Siemens IO Input" => ApplySiemensPattern(text, "E"),
"Siemens IO Output" => ApplySiemensPattern(text, "A"), "Siemens IO Output" => ApplySiemensPattern(text, "A"),
"LETRASNUMEROS" => ApplyLetrasNumerosPattern(text), "LETRASNUMEROS" => ApplyLetrasNumerosPattern(text),
"LETRASNUMEROSESPACIOS" => ApplyLetrasNumerosEspaciosPattern(text),
"Numero" => ApplyNumberPattern(text), "Numero" => ApplyNumberPattern(text),
_ => text _ => text
}; };
@ -77,6 +79,15 @@ namespace CtrEditor.FuncionesBase
return $"{letters.ToUpper()}{numbers}"; return $"{letters.ToUpper()}{numbers}";
} }
private static string ApplyLetrasNumerosEspaciosPattern(string text)
{
// Keep only letters, numbers and spaces
var cleanedText = new string(text.Where(c => char.IsLetterOrDigit(c) || char.IsWhiteSpace(c)).ToArray());
// Convert to uppercase
return cleanedText.ToUpper();
}
private static string ApplyNumberPattern(string text) private static string ApplyNumberPattern(string text)
{ {
var match = Regex.Match(text, @"-?\d+\.?\d*"); var match = Regex.Match(text, @"-?\d+\.?\d*");

View File

@ -21,13 +21,14 @@ using System.Windows.Data;
using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Input;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Collections.Specialized; using System.Collections.Specialized;
using CtrEditor.Serialization; // Add this line
namespace CtrEditor namespace CtrEditor
{ {
public partial class MainViewModel : ObservableObject public partial class MainViewModel : ObservableObject
{ {
private readonly StateSerializer _stateSerializer;
public Stopwatch stopwatch_Sim; public Stopwatch stopwatch_Sim;
private double stopwatch_SimPLC_last; private double stopwatch_SimPLC_last;
@ -367,6 +368,7 @@ namespace CtrEditor
OpenRecentDirectoryCommand = new RelayCommand<string>(OpenRecentDirectory); OpenRecentDirectoryCommand = new RelayCommand<string>(OpenRecentDirectory);
_stateManager = new StateManager(EstadoPersistente.Instance.directorio, this); _stateManager = new StateManager(EstadoPersistente.Instance.directorio, this);
_stateSerializer = new StateSerializer(this, datosDeTrabajo, simulationManager);
} }
private void OsListFilter_PropertyChanged(object? sender, PropertyChangedEventArgs e) private void OsListFilter_PropertyChanged(object? sender, PropertyChangedEventArgs e)
@ -425,7 +427,7 @@ namespace CtrEditor
} }
// Crear UserControl desde osBase : Nuevo o desde Deserealizacion // Crear UserControl desde osBase : Nuevo o desde Deserealizacion
private bool CrearUserControlDesdeObjetoSimulable(osBase osObjeto) public bool CrearUserControlDesdeObjetoSimulable(osBase osObjeto)
{ {
Type tipoObjeto = osObjeto.GetType(); Type tipoObjeto = osObjeto.GetType();
@ -513,25 +515,9 @@ namespace CtrEditor
} }
public void EliminarObjetoSeleccionado()
{
if (SelectedItemOsList is osBase objEliminar)
{
var result = MessageBox.Show($"¿Está seguro que desea eliminar el objeto '{objEliminar.Nombre}'?",
"Confirmar eliminación",
MessageBoxButton.YesNo,
MessageBoxImage.Question);
if (result == MessageBoxResult.Yes)
{
RemoverObjetoSimulable(objEliminar);
}
}
}
private void EliminarUserControl() private void EliminarUserControl()
{ {
EliminarObjetoSeleccionado(); _objectManager.EliminarObjetosSeleccionados();
} }
@ -661,7 +647,7 @@ namespace CtrEditor
simulationManager.Start(); simulationManager.Start();
} }
private void StopSimulation() public void StopSimulation()
{ {
IsSimulationRunning = false; IsSimulationRunning = false;
@ -717,7 +703,7 @@ namespace CtrEditor
objetoSimulable.SetPLC(PLCViewModel); objetoSimulable.SetPLC(PLCViewModel);
} }
private void DisconnectPLC() public void DisconnectPLC()
{ {
PLCViewModel.Disconnect(); PLCViewModel.Disconnect();
_timerPLCUpdate.Stop(); _timerPLCUpdate.Stop();
@ -822,138 +808,17 @@ namespace CtrEditor
{ {
if (SelectedImage != null) if (SelectedImage != null)
{ {
StopSimulation(); _stateSerializer.SaveState(SelectedImage);
DisconnectPLC();
ObservableCollection<osBase> _objetosSimulables = new ObservableCollection<osBase>();
ObservableCollection<osBase> _objetosSimulablesAllPages = new ObservableCollection<osBase>();
foreach (var obj in ObjetosSimulables)
{
// Guardar referencias temporales
obj.SalvarDatosNoSerializables();
if (!obj.Enable_On_All_Pages)
_objetosSimulables.Add(obj);
else
_objetosSimulablesAllPages.Add(obj);
}
// Salvar los objetos de la pagina actual
// Crear un objeto que incluya tanto los ObjetosSimulables como el UnitConverter y PLC_ConnectionData
var dataToSerialize = new SimulationData
{
ObjetosSimulables = _objetosSimulables,
UnitConverter = PixelToMeter.Instance.calc,
PLC_ConnectionData = PLCViewModel
};
// Ruta del archivo a ser guardado
var path = DatosDeTrabajo.ObtenerPathImagenConExtension(SelectedImage, ".json");
if (path != null)
SerializarYSalvar(dataToSerialize, path);
// Salvar los objetos de todas las paginas
// Ruta del archivo a ser guardado
path = DatosDeTrabajo.ObtenerPathAllPages(".json");
if (path != null)
SerializarYSalvar(_objetosSimulablesAllPages, path);
// Restaurar las propiedades originales de los objetos
foreach (var obj in ObjetosSimulables)
obj.RestaurarDatosNoSerializables();
HasUnsavedChanges = false; HasUnsavedChanges = false;
} }
} }
private void SerializarYSalvar(object listaObjetos, string path)
{
// Verificar si el archivo ya existe y crear un respaldo
if (File.Exists(path))
{
var backupPath = Path.ChangeExtension(path, ".bak");
File.Copy(path, backupPath, true); // Copia el archivo existente a un nuevo archivo .bak, sobrescribiendo si es necesario
}
var settings = new JsonSerializerSettings
{
Formatting = Formatting.Indented,
// PreserveReferencesHandling = PreserveReferencesHandling.Objects,
NullValueHandling = NullValueHandling.Ignore,
TypeNameHandling = TypeNameHandling.Auto
};
// Serializar
var serializedData = JsonConvert.SerializeObject(listaObjetos, settings);
File.WriteAllText(path, serializedData); // Escribir el nuevo archivo JSON
}
public void LoadStateObjetosSimulables() public void LoadStateObjetosSimulables()
{ {
try if (SelectedImage != null)
{ {
StopSimulation(); _stateSerializer.LoadState(SelectedImage);
DisconnectPLC();
ObjetosSimulables.Clear();
simulationManager.Clear();
if (SelectedImage != null)
{
var settings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto,
ObjectCreationHandling = ObjectCreationHandling.Replace,
// PreserveReferencesHandling = PreserveReferencesHandling.Objects,
ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor
};
string jsonPath = datosDeTrabajo.ObtenerPathImagenConExtension(SelectedImage, ".json");
if (File.Exists(jsonPath))
{
string jsonString = File.ReadAllText(jsonPath);
var simulationData = JsonConvert.DeserializeObject<SimulationData>(jsonString, settings);
if (simulationData != null)
{
if (simulationData.ObjetosSimulables is not null)
ObjetosSimulables = simulationData.ObjetosSimulables;
if (simulationData.PLC_ConnectionData is not null)
PLCViewModel = simulationData.PLC_ConnectionData;
else
PLCViewModel = new PLCViewModel();
PixelToMeter.Instance.calc = simulationData.UnitConverter;
}
}
jsonPath = DatosDeTrabajo.ObtenerPathAllPages(".json");
if (File.Exists(jsonPath))
{
string jsonString = File.ReadAllText(jsonPath);
ObservableCollection<osBase> _objetosSimulablesAllPages = new ObservableCollection<osBase>();
_objetosSimulablesAllPages = JsonConvert.DeserializeObject<ObservableCollection<osBase>>(jsonString, settings);
if (_objetosSimulablesAllPages != null)
foreach (var obj in _objetosSimulablesAllPages)
ObjetosSimulables.Add(obj);
}
// Recorrer la colección de objetos simulables
foreach (var objetoSimulable in ObjetosSimulables)
if (objetoSimulable != null)
{
objetoSimulable.CheckData();
CrearUserControlDesdeObjetoSimulable(objetoSimulable);
}
}
} }
catch { /* Consider logging the error or handling it appropriately */ }
} }
// Se cargan los datos de cada UserControl en el StackPanel // Se cargan los datos de cada UserControl en el StackPanel

View File

@ -153,9 +153,17 @@
</ToolBar> </ToolBar>
</ToolBarTray> </ToolBarTray>
<ScrollViewer Grid.Row="1" x:Name="ImagenEnTrabajoScrollViewer" HorizontalScrollBarVisibility="Auto" <ScrollViewer Grid.Row="1" x:Name="ImagenEnTrabajoScrollViewer"
VerticalScrollBarVisibility="Auto" PanningMode="Both"> HorizontalScrollBarVisibility="Auto"
<Canvas x:Name="ImagenEnTrabajoCanvas" Margin="0" Background="Transparent"> VerticalScrollBarVisibility="Auto"
PanningMode="Both"
PreviewKeyDown="ScrollViewer_PreviewKeyDown">
<Canvas x:Name="ImagenEnTrabajoCanvas"
Margin="0"
Background="Transparent"
Focusable="True"
FocusVisualStyle="{x:Null}"
KeyDown="Canvas_KeyDown">
<!-- Agregar Background="Transparent" para que capture los eventos del mouse y --> <!-- Agregar Background="Transparent" para que capture los eventos del mouse y -->
<!-- asegurar que el Canvas reciba los eventos del botón derecho --> <!-- asegurar que el Canvas reciba los eventos del botón derecho -->
<Canvas.RenderTransform> <Canvas.RenderTransform>
@ -248,8 +256,12 @@
ResizeDirection="Rows" VerticalAlignment="Center" /> ResizeDirection="Rows" VerticalAlignment="Center" />
<!-- PanelEdicion --> <!-- PanelEdicion -->
<xctk:PropertyGrid Grid.Row="4" Margin="5" x:Name="PanelEdicion" AutoGenerateProperties="False" <xctk:PropertyGrid Grid.Row="4"
ShowDescriptionByTooltip="True"> Margin="5"
x:Name="PanelEdicion"
AutoGenerateProperties="False"
ShowDescriptionByTooltip="True"
FocusManager.IsFocusScope="True">
<xctk:PropertyGrid.EditorDefinitions> <xctk:PropertyGrid.EditorDefinitions>
<!-- String --> <!-- String -->

View File

@ -151,6 +151,7 @@ namespace CtrEditor
// y no en otros controles // y no en otros controles
if (e.Source == ImagenEnTrabajoCanvas || e.Source == imagenDeFondo) if (e.Source == ImagenEnTrabajoCanvas || e.Source == imagenDeFondo)
{ {
ImagenEnTrabajoCanvas.Focus(); // Asegurar que el canvas tiene el foco
_isDraggingCanvas = true; _isDraggingCanvas = true;
_lastMousePosition = e.GetPosition(ImagenEnTrabajoScrollViewer); _lastMousePosition = e.GetPosition(ImagenEnTrabajoScrollViewer);
ImagenEnTrabajoCanvas.CaptureMouse(); ImagenEnTrabajoCanvas.CaptureMouse();
@ -259,6 +260,7 @@ namespace CtrEditor
private void ListaOs_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e) private void ListaOs_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{ {
ImagenEnTrabajoCanvas.Focus(); // Asegurar que el canvas tiene el foco
UserControlFactory.LimpiarPropiedadesosDatos(PanelEdicion); UserControlFactory.LimpiarPropiedadesosDatos(PanelEdicion);
if (e.AddedItems.Count > 0 && e.AddedItems[0] is osBase selectedObject) if (e.AddedItems.Count > 0 && e.AddedItems[0] is osBase selectedObject)
@ -285,25 +287,73 @@ namespace CtrEditor
private void MainWindow_KeyDown(object sender, KeyEventArgs e) private void MainWindow_KeyDown(object sender, KeyEventArgs e)
{ {
// Only force canvas focus if PanelEdicion doesn't have focus
if (!PanelEdicion.IsKeyboardFocusWithin)
{
if (!ImagenEnTrabajoCanvas.IsFocused)
{
ImagenEnTrabajoCanvas.Focus();
}
}
if (DataContext is MainViewModel viewModel) if (DataContext is MainViewModel viewModel)
{ {
if (e.Key == Key.Delete) if (e.Key == Key.Delete)
{ {
viewModel.EliminarObjetoSeleccionado(); _objectManager.EliminarObjetosSeleccionados();
e.Handled = true; e.Handled = true;
} }
else if (e.Key == Key.Escape) else if (e.Key == Key.Escape)
{ {
// Limpiar la selección en el ListBox
viewModel.SelectedItemOsList = null; viewModel.SelectedItemOsList = null;
// Limpiar la selección múltiple
_objectManager.ClearSelection(); _objectManager.ClearSelection();
_objectManager.RemoveResizeRectangles(); _objectManager.RemoveResizeRectangles();
e.Handled = true; e.Handled = true;
} }
else if (_objectManager.SelectedObjects.Any())
{
const float moveDistance = 0.1f;
switch (e.Key)
{
case Key.Left:
MoveSelectedObjects(-moveDistance, 0);
e.Handled = true;
break;
case Key.Right:
MoveSelectedObjects(moveDistance, 0);
e.Handled = true;
break;
case Key.Up:
MoveSelectedObjects(0, -moveDistance);
e.Handled = true;
break;
case Key.Down:
MoveSelectedObjects(0, moveDistance);
e.Handled = true;
break;
}
}
}
}
private void MoveSelectedObjects(float deltaX, float deltaY)
{
// Mover todos los objetos primero
foreach (var obj in _objectManager.SelectedObjects)
{
obj.Left += deltaX;
obj.Top += deltaY;
}
// Forzar una actualización del layout antes de actualizar los visuales
ImagenEnTrabajoCanvas.UpdateLayout();
// Ahora actualizar los visuales de selección
_objectManager.UpdateSelectionVisuals();
if (DataContext is MainViewModel viewModel)
{
viewModel.HasUnsavedChanges = true;
} }
} }
@ -377,6 +427,7 @@ namespace CtrEditor
if ((e.Source == ImagenEnTrabajoCanvas || e.Source == imagenDeFondo || e.Source is UserControl) && if ((e.Source == ImagenEnTrabajoCanvas || e.Source == imagenDeFondo || e.Source is UserControl) &&
DataContext is MainViewModel viewModel) DataContext is MainViewModel viewModel)
{ {
ImagenEnTrabajoCanvas.Focus(); // Asegurar que el canvas tiene el foco
e.Handled = true; e.Handled = true;
ShowContextMenu(e.GetPosition(ImagenEnTrabajoCanvas)); ShowContextMenu(e.GetPosition(ImagenEnTrabajoCanvas));
} }
@ -440,6 +491,68 @@ namespace CtrEditor
contextMenu.IsOpen = true; contextMenu.IsOpen = true;
} }
} }
private void Canvas_KeyDown(object sender, KeyEventArgs e)
{
// Only handle if PanelEdicion doesn't have focus
if (!PanelEdicion.IsKeyboardFocusWithin)
{
HandleKeyDown(e);
}
}
private void ScrollViewer_PreviewKeyDown(object sender, KeyEventArgs e)
{
// Only handle if PanelEdicion doesn't have focus
if (!PanelEdicion.IsKeyboardFocusWithin &&
(e.Key == Key.Left || e.Key == Key.Right || e.Key == Key.Up || e.Key == Key.Down))
{
HandleKeyDown(e);
e.Handled = true;
}
}
private void HandleKeyDown(KeyEventArgs e)
{
if (DataContext is MainViewModel viewModel)
{
if (e.Key == Key.Delete)
{
_objectManager.EliminarObjetosSeleccionados(); // Cambiar aquí
e.Handled = true;
}
else if (e.Key == Key.Escape)
{
viewModel.SelectedItemOsList = null;
_objectManager.ClearSelection();
_objectManager.RemoveResizeRectangles();
e.Handled = true;
}
else if (_objectManager.SelectedObjects.Any())
{
const float moveDistance = 0.01f;
switch (e.Key)
{
case Key.Left:
MoveSelectedObjects(-moveDistance, 0);
e.Handled = true;
break;
case Key.Right:
MoveSelectedObjects(moveDistance, 0);
e.Handled = true;
break;
case Key.Up:
MoveSelectedObjects(0, -moveDistance);
e.Handled = true;
break;
case Key.Down:
MoveSelectedObjects(0, moveDistance);
e.Handled = true;
break;
}
}
}
}
} }
public class FloatValidationRule : ValidationRule public class FloatValidationRule : ValidationRule

View File

@ -7,10 +7,12 @@ namespace CtrEditor
public class ObjectAlignment public class ObjectAlignment
{ {
private readonly ObservableCollection<osBase> _selectedObjects; private readonly ObservableCollection<osBase> _selectedObjects;
private readonly osBase _referenceObject;
public ObjectAlignment(ObservableCollection<osBase> selectedObjects) public ObjectAlignment(ObservableCollection<osBase> selectedObjects, osBase referenceObject = null)
{ {
_selectedObjects = selectedObjects; _selectedObjects = selectedObjects;
_referenceObject = referenceObject ?? selectedObjects.FirstOrDefault();
} }
public void AlignLeft() public void AlignLeft()
@ -83,31 +85,27 @@ namespace CtrEditor
{ {
if (_selectedObjects.Count <= 2) return; if (_selectedObjects.Count <= 2) return;
// Ordenar objetos por su centro X real (considerando rotación)
var objectsWithCenters = _selectedObjects var objectsWithCenters = _selectedObjects
.Select(obj => new .Select(obj => new
{ {
Object = obj, Object = obj,
Center = GetObjectCenter(obj) Center = GetObjectCenter(obj),
Dimensions = GetEffectiveDimensions(obj)
}) })
.OrderBy(x => x.Center.X) .OrderBy(x => x.Center.X)
.ToList(); .ToList();
// Calcular el espacio total y el espaciado
float leftMost = (float)objectsWithCenters.First().Center.X; float leftMost = (float)objectsWithCenters.First().Center.X;
float rightMost = (float)objectsWithCenters.Last().Center.X; float rightMost = (float)objectsWithCenters.Last().Center.X;
float totalWidth = rightMost - leftMost; float totalDistance = rightMost - leftMost;
float spacing = totalWidth / (_selectedObjects.Count - 1); float spacing = totalDistance / (_selectedObjects.Count - 1);
// Distribuir objetos basados en sus centros
for (int i = 1; i < objectsWithCenters.Count - 1; i++) for (int i = 1; i < objectsWithCenters.Count - 1; i++)
{ {
var obj = objectsWithCenters[i].Object; var obj = objectsWithCenters[i];
var targetCenterX = leftMost + (spacing * i); var targetX = leftMost + (spacing * i);
var currentCenter = GetObjectCenter(obj); float deltaX = (float)(targetX - obj.Center.X);
var deltaX = targetCenterX - currentCenter.X; obj.Object.Left += deltaX;
obj.Left += (float)deltaX;
} }
} }
@ -115,31 +113,27 @@ namespace CtrEditor
{ {
if (_selectedObjects.Count <= 2) return; if (_selectedObjects.Count <= 2) return;
// Ordenar objetos por su centro Y real (considerando rotación)
var objectsWithCenters = _selectedObjects var objectsWithCenters = _selectedObjects
.Select(obj => new .Select(obj => new
{ {
Object = obj, Object = obj,
Center = GetObjectCenter(obj) Center = GetObjectCenter(obj),
Dimensions = GetEffectiveDimensions(obj)
}) })
.OrderBy(x => x.Center.Y) .OrderBy(x => x.Center.Y)
.ToList(); .ToList();
// Calcular el espacio total y el espaciado
float topMost = (float)objectsWithCenters.First().Center.Y; float topMost = (float)objectsWithCenters.First().Center.Y;
float bottomMost = (float)objectsWithCenters.Last().Center.Y; float bottomMost = (float)objectsWithCenters.Last().Center.Y;
float totalHeight = bottomMost - topMost; float totalDistance = bottomMost - topMost;
float spacing = totalHeight / (_selectedObjects.Count - 1); float spacing = totalDistance / (_selectedObjects.Count - 1);
// Distribuir objetos basados en sus centros
for (int i = 1; i < objectsWithCenters.Count - 1; i++) for (int i = 1; i < objectsWithCenters.Count - 1; i++)
{ {
var obj = objectsWithCenters[i].Object; var obj = objectsWithCenters[i];
var targetCenterY = topMost + (spacing * i); var targetY = topMost + (spacing * i);
var currentCenter = GetObjectCenter(obj); float deltaY = (float)(targetY - obj.Center.Y);
var deltaY = targetCenterY - currentCenter.Y; obj.Object.Top += deltaY;
obj.Top += (float)deltaY;
} }
} }
@ -147,10 +141,11 @@ namespace CtrEditor
{ {
if (_selectedObjects.Count <= 1) return; if (_selectedObjects.Count <= 1) return;
float averageWidth = _selectedObjects.Average(obj => obj.Ancho); float referenceWidth = GetEffectiveDimensions(_referenceObject).Width;
foreach (var obj in _selectedObjects) foreach (var obj in _selectedObjects.Where(o => o != _referenceObject))
{ {
obj.Ancho = averageWidth; var currentDims = GetEffectiveDimensions(obj);
SetEffectiveDimensions(obj, referenceWidth, currentDims.Height);
} }
} }
@ -158,10 +153,11 @@ namespace CtrEditor
{ {
if (_selectedObjects.Count <= 1) return; if (_selectedObjects.Count <= 1) return;
float averageHeight = _selectedObjects.Average(obj => obj.Alto); float referenceHeight = GetEffectiveDimensions(_referenceObject).Height;
foreach (var obj in _selectedObjects) foreach (var obj in _selectedObjects.Where(o => o != _referenceObject))
{ {
obj.Alto = averageHeight; var currentDims = GetEffectiveDimensions(obj);
SetEffectiveDimensions(obj, currentDims.Width, referenceHeight);
} }
} }
@ -169,8 +165,8 @@ namespace CtrEditor
{ {
if (_selectedObjects.Count <= 1) return; if (_selectedObjects.Count <= 1) return;
float referenceAngle = _selectedObjects.First().Angulo; float referenceAngle = _referenceObject.Angulo;
foreach (var obj in _selectedObjects) foreach (var obj in _selectedObjects.Where(o => o != _referenceObject))
{ {
obj.Angulo = referenceAngle; obj.Angulo = referenceAngle;
} }
@ -181,14 +177,21 @@ namespace CtrEditor
if (_selectedObjects.Count <= 1) return; if (_selectedObjects.Count <= 1) return;
var sortedObjects = _selectedObjects var sortedObjects = _selectedObjects
.OrderBy(obj => obj.Left) .OrderBy(obj => GetObjectCenter(obj).X)
.ToList(); .ToList();
for (int i = 1; i < sortedObjects.Count; i++) for (int i = 1; i < sortedObjects.Count; i++)
{ {
var previousObj = sortedObjects[i - 1]; var previousObj = sortedObjects[i - 1];
var currentObj = sortedObjects[i]; var currentObj = sortedObjects[i];
currentObj.Left = previousObj.Left + previousObj.Ancho; var previousCenter = GetObjectCenter(previousObj);
var currentCenter = GetObjectCenter(currentObj);
var previousDims = GetEffectiveDimensions(previousObj);
float offset = previousDims.Width / 2;
float newX = (float)(previousCenter.X + offset);
float deltaX = (float)(newX - (currentCenter.X - GetEffectiveDimensions(currentObj).Width / 2));
currentObj.Left += deltaX;
} }
} }
@ -197,14 +200,21 @@ namespace CtrEditor
if (_selectedObjects.Count <= 1) return; if (_selectedObjects.Count <= 1) return;
var sortedObjects = _selectedObjects var sortedObjects = _selectedObjects
.OrderBy(obj => obj.Top) .OrderBy(obj => GetObjectCenter(obj).Y)
.ToList(); .ToList();
for (int i = 1; i < sortedObjects.Count; i++) for (int i = 1; i < sortedObjects.Count; i++)
{ {
var previousObj = sortedObjects[i - 1]; var previousObj = sortedObjects[i - 1];
var currentObj = sortedObjects[i]; var currentObj = sortedObjects[i];
currentObj.Top = previousObj.Top + previousObj.Alto; var previousCenter = GetObjectCenter(previousObj);
var currentCenter = GetObjectCenter(currentObj);
var previousDims = GetEffectiveDimensions(previousObj);
float offset = previousDims.Height / 2;
float newY = (float)(previousCenter.Y + offset);
float deltaY = (float)(newY - (currentCenter.Y - GetEffectiveDimensions(currentObj).Height / 2));
currentObj.Top += deltaY;
} }
} }
@ -214,10 +224,8 @@ namespace CtrEditor
float centerX = obj.Left + (obj.Ancho / 2); float centerX = obj.Left + (obj.Ancho / 2);
float centerY = obj.Top + (obj.Alto / 2); float centerY = obj.Top + (obj.Alto / 2);
// Si el objeto está rotado, calcular el centro real
if (obj.Angulo != 0) if (obj.Angulo != 0)
{ {
// Obtener el centro después de la rotación
var rotatedX = obj.Left + (Math.Cos(angleRad) * obj.Ancho / 2 - Math.Sin(angleRad) * obj.Alto / 2); var rotatedX = obj.Left + (Math.Cos(angleRad) * obj.Ancho / 2 - Math.Sin(angleRad) * obj.Alto / 2);
var rotatedY = obj.Top + (Math.Sin(angleRad) * obj.Ancho / 2 + Math.Cos(angleRad) * obj.Alto / 2); var rotatedY = obj.Top + (Math.Sin(angleRad) * obj.Ancho / 2 + Math.Cos(angleRad) * obj.Alto / 2);
@ -226,5 +234,35 @@ namespace CtrEditor
return new Point(centerX, centerY); return new Point(centerX, centerY);
} }
private bool IsObjectVertical(osBase obj)
{
double normalizedAngle = obj.Angulo % 180;
if (normalizedAngle < 0) normalizedAngle += 180;
return normalizedAngle >= 45 && normalizedAngle < 135;
}
private (float Width, float Height) GetEffectiveDimensions(osBase obj)
{
if (IsObjectVertical(obj))
{
return (obj.Alto, obj.Ancho);
}
return (obj.Ancho, obj.Alto);
}
private void SetEffectiveDimensions(osBase obj, float width, float height)
{
if (IsObjectVertical(obj))
{
obj.Alto = width;
obj.Ancho = height;
}
else
{
obj.Ancho = width;
obj.Alto = height;
}
}
} }
} }

View File

@ -75,9 +75,25 @@ namespace CtrEditor
_canvas = canvas; _canvas = canvas;
} }
private void PurgeDeletedObjects()
{
var deletedObjects = _selectedObjects.Where(obj =>
obj.VisualRepresentation == null ||
!_canvas.Children.Contains(obj.VisualRepresentation)).ToList();
foreach (var obj in deletedObjects)
{
DeselectObject(obj);
}
}
public ObservableCollection<osBase> SelectedObjects public ObservableCollection<osBase> SelectedObjects
{ {
get => _selectedObjects; get
{
PurgeDeletedObjects();
return _selectedObjects;
}
private set private set
{ {
_selectedObjects = value; _selectedObjects = value;
@ -87,10 +103,15 @@ namespace CtrEditor
public void UpdateSelectionVisuals() public void UpdateSelectionVisuals()
{ {
PurgeDeletedObjects();
// Asegurarse de que el canvas haya actualizado su layout
_canvas.UpdateLayout();
RemoveResizeRectangles(); RemoveResizeRectangles();
if (_selectedObjects.Any()) if (_selectedObjects.Any())
{ {
AddResizeRectangles(_selectedObjects); AddResizeRectangles(_selectedObjects);
UpdateSelectionHighlights();
} }
} }
@ -304,6 +325,7 @@ namespace CtrEditor
private void HandleObjectSelection(UserControl userControl, osBase datos) private void HandleObjectSelection(UserControl userControl, osBase datos)
{ {
PurgeDeletedObjects();
var viewModel = _mainWindow.DataContext as MainViewModel; var viewModel = _mainWindow.DataContext as MainViewModel;
if (viewModel == null) return; if (viewModel == null) return;
@ -389,22 +411,14 @@ namespace CtrEditor
} }
else else
{ {
// Agregar destacados de selección para los objetos ya seleccionados UpdateAllSelectionHighlights();
foreach (var obj in _selectedObjects)
{
if (obj.VisualRepresentation != null)
{
AddSelectionHighlight(obj.VisualRepresentation);
}
}
} }
} }
private void AddSelectionHighlight(UserControl userControl) private void AddSelectionHighlight(UserControl userControl, bool isReference = false)
{ {
if (userControl == null) return; if (userControl == null) return;
// Primero remover cualquier highlight existente para este control
RemoveSelectionHighlight(userControl); RemoveSelectionHighlight(userControl);
Rect boundingBox = VisualTreeHelper.GetDescendantBounds(userControl); Rect boundingBox = VisualTreeHelper.GetDescendantBounds(userControl);
@ -416,8 +430,10 @@ namespace CtrEditor
Width = transformedBoundingBox.Width, Width = transformedBoundingBox.Width,
Height = transformedBoundingBox.Height, Height = transformedBoundingBox.Height,
Fill = Brushes.Transparent, Fill = Brushes.Transparent,
Stroke = new SolidColorBrush(Color.FromArgb(180, 255, 0, 0)), Stroke = new SolidColorBrush(isReference ?
StrokeThickness = 2, Color.FromArgb(180, 128, 0, 128) : // Purple for reference
Color.FromArgb(180, 255, 0, 0)), // Red for others
StrokeThickness = isReference ? 3 : 2, // Más grueso para el de referencia
Tag = "SelectionHighlight", Tag = "SelectionHighlight",
IsHitTestVisible = false IsHitTestVisible = false
}; };
@ -449,21 +465,28 @@ namespace CtrEditor
_selectionHighlightPairs.Clear(); _selectionHighlightPairs.Clear();
} }
private void UpdateSelectionHighlights() private void UpdateAllSelectionHighlights()
{ {
if (_mainWindow.DataContext is MainViewModel viewModel && viewModel.IsMultiSelectionActive) if (_mainWindow.DataContext is MainViewModel viewModel && viewModel.IsMultiSelectionActive)
{ {
RemoveAllSelectionHighlights(); RemoveAllSelectionHighlights();
bool isFirst = true;
foreach (var selectedObject in _selectedObjects) foreach (var selectedObject in _selectedObjects)
{ {
if (selectedObject.VisualRepresentation != null) if (selectedObject.VisualRepresentation != null)
{ {
AddSelectionHighlight(selectedObject.VisualRepresentation); AddSelectionHighlight(selectedObject.VisualRepresentation, isFirst);
isFirst = false;
} }
} }
} }
} }
private void UpdateSelectionHighlights()
{
UpdateAllSelectionHighlights();
}
private void UserControl_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) private void UserControl_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{ {
if (!_isDrawingCanvas) if (!_isDrawingCanvas)
@ -540,6 +563,7 @@ namespace CtrEditor
private void HandleDrag(Point currentPosition) private void HandleDrag(Point currentPosition)
{ {
PurgeDeletedObjects();
var dx = currentPosition.X - _startPointUserControl.X; var dx = currentPosition.X - _startPointUserControl.X;
var dy = currentPosition.Y - _startPointUserControl.Y; var dy = currentPosition.Y - _startPointUserControl.Y;
@ -553,7 +577,7 @@ namespace CtrEditor
} }
_startPointUserControl = currentPosition; _startPointUserControl = currentPosition;
UpdateSelectionHighlights(); UpdateAllSelectionHighlights();
} }
private void UserControl_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) private void UserControl_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
@ -571,6 +595,7 @@ namespace CtrEditor
RemoveAllSelectionHighlights(); // Remover antes de actualizar RemoveAllSelectionHighlights(); // Remover antes de actualizar
UpdateSelectionVisuals(); UpdateSelectionVisuals();
UpdateAllSelectionHighlights();
// Restaurar los rectángulos de selección si es necesario // Restaurar los rectángulos de selección si es necesario
if (_mainWindow.DataContext is MainViewModel viewModel && viewModel.IsMultiSelectionActive) if (_mainWindow.DataContext is MainViewModel viewModel && viewModel.IsMultiSelectionActive)
@ -689,6 +714,7 @@ namespace CtrEditor
private void HandleResize(Point currentPosition, HandleMode mode) private void HandleResize(Point currentPosition, HandleMode mode)
{ {
PurgeDeletedObjects();
RemoveAllSelectionHighlights(); // Remover antes de redimensionar RemoveAllSelectionHighlights(); // Remover antes de redimensionar
foreach (var selectedObject in _selectedObjects) foreach (var selectedObject in _selectedObjects)
@ -699,7 +725,7 @@ namespace CtrEditor
} }
_startPointUserControl = currentPosition; _startPointUserControl = currentPosition;
UpdateSelectionHighlights(); UpdateAllSelectionHighlights();
} }
private void HandleResizeForObject(osBase obj, Point currentPosition, bool activateSizeWidth, bool activateSizeHeight) private void HandleResizeForObject(osBase obj, Point currentPosition, bool activateSizeWidth, bool activateSizeHeight)
@ -712,6 +738,7 @@ namespace CtrEditor
private void HandleRotation(Point currentPosition) private void HandleRotation(Point currentPosition)
{ {
PurgeDeletedObjects();
RemoveAllSelectionHighlights(); // Remover antes de rotar RemoveAllSelectionHighlights(); // Remover antes de rotar
// Calcular el ángulo respecto al centro del bounding box que contiene todos los objetos seleccionados // Calcular el ángulo respecto al centro del bounding box que contiene todos los objetos seleccionados
@ -733,14 +760,15 @@ namespace CtrEditor
_lastAngle = (float)angle; _lastAngle = (float)angle;
} }
UpdateSelectionHighlights(); UpdateAllSelectionHighlights();
} }
public void AlignObjects(AlignmentType alignmentType) public void AlignObjects(AlignmentType alignmentType)
{ {
PurgeDeletedObjects();
if (_selectedObjects.Count <= 1) return; if (_selectedObjects.Count <= 1) return;
var alignment = new ObjectAlignment(_selectedObjects); var alignment = new ObjectAlignment(_selectedObjects, _selectedObjects.FirstOrDefault());
switch (alignmentType) switch (alignmentType)
{ {
@ -788,5 +816,32 @@ namespace CtrEditor
// Update the selection visuals after alignment // Update the selection visuals after alignment
UpdateSelectionVisuals(); UpdateSelectionVisuals();
} }
public void EliminarObjetosSeleccionados()
{
if (_selectedObjects.Count == 0) return;
var viewModel = _mainWindow.DataContext as MainViewModel;
if (viewModel == null) return;
// Crear una copia de la lista para evitar modificaciones durante la iteración
var objectsToRemove = _selectedObjects.ToList();
foreach (var obj in objectsToRemove)
{
viewModel.RemoverObjetoSimulable(obj);
}
// Limpiar la selección y actualizar la interfaz
ClearSelection();
RemoveResizeRectangles();
RemoveAllSelectionHighlights();
// Actualizar el estado de cambios sin guardar
if (viewModel != null)
{
viewModel.HasUnsavedChanges = true;
}
}
} }
} }

View File

@ -0,0 +1,162 @@
using CtrEditor.ObjetosSim;
using LibS7Adv;
using Newtonsoft.Json;
using System.Collections.ObjectModel;
using System.Windows;
using CtrEditor.Simulacion;
using System.IO;
namespace CtrEditor.Serialization
{
public class StateSerializer
{
private readonly DatosDeTrabajo _datosDeTrabajo;
private readonly MainViewModel _mainViewModel;
private readonly SimulationManagerFP _simulationManager;
public StateSerializer(MainViewModel mainViewModel, DatosDeTrabajo datosDeTrabajo, SimulationManagerFP simulationManager)
{
_mainViewModel = mainViewModel;
_datosDeTrabajo = datosDeTrabajo;
_simulationManager = simulationManager;
}
public void SaveState(string selectedImage)
{
if (selectedImage != null)
{
_mainViewModel.StopSimulation();
_mainViewModel.DisconnectPLC();
var objetosSimulables = new ObservableCollection<osBase>();
var objetosSimulablesAllPages = new ObservableCollection<osBase>();
foreach (var obj in _mainViewModel.ObjetosSimulables)
{
obj.SalvarDatosNoSerializables();
if (!obj.Enable_On_All_Pages)
objetosSimulables.Add(obj);
else
objetosSimulablesAllPages.Add(obj);
}
// Save current page objects
var dataToSerialize = new SimulationData
{
ObjetosSimulables = objetosSimulables,
UnitConverter = PixelToMeter.Instance.calc,
PLC_ConnectionData = _mainViewModel.PLCViewModel
};
var path = _datosDeTrabajo.ObtenerPathImagenConExtension(selectedImage, ".json");
if (path != null)
SerializeAndSave(dataToSerialize, path);
// Save all pages objects
path = _datosDeTrabajo.ObtenerPathAllPages(".json");
if (path != null)
SerializeAndSave(objetosSimulablesAllPages, path);
// Restore original properties
foreach (var obj in _mainViewModel.ObjetosSimulables)
obj.RestaurarDatosNoSerializables();
}
}
public void LoadState(string selectedImage)
{
try
{
_mainViewModel.StopSimulation();
_mainViewModel.DisconnectPLC();
_mainViewModel.ObjetosSimulables.Clear();
_simulationManager.Clear();
if (selectedImage != null)
{
var settings = GetJsonSerializerSettings();
LoadCurrentPageState(selectedImage, settings);
LoadAllPagesState(settings);
// Create UserControls for all loaded objects
foreach (var objetoSimulable in _mainViewModel.ObjetosSimulables)
{
if (objetoSimulable != null)
{
objetoSimulable.CheckData();
_mainViewModel.CrearUserControlDesdeObjetoSimulable(objetoSimulable);
}
}
}
}
catch (Exception ex)
{
MessageBox.Show($"Error loading state: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void LoadCurrentPageState(string selectedImage, JsonSerializerSettings settings)
{
string jsonPath = _datosDeTrabajo.ObtenerPathImagenConExtension(selectedImage, ".json");
if (File.Exists(jsonPath))
{
string jsonString = File.ReadAllText(jsonPath);
var simulationData = JsonConvert.DeserializeObject<SimulationData>(jsonString, settings);
if (simulationData != null)
{
if (simulationData.ObjetosSimulables is not null)
_mainViewModel.ObjetosSimulables = simulationData.ObjetosSimulables;
if (simulationData.PLC_ConnectionData is not null)
_mainViewModel.PLCViewModel = simulationData.PLC_ConnectionData;
else
_mainViewModel.PLCViewModel = new PLCViewModel();
PixelToMeter.Instance.calc = simulationData.UnitConverter;
}
}
}
private void LoadAllPagesState(JsonSerializerSettings settings)
{
string jsonPath = _datosDeTrabajo.ObtenerPathAllPages(".json");
if (File.Exists(jsonPath))
{
string jsonString = File.ReadAllText(jsonPath);
var objetosSimulablesAllPages = JsonConvert.DeserializeObject<ObservableCollection<osBase>>(jsonString, settings);
if (objetosSimulablesAllPages != null)
{
foreach (var obj in objetosSimulablesAllPages)
_mainViewModel.ObjetosSimulables.Add(obj);
}
}
}
private void SerializeAndSave(object objectToSerialize, string path)
{
// Create backup if file exists
if (File.Exists(path))
{
var backupPath = Path.ChangeExtension(path, ".bak");
File.Copy(path, backupPath, true);
}
var settings = GetJsonSerializerSettings();
var serializedData = JsonConvert.SerializeObject(objectToSerialize, settings);
File.WriteAllText(path, serializedData);
}
private JsonSerializerSettings GetJsonSerializerSettings()
{
return new JsonSerializerSettings
{
Formatting = Formatting.Indented,
NullValueHandling = NullValueHandling.Ignore,
TypeNameHandling = TypeNameHandling.Auto,
ObjectCreationHandling = ObjectCreationHandling.Replace,
ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor
};
}
}
}