Compare commits

..

26 Commits

Author SHA1 Message Date
Miguel 288635b9bf Funcionando con Motor Aether 2024-05-25 14:38:36 +02:00
Miguel e54dba175a Cambios de directorio 2024-05-25 12:53:34 +02:00
Miguel 25da24d042 Algunas mejoras y cambios de directorio 2024-05-25 12:53:20 +02:00
Miguel ecf433cecc Con RuedaFonica agragada 2024-05-23 19:56:14 +02:00
Miguel 0e174fc308 Actualizado a CommunityToolkit.Mvvm.ComponentModel para todos los UserControl 2024-05-22 19:21:39 +02:00
Miguel 260362dc24 Agregando simulacion a los transportes 2024-05-22 11:19:31 +02:00
Miguel f090722de0 Incluyendo Trasnporte Curve y la biblioteca CommunityToolkit.Mvvm 2024-05-21 12:52:44 +02:00
Miguel 334b1a2fd8 Creado boton Duplicar 2024-05-20 14:05:34 +02:00
Miguel 0d18cae40a Cambiada la lista de Funciones para que muestre Nombre en vez del nombre de la clase 2024-05-19 21:38:57 +02:00
Miguel 1bccd5d33b con UserControl Photecell creada y el boton de Eliminar UserControl 2024-05-19 19:56:08 +02:00
Miguel 6155f8475e UserControl bajo carpeta de UserControl 2024-05-18 23:14:46 +02:00
Miguel f458a031c5 Creada la clase simBase 2024-05-18 17:42:27 +02:00
Miguel 2e8c3b7d83 Con remocion del World de simulacion 2024-05-18 15:39:46 +02:00
Miguel 9cf86d001e Falta remover los objetos del World de simulacion 2024-05-18 15:35:46 +02:00
Miguel 9ed8a0b7bd Mejorado el sistema de SaveStateObjetosSimulables y trabajando en el usercontrol Descarte 2024-05-18 14:58:41 +02:00
Miguel ebe7986142 Utilizando MainWindow desde MainViewModel 2024-05-18 11:49:02 +02:00
Miguel 664d325de8 Limpiando el codigo de MainWindow y MainViewModel 2024-05-18 10:53:04 +02:00
Miguel 77ff643642 Mejorado osBase 2024-05-16 18:45:14 +02:00
Miguel f3e5992433 Agregado de toolbar a MainWindow 2024-05-15 11:20:09 +02:00
Miguel 794a5898c5 Mejora con la implementacion de la funcion que calcula el porcentaje de superfecie compartida por una botella y un transporte 2024-05-15 09:59:48 +02:00
Miguel 494f960628 Tanque funcionando 2024-05-14 18:17:46 +02:00
Miguel 6da98e621b Funcionando con el Save y el link ente los Motor y los TransportesGuia 2024-05-14 17:10:32 +02:00
Miguel b224070690 sin conseguir capturar los eventos del boton 2024-05-14 14:15:10 +02:00
Miguel 9c8eb0b348 Trabajando en la simulacion del Motor con el PLC 2024-05-14 12:04:22 +02:00
Miguel 56a2e994a2 Primera revision. trabajando en la aplicacion de velocidad sobre los transportes. 2024-05-14 08:15:54 +02:00
Miguel 2bfd83ef3b Antes de Implementar FarseerPhysics 2024-05-11 20:55:44 +02:00
75 changed files with 6344 additions and 1458 deletions

View File

@ -6,9 +6,100 @@ using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;
using System.Windows;
using System.Windows.Media;
namespace CtrEditor.Convertidores
{
public class HalfWidthConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is double width)
{
return (width / 2.0) - (double.Parse(parameter.ToString()) / 2.0);
}
return 0;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class WidthPercentageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is double width)
{
return width * 0.25;
}
return 0;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class LevelToHeightMultiConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values[0] is float level && values[1] is double containerHeight)
{
return containerHeight * (level / 100);
}
return 0;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class BrushToColorNameConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is SolidColorBrush brush)
{
if (brush == Brushes.Red) return "Rojo";
if (brush == Brushes.Blue) return "Azul";
if (brush == Brushes.Black) return "Negro";
if (brush == Brushes.Green) return "Verde";
if (brush == Brushes.Gray) return "Gris";
}
return "Unknown";
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is string colorName)
{
switch (colorName)
{
case "Rojo":
return Brushes.Red;
case "Azul":
return Brushes.Blue;
case "Negro":
return Brushes.Black;
case "Verde":
return Brushes.Green;
case "Gris":
return Brushes.Gray;
}
}
return Brushes.Transparent;
}
}
public class PixelToMeter
{
// Instancia privada estática, parte del patrón Singleton
@ -33,22 +124,55 @@ namespace CtrEditor.Convertidores
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
float meters = (float)value;
float factor = 1;
if (parameter != null)
if (parameter.ToString() == "0.5") factor = 0.5f;
else if (parameter.ToString() == "-0.5") factor = -0.5f;
if (value == null) return 0; // Aseguramos que el valor no sea nulo
// Convertimos el valor de entrada en un número flotante
float meters = System.Convert.ToSingle(value);
float factor = 1; // Valor por defecto del factor
// Si el parámetro no es nulo, intentamos convertirlo a float
if (parameter != null)
{
string paramStr = parameter.ToString();
// Normalizamos el parámetro para asegurar que el punto sea el separador decimal
paramStr = paramStr.Replace(',', '.');
// Utilizamos CultureInfo.InvariantCulture para evitar problemas con el separador decimal
if (float.TryParse(paramStr, NumberStyles.Any, CultureInfo.InvariantCulture, out float parsedFactor))
{
factor = parsedFactor; // Asignamos el factor parseado si la conversión es exitosa
}
}
// Calculamos los píxeles llamando a la instancia de PixelToMeter y multiplicamos por el factor
return PixelToMeter.Instance.calc.MetersToPixels(meters) * factor;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
float pixels = (float)value;
float factor = 1;
if (value == null) return 0; // Aseguramos que el valor no sea nulo
// Convertimos el valor de entrada en un número flotante
float pixels = System.Convert.ToSingle(value);
float factor = 1; // Valor por defecto del factor
// Si el parámetro no es nulo, intentamos convertirlo a float
if (parameter != null)
if (parameter.ToString() == "0.5") factor = 0.5f;
else if (parameter.ToString() == "-0.5") factor = -0.5f;
{
string paramStr = parameter.ToString();
// Normalizamos el parámetro para asegurar que el punto sea el separador decimal
paramStr = paramStr.Replace(',', '.');
// Utilizamos CultureInfo.InvariantCulture para evitar problemas con el separador decimal
if (float.TryParse(paramStr, NumberStyles.Any, CultureInfo.InvariantCulture, out float parsedFactor))
{
factor = parsedFactor; // Asignamos el factor parseado si la conversión es exitosa
}
}
return PixelToMeter.Instance.calc.PixelsToMeters(pixels) * factor;
}
@ -92,6 +216,28 @@ namespace CtrEditor.Convertidores
}
}
public class DoubleToFormattedStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is double floatValue)
{
return floatValue.ToString("0.00", culture); // Formatear a dos decimales
}
return value; // Devolver el valor original si no es un float
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is string stringValue && double.TryParse(stringValue, NumberStyles.Float, culture, out double result))
{
return result;
}
return value; // Devolver el valor original si no se puede convertir
}
}
public class UnitConverter
{
// La escala representa cuántos metros hay en un píxel

View File

@ -9,14 +9,53 @@
</PropertyGroup>
<ItemGroup>
<None Remove="motor2.png" />
<Compile Remove="ObjetosSim\ucTransporteCurva.xaml.cs" />
<Compile Remove="Simulacion\FPhysics.cs" />
<Compile Remove="Simulacion\GeometrySimulator.cs" />
</ItemGroup>
<ItemGroup>
<None Remove="app2.png" />
<None Remove="CtrEditorE.png" />
<None Remove="Icons\app.256x256.ico" />
<None Remove="Icons\app.png" />
<None Remove="Icons\app2.128x128.ico" />
<None Remove="Icons\app2.256x256.ico" />
<None Remove="Icons\app2.png" />
<None Remove="Icons\borrar.png" />
<None Remove="Icons\connect.png" />
<None Remove="Icons\CtrEditorA.png" />
<None Remove="Icons\CtrEditorC.png" />
<None Remove="Icons\CtrEditorE.png" />
<None Remove="Icons\disconnect.png" />
<None Remove="Icons\duplicate.png" />
<None Remove="Icons\fotocelula.png" />
<None Remove="Icons\save.png" />
<None Remove="Icons\start.png" />
<None Remove="Icons\stop.png" />
<None Remove="imagenes\filler.png" />
<None Remove="imagenes\motorNegro.png" />
<None Remove="imagenes\motorVerde.png" />
<None Remove="motor2.png" />
<None Remove="tank.png" />
</ItemGroup>
<ItemGroup>
<Page Remove="ObjetosSim\ucTransporteCurva.xaml" />
</ItemGroup>
<ItemGroup>
<None Include="Simulacion\FPhysics.cs" />
<None Include="Simulacion\GeometrySimulator.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Aether.Physics2D" Version="2.1.0" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" />
<PackageReference Include="FarseerPhysics" Version="3.5.0" />
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.77" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Ookii.Dialogs.Wpf" Version="5.0.1" />
<PackageReference Include="OpenCvSharp4.Windows" Version="4.9.0.20240103" />
</ItemGroup>
<ItemGroup>
@ -27,7 +66,25 @@
</ItemGroup>
<ItemGroup>
<Resource Include="motor2.png" />
<Resource Include="app2.png" />
<Resource Include="CtrEditorE.png" />
<Resource Include="Icons\app.png" />
<Resource Include="Icons\app2.png" />
<Resource Include="Icons\borrar.png" />
<Resource Include="Icons\connect.png" />
<Resource Include="Icons\CtrEditorA.png" />
<Resource Include="Icons\CtrEditorC.png" />
<Resource Include="Icons\CtrEditorE.png" />
<Resource Include="Icons\disconnect.png" />
<Resource Include="Icons\duplicate.png" />
<Resource Include="Icons\fotocelula.png" />
<Resource Include="Icons\save.png" />
<Resource Include="Icons\start.png" />
<Resource Include="Icons\stop.png" />
<Resource Include="imagenes\filler.png" />
<Resource Include="imagenes\motorNegro.png" />
<Resource Include="imagenes\motorVerde.png" />
<Resource Include="imagenes\tank.png" />
</ItemGroup>
</Project>

BIN
CtrEditorE.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 655 KiB

BIN
Icons/CtrEditorA.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 453 KiB

BIN
Icons/CtrEditorC.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 298 KiB

BIN
Icons/CtrEditorE.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 316 KiB

BIN
Icons/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

BIN
Icons/app2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

BIN
Icons/borrar.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
Icons/connect.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

BIN
Icons/disconnect.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

BIN
Icons/duplicate.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

BIN
Icons/fotocelula.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
Icons/save.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
Icons/start.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
Icons/stop.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -1,154 +1,98 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows.Input;
using Ookii.Dialogs.Wpf;
using System.Collections.ObjectModel;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using static System.Runtime.InteropServices.JavaScript.JSType;
using System.Windows.Threading;
using CtrEditor.ObjetosSim;
using CtrEditor.Siemens;
using System.IO;
// using System.Windows.Forms;
using System.Text.Json.Serialization;
using System.Text.Json;
using Newtonsoft.Json;
using System.Windows.Data;
using System.Windows;
using static System.Resources.ResXFileRef;
using CtrEditor.Convertidores;
using CtrEditor.Simulacion;
using System.Diagnostics;
using System.Reflection;
using CommunityToolkit.Mvvm.ComponentModel;
namespace CtrEditor
{
public class MainViewModel : INotifyPropertyChanged
public partial class MainViewModel : ObservableObject
{
public DatosDeTrabajo datosDeTrabajo { get; }
public ObservableCollection<string> listaImagenes { get; private set; } // Publicación de las claves del diccionario
public ObservableCollection<TipoSimulable> ListaOsBase { get; } = new ObservableCollection<TipoSimulable>();
private ObservableCollection<osBase> _objetosSimulables = new ObservableCollection<osBase>();
public PLCViewModel _plcViewModelData;
public Stopwatch stopwatch_Sim;
private SimulationManager simulationManager = new SimulationManager();
private double stopwatch_SimPLC_last;
private double stopwatch_SimModel_last;
private float TiempoDesdeStartSimulacion;
private bool Debug_SimulacionCreado = false;
public SimulationManagerFP simulationManager = new SimulationManagerFP();
private readonly DispatcherTimer _timerSimulacion;
[ObservableProperty]
private DatosDeTrabajo datosDeTrabajo;
[ObservableProperty]
private ObservableCollection<string> listaImagenes; // Publicación de las claves del diccionario
[ObservableProperty]
public ObservableCollection<TipoSimulable> listaOsBase;
public ICommand StartSimulationCommand { get; }
public ICommand StopSimulationCommand { get; }
public ICommand ItemDoubleClickCommand { get; private set; }
public ICommand TBStartSimulationCommand { get; }
public ICommand TBStopSimulationCommand { get; }
public ICommand TBSaveCommand { get; }
public ICommand TBConnectPLCCommand { get; }
public ICommand TBDisconnectPLCCommand { get; }
public ICommand TBEliminarUserControlCommand { get; }
public ICommand TBDuplicarUserControlCommand { get; }
public ICommand OpenWorkDirectoryCommand { get; }
// Evento que se dispara cuando se selecciona una nueva imagen
public event EventHandler<string> ImageSelected;
public event EventHandler<TickSimulacionEventArgs> TickSimulacion;
public event Action<UserControl> OnUserControlSelected;
public MainViewModel()
// Propiedades
private bool habilitarEliminarUserControl;
private MainWindow mainWindow;
public MainWindow MainWindow { get => mainWindow; set => mainWindow = value; }
[ObservableProperty]
private float canvasLeft;
[ObservableProperty]
private float canvasTop;
[ObservableProperty]
private bool isSimulationRunning;
partial void OnIsSimulationRunningChanged(bool value)
{
OpenWorkDirectoryCommand = new RelayCommand(OpenWorkDirectory);
datosDeTrabajo = new DatosDeTrabajo();
// Inicializa el PLCViewModel
_plcViewModelData = new PLCViewModel();
InitializeTipoSimulableList();
ItemDoubleClickCommand = new ParameterizedRelayCommand(ExecuteDoubleClick);
_timerSimulacion = new DispatcherTimer();
_timerSimulacion.Interval = TimeSpan.FromMilliseconds(20); // ajusta el intervalo según sea necesario
_timerSimulacion.Tick += OnTickSimulacion;
StartSimulationCommand = new RelayCommand(StartSimulation);
StopSimulationCommand = new RelayCommand(StopSimulation);
CommandManager.InvalidateRequerySuggested(); // Notificar que el estado de los comandos ha cambiado
}
public void LoadInitialData()
[ObservableProperty]
private bool isConnected;
partial void OnIsConnectedChanged(bool value)
{
// Suponiendo que "SelectedImage" es una propiedad que al establecerse dispara "ImageSelected"
directorioTrabajo = EstadoPersistente.Instance.directorio;
CommandManager.InvalidateRequerySuggested();
}
private TipoSimulable _selectedItem = null;
public TipoSimulable SelectedItem
{
get => _selectedItem;
set
{
if (_selectedItem != value)
{
_selectedItem = value;
OnPropertyChanged(nameof(SelectedItem)); // Notificar que la propiedad ha cambiado
}
}
}
private void ExecuteDoubleClick(object parameter)
{
if (parameter is TipoSimulable tipoSimulable)
{
// Crear una nueva instancia del osBase correspondiente
osBase? newosBase = UserControlFactory.GetInstanceForType(tipoSimulable.Tipo);
if (newosBase != null)
{
if (CrearUsercontrol(newosBase))
// Añadir el nuevo osBase a la colección de objetos simulables
ObjetosSimulables.Add(newosBase);
}
}
}
private void InitializeTipoSimulableList()
{
var baseType = typeof(osBase);
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(assembly => assembly.GetTypes())
.Where(type => type.IsSubclassOf(baseType) && !type.IsAbstract);
foreach (var type in types)
{
ListaOsBase.Add(new TipoSimulable { Nombre = type.Name, Tipo = type });
}
}
private void StartSimulation()
{
_timerSimulacion.Start();
}
private void StopSimulation()
{
_timerSimulacion.Stop();
}
private void OnTickSimulacion(object sender, EventArgs e)
{
foreach (var objetoSimulable in ObjetosSimulables)
objetoSimulable.UpdateGeometry();
simulationManager.Step((float)_timerSimulacion.Interval.TotalMilliseconds);
foreach (var objetoSimulable in ObjetosSimulables)
objetoSimulable.UpdateControl();
}
//protected virtual void OnTickSimulacion(TickSimulacionEventArgs e)
//{
// TickSimulacion?.Invoke(this, e);
//}
public string directorioTrabajo
{
get => EstadoPersistente.Instance.directorio;
@ -158,62 +102,316 @@ namespace CtrEditor
{
EstadoPersistente.Instance.directorio = value; // Actualizar el estado persistente
EstadoPersistente.Instance.GuardarEstado(); // Guardar el estado actualizado
datosDeTrabajo.CargarImagenes();
listaImagenes = new ObservableCollection<string>(datosDeTrabajo.Imagenes.Keys); // Actualizar claves
DatosDeTrabajo.CargarImagenes();
ListaImagenes = new ObservableCollection<string>(DatosDeTrabajo.Imagenes.Keys); // Actualizar claves
SelectedImage = null;
if (listaImagenes.FirstOrDefault() != null)
SelectedImage = listaImagenes.FirstOrDefault();
if (ListaImagenes.FirstOrDefault() != null)
SelectedImage = ListaImagenes.FirstOrDefault();
OnPropertyChanged(nameof(directorioTrabajo)); // Notificar el cambio de propiedad
OnPropertyChanged(nameof(listaImagenes)); // Notificar que la lista de imágenes ha cambiado
OnPropertyChanged(nameof(ListaImagenes)); // Notificar que la lista de imágenes ha cambiado
}
}
}
[ObservableProperty]
private PLCViewModel pLCViewModel;
public PLCViewModel PLCViewModel
[ObservableProperty]
private string selectedImage;
partial void OnSelectedImageChanged(string value)
{
get { return _plcViewModelData; }
set
if (value != null)
{
_plcViewModelData = value;
OnPropertyChanged(nameof(PLCViewModel));
StopSimulation();
// SaveStateObjetosSimulables(); // Guarda el estado antes de cambiar la imagen
ImageSelected?.Invoke(this, datosDeTrabajo.Imagenes[value]); // Dispara el evento con la nueva ruta de imagen
LoadStateObjetosSimulables();
}
}
private string _selectedImage = null;
public string SelectedImage
[ObservableProperty]
private osBase selectedItemOsList;
partial void OnSelectedItemOsListChanged(osBase value)
{
get => _selectedImage;
set
if (value != null)
habilitarEliminarUserControl = true;
else
habilitarEliminarUserControl = false;
}
[ObservableProperty]
private TipoSimulable selectedItem;
[ObservableProperty]
public ObservableCollection<osBase> objetosSimulables;
//
// Constructor
//
public MainViewModel()
{
OpenWorkDirectoryCommand = new RelayCommand(OpenWorkDirectory);
datosDeTrabajo = new DatosDeTrabajo();
ObjetosSimulables = new ObservableCollection<osBase>();
ListaOsBase = new ObservableCollection<TipoSimulable>();
// Inicializa el PLCViewModel
PLCViewModel = new PLCViewModel();
PLCViewModel.RefreshEvent += OnRefreshEvent;
InitializeTipoSimulableList();
ItemDoubleClickCommand = new ParameterizedRelayCommand(ExecuteDoubleClick);
_timerSimulacion = new DispatcherTimer();
_timerSimulacion.Interval = TimeSpan.FromMilliseconds(1); // ajusta el intervalo según sea necesario
_timerSimulacion.Tick += OnTickSimulacion;
StartSimulationCommand = new RelayCommand(StartSimulation);
StopSimulationCommand = new RelayCommand(StopSimulation);
TBStartSimulationCommand = new RelayCommand(StartSimulation, () => !IsSimulationRunning);
TBStopSimulationCommand = new RelayCommand(StopSimulation, () => IsSimulationRunning);
TBSaveCommand = new RelayCommand(Save);
TBConnectPLCCommand = new RelayCommand(ConnectPLC, () => !IsConnected);
TBDisconnectPLCCommand = new RelayCommand(DisconnectPLC, () => IsConnected);
TBEliminarUserControlCommand = new RelayCommand(EliminarUserControl, () => habilitarEliminarUserControl);
TBDuplicarUserControlCommand = new RelayCommand(DuplicarUserControl, () => habilitarEliminarUserControl);
stopwatch_Sim = new Stopwatch();
stopwatch_Sim.Start();
}
public void LoadInitialData()
{
// Suponiendo que "SelectedImage" es una propiedad que al establecerse dispara "ImageSelected"
directorioTrabajo = EstadoPersistente.Instance.directorio;
}
// Crear un nuevo Objeto
private void ExecuteDoubleClick(object parameter)
{
if (parameter is TipoSimulable tipoSimulable)
{
if (_selectedImage != value && value != null)
CrearObjetoSimulableEnCentroCanvas(tipoSimulable.Tipo);
}
}
public void CrearObjetoSimulableEnCentroCanvas(Type tipoSimulable)
{
var CentroCanvas = MainWindow.ObtenerCentroCanvasMeters();
CrearObjetoSimulable(tipoSimulable,CentroCanvas.X,CentroCanvas.Y);
}
public osBase CrearObjetoSimulable(Type tipoSimulable,float Left,float Top)
{
// Crear una nueva instancia del osBase correspondiente
osBase? NuevoOsBase = UserControlFactory.GetInstanceForType(tipoSimulable);
NuevoOsBase.Left = Left;
NuevoOsBase.Top = Top;
if (NuevoOsBase != null)
{
if (CrearUserControlDesdeObjetoSimulable(NuevoOsBase))
// Añadir el nuevo osBase a la colección de objetos simulables
ObjetosSimulables.Add(NuevoOsBase);
}
return NuevoOsBase;
}
// Crear UserControl desde osBase : Nuevo o desde Deserealizacion
private bool CrearUserControlDesdeObjetoSimulable(osBase osObjeto)
{
Type tipoObjeto = osObjeto.GetType();
// Obtén el UserControl correspondiente para el tipo de objeto
UserControl? userControl = UserControlFactory.GetControlForType(tipoObjeto);
if (userControl != null)
{
// Asignar los datos al UserControl
UserControlFactory.AssignDatos(userControl, osObjeto, simulationManager);
osObjeto._mainViewModel = this;
MainWindow.AgregarRegistrarUserControlCanvas(userControl);
return true;
}
return false;
}
public void RemoverObjetoSimulable(osBase osObjeto)
{
if (osObjeto != null && ObjetosSimulables.Contains(osObjeto))
{
ObjetosSimulables.Remove(osObjeto);
if (osObjeto.VisualRepresentation != null)
MainWindow.EliminarUserControlDelCanvas(osObjeto.VisualRepresentation);
}
}
private void DuplicarUserControl()
{
if (SelectedItemOsList is osBase objDuplicar)
{
StopSimulation();
DisconnectPLC();
objDuplicar.SalvarDatosNoSerializables();
var settings = new JsonSerializerSettings
{
StopSimulation();
SaveStateObjetosSimulables(); // Guarda el estado antes de cambiar la imagen
_selectedImage = value;
ImageSelected?.Invoke(this, datosDeTrabajo.Imagenes[value]); // Dispara el evento con la nueva ruta de imagen
LoadStateObjetosSimulables();
}
_selectedImage = value;
OnPropertyChanged(nameof(SelectedImage));
}
}
Formatting = Formatting.Indented,
NullValueHandling = NullValueHandling.Ignore,
TypeNameHandling = TypeNameHandling.All
};
private osBase _selectedItemOsList;
public osBase SelectedItemOsList
{
get => _selectedItemOsList;
set
{
if (_selectedItemOsList != value)
try
{
_selectedItemOsList = value;
OnPropertyChanged(nameof(SelectedItemOsList));
// Serializar
var serializedData = JsonConvert.SerializeObject(objDuplicar, settings);
// Duplicar
var NuevoObjetoDuplicado = JsonConvert.DeserializeObject<osBase>(serializedData, settings);
if (NuevoObjetoDuplicado != null)
{
NuevoObjetoDuplicado.Nombre += "_Duplicado";
NuevoObjetoDuplicado.Left += 0.5f;
NuevoObjetoDuplicado.Top += 0.5f;
ObjetosSimulables.Add(NuevoObjetoDuplicado);
CrearUserControlDesdeObjetoSimulable(NuevoObjetoDuplicado);
}
}
catch
{
// Log error or handle it accordingly
}
finally {
objDuplicar.RestaurarDatosNoSerializables();
}
}
}
public ICommand OpenWorkDirectoryCommand { get; }
private void EliminarUserControl()
{
if (SelectedItemOsList is osBase objEliminar)
RemoverObjetoSimulable(objEliminar);
}
private void InitializeTipoSimulableList()
{
var baseType = typeof(osBase);
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(assembly => assembly.GetTypes())
.Where(type => type.IsSubclassOf(baseType) && !type.IsAbstract && typeof(IosBase).IsAssignableFrom(type));
foreach (var type in types)
{
var methodInfo = type.GetMethod("NombreClase", BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy);
string nombre = methodInfo != null ? methodInfo.Invoke(null, null)?.ToString() : "Nombre no encontrado";
ListaOsBase.Add(new TipoSimulable { Nombre = nombre, Tipo = type });
}
}
private void StartSimulation()
{
IsSimulationRunning = true;
foreach (var objetoSimulable in ObjetosSimulables)
objetoSimulable.UpdateGeometryStart();
simulationManager.Debug_DrawInitialBodies();
TiempoDesdeStartSimulacion = 0;
Debug_SimulacionCreado = true;
_timerSimulacion.Start();
}
private void StopSimulation()
{
IsSimulationRunning = false;
foreach (var objetoSimulable in ObjetosSimulables)
objetoSimulable.SimulationStop();
if (Debug_SimulacionCreado)
{
simulationManager.Debug_ClearSimulationShapes();
Debug_SimulacionCreado = false;
}
_timerSimulacion.Stop();
}
private void OnTickSimulacion(object sender, EventArgs e)
{
// Detener el cronómetro y obtener el tiempo transcurrido en milisegundos
var elapsedMilliseconds = stopwatch_Sim.Elapsed.TotalMilliseconds - stopwatch_SimModel_last;
stopwatch_SimModel_last = stopwatch_Sim.Elapsed.TotalMilliseconds;
// Eliminar el diseño de Debug luego de 2 segundos
if (TiempoDesdeStartSimulacion > 2000)
simulationManager.Debug_ClearSimulationShapes();
else
TiempoDesdeStartSimulacion += (float)elapsedMilliseconds;
foreach (var objetoSimulable in ObjetosSimulables)
objetoSimulable.UpdateGeometryStep();
simulationManager.Step();
var objetosSimulablesCopy = new List<osBase>(ObjetosSimulables);
foreach (var objetoSimulable in objetosSimulablesCopy)
{
if (!objetoSimulable.RemoverDesdeSimulacion)
objetoSimulable.UpdateControl((int)elapsedMilliseconds);
else
RemoverObjetoSimulable(objetoSimulable);
}
}
private void ConnectPLC()
{
PLCViewModel.Connect();
}
private void DisconnectPLC()
{
PLCViewModel.Disconnect();
IsConnected = false;
foreach (var objetoSimulable in ObjetosSimulables)
objetoSimulable.SetPLC(null);
}
private void OnRefreshEvent(object sender, EventArgs e)
{
if (PLCViewModel.IsConnected)
{
if (!isConnected)
{
IsConnected = true;
foreach (var objetoSimulable in ObjetosSimulables)
objetoSimulable.SetPLC(PLCViewModel.PLCInterface);
}
// Detener el cronómetro y obtener el tiempo transcurrido en milisegundos
var elapsedMilliseconds = stopwatch_Sim.Elapsed.TotalMilliseconds - stopwatch_SimPLC_last;
stopwatch_SimPLC_last = stopwatch_Sim.Elapsed.TotalMilliseconds;
// Reiniciar el cronómetro para la próxima medición
foreach (var objetoSimulable in ObjetosSimulables)
objetoSimulable.UpdatePLC(PLCViewModel.PLCInterface, (int) elapsedMilliseconds);
}
}
private void OpenWorkDirectory()
{
@ -228,35 +426,40 @@ namespace CtrEditor
// Lista de osBase
//
public ObservableCollection<osBase> ObjetosSimulables
public void Save()
{
get => _objetosSimulables;
set
{
if (_objetosSimulables != value)
{
_objetosSimulables = value;
OnPropertyChanged(nameof(ObjetosSimulables));
}
}
SaveStateObjetosSimulables();
}
public void SaveStateObjetosSimulables()
{
if (_selectedImage != null)
if (SelectedImage != null)
{
StopSimulation();
DisconnectPLC();
// Ruta del archivo a ser guardado
var path = DatosDeTrabajo.ObtenerPathImagenConExtension(SelectedImage, ".json");
// 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
}
foreach (var obj in ObjetosSimulables)
// Guardar referencias temporales
obj.SalvarDatosNoSerializables();
var settings = new JsonSerializerSettings
{
Formatting = Formatting.Indented,
NullValueHandling = NullValueHandling.Ignore,
TypeNameHandling = TypeNameHandling.Auto
};
foreach (var obj in ObjetosSimulables)
{
obj.VisualRepresentation = null;
}
// Crear un objeto que incluya tanto los ObjetosSimulables como el UnitConverter
// Crear un objeto que incluya tanto los ObjetosSimulables como el UnitConverter y PLC_ConnectionData
var dataToSerialize = new SimulationData
{
ObjetosSimulables = ObjetosSimulables,
@ -264,8 +467,13 @@ namespace CtrEditor
PLC_ConnectionData = PLCViewModel
};
// Serializar
var serializedData = JsonConvert.SerializeObject(dataToSerialize, settings);
File.WriteAllText(datosDeTrabajo.ObtenerPathImagenConExtension(_selectedImage, ".json"), serializedData);
File.WriteAllText(path, serializedData); // Escribir el nuevo archivo JSON
// Restaurar las propiedades originales de los objetos
foreach (var obj in ObjetosSimulables)
obj.RestaurarDatosNoSerializables();
}
}
@ -273,10 +481,13 @@ namespace CtrEditor
{
try
{
StopSimulation();
DisconnectPLC();
ObjetosSimulables.Clear();
if (_selectedImage != null)
simulationManager.Clear();
if (SelectedImage != null)
{
string jsonPath = datosDeTrabajo.ObtenerPathImagenConExtension(_selectedImage, ".json");
string jsonPath = datosDeTrabajo.ObtenerPathImagenConExtension(SelectedImage, ".json");
if (File.Exists(jsonPath))
{
string jsonString = File.ReadAllText(jsonPath);
@ -301,9 +512,12 @@ namespace CtrEditor
PixelToMeter.Instance.calc = simulationData.UnitConverter;
// Re-register to the events
PLCViewModel.RefreshEvent += OnRefreshEvent;
// Recorrer la colección de objetos simulables
foreach (var objetoSimulable in ObjetosSimulables)
CrearUsercontrol(objetoSimulable);
CrearUserControlDesdeObjetoSimulable(objetoSimulable);
}
}
}
@ -311,49 +525,39 @@ namespace CtrEditor
catch { /* Consider logging the error or handling it appropriately */ }
}
private bool CrearUsercontrol(osBase osObjeto)
// Se cargan los datos de cada UserControl en el StackPanel
public void CargarPropiedadesosDatos(osBase selectedObject, StackPanel PanelEdicion, ResourceDictionary Resources)
{
Type tipoObjeto = osObjeto.GetType();
// Obtén el UserControl correspondiente para el tipo de objeto
UserControl? userControl = UserControlFactory.GetControlForType(tipoObjeto);
if (userControl != null)
{
// Asignar los datos al UserControl
UserControlFactory.AssignDatos(userControl, osObjeto, simulationManager);
OnUserControlSelected?.Invoke(userControl);
return true;
}
return false;
UserControlFactory.CargarPropiedadesosDatos(selectedObject,PanelEdicion, Resources);
}
private RelayCommand saveCommand;
public ICommand SaveCommand => saveCommand ??= new RelayCommand(Save);
// Implementación de INotifyPropertyChanged...
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
private void Save(object commandParameter)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private RelayCommand exitCommand;
public ICommand ExitCommand => exitCommand ??= new RelayCommand(Exit);
private void Exit()
{
Save();
Application.Current.Shutdown();
}
}
public class SimulationData
{
public ObservableCollection<osBase> ObjetosSimulables { get; set; }
public UnitConverter UnitConverter { get; set; }
public PLCViewModel PLC_ConnectionData { get; set; }
public ObservableCollection<osBase>? ObjetosSimulables { get; set; }
public UnitConverter? UnitConverter { get; set; }
public PLCViewModel? PLC_ConnectionData { get; set; }
}
public class TipoSimulable
{
public string Nombre { get; set; }
public Type Tipo { get; set; }
public string? Nombre { get; set; }
public Type? Tipo { get; set; }
}
public class TickSimulacionEventArgs : EventArgs

View File

@ -3,19 +3,42 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Siemens="clr-namespace:CtrEditor.Siemens" x:Class="CtrEditor.MainWindow"
xmlns:Siemens="clr-namespace:CtrEditor.Siemens"
xmlns:convert="clr-namespace:CtrEditor.Convertidores"
Height="900" Width="1600"
ResizeMode="CanResize" Title="{Binding directorioTrabajo}">
<Window.Resources>
<convert:FloatToFormattedStringConverter x:Key="floatFormatter"/>
</Window.Resources>
xmlns:ObjetosSim="clr-namespace:CtrEditor.ObjetosSim" x:Class="CtrEditor.MainWindow"
Height="900" Width="1600" WindowState="Maximized"
ResizeMode="CanResize" Title="{Binding directorioTrabajo}" Icon="/app2.png">
<Window.DataContext>
<ctreditor:MainViewModel/>
</Window.DataContext>
<Window.Resources>
<convert:FloatToFormattedStringConverter x:Key="floatFormatter"/>
<convert:DoubleToFormattedStringConverter x:Key="doubleFormatter"/>
<convert:BrushToColorNameConverter x:Key="BrushToColorNameConverter"/>
<!-- Style for Start/Stop Button -->
<Style x:Key="StartStopButtonStyle" TargetType="Button">
<Setter Property="Background" Value="Transparent"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsSimulationRunning}" Value="True">
<Setter Property="Background" Value="LightGreen"/>
</DataTrigger>
</Style.Triggers>
</Style>
<!-- Style for Connect/Disconnect Button -->
<Style x:Key="ConnectDisconnectButtonStyle" TargetType="Button">
<Setter Property="Background" Value="Transparent"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsConnected}" Value="True">
<Setter Property="Background" Value="LightGreen"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<!-- Menú Principal sobre toda la ventana -->
<Menu VerticalAlignment="Top" HorizontalAlignment="Stretch">
@ -32,8 +55,8 @@
<!-- Margen superior para el menú -->
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" MinWidth="100"/>
<ColumnDefinition Width="4*" MinWidth="200"/>
<ColumnDefinition Width="1*" MinWidth="100"/>
<ColumnDefinition Width="8*" MinWidth="200"/>
<ColumnDefinition Width="2*" MinWidth="100"/>
</Grid.ColumnDefinitions>
<!-- Primera Columna -->
@ -43,7 +66,7 @@
<RowDefinition Height="2*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ListBox x:Name="ListaImagenes" Grid.Row="0" Margin="5" ItemsSource="{Binding listaImagenes}" SelectedItem="{Binding SelectedImage}" />
<ListBox x:Name="ListaImagenes" Grid.Row="0" Margin="5" ItemsSource="{Binding ListaImagenes}" SelectedItem="{Binding SelectedImage}" />
<ListBox x:Name="ListaFunciones" Grid.Row="1" Margin="5" ItemsSource="{Binding ListaOsBase}" DisplayMemberPath="Nombre" SelectedItem="{Binding SelectedItem}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
@ -58,8 +81,48 @@
<!-- Segunda Columna -->
<Grid Grid.Column="1">
<ScrollViewer x:Name="ImagenEnTrabajoScrollViewer" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" PanningMode="Both">
<Canvas x:Name="ImagenEnTrabajoCanvas" Margin="20">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ToolBarTray Grid.Row="0">
<ToolBar>
<Button Command="{Binding TBStartSimulationCommand}" ToolTip="Iniciar Simulación" Style="{StaticResource StartStopButtonStyle}">
<StackPanel>
<Image Source="Icons/start.png" Width="16" Height="16"/>
<TextBlock Text="Iniciar"/>
</StackPanel>
</Button>
<Button Command="{Binding TBStopSimulationCommand}" ToolTip="Detener Simulación">
<StackPanel>
<Image Source="Icons/stop.png" Width="16" Height="16"/>
<TextBlock Text="Detener"/>
</StackPanel>
</Button>
<Button Command="{Binding TBSaveCommand}" ToolTip="Guardar">
<StackPanel>
<Image Source="Icons/save.png" Width="16" Height="16"/>
<TextBlock Text="Guardar"/>
</StackPanel>
</Button>
<Button Command="{Binding TBConnectPLCCommand}" ToolTip="Conectar PLC" Style="{StaticResource ConnectDisconnectButtonStyle}">
<StackPanel>
<Image Source="Icons/connect.png" Width="16" Height="16"/>
<TextBlock Text="Conectar"/>
</StackPanel>
</Button>
<Button Command="{Binding TBDisconnectPLCCommand}" ToolTip="Desconectar PLC">
<StackPanel>
<Image Source="Icons/disconnect.png" Width="16" Height="16"/>
<TextBlock Text="Desconectar"/>
</StackPanel>
</Button>
</ToolBar>
</ToolBarTray>
<ScrollViewer Grid.Row="1" x:Name="ImagenEnTrabajoScrollViewer" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" PanningMode="Both">
<Canvas x:Name="ImagenEnTrabajoCanvas" Margin="400">
<!-- El Margin puede ser ajustado según el espacio adicional que quieras proporcionar -->
<Canvas.LayoutTransform>
<ScaleTransform ScaleX="1" ScaleY="1"/>
@ -80,6 +143,7 @@
<!-- Espacio para el GridSplitter -->
<RowDefinition Height="*" />
<!-- Altura ajustable para el PanelEdicion -->
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<!-- ListBox -->
@ -105,6 +169,24 @@
<!-- Aquí puedes agregar los controles para editar propiedades -->
</StackPanel>
</ScrollViewer>
<ToolBarTray Grid.Row="3">
<ToolBar>
<Button Command="{Binding TBEliminarUserControlCommand}" ToolTip="Eliminar Control">
<StackPanel>
<Image Source="Icons/borrar.png" Width="16" Height="16"/>
<TextBlock Text="Eliminar"/>
</StackPanel>
</Button>
<Button Command="{Binding TBDuplicarUserControlCommand}" ToolTip="Duplicar Control">
<StackPanel>
<Image Source="Icons/duplicate.png" Width="16" Height="16"/>
<TextBlock Text="Duplicar"/>
</StackPanel>
</Button>
</ToolBar>
</ToolBarTray>
</Grid>
</Grid>

View File

@ -10,7 +10,6 @@ using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using CtrEditor.ObjetosSim;
using CtrEditor.Convertidores;
using CtrEditor.Siemens;
using static System.Runtime.InteropServices.JavaScript.JSType;
@ -19,6 +18,10 @@ using Label = System.Windows.Controls.Label;
using MouseEventArgs = System.Windows.Input.MouseEventArgs;
using TextBox = System.Windows.Controls.TextBox;
using UserControl = System.Windows.Controls.UserControl;
using CheckBox = System.Windows.Controls.CheckBox;
using Orientation = System.Windows.Controls.Orientation;
using ListBox = System.Windows.Controls.ListBox;
using CtrEditor.ObjetosSim;
namespace CtrEditor
@ -58,66 +61,68 @@ namespace CtrEditor
{
if (DataContext is MainViewModel viewModel)
{
viewModel.ImageSelected += ViewModel_ImageSelected;
//viewModel.TickSimulacion += MainViewModel_TickSimulacion;
viewModel.OnUserControlSelected += AgregarUserControl;
viewModel.MainWindow = this;
viewModel.ImageSelected += ViewModel_ImageSelected;
viewModel?.LoadInitialData(); // Carga la primera imagen por defecto una vez cargada la ventana principal
viewModel.simulationManager.DebugCanvas = ImagenEnTrabajoCanvas;
}
}
private void AgregarUserControl(UserControl userControl)
public (float X, float Y) ObtenerCentroCanvasPixels()
{
var scaleTransform = ImagenEnTrabajoCanvas.LayoutTransform as ScaleTransform;
float scaleX = (float)(scaleTransform?.ScaleX ?? 1.0);
float scaleY = (float)(scaleTransform?.ScaleY ?? 1.0);
// Obtiene el área visible del ScrollViewer
float visibleWidth = (float)ImagenEnTrabajoScrollViewer.ViewportWidth;
float visibleHeight = (float)ImagenEnTrabajoScrollViewer.ViewportHeight;
// Obtiene la posición actual del desplazamiento ajustada por el zoom
float offsetX = (float)ImagenEnTrabajoScrollViewer.HorizontalOffset / scaleX;
float offsetY = (float)ImagenEnTrabajoScrollViewer.VerticalOffset / scaleY;
// Calcula el centro visible ajustado
float centerX = offsetX + (visibleWidth / scaleX) / 2;
float centerY = offsetY + (visibleHeight / scaleY) / 2;
return (centerX, centerY);
}
public (float X, float Y) ObtenerCentroCanvasMeters()
{
var c = ObtenerCentroCanvasPixels();
return (PixelToMeter.Instance.calc.PixelsToMeters(c.X), PixelToMeter.Instance.calc.PixelsToMeters(c.Y));
}
public void SuscribirEventos(UserControl userControl)
{
userControl.MouseEnter += UserControl_MouseEnter;
userControl.MouseLeave += UserControl_MouseLeave;
// Suscribir a eventos de mouse para panning
userControl.MouseLeftButtonDown += UserControl_MouseLeftButtonDown;
userControl.MouseLeftButtonUp += UserControl_MouseLeftButtonUp;
userControl.MouseMove += UserControl_MouseMove;
}
public void AgregarRegistrarUserControlCanvas(UserControl userControl)
{
if (userControl is IDataContainer dataContainer)
{
var NuevoOS = dataContainer.Datos;
if (!NuevoOS.Inicializado) // Aun no fue inicializado
{
// Obtiene el factor de escala
var scaleTransform = ImagenEnTrabajoCanvas.LayoutTransform as ScaleTransform;
double scaleX = scaleTransform?.ScaleX ?? 1.0;
double scaleY = scaleTransform?.ScaleY ?? 1.0;
// Obtiene el área visible del ScrollViewer
double visibleWidth = ImagenEnTrabajoScrollViewer.ViewportWidth;
double visibleHeight = ImagenEnTrabajoScrollViewer.ViewportHeight;
// Obtiene la posición actual del desplazamiento ajustada por el zoom
double offsetX = ImagenEnTrabajoScrollViewer.HorizontalOffset / scaleX;
double offsetY = ImagenEnTrabajoScrollViewer.VerticalOffset / scaleY;
// Calcula el centro visible ajustado
double centerX = offsetX + (visibleWidth / scaleX) / 2;
double centerY = offsetY + (visibleHeight / scaleY) / 2;
// Ajusta la posición del UserControl para que esté centrado en el área visible
double leftPixels = centerX - (userControl.ActualWidth / 2);
double topPixels = centerY - (userControl.ActualHeight / 2);
// Establece la posición del UserControl
NuevoOS.Left = PixelToMeter.Instance.calc.PixelsToMeters((float)leftPixels);
NuevoOS.Top = PixelToMeter.Instance.calc.PixelsToMeters((float)topPixels);
NuevoOS.Inicializado = true;
}
else
{
// Fuerza a Establecer la posición del UserControl
NuevoOS.Left = NuevoOS.Left;
NuevoOS.Top = NuevoOS.Top;
}
// Suscribirse a eventos de mouse para marcar el Control
userControl.MouseEnter += UserControl_MouseEnter;
userControl.MouseLeave += UserControl_MouseLeave;
// Suscribir a eventos de mouse para panning
userControl.MouseLeftButtonDown += UserControl_MouseLeftButtonDown;
userControl.MouseLeftButtonUp += UserControl_MouseLeftButtonUp;
userControl.MouseMove += UserControl_MouseMove;
SuscribirEventos(userControl);
// Añade el UserControl al Canvas
Canvas.SetZIndex(userControl, dataContainer.ZIndex());
ImagenEnTrabajoCanvas.Children.Add(userControl);
ImagenEnTrabajoCanvas.Children.Add(userControl);
}
}
public void EliminarUserControlDelCanvas(UserControl userControl)
{
if (ImagenEnTrabajoCanvas.Children.Contains(userControl))
{
ImagenEnTrabajoCanvas.Children.Remove(userControl);
}
}
@ -271,17 +276,29 @@ namespace CtrEditor
// Calcular la diferencia en la posición X desde el punto de inicio
double widthChange = currentPosition.X - _startPointUserControl.X;
// Calcular la diferencia en la posición Y desde el punto de inicio
double heightChange = currentPosition.Y - _startPointUserControl.Y;
// Actualizar el ancho del control
double newWidth = Math.Max(control.ActualWidth + widthChange, control.MinWidth);
control.Width = newWidth; // Asegurar que no sea menor que el mínimo
// Actualizar la altura del control
double newHeight = Math.Max(control.ActualHeight + heightChange, control.MinHeight);
// Asegurar que el nuevo tamaño no sea menor que los mínimos
control.Width = newWidth;
control.Height = newHeight;
if (control is IDataContainer dataContainer)
dataContainer.Resize((float)newWidth, 0);
{
dataContainer.Resize((float)newWidth, (float)newHeight);
}
// Actualizar el punto de inicio para el próximo evento de movimiento
_startPointUserControl = currentPosition;
}
private void UserControl_MouseEnter(object sender, MouseEventArgs e)
{
if (sender is UserControl userControl)
@ -408,46 +425,10 @@ namespace CtrEditor
private void CargarPropiedadesosDatos(osBase selectedObject)
{
PanelEdicion.Children.Clear();
var properties = selectedObject.GetType().GetProperties();
foreach (var property in properties)
{
if (property.PropertyType == typeof(float) || property.PropertyType == typeof(string))
{
var label = new Label { Content = property.Name };
var textBox = new TextBox { Width = 200, Margin = new Thickness(0) };
var binding = new Binding(property.Name)
{
Source = selectedObject,
Mode = BindingMode.TwoWay,
UpdateSourceTrigger = UpdateSourceTrigger.LostFocus, // Actualizar solo al perder el foco
Converter = (FloatToFormattedStringConverter)Resources["floatFormatter"] // Usar el convertidor
};
// Aplicar el convertidor solo a propiedades float
if (property.PropertyType == typeof(float))
{
textBox.SetBinding(TextBox.TextProperty, binding);
}
else
{
textBox.SetBinding(TextBox.TextProperty, new Binding(property.Name)
{
Source = selectedObject,
Mode = BindingMode.TwoWay,
UpdateSourceTrigger = UpdateSourceTrigger.LostFocus
});
}
PanelEdicion.Children.Add(label);
PanelEdicion.Children.Add(textBox);
}
}
if (DataContext is MainViewModel viewModel)
viewModel.CargarPropiedadesosDatos(selectedObject, PanelEdicion, Resources);
}
private void MainWindow_Closed(object sender, EventArgs e)
{
if (DataContext is MainViewModel viewModel)

View File

@ -0,0 +1,155 @@
using System.Windows;
using System.Windows.Controls;
//using using Microsoft.Xna.Framework;
using CtrEditor.Convertidores;
using CtrEditor.Siemens;
using CtrEditor.Simulacion;
using CommunityToolkit.Mvvm.ComponentModel;
using nkast.Aether.Physics2D.Common;
namespace CtrEditor.ObjetosSim
{
/// <summary>
/// Interaction logic for ucBotella.xaml
/// </summary>
///
public partial class osBotella : osBase, IosBase
{
private simBotella SimGeometria;
// Otros datos y métodos relevantes para la simulación
public static string NombreClase()
{
return "Botella";
}
private string nombre = NombreClase();
public override string Nombre
{
get => nombre;
set => SetProperty(ref nombre, value);
}
[ObservableProperty]
private float diametro;
partial void OnDiametroChanged(float value)
{
SimGeometria?.SetDiameter(Diametro);
}
[ObservableProperty]
private float mass;
partial void OnMassChanged(float value)
{
SimGeometria?.SetMass(value);
}
public Vector2 GetCentro()
{
return new Vector2 (Left+Diametro/2,Top+Diametro/2);
}
public void SetCentro(float x, float y)
{ Left = x; Top = y; }
public void SetCentro(Vector2 centro)
{
Left = centro.X - Diametro / 2;
Top = centro.Y - Diametro / 2;
}
private void ActualizarGeometrias()
{
if (SimGeometria != null)
{
SimGeometria.SetDiameter(Diametro);
SimGeometria.SetPosition(GetCentro());
}
}
public osBotella()
{
Diametro = 0.10f;
Mass = 1;
}
public override void UpdateGeometryStart()
{
// Se llama antes de la simulacion
ActualizarGeometrias();
}
public override void SimulationStop()
{
// Se llama al detener la simulacion. Util para detener Storyboards
}
public override void UpdateGeometryStep()
{
// Se llama antes de la simulacion
ActualizarGeometrias();
}
public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds) { }
public override void UpdateControl(int elapsedMilliseconds)
{
SetCentro(SimGeometria.Center);
if (SimGeometria.Descartar) // Ha sido marcada para remover
RemoverDesdeSimulacion = true;
}
public override void ucLoaded()
{
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
ActualizarLeftTop();
SimGeometria = simulationManager.AddCircle(Diametro, GetCentro(), Mass);
}
public override void ucUnLoaded()
{
// El UserControl se esta eliminando
// eliminar el objeto de simulacion
simulationManager.Remove(SimGeometria);
}
}
public partial class ucBotella : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public ucBotella()
{
InitializeComponent();
this.Loaded += OnLoaded;
this.Unloaded += OnUnloaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
Datos?.ucLoaded();
}
private void OnUnloaded(object sender, RoutedEventArgs e)
{
Datos?.ucUnLoaded();
}
public void Resize(float width, float height) { }
public void Move(float LeftPixels, float TopPixels)
{
if (Datos != null)
{
Datos.Left = PixelToMeter.Instance.calc.PixelsToMeters(LeftPixels);
Datos.Top = PixelToMeter.Instance.calc.PixelsToMeters(TopPixels);
}
}
public void Rotate(float Angle) { }
public void Highlight(bool State) { }
public int ZIndex()
{
return 10;
}
}
}

View File

@ -0,0 +1,29 @@
<UserControl x:Class="CtrEditor.ObjetosSim.ucFiller"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:CtrEditor.ObjetosSim"
mc:Ignorable="d"
xmlns:convert="clr-namespace:CtrEditor.Convertidores"
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim">
<UserControl.Resources>
<convert:MeterToPixelConverter x:Key="MeterToPixelConverter"/>
</UserControl.Resources>
<UserControl.DataContext>
<vm:osFiller/>
</UserControl.DataContext>
<Grid>
<Image Source="/imagenes/filler.png"
Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}"
Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}}"
Stretch="Uniform">
<Image.RenderTransform>
<RotateTransform Angle="{Binding Angulo}" />
</Image.RenderTransform>
</Image>
</Grid>
</UserControl>

View File

@ -0,0 +1,181 @@
using CtrEditor.Convertidores;
using CtrEditor.Siemens;
using System.Windows;
using System.Windows.Controls;
using CommunityToolkit.Mvvm.ComponentModel;
namespace CtrEditor.ObjetosSim
{
/// <summary>
/// Interaction logic for ucFiller.xaml
/// </summary>
public partial class osFiller : osBase, IosBase
{
// Otros datos y métodos relevantes para la simulación
private float TiempoRestante;
private osBotella UltimaBotella;
public static string NombreClase()
{
return "Filler";
}
private string nombre = NombreClase();
public override string Nombre
{
get => nombre;
set => SetProperty(ref nombre, value);
}
[ObservableProperty]
private float offsetLeftSalida;
[ObservableProperty]
private float offsetTopSalida;
[ObservableProperty]
private bool consenso;
[ObservableProperty]
private float botellas_hora;
partial void OnBotellas_horaChanged(float value)
{
Botellas_segundo = value / 3600;
}
[ObservableProperty]
private float botellas_segundo;
partial void OnBotellas_segundoChanged(float value)
{
Botellas_hora = value * 3600;
}
[ObservableProperty]
private float velocidad_actual_percentual;
[ObservableProperty]
private float diametro_botella;
[ObservableProperty]
private string tag_consenso;
[ObservableProperty]
private float ancho;
[ObservableProperty]
private float alto;
[ObservableProperty]
private float angulo;
public osFiller()
{
Ancho = 0.30f;
Alto = 0.30f;
Angulo = 0;
Velocidad_actual_percentual = 0;
Diametro_botella = 0.1f;
Botellas_hora = 10000;
}
public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds)
{
Consenso = LeerBitTag(Tag_consenso);
}
public override void UpdateControl(int elapsedMilliseconds)
{
if (Consenso && Velocidad_actual_percentual > 0)
{
TiempoRestante -= elapsedMilliseconds / 1000.0f;
if (TiempoRestante <= 0)
{
TiempoRestante += 3600 / (Botellas_hora * (Velocidad_actual_percentual / 100.0f));
var X = Left + OffsetLeftSalida;
var Y = Top + OffsetTopSalida;
if (UltimaBotella != null && UltimaBotella.RemoverDesdeSimulacion)
UltimaBotella = null;
if (UltimaBotella == null)
{
// No hay botellas, se puede crear una nueva directamente
var nuevaBotella = _mainViewModel.CrearObjetoSimulable(typeof(osBotella), X, Y);
((osBotella)nuevaBotella).Diametro = Diametro_botella;
nuevaBotella.AutoCreated = true;
UltimaBotella = (osBotella)nuevaBotella;
}
else
{
// Calcular la distancia entre el centro de la última botella y la nueva posición
float distancia = (float)Math.Sqrt(Math.Pow(UltimaBotella.Left - X, 2) + Math.Pow(UltimaBotella.Top - Y, 2));
float distanciaMinima = Diametro_botella / 2; // Asumiendo que el diámetro de la nueva botella es similar
if (distancia > distanciaMinima)
{
var nuevaBotella = _mainViewModel.CrearObjetoSimulable(typeof(osBotella), X, Y);
((osBotella)nuevaBotella).Diametro = Diametro_botella;
nuevaBotella.AutoCreated = true;
UltimaBotella = (osBotella)nuevaBotella;
}
}
}
}
else
{
TiempoRestante = 0;
}
}
public override void ucLoaded()
{
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
ActualizarLeftTop();
}
}
public partial class ucFiller : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public ucFiller()
{
InitializeComponent();
this.Loaded += OnLoaded;
this.Unloaded += OnUnloaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
Datos?.ucLoaded();
}
private void OnUnloaded(object sender, RoutedEventArgs e)
{
Datos?.ucUnLoaded();
}
public void Resize(float width, float height)
{
if (Datos is osFiller datos)
{
datos.Ancho = PixelToMeter.Instance.calc.PixelsToMeters(width);
datos.Alto = PixelToMeter.Instance.calc.PixelsToMeters(width);
}
}
public void Move(float LeftPixels, float TopPixels)
{
if (Datos != null)
{
Datos.Left = PixelToMeter.Instance.calc.PixelsToMeters(LeftPixels);
Datos.Top = PixelToMeter.Instance.calc.PixelsToMeters(TopPixels);
}
}
public void Rotate(float Angle)
{
if (Datos != null)
if (Datos is osFiller datos)
datos.Angulo = Angle;
}
public void Highlight(bool State) { }
public int ZIndex()
{
return 10;
}
}
}

View File

@ -0,0 +1,53 @@
<UserControl x:Class="CtrEditor.ObjetosSim.ucTanque"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:CtrEditor.ObjetosSim"
mc:Ignorable="d"
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim"
xmlns:convert="clr-namespace:CtrEditor.Convertidores">
<UserControl.Resources>
<convert:MeterToPixelConverter x:Key="MeterToPixelConverter"/>
<convert:LevelToHeightMultiConverter x:Key="LevelToHeightMultiConverter"/>
<convert:WidthPercentageConverter x:Key="WidthPercentageConverter"/>
</UserControl.Resources>
<UserControl.DataContext>
<vm:osTanque Alto="1" Ancho="1" Angulo="-4" />
</UserControl.DataContext>
<Grid>
<Image x:Name="TankImage"
Source="/imagenes/tank.png"
Width="{Binding Alto, Converter={StaticResource MeterToPixelConverter}}"
Height="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}"
Stretch="Uniform">
</Image>
<Slider
HorizontalAlignment="Left"
VerticalAlignment="Center"
Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}}"
Orientation="Vertical"
Value="{Binding Level}"
Maximum="100"/>
<Rectangle x:Name="WaterLevel"
Fill="Blue"
VerticalAlignment="Bottom"
HorizontalAlignment="Center">
<Rectangle.Width>
<Binding ElementName="TankImage" Path="ActualWidth" Converter="{StaticResource WidthPercentageConverter}" />
</Rectangle.Width>
<Rectangle.Height>
<MultiBinding Converter="{StaticResource LevelToHeightMultiConverter}">
<Binding Path="Level" />
<Binding ElementName="TankImage" Path="ActualHeight" />
</MultiBinding>
</Rectangle.Height>
</Rectangle>
<Label Content="{Binding Level}" HorizontalAlignment="Center" Margin="10,10,0,0" VerticalAlignment="Top"/>
</Grid>
</UserControl>

View File

@ -0,0 +1,141 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CtrEditor.Convertidores;
using CtrEditor.Siemens;
using System.Windows;
using System.Windows.Controls;
namespace CtrEditor.ObjetosSim
{
/// <summary>
/// Interaction logic for ucTanque.xaml
/// </summary>
public partial class osTanque : osBase, IosBase
{
// Otros datos y métodos relevantes para la simulación
public static string NombreClase()
{
return "Tanque";
}
private string nombre = NombreClase();
public override string Nombre
{
get => nombre;
set => SetProperty(ref nombre, value);
}
[ObservableProperty]
public float capacidad_Litros;
[ObservableProperty]
public bool ingreso_Abierto;
[ObservableProperty]
public bool salida_Abierta;
[ObservableProperty]
public string tagNivel_Word;
[ObservableProperty]
public string tagIngresoAbierto_Bool;
[ObservableProperty]
public string tagSalidaAbierta_Bool;
[ObservableProperty]
public float velocidad_Ingreso;
[ObservableProperty]
public float velocidad_Salida;
[ObservableProperty]
public float min_OUT_Scaled;
[ObservableProperty]
public float max_OUT_Scaled;
[ObservableProperty]
public float level;
[ObservableProperty]
public float ancho;
[ObservableProperty]
public float alto;
[ObservableProperty]
public float angulo;
public osTanque()
{
Ancho = 0.30f;
Alto = 0.30f;
Max_OUT_Scaled = 27648;
Min_OUT_Scaled = 0;
}
public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds)
{
EscribirWordTagScaled(TagNivel_Word, Level, 0, 100, Min_OUT_Scaled, Max_OUT_Scaled);
Ingreso_Abierto = LeerBitTag(TagIngresoAbierto_Bool);
Salida_Abierta = LeerBitTag(TagSalidaAbierta_Bool);
}
public override void UpdateControl(int elapsedMilliseconds)
{
if (Salida_Abierta && Level > 0)
{
var l_por_milisegundo = Velocidad_Salida / 60000.0f;
Level -= l_por_milisegundo * elapsedMilliseconds;
if (Level < 0) Level = 0;
}
if (Ingreso_Abierto && Level < 100)
{
var l_por_milisegundo = Velocidad_Ingreso / 60000.0f;
Level += l_por_milisegundo * elapsedMilliseconds;
if (Level > 100) Level = 100;
}
}
public override void ucLoaded()
{
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
ActualizarLeftTop();
}
}
public partial class ucTanque : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public ucTanque()
{
InitializeComponent();
this.Loaded += OnLoaded;
this.Unloaded += OnUnloaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
Datos?.ucLoaded();
}
private void OnUnloaded(object sender, RoutedEventArgs e)
{
Datos?.ucUnLoaded();
}
public void Resize(float width, float height)
{
if (Datos is osTanque datos)
{
datos.Ancho = PixelToMeter.Instance.calc.PixelsToMeters(width);
datos.Alto = PixelToMeter.Instance.calc.PixelsToMeters(width);
}
}
public void Move(float LeftPixels, float TopPixels)
{
if (Datos != null)
{
Datos.Left = PixelToMeter.Instance.calc.PixelsToMeters(LeftPixels);
Datos.Top = PixelToMeter.Instance.calc.PixelsToMeters(TopPixels);
}
}
public void Rotate(float Angle)
{
if (Datos != null)
if (Datos is osTanque datos)
datos.Angulo = Angle;
}
public void Highlight(bool State) { }
public int ZIndex()
{
return 10;
}
}
}

View File

@ -0,0 +1,48 @@
<UserControl x:Class="CtrEditor.ObjetosSim.ucDescarte"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:CtrEditor.ObjetosSim"
mc:Ignorable="d"
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim"
xmlns:convert="clr-namespace:CtrEditor.Convertidores">
<UserControl.Resources>
<convert:MeterToPixelConverter x:Key="MeterToPixelConverter"/>
<Storyboard x:Key="PulsingStoryboard" RepeatBehavior="Forever">
<DoubleAnimation
Storyboard.TargetName="AnimatedEllipse"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)"
From="1" To="0.5" Duration="0:0:1" AutoReverse="True" />
<DoubleAnimation
Storyboard.TargetName="AnimatedEllipse"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)"
From="1" To="0.5" Duration="0:0:1" AutoReverse="True" />
</Storyboard>
</UserControl.Resources>
<UserControl.DataContext>
<vm:osDescarte/>
</UserControl.DataContext>
<Grid>
<Ellipse
Height="{Binding Diametro, Converter={StaticResource MeterToPixelConverter}}"
Width="{Binding Diametro, Converter={StaticResource MeterToPixelConverter}}"
Stroke="Yellow"
Fill="Black"
Opacity="0.5"/>
<Ellipse x:Name="AnimatedEllipse"
Height="{Binding Diametro, Converter={StaticResource MeterToPixelConverter}}"
Width="{Binding Diametro, Converter={StaticResource MeterToPixelConverter}}"
Stroke="Blue"
Fill="Transparent"
RenderTransformOrigin="0.5,0.5">
<Ellipse.RenderTransform>
<TransformGroup>
<ScaleTransform />
</TransformGroup>
</Ellipse.RenderTransform>
</Ellipse>
</Grid>
</UserControl>

View File

@ -0,0 +1,157 @@
using CtrEditor.Convertidores;
using CtrEditor.Siemens;
using CtrEditor.Simulacion;
using System.Windows;
using System.Windows.Controls;
using nkast.Aether.Physics2D.Common;
using System.Windows.Media.Animation;
using CommunityToolkit.Mvvm.ComponentModel;
namespace CtrEditor.ObjetosSim
{
/// <summary>
/// Interaction logic for ucDescarte.xaml
/// </summary>
public partial class osDescarte : osBase, IosBase
{
// Otros datos y métodos relevantes para la simulación
private simDescarte SimGeometria;
public static string NombreClase()
{
return "Descarte";
}
private string nombre = NombreClase();
public override string Nombre
{
get => nombre;
set => SetProperty(ref nombre, value);
}
[ObservableProperty]
private float diametro;
partial void OnDiametroChanged(float value)
{
SimGeometria?.SetDiameter(Diametro);
}
public Vector2 GetCentro()
{
return new Vector2(Left + Diametro / 2, Top + Diametro / 2);
}
public void SetCentro(float x, float y)
{ Left = x; Top = y; }
public void SetCentro(Vector2 centro)
{
Left = centro.X - Diametro / 2;
Top = centro.Y - Diametro / 2;
}
public override void LeftChanged(float value)
{
ActualizarGeometrias();
}
public override void TopChanged(float value)
{
ActualizarGeometrias();
}
private void ActualizarGeometrias()
{
if (SimGeometria != null)
{
SimGeometria.SetDiameter(Diametro);
SimGeometria.SetPosition(GetCentro());
}
}
public osDescarte()
{
Diametro = 1f;
}
public override void UpdateGeometryStart()
{
// Se llama antes de la simulacion
ActualizarGeometrias();
}
public override void UpdateGeometryStep()
{
}
public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds)
{
}
public override void UpdateControl(int elapsedMilliseconds)
{
}
public override void ucLoaded()
{
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
ActualizarLeftTop();
SimGeometria = simulationManager.AddDescarte(Diametro, GetCentro());
}
public override void ucUnLoaded()
{
// El UserControl se esta eliminando
// eliminar el objeto de simulacion
simulationManager.Remove(SimGeometria);
}
}
public partial class ucDescarte : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public ucDescarte()
{
InitializeComponent();
this.Loaded += OnLoaded;
this.Unloaded += OnUnloaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
Datos?.ucLoaded();
Storyboard storyboard = this.Resources["PulsingStoryboard"] as Storyboard;
if (storyboard != null)
{
storyboard.Begin();
}
}
private void OnUnloaded(object sender, RoutedEventArgs e)
{
Datos?.ucUnLoaded();
}
public void Resize(float width, float height)
{
if (Datos is osDescarte datos)
{
datos.Diametro = PixelToMeter.Instance.calc.PixelsToMeters(width);
}
}
public void Move(float LeftPixels, float TopPixels)
{
if (Datos != null)
{
Datos.Left = PixelToMeter.Instance.calc.PixelsToMeters(LeftPixels);
Datos.Top = PixelToMeter.Instance.calc.PixelsToMeters(TopPixels);
}
}
public void Rotate(float Angle)
{
}
public void Highlight(bool State) { }
public int ZIndex()
{
return 10;
}
}
}

View File

@ -4,14 +4,19 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim"
xmlns:convert="clr-namespace:CtrEditor.Convertidores">
<UserControl.Resources>
<convert:MeterToPixelConverter x:Key="MeterToPixelConverter"/>
</UserControl.Resources>
<UserControl.DataContext>
<vm:osGuia/>
</UserControl.DataContext>
<Canvas>
<Rectangle Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}" Height="{Binding AltoGuia, Converter={StaticResource MeterToPixelConverter}}" Fill="Blue">
<Rectangle x:Name="Guia" Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}" Height="{Binding AltoGuia, Converter={StaticResource MeterToPixelConverter}}" Fill="Blue">
<Rectangle.RenderTransform>
<RotateTransform Angle="{Binding Angulo}"/>
</Rectangle.RenderTransform>

View File

@ -0,0 +1,124 @@
using System.Windows;
using System.Windows.Controls;
using CommunityToolkit.Mvvm.ComponentModel;
using CtrEditor.Convertidores;
using CtrEditor.Siemens;
using CtrEditor.Simulacion;
namespace CtrEditor.ObjetosSim
{
/// <summary>
/// Interaction logic for ucGuia.xaml
/// </summary>
public partial class osGuia : osBase, IosBase
{
private simGuia SimGeometria;
public static string NombreClase()
{
return "Guia";
}
private string nombre = "Guia";
public override string Nombre
{
get => nombre;
set => SetProperty(ref nombre, value);
}
[ObservableProperty]
public float ancho;
[ObservableProperty]
public float altoGuia;
[ObservableProperty]
public float angulo;
private void ActualizarGeometrias()
{
if (_visualRepresentation is ucGuia uc)
UpdateOrCreateLine(SimGeometria, uc.Guia);
}
public osGuia()
{
Ancho = 1;
AltoGuia = 0.03f;
}
public override void UpdateGeometryStart()
{
// Se llama antes de la simulacion
ActualizarGeometrias();
}
public override void UpdateControl(int elapsedMilliseconds)
{
}
public override void UpdateGeometryStep()
{
}
public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds) { }
public override void ucLoaded()
{
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
ActualizarLeftTop();
if (_visualRepresentation is ucGuia uc)
SimGeometria = AddLine(simulationManager, uc.Guia);
}
public override void ucUnLoaded()
{
// El UserControl se esta eliminando
// eliminar el objeto de simulacion
simulationManager?.Remove(SimGeometria);
}
}
public partial class ucGuia : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public ucGuia()
{
InitializeComponent();
this.Loaded += OnLoaded;
this.Unloaded += OnUnloaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
Datos?.ucLoaded();
}
private void OnUnloaded(object sender, RoutedEventArgs e)
{
Datos?.ucUnLoaded();
}
public void Resize(float width, float height)
{
if (Datos is osGuia datos)
datos.Ancho = PixelToMeter.Instance.calc.PixelsToMeters(width);
}
public void Move(float LeftPixels, float TopPixels)
{
if (Datos != null)
{
Datos.Left = PixelToMeter.Instance.calc.PixelsToMeters(LeftPixels);
Datos.Top = PixelToMeter.Instance.calc.PixelsToMeters(TopPixels);
}
}
public void Rotate(float Angle)
{
if (Datos != null)
if (Datos is osGuia datos)
datos.Angulo = Angle;
}
public void Highlight(bool State) { }
public int ZIndex()
{
return 1;
}
}
}

View File

@ -5,12 +5,19 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:localuc="clr-namespace:CtrEditor.ObjetosSim.UserControls"
xmlns:convert="clr-namespace:CtrEditor.Convertidores"
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim"
mc:Ignorable="d">
<UserControl.Resources>
<convert:MeterToPixelConverter x:Key="MeterToPixelConverter"/>
</UserControl.Resources>
<Canvas>
<localuc:CircularSegment Angle="{Binding Angulo}" OuterRadius="{Binding RadioExterno, Converter={StaticResource MeterToPixelConverter}}" InnerRadius="{Binding RadioInterno, Converter={StaticResource MeterToPixelConverter}}"
StartAngle="0" EndAngle="90" />
<UserControl.DataContext>
<vm:osTransporteCurva />
</UserControl.DataContext>
<Canvas x:Name="MainCanvas">
<localuc:CircularSegment x:Name="Transporte" Angle="0" OuterRadius="{Binding RadioExterno, Converter={StaticResource MeterToPixelConverter}}" InnerRadius="{Binding RadioInterno, Converter={StaticResource MeterToPixelConverter}}"
StartAngle="{Binding Angulo}" EndAngle="{Binding AnguloFinal}" />
</Canvas>
</UserControl>

View File

@ -0,0 +1,177 @@
using System.Windows;
using System.Windows.Controls;
using CommunityToolkit.Mvvm.ComponentModel;
using CtrEditor.Convertidores;
using CtrEditor.Siemens;
using CtrEditor.Simulacion;
namespace CtrEditor.ObjetosSim
{
/// <summary>
/// Interaction logic for ucTransporteCurva.xaml
/// </summary>
public partial class osTransporteCurva : osBase, IosBase
{
private float frictionCoefficient;
private float velMax50hz; // en metros por minuto
private float tiempoRampa;
private bool esMarcha;
private float _velocidadActual;
private osBase _osMotor = null;
private simCurve Simulation_TransporteCurva;
public static string NombreClase()
{
return "Transporte Curva 90";
}
private string nombre = "Transporte Curva 90";
public override string Nombre
{
get => nombre;
set => SetProperty(ref nombre, value);
}
[ObservableProperty]
private float radioExterno;
[ObservableProperty]
private float radioInterno;
[ObservableProperty]
private string motor;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(AnguloFinal))]
public float angulo;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(AnguloFinal))]
private float arco_en_grados;
[Hidden]
public float AnguloFinal
{
get => Angulo+Arco_en_grados;
}
public float VelocidadActual
{
get => _velocidadActual;
set
{
_velocidadActual = value;
Simulation_TransporteCurva?.SetSpeed(value);
OnPropertyChanged(nameof(VelocidadActual));
}
}
private void ActualizarGeometrias()
{
if (_visualRepresentation is ucTransporteCurva uc)
{
UpdateCurve(Simulation_TransporteCurva, RadioInterno, RadioExterno, Angulo, Angulo+Arco_en_grados);
Simulation_TransporteCurva.Speed = VelocidadActual;
}
}
public float FrictionCoefficient { get => frictionCoefficient; set => frictionCoefficient = value; }
public float VelMax50hz { get => velMax50hz; set => velMax50hz = value; }
public float TiempoRampa { get => tiempoRampa; set => tiempoRampa = value; }
public bool EsMarcha { get => esMarcha; set => esMarcha = value; }
public osTransporteCurva()
{
RadioExterno = 1.3f;
RadioInterno = 1;
Arco_en_grados = 90;
}
public override void UpdateGeometryStart()
{
// Se llama antes de la simulacion
ActualizarGeometrias();
}
public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds)
{
if (_osMotor != null)
{
if (_osMotor is osVMmotorSim motor)
VelocidadActual = motor.Velocidad;
}
else
_osMotor = ObtenerLink(Motor, typeof(osVMmotorSim));
}
public override void ucLoaded()
{
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
ActualizarLeftTop();
if (_visualRepresentation is ucTransporteCurva uc)
Simulation_TransporteCurva = AddCurve(RadioInterno,RadioExterno, Angulo, Angulo + Arco_en_grados);
// AddCurve(float innerRadius, float outerRadius, float startAngle, float endAngle, Vector2 position)
}
public override void ucUnLoaded()
{
// El UserControl se esta eliminando
// eliminar el objeto de simulacion
simulationManager?.Remove(Simulation_TransporteCurva);
}
}
public partial class ucTransporteCurva : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public ucTransporteCurva()
{
InitializeComponent();
this.Loaded += OnLoaded;
this.Unloaded += OnUnloaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
Datos?.ucLoaded();
}
private void OnUnloaded(object sender, RoutedEventArgs e)
{
Datos?.ucUnLoaded();
}
public void Resize(float RadioExterno, float RadioInterno)
{
if (Datos is osTransporteCurva datos)
{
if (RadioExterno > RadioInterno && RadioExterno > 0 && RadioInterno >= 0)
{
datos.RadioExterno = PixelToMeter.Instance.calc.PixelsToMeters(RadioExterno);
datos.RadioInterno = PixelToMeter.Instance.calc.PixelsToMeters(RadioInterno);
}
}
}
public void Move(float LeftPixels, float TopPixels)
{
if (Datos != null)
{
Datos.Left = PixelToMeter.Instance.calc.PixelsToMeters(LeftPixels);
Datos.Top = PixelToMeter.Instance.calc.PixelsToMeters(TopPixels);
}
}
public void Rotate(float Angle)
{
if (Datos != null)
if (Datos is osTransporteCurva datos)
datos.Angulo = Angle;
}
public void Highlight(bool State) { }
public int ZIndex()
{
return 1;
}
}
}

View File

@ -3,14 +3,34 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim"
xmlns:convert="clr-namespace:CtrEditor.Convertidores"
mc:Ignorable="d">
<UserControl.Resources>
<convert:MeterToPixelConverter x:Key="MeterToPixelConverter"/>
<convert:DistanceToMarginConverter x:Key="DistanceToMarginConverter"/>
<!-- Define the VisualBrush for the conveyor belt pattern -->
<VisualBrush x:Key="BeltBrush" TileMode="Tile" Viewport="0,0,20,10" ViewportUnits="Absolute" Viewbox="0,0,20,10" ViewboxUnits="Absolute">
<VisualBrush.Transform>
<TransformGroup>
<TranslateTransform/>
</TransformGroup>
</VisualBrush.Transform>
<VisualBrush.Visual>
<Canvas>
<Rectangle Fill="#FFBFBFBF" Width="10" Height="10"/>
<Rectangle Fill="LightGray" Width="10" Height="10" Canvas.Left="10"/>
</Canvas>
</VisualBrush.Visual>
</VisualBrush>
</UserControl.Resources>
<UserControl.DataContext>
<vm:osTransporteGuias/>
</UserControl.DataContext>
<Grid>
<Canvas>
<StackPanel x:Name="RectanglesContainer">
@ -19,8 +39,9 @@
</StackPanel.RenderTransform>
<Rectangle x:Name="GuiaSuperior" Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}" Height="{Binding AltoGuia, Converter={StaticResource MeterToPixelConverter}}" Fill="Blue"
Margin="{Binding Distance, Converter={StaticResource DistanceToMarginConverter}}"/>
<Rectangle x:Name="Transporte" Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}" Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}}" Fill="Gray"
Margin="{Binding Distance, Converter={StaticResource DistanceToMarginConverter}}"/>
<Rectangle x:Name="Transporte" Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}" Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}}"
Margin="{Binding Distance, Converter={StaticResource DistanceToMarginConverter}}"
Fill="{StaticResource BeltBrush}"/>
<Rectangle x:Name="GuiaInferior" Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}" Height="{Binding AltoGuia, Converter={StaticResource MeterToPixelConverter}}" Fill="Blue"/>
</StackPanel>
</Canvas>

View File

@ -0,0 +1,194 @@
using System.Windows;
using System.Windows.Controls;
using CommunityToolkit.Mvvm.ComponentModel;
using CtrEditor.Convertidores;
using CtrEditor.Siemens;
using CtrEditor.Simulacion;
namespace CtrEditor.ObjetosSim
{
/// <summary>
/// Interaction logic for ucTransporteGuias.xaml
/// </summary>
public partial class osTransporteGuias : osBase, IosBase
{
private osBase _osMotor = null;
private simTransporte? SimGeometria;
private simGuia? Guia_Superior;
private simGuia? Guia_Inferior;
public static string NombreClase()
{
return "Transporte Guias";
}
private string nombre = NombreClase();
public override string Nombre
{
get => nombre;
set => SetProperty(ref nombre, value);
}
[ObservableProperty]
private float velocidadActual;
partial void OnVelocidadActualChanged(float value)
{
SimGeometria?.SetSpeed(value);
ActualizarAnimacionStoryBoardTransporte(VelocidadActual);
}
[ObservableProperty]
public string motor;
partial void OnMotorChanged(string value)
{
_osMotor = ObtenerLink(Motor, typeof(osVMmotorSim));
}
[ObservableProperty]
public float ancho;
[ObservableProperty]
public float alto;
partial void OnAltoChanged(float value)
{
ActualizarGeometrias();
}
[ObservableProperty]
public float angulo;
[ObservableProperty]
public float frictionCoefficient;
[ObservableProperty]
public float velMax50hz;
[ObservableProperty]
public float tiempoRampa;
[ObservableProperty]
public bool esMarcha;
[ObservableProperty]
private float distance;
[ObservableProperty]
private float altoGuia;
private void ActualizarGeometrias()
{
if (_visualRepresentation is ucTransporteGuias uc)
{
UpdateRectangle(SimGeometria, uc.Transporte, Alto, Ancho, Angulo);
UpdateOrCreateLine(Guia_Superior, uc.GuiaSuperior);
UpdateOrCreateLine(Guia_Inferior, uc.GuiaInferior) ;
SimGeometria.DistanceGuide2Guide = Alto;
SimGeometria.Speed = VelocidadActual;
ActualizarAnimacionStoryBoardTransporte(VelocidadActual);
}
}
public osTransporteGuias()
{
Ancho = 1;
Alto = 0.10f;
AltoGuia = 0.03f;
Distance = 0.01f;
}
public override void UpdateGeometryStart()
{
// Se llama antes de la simulacion
ActualizarGeometrias();
}
public override void SimulationStop()
{
// Se llama al detener la simulacion
ActualizarAnimacionStoryBoardTransporte(VelocidadActual);
}
public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds)
{
if (_osMotor != null)
{
if (_osMotor is osVMmotorSim motor)
VelocidadActual = motor.Velocidad;
} else if (Motor.Length > 0)
_osMotor = ObtenerLink(Motor, typeof(osVMmotorSim));
}
public override void ucLoaded()
{
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
ActualizarLeftTop();
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
if (_visualRepresentation is ucTransporteGuias uc)
{
SimGeometria = AddRectangle(simulationManager, uc.Transporte, Alto, Ancho, Angulo);
SimGeometria.TransportWithGuides = true;
SimGeometria.DistanceGuide2Guide = Alto;
Guia_Superior = AddLine(simulationManager, uc.GuiaSuperior);
Guia_Inferior = AddLine(simulationManager, uc.GuiaInferior);
CrearAnimacionStoryBoardTrasnporte(uc.Transporte);
}
Motor = Motor; // Forzar la busqueda
}
public override void ucUnLoaded()
{
// El UserControl se esta eliminando
// eliminar el objeto de simulacion
simulationManager.Remove(SimGeometria);
simulationManager.Remove(Guia_Superior);
simulationManager.Remove(Guia_Inferior);
}
}
public partial class ucTransporteGuias : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public ucTransporteGuias()
{
InitializeComponent();
this.Loaded += OnLoaded;
this.Unloaded += OnUnloaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
Datos?.ucLoaded();
}
private void OnUnloaded(object sender, RoutedEventArgs e)
{
Datos?.ucUnLoaded();
}
public void Resize(float width, float height)
{
if (Datos is osTransporteGuias datos)
datos.Ancho = PixelToMeter.Instance.calc.PixelsToMeters(width);
}
public void Move(float LeftPixels, float TopPixels)
{
if (Datos != null)
{
Datos.Left = PixelToMeter.Instance.calc.PixelsToMeters(LeftPixels);
Datos.Top = PixelToMeter.Instance.calc.PixelsToMeters(TopPixels);
}
}
public void Rotate(float Angle)
{
if (Datos != null)
if (Datos is osTransporteGuias datos)
datos.Angulo = Angle;
}
public void Highlight(bool State) { }
public int ZIndex()
{
return 1;
}
}
}

View File

@ -0,0 +1,44 @@
<UserControl x:Class="CtrEditor.ObjetosSim.ucTransporteTTop"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
mc:Ignorable="d"
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim"
xmlns:convert="clr-namespace:CtrEditor.Convertidores">
<UserControl.Resources>
<convert:MeterToPixelConverter x:Key="MeterToPixelConverter"/>
<!-- Define the VisualBrush for the conveyor belt pattern -->
<VisualBrush x:Key="BeltBrush" TileMode="Tile" Viewport="0,0,20,10" ViewportUnits="Absolute" Viewbox="0,0,20,10" ViewboxUnits="Absolute">
<VisualBrush.Transform>
<TransformGroup>
<TranslateTransform/>
</TransformGroup>
</VisualBrush.Transform>
<VisualBrush.Visual>
<Canvas>
<Rectangle Fill="Gray" Width="10" Height="10"/>
<Rectangle Fill="DarkGray" Width="10" Height="10" Canvas.Left="10"/>
</Canvas>
</VisualBrush.Visual>
</VisualBrush>
</UserControl.Resources>
<UserControl.DataContext>
<vm:osTransporteTTop Ancho="2"/>
</UserControl.DataContext>
<Canvas>
<Rectangle x:Name="Transporte"
Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}"
Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}}"
Fill="{StaticResource BeltBrush}">
<Rectangle.RenderTransform>
<RotateTransform Angle="{Binding Angulo}"/>
</Rectangle.RenderTransform>
</Rectangle>
</Canvas>
</UserControl>

View File

@ -0,0 +1,181 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Animation;
using System.Windows.Media;
using CommunityToolkit.Mvvm.ComponentModel;
using CtrEditor.Convertidores;
using CtrEditor.Siemens;
using CtrEditor.Simulacion;
using System.Windows.Input;
namespace CtrEditor.ObjetosSim
{
/// <summary>
/// Interaction logic for ucTransporteTTop.xaml
/// </summary>
///
public partial class osTransporteTTop : osBase, IosBase
{
private osBase _osMotor = null;
private simTransporte SimGeometria;
public static string NombreClase()
{
return "Transporte";
}
private string nombre = "Transporte TTOP";
public override string Nombre
{
get => nombre;
set => SetProperty(ref nombre, value);
}
private float velocidadActual;
public float VelocidadActual
{
get => velocidadActual;
set
{
if (value != velocidadActual)
{
velocidadActual = value;
SimGeometria?.SetSpeed(value);
SetProperty(ref velocidadActual, value);
ActualizarAnimacionStoryBoardTransporte(VelocidadActual);
}
}
}
[ObservableProperty]
public string motor;
partial void OnMotorChanged(string value)
{
_osMotor = ObtenerLink(Motor, typeof(osVMmotorSim));
}
[ObservableProperty]
public float ancho;
[ObservableProperty]
public float alto;
[ObservableProperty]
public float angulo;
[ObservableProperty]
public float frictionCoefficient;
[ObservableProperty]
public float velMax50hz;
[ObservableProperty]
public float tiempoRampa;
[ObservableProperty]
public bool esMarcha;
private void ActualizarGeometrias()
{
if (_visualRepresentation is ucTransporteTTop uc)
{
UpdateRectangle(SimGeometria, uc.Transporte,Alto,Ancho,Angulo);
SimGeometria.Speed = VelocidadActual;
}
ActualizarAnimacionStoryBoardTransporte(VelocidadActual);
}
public osTransporteTTop()
{
Ancho = 1;
Alto = 0.10f;
}
public override void SimulationStop()
{
// Se llama al detener la simulacion
ActualizarAnimacionStoryBoardTransporte(VelocidadActual);
}
public override void UpdateGeometryStart()
{
// Se llama antes de la simulacion
ActualizarGeometrias();
}
public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds)
{
if (_osMotor != null)
{
if (_osMotor is osVMmotorSim motor)
VelocidadActual = motor.Velocidad;
}
else if (Motor.Length > 0)
_osMotor = ObtenerLink(Motor, typeof(osVMmotorSim));
}
public override void ucLoaded()
{
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
ActualizarLeftTop();
if (_visualRepresentation is ucTransporteTTop uc)
{
SimGeometria = AddRectangle(simulationManager, uc.Transporte, Alto, Ancho, Angulo);
CrearAnimacionStoryBoardTrasnporte(uc.Transporte);
}
}
public override void ucUnLoaded()
{
// El UserControl se esta eliminando
// eliminar el objeto de simulacion
simulationManager.Remove(SimGeometria);
}
}
public partial class ucTransporteTTop : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public ucTransporteTTop()
{
InitializeComponent();
this.Loaded += OnLoaded;
this.Unloaded += OnUnloaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
Datos?.ucLoaded();
}
private void OnUnloaded(object sender, RoutedEventArgs e)
{
Datos?.ucUnLoaded();
}
public void Resize(float width, float height)
{
if (Datos is osTransporteTTop datos)
datos.Ancho = PixelToMeter.Instance.calc.PixelsToMeters(width);
}
public void Move(float LeftPixels, float TopPixels)
{
if (Datos != null)
{
Datos.Left = PixelToMeter.Instance.calc.PixelsToMeters(LeftPixels);
Datos.Top = PixelToMeter.Instance.calc.PixelsToMeters(TopPixels);
}
}
public void Rotate(float Angle) {
if (Datos != null)
if (Datos is osTransporteTTop datos)
datos.Angulo = Angle;
}
public void Highlight(bool State) { }
public int ZIndex()
{
return 1;
}
}
}

View File

@ -0,0 +1,37 @@
<UserControl x:Class="CtrEditor.ObjetosSim.ucVMmotorSim"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:CtrEditor.ObjetosSim"
mc:Ignorable="d"
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim"
xmlns:convert="clr-namespace:CtrEditor.Convertidores">
<UserControl.Resources>
<convert:MeterToPixelConverter x:Key="MeterToPixelConverter"/>
</UserControl.Resources>
<UserControl.DataContext>
<vm:osVMmotorSim ImageSource_oculta="/imagenes/motorNegro.png" />
</UserControl.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Label Grid.Row="0" Content="{Binding Nombre}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Background="Transparent"
Foreground="Black"/>
<Image Grid.Row="1" Source="{Binding ImageSource_oculta}"
Width="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}}"
Height="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}}"
Stretch="Uniform"/>
</Grid>
</UserControl>

View File

@ -0,0 +1,220 @@
using CtrEditor.Convertidores;
using CtrEditor.Siemens;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using Newtonsoft.Json;
using CommunityToolkit.Mvvm.ComponentModel;
namespace CtrEditor.ObjetosSim
{
/// <summary>
/// Interaction logic for ucVMmotorSim.xaml
/// </summary>
///
public partial class osVMmotorSim : osBase, IosBase
{
// Otros datos y métodos relevantes para la simulación
private VMSimMotor motState = new VMSimMotor();
public static string NombreClase()
{
return "VetroMeccanica Motor";
}
private string nombre = NombreClase();
public override string Nombre
{
get => nombre;
set => SetProperty(ref nombre, value);
}
[JsonIgnore]
[ObservableProperty]
public ImageSource imageSource_oculta;
[ObservableProperty]
public float tamano;
[ObservableProperty]
public float maxRatedHz;
[ObservableProperty]
public float tiempoRampa;
partial void OnTiempoRampaChanged(float value)
{
if (value < 0.1f)
value = 0.1f;
tiempoRampa = value;
}
[ObservableProperty]
public bool encendido;
[ObservableProperty]
public int pLC_NumeroMotor;
[ObservableProperty]
public float ratio;
[ObservableProperty]
public float velocidad;
partial void OnVelocidadChanged(float value)
{
if (value > 0)
ImageSource_oculta = ImageFromPath("/imagenes/motorVerde.png");
else
ImageSource_oculta = ImageFromPath("/imagenes/motorNegro.png");
}
public osVMmotorSim()
{
Tamano = 0.30f;
PLC_NumeroMotor = 31;
MaxRatedHz = 100;
TiempoRampa = 3;
ImageSource_oculta = ImageFromPath("/imagenes/motor2.png");
}
public override void UpdateGeometryStart()
{
// Se llama antes de la simulacion
}
public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds) {
motState.UpdatePLC(plc,PLC_NumeroMotor,Encendido, elapsedMilliseconds);
Velocidad = motState.STATUS_VFD_ACT_Speed_Hz / 10;
}
public override void UpdateControl(int elapsedMilliseconds)
{
// Calculamos la velocidad
motState.UpdateSpeed(MaxRatedHz,TiempoRampa, elapsedMilliseconds);
}
public override void ucLoaded()
{
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
ActualizarLeftTop();
}
}
public partial class ucVMmotorSim : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public ucVMmotorSim()
{
InitializeComponent();
this.Loaded += OnLoaded;
this.Unloaded += OnUnloaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
Datos?.ucLoaded();
}
private void OnUnloaded(object sender, RoutedEventArgs e)
{
Datos?.ucUnLoaded();
}
public void Resize(float width, float height) { }
public void Move(float LeftPixels, float TopPixels)
{
if (Datos != null)
{
Datos.Left = PixelToMeter.Instance.calc.PixelsToMeters(LeftPixels);
Datos.Top = PixelToMeter.Instance.calc.PixelsToMeters(TopPixels);
}
}
public void Rotate(float Angle) { }
public void Highlight(bool State) { }
public int ZIndex()
{
return 10;
}
}
public class VMSimMotor
{
public bool _STATUS_VFD_Ready;
public float STATUS_VFD_ACT_Speed_Hz;
public bool Motor_Running;
public bool STATUS_VFD_Trip;
public bool STATUS_VFD_Warning;
public bool STATUS_VFD_Coasting;
public bool OUT_Run;
public bool OUT_Stop;
public bool OUT_Reversal;
public float OUT_OUT_VFD_REQ_Speed_Hz;
public void UpdatePLC(PLCModel plc, int NumeroMotor, bool Encendido, int elapsedMilliseconds)
{
var index = 0;
switch (NumeroMotor)
{
case < 100:
index = (int)NumeroMotor - 30 + 300;
break;
}
OUT_Run = plc.LeerTagBool($"\"DB MotorSimulate\".Motors[{index}].OUT.Run");
OUT_Reversal = plc.LeerTagBool($"\"DB MotorSimulate\".Motors[{index}].OUT.\"Reversal Direction\"");
OUT_OUT_VFD_REQ_Speed_Hz = (float)plc.LeerTagInt16($"\"DB MotorSimulate\".Motors[{index}].OUT.OUT_VFD_REQ_Speed_Hz");
if (Encendido)
{
_STATUS_VFD_Ready = true;
Motor_Running = true;
STATUS_VFD_Trip = false;
STATUS_VFD_Warning = false;
STATUS_VFD_Coasting = false;
}
else
{
_STATUS_VFD_Ready = false;
Motor_Running = false;
STATUS_VFD_Trip = true;
STATUS_VFD_Warning = false;
STATUS_VFD_Coasting = false;
}
plc.EscribirTagBool($"\"DB MotorSimulate\".Motors[{index}].STATUS_VFD_Ready", _STATUS_VFD_Ready);
plc.EscribirTagBool($"\"DB MotorSimulate\".Motors[{index}].Motor_Running", Motor_Running);
plc.EscribirTagBool($"\"DB MotorSimulate\".Motors[{index}].STATUS_VFD_Trip", STATUS_VFD_Trip);
plc.EscribirTagBool($"\"DB MotorSimulate\".Motors[{index}].STATUS_VFD_Warning", STATUS_VFD_Warning);
plc.EscribirTagBool($"\"DB MotorSimulate\".Motors[{index}].STATUS_VFD_Coasting", STATUS_VFD_Coasting);
plc.EscribirTagInt16($"\"DB MotorSimulate\".Motors[{index}].STATUS_VFD_ACT_Speed_Hz", (int)STATUS_VFD_ACT_Speed_Hz);
}
private float CalcSpeedRamp(float MaxRatedHz, float TiempoRampa, float actual, float expected, int elapsedMilliseconds)
{
float hzIncrementsRamp = (MaxRatedHz * 10) / (TiempoRampa * (1000.0f / elapsedMilliseconds));
float delta = expected - actual;
// Conrtolar si la diferencia no es mayor de lo que falta
if (Math.Abs(hzIncrementsRamp) > Math.Abs(delta))
hzIncrementsRamp = Math.Abs(delta);
if (delta < 0)
return -hzIncrementsRamp;
else
return hzIncrementsRamp;
}
public void UpdateSpeed(float MaxRatedHz, float TiempoRampa, int elapsedMilliseconds)
{
// Calculamos la velocidad
STATUS_VFD_ACT_Speed_Hz += CalcSpeedRamp(MaxRatedHz, TiempoRampa, STATUS_VFD_ACT_Speed_Hz, OUT_OUT_VFD_REQ_Speed_Hz, elapsedMilliseconds);
}
}
}

View File

@ -0,0 +1,41 @@
<UserControl x:Class="CtrEditor.ObjetosSim.ucBoton"
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:convert="clr-namespace:CtrEditor.Convertidores"
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim"
mc:Ignorable="d">
<UserControl.Resources>
<convert:MeterToPixelConverter x:Key="MeterToPixelConverter"/>
</UserControl.Resources>
<UserControl.DataContext>
<vm:osBoton Color="#FFADE6C0" ColorButton_oculto="#FFC72323"/>
</UserControl.DataContext>
<Grid>
<Border x:Name="BackgroundRectangle"
BorderBrush="Black"
BorderThickness="2"
CornerRadius="0,0,10,10"
Width="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}}"
Height="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}, ConverterParameter=1.5}"
VerticalAlignment="Top"
HorizontalAlignment="Left"
Background="Gray"/>
<Ellipse Fill="{Binding ColorButton_oculto}"
Stroke="Black"
StrokeThickness="2"
Width="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}}"
Height="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}}"
HorizontalAlignment="Left"
VerticalAlignment="Bottom"
MouseLeftButtonDown="Ellipse_MouseLeftButtonDown"
MouseLeftButtonUp="Ellipse_MouseLeftButtonUp">
</Ellipse>
</Grid>
</UserControl>

View File

@ -0,0 +1,160 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CtrEditor.Convertidores;
using CtrEditor.Siemens;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
namespace CtrEditor.ObjetosSim
{
/// <summary>
/// Interaction logic for ucBoton.xaml
/// </summary>
public partial class osBoton : osBase, IosBase
{
// Otros datos y métodos relevantes para la simulación
public static string NombreClase()
{
return "Boton";
}
private string nombre = NombreClase();
public override string Nombre
{
get => nombre;
set => SetProperty(ref nombre, value);
}
[ObservableProperty]
public bool tipo_NC;
[ObservableProperty]
private Brush color;
[ObservableProperty]
private Brush colorButton_oculto;
[ObservableProperty]
public float tamano;
[ObservableProperty]
public bool estado;
partial void OnEstadoChanged(bool value)
{
if (value)
ColorButton_oculto = Brushes.LightGreen;
else
ColorButton_oculto = Color;
if (!tipo_NC)
EscribirBitTag(Tag, value);
else
EscribirBitTag(Tag, !value);
}
[ObservableProperty]
public string tag;
public void ButtonDownCommand()
{
Estado = true;
}
public void ButtonUpCommand()
{
Estado = false;
}
public osBoton()
{
Tamano = 0.30f;
Estado = false;
Tag = "M50.0";
// Set initial color
Color = Brushes.LightBlue;
}
public override void UpdateGeometryStart()
{
// Se llama antes de la simulacion
}
public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds)
{
}
public override void UpdateControl(int elapsedMilliseconds)
{
}
public override void ucLoaded()
{
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
ActualizarLeftTop();
}
public override void ucUnLoaded()
{
// El UserControl se esta eliminando
// eliminar el objeto de simulacion
}
}
public partial class ucBoton : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public ucBoton()
{
InitializeComponent();
this.DataContextChanged += OnDataContextChanged;
this.Loaded += OnLoaded;
this.Unloaded += OnUnloaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
Datos?.ucLoaded();
}
private void OnUnloaded(object sender, RoutedEventArgs e)
{
Datos?.ucUnLoaded();
}
private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
Datos = e.NewValue as osBase;
}
private void Ellipse_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (Datos is osBoton osBotonData)
{
osBotonData.ButtonDownCommand();
e.Handled = true; // Evita que el evento se propague
}
}
private void Ellipse_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (Datos is osBoton osBotonData)
{
osBotonData.ButtonUpCommand();
e.Handled = true; // Evita que el evento se propague
}
}
public void Resize(float width, float height) { }
public void Move(float LeftPixels, float TopPixels)
{
if (Datos != null)
{
Datos.Left = PixelToMeter.Instance.calc.PixelsToMeters(LeftPixels);
Datos.Top = PixelToMeter.Instance.calc.PixelsToMeters(TopPixels);
}
}
public void Rotate(float Angle) { }
public void Highlight(bool State) { }
public int ZIndex()
{
return 10;
}
}
}

View File

@ -0,0 +1,46 @@
<UserControl x:Class="CtrEditor.ObjetosSim.ucGearEncoder"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:CtrEditor.ObjetosSim.UserControls"
mc:Ignorable="d"
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim"
xmlns:convert="clr-namespace:CtrEditor.Convertidores">
<UserControl.Resources>
<convert:MeterToPixelConverter x:Key="MeterToPixelConverter"/>
</UserControl.Resources>
<UserControl.DataContext>
<vm:osGearEncoder Dientes="9"/>
</UserControl.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding Radio_Externo, Converter={StaticResource MeterToPixelConverter}, ConverterParameter=1}"/>
<ColumnDefinition Width="{Binding Radio_Externo, Converter={StaticResource MeterToPixelConverter}, ConverterParameter=0.02}"/>
<!-- Espacio entre el Rectangle y el Canvas -->
<ColumnDefinition Width="{Binding Radio_Externo, Converter={StaticResource MeterToPixelConverter}, ConverterParameter=1}"/>
</Grid.ColumnDefinitions>
<!-- StackPanel to contain the Rectangle -->
<Canvas Grid.Column="0" HorizontalAlignment="Right">
<Rectangle Canvas.Top="{Binding Radio_Externo, Converter={StaticResource MeterToPixelConverter}, ConverterParameter=-0.12}"
Grid.Column="0" Width="{Binding Radio_Externo, Converter={StaticResource MeterToPixelConverter}, ConverterParameter=1}"
Height="{Binding Radio_Externo, Converter={StaticResource MeterToPixelConverter}, ConverterParameter=0.25}"
Fill="{Binding Color_oculto}"
HorizontalAlignment="Left" VerticalAlignment="Center"/>
</Canvas>
<Canvas Grid.Column="0" HorizontalAlignment="Left">
<Canvas.RenderTransform>
<RotateTransform Angle="{Binding Angulo}" CenterX="0" CenterY="0"/>
</Canvas.RenderTransform>
<local:GearControl ToothWidthRatio="{Binding Ancho_Dientes}" TeethCount="{Binding Dientes}"
InnerRadius="{Binding Radio_Interno, Converter={StaticResource MeterToPixelConverter}}"
OuterRadius="{Binding Radio_Externo, Converter={StaticResource MeterToPixelConverter}}"
HorizontalAlignment="Center" VerticalAlignment="Center" />
</Canvas>
</Grid>
</UserControl>

View File

@ -0,0 +1,233 @@
using CtrEditor.Convertidores;
using CtrEditor.Siemens;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using Newtonsoft.Json;
using CommunityToolkit.Mvvm.ComponentModel;
using System.Diagnostics;
namespace CtrEditor.ObjetosSim
{
/// <summary>
/// Interaction logic for ucGearEncoder.xaml
/// </summary>
public partial class osGearEncoder : osBase, IosBase
{
private osBase _osMotor = null;
private Stopwatch Stopwatch = new Stopwatch();
private double stopwatch_last = 0;
// Otros datos y métodos relevantes para la simulación
public static string NombreClase()
{
return "Ruota Fonica";
}
private string nombre = NombreClase();
public override string Nombre
{
get => nombre;
set => SetProperty(ref nombre, value);
}
[ObservableProperty]
public string tag;
[ObservableProperty]
public bool pulso;
partial void OnPulsoChanged(bool value)
{
if (value)
{
var dTime = Stopwatch.ElapsedMilliseconds - stopwatch_last;
stopwatch_last = Stopwatch.ElapsedMilliseconds;
Tiempo_Pulso = (float)dTime;
}
EscribirBitTag(Tag, Pulso);
if (value)
Color_oculto = Brushes.LightGreen;
else
Color_oculto = Brushes.Gray;
}
[ObservableProperty]
private Brush color_oculto;
[ObservableProperty]
public float velocidadActual;
[ObservableProperty]
public double angulo;
partial void OnAnguloChanged(double value)
{
// Generar pulsos cuadrados en función del ángulo
Pulso = DetectarDiente();
}
[ObservableProperty]
public float tiempo_Pulso;
[ObservableProperty]
public float pulsos_Por_Segundo;
partial void OnPulsos_Por_SegundoChanged(float value)
{
if (VelocidadActual > 0)
Giros_segundo_a_100 = (float)((pulsos_Por_Segundo / Dientes) * 100 / VelocidadActual);
}
[ObservableProperty]
private bool homing;
partial void OnHomingChanged(bool value)
{
homing = false;
Angulo = 0;
}
[ObservableProperty]
public float dientes;
[ObservableProperty]
public float radio_Interno;
[ObservableProperty]
public float radio_Externo;
[ObservableProperty]
public float ancho_Dientes;
[ObservableProperty]
public float giros_segundo_a_100;
[ObservableProperty]
public string motor;
partial void OnMotorChanged(string value)
{
_osMotor = ObtenerLink(Motor, typeof(osVMmotorSim));
}
public osGearEncoder()
{
Ancho_Dientes = 0.5f;
Dientes = 10;
Radio_Interno = 0.5f;
Radio_Externo = 0.6f;
Color_oculto = Brushes.Gray;
Stopwatch.Start();
}
public override void UpdateGeometryStart()
{
// Se llama antes de la simulacion
}
public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds)
{
if (_osMotor != null)
{
if (_osMotor is osVMmotorSim motor)
{
VelocidadActual = motor.Velocidad;
// Calcular la cantidad de giros por segundo
double girosPorSegundo = (VelocidadActual / 100.0) * Giros_segundo_a_100;
// Calcular la fracción del segundo que ha pasado
double segundosTranscurridos = elapsedMilliseconds / 1000.0;
// Calcular el incremento de ángulo
double incrementoAngulo = (girosPorSegundo * 360.0) * segundosTranscurridos;
// Actualizar el ángulo
Angulo = (Angulo + incrementoAngulo) % 360;
}
} else if (Motor.Length>0)
_osMotor = ObtenerLink(Motor, typeof(osVMmotorSim));
}
public bool DetectarDiente()
{
double angleStep = 360.0 / Dientes;
double halfToothWidthAngle = (angleStep * Ancho_Dientes) / 2;
// Normalize the Angulo to be within 0-360 degrees
double normalizedAngle = Angulo % 360;
if (normalizedAngle < 0) normalizedAngle += 360;
// Calculate the position of the first tooth's center at angle 0
double firstToothCenterAngle = 0;
// Calculate the angular distance from the current angle to the first tooth's center
double angularDistance = Math.Abs(firstToothCenterAngle - normalizedAngle);
if (angularDistance > 180) angularDistance = 360 - angularDistance;
// Check if the normalized angle falls within the segment of the first tooth
if (angularDistance <= halfToothWidthAngle)
{
return true;
}
// Calculate the number of steps to reach the nearest tooth's center
int steps = (int)Math.Round(normalizedAngle / angleStep);
// Calculate the angular position of the nearest tooth's center
double nearestToothCenterAngle = steps * angleStep;
// Calculate the angular distance from the current angle to the nearest tooth's center
angularDistance = Math.Abs(nearestToothCenterAngle - normalizedAngle);
if (angularDistance > 180) angularDistance = 360 - angularDistance;
// Check if the normalized angle falls within the segment of the nearest tooth
return angularDistance <= halfToothWidthAngle;
}
public override void UpdateControl(int elapsedMilliseconds)
{
// Calculamos la velocidad
}
public override void ucLoaded()
{
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
ActualizarLeftTop();
}
}
public partial class ucGearEncoder : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public ucGearEncoder()
{
InitializeComponent();
this.Loaded += OnLoaded;
this.Unloaded += OnUnloaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
Datos?.ucLoaded();
}
private void OnUnloaded(object sender, RoutedEventArgs e)
{
Datos?.ucUnLoaded();
}
public void Resize(float width, float height) { }
public void Move(float LeftPixels, float TopPixels)
{
if (Datos != null)
{
Datos.Left = PixelToMeter.Instance.calc.PixelsToMeters(LeftPixels);
Datos.Top = PixelToMeter.Instance.calc.PixelsToMeters(TopPixels);
}
}
public void Rotate(float Angle) { }
public void Highlight(bool State) { }
public int ZIndex()
{
return 10;
}
}
}

View File

@ -0,0 +1,57 @@
<UserControl x:Class="CtrEditor.ObjetosSim.ucPhotocell"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:CtrEditor.ObjetosSim.UserControls"
mc:Ignorable="d"
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim"
xmlns:convert="clr-namespace:CtrEditor.Convertidores">
<UserControl.Resources>
<convert:MeterToPixelConverter x:Key="MeterToPixelConverter"/>
</UserControl.Resources>
<UserControl.DataContext>
<vm:osPhotocell Color="#FFCA1C1C"/>
</UserControl.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<!-- Columna para el Label -->
<ColumnDefinition Width="*"/>
<!-- Columna para la Image -->
<ColumnDefinition Width="*"/>
<!-- Columna para el Rectangle -->
</Grid.ColumnDefinitions>
<Grid.RenderTransform>
<RotateTransform Angle="{Binding Angulo}" CenterX="0" CenterY="0"/>
</Grid.RenderTransform>
<!-- Label en la primera columna -->
<Label Content="{Binding Nombre}" VerticalAlignment="Center" HorizontalAlignment="Center" Grid.Column="0" Foreground="{Binding Color}" >
</Label>
<Image Source="/Icons/fotocelula.png"
Width="{Binding Alto, Converter={StaticResource MeterToPixelConverter}, ConverterParameter=3}"
Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}, ConverterParameter=3}"
VerticalAlignment="Center" HorizontalAlignment="Left" Margin="0,0,0,0" Grid.Column="1"/>
<Rectangle x:Name="Photocell" Grid.Column="2" Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}" Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}}">
<Rectangle.Fill>
<VisualBrush x:Name="MovingPattern" TileMode="Tile" Viewport="0,0,3,3" ViewportUnits="Absolute" Viewbox="0,0,3,3" ViewboxUnits="Absolute">
<VisualBrush.Visual>
<Canvas>
<Ellipse Width="2" Height="2" Fill="{Binding Color}"/>
</Canvas>
</VisualBrush.Visual>
</VisualBrush>
</Rectangle.Fill>
<!-- No se aplica la transformación aquí -->
</Rectangle>
</Grid>
</UserControl>

View File

@ -0,0 +1,180 @@
using CtrEditor.Convertidores;
using CtrEditor.Simulacion;
using CtrEditor.Siemens;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using CommunityToolkit.Mvvm.ComponentModel;
namespace CtrEditor.ObjetosSim
{
/// <summary>
/// Interaction logic for ucPhotocell.xaml
/// </summary>
public partial class osPhotocell : osBase, IosBase
{
private simBarrera Simulation_Photocell;
public static string NombreClase()
{
return "Photocell";
}
private string nombre = NombreClase();
public override string Nombre
{
get => nombre;
set => SetProperty(ref nombre, value);
}
[ObservableProperty]
public Brush color;
[ObservableProperty]
public bool luzCortada;
partial void OnLuzCortadaChanged(bool value)
{
if (LuzCortada)
Color = Brushes.Blue;
else
Color = Brushes.Green;
if (Tipo_NC)
EscribirBitTag(TagPhotocell_OUT, !LuzCortada);
else
EscribirBitTag(TagPhotocell_OUT, LuzCortada);
}
[ObservableProperty]
public bool tipo_NC;
[ObservableProperty]
public string tagPhotocell_OUT;
public override void LeftChanged(float value)
{
ActualizarGeometrias();
}
public override void TopChanged(float value)
{
ActualizarGeometrias();
}
[ObservableProperty]
public float ancho;
partial void OnAnchoChanged(float value)
{
ActualizarGeometrias();
}
[ObservableProperty]
public float alto;
partial void OnAltoChanged(float value)
{
ActualizarGeometrias();
}
[ObservableProperty]
public float angulo;
partial void OnAnguloChanged(float value)
{
ActualizarGeometrias();
}
private void ActualizarGeometrias()
{
if (_visualRepresentation is ucPhotocell uc)
UpdateRectangle(Simulation_Photocell, uc.Photocell, Alto, Ancho, Angulo);
}
public osPhotocell()
{
Ancho = 1;
Alto = 0.03f;
}
public override void UpdateGeometryStart()
{
// Se llama antes de la simulacion
ActualizarGeometrias();
}
public override void UpdateControl(int elapsedMilliseconds)
{
LuzCortada = Simulation_Photocell.LuzCortada;
}
public override void UpdateGeometryStep()
{
Simulation_Photocell.LuzCortada = false;
}
public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds) {
}
public override void ucLoaded()
{
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
ActualizarLeftTop();
if (_visualRepresentation is ucPhotocell uc)
Simulation_Photocell = AddBarrera(simulationManager, uc.Photocell, Alto, Ancho, Angulo);
}
public override void ucUnLoaded()
{
// El UserControl se esta eliminando
// eliminar el objeto de simulacion
simulationManager.Remove(Simulation_Photocell);
}
}
public partial class ucPhotocell : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public ucPhotocell()
{
InitializeComponent();
this.Loaded += OnLoaded;
this.Unloaded += OnUnloaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
Datos?.ucLoaded();
}
private void OnUnloaded(object sender, RoutedEventArgs e)
{
Datos?.ucUnLoaded();
}
public void Resize(float width, float height)
{
if (width == 0) return;
if (Datos is osPhotocell datos)
datos.Ancho = PixelToMeter.Instance.calc.PixelsToMeters(width);
}
public void Move(float LeftPixels, float TopPixels)
{
if (Datos != null)
{
Datos.Left = PixelToMeter.Instance.calc.PixelsToMeters(LeftPixels);
Datos.Top = PixelToMeter.Instance.calc.PixelsToMeters(TopPixels);
}
}
public void Rotate(float Angle)
{
if (Datos != null)
if (Datos is osPhotocell datos)
datos.Angulo = Angle;
}
public void Highlight(bool State) { }
public int ZIndex()
{
return 16;
}
}
}

View File

@ -0,0 +1,39 @@
<UserControl x:Class="CtrEditor.ObjetosSim.ucSensTemperatura"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:CtrEditor.ObjetosSim"
mc:Ignorable="d"
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim"
xmlns:convert="clr-namespace:CtrEditor.Convertidores">
<UserControl.Resources>
<convert:MeterToPixelConverter x:Key="MeterToPixelConverter"/>
</UserControl.Resources>
<UserControl.DataContext>
<vm:osSensTemperatura />
</UserControl.DataContext>
<Grid>
<Border x:Name="BackgroundRectangle"
BorderBrush="Black"
BorderThickness="2"
Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}, ConverterParameter=1.5}"
VerticalAlignment="Top"
Background="Gray"
HorizontalAlignment="Center"
Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}">
<Grid>
<Slider
HorizontalAlignment="Center"
VerticalAlignment="Center"
Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}}"
Orientation="Vertical"
Value="{Binding Value}"
Maximum="100"/>
</Grid>
</Border>
</Grid>
</UserControl>

View File

@ -0,0 +1,117 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CtrEditor.Convertidores;
using CtrEditor.Siemens;
using System.Windows;
using System.Windows.Controls;
namespace CtrEditor.ObjetosSim
{
/// <summary>
/// Interaction logic for ucSensTemperatura.xaml
/// </summary>
public partial class osSensTemperatura : osBase, IosBase
{
// Otros datos y métodos relevantes para la simulación
public static string NombreClase()
{
return "Temperatura";
}
private string nombre = NombreClase();
public override string Nombre
{
get => nombre;
set => SetProperty(ref nombre, value);
}
[ObservableProperty]
public string tag;
[ObservableProperty]
public float min_OUT_Scaled;
[ObservableProperty]
public float max_OUT_Scaled;
[ObservableProperty]
public float value;
partial void OnValueChanged(float value)
{
EscribirWordTagScaled(Tag, value, 0, 100, Min_OUT_Scaled, Max_OUT_Scaled);
}
[ObservableProperty]
public float ancho;
[ObservableProperty]
public float alto;
[ObservableProperty]
public float angulo;
public osSensTemperatura()
{
Ancho = 0.4f;
Alto = 1f;
Max_OUT_Scaled = 27648;
Min_OUT_Scaled = 0;
}
public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds)
{
}
public override void ucLoaded()
{
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
ActualizarLeftTop();
}
}
public partial class ucSensTemperatura : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public ucSensTemperatura()
{
InitializeComponent();
this.Loaded += OnLoaded;
this.Unloaded += OnUnloaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
Datos?.ucLoaded();
}
private void OnUnloaded(object sender, RoutedEventArgs e)
{
Datos?.ucUnLoaded();
}
public void Resize(float width, float height)
{
if (Datos is osSensTemperatura datos)
{
datos.Ancho = PixelToMeter.Instance.calc.PixelsToMeters(width);
datos.Alto = PixelToMeter.Instance.calc.PixelsToMeters(width);
}
}
public void Move(float LeftPixels, float TopPixels)
{
if (Datos != null)
{
Datos.Left = PixelToMeter.Instance.calc.PixelsToMeters(LeftPixels);
Datos.Top = PixelToMeter.Instance.calc.PixelsToMeters(TopPixels);
}
}
public void Rotate(float Angle)
{
if (Datos != null)
if (Datos is osSensTemperatura datos)
datos.Angulo = Angle;
}
public void Highlight(bool State) { }
public int ZIndex()
{
return 10;
}
}
}

View File

@ -0,0 +1,39 @@
<UserControl x:Class="CtrEditor.ObjetosSim.ucAnalogTag"
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:convert="clr-namespace:CtrEditor.Convertidores"
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim"
mc:Ignorable="d">
<UserControl.Resources>
<convert:MeterToPixelConverter x:Key="MeterToPixelConverter"/>
</UserControl.Resources>
<UserControl.DataContext>
<vm:osAnalogTag/>
</UserControl.DataContext>
<Grid Margin="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<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="BlueViolet"/>
<Label Content="{Binding Descripcion}" Grid.Column="1" VerticalAlignment="Center" Background="{Binding Color}" />
<TextBox Grid.Column="2" Text="{Binding Value}" VerticalAlignment="Center" Width="80" TextAlignment="Center" Background="White"/>
</Grid>
</UserControl>

View File

@ -0,0 +1,130 @@
using CtrEditor.Convertidores;
using CtrEditor.Siemens;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using CommunityToolkit.Mvvm.ComponentModel;
using Newtonsoft.Json.Linq;
namespace CtrEditor.ObjetosSim
{
/// <summary>
/// Interaction logic for ucAnalogTag.xaml
/// </summary>
///
public partial class osAnalogTag : osBase, IosBase
{
// Otros datos y métodos relevantes para la simulación
public static string NombreClase()
{
return "Analog Tag";
}
private string nombre = NombreClase();
public override string Nombre
{
get => nombre;
set => SetProperty(ref nombre, value);
}
[ObservableProperty]
public float tamano;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(Value))]
public string tag;
[ObservableProperty]
public string descripcion;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(Value))]
public float min_IN_Scaled;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(Value))]
public float max_IN_Scaled;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(Value))]
public float min_OUT_Scaled;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(Value))]
public float max_OUT_Scaled;
[ObservableProperty]
public float value;
partial void OnValueChanged(float value)
{
EscribirWordTagScaled(Tag, Value, Min_IN_Scaled, Max_IN_Scaled, Min_OUT_Scaled, Max_OUT_Scaled);
}
public osAnalogTag()
{
Tamano = 0.30f;
tag = "%MW50.0";
Descripcion = "Nombre del Tag:";
Max_OUT_Scaled = 27648;
Min_OUT_Scaled = 0;
min_IN_Scaled = 0;
max_IN_Scaled = 100;
}
public override void UpdateGeometryStart()
{
// Se llama antes de la simulacion
}
public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds)
{
}
public override void UpdateControl(int elapsedMilliseconds)
{
// Calculamos la velocidad
}
public override void ucLoaded()
{
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
ActualizarLeftTop();
}
}
public partial class ucAnalogTag : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public ucAnalogTag()
{
InitializeComponent();
this.Loaded += OnLoaded;
this.Unloaded += OnUnloaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
Datos?.ucLoaded();
}
private void OnUnloaded(object sender, RoutedEventArgs e)
{
Datos?.ucUnLoaded();
}
public void Resize(float width, float height) { }
public void Move(float LeftPixels, float TopPixels)
{
if (Datos != null)
{
Datos.Left = PixelToMeter.Instance.calc.PixelsToMeters(LeftPixels);
Datos.Top = PixelToMeter.Instance.calc.PixelsToMeters(TopPixels);
}
}
public void Rotate(float Angle) { }
public void Highlight(bool State) { }
public int ZIndex()
{
return 10;
}
}
}

View File

@ -0,0 +1,37 @@
<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:convert="clr-namespace:CtrEditor.Convertidores"
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim"
mc:Ignorable="d">
<UserControl.Resources>
<convert:MeterToPixelConverter x:Key="MeterToPixelConverter"/>
</UserControl.Resources>
<UserControl.DataContext>
<vm:osBoolTag/>
</UserControl.DataContext>
<Grid Margin="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<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="Gray"/>
<Label Content="{Binding Descripcion}" Grid.Column="1" VerticalAlignment="Center" Background="{Binding Color}" />
<CheckBox Grid.Column="2" VerticalAlignment="Center" IsChecked="{Binding Estado}" />
</Grid>
</UserControl>

View File

@ -0,0 +1,106 @@
using CtrEditor.Convertidores;
using CtrEditor.Siemens;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using CommunityToolkit.Mvvm.ComponentModel;
using Newtonsoft.Json.Linq;
namespace CtrEditor.ObjetosSim
{
/// <summary>
/// Interaction logic for ucBoolTag.xaml
/// </summary>
///
public partial class osBoolTag : osBase, IosBase
{
// Otros datos y métodos relevantes para la simulación
public static string NombreClase()
{
return "Bool Tag";
}
private string nombre = NombreClase();
public override string Nombre
{
get => nombre;
set => SetProperty(ref nombre, value);
}
[ObservableProperty]
private Brush color_oculto;
[ObservableProperty]
public bool estado;
partial void OnEstadoChanged(bool value)
{
EscribirBitTag(Tag, value);
if (value)
Color_oculto = Brushes.LightGreen;
else
Color_oculto = Brushes.Transparent;
}
[ObservableProperty]
public float tamano;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(Estado))]
public string tag;
[ObservableProperty]
public string descripcion;
public osBoolTag()
{
Tamano = 0.30f;
tag = "%M50.0";
Descripcion = "Nombre del Tag";
}
public override void ucLoaded()
{
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
ActualizarLeftTop();
}
}
public partial class ucBoolTag : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public ucBoolTag()
{
InitializeComponent();
this.Loaded += OnLoaded;
this.Unloaded += OnUnloaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
Datos?.ucLoaded();
}
private void OnUnloaded(object sender, RoutedEventArgs e)
{
Datos?.ucUnLoaded();
}
public void Resize(float width, float height) { }
public void Move(float LeftPixels, float TopPixels)
{
if (Datos != null)
{
Datos.Left = PixelToMeter.Instance.calc.PixelsToMeters(LeftPixels);
Datos.Top = PixelToMeter.Instance.calc.PixelsToMeters(TopPixels);
}
}
public void Rotate(float Angle) { }
public void Highlight(bool State) { }
public int ZIndex()
{
return 10;
}
}
}

View File

@ -0,0 +1,23 @@
<UserControl x:Class="CtrEditor.ObjetosSim.ucConsensGeneric"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim"
xmlns:convert="clr-namespace:CtrEditor.Convertidores"
Background="LightGray">
<UserControl.DataContext>
<vm:osConsensGeneric />
</UserControl.DataContext>
<!-- PanelEdicion con borde superior -->
<Border BorderBrush="LightGreen" BorderThickness="0,10,0,0">
<ScrollViewer Margin="5" VerticalScrollBarVisibility="Auto">
<StackPanel x:Name="PanelEdicion">
<!-- Aquí puedes agregar los controles para editar propiedades -->
</StackPanel>
</ScrollViewer>
</Border>
</UserControl>

View File

@ -0,0 +1,92 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CtrEditor.Convertidores;
using CtrEditor.Siemens;
using System.Windows;
using System.Windows.Controls;
namespace CtrEditor.ObjetosSim
{
/// <summary>
/// Interaction logic for ucConsensGeneric.xaml
/// </summary>
public partial class osConsensGeneric : osBase, IosBase
{
public TagsConsensos Consensos = new TagsConsensos();
[ObservableProperty]
public List<string> tags;
// Otros datos y métodos relevantes para la simulación
public static string NombreClase()
{
return "Consensi";
}
private string nombre = NombreClase();
public override string Nombre
{
get => nombre;
set => SetProperty(ref nombre, value);
}
[ObservableProperty]
public float tamano;
public override void ucLoaded()
{
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
ActualizarLeftTop();
if (_visualRepresentation is ucConsensGeneric uc)
Tags = UserControlFactory.CargarPropiedadesosDatosTags(Consensos, uc.PanelEdicion, null);
}
}
public partial class ucConsensGeneric : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public ucConsensGeneric()
{
InitializeComponent();
this.Loaded += OnLoaded;
this.Unloaded += OnUnloaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
Datos?.ucLoaded();
}
private void OnUnloaded(object sender, RoutedEventArgs e)
{
Datos?.ucUnLoaded();
}
public void Resize(float width, float height) { }
public void Move(float LeftPixels, float TopPixels)
{
if (Datos != null)
{
Datos.Left = PixelToMeter.Instance.calc.PixelsToMeters(LeftPixels);
Datos.Top = PixelToMeter.Instance.calc.PixelsToMeters(TopPixels);
}
}
public void Rotate(float Angle) { }
public void Highlight(bool State) { }
public int ZIndex()
{
return 10;
}
}
public class TagsConsensos
{
public string Tag { get; set; }
public bool Consenso_Uscita { get; set; }
public string Velocidad { get; set; }
}
}

View File

@ -1,10 +1,13 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls;
using CtrEditor.Simulacion;
using System.Reflection;
using CtrEditor.Convertidores;
using System.Windows.Data;
using System.Windows;
using System.Windows.Media;
using CtrEditor.ObjetosSim.UserControls;
using System.Collections;
namespace CtrEditor.ObjetosSim
{
@ -12,43 +15,42 @@ namespace CtrEditor.ObjetosSim
{
public static UserControl? GetControlForType(Type tipoObjeto)
{
if (tipoObjeto == typeof(osBotella))
return new ucBotella();
if (tipoObjeto == typeof(osTransporteTTop))
return new ucTransporteTTop();
if (tipoObjeto == typeof(osGuia))
return new ucGuia();
if (tipoObjeto == typeof(osTransporteGuias))
return new ucTransporteGuias();
if (tipoObjeto == typeof(osTransporteCurva))
return new ucTransporteCurva();
if (tipoObjeto == typeof(osVMmotorSim ))
return new ucVMmotorSim();
// Obtener el nombre del tipo de objeto
string typeName = tipoObjeto.Name;
// Puedes añadir más condiciones para otros tipos
// Cambiar las primeras dos letras de 'os' a 'uc'
if (typeName.StartsWith("os"))
{
string newTypeName = "uc" + typeName.Substring(2);
// Obtener el ensamblado donde se encuentra el tipo UserControl
Assembly assembly = Assembly.GetExecutingAssembly();
// Buscar el tipo en los ensamblados cargados
Type? controlType = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(a => a.GetTypes())
.FirstOrDefault(t => t.Name == newTypeName);
if (controlType != null && typeof(UserControl).IsAssignableFrom(controlType))
{
// Crear una instancia del tipo encontrado
return (UserControl?)Activator.CreateInstance(controlType);
}
}
return null;
}
public static osBase? GetInstanceForType(Type tipoObjeto)
{
if (tipoObjeto == typeof(osBotella))
return new osBotella();
if (tipoObjeto == typeof(osTransporteTTop))
return new osTransporteTTop();
if (tipoObjeto == typeof(osGuia))
return new osGuia();
if (tipoObjeto == typeof(osTransporteGuias))
return new osTransporteGuias();
if (tipoObjeto == typeof(osTransporteCurva))
return new osTransporteCurva();
if (tipoObjeto == typeof(osVMmotorSim))
return new osVMmotorSim();
// Verifica si el tipo pasado es un subtipo de osBase
if (!typeof(osBase).IsAssignableFrom(tipoObjeto))
{
throw new ArgumentException("El tipo pasado no es un subtipo de osBase", nameof(tipoObjeto));
}
// Puedes añadir más condiciones para otros tipos
return null;
// Crear una instancia del tipo especificado
return (osBase?)Activator.CreateInstance(tipoObjeto);
}
public static osBase? CreateInstanceAndPopulate(Type tipoObjeto, string jsonString)
@ -62,17 +64,298 @@ namespace CtrEditor.ObjetosSim
return instance;
}
public static void AssignDatos(UserControl userControl, osBase datos, SimulationManager simulationManager)
public static void AssignDatos(UserControl userControl, osBase datos, SimulationManagerFP simulationManager)
{
if (userControl is IDataContainer dataContainer)
{
dataContainer.Datos = datos;
userControl.DataContext = datos;
datos.VisualRepresentation = userControl;
datos.ConnectSimManager(simulationManager);
datos.simulationManager = simulationManager;
}
}
public static void CargarPropiedadesosDatos(Object selectedObject, StackPanel PanelEdicion, ResourceDictionary Resources)
{
PanelEdicion.Children.Clear();
var properties = selectedObject.GetType().GetProperties();
foreach (var property in properties)
{
if (Attribute.IsDefined(property, typeof(HiddenAttribute)))
continue;
if (property.Name.EndsWith("_oculto"))
continue;
var grid = new Grid();
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
var label = new Label
{
Content = property.Name.Replace("_", " ") + ":",
Margin = new Thickness(0, 0, 5, 0),
VerticalAlignment = VerticalAlignment.Center
};
Grid.SetColumn(label, 0);
grid.Children.Add(label);
if (property.PropertyType == typeof(double) || property.PropertyType == typeof(float) || property.PropertyType == typeof(string) || property.PropertyType == typeof(int))
{
var textBox = new TextBox
{
Margin = new Thickness(0),
MinWidth = 200,
VerticalContentAlignment = VerticalAlignment.Center
};
var binding = new Binding(property.Name)
{
Source = selectedObject,
Mode = BindingMode.TwoWay,
UpdateSourceTrigger = UpdateSourceTrigger.LostFocus
};
if (property.PropertyType == typeof(float))
{
if (Resources != null)
binding.Converter = (FloatToFormattedStringConverter)Resources["floatFormatter"];
}
if (property.PropertyType == typeof(double))
{
if (Resources != null)
binding.Converter = (DoubleToFormattedStringConverter)Resources["doubleFormatter"];
}
textBox.SetBinding(TextBox.TextProperty, binding);
Grid.SetColumn(textBox, 1);
grid.Children.Add(textBox);
}
else if (property.PropertyType == typeof(bool))
{
var checkBox = new CheckBox
{
Margin = new Thickness(5, 0, 0, 0),
VerticalAlignment = VerticalAlignment.Center
};
var binding = new Binding(property.Name)
{
Source = selectedObject,
Mode = BindingMode.TwoWay
};
checkBox.SetBinding(CheckBox.IsCheckedProperty, binding);
Grid.SetColumn(checkBox, 1);
grid.Children.Add(checkBox);
}
else if (property.PropertyType == typeof(Brush))
{
var listBox = new ListBox
{
ItemsSource = new List<string> { "Rojo", "Azul", "Negro", "Verde", "Gris" },
Margin = new Thickness(0),
MinWidth = 200
};
listBox.SelectionChanged += (sender, e) =>
{
if (listBox.SelectedItem != null)
{
switch (listBox.SelectedItem.ToString())
{
case "Rojo":
property.SetValue(selectedObject, Brushes.Red);
break;
case "Azul":
property.SetValue(selectedObject, Brushes.Blue);
break;
case "Negro":
property.SetValue(selectedObject, Brushes.Black);
break;
case "Verde":
property.SetValue(selectedObject, Brushes.Green);
break;
case "Gris":
property.SetValue(selectedObject, Brushes.Gray);
break;
}
}
};
var binding = new Binding(property.Name)
{
Source = selectedObject,
Mode = BindingMode.TwoWay,
Converter = new BrushToColorNameConverter()
};
listBox.SetBinding(ListBox.SelectedItemProperty, binding);
Grid.SetColumn(listBox, 1);
grid.Children.Add(listBox);
}
PanelEdicion.Children.Add(grid);
}
}
public static List<string> CargarPropiedadesosDatosTags(Object selectedObject, StackPanel PanelEdicion, ResourceDictionary Resources)
{
PanelEdicion.Children.Clear();
var properties = selectedObject.GetType().GetProperties();
List<string> tags = new List<string>();
foreach (var property in properties)
{
if (Attribute.IsDefined(property, typeof(HiddenAttribute)))
continue;
if (property.Name.EndsWith("_oculto"))
continue;
var grid = new Grid();
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
var label = new Label
{
Content = property.Name.Replace("_", " ") + ":",
Margin = new Thickness(0, 0, 5, 0),
VerticalAlignment = VerticalAlignment.Center
};
Grid.SetColumn(label, 0);
grid.Children.Add(label);
if (property.PropertyType == typeof(double) || property.PropertyType == typeof(float) || property.PropertyType == typeof(string) || property.PropertyType == typeof(int))
{
var textBox = new TextBox
{
Margin = new Thickness(0),
MinWidth = 200,
VerticalContentAlignment = VerticalAlignment.Center
};
var binding = new Binding(property.Name)
{
Source = selectedObject,
Mode = BindingMode.TwoWay,
UpdateSourceTrigger = UpdateSourceTrigger.LostFocus
};
if (property.PropertyType == typeof(float))
{
if (Resources != null)
binding.Converter = (FloatToFormattedStringConverter)Resources["floatFormatter"];
}
if (property.PropertyType == typeof(double))
{
if (Resources != null)
binding.Converter = (DoubleToFormattedStringConverter)Resources["doubleFormatter"];
}
textBox.SetBinding(TextBox.TextProperty, binding);
Grid.SetColumn(textBox, 1);
grid.Children.Add(textBox);
var tagTextBox = new TextBox
{
Margin = new Thickness(0),
MinWidth = 100,
VerticalContentAlignment = VerticalAlignment.Center
};
// Add an empty string to the tags list and get the index
tags.Add(string.Empty);
int tagIndex = tags.Count - 1;
// Event handler to update the tags list when the text changes
tagTextBox.TextChanged += (sender, args) =>
{
tags[tagIndex] = tagTextBox.Text;
};
Grid.SetColumn(tagTextBox, 2);
grid.Children.Add(tagTextBox);
}
else if (property.PropertyType == typeof(bool))
{
var checkBox = new CheckBox
{
Margin = new Thickness(5, 0, 0, 0),
VerticalAlignment = VerticalAlignment.Center
};
var binding = new Binding(property.Name)
{
Source = selectedObject,
Mode = BindingMode.TwoWay
};
checkBox.SetBinding(CheckBox.IsCheckedProperty, binding);
Grid.SetColumn(checkBox, 1);
grid.Children.Add(checkBox);
}
else if (property.PropertyType == typeof(Brush))
{
var listBox = new ListBox
{
ItemsSource = new List<string> { "Rojo", "Azul", "Negro", "Verde", "Gris" },
Margin = new Thickness(0),
MinWidth = 200
};
listBox.SelectionChanged += (sender, e) =>
{
if (listBox.SelectedItem != null)
{
switch (listBox.SelectedItem.ToString())
{
case "Rojo":
property.SetValue(selectedObject, Brushes.Red);
break;
case "Azul":
property.SetValue(selectedObject, Brushes.Blue);
break;
case "Negro":
property.SetValue(selectedObject, Brushes.Black);
break;
case "Verde":
property.SetValue(selectedObject, Brushes.Green);
break;
case "Gris":
property.SetValue(selectedObject, Brushes.Gray);
break;
}
}
};
var binding = new Binding(property.Name)
{
Source = selectedObject,
Mode = BindingMode.TwoWay,
Converter = new BrushToColorNameConverter()
};
listBox.SetBinding(ListBox.SelectedItemProperty, binding);
Grid.SetColumn(listBox, 1);
grid.Children.Add(listBox);
}
PanelEdicion.Children.Add(grid);
}
return tags;
}
}
}

View File

@ -1,17 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace CtrEditor.ObjetosSim.UserControls
{

View File

@ -0,0 +1,11 @@
<UserControl x:Class="CtrEditor.ObjetosSim.UserControls.GearControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:CtrEditor.ObjetosSim.UserControls"
mc:Ignorable="d" Name="circularSegmentControl">
<Grid>
<Canvas x:Name="GearCanvas" />
</Grid>
</UserControl>

View File

@ -0,0 +1,129 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
namespace CtrEditor.ObjetosSim.UserControls
{
/// <summary>
/// Interaction logic for GearControl.xaml
/// </summary>
public partial class GearControl : UserControl
{
public static readonly DependencyProperty TeethCountProperty =
DependencyProperty.Register("TeethCount", typeof(int), typeof(GearControl),
new PropertyMetadata(10, OnGearPropertyChanged));
public static readonly DependencyProperty InnerRadiusProperty =
DependencyProperty.Register("InnerRadius", typeof(double), typeof(GearControl),
new PropertyMetadata(40.0, OnGearPropertyChanged));
public static readonly DependencyProperty OuterRadiusProperty =
DependencyProperty.Register("OuterRadius", typeof(double), typeof(GearControl),
new PropertyMetadata(60.0, OnGearPropertyChanged));
public static readonly DependencyProperty ToothWidthRatioProperty =
DependencyProperty.Register("ToothWidthRatio", typeof(double), typeof(GearControl),
new PropertyMetadata(0.5, OnGearPropertyChanged));
public int TeethCount
{
get { return (int)GetValue(TeethCountProperty); }
set { SetValue(TeethCountProperty, value); }
}
public double InnerRadius
{
get { return (double)GetValue(InnerRadiusProperty); }
set { SetValue(InnerRadiusProperty, value); }
}
public double OuterRadius
{
get { return (double)GetValue(OuterRadiusProperty); }
set { SetValue(OuterRadiusProperty, value); }
}
public double ToothWidthRatio
{
get { return (double)GetValue(ToothWidthRatioProperty); }
set { SetValue(ToothWidthRatioProperty, value); }
}
public GearControl()
{
InitializeComponent();
DrawGear();
}
private static void OnGearPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as GearControl;
control?.DrawGear();
}
private void DrawGear()
{
GearCanvas.Children.Clear();
double centerX = ActualWidth / 2;
double centerY = ActualHeight / 2;
double angleStep = 360.0 / TeethCount;
double toothWidthAngle = angleStep * ToothWidthRatio;
// Draw inner circle
Ellipse innerCircle = new Ellipse
{
Width = InnerRadius * 2,
Height = InnerRadius * 2,
Stroke = Brushes.Black,
StrokeThickness = 2,
Fill = Brushes.Gray
};
Canvas.SetLeft(innerCircle, centerX - InnerRadius);
Canvas.SetTop(innerCircle, centerY - InnerRadius);
GearCanvas.Children.Add(innerCircle);
// Draw the zero angle marker
double zeroAngle = 0;
double radianZero = zeroAngle * Math.PI / 180;
double markerLength = InnerRadius * 1.2; // Length of the marker line extending outside the inner circle
Line zeroAngleMarker = new Line
{
X1 = centerX,
Y1 = centerY,
X2 = centerX + markerLength * Math.Cos(radianZero),
Y2 = centerY - markerLength * Math.Sin(radianZero),
Stroke = Brushes.Red,
StrokeThickness = 2
};
GearCanvas.Children.Add(zeroAngleMarker);
for (int i = 0; i < TeethCount; i++)
{
// Offset the angle to center the first tooth on the 0 angle
double angle = i * angleStep;
double radianStart = (angle - toothWidthAngle / 2) * Math.PI / 180;
double radianEnd = (angle + toothWidthAngle / 2) * Math.PI / 180;
Point p1 = new Point(centerX + InnerRadius * Math.Cos(radianStart), centerY + InnerRadius * Math.Sin(radianStart));
Point p2 = new Point(centerX + OuterRadius * Math.Cos(radianStart), centerY + OuterRadius * Math.Sin(radianStart));
Point p3 = new Point(centerX + OuterRadius * Math.Cos(radianEnd), centerY + OuterRadius * Math.Sin(radianEnd));
Point p4 = new Point(centerX + InnerRadius * Math.Cos(radianEnd), centerY + InnerRadius * Math.Sin(radianEnd));
Polygon tooth = new Polygon
{
Fill = Brushes.Black,
Points = new PointCollection { p1, p2, p3, p4 }
};
GearCanvas.Children.Add(tooth);
}
}
}
}

View File

@ -12,6 +12,16 @@ using System.Windows.Controls;
using System.Windows.Data;
using static System.Runtime.InteropServices.JavaScript.JSType;
using CtrEditor.Convertidores;
using CtrEditor.Siemens;
using CtrEditor.Simulacion;
using System.Windows.Media;
using nkast.Aether.Physics2D.Common;
using FarseerPhysics.Dynamics;
using Siemens.Simatic.Simulation.Runtime;
using System.Windows.Media.Imaging;
using System.Windows.Input;
using CommunityToolkit.Mvvm.ComponentModel;
using System.Windows.Media.Animation;
namespace CtrEditor.ObjetosSim
{
@ -20,14 +30,12 @@ namespace CtrEditor.ObjetosSim
public interface IosBase
{
string Nombre { get; }
void ConnectSimManager(SimulationManager simulationManager);
void UpdateControl();
static abstract string NombreClase();
}
public interface IDataContainer
{
[JsonIgnore]
osBase? Datos { get; set; }
void Resize(float width, float height);
void Move(float Left, float Top);
@ -36,20 +44,91 @@ namespace CtrEditor.ObjetosSim
int ZIndex();
}
public abstract class osBase : INotifyPropertyChanged, IosBase
{
public abstract float Left { get; set; }
public abstract float Top { get; set; }
public class DataSaveToSerialize
{
private MainViewModel? _mainViewModel;
private UserControl? VisualRepresentation;
private SimulationManagerFP? simulationManager;
public DataSaveToSerialize(MainViewModel a, UserControl b, SimulationManagerFP c )
{
_mainViewModel = a;
VisualRepresentation = b;
simulationManager = c;
}
public void DataRestoreAfterSerialize(out MainViewModel a, out UserControl b, out SimulationManagerFP c)
{
a = _mainViewModel;
b = VisualRepresentation;
c = simulationManager;
}
}
public abstract partial class osBase : ObservableObject
{
public virtual string Nombre { get; set; } = "osBase";
[JsonIgnore]
private Storyboard _storyboard;
[ObservableProperty]
private float left;
partial void OnLeftChanged(float value)
{
CanvasSetLeftinMeter(value);
LeftChanged(value);
}
public virtual void LeftChanged(float value) { }
[ObservableProperty]
private float top;
partial void OnTopChanged(float value)
{
CanvasSetTopinMeter(value);
TopChanged(value);
}
public virtual void TopChanged(float value) { }
public bool Inicializado = false;
public bool AutoCreated = false;
public bool RemoverDesdeSimulacion = false; // La simulacion indica que se debe remover
[JsonIgnore]
private DataSaveToSerialize DataSave;
[JsonIgnore]
protected UserControl? _visualRepresentation = null;
public abstract string Nombre { get; set; }
[JsonIgnore]
protected PLCModel? _plc = null;
public abstract void ConnectSimManager(SimulationManager simulationManager);
public abstract void UpdateControl();
public abstract void UpdateGeometry();
public virtual void UpdateControl(int elapsedMilliseconds) { }
public virtual void UpdateGeometryStart()
{
// Se llama antes de la simulacion
}
public virtual void SimulationStop() { }
public virtual void UpdateGeometryStep() { }
public virtual void UpdatePLC(PLCModel plc, int elapsedMilliseconds) { }
public virtual void ucLoaded()
{
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
ActualizarLeftTop();
}
public virtual void ucUnLoaded()
{
// El UserControl se esta eliminando
// eliminar el objeto de simulacion
}
[JsonIgnore]
public MainViewModel _mainViewModel;
[JsonIgnore]
public UserControl? VisualRepresentation
@ -57,11 +136,132 @@ namespace CtrEditor.ObjetosSim
get => _visualRepresentation;
set => _visualRepresentation = value;
}
[JsonIgnore]
public SimulationManagerFP simulationManager;
public void SalvarDatosNoSerializables()
{
DataSave = new DataSaveToSerialize(_mainViewModel,_visualRepresentation,simulationManager);
_mainViewModel = null;
_visualRepresentation = null;
simulationManager = null;
}
public void RestaurarDatosNoSerializables()
{
if (DataSave == null) return;
DataSave.DataRestoreAfterSerialize(out _mainViewModel,out _visualRepresentation,out simulationManager);
}
public void SetPLC(PLCModel plc)
{
_plc = plc;
}
protected osBase ObtenerLink(string NameLink, Type tipoOsBase)
{
if (!string.IsNullOrEmpty(NameLink) && _mainViewModel != null)
{
foreach (var objetoSimulable in _mainViewModel.ObjetosSimulables)
{
if (tipoOsBase.IsInstanceOfType(objetoSimulable) && objetoSimulable.Nombre == NameLink)
{
return objetoSimulable;
}
}
}
return null;
}
protected void CrearAnimacionStoryBoardTrasnporte(System.Windows.Shapes.Rectangle transporte)
{
if (_visualRepresentation == null) return;
if (transporte == null) return;
_storyboard = new Storyboard();
var animation = new DoubleAnimation
{
From = 0,
To = 20, // Total Pixels Brush
Duration = TimeSpan.FromSeconds(PixelToMeter.Instance.calc.PixelsToMeters(20) * 60),
RepeatBehavior = RepeatBehavior.Forever
};
Storyboard.SetTarget(animation, transporte);
Storyboard.SetTargetProperty(animation, new PropertyPath("(Rectangle.Fill).(VisualBrush.Transform).(TransformGroup.Children)[0].(TranslateTransform.X)"));
_storyboard.Children.Add(animation);
_storyboard.Begin();
_storyboard.SetSpeedRatio(0);
}
protected void ActualizarAnimacionStoryBoardTransporte(float velocidadActual)
{
if (_visualRepresentation == null) return;
if (_storyboard == null) return;
if (!_mainViewModel.IsSimulationRunning)
_storyboard.SetSpeedRatio(0);
else
_storyboard.SetSpeedRatio(velocidadActual);
}
public void ActualizarLeftTop()
{
CanvasSetLeftinMeter(Left);
CanvasSetTopinMeter(Top);
}
public bool LeerBitTag(string Tag)
{
if (this._plc == null) return false;
if (!string.IsNullOrEmpty(Tag))
{
if (Tag == "1") return true;
else if (Tag == "0") return false;
if (_plc != null)
return _plc.LeerTagBool(Tag);
}
return false;
}
public void EscribirBitTag(string Tag, bool Value)
{
if (_plc == null) return;
if (!string.IsNullOrEmpty(Tag))
if (_plc != null)
_plc.EscribirTagBool(Tag, Value);
}
public void EscribirWordTagScaled(string Tag, float Value, float IN_scale_Min, float IN_scale_Max, float OUT_scale_Min, float OUT_scale_Max)
{
if (_plc == null) return;
if (!string.IsNullOrEmpty(Tag))
{
SDataValue plcData = new SDataValue();
plcData.UInt16 = (ushort)((Value - IN_scale_Min) / (IN_scale_Max - IN_scale_Min) * (OUT_scale_Max - OUT_scale_Min) + OUT_scale_Min);
_plc.EscribirTag(Tag, plcData);
}
}
public float LeerWordTagScaled(PLCModel _plc, string Tag, float IN_scale_Min, float IN_scale_Max, float OUT_scale_Min, float OUT_scale_Max)
{
if (_plc == null) return 0;
if (!string.IsNullOrEmpty(Tag))
{
if (float.TryParse(Tag, out float v))
return v;
if (_plc != null)
{
SDataValue plcData = _plc.LeerTag(Tag);
float Value = plcData.UInt16; // WORD
return (Value - OUT_scale_Min) / (OUT_scale_Max - OUT_scale_Min) * (IN_scale_Max - IN_scale_Min) + IN_scale_Min;
}
}
return 0;
}
public void CanvasSetLeftinMeter(float left)
{
if (_visualRepresentation != null)
Canvas.SetLeft(_visualRepresentation, PixelToMeter.Instance.calc.MetersToPixels(left));
Canvas.SetLeft(_visualRepresentation, PixelToMeter.Instance.calc.MetersToPixels(left));
}
public float CanvasGetLeftinMeter()
{
@ -82,16 +282,151 @@ namespace CtrEditor.ObjetosSim
else return 0f;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
public (Vector2 TopLeft, Vector2 BottomRight) GetRectangleCoordinatesInMeter(System.Windows.Shapes.Rectangle rect)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
if (rect != null)
{
var _canvasLeft = CanvasGetLeftinMeter();
var _canvasTop = CanvasGetTopinMeter();
// Obtiene la transformada del objeto visual
GeneralTransform transform = rect.TransformToAncestor(_visualRepresentation);
// Obtiene la posición absoluta
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));
}
else return (new Vector2(0, 0), new Vector2(0, 0));
}
public (Vector2 Start, Vector2 End) GetCenterLineVectors(System.Windows.Shapes.Rectangle rect)
{
if (rect == null)
return (new Vector2(0, 0), new Vector2(0, 0));
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)
Vector2 start = new Vector2(PixelToMeter.Instance.calc.PixelsToMeters((float)transformedStart.X) + _canvasLeft, PixelToMeter.Instance.calc.PixelsToMeters((float)transformedStart.Y) + _canvasTop);
Vector2 end = new Vector2(PixelToMeter.Instance.calc.PixelsToMeters((float)transformedEnd.X) + _canvasLeft, PixelToMeter.Instance.calc.PixelsToMeters((float)transformedEnd.Y) + _canvasTop);
return (start, end);
}
public Vector2 GetCurveCenterInMeter(float RadioExterno)
{
var _canvasLeft = CanvasGetLeftinMeter();
var _canvasTop = CanvasGetTopinMeter();
// El centro del Canvas
double centerX = RadioExterno + _canvasLeft;
double centerY = RadioExterno + _canvasTop;
// Convertir a Vector2
return new Vector2((float)centerX, (float)centerY);
}
public Vector2 GetRectangleCenter(System.Windows.Shapes.Rectangle wpfRect)
{
var coords = GetRectangleCoordinatesInMeter(wpfRect);
Vector2 topLeft = coords.TopLeft;
Vector2 bottomRight = coords.BottomRight;
// Calcular el centro, ancho y alto en metros
return new Vector2((topLeft.X + bottomRight.X) / 2, (topLeft.Y + bottomRight.Y) / 2);
}
public void UpdateRectangle(simTransporte simRect, System.Windows.Shapes.Rectangle wpfRect, float Alto, float Ancho, float Angulo)
{
if (simRect != null)
simRect.Create(Ancho, Alto, GetRectangleCenter(wpfRect), Angulo);
}
public void UpdateRectangle(simBarrera simRect, System.Windows.Shapes.Rectangle wpfRect, float Alto, float Ancho, float Angulo)
{
if (simRect != null)
simRect.Create(Ancho, Alto, GetRectangleCenter(wpfRect), Angulo);
}
public void UpdateCurve(simCurve curva,float RadioInterno, float RadioExterno, float startAngle, float endAngle)
{
curva.Create(RadioInterno, RadioExterno, startAngle, endAngle, GetCurveCenterInMeter(RadioExterno));
}
public simCurve AddCurve(float RadioInterno, float RadioExterno, float startAngle, float endAngle)
{
return simulationManager.AddCurve(RadioInterno, RadioExterno, startAngle, endAngle, GetCurveCenterInMeter(RadioExterno));
}
public simTransporte AddRectangle(SimulationManagerFP simulationManager, System.Windows.Shapes.Rectangle wpfRect, float Alto, float Ancho, float Angulo)
{
return simulationManager.AddRectangle(Ancho, Alto, GetRectangleCenter(wpfRect), Angulo);
}
public simBarrera AddBarrera(SimulationManagerFP simulationManager, System.Windows.Shapes.Rectangle wpfRect, float Alto, float Ancho, float Angulo)
{
return simulationManager.AddBarrera(Ancho, Alto, GetRectangleCenter(wpfRect), Angulo);
}
public void UpdateOrCreateLine(simGuia simGuia, System.Windows.Shapes.Rectangle wpfRect)
{
if (simGuia != null)
{
var coords = GetCenterLineVectors(wpfRect);
// Crear o actualizar simRectangle
simGuia.Create(coords.Start, coords.End); // asumiendo que el ángulo inicial es 0
}
}
public simGuia AddLine(SimulationManagerFP simulationManager, System.Windows.Shapes.Rectangle wpfRect)
{
var coords = GetCenterLineVectors(wpfRect);
return simulationManager.AddLine(coords.Start, coords.End);
}
public ImageSource ImageFromPath(string value)
{
if (value is string stringValue)
{
return new BitmapImage(new Uri(stringValue, UriKind.RelativeOrAbsolute));
}
return null;
}
static protected T GetLastElement<T>(List<T> list) where T : class
{
if (list.Count > 0)
{
return list[list.Count - 1];
}
else
{
return null;
}
}
}
[AttributeUsage(AttributeTargets.Property)]
public class HiddenAttribute : Attribute
{
}
}

View File

@ -0,0 +1,37 @@
<UserControl x:Class="CtrEditor.ObjetosSim.ucBasicExample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:CtrEditor.ObjetosSim"
mc:Ignorable="d"
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim"
xmlns:convert="clr-namespace:CtrEditor.Convertidores">
<UserControl.Resources>
<convert:MeterToPixelConverter x:Key="MeterToPixelConverter"/>
</UserControl.Resources>
<UserControl.DataContext>
<vm:osBasicExample />
</UserControl.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Label Grid.Row="0" Content="{Binding Nombre}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Background="Transparent"
Foreground="Black"/>
<Image Grid.Row="1" Source="{Binding ImageSource_oculta}"
Width="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}}"
Height="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}}"
Stretch="Uniform"/>
</Grid>
</UserControl>

View File

@ -0,0 +1,137 @@
using CtrEditor.Convertidores;
using CtrEditor.Siemens;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using Newtonsoft.Json;
using CommunityToolkit.Mvvm.ComponentModel;
namespace CtrEditor.ObjetosSim
{
/// <summary>
/// Interaction logic for ucBasicExample.xaml
/// </summary>
///
public partial class osBasicExample : osBase, IosBase
{
// Otros datos y métodos relevantes para la simulación
public static string NombreClase()
{
return "Ejemplo";
}
private string nombre = NombreClase();
public override string Nombre
{
get => nombre;
set => SetProperty(ref nombre, value);
}
[JsonIgnore]
[ObservableProperty]
public ImageSource imageSource_oculta;
[ObservableProperty]
public float tamano;
[ObservableProperty]
public float maxRatedHz;
[ObservableProperty]
public float tiempoRampa;
partial void OnTiempoRampaChanged(float value)
{
if (value < 0.1f)
value = 0.1f;
tiempoRampa = value;
}
[ObservableProperty]
public bool encendido;
[ObservableProperty]
public int pLC_NumeroMotor;
[ObservableProperty]
public float ratio;
[ObservableProperty]
public float velocidad;
partial void OnVelocidadChanged(float value)
{
if (value > 0)
ImageSource_oculta = ImageFromPath("/imagenes/motorVerde.png");
else
ImageSource_oculta = ImageFromPath("/imagenes/motorNegro.png");
}
public osBasicExample()
{
Tamano = 0.30f;
PLC_NumeroMotor = 31;
MaxRatedHz = 100;
TiempoRampa = 3;
ImageSource_oculta = ImageFromPath("/imagenes/motor2.png");
}
public override void UpdateGeometryStart()
{
// Se llama antes de la simulacion
}
public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds)
{
}
public override void UpdateControl(int elapsedMilliseconds)
{
// Calculamos la velocidad
}
public override void ucLoaded()
{
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
ActualizarLeftTop();
}
}
public partial class ucBasicExample : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public ucBasicExample()
{
InitializeComponent();
this.Loaded += OnLoaded;
this.Unloaded += OnUnloaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
Datos?.ucLoaded();
}
private void OnUnloaded(object sender, RoutedEventArgs e)
{
Datos?.ucUnLoaded();
}
public void Resize(float width, float height) { }
public void Move(float LeftPixels, float TopPixels)
{
if (Datos != null)
{
Datos.Left = PixelToMeter.Instance.calc.PixelsToMeters(LeftPixels);
Datos.Top = PixelToMeter.Instance.calc.PixelsToMeters(TopPixels);
}
}
public void Rotate(float Angle) { }
public void Highlight(bool State) { }
public int ZIndex()
{
return 10;
}
}
}

View File

@ -1,142 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Numerics;
using CtrEditor.Convertidores;
namespace CtrEditor.ObjetosSim
{
/// <summary>
/// Interaction logic for ucBotella.xaml
/// </summary>
///
public class osBotella : osBase
{
private Circle Geometria = new Circle();
// Otros datos y métodos relevantes para la simulación
private string _nombre = "Botella";
public float Diametro {
get => Geometria.Diameter;
set
{
Geometria.Diameter = value;
OnPropertyChanged(nameof(Diametro));
}
}
public float Mass {
get => Geometria.Mass;
set
{
Geometria.Mass = value;
OnPropertyChanged(nameof(Mass));
}
}
public float Overlap
{
get => Geometria.Overlap;
set
{
Geometria.Overlap = value;
OnPropertyChanged(nameof(Overlap));
}
}
public override float Left
{
get => Geometria.Left;
set
{
Geometria.Left = value;
CanvasSetLeftinMeter(value);
OnPropertyChanged(nameof(Left));
}
}
public override float Top
{
get => Geometria.Top;
set
{
Geometria.Top = value;
CanvasSetTopinMeter(value);
OnPropertyChanged(nameof(Top));
}
}
public override string Nombre
{
get => _nombre;
set
{
if (_nombre != value)
{
_nombre = value;
OnPropertyChanged(nameof(Nombre));
}
}
}
public osBotella()
{
Diametro = 0.10f;
}
public override void ConnectSimManager(SimulationManager simulationManager)
{
simulationManager.circles.Add(Geometria);
}
public override void UpdateGeometry()
{
// Se llama antes de la simulacion
}
public override void UpdateControl()
{
Top = Geometria.Top;
Left = Geometria.Left;
Overlap = Geometria.Overlap;
}
}
public partial class ucBotella : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public ucBotella()
{
InitializeComponent();
}
public void Resize(float width, float height) { }
public void Move(float LeftPixels, float TopPixels)
{
if (Datos != null)
{
Datos.Left = PixelToMeter.Instance.calc.PixelsToMeters(LeftPixels);
Datos.Top = PixelToMeter.Instance.calc.PixelsToMeters(TopPixels);
}
}
public void Rotate(float Angle) { }
public void Highlight(bool State) { }
public int ZIndex()
{
return 10;
}
}
}

View File

@ -1,145 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using CtrEditor.Convertidores;
namespace CtrEditor.ObjetosSim
{
/// <summary>
/// Interaction logic for ucGuia.xaml
/// </summary>
public class osGuia : osBase
{
private string _nombre = "Guia";
private Line Geometria = new Line();
public override float Left
{
get => Geometria.Left;
set
{
Geometria.Left = value;
CanvasSetLeftinMeter(value);
OnPropertyChanged(nameof(Left));
}
}
public override float Top
{
get => Geometria.Top;
set
{
Geometria.Top = value;
CanvasSetTopinMeter(value);
OnPropertyChanged(nameof(Top));
}
}
public float Ancho
{
get => Geometria.Length;
set
{
Geometria.Length = value;
OnPropertyChanged(nameof(Ancho));
}
}
public float AltoGuia
{
get => Geometria.Width;
set
{
Geometria.Width = value;
OnPropertyChanged(nameof(AltoGuia));
}
}
public float Angulo
{
get => Geometria.Angle;
set
{
Geometria.Angle = value;
OnPropertyChanged(nameof(Angulo));
}
}
public override string Nombre
{
get => _nombre;
set
{
if (_nombre != value)
{
_nombre = value;
OnPropertyChanged(nameof(Nombre));
}
}
}
public osGuia()
{
Ancho = 1;
AltoGuia = 0.03f;
}
public override void ConnectSimManager(SimulationManager simulationManager)
{
simulationManager.lines.Add(Geometria);
}
public override void UpdateGeometry()
{
// Se llama antes de la simulacion
}
public override void UpdateControl()
{
}
}
public partial class ucGuia : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public ucGuia()
{
InitializeComponent();
}
public void Resize(float width, float height)
{
if (Datos is osGuia datos)
datos.Ancho = PixelToMeter.Instance.calc.PixelsToMeters(width);
}
public void Move(float LeftPixels, float TopPixels)
{
if (Datos != null)
{
Datos.Left = PixelToMeter.Instance.calc.PixelsToMeters(LeftPixels);
Datos.Top = PixelToMeter.Instance.calc.PixelsToMeters(TopPixels);
}
}
public void Rotate(float Angle)
{
if (Datos != null)
if (Datos is osGuia datos)
datos.Angulo = Angle;
}
public void Highlight(bool State) { }
public int ZIndex()
{
return 1;
}
}
}

View File

@ -1,168 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using CtrEditor.Convertidores;
namespace CtrEditor.ObjetosSim
{
/// <summary>
/// Interaction logic for ucTransporteCurva.xaml
/// </summary>
public class osTransporteCurva : osBase
{
private string _nombre = "Transporte Curva";
private float frictionCoefficient;
private float velMax50hz; // en metros por minuto
private float tiempoRampa;
private bool esMarcha;
private Rectangle Geometria = new Rectangle();
public override float Left
{
get => Geometria.Left;
set
{
Geometria.Left = value;
CanvasSetLeftinMeter(value);
OnPropertyChanged(nameof(Left));
}
}
public override float Top
{
get => Geometria.Top;
set
{
Geometria.Top = value;
CanvasSetTopinMeter(value);
OnPropertyChanged(nameof(Top));
}
}
public float RadioExterno
{
get => Geometria.Length;
set
{
Geometria.Length = value;
OnPropertyChanged(nameof(RadioExterno));
}
}
public float RadioInterno
{
get => Geometria.Width;
set
{
Geometria.Width = value;
OnPropertyChanged(nameof(RadioInterno));
}
}
public float Angulo
{
get => Geometria.Angle;
set
{
Geometria.Angle = value;
OnPropertyChanged(nameof(Angulo));
}
}
public float VelocidadActual
{
get => Geometria.Speed;
set
{
Geometria.Speed = value;
OnPropertyChanged(nameof(VelocidadActual));
}
}
public override string Nombre
{
get => _nombre;
set
{
if (_nombre != value)
{
_nombre = value;
OnPropertyChanged(nameof(Nombre));
}
}
}
public float FrictionCoefficient { get => frictionCoefficient; set => frictionCoefficient = value; }
public float VelMax50hz { get => velMax50hz; set => velMax50hz = value; }
public float TiempoRampa { get => tiempoRampa; set => tiempoRampa = value; }
public bool EsMarcha { get => esMarcha; set => esMarcha = value; }
public osTransporteCurva()
{
RadioExterno = 2;
RadioInterno = 1;
}
public override void ConnectSimManager(SimulationManager simulationManager)
{
simulationManager.rectangles.Add(Geometria);
}
public override void UpdateGeometry()
{
// Se llama antes de la simulacion
}
public override void UpdateControl()
{
}
}
public partial class ucTransporteCurva : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public ucTransporteCurva()
{
InitializeComponent();
}
public void Resize(float width, float height)
{
if (Datos is osTransporteCurva datos)
datos.RadioExterno = PixelToMeter.Instance.calc.PixelsToMeters(width);
}
public void Move(float LeftPixels, float TopPixels)
{
if (Datos != null)
{
Datos.Left = PixelToMeter.Instance.calc.PixelsToMeters(LeftPixels);
Datos.Top = PixelToMeter.Instance.calc.PixelsToMeters(TopPixels);
}
}
public void Rotate(float Angle)
{
if (Datos != null)
if (Datos is osTransporteCurva datos)
datos.Angulo = Angle;
}
public void Highlight(bool State) { }
public int ZIndex()
{
return 1;
}
}
}

View File

@ -1,243 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using CtrEditor.Convertidores;
namespace CtrEditor.ObjetosSim
{
/// <summary>
/// Interaction logic for ucTransporteGuias.xaml
/// </summary>
public class osTransporteGuias : osBase
{
private string _nombre = "Transporte Guias";
private float frictionCoefficient;
private float velMax50hz; // en metros por minuto
private float tiempoRampa;
private bool esMarcha;
private double _distance;
private float altoGuia;
private float left;
private float top;
private Rectangle TransporteCentral = new Rectangle();
private Line Guia_Superior = new Line();
private Line Guia_Inferior = new Line();
public override float Left
{
get => left;
set
{
left = value;
CanvasSetLeftinMeter(value);
OnPropertyChanged(nameof(Left));
}
}
public override float Top
{
get => top;
set
{
top = value;
CanvasSetTopinMeter(value);
OnPropertyChanged(nameof(Top));
}
}
public float Ancho
{
get => TransporteCentral.Length;
set
{
TransporteCentral.Length = value;
OnPropertyChanged(nameof(Ancho));
}
}
public float AltoGuia
{
get => altoGuia;
set
{
altoGuia = value;
OnPropertyChanged(nameof(AltoGuia));
}
}
public float Alto
{
get => TransporteCentral.Width;
set
{
TransporteCentral.Width = value;
OnPropertyChanged(nameof(Alto));
}
}
public float Angulo
{
get => TransporteCentral.Angle;
set
{
TransporteCentral.Angle = value;
OnPropertyChanged(nameof(Angulo));
}
}
public float VelocidadActual
{
get => TransporteCentral.Speed;
set
{
TransporteCentral.Speed = value;
OnPropertyChanged(nameof(VelocidadActual));
}
}
public double Distance
{
get { return _distance; }
set
{
if (_distance != value)
{
_distance = value;
OnPropertyChanged("Distance");
}
}
}
public override string Nombre
{
get => _nombre;
set
{
if (_nombre != value)
{
_nombre = value;
OnPropertyChanged(nameof(Nombre));
}
}
}
private void ActualizarGeometrias()
{
ucTransporteGuias ucTG = (ucTransporteGuias)_visualRepresentation;
if (ucTG != null)
{
var _canvasLeft = CanvasGetLeftinMeter();
var _canvasTop = CanvasGetTopinMeter();
var coordenadas = GetRectangleCoordinatesInMeter(ucTG.Transporte, ucTG);
TransporteCentral.Left = coordenadas.Left + _canvasLeft;
TransporteCentral.Top = coordenadas.Top + _canvasTop;
coordenadas = GetRectangleCoordinatesInMeter(ucTG.GuiaSuperior, ucTG);
Guia_Superior.Left = coordenadas.Left + _canvasLeft;
Guia_Superior.Top = coordenadas.Top + _canvasTop; ;
coordenadas = GetRectangleCoordinatesInMeter(ucTG.GuiaInferior, ucTG);
Guia_Inferior.Left = coordenadas.Left + _canvasLeft;
Guia_Inferior.Top = coordenadas.Top + _canvasTop; ;
TransporteCentral.Angle = Guia_Superior.Angle = Guia_Inferior.Angle = Angulo;
Guia_Superior.Length = Guia_Inferior.Length = Ancho;
}
}
private (float Left, float Top) GetRectangleCoordinatesInMeter(System.Windows.Shapes.Rectangle rect, ucTransporteGuias ucTG)
{
if (rect != null)
{
// Obtiene la transformada del objeto visual
GeneralTransform transform = rect.TransformToAncestor(ucTG);
// Obtiene la posición absoluta
Point topLeft = transform.Transform(new Point(0, 0));
//Point bottomRight = transform.Transform(new Point(rect.ActualWidth, rect.ActualHeight));
return (PixelToMeter.Instance.calc.PixelsToMeters((float)topLeft.X), PixelToMeter.Instance.calc.PixelsToMeters((float)topLeft.Y));
}
else return (0,0);
}
public float FrictionCoefficient { get => frictionCoefficient; set => frictionCoefficient = value; }
public float VelMax50hz { get => velMax50hz; set => velMax50hz = value; }
public float TiempoRampa { get => tiempoRampa; set => tiempoRampa = value; }
public bool EsMarcha { get => esMarcha; set => esMarcha = value; }
public osTransporteGuias()
{
Ancho = 1;
Alto = 0.10f;
AltoGuia = 0.03f;
Distance = 0.01f;
}
public override void ConnectSimManager(SimulationManager simulationManager)
{
simulationManager.rectangles.Add(TransporteCentral);
simulationManager.lines.Add(Guia_Superior);
simulationManager.lines.Add(Guia_Inferior);
}
public override void UpdateGeometry()
{
// Se llama antes de la simulacion
ActualizarGeometrias();
}
public override void UpdateControl()
{
}
}
public partial class ucTransporteGuias : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public ucTransporteGuias()
{
InitializeComponent();
}
public void Resize(float width, float height)
{
if (Datos is osTransporteGuias datos)
datos.Ancho = PixelToMeter.Instance.calc.PixelsToMeters(width);
}
public void Move(float LeftPixels, float TopPixels)
{
if (Datos != null)
{
Datos.Left = PixelToMeter.Instance.calc.PixelsToMeters(LeftPixels);
Datos.Top = PixelToMeter.Instance.calc.PixelsToMeters(TopPixels);
}
}
public void Rotate(float Angle)
{
if (Datos != null)
if (Datos is osTransporteGuias datos)
datos.Angulo = Angle;
}
public void Highlight(bool State) { }
public int ZIndex()
{
return 1;
}
}
}

View File

@ -1,22 +0,0 @@
<UserControl x:Class="CtrEditor.ObjetosSim.ucTransporteTTop"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
xmlns:convert="clr-namespace:CtrEditor.Convertidores">
<UserControl.Resources>
<convert:MeterToPixelConverter x:Key="MeterToPixelConverter"/>
</UserControl.Resources>
<Canvas>
<Rectangle Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}" Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}}" Fill="Gray">
<Rectangle.RenderTransform>
<RotateTransform Angle="{Binding Angulo}"/>
</Rectangle.RenderTransform>
</Rectangle>
</Canvas>
</UserControl>

View File

@ -1,170 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using static System.Runtime.InteropServices.JavaScript.JSType;
using System.Numerics;
using System.Windows.Markup;
using CtrEditor.Convertidores;
namespace CtrEditor.ObjetosSim
{
/// <summary>
/// Interaction logic for ucTransporteTTop.xaml
/// </summary>
///
public class osTransporteTTop : osBase
{
private string _nombre = "Transporte TTOP";
private float frictionCoefficient;
private float velMax50hz; // en metros por minuto
private float tiempoRampa;
private bool esMarcha;
private Rectangle Geometria = new Rectangle();
public override float Left
{
get => Geometria.Left;
set
{
Geometria.Left = value;
CanvasSetLeftinMeter(value);
OnPropertyChanged(nameof(Left));
}
}
public override float Top
{
get => Geometria.Top;
set
{
Geometria.Top = value;
CanvasSetTopinMeter(value);
OnPropertyChanged(nameof(Top));
}
}
public float Ancho {
get => Geometria.Length;
set
{
Geometria.Length = value;
OnPropertyChanged(nameof(Ancho));
}
}
public float Alto {
get => Geometria.Width;
set
{
Geometria.Width = value;
OnPropertyChanged(nameof(Alto));
}
}
public float Angulo
{
get => Geometria.Angle;
set
{
Geometria.Angle = value;
OnPropertyChanged(nameof(Angulo));
}
}
public float VelocidadActual
{
get => Geometria.Speed;
set {
Geometria.Speed = value;
OnPropertyChanged(nameof(VelocidadActual));
}
}
public override string Nombre
{
get => _nombre;
set
{
if (_nombre != value)
{
_nombre = value;
OnPropertyChanged(nameof(Nombre));
}
}
}
public float FrictionCoefficient { get => frictionCoefficient; set => frictionCoefficient = value; }
public float VelMax50hz { get => velMax50hz; set => velMax50hz = value; }
public float TiempoRampa { get => tiempoRampa; set => tiempoRampa = value; }
public bool EsMarcha { get => esMarcha; set => esMarcha = value; }
public osTransporteTTop()
{
Ancho = 1;
Alto = 0.10f;
}
public override void ConnectSimManager(SimulationManager simulationManager)
{
simulationManager.rectangles.Add(Geometria);
}
public override void UpdateGeometry()
{
// Se llama antes de la simulacion
}
public override void UpdateControl()
{
}
}
public partial class ucTransporteTTop : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public ucTransporteTTop()
{
InitializeComponent();
}
public void Resize(float width, float height)
{
if (Datos is osTransporteTTop datos)
datos.Ancho = PixelToMeter.Instance.calc.PixelsToMeters(width);
}
public void Move(float LeftPixels, float TopPixels)
{
if (Datos != null)
{
Datos.Left = PixelToMeter.Instance.calc.PixelsToMeters(LeftPixels);
Datos.Top = PixelToMeter.Instance.calc.PixelsToMeters(TopPixels);
}
}
public void Rotate(float Angle) {
if (Datos != null)
if (Datos is osTransporteTTop datos)
datos.Angulo = Angle;
}
public void Highlight(bool State) { }
public int ZIndex()
{
return 1;
}
}
}

View File

@ -1,21 +0,0 @@
<UserControl x:Class="CtrEditor.ObjetosSim.ucVMmotorSim"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:CtrEditor.ObjetosSim"
mc:Ignorable="d"
xmlns:convert="clr-namespace:CtrEditor.Convertidores">
<UserControl.Resources>
<convert:MeterToPixelConverter x:Key="MeterToPixelConverter"/>
</UserControl.Resources>
<Grid>
<Image Source="/motor2.png"
Width="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}}"
Height="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}}"
Stretch="Uniform"/>
</Grid>
</UserControl>

View File

@ -1,132 +0,0 @@
using CtrEditor.Convertidores;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace CtrEditor.ObjetosSim
{
/// <summary>
/// Interaction logic for ucVMmotorSim.xaml
/// </summary>
public class osVMmotorSim : osBase
{
// Otros datos y métodos relevantes para la simulación
private string _nombre = "VetroMeccanica Motor";
private float _tamano;
private float _left;
private float _top;
private float _numeroMotor;
public float Tamano
{
get => _tamano;
set
{
_tamano = value;
OnPropertyChanged(nameof(Tamano));
}
}
public float PLC_NumeroMotor
{
get => _numeroMotor;
set
{
_numeroMotor = value;
OnPropertyChanged(nameof(PLC_NumeroMotor));
}
}
public override float Left
{
get => _left;
set
{
_left = value;
CanvasSetLeftinMeter(value);
OnPropertyChanged(nameof(Left));
}
}
public override float Top
{
get => _top;
set
{
_top = value;
CanvasSetTopinMeter(value);
OnPropertyChanged(nameof(Top));
}
}
public override string Nombre
{
get => _nombre;
set
{
if (_nombre != value)
{
_nombre = value;
OnPropertyChanged(nameof(Nombre));
}
}
}
public osVMmotorSim()
{
Tamano = 0.30f;
}
public override void ConnectSimManager(SimulationManager simulationManager)
{
}
public override void UpdateGeometry()
{
// Se llama antes de la simulacion
}
public override void UpdateControl()
{
}
}
public partial class ucVMmotorSim : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public ucVMmotorSim()
{
InitializeComponent();
}
public void Resize(float width, float height) { }
public void Move(float LeftPixels, float TopPixels)
{
if (Datos != null)
{
Datos.Left = PixelToMeter.Instance.calc.PixelsToMeters(LeftPixels);
Datos.Top = PixelToMeter.Instance.calc.PixelsToMeters(TopPixels);
}
}
public void Rotate(float Angle) { }
public void Highlight(bool State) { }
public int ZIndex()
{
return 10;
}
}
}

View File

@ -25,20 +25,24 @@ namespace CtrEditor.Siemens
/// </summary>
public class PLCViewModel : INotifyPropertyChanged
{
private readonly PLCModel _plcModel;
public readonly PLCModel PLCInterface;
private readonly DispatcherTimer _timer;
private string _cpuTime;
private string _connectionStatus = "offline";
private string _ip = "10.1.30.11";
private string _name = "PLC";
private string lastError;
public bool IsConnected { get; private set; }
public event PropertyChangedEventHandler PropertyChanged;
public event EventHandler RefreshEvent;
public PLCViewModel()
{
_plcModel = new PLCModel();
_timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(1000) };
IsConnected = false;
PLCInterface = new PLCModel();
_timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(30) };
_timer.Tick += (s, e) => Refresh();
ConnectCommand = new RelayCommand(Connect, () => true);
@ -98,41 +102,51 @@ namespace CtrEditor.Siemens
}
}
private void Connect()
public void Connect()
{
// Implementa la conexión utilizando PLCModel
_plcModel.Instance = SimulationRuntimeManager.CreateInterface(Name);
_plcModel.Instance.OnSoftwareConfigurationChanged += Instance_OnSoftwareConfigurationChanged;
//_plcModel.Instance.CommunicationInterface = ECommunicationInterface.Softbus;
if (_plcModel.Instance != null)
try
{
_plcModel.UpdateTagList();
_timer.Start();
ConnectionStatus = "connected";
// Implementa la conexión utilizando PLCModel
PLCInterface.Instance = SimulationRuntimeManager.CreateInterface(Name);
PLCInterface.Instance.OnSoftwareConfigurationChanged += Instance_OnSoftwareConfigurationChanged;
//_plcModel.Instance.CommunicationInterface = ECommunicationInterface.Softbus;
if (PLCInterface.Instance != null)
{
PLCInterface.UpdateTagList();
_timer.Start();
ConnectionStatus = "connected";
IsConnected = true;
}
} catch (Exception ex)
{
LastError = ex.Message;
ConnectionStatus = "offline";
}
}
private void Instance_OnSoftwareConfigurationChanged(IInstance instance, SOnSoftwareConfigChangedParameter event_param)
{
_plcModel.UpdateTagList();
PLCInterface.UpdateTagList();
}
private void Disconnect()
public void Disconnect()
{
IsConnected = false;
_timer.Stop();
ConnectionStatus = "offline";
_plcModel.Instance = null;
PLCInterface.Instance = null;
}
private void Refresh()
{
if (_plcModel.Instance != null)
if (PLCInterface.Instance != null)
{
CpuTime = _plcModel.LeerInt16("\"DB HMI\".CPU_Scan_Time")?.ToString() ?? "N/A";
LastError = _plcModel.LastError;
CpuTime = PLCInterface.LeerTagInt16("\"DB HMI\".CPU_Scan_Time")?.ToString() ?? "N/A";
LastError = PLCInterface.LastError;
// Disparar el evento RefreshEvent
RefreshEvent?.Invoke(this, EventArgs.Empty);
}
}
@ -153,7 +167,7 @@ namespace CtrEditor.Siemens
IsConfigured = false;
try
{
Instance?.UpdateTagList( ETagListDetails.IO | ETagListDetails.DB);
Instance?.UpdateTagList(ETagListDetails.IO | ETagListDetails.DB, true); // ETagListDetails.IO | ETagListDetails.DB
IsConfigured = true;
}
catch (Exception ex)
@ -186,7 +200,29 @@ namespace CtrEditor.Siemens
LastError = ex.Message;
}
}
public void EscribirTag(string pTag, SDataValue Value)
{
try
{
Instance?.Write(pTag, Value);
}
catch (Exception ex)
{
LastError = pTag + ":" + ex.Message;
}
}
public SDataValue LeerTag(string pTag)
{
try
{
return Instance.Read(pTag);
}
catch (Exception ex)
{
LastError = pTag + ":" + ex.Message;
return new SDataValue();
}
}
public void EscribirTagBool(string pTag, bool pValue)
{
try
@ -198,6 +234,18 @@ namespace CtrEditor.Siemens
LastError = pTag + ":" + ex.Message;
}
}
public void EscribirTagInt16(string pTag, int pValue)
{
try
{
Instance?.WriteInt16(pTag,(short) pValue);
}
catch (Exception ex)
{
LastError = pTag + ":" + ex.Message;
}
}
public bool LeerTagBool(string pTag)
{
try
@ -211,7 +259,7 @@ namespace CtrEditor.Siemens
}
}
public int? LeerInt16(string pTag)
public int? LeerTagInt16(string pTag)
{
try
{

641
Simulacion/Aether.cs Normal file
View File

@ -0,0 +1,641 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
using CtrEditor.Convertidores;
using System.Windows;
using System.Diagnostics;
using CtrEditor.ObjetosSim;
using System.Windows.Documents;
using nkast.Aether.Physics2D;
using nkast.Aether.Physics2D.Dynamics;
using nkast.Aether.Physics2D.Common;
using nkast.Aether.Physics2D.Collision.Shapes;
namespace CtrEditor.Simulacion
{
public class simBase
{
public Body Body { get; protected set; }
public World _world;
public void RemoverBody()
{
if (Body != null)
{
_world.Remove(Body);
}
}
public void SetPosition(float x, float y)
{
Body.SetTransform(new Vector2(x, y), Body.Rotation);
}
public void SetPosition(Vector2 centro)
{
Body.SetTransform(centro, Body.Rotation);
}
}
public class simCurve : simBase
{
private float _innerRadius;
private float _outerRadius;
private float _startAngle;
private float _endAngle;
public float Speed { get; set; } // Velocidad para efectos de cinta transportadora
public simCurve(World world, float innerRadius, float outerRadius, float startAngle, float endAngle, Vector2 position)
{
_world = world;
_innerRadius = innerRadius;
_outerRadius = outerRadius;
_startAngle = Microsoft.Xna.Framework.MathHelper.ToRadians(startAngle);
_endAngle = Microsoft.Xna.Framework.MathHelper.ToRadians(endAngle);
Create(position);
}
public void Create(float innerRadius, float outerRadius, float startAngle, float endAngle, Vector2 position)
{
if (_world == null) return;
_innerRadius = innerRadius;
_outerRadius = outerRadius;
_startAngle = Microsoft.Xna.Framework.MathHelper.ToRadians(startAngle);
_endAngle = Microsoft.Xna.Framework.MathHelper.ToRadians(endAngle);
Create(position);
}
public void Create(Vector2 position)
{
RemoverBody();
// Crear la geometría del sensor de curva
List<Vertices> segments = CreateCurveVertices(_innerRadius, _outerRadius, _startAngle, _endAngle);
Body = new Body();
foreach (var segment in segments)
{
var shape = new PolygonShape(segment, 1f);
var fixture = Body.CreateFixture(shape);
fixture.IsSensor = true;
}
Body.Position = position;
Body.BodyType = BodyType.Static;
Body.Tag = this;
}
public void SetSpeed(float speed)
{
Speed = speed;
}
private List<Vertices> CreateCurveVertices(float innerRadius, float outerRadius, float startAngle, float endAngle)
{
List<Vertices> verticesList = new List<Vertices>();
int segments = 32;
float angleStep = (endAngle - startAngle) / segments;
Vertices innerVertices = new Vertices();
Vertices outerVertices = new Vertices();
for (int i = 0; i <= segments; i++)
{
float angle = startAngle + i * angleStep;
innerVertices.Add(new Vector2(innerRadius * (float)Math.Cos(angle), innerRadius * (float)Math.Sin(angle)));
outerVertices.Add(new Vector2(outerRadius * (float)Math.Cos(angle), outerRadius * (float)Math.Sin(angle)));
}
outerVertices.Reverse();
innerVertices.AddRange(outerVertices);
verticesList.Add(innerVertices);
return verticesList;
}
public void ApplyCurveEffect(Fixture bottle)
{
Vector2 centerToBottle = bottle.Body.Position - Body.Position;
float distanceToCenter = centerToBottle.Length();
if (distanceToCenter >= _innerRadius && distanceToCenter <= _outerRadius)
{
// Calcular la velocidad tangencial
float speedMetersPerSecond = Speed / 60.0f;
float angularVelocity = speedMetersPerSecond / distanceToCenter;
// Vector tangente (perpendicular al radio)
Vector2 tangent = new Vector2(-centerToBottle.Y, centerToBottle.X);
tangent.Normalize();
// Velocidad deseada
Vector2 desiredVelocity = tangent * angularVelocity * distanceToCenter;
bottle.Body.LinearVelocity = desiredVelocity;
}
}
}
public class simDescarte : simBase
{
private float _radius;
public simDescarte(World world, float diameter, Vector2 position)
{
_world = world;
_radius = diameter / 2;
Create(position);
}
public void SetDiameter(float diameter)
{
_radius = diameter / 2;
Create(Body.Position); // Recrear el círculo con el nuevo tamaño
}
public void Create(Vector2 position)
{
RemoverBody();
Body = _world.CreateCircle(_radius, 1f, position);
Body.FixtureList[0].IsSensor = true;
Body.BodyType = BodyType.Static;
Body.Tag = this; // Importante para la identificación durante la colisión
}
}
public class simTransporte : simBase
{
public float Speed { get; set; } // Velocidad para efectos de cinta transportadora
public float DistanceGuide2Guide { get; set; }
public bool TransportWithGuides = false;
public simTransporte(World world, float width, float height, Vector2 position, float angle = 0)
{
_world = world;
Create(width, height, position, angle);
}
public float Angle
{
get { return Microsoft.Xna.Framework.MathHelper.ToDegrees(Body.Rotation); }
set { Body.Rotation = Microsoft.Xna.Framework.MathHelper.ToRadians(value); }
}
public new void SetPosition(float x, float y)
{
Body.Position = new Vector2(x, y);
}
public void SetSpeed(float speed)
{
Speed = speed;
}
public void SetDimensions(float width, float height)
{
Body.Remove(Body.FixtureList[0]);
var newShape = new PolygonShape(PolygonTools.CreateRectangle(width / 2, height / 2), 1f);
Body.CreateFixture(newShape);
}
public void Create(float width, float height, Vector2 position, float angle = 0)
{
RemoverBody();
Body = _world.CreateRectangle( width, height, 1f, position);
Body.FixtureList[0].IsSensor = true;
Body.BodyType = BodyType.Static;
Body.Rotation = Microsoft.Xna.Framework.MathHelper.ToRadians(angle);
Body.Tag = this; // Importante para la identificación durante la colisión
}
}
public class simBarrera : simBase
{
public bool LuzCortada = false;
public simBarrera(World world, float width, float height, Vector2 position, float angle = 0)
{
_world = world;
Create(width, height, position, angle);
}
public float Angle
{
get { return Microsoft.Xna.Framework.MathHelper.ToDegrees(Body.Rotation); }
set { Body.Rotation = Microsoft.Xna.Framework.MathHelper.ToRadians(value); }
}
public new void SetPosition(float x, float y)
{
Body.Position = new Vector2(x, y);
}
public void SetDimensions(float width, float height)
{
Body.Remove(Body.FixtureList[0]);
var newShape = new PolygonShape(PolygonTools.CreateRectangle(width / 2, height / 2), 1f);
Body.CreateFixture(newShape);
}
public void Create(float width, float height, Vector2 position, float angle = 0)
{
RemoverBody();
Body = _world.CreateRectangle( width, height, 1f, position);
Body.FixtureList[0].IsSensor = true;
Body.BodyType = BodyType.Static;
Body.Rotation = Microsoft.Xna.Framework.MathHelper.ToRadians(angle);
Body.Tag = this; // Importante para la identificación durante la colisión
LuzCortada = false;
}
}
public class simGuia : simBase
{
public simGuia(World world, Vector2 start, Vector2 end)
{
_world = world;
Create(start, end);
}
public void Create(Vector2 start, Vector2 end)
{
RemoverBody();
Body = _world.CreateEdge( start, end);
Body.BodyType = BodyType.Static;
Body.Tag = this; // Importante para la identificación durante la colisión
}
public void UpdateVertices(Vector2 newStart, Vector2 newEnd)
{
Create(newStart, newEnd); // Recrear la línea con nuevos vértices
}
}
public class simBotella : simBase
{
private float _radius;
private float _mass;
public bool Descartar = false;
public simBotella(World world, float diameter, Vector2 position, float mass)
{
_world = world;
_radius = diameter / 2;
_mass = mass;
Create(position);
}
public float CenterX
{
get { return Body.Position.X; }
set { }
}
public float CenterY
{
get { return Body.Position.Y; }
set { }
}
public Vector2 Center
{
get { return Body.Position; }
}
public float Mass
{
get
{
if (_mass <= 0)
_mass = 1;
return _mass;
}
set { _mass = value; }
}
private void Create(Vector2 position)
{
RemoverBody();
Body = _world.CreateCircle( _radius, 0.2f, position);
Body.BodyType = BodyType.Dynamic;
// Restablecer manejador de eventos de colisión
Body.OnCollision += HandleCollision;
//Body.OnSeparation += HandleOnSeparation;
Body.Tag = this; // Importante para la identificación durante la colisión
// Configurar la fricción
Body.SetFriction(0.3f);
// Configurar amortiguamiento
Body.LinearDamping = 0.4f; // Ajustar para controlar la reducción de la velocidad lineal
Body.AngularDamping = 0.4f; // Ajustar para controlar la reducción de la velocidad angular
Body.SetRestitution(0.2f); // Baja restitución para menos rebote
Body.IsBullet = true;
}
public void SetDiameter(float diameter)
{
_radius = diameter / 2;
Create(Body.Position); // Recrear el círculo con el nuevo tamaño
}
public void SetMass(float mass)
{
Mass = mass;
}
private bool HandleCollision(Fixture fixtureA, Fixture fixtureB, nkast.Aether.Physics2D.Dynamics.Contacts.Contact contact)
{
if (fixtureB.Body.Tag is simBarrera Sensor)
{
Sensor.LuzCortada = true;
return true;
}
else if (fixtureB.Body.Tag is simCurve curve)
{
curve.ApplyCurveEffect(fixtureA);
return true; // No aplicar respuestas físicas
}
else if (fixtureB.Body.Tag is simDescarte)
{
Descartar = true;
return true;
}
else if (fixtureB.Body.Tag is simTransporte)
{
simTransporte conveyor = fixtureB.Body.Tag as simTransporte;
if (conveyor.Speed != 0)
{
CircleShape circleShape = fixtureA.Shape as CircleShape;
PolygonShape polygonShape = fixtureB.Shape as PolygonShape;
// Obtener centro y radio del círculo
Vector2 centroCirculo = fixtureA.Body.Position;
float radio = circleShape.Radius;
// Obtener los vértices del polígono (rectángulo)
Vector2[] vertices = new Vector2[polygonShape.Vertices.Count];
float cos = (float)Math.Cos(fixtureB.Body.Rotation);
float sin = (float)Math.Sin(fixtureB.Body.Rotation);
for (int i = 0; i < polygonShape.Vertices.Count; i++)
{
Vector2 vertex = polygonShape.Vertices[i];
float rotatedX = vertex.X * cos - vertex.Y * sin + fixtureB.Body.Position.X;
float rotatedY = vertex.X * sin + vertex.Y * cos + fixtureB.Body.Position.Y;
vertices[i] = new Vector2(rotatedX, rotatedY);
}
// Calcular el porcentaje de la superficie compartida
float porcentajeCompartido = InterseccionCirculoRectanguloAether.CalcularSuperficieCompartida(vertices, centroCirculo, radio);
// Aplicar el efecto del transportador usando el porcentaje calculado
//if (conveyor.TransportWithGuides)
// if (conveyor.DistanceGuide2Guide <= radio * 2)
// CenterFixtureOnConveyor(fixtureA, conveyor);
ApplyConveyorEffect(conveyor, fixtureA, porcentajeCompartido);
}
return true; // No aplicar respuestas físicas
}
return true; // No aplicar respuestas físicas
}
private void ApplyConveyorEffect(simTransporte conveyor, Fixture circleFixture, float porcentajeCompartido)
{
float speedMetersPerSecond = conveyor.Speed / 60.0f;
Vector2 desiredVelocity = new Vector2((float)Math.Cos(conveyor.Body.Rotation), (float)Math.Sin(conveyor.Body.Rotation)) * speedMetersPerSecond;
circleFixture.Body.LinearVelocity += desiredVelocity * porcentajeCompartido;
}
private void CenterFixtureOnConveyor(Fixture fixtureA, simTransporte conveyor)
{
// Obtener el centro del conveyor
Vector2 conveyorCenter = conveyor.Body.Position;
// Calcular el vector de la línea horizontal centrada de conveyor
float halfDistance = conveyor.DistanceGuide2Guide / 2;
float cos = (float)Math.Cos(conveyor.Body.Rotation);
float sin = (float)Math.Sin(conveyor.Body.Rotation);
Vector2 offset = new Vector2(halfDistance * cos, halfDistance * sin);
// Línea horizontal centrada de conveyor en el espacio del mundo
Vector2 lineStart = conveyorCenter - offset;
Vector2 lineEnd = conveyorCenter + offset;
// Proyectar el centro de fixtureA sobre la línea horizontal
Vector2 fixtureCenter = fixtureA.Body.Position;
Vector2 closestPoint = ProjectPointOntoLine(fixtureCenter, lineStart, lineEnd);
// Mover fixtureA al punto más cercano en la línea horizontal
fixtureA.Body.Position = closestPoint;
}
private Vector2 ProjectPointOntoLine(Vector2 point, Vector2 lineStart, Vector2 lineEnd)
{
Vector2 lineDirection = lineEnd - lineStart;
lineDirection.Normalize();
Vector2 pointToLineStart = point - lineStart;
Vector2.Dot(ref pointToLineStart,ref lineDirection, out float projectionLength);
return lineStart + projectionLength * lineDirection;
}
}
public class SimulationManagerFP
{
private World world;
private Canvas simulationCanvas;
public List<simBase> Cuerpos;
private Stopwatch stopwatch;
private double stopwatch_last;
public Canvas DebugCanvas { get => simulationCanvas; set => simulationCanvas = value; }
public SimulationManagerFP()
{
world = new World(new Vector2(0, 0)); // Vector2.Zero
Cuerpos = new List<simBase>();
stopwatch = new Stopwatch();
stopwatch.Start();
}
public void Clear()
{
if (world.BodyList.Count > 0)
world.Clear();
if (Cuerpos.Count > 0)
Cuerpos.Clear();
}
public void Step()
{
// Detener el cronómetro y obtener el tiempo transcurrido en milisegundos
float elapsedMilliseconds = (float)(stopwatch.Elapsed.TotalMilliseconds - stopwatch_last);
stopwatch_last = stopwatch.Elapsed.TotalMilliseconds;
// Pasar el tiempo transcurrido al método Step
world.Step(elapsedMilliseconds / 1000.0f);
}
public void Remove(simBase Objeto)
{
if (Objeto != null)
{
Objeto.RemoverBody();
Cuerpos.Remove(Objeto);
}
}
public simCurve AddCurve(float innerRadius, float outerRadius, float startAngle, float endAngle, Vector2 position)
{
simCurve curva = new simCurve(world, innerRadius, outerRadius, startAngle, endAngle, position);
Cuerpos.Add(curva);
return curva;
}
public simBotella AddCircle(float diameter, Vector2 position, float mass)
{
simBotella circle = new simBotella(world, diameter, position, mass);
Cuerpos.Add(circle);
return circle;
}
public simTransporte AddRectangle(float width, float height, Vector2 position, float angle)
{
simTransporte rectangle = new simTransporte(world, width, height, position, angle);
Cuerpos.Add(rectangle);
return rectangle;
}
public simBarrera AddBarrera(float width, float height, Vector2 position, float angle)
{
simBarrera rectangle = new simBarrera(world, width, height, position, angle);
Cuerpos.Add(rectangle);
return rectangle;
}
public simGuia AddLine(Vector2 start, Vector2 end)
{
simGuia line = new simGuia(world, start, end);
Cuerpos.Add(line);
return line;
}
public simDescarte AddDescarte(float diameter, Vector2 position)
{
simDescarte descarte = new simDescarte(world, diameter, position);
Cuerpos.Add(descarte);
return descarte;
}
public void Debug_DrawInitialBodies()
{
Debug_ClearSimulationShapes();
world.Step(0.01f); // Para actualizar la BodyList
foreach (Body body in world.BodyList)
{
foreach (Fixture fixture in body.FixtureList)
{
DrawShape(fixture);
}
}
}
public void Debug_ClearSimulationShapes()
{
var simulationShapes = simulationCanvas.Children.OfType<System.Windows.Shapes.Shape>().Where(s => s.Tag as string == "Simulation").ToList();
foreach (var shape in simulationShapes)
{
simulationCanvas.Children.Remove(shape);
}
}
private void DrawShape(Fixture fixture)
{
System.Windows.Shapes.Shape shape;
switch (fixture.Shape.ShapeType)
{
case ShapeType.Circle:
shape = DrawCircle(fixture);
break;
case ShapeType.Polygon:
shape = DrawPolygon(fixture);
break;
case ShapeType.Edge:
shape = DrawEdge(fixture);
break;
default:
return;
}
shape.Tag = "Simulation"; // Marcar para simulación
Canvas.SetZIndex(shape, 20);
simulationCanvas.Children.Add(shape);
}
private float p(float x)
{
float c = PixelToMeter.Instance.calc.MetersToPixels(x);
return c;
}
private System.Windows.Shapes.Shape DrawEdge(Fixture fixture)
{
EdgeShape edge = fixture.Shape as EdgeShape;
Line line = new Line
{
X1 = p(edge.Vertex1.X + fixture.Body.Position.X), // Aplicar escala y posición
Y1 = p(edge.Vertex1.Y + fixture.Body.Position.Y),
X2 = p(edge.Vertex2.X + fixture.Body.Position.X),
Y2 = p(edge.Vertex2.Y + fixture.Body.Position.Y),
Stroke = Brushes.Black,
StrokeThickness = 2
};
return line;
}
private System.Windows.Shapes.Shape DrawCircle(Fixture fixture)
{
CircleShape circle = fixture.Shape as CircleShape;
Ellipse ellipse = new Ellipse
{
Width = p(circle.Radius * 2), // Escalado para visualización
Height = p(circle.Radius * 2), // Escalado para visualización
Stroke = Brushes.Black,
StrokeThickness = 2
};
Canvas.SetLeft(ellipse, p(fixture.Body.Position.X - circle.Radius));
Canvas.SetTop(ellipse, p(fixture.Body.Position.Y - circle.Radius));
return ellipse;
}
private System.Windows.Shapes.Shape DrawPolygon(Fixture fixture)
{
Polygon polygon = new Polygon { Stroke = Brushes.Black, StrokeThickness = 2 };
PolygonShape polyShape = fixture.Shape as PolygonShape;
float cos = (float)Math.Cos(fixture.Body.Rotation);
float sin = (float)Math.Sin(fixture.Body.Rotation);
foreach (Vector2 vertex in polyShape.Vertices)
{
float rotatedX = vertex.X * cos - vertex.Y * sin + fixture.Body.Position.X;
float rotatedY = vertex.X * sin + vertex.Y * cos + fixture.Body.Position.Y;
polygon.Points.Add(new Point(p(rotatedX), p(rotatedY)));
}
return polygon;
}
}
}

641
Simulacion/FPhysics.cs Normal file
View File

@ -0,0 +1,641 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Factories;
using FarseerPhysics.Collision.Shapes;
using nkast.Aether.Physics2D.Common;
using CtrEditor.Convertidores;
using FarseerPhysics.Common;
using System.Windows;
using System.Diagnostics;
using FarseerPhysics.Dynamics.Joints;
using CtrEditor.ObjetosSim;
using System.Windows.Documents;
namespace CtrEditor.Simulacion
{
public class simBase
{
public Body Body { get; protected set; }
public World _world;
public void RemoverBody()
{
if (Body != null)
{
_world.RemoveBody(Body);
}
}
public void SetPosition(float x, float y)
{
Body.SetTransform(new Vector2(x, y), Body.Rotation);
}
public void SetPosition(Vector2 centro)
{
Body.SetTransform(centro, Body.Rotation);
}
}
public class simCurve : simBase
{
private float _innerRadius;
private float _outerRadius;
private float _startAngle;
private float _endAngle;
public float Speed { get; set; } // Velocidad para efectos de cinta transportadora
public simCurve(World world, float innerRadius, float outerRadius, float startAngle, float endAngle, Vector2 position)
{
_world = world;
_innerRadius = innerRadius;
_outerRadius = outerRadius;
_startAngle = MathHelper.ToRadians(startAngle);
_endAngle = MathHelper.ToRadians(endAngle);
Create(position);
}
public void Create(float innerRadius, float outerRadius, float startAngle, float endAngle, Vector2 position)
{
if (_world == null) return;
_innerRadius = innerRadius;
_outerRadius = outerRadius;
_startAngle = MathHelper.ToRadians(startAngle);
_endAngle = MathHelper.ToRadians(endAngle);
Create(position);
}
public void Create(Vector2 position)
{
RemoverBody();
// Crear la geometría del sensor de curva
List<Vertices> segments = CreateCurveVertices(_innerRadius, _outerRadius, _startAngle, _endAngle);
Body = new Body(_world);
foreach (var segment in segments)
{
var shape = new PolygonShape(segment, 1f);
var fixture = Body.CreateFixture(shape);
fixture.IsSensor = true;
}
Body.Position = position;
Body.BodyType = BodyType.Static;
Body.UserData = this;
}
public void SetSpeed(float speed)
{
Speed = speed;
}
private List<Vertices> CreateCurveVertices(float innerRadius, float outerRadius, float startAngle, float endAngle)
{
List<Vertices> verticesList = new List<Vertices>();
int segments = 32;
float angleStep = (endAngle - startAngle) / segments;
Vertices innerVertices = new Vertices();
Vertices outerVertices = new Vertices();
for (int i = 0; i <= segments; i++)
{
float angle = startAngle + i * angleStep;
innerVertices.Add(new Vector2(innerRadius * (float)Math.Cos(angle), innerRadius * (float)Math.Sin(angle)));
outerVertices.Add(new Vector2(outerRadius * (float)Math.Cos(angle), outerRadius * (float)Math.Sin(angle)));
}
outerVertices.Reverse();
innerVertices.AddRange(outerVertices);
verticesList.Add(innerVertices);
return verticesList;
}
public void ApplyCurveEffect(Fixture bottle)
{
Vector2 centerToBottle = bottle.Body.Position - Body.Position;
float distanceToCenter = centerToBottle.Length();
if (distanceToCenter >= _innerRadius && distanceToCenter <= _outerRadius)
{
// Calcular la velocidad tangencial
float speedMetersPerSecond = Speed / 60.0f;
float angularVelocity = speedMetersPerSecond / distanceToCenter;
// Vector tangente (perpendicular al radio)
Vector2 tangent = new Vector2(-centerToBottle.Y, centerToBottle.X);
tangent.Normalize();
// Velocidad deseada
Vector2 desiredVelocity = tangent * angularVelocity * distanceToCenter;
bottle.Body.LinearVelocity = desiredVelocity;
}
}
}
public class simDescarte : simBase
{
private float _radius;
public simDescarte(World world, float diameter, Vector2 position)
{
_world = world;
_radius = diameter / 2;
Create(position);
}
public void SetDiameter(float diameter)
{
_radius = diameter / 2;
Create(Body.Position); // Recrear el círculo con el nuevo tamaño
}
public void Create(Vector2 position)
{
RemoverBody();
Body = BodyFactory.CreateCircle(_world, _radius, 1f, position);
Body.FixtureList[0].IsSensor = true;
Body.BodyType = BodyType.Static;
Body.UserData = this; // Importante para la identificación durante la colisión
}
}
public class simTransporte : simBase
{
public float Speed { get; set; } // Velocidad para efectos de cinta transportadora
public float DistanceGuide2Guide { get; set; }
public bool TransportWithGuides = false;
public simTransporte(World world, float width, float height, Vector2 position, float angle = 0)
{
_world = world;
Create(width, height, position, angle);
}
public float Angle
{
get { return MathHelper.ToDegrees(Body.Rotation); }
set { Body.Rotation = MathHelper.ToRadians(value); }
}
public new void SetPosition(float x, float y)
{
Body.Position = new Vector2(x, y);
}
public void SetSpeed(float speed)
{
Speed = speed;
}
public void SetDimensions(float width, float height)
{
Body.DestroyFixture(Body.FixtureList[0]);
var newShape = new PolygonShape(PolygonTools.CreateRectangle(width / 2, height / 2), 1f);
Body.CreateFixture(newShape);
}
public void Create(float width, float height, Vector2 position, float angle = 0)
{
RemoverBody();
Body = BodyFactory.CreateRectangle(_world, width, height, 1f, position);
Body.FixtureList[0].IsSensor = true;
Body.BodyType = BodyType.Static;
Body.Rotation = MathHelper.ToRadians(angle);
Body.UserData = this; // Importante para la identificación durante la colisión
}
}
public class simBarrera : simBase
{
public bool LuzCortada = false;
public simBarrera(World world, float width, float height, Vector2 position, float angle = 0)
{
_world = world;
Create(width, height, position, angle);
}
public float Angle
{
get { return MathHelper.ToDegrees(Body.Rotation); }
set { Body.Rotation = MathHelper.ToRadians(value); }
}
public new void SetPosition(float x, float y)
{
Body.Position = new Vector2(x, y);
}
public void SetDimensions(float width, float height)
{
Body.DestroyFixture(Body.FixtureList[0]);
var newShape = new PolygonShape(PolygonTools.CreateRectangle(width / 2, height / 2), 1f);
Body.CreateFixture(newShape);
}
public void Create(float width, float height, Vector2 position, float angle = 0)
{
RemoverBody();
Body = BodyFactory.CreateRectangle(_world, width, height, 1f, position);
Body.FixtureList[0].IsSensor = true;
Body.BodyType = BodyType.Static;
Body.Rotation = MathHelper.ToRadians(angle);
Body.UserData = this; // Importante para la identificación durante la colisión
LuzCortada = false;
}
}
public class simGuia : simBase
{
public simGuia(World world, Vector2 start, Vector2 end)
{
_world = world;
Create(start, end);
}
public void Create(Vector2 start, Vector2 end)
{
RemoverBody();
Body = BodyFactory.CreateEdge(_world, start, end);
Body.BodyType = BodyType.Static;
Body.UserData = this; // Importante para la identificación durante la colisión
}
public void UpdateVertices(Vector2 newStart, Vector2 newEnd)
{
Create(newStart, newEnd); // Recrear la línea con nuevos vértices
}
}
public class simBotella : simBase
{
private float _radius;
private float _mass;
public bool Descartar = false;
public simBotella(World world, float diameter, Vector2 position, float mass)
{
_world = world;
_radius = diameter / 2;
_mass = mass;
Create(position);
}
public float CenterX
{
get { return Body.Position.X; }
set { }
}
public float CenterY
{
get { return Body.Position.Y; }
set { }
}
public Vector2 Center
{
get { return Body.Position; }
}
public float Mass
{
get
{
if (_mass <= 0)
_mass = 1;
return _mass;
}
set { _mass = value; }
}
private void Create(Vector2 position)
{
RemoverBody();
Body = BodyFactory.CreateCircle(_world, _radius, 0.2f, position);
Body.BodyType = BodyType.Dynamic;
// Restablecer manejador de eventos de colisión
Body.OnCollision += HandleCollision;
//Body.OnSeparation += HandleOnSeparation;
Body.UserData = this; // Importante para la identificación durante la colisión
// Configurar la fricción
Body.Friction = 0.3f; // Ajustar según sea necesario para tu simulación
// Configurar amortiguamiento
Body.LinearDamping = 0.4f; // Ajustar para controlar la reducción de la velocidad lineal
Body.AngularDamping = 0.4f; // Ajustar para controlar la reducción de la velocidad angular
Body.Restitution = 0.2f; // Baja restitución para menos rebote
// Body.IsBullet = true;
}
public void SetDiameter(float diameter)
{
_radius = diameter / 2;
Create(Body.Position); // Recrear el círculo con el nuevo tamaño
}
public void SetMass(float mass)
{
Mass = mass;
}
private bool HandleCollision(Fixture fixtureA, Fixture fixtureB, FarseerPhysics.Dynamics.Contacts.Contact contact)
{
if (fixtureB.Body.UserData is simBarrera Sensor)
{
Sensor.LuzCortada = true;
return true;
}
else if (fixtureB.Body.UserData is simCurve curve)
{
curve.ApplyCurveEffect(fixtureA);
return true; // No aplicar respuestas físicas
}
else if (fixtureB.Body.UserData is simDescarte)
{
Descartar = true;
return true;
} else if (fixtureB.Body.UserData is simTransporte)
{
simTransporte conveyor = fixtureB.Body.UserData as simTransporte;
if ( conveyor.Speed != 0 ) {
CircleShape circleShape = fixtureA.Shape as CircleShape;
PolygonShape polygonShape = fixtureB.Shape as PolygonShape;
// Obtener centro y radio del círculo
Vector2 centroCirculo = fixtureA.Body.Position;
float radio = circleShape.Radius;
// Obtener los vértices del polígono (rectángulo)
Vector2[] vertices = new Vector2[polygonShape.Vertices.Count];
float cos = (float)Math.Cos(fixtureB.Body.Rotation);
float sin = (float)Math.Sin(fixtureB.Body.Rotation);
for (int i = 0; i < polygonShape.Vertices.Count; i++)
{
Vector2 vertex = polygonShape.Vertices[i];
float rotatedX = vertex.X * cos - vertex.Y * sin + fixtureB.Body.Position.X;
float rotatedY = vertex.X * sin + vertex.Y * cos + fixtureB.Body.Position.Y;
vertices[i] = new Vector2(rotatedX, rotatedY);
}
// Calcular el porcentaje de la superficie compartida
float porcentajeCompartido = InterseccionCirculoRectangulo.CalcularSuperficieCompartida(vertices, centroCirculo, radio);
// Aplicar el efecto del transportador usando el porcentaje calculado
if (conveyor.TransportWithGuides)
if (conveyor.DistanceGuide2Guide <= radio * 2)
CenterFixtureOnConveyor(fixtureA, conveyor);
ApplyConveyorEffect(conveyor, fixtureA, porcentajeCompartido);
}
return true; // No aplicar respuestas físicas
}
return true; // No aplicar respuestas físicas
}
private void ApplyConveyorEffect(simTransporte conveyor, Fixture circleFixture, float porcentajeCompartido)
{
float speedMetersPerSecond = conveyor.Speed / 60.0f;
Vector2 desiredVelocity = new Vector2((float)Math.Cos(conveyor.Body.Rotation), (float)Math.Sin(conveyor.Body.Rotation)) * speedMetersPerSecond;
circleFixture.Body.LinearVelocity += desiredVelocity * porcentajeCompartido;
}
private void CenterFixtureOnConveyor(Fixture fixtureA, simTransporte conveyor)
{
// Obtener el centro del conveyor
Vector2 conveyorCenter = conveyor.Body.Position;
// Calcular el vector de la línea horizontal centrada de conveyor
float halfDistance = conveyor.DistanceGuide2Guide / 2;
float cos = (float)Math.Cos(conveyor.Body.Rotation);
float sin = (float)Math.Sin(conveyor.Body.Rotation);
Vector2 offset = new Vector2(halfDistance * cos, halfDistance * sin);
// Línea horizontal centrada de conveyor en el espacio del mundo
Vector2 lineStart = conveyorCenter - offset;
Vector2 lineEnd = conveyorCenter + offset;
// Proyectar el centro de fixtureA sobre la línea horizontal
Vector2 fixtureCenter = fixtureA.Body.Position;
Vector2 closestPoint = ProjectPointOntoLine(fixtureCenter, lineStart, lineEnd);
// Mover fixtureA al punto más cercano en la línea horizontal
fixtureA.Body.Position = closestPoint;
}
private Vector2 ProjectPointOntoLine(Vector2 point, Vector2 lineStart, Vector2 lineEnd)
{
Vector2 lineDirection = lineEnd - lineStart;
lineDirection.Normalize();
Vector2 pointToLineStart = point - lineStart;
float projectionLength = Vector2.Dot(pointToLineStart, lineDirection);
return lineStart + projectionLength * lineDirection;
}
}
public class SimulationManagerFP
{
private World world;
private Canvas simulationCanvas;
public List<simBase> Cuerpos;
private Stopwatch stopwatch;
private double stopwatch_last;
public Canvas DebugCanvas { get => simulationCanvas; set => simulationCanvas = value; }
public SimulationManagerFP()
{
world = new World(new Vector2(0, 0)); // Vector2.Zero
Cuerpos = new List<simBase>();
stopwatch = new Stopwatch();
stopwatch.Start();
}
public void Clear()
{
if (world.BodyList.Count > 0)
world.Clear();
if (Cuerpos.Count > 0)
Cuerpos.Clear();
}
public void Step()
{
// Detener el cronómetro y obtener el tiempo transcurrido en milisegundos
float elapsedMilliseconds = (float) (stopwatch.Elapsed.TotalMilliseconds - stopwatch_last);
stopwatch_last = stopwatch.Elapsed.TotalMilliseconds;
// Pasar el tiempo transcurrido al método Step
world.Step(elapsedMilliseconds / 1000.0f);
}
public void Remove(simBase Objeto)
{
if (Objeto != null)
{
Objeto.RemoverBody();
Cuerpos.Remove(Objeto);
}
}
public simCurve AddCurve(float innerRadius, float outerRadius, float startAngle, float endAngle, Vector2 position)
{
simCurve curva = new simCurve( world, innerRadius, outerRadius, startAngle, endAngle, position);
Cuerpos.Add(curva);
return curva;
}
public simBotella AddCircle(float diameter, Vector2 position, float mass)
{
simBotella circle = new simBotella(world, diameter, position, mass);
Cuerpos.Add(circle);
return circle;
}
public simTransporte AddRectangle(float width, float height, Vector2 position, float angle)
{
simTransporte rectangle = new simTransporte(world, width, height, position, angle);
Cuerpos.Add(rectangle);
return rectangle;
}
public simBarrera AddBarrera(float width, float height, Vector2 position, float angle)
{
simBarrera rectangle = new simBarrera(world, width, height, position, angle);
Cuerpos.Add(rectangle);
return rectangle;
}
public simGuia AddLine(Vector2 start, Vector2 end)
{
simGuia line = new simGuia(world, start, end);
Cuerpos.Add(line);
return line;
}
public simDescarte AddDescarte(float diameter, Vector2 position)
{
simDescarte descarte = new simDescarte(world, diameter, position);
Cuerpos.Add(descarte);
return descarte;
}
public void Debug_DrawInitialBodies()
{
Debug_ClearSimulationShapes();
world.Step(0.01f); // Para actualizar la BodyList
foreach (Body body in world.BodyList)
{
foreach (Fixture fixture in body.FixtureList)
{
DrawShape(fixture);
}
}
}
public void Debug_ClearSimulationShapes()
{
var simulationShapes = simulationCanvas.Children.OfType<System.Windows.Shapes.Shape>().Where(s => s.Tag as string == "Simulation").ToList();
foreach (var shape in simulationShapes)
{
simulationCanvas.Children.Remove(shape);
}
}
private void DrawShape(Fixture fixture)
{
System.Windows.Shapes.Shape shape;
switch (fixture.ShapeType)
{
case ShapeType.Circle:
shape = DrawCircle(fixture);
break;
case ShapeType.Polygon:
shape = DrawPolygon(fixture);
break;
case ShapeType.Edge:
shape = DrawEdge(fixture);
break;
default:
return;
}
shape.Tag = "Simulation"; // Marcar para simulación
Canvas.SetZIndex(shape, 20);
simulationCanvas.Children.Add(shape);
}
private float p(float x)
{
float c = PixelToMeter.Instance.calc.MetersToPixels(x);
return c;
}
private System.Windows.Shapes.Shape DrawEdge(Fixture fixture)
{
EdgeShape edge = fixture.Shape as EdgeShape;
Line line = new Line
{
X1 = p(edge.Vertex1.X + fixture.Body.Position.X), // Aplicar escala y posición
Y1 = p(edge.Vertex1.Y + fixture.Body.Position.Y),
X2 = p(edge.Vertex2.X + fixture.Body.Position.X),
Y2 = p(edge.Vertex2.Y + fixture.Body.Position.Y),
Stroke = Brushes.Black,
StrokeThickness = 2
};
return line;
}
private System.Windows.Shapes.Shape DrawCircle(Fixture fixture)
{
CircleShape circle = fixture.Shape as CircleShape;
Ellipse ellipse = new Ellipse
{
Width = p(circle.Radius * 2), // Escalado para visualización
Height = p(circle.Radius * 2), // Escalado para visualización
Stroke = Brushes.Black,
StrokeThickness = 2
};
Canvas.SetLeft(ellipse, p(fixture.Body.Position.X - circle.Radius));
Canvas.SetTop(ellipse, p(fixture.Body.Position.Y - circle.Radius));
return ellipse;
}
private System.Windows.Shapes.Shape DrawPolygon(Fixture fixture)
{
Polygon polygon = new Polygon { Stroke = Brushes.Black, StrokeThickness = 2 };
PolygonShape polyShape = fixture.Shape as PolygonShape;
float cos = (float)Math.Cos(fixture.Body.Rotation);
float sin = (float)Math.Sin(fixture.Body.Rotation);
foreach (Vector2 vertex in polyShape.Vertices)
{
float rotatedX = vertex.X * cos - vertex.Y * sin + fixture.Body.Position.X;
float rotatedY = vertex.X * sin + vertex.Y * cos + fixture.Body.Position.Y;
polygon.Points.Add(new Point(p(rotatedX), p(rotatedY)));
}
return polygon;
}
}
}

View File

@ -0,0 +1,134 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using FarseerPhysics.Collision;
using Microsoft.VisualBasic.Devices;
using nkast.Aether.Physics2D.Common;
namespace CtrEditor.Simulacion
{
internal class InterseccionCirculoRectangulo
{
// Definición de la función CalcularSuperficieCompartida
public static float CalcularSuperficieCompartida(Vector2[] vertices, Vector2 center, float r)
{
float totalCircleArea = (float)Math.PI * r * r;
// Distancia a líneas ajustado
float[] distances = new float[4];
for (int i = 0; i < 4; i++)
{
distances[i] = DistanceFromLine(center, vertices[i], vertices[(i + 1) % 4]);
}
float minDistance = float.MaxValue;
foreach (var dist in distances)
{
if (Math.Abs(dist) < Math.Abs(minDistance))
minDistance = dist;
}
float d = Math.Abs(minDistance);
float sharedArea = 0;
if (Array.TrueForAll(distances, dist => Math.Abs(dist) > r))
{
sharedArea = totalCircleArea;
}
else if (d < r)
{
float cosTheta = Math.Min(1, d / r);
float sinTheta = (float)Math.Sqrt(Math.Max(0, r * r - d * d));
if (minDistance < 0) // El centro está dentro del rectángulo
{
float areaOutside = r * r * (float)Math.Acos(cosTheta) - d * sinTheta;
sharedArea = totalCircleArea - areaOutside;
}
else // El centro está fuera del rectángulo
{
sharedArea = r * r * (float)Math.Acos(cosTheta) - d * sinTheta;
}
}
else
{
sharedArea = 0;
}
return sharedArea / totalCircleArea;
}
public static float DistanceFromLine(Vector2 point, Vector2 start, Vector2 end)
{
float A = end.Y - start.Y;
float B = start.X - end.X;
float C = end.X * start.Y - start.X * end.Y;
float distance = (A * point.X + B * point.Y + C) / (float)Math.Sqrt(A * A + B * B);
return distance;
}
}
internal class InterseccionCirculoRectanguloAether
{
// Definición de la función CalcularSuperficieCompartida
public static float CalcularSuperficieCompartida(nkast.Aether.Physics2D.Common.Vector2[] vertices, nkast.Aether.Physics2D.Common.Vector2 center, float r)
{
float totalCircleArea = (float)Math.PI * r * r;
// Distancia a líneas ajustado
float[] distances = new float[4];
for (int i = 0; i < 4; i++)
{
distances[i] = DistanceFromLine(center, vertices[i], vertices[(i + 1) % 4]);
}
float minDistance = float.MaxValue;
foreach (var dist in distances)
{
if (Math.Abs(dist) < Math.Abs(minDistance))
minDistance = dist;
}
float d = Math.Abs(minDistance);
float sharedArea = 0;
if (Array.TrueForAll(distances, dist => Math.Abs(dist) > r))
{
sharedArea = totalCircleArea;
}
else if (d < r)
{
float cosTheta = Math.Min(1, d / r);
float sinTheta = (float)Math.Sqrt(Math.Max(0, r * r - d * d));
if (minDistance < 0) // El centro está dentro del rectángulo
{
float areaOutside = r * r * (float)Math.Acos(cosTheta) - d * sinTheta;
sharedArea = totalCircleArea - areaOutside;
}
else // El centro está fuera del rectángulo
{
sharedArea = r * r * (float)Math.Acos(cosTheta) - d * sinTheta;
}
}
else
{
sharedArea = 0;
}
return sharedArea / totalCircleArea;
}
public static float DistanceFromLine(nkast.Aether.Physics2D.Common.Vector2 point, nkast.Aether.Physics2D.Common.Vector2 start, nkast.Aether.Physics2D.Common.Vector2 end)
{
float A = end.Y - start.Y;
float B = start.X - end.X;
float C = end.X * start.Y - start.X * end.Y;
float distance = (A * point.X + B * point.Y + C) / (float)Math.Sqrt(A * A + B * B);
return distance;
}
}
}

BIN
app2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

BIN
imagenes/filler.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

BIN
imagenes/motorVerde.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
imagenes/tank.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB