From 81450e56aa5386021c50f5622e2d156840344656 Mon Sep 17 00:00:00 2001 From: Miguel Date: Sat, 11 May 2024 16:58:55 +0200 Subject: [PATCH] Agregando el uc Motor --- Convertidores/Converters.cs | 130 ++++++++++++++ CtrEditor.csproj | 9 + MainViewModel.cs | 1 + MainWindow.xaml | 45 +++-- MainWindow.xaml.cs | 68 +++++-- ObjetosSim/UserControlFactory.cs | 9 + ObjetosSim/UserControls/CircularSegment.xaml | 15 ++ .../UserControls/CircularSegment.xaml.cs | 115 ++++++++++++ ObjetosSim/osBase.cs | 99 +---------- ObjetosSim/ucBotella.xaml | 4 +- ObjetosSim/ucBotella.xaml.cs | 1 + ObjetosSim/ucGuia.xaml | 4 +- ObjetosSim/ucGuia.xaml.cs | 1 + ObjetosSim/ucTransporteCurva.xaml | 16 ++ ObjetosSim/ucTransporteCurva.xaml.cs | 168 ++++++++++++++++++ ObjetosSim/ucTransporteGuias.xaml | 6 +- ObjetosSim/ucTransporteGuias.xaml.cs | 1 + ObjetosSim/ucTransporteTTop.xaml | 4 +- ObjetosSim/ucTransporteTTop.xaml.cs | 1 + ObjetosSim/ucVMmotorSim.xaml | 21 +++ ObjetosSim/ucVMmotorSim.xaml.cs | 132 ++++++++++++++ motor2.png | Bin 0 -> 18909 bytes 22 files changed, 718 insertions(+), 132 deletions(-) create mode 100644 Convertidores/Converters.cs create mode 100644 ObjetosSim/UserControls/CircularSegment.xaml create mode 100644 ObjetosSim/UserControls/CircularSegment.xaml.cs create mode 100644 ObjetosSim/ucTransporteCurva.xaml create mode 100644 ObjetosSim/ucTransporteCurva.xaml.cs create mode 100644 ObjetosSim/ucVMmotorSim.xaml create mode 100644 ObjetosSim/ucVMmotorSim.xaml.cs create mode 100644 motor2.png diff --git a/Convertidores/Converters.cs b/Convertidores/Converters.cs new file mode 100644 index 0000000..e5aa304 --- /dev/null +++ b/Convertidores/Converters.cs @@ -0,0 +1,130 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Data; +using System.Windows; + +namespace CtrEditor.Convertidores +{ + public class PixelToMeter + { + // Instancia privada estática, parte del patrón Singleton + private static PixelToMeter? _instance; + public UnitConverter calc = new UnitConverter(0.01f); + + // Propiedad pública estática para acceder a la instancia + public static PixelToMeter Instance + { + get + { + if (_instance == null) + { + _instance = new PixelToMeter(); + } + return _instance; + } + } + } + + public class MeterToPixelConverter : IValueConverter + { + 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; + + 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 (parameter != null) + if (parameter.ToString() == "0.5") factor = 0.5f; + else if (parameter.ToString() == "-0.5") factor = -0.5f; + + return PixelToMeter.Instance.calc.PixelsToMeters(pixels) * factor; + } + } + + public class DistanceToMarginConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value is double distance) + { + return new Thickness(0, 0, 0, PixelToMeter.Instance.calc.MetersToPixels((float)distance)); // Ajustar Bottom a 'distance' + } + return new Thickness(); + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotSupportedException("ConvertBack is not supported."); + } + } + + public class FloatToFormattedStringConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value is float 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 && float.TryParse(stringValue, NumberStyles.Float, culture, out float 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 + public float Scale { get; private set; } + + + public UnitConverter(float scale) + { + if (scale <= 0) + throw new ArgumentException("Scale must be greater than zero."); + + Scale = scale; + } + + // Convierte una distancia en metros a píxeles + public float MetersToPixels(float meters) + { + return meters / Scale; + } + + // Convierte una distancia en píxeles a metros + public float PixelsToMeters(float pixels) + { + return pixels * Scale; + } + + // Configurar o ajustar la escala + public void SetScale(float newScale) + { + if (newScale <= 0) + throw new ArgumentException("Scale must be greater than zero."); + + Scale = newScale; + } + } +} diff --git a/CtrEditor.csproj b/CtrEditor.csproj index 6d341c5..17f0cd1 100644 --- a/CtrEditor.csproj +++ b/CtrEditor.csproj @@ -8,6 +8,10 @@ true + + + + @@ -18,7 +22,12 @@ C:\Program Files (x86)\Common Files\Siemens\PLCSIMADV\API\6.0\Siemens.Simatic.Simulation.Runtime.Api.x64.dll + True + + + + diff --git a/MainViewModel.cs b/MainViewModel.cs index 0c70a4f..e74a5f0 100644 --- a/MainViewModel.cs +++ b/MainViewModel.cs @@ -23,6 +23,7 @@ using Newtonsoft.Json; using System.Windows.Data; using System.Windows; using static System.Resources.ResXFileRef; +using CtrEditor.Convertidores; namespace CtrEditor { diff --git a/MainWindow.xaml b/MainWindow.xaml index 08d20ac..f3f631c 100644 --- a/MainWindow.xaml +++ b/MainWindow.xaml @@ -4,9 +4,14 @@ 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:convert="clr-namespace:CtrEditor.Convertidores" Height="900" Width="1600" ResizeMode="CanResize" Title="{Binding directorioTrabajo}"> + + + + @@ -69,19 +74,39 @@ - - + + + + + + + + - - - + Grid.Row="0" + Margin="5" + ItemsSource="{Binding ObjetosSimulables}" + DisplayMemberPath="Nombre" + SelectedItem="{Binding SelectedItemOsList, Mode=TwoWay}" + SelectionChanged="ListaOs_SelectionChanged"/> + + + + + + + + + + + diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs index fe2acc2..322df05 100644 --- a/MainWindow.xaml.cs +++ b/MainWindow.xaml.cs @@ -1,4 +1,5 @@ -using System.Text; +using System.Globalization; +using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; @@ -10,6 +11,7 @@ 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; using Binding = System.Windows.Data.Binding; @@ -18,6 +20,7 @@ using MouseEventArgs = System.Windows.Input.MouseEventArgs; using TextBox = System.Windows.Controls.TextBox; using UserControl = System.Windows.Controls.UserControl; + namespace CtrEditor { /// @@ -405,30 +408,46 @@ namespace CtrEditor private void CargarPropiedadesosDatos(osBase selectedObject) { - PanelEdicion.Children.Clear(); // Limpiar el panel existente - - // Reflexión para obtener todas las propiedades del objeto seleccionado + PanelEdicion.Children.Clear(); var properties = selectedObject.GetType().GetProperties(); foreach (var property in properties) { - // Crear un Label y un TextBox para cada propiedad - var label = new Label { Content = property.Name }; - var textBox = new TextBox { Width = 200, Margin = new Thickness(2) }; - textBox.SetBinding(TextBox.TextProperty, new Binding(property.Name) + if (property.PropertyType == typeof(float) || property.PropertyType == typeof(string)) { - Source = selectedObject, - Mode = BindingMode.TwoWay, - UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged - }); + var label = new Label { Content = property.Name }; + var textBox = new TextBox { Width = 200, Margin = new Thickness(0) }; - // Agregar controles al StackPanel - PanelEdicion.Children.Add(label); - PanelEdicion.Children.Add(textBox); + 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); + } } - } + private void MainWindow_Closed(object sender, EventArgs e) { if (DataContext is MainViewModel viewModel) @@ -438,4 +457,21 @@ namespace CtrEditor } } + + public class FloatValidationRule : ValidationRule + { + public override ValidationResult Validate(object value, CultureInfo cultureInfo) + { + // Comprobamos si el valor es nulo o está vacío + if (string.IsNullOrEmpty(value?.ToString())) + return new ValidationResult(false, "El campo no puede estar vacío."); + + // Intentamos convertir el valor a un tipo float + if (float.TryParse(value.ToString(), NumberStyles.Float, cultureInfo, out float result)) + return ValidationResult.ValidResult; // La validación es exitosa + else + return new ValidationResult(false, "Ingrese un número válido."); + } + } + } \ No newline at end of file diff --git a/ObjetosSim/UserControlFactory.cs b/ObjetosSim/UserControlFactory.cs index 1738944..bdf0bf0 100644 --- a/ObjetosSim/UserControlFactory.cs +++ b/ObjetosSim/UserControlFactory.cs @@ -20,6 +20,10 @@ namespace CtrEditor.ObjetosSim return new ucGuia(); if (tipoObjeto == typeof(osTransporteGuias)) return new ucTransporteGuias(); + if (tipoObjeto == typeof(osTransporteCurva)) + return new ucTransporteCurva(); + if (tipoObjeto == typeof(osVMmotorSim )) + return new ucVMmotorSim(); // Puedes añadir más condiciones para otros tipos @@ -36,6 +40,11 @@ namespace CtrEditor.ObjetosSim return new osGuia(); if (tipoObjeto == typeof(osTransporteGuias)) return new osTransporteGuias(); + if (tipoObjeto == typeof(osTransporteCurva)) + return new osTransporteCurva(); + if (tipoObjeto == typeof(osVMmotorSim)) + return new osVMmotorSim(); + // Puedes añadir más condiciones para otros tipos diff --git a/ObjetosSim/UserControls/CircularSegment.xaml b/ObjetosSim/UserControls/CircularSegment.xaml new file mode 100644 index 0000000..3646036 --- /dev/null +++ b/ObjetosSim/UserControls/CircularSegment.xaml @@ -0,0 +1,15 @@ + + + + + + + + + diff --git a/ObjetosSim/UserControls/CircularSegment.xaml.cs b/ObjetosSim/UserControls/CircularSegment.xaml.cs new file mode 100644 index 0000000..9f38c9d --- /dev/null +++ b/ObjetosSim/UserControls/CircularSegment.xaml.cs @@ -0,0 +1,115 @@ +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.UserControls +{ + /// + /// Interaction logic for CircularSegment.xaml + /// + public partial class CircularSegment : UserControl + { + public double Angle + { + get { return (double)GetValue(AngleProperty); } + set { SetValue(AngleProperty, value); } + } + + public static readonly DependencyProperty AngleProperty = + DependencyProperty.Register("Angle", typeof(double), typeof(CircularSegment), new PropertyMetadata(0.0)); + + public double OuterRadius + { + get { return (double)GetValue(OuterRadiusProperty); } + set { SetValue(OuterRadiusProperty, value); } + } + + public static readonly DependencyProperty OuterRadiusProperty = + DependencyProperty.Register("OuterRadius", typeof(double), typeof(CircularSegment), new PropertyMetadata(100.0, OnPropertyChanged)); + + public double InnerRadius + { + get { return (double)GetValue(InnerRadiusProperty); } + set { SetValue(InnerRadiusProperty, value); } + } + + public static readonly DependencyProperty InnerRadiusProperty = + DependencyProperty.Register("InnerRadius", typeof(double), typeof(CircularSegment), new PropertyMetadata(80.0, OnPropertyChanged)); + + public double StartAngle + { + get { return (double)GetValue(StartAngleProperty); } + set { SetValue(StartAngleProperty, value); } + } + + public static readonly DependencyProperty StartAngleProperty = + DependencyProperty.Register("StartAngle", typeof(double), typeof(CircularSegment), new PropertyMetadata(0.0, OnPropertyChanged)); + + public double EndAngle + { + get { return (double)GetValue(EndAngleProperty); } + set { SetValue(EndAngleProperty, value); } + } + + public static readonly DependencyProperty EndAngleProperty = + DependencyProperty.Register("EndAngle", typeof(double), typeof(CircularSegment), new PropertyMetadata(90.0, OnPropertyChanged)); + + public CircularSegment() + { + InitializeComponent(); + } + + private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + (d as CircularSegment)?.DrawSegment(); + } + + private void DrawSegment() + { + if (OuterRadius <= 0 || InnerRadius <= 0 || StartAngle == EndAngle) + return; + + PathGeometry geometry = new PathGeometry(); + PathFigure figure = new PathFigure(); + + // Calcula el centro del Canvas (suponemos que el Canvas es lo suficientemente grande) + Point center = new Point(OuterRadius, OuterRadius); + + // Calcular los puntos de inicio y fin del arco externo + Point startPoint = new Point(center.X + Math.Cos(StartAngle * Math.PI / 180) * OuterRadius, + center.Y + Math.Sin(StartAngle * Math.PI / 180) * OuterRadius); + Point endPoint = new Point(center.X + Math.Cos(EndAngle * Math.PI / 180) * OuterRadius, + center.Y + Math.Sin(EndAngle * Math.PI / 180) * OuterRadius); + + // Crear arco externo + bool largeArc = Math.Abs(EndAngle - StartAngle) > 180.0; + figure.StartPoint = startPoint; + figure.Segments.Add(new ArcSegment(endPoint, new Size(OuterRadius, OuterRadius), 0, largeArc, SweepDirection.Clockwise, true)); + + // Crear línea hacia el arco interno + figure.Segments.Add(new LineSegment(new Point(center.X + Math.Cos(EndAngle * Math.PI / 180) * InnerRadius, + center.Y + Math.Sin(EndAngle * Math.PI / 180) * InnerRadius), true)); + + // Crear arco interno + startPoint = new Point(center.X + Math.Cos(StartAngle * Math.PI / 180) * InnerRadius, + center.Y + Math.Sin(StartAngle * Math.PI / 180) * InnerRadius); + figure.Segments.Add(new ArcSegment(startPoint, new Size(InnerRadius, InnerRadius), 0, largeArc, SweepDirection.Counterclockwise, true)); + + // Cerrar la figura + figure.IsClosed = true; + geometry.Figures.Add(figure); + path.Data = geometry; + } + } +} diff --git a/ObjetosSim/osBase.cs b/ObjetosSim/osBase.cs index f3bc049..7f6c57b 100644 --- a/ObjetosSim/osBase.cs +++ b/ObjetosSim/osBase.cs @@ -11,6 +11,7 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Data; using static System.Runtime.InteropServices.JavaScript.JSType; +using CtrEditor.Convertidores; namespace CtrEditor.ObjetosSim { @@ -90,103 +91,7 @@ namespace CtrEditor.ObjetosSim } } - public class PixelToMeter - { - // Instancia privada estática, parte del patrón Singleton - private static PixelToMeter? _instance; - public UnitConverter calc = new UnitConverter(0.01f); - - // Propiedad pública estática para acceder a la instancia - public static PixelToMeter Instance - { - get - { - if (_instance == null) - { - _instance = new PixelToMeter(); - } - return _instance; - } - } - } - - public class MeterToPixelConverter : IValueConverter - { - 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; - - 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 (parameter != null) - if (parameter.ToString() == "0.5") factor = 0.5f; - else if (parameter.ToString() == "-0.5") factor = -0.5f; - - return PixelToMeter.Instance.calc.PixelsToMeters(pixels) * factor; - } - } - - public class DistanceToMarginConverter : IValueConverter - { - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - if (value is double distance) - { - return new Thickness(0, 0, 0, PixelToMeter.Instance.calc.MetersToPixels((float)distance)); // Ajustar Bottom a 'distance' - } - return new Thickness(); - } - - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - throw new NotSupportedException("ConvertBack is not supported."); - } - } - - public class UnitConverter - { - // La escala representa cuántos metros hay en un píxel - public float Scale { get; private set; } - - - public UnitConverter(float scale) - { - if (scale <= 0) - throw new ArgumentException("Scale must be greater than zero."); - - Scale = scale; - } - - // Convierte una distancia en metros a píxeles - public float MetersToPixels(float meters) - { - return meters / Scale; - } - - // Convierte una distancia en píxeles a metros - public float PixelsToMeters(float pixels) - { - return pixels * Scale; - } - - // Configurar o ajustar la escala - public void SetScale(float newScale) - { - if (newScale <= 0) - throw new ArgumentException("Scale must be greater than zero."); - - Scale = newScale; - } - } + } diff --git a/ObjetosSim/ucBotella.xaml b/ObjetosSim/ucBotella.xaml index eab4021..945e485 100644 --- a/ObjetosSim/ucBotella.xaml +++ b/ObjetosSim/ucBotella.xaml @@ -1,9 +1,9 @@  + xmlns:convert="clr-namespace:CtrEditor.Convertidores"> - + + xmlns:convert="clr-namespace:CtrEditor.Convertidores"> - + diff --git a/ObjetosSim/ucGuia.xaml.cs b/ObjetosSim/ucGuia.xaml.cs index 144892c..271e3f1 100644 --- a/ObjetosSim/ucGuia.xaml.cs +++ b/ObjetosSim/ucGuia.xaml.cs @@ -12,6 +12,7 @@ using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; +using CtrEditor.Convertidores; namespace CtrEditor.ObjetosSim { diff --git a/ObjetosSim/ucTransporteCurva.xaml b/ObjetosSim/ucTransporteCurva.xaml new file mode 100644 index 0000000..9f39d9d --- /dev/null +++ b/ObjetosSim/ucTransporteCurva.xaml @@ -0,0 +1,16 @@ + + + + + + + + diff --git a/ObjetosSim/ucTransporteCurva.xaml.cs b/ObjetosSim/ucTransporteCurva.xaml.cs new file mode 100644 index 0000000..396bfc9 --- /dev/null +++ b/ObjetosSim/ucTransporteCurva.xaml.cs @@ -0,0 +1,168 @@ +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 +{ + /// + /// Interaction logic for ucTransporteCurva.xaml + /// + 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; + } + + + } + +} diff --git a/ObjetosSim/ucTransporteGuias.xaml b/ObjetosSim/ucTransporteGuias.xaml index 02a5d3e..a9ba9ff 100644 --- a/ObjetosSim/ucTransporteGuias.xaml +++ b/ObjetosSim/ucTransporteGuias.xaml @@ -3,12 +3,12 @@ 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" + xmlns:convert="clr-namespace:CtrEditor.Convertidores" mc:Ignorable="d"> - - + + diff --git a/ObjetosSim/ucTransporteGuias.xaml.cs b/ObjetosSim/ucTransporteGuias.xaml.cs index a5640d3..254069c 100644 --- a/ObjetosSim/ucTransporteGuias.xaml.cs +++ b/ObjetosSim/ucTransporteGuias.xaml.cs @@ -12,6 +12,7 @@ using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; +using CtrEditor.Convertidores; namespace CtrEditor.ObjetosSim { diff --git a/ObjetosSim/ucTransporteTTop.xaml b/ObjetosSim/ucTransporteTTop.xaml index bc0b4d2..f6352bc 100644 --- a/ObjetosSim/ucTransporteTTop.xaml +++ b/ObjetosSim/ucTransporteTTop.xaml @@ -4,10 +4,10 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d" - xmlns:local="clr-namespace:CtrEditor.ObjetosSim"> + xmlns:convert="clr-namespace:CtrEditor.Convertidores"> - + diff --git a/ObjetosSim/ucTransporteTTop.xaml.cs b/ObjetosSim/ucTransporteTTop.xaml.cs index 0c5647b..0ba6a65 100644 --- a/ObjetosSim/ucTransporteTTop.xaml.cs +++ b/ObjetosSim/ucTransporteTTop.xaml.cs @@ -15,6 +15,7 @@ using System.Windows.Shapes; using static System.Runtime.InteropServices.JavaScript.JSType; using System.Numerics; using System.Windows.Markup; +using CtrEditor.Convertidores; namespace CtrEditor.ObjetosSim { diff --git a/ObjetosSim/ucVMmotorSim.xaml b/ObjetosSim/ucVMmotorSim.xaml new file mode 100644 index 0000000..c555554 --- /dev/null +++ b/ObjetosSim/ucVMmotorSim.xaml @@ -0,0 +1,21 @@ + + + + + + + + + + + diff --git a/ObjetosSim/ucVMmotorSim.xaml.cs b/ObjetosSim/ucVMmotorSim.xaml.cs new file mode 100644 index 0000000..6304a66 --- /dev/null +++ b/ObjetosSim/ucVMmotorSim.xaml.cs @@ -0,0 +1,132 @@ +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 +{ + /// + /// Interaction logic for ucVMmotorSim.xaml + /// + 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; + } + } +} diff --git a/motor2.png b/motor2.png new file mode 100644 index 0000000000000000000000000000000000000000..eb74dcd3c690751a935f67b3680bb9d14743b686 GIT binary patch literal 18909 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

1<{AqhMk+tDCYbwbxg|x?i3b1>jv&f}KWJ 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