From 6da98e621b7fb91aa1f76e6f4e3fe355f75ca9bc Mon Sep 17 00:00:00 2001 From: Miguel Date: Tue, 14 May 2024 17:10:32 +0200 Subject: [PATCH] Funcionando con el Save y el link ente los Motor y los TransportesGuia --- Convertidores/Converters.cs | 38 +++++++++++++++++ MainViewModel.cs | 59 ++++++++++++++++++++++---- MainWindow.xaml | 1 + MainWindow.xaml.cs | 56 +++++++++++++++++++++++++ ObjetosSim/osBase.cs | 9 +++- ObjetosSim/ucBotella.xaml.cs | 2 +- ObjetosSim/ucBoton.xaml | 49 ++++++++-------------- ObjetosSim/ucBoton.xaml.cs | 62 ++++++++++++++++------------ ObjetosSim/ucGuia.xaml.cs | 2 +- ObjetosSim/ucTransporteGuias.xaml.cs | 37 ++++++++++++++++- ObjetosSim/ucTransporteTTop.xaml.cs | 2 +- ObjetosSim/ucVMmotorSim.xaml.cs | 55 +++++++++++++++++++++--- Siemens/PLCControl.xaml.cs | 12 +++--- 13 files changed, 302 insertions(+), 82 deletions(-) diff --git a/Convertidores/Converters.cs b/Convertidores/Converters.cs index 217caf6..b72972d 100644 --- a/Convertidores/Converters.cs +++ b/Convertidores/Converters.cs @@ -6,9 +6,47 @@ using System.Text; using System.Threading.Tasks; using System.Windows.Data; using System.Windows; +using System.Windows.Media; namespace CtrEditor.Convertidores { + 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 diff --git a/MainViewModel.cs b/MainViewModel.cs index cf43230..ddaeae2 100644 --- a/MainViewModel.cs +++ b/MainViewModel.cs @@ -1,4 +1,5 @@ -using System; +using CtrEditor; +using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; @@ -25,6 +26,8 @@ using System.Windows; using static System.Resources.ResXFileRef; using CtrEditor.Convertidores; using CtrEditor.Simulacion; +using System.Diagnostics; +using Newtonsoft.Json.Linq; namespace CtrEditor { @@ -36,6 +39,7 @@ namespace CtrEditor public ObservableCollection ListaOsBase { get; } = new ObservableCollection(); private ObservableCollection _objetosSimulables = new ObservableCollection(); public PLCViewModel _plcViewModelData; + public Stopwatch stopwatch; public SimulationManagerFP simulationManager = new SimulationManagerFP(); @@ -69,7 +73,9 @@ namespace CtrEditor _timerSimulacion.Tick += OnTickSimulacion; StartSimulationCommand = new RelayCommand(StartSimulation); - StopSimulationCommand = new RelayCommand(StopSimulation); + StopSimulationCommand = new RelayCommand(StopSimulation); + + stopwatch = new Stopwatch(); } public void LoadInitialData() @@ -155,11 +161,21 @@ namespace CtrEditor private void OnRefreshEvent(object sender, EventArgs e) { - foreach (var objetoSimulable in ObjetosSimulables) + if (_plcViewModelData.IsConnected) { - if (_plcViewModelData.IsConnected) - objetoSimulable.UpdatePLC(_plcViewModelData.PLCInterface); - } + // Detener el cronómetro y obtener el tiempo transcurrido en milisegundos + stopwatch.Stop(); + float elapsedMilliseconds = (float)stopwatch.Elapsed.TotalMilliseconds; + + // Reiniciar el cronómetro para la próxima medición + stopwatch.Restart(); + + foreach (var objetoSimulable in ObjetosSimulables) + objetoSimulable.UpdatePLC(_plcViewModelData.PLCInterface, (int) elapsedMilliseconds); + } else + stopwatch.Stop(); + + } //protected virtual void OnTickSimulacion(TickSimulacionEventArgs e) @@ -260,10 +276,19 @@ namespace CtrEditor } } + public void Save() + { + SaveStateObjetosSimulables(); + ImageSelected?.Invoke(this, datosDeTrabajo.Imagenes[_selectedImage]); // Dispara el evento con la nueva ruta de imagen + LoadStateObjetosSimulables(); + } + public void SaveStateObjetosSimulables() { if (_selectedImage != null) { + StopSimulation(); + PLCViewModel.Disconnect(); var settings = new JsonSerializerSettings { Formatting = Formatting.Indented, @@ -274,6 +299,7 @@ namespace CtrEditor { obj.VisualRepresentation = null; obj.simulationManager = null; + obj._mainViewModel = null; } // Crear un objeto que incluya tanto los ObjetosSimulables como el UnitConverter var dataToSerialize = new SimulationData @@ -292,6 +318,8 @@ namespace CtrEditor { try { + StopSimulation(); + PLCViewModel.Disconnect(); ObjetosSimulables.Clear(); simulationManager.Clear(); if (_selectedImage != null) @@ -321,6 +349,9 @@ namespace CtrEditor PixelToMeter.Instance.calc = simulationData.UnitConverter; + // Re-register to the events + _plcViewModelData.RefreshEvent += OnRefreshEvent; + // Recorrer la colección de objetos simulables foreach (var objetoSimulable in ObjetosSimulables) CrearUsercontrol(objetoSimulable); @@ -343,6 +374,7 @@ namespace CtrEditor { // Asignar los datos al UserControl UserControlFactory.AssignDatos(userControl, osObjeto, simulationManager); + osObjeto._mainViewModel = this; OnUserControlSelected?.Invoke(userControl); @@ -353,7 +385,6 @@ namespace CtrEditor - // Implementación de INotifyPropertyChanged... public event PropertyChangedEventHandler PropertyChanged; @@ -362,7 +393,21 @@ namespace CtrEditor PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } + private RelayCommand saveCommand; + public ICommand SaveCommand => saveCommand ??= new RelayCommand(Save); + private void Save(object commandParameter) + { + } + + private RelayCommand exitCommand; + public ICommand ExitCommand => exitCommand ??= new RelayCommand(Exit); + + private void Exit() + { + Save(); + Application.Current.Shutdown(); + } } public class SimulationData { diff --git a/MainWindow.xaml b/MainWindow.xaml index ed0b2b1..7cdae76 100644 --- a/MainWindow.xaml +++ b/MainWindow.xaml @@ -11,6 +11,7 @@ + diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs index f42a830..bcd5029 100644 --- a/MainWindow.xaml.cs +++ b/MainWindow.xaml.cs @@ -21,6 +21,7 @@ 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 OpenCvSharp; @@ -418,6 +419,11 @@ namespace CtrEditor foreach (var property in properties) { + if (Attribute.IsDefined(property, typeof(HiddenAttribute))) + { + continue; + } + var grid = new Grid(); grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto }); grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) }); @@ -480,11 +486,61 @@ namespace CtrEditor grid.Children.Add(label); grid.Children.Add(checkBox); } + else if (property.PropertyType == typeof(Brush)) + { + var listBox = new ListBox + { + ItemsSource = new List { "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(label, 0); + Grid.SetColumn(listBox, 1); + + grid.Children.Add(label); + grid.Children.Add(listBox); + } PanelEdicion.Children.Add(grid); } } + private void MainWindow_Closed(object sender, EventArgs e) { if (DataContext is MainViewModel viewModel) diff --git a/ObjetosSim/osBase.cs b/ObjetosSim/osBase.cs index bd35f5c..30ecf0a 100644 --- a/ObjetosSim/osBase.cs +++ b/ObjetosSim/osBase.cs @@ -54,9 +54,12 @@ namespace CtrEditor.ObjetosSim public abstract void UpdateControl(); public abstract void UpdateGeometryStart(); public abstract void UpdateGeometryStep(); - public abstract void UpdatePLC(PLCModel plc); + public abstract void UpdatePLC(PLCModel plc, int elapsedMilliseconds); public abstract void ucLoaded(); + [JsonIgnore] + public MainViewModel _mainViewModel; + [JsonIgnore] public UserControl? VisualRepresentation { @@ -185,4 +188,8 @@ namespace CtrEditor.ObjetosSim } } + [AttributeUsage(AttributeTargets.Property)] + public class HiddenAttribute : Attribute + { + } } diff --git a/ObjetosSim/ucBotella.xaml.cs b/ObjetosSim/ucBotella.xaml.cs index 97a1482..3674a3e 100644 --- a/ObjetosSim/ucBotella.xaml.cs +++ b/ObjetosSim/ucBotella.xaml.cs @@ -139,7 +139,7 @@ namespace CtrEditor.ObjetosSim ActualizarGeometrias(); } - public override void UpdatePLC(PLCModel plc) { } + public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds) { } public override void UpdateControl() { diff --git a/ObjetosSim/ucBoton.xaml b/ObjetosSim/ucBoton.xaml index 37b753a..7c15b83 100644 --- a/ObjetosSim/ucBoton.xaml +++ b/ObjetosSim/ucBoton.xaml @@ -7,43 +7,30 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:convert="clr-namespace:CtrEditor.Convertidores" mc:Ignorable="d"> - + - - + Background="Gray"/> + + diff --git a/ObjetosSim/ucBoton.xaml.cs b/ObjetosSim/ucBoton.xaml.cs index 2c52eef..dfacf72 100644 --- a/ObjetosSim/ucBoton.xaml.cs +++ b/ObjetosSim/ucBoton.xaml.cs @@ -32,6 +32,8 @@ namespace CtrEditor.ObjetosSim private string _tag; private Brush _color; + private Brush _colorButton; + public Brush Color { get => _color; @@ -42,9 +44,16 @@ namespace CtrEditor.ObjetosSim } } - public ICommand ButtonClickCommand { get; } - public ICommand ButtonDownCommand { get; } - public ICommand ButtonUpCommand { get; } + [Hidden] + public Brush ColorButton + { + get => _colorButton; + set + { + _colorButton = value; + OnPropertyChanged(nameof(ColorButton)); + } + } public override float Left { @@ -81,6 +90,10 @@ namespace CtrEditor.ObjetosSim set { _estado = value; + if (value) + ColorButton = Brushes.LightGreen; + else + ColorButton = Color; OnPropertyChanged(nameof(Estado)); } } @@ -110,20 +123,11 @@ namespace CtrEditor.ObjetosSim } } - private void OnButtonClick() - { - // Handle the click event here - // Example: Change color on click - Color = Brushes.LightGreen; - - } - - private void OnButtonDown() + public void ButtonDownCommand() { Estado = true; } - - private void OnButtonUp() + public void ButtonUpCommand() { Estado = false; } @@ -135,11 +139,6 @@ namespace CtrEditor.ObjetosSim Tag = "M50.0"; // Set initial color Color = Brushes.LightBlue; - - // Initialize the command - ButtonClickCommand = new RelayCommand(OnButtonClick); - ButtonDownCommand = new RelayCommand(OnButtonDown); - ButtonUpCommand = new RelayCommand(OnButtonUp); } public override void UpdateGeometryStart() @@ -150,9 +149,9 @@ namespace CtrEditor.ObjetosSim public override void UpdateGeometryStep() { } - public override void UpdatePLC(PLCModel plc) + public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds) { - plc.EscribirTagBool(Tag, Estado); + plc.EscribirTagBool(_tag, Estado); } public override void UpdateControl() @@ -175,28 +174,37 @@ namespace CtrEditor.ObjetosSim { InitializeComponent(); this.Loaded += OnLoaded; + this.DataContextChanged += OnDataContextChanged; } + private void OnLoaded(object sender, RoutedEventArgs e) { Datos?.ucLoaded(); } - private void Button_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) + + 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.Execute(null); - Mouse.Capture((UIElement)sender); + osBotonData.ButtonDownCommand(); + e.Handled = true; // Evita que el evento se propague } } - private void Button_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) + private void Ellipse_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { if (Datos is osBoton osBotonData) { - osBotonData.ButtonUpCommand.Execute(null); - Mouse.Capture(null); // Release mouse capture + 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) { diff --git a/ObjetosSim/ucGuia.xaml.cs b/ObjetosSim/ucGuia.xaml.cs index 347d525..37089d3 100644 --- a/ObjetosSim/ucGuia.xaml.cs +++ b/ObjetosSim/ucGuia.xaml.cs @@ -121,7 +121,7 @@ namespace CtrEditor.ObjetosSim public override void UpdateGeometryStep() { } - public override void UpdatePLC(PLCModel plc) { } + public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds) { } public override void ucLoaded() { // El UserControl ya se ha cargado y podemos obtener las coordenadas para diff --git a/ObjetosSim/ucTransporteGuias.xaml.cs b/ObjetosSim/ucTransporteGuias.xaml.cs index c78d139..95552cb 100644 --- a/ObjetosSim/ucTransporteGuias.xaml.cs +++ b/ObjetosSim/ucTransporteGuias.xaml.cs @@ -15,6 +15,7 @@ using System.Windows.Shapes; using CtrEditor.Convertidores; using CtrEditor.Siemens; using CtrEditor.Simulacion; +using static System.Runtime.InteropServices.JavaScript.JSType; namespace CtrEditor.ObjetosSim { @@ -39,12 +40,28 @@ namespace CtrEditor.ObjetosSim private float _top; private float _angulo; private float _velocidadActual; + private osBase _osMotor = null; + private string _motor; private simRectangle? TransporteCentral; private simLine? Guia_Superior; private simLine? Guia_Inferior; + + public string Motor + { + get => _motor; + set + { + if (_motor != value) + { + _motor = value; + OnPropertyChanged(nameof(Motor)); + } + } + } + public override float Left { get => _left; @@ -179,7 +196,24 @@ namespace CtrEditor.ObjetosSim public override void UpdateControl() { } - public override void UpdatePLC(PLCModel plc) { } + public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds) + { + if (_osMotor != null) + { + if (_osMotor is osVMmotorSim motor) + VelocidadActual = motor.Velocidad; + } else + { + if (Motor.Length > 0) + if (_mainViewModel != null) + foreach (var objetoSimulable in _mainViewModel.ObjetosSimulables) + if (objetoSimulable.Nombre == _motor) + { + _osMotor = objetoSimulable; + break; + } + } + } public override void ucLoaded() { // El UserControl ya se ha cargado y podemos obtener las coordenadas para @@ -196,6 +230,7 @@ namespace CtrEditor.ObjetosSim Guia_Superior = AddLine(simulationManager, uc.GuiaSuperior); Guia_Inferior = AddLine(simulationManager, uc.GuiaInferior); } + Motor = Motor; // Forzar la busqueda } diff --git a/ObjetosSim/ucTransporteTTop.xaml.cs b/ObjetosSim/ucTransporteTTop.xaml.cs index caf2344..0db5381 100644 --- a/ObjetosSim/ucTransporteTTop.xaml.cs +++ b/ObjetosSim/ucTransporteTTop.xaml.cs @@ -132,7 +132,7 @@ namespace CtrEditor.ObjetosSim public override void UpdateGeometryStep() { } - public override void UpdatePLC(PLCModel plc) { } + public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds) { } public override void UpdateControl() diff --git a/ObjetosSim/ucVMmotorSim.xaml.cs b/ObjetosSim/ucVMmotorSim.xaml.cs index 88f3b86..4d8e4c6 100644 --- a/ObjetosSim/ucVMmotorSim.xaml.cs +++ b/ObjetosSim/ucVMmotorSim.xaml.cs @@ -51,6 +51,9 @@ namespace CtrEditor.ObjetosSim private float _ratio; private float _velocidad; private bool _encendido; + private float _rampaSegundos; + private float _maxHz; + public VMSimMotor motState = new VMSimMotor(); @@ -64,6 +67,29 @@ namespace CtrEditor.ObjetosSim } } + public float MaxRatedHz + { + get => _maxHz; + set + { + _maxHz = value; + OnPropertyChanged(nameof(Tamano)); + } + } + + public float TiempoRampa + { + get => _rampaSegundos; + set + { + if (value < 0.1f) + value = 0.1f; + _rampaSegundos = value; + OnPropertyChanged(nameof(Tamano)); + } + } + + public bool Encendido { get => _encendido; @@ -139,6 +165,8 @@ namespace CtrEditor.ObjetosSim { Tamano = 0.30f; PLC_NumeroMotor = 31; + MaxRatedHz = 100; + TiempoRampa = 3; } public override void UpdateGeometryStart() @@ -149,7 +177,7 @@ namespace CtrEditor.ObjetosSim public override void UpdateGeometryStep() { } - public override void UpdatePLC(PLCModel plc) { + public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds) { var index = 0; switch (PLC_NumeroMotor) { @@ -162,6 +190,8 @@ namespace CtrEditor.ObjetosSim motState.OUT_Reversal = plc.LeerTagBool($"\"DB MotorSimulate\".Motors[{index}].OUT.\"Reversal Direction\""); motState.OUT_OUT_VFD_REQ_Speed_Hz = (float)plc.LeerTagInt16($"\"DB MotorSimulate\".Motors[{index}].OUT.OUT_VFD_REQ_Speed_Hz"); + + if (Encendido) { motState._STATUS_VFD_Ready = true; @@ -169,19 +199,19 @@ namespace CtrEditor.ObjetosSim motState.STATUS_VFD_Trip = false; motState.STATUS_VFD_Warning = false; motState.STATUS_VFD_Coasting = false; - if (motState.STATUS_VFD_ACT_Speed_Hz < motState.OUT_OUT_VFD_REQ_Speed_Hz) - motState.STATUS_VFD_ACT_Speed_Hz += motState.OUT_OUT_VFD_REQ_Speed_Hz / 375 ; // Simulate Ramp - } else + } + else { motState._STATUS_VFD_Ready = false; motState.Motor_Running = false; motState.STATUS_VFD_Trip = true; motState.STATUS_VFD_Warning = false; motState.STATUS_VFD_Coasting = false; - if (motState.STATUS_VFD_ACT_Speed_Hz > 0) - motState.STATUS_VFD_ACT_Speed_Hz -= 1; // Simulate Ramp } + // Calculamos la velocidad + motState.STATUS_VFD_ACT_Speed_Hz += CalcSpeedRamp(motState.STATUS_VFD_ACT_Speed_Hz, motState.OUT_OUT_VFD_REQ_Speed_Hz, elapsedMilliseconds); + plc.EscribirTagBool($"\"DB MotorSimulate\".Motors[{index}].STATUS_VFD_Ready", motState._STATUS_VFD_Ready); plc.EscribirTagBool($"\"DB MotorSimulate\".Motors[{index}].Motor_Running", motState.Motor_Running); plc.EscribirTagBool($"\"DB MotorSimulate\".Motors[{index}].STATUS_VFD_Trip", motState.STATUS_VFD_Trip); @@ -193,6 +223,19 @@ namespace CtrEditor.ObjetosSim Velocidad = motState.STATUS_VFD_ACT_Speed_Hz/10; } + private float CalcSpeedRamp(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 override void UpdateControl() { diff --git a/Siemens/PLCControl.xaml.cs b/Siemens/PLCControl.xaml.cs index 9d8350f..6634f0b 100644 --- a/Siemens/PLCControl.xaml.cs +++ b/Siemens/PLCControl.xaml.cs @@ -42,7 +42,7 @@ namespace CtrEditor.Siemens { IsConnected = false; PLCInterface = new PLCModel(); - _timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(1000) }; + _timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(10) }; _timer.Tick += (s, e) => Refresh(); ConnectCommand = new RelayCommand(Connect, () => true); @@ -102,7 +102,7 @@ namespace CtrEditor.Siemens } } - private void Connect() + public void Connect() { // Implementa la conexión utilizando PLCModel PLCInterface.Instance = SimulationRuntimeManager.CreateInterface(Name); @@ -123,7 +123,7 @@ namespace CtrEditor.Siemens PLCInterface.UpdateTagList(); } - private void Disconnect() + public void Disconnect() { IsConnected = false; _timer.Stop(); @@ -137,10 +137,10 @@ namespace CtrEditor.Siemens { CpuTime = PLCInterface.LeerTagInt16("\"DB HMI\".CPU_Scan_Time")?.ToString() ?? "N/A"; LastError = PLCInterface.LastError; - } - // Disparar el evento RefreshEvent - RefreshEvent?.Invoke(this, EventArgs.Empty); + // Disparar el evento RefreshEvent + RefreshEvent?.Invoke(this, EventArgs.Empty); + } } private void OnPropertyChanged([CallerMemberName] string propertyName = null)