From 664d325de81c5e79dca11a0be43691fbe8e03078 Mon Sep 17 00:00:00 2001 From: Miguel Date: Sat, 18 May 2024 10:53:04 +0200 Subject: [PATCH] Limpiando el codigo de MainWindow y MainViewModel --- CtrEditor.csproj | 8 +- MainViewModel.cs | 321 +++++++++++++----------- MainWindow.xaml.cs | 48 ++-- ObjetosSim/UserControlFactory.cs | 4 + ObjetosSim/osBase.cs | 27 +- ObjetosSim/ucBasicExample.xaml | 10 +- ObjetosSim/ucBasicExample.xaml.cs | 4 +- ObjetosSim/ucFiller.xaml | 24 ++ ObjetosSim/ucFiller.xaml.cs | 242 ++++++++++++++++++ ObjetosSim/ucTanque.xaml.cs | 2 - ObjetosSim/ucVMmotorSim.xaml | 2 +- ObjetosSim/ucVMmotorSim.xaml.cs | 182 ++++++++------ imagenes/filler.png | Bin 0 -> 16708 bytes imagenes/{motor2.png => motorNegro.png} | Bin imagenes/motorVerde.png | Bin 0 -> 19454 bytes 15 files changed, 619 insertions(+), 255 deletions(-) create mode 100644 ObjetosSim/ucFiller.xaml create mode 100644 ObjetosSim/ucFiller.xaml.cs create mode 100644 imagenes/filler.png rename imagenes/{motor2.png => motorNegro.png} (100%) create mode 100644 imagenes/motorVerde.png diff --git a/CtrEditor.csproj b/CtrEditor.csproj index 9a88f2c..9b3f0af 100644 --- a/CtrEditor.csproj +++ b/CtrEditor.csproj @@ -20,6 +20,9 @@ + + + @@ -42,7 +45,6 @@ - @@ -58,7 +60,9 @@ - + + + diff --git a/MainViewModel.cs b/MainViewModel.cs index 61d074b..7ad26c2 100644 --- a/MainViewModel.cs +++ b/MainViewModel.cs @@ -37,14 +37,9 @@ namespace CtrEditor public DatosDeTrabajo datosDeTrabajo { get; } public ObservableCollection listaImagenes { get; private set; } // Publicación de las claves del diccionario public ObservableCollection ListaOsBase { get; } = new ObservableCollection(); - private ObservableCollection _objetosSimulables = new ObservableCollection(); - public PLCViewModel plcViewModelData; public Stopwatch stopwatch_PLCRefresh; public Stopwatch stopwatch_SimRefresh; - private bool isSimulationRunning; - private bool isConnected; - public SimulationManagerFP simulationManager = new SimulationManagerFP(); private readonly DispatcherTimer _timerSimulacion; @@ -59,11 +54,153 @@ namespace CtrEditor public ICommand TBConnectPLCCommand { get; } public ICommand TBDisconnectPLCCommand { get; } + public ICommand OpenWorkDirectoryCommand { get; } + // Evento que se dispara cuando se selecciona una nueva imagen public event EventHandler ImageSelected; public event EventHandler TickSimulacion; public event Action OnUserControlSelected; + + private bool isSimulationRunning; + private bool isConnected; + public PLCViewModel plcViewModelData; + private osBase _selectedItemOsList; + private string _selectedImage = null; + private ObservableCollection _objetosSimulables = new ObservableCollection(); + private float _left; + private float _top; + + public float CanvasLeft + { + get => _left; + set + { + _left = value; + OnPropertyChanged(nameof(CanvasLeft)); + } + } + public float CanvasTop + { + get => _top; + set + { + _top = value; + OnPropertyChanged(nameof(CanvasTop)); + } + } + + public bool IsSimulationRunning + { + get => isSimulationRunning; + set + { + isSimulationRunning = value; + OnPropertyChanged(nameof(IsSimulationRunning)); + CommandManager.InvalidateRequerySuggested(); // Notificar que el estado de los comandos ha cambiado + } + } + + public bool IsConnected + { + get => isConnected; + set + { + isConnected = value; + OnPropertyChanged(nameof(IsConnected)); + CommandManager.InvalidateRequerySuggested(); + } + } + + public string directorioTrabajo + { + get => EstadoPersistente.Instance.directorio; + set + { + if (value != null) + { + EstadoPersistente.Instance.directorio = value; // Actualizar el estado persistente + EstadoPersistente.Instance.GuardarEstado(); // Guardar el estado actualizado + datosDeTrabajo.CargarImagenes(); + listaImagenes = new ObservableCollection(datosDeTrabajo.Imagenes.Keys); // Actualizar claves + SelectedImage = null; + 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 + } + } + } + + public PLCViewModel PLCViewModel + { + get { return plcViewModelData; } + set + { + plcViewModelData = value; + OnPropertyChanged(nameof(PLCViewModel)); + } + } + + public string SelectedImage + { + get => _selectedImage; + set + { + if (_selectedImage != value && value != null) + { + 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)); + } + } + + public osBase SelectedItemOsList + { + get => _selectedItemOsList; + set + { + if (_selectedItemOsList != value) + { + _selectedItemOsList = value; + OnPropertyChanged(nameof(SelectedItemOsList)); + } + } + } + + private TipoSimulable _selectedItem = null; + public TipoSimulable SelectedItem + { + get => _selectedItem; + set + { + if (_selectedItem != value) + { + _selectedItem = value; + OnPropertyChanged(nameof(SelectedItem)); // Notificar que la propiedad ha cambiado + } + } + } + + public ObservableCollection ObjetosSimulables + { + get => _objetosSimulables; + set + { + if (_objetosSimulables != value) + { + _objetosSimulables = value; + OnPropertyChanged(nameof(ObjetosSimulables)); + } + } + } + + public MainViewModel() { OpenWorkDirectoryCommand = new RelayCommand(OpenWorkDirectory); @@ -101,33 +238,44 @@ namespace CtrEditor } - private TipoSimulable _selectedItem = null; - public TipoSimulable SelectedItem + + public void CrearUserControl(Type tipoSimulable) { - get => _selectedItem; - set + // Crear una nueva instancia del osBase correspondiente + osBase? newosBase = UserControlFactory.GetInstanceForType(tipoSimulable); + if (newosBase != null) { - if (_selectedItem != value) - { - _selectedItem = value; - OnPropertyChanged(nameof(SelectedItem)); // Notificar que la propiedad ha cambiado - } + if (CrearUsercontrol(newosBase)) + // Añadir el nuevo osBase a la colección de objetos simulables + ObjetosSimulables.Add(newosBase); } } + private bool CrearUsercontrol(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; + + OnUserControlSelected?.Invoke(userControl); + + return true; + } + return false; + } 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); - } + CrearUserControl(tipoSimulable.Tipo); } } @@ -144,28 +292,6 @@ namespace CtrEditor } } - public bool IsSimulationRunning - { - get => isSimulationRunning; - set - { - isSimulationRunning = value; - OnPropertyChanged(nameof(IsSimulationRunning)); - CommandManager.InvalidateRequerySuggested(); // Notificar que el estado de los comandos ha cambiado - } - } - - public bool IsConnected - { - get => isConnected; - set - { - isConnected = value; - OnPropertyChanged(nameof(IsConnected)); - CommandManager.InvalidateRequerySuggested(); - } - } - private void StartSimulation() { foreach (var objetoSimulable in ObjetosSimulables) @@ -238,77 +364,6 @@ namespace CtrEditor } - //protected virtual void OnTickSimulacion(TickSimulacionEventArgs e) - //{ - // TickSimulacion?.Invoke(this, e); - //} - - public string directorioTrabajo - { - get => EstadoPersistente.Instance.directorio; - set - { - if (value != null) - { - EstadoPersistente.Instance.directorio = value; // Actualizar el estado persistente - EstadoPersistente.Instance.GuardarEstado(); // Guardar el estado actualizado - datosDeTrabajo.CargarImagenes(); - listaImagenes = new ObservableCollection(datosDeTrabajo.Imagenes.Keys); // Actualizar claves - SelectedImage = null; - 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 - } - } - } - - - public PLCViewModel PLCViewModel - { - get { return plcViewModelData; } - set - { - plcViewModelData = value; - OnPropertyChanged(nameof(PLCViewModel)); - } - } - - private string _selectedImage = null; - public string SelectedImage - { - get => _selectedImage; - set - { - if (_selectedImage != value && value != null) - { - 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)); - } - } - - private osBase _selectedItemOsList; - public osBase SelectedItemOsList - { - get => _selectedItemOsList; - set - { - if (_selectedItemOsList != value) - { - _selectedItemOsList = value; - OnPropertyChanged(nameof(SelectedItemOsList)); - } - } - } - - public ICommand OpenWorkDirectoryCommand { get; } - private void OpenWorkDirectory() { var dialog = new VistaFolderBrowserDialog(); @@ -322,20 +377,6 @@ namespace CtrEditor // Lista de osBase // - - public ObservableCollection ObjetosSimulables - { - get => _objetosSimulables; - set - { - if (_objetosSimulables != value) - { - _objetosSimulables = value; - OnPropertyChanged(nameof(ObjetosSimulables)); - } - } - } - public void Save() { SaveStateObjetosSimulables(); @@ -423,28 +464,6 @@ namespace CtrEditor } - private bool CrearUsercontrol(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; - - OnUserControlSelected?.Invoke(userControl); - - return true; - } - return false; - } - - - // Implementación de INotifyPropertyChanged... public event PropertyChangedEventHandler PropertyChanged; diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs index bcd5029..2709948 100644 --- a/MainWindow.xaml.cs +++ b/MainWindow.xaml.cs @@ -22,7 +22,6 @@ 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; namespace CtrEditor @@ -70,6 +69,33 @@ namespace CtrEditor } } + private (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); + } + + private (float X, float Y) ObtenerCentroCanvasMeters() + { + var c = ObtenerCentroCanvasPixels(); + return (PixelToMeter.Instance.calc.PixelsToMeters(c.X), PixelToMeter.Instance.calc.PixelsToMeters(c.Y)); + } + private void AgregarUserControl(UserControl userControl) { if (userControl is IDataContainer dataContainer) @@ -78,26 +104,12 @@ namespace CtrEditor if (!NuevoOS.Inicializado) // Aun no fue inicializado { Random rnd = new Random(); - // 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; + var centro = ObtenerCentroCanvasPixels(); // 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); + double leftPixels = centro.X - (userControl.ActualWidth / 2); + double topPixels = centro.Y - (userControl.ActualHeight / 2); // Establece la posición del UserControl NuevoOS.Left = PixelToMeter.Instance.calc.PixelsToMeters((float)leftPixels + (float)(rnd.NextDouble() - 0.5) ); diff --git a/ObjetosSim/UserControlFactory.cs b/ObjetosSim/UserControlFactory.cs index e281b25..f792f63 100644 --- a/ObjetosSim/UserControlFactory.cs +++ b/ObjetosSim/UserControlFactory.cs @@ -31,6 +31,8 @@ namespace CtrEditor.ObjetosSim return new ucTanque(); if (tipoObjeto == typeof(osSensTemperatura)) return new ucSensTemperatura(); + if (tipoObjeto == typeof(osFiller)) + return new ucFiller(); // Puedes añadir más condiciones para otros tipos @@ -58,6 +60,8 @@ namespace CtrEditor.ObjetosSim return new osTanque(); if (tipoObjeto == typeof(osSensTemperatura)) return new osSensTemperatura(); + if (tipoObjeto == typeof(osFiller)) + return new osFiller(); // Puedes añadir más condiciones para otros tipos diff --git a/ObjetosSim/osBase.cs b/ObjetosSim/osBase.cs index 624d1da..607e349 100644 --- a/ObjetosSim/osBase.cs +++ b/ObjetosSim/osBase.cs @@ -18,6 +18,7 @@ using System.Windows.Media; using Microsoft.Xna.Framework; using FarseerPhysics.Dynamics; using Siemens.Simatic.Simulation.Runtime; +using System.Windows.Media.Imaging; namespace CtrEditor.ObjetosSim { @@ -33,6 +34,7 @@ namespace CtrEditor.ObjetosSim public interface IDataContainer { + [JsonIgnore] osBase? Datos { get; set; } void Resize(float width, float height); void Move(float Left, float Top); @@ -47,7 +49,8 @@ namespace CtrEditor.ObjetosSim public abstract float Top { get; set; } public bool Inicializado = false; - + + [JsonIgnore] protected UserControl? _visualRepresentation = null; public abstract string Nombre { get; set; } @@ -241,6 +244,28 @@ namespace CtrEditor.ObjetosSim 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(List list) where T : class + { + if (list.Count > 0) + { + return list[list.Count - 1]; + } + else + { + return null; + } + } + + public event PropertyChangedEventHandler PropertyChanged; diff --git a/ObjetosSim/ucBasicExample.xaml b/ObjetosSim/ucBasicExample.xaml index 4da1c1f..4da0235 100644 --- a/ObjetosSim/ucBasicExample.xaml +++ b/ObjetosSim/ucBasicExample.xaml @@ -13,8 +13,12 @@ + Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}" + Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}}" + Stretch="Uniform"> + + + + diff --git a/ObjetosSim/ucBasicExample.xaml.cs b/ObjetosSim/ucBasicExample.xaml.cs index ff5445e..7448c26 100644 --- a/ObjetosSim/ucBasicExample.xaml.cs +++ b/ObjetosSim/ucBasicExample.xaml.cs @@ -27,9 +27,9 @@ namespace CtrEditor.ObjetosSim private string _nombre = "Ejemplo"; private float _ancho; private float _alto; + private float _angulo; private float _left; private float _top; - private float _angulo; public override float Left { @@ -112,7 +112,7 @@ namespace CtrEditor.ObjetosSim { } - public override void UpdateControl() + public override void UpdateControl(int elapsedMilliseconds) { } diff --git a/ObjetosSim/ucFiller.xaml b/ObjetosSim/ucFiller.xaml new file mode 100644 index 0000000..f39e039 --- /dev/null +++ b/ObjetosSim/ucFiller.xaml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + diff --git a/ObjetosSim/ucFiller.xaml.cs b/ObjetosSim/ucFiller.xaml.cs new file mode 100644 index 0000000..8c0ceb9 --- /dev/null +++ b/ObjetosSim/ucFiller.xaml.cs @@ -0,0 +1,242 @@ +using CtrEditor.Convertidores; +using CtrEditor.Siemens; +using CtrEditor.Simulacion; +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 Microsoft.Xna.Framework; + +namespace CtrEditor.ObjetosSim +{ + /// + /// Interaction logic for ucFiller.xaml + /// + public class osFiller : osBase + { + // Otros datos y métodos relevantes para la simulación + + private string _nombre = "Filler"; + private float _ancho; + private float _alto; + private float _left; + private float _top; + private float _angulo; + private float _botellas_hora; + private float _velocidad_actual; + private float _diametro_botella; + private string _tag_consenso; + private bool _consenso; + private float TiempoRestante; + private List Botellas = new List(); + + public bool Consenso + { + get => _consenso; + set + { + _consenso = value; + OnPropertyChanged(nameof(Consenso)); + } + } + + public float Botellas_hora + { + get => _botellas_hora; + set + { + _botellas_hora = value; + OnPropertyChanged(nameof(Botellas_hora)); + } + } + public float Velocidad_actual_percentual + { + get => _velocidad_actual; + set + { + _velocidad_actual = value; + OnPropertyChanged(nameof(Velocidad_actual_percentual)); + } + } + public float Diametro_botella + { + get => _diametro_botella; + set + { + _diametro_botella = value; + OnPropertyChanged(nameof(Diametro_botella)); + } + } + public string Tag_consenso + { + get => _tag_consenso; + set + { + _tag_consenso = value; + OnPropertyChanged(nameof(Tag_consenso)); + } + } + + 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 => _ancho; + set + { + _ancho = value; + OnPropertyChanged(nameof(Ancho)); + } + } + public float Alto + { + get => _alto; + set + { + _alto = value; + OnPropertyChanged(nameof(Alto)); + } + } + + public float Angulo + { + get => _angulo; + set + { + _angulo = value; + OnPropertyChanged(nameof(Angulo)); + } + } + + public override string Nombre + { + get => _nombre; + set + { + if (_nombre != value) + { + _nombre = value; + OnPropertyChanged(nameof(Nombre)); + } + } + } + + public osFiller() + { + Ancho = 0.30f; + Alto = 0.30f; + Angulo = 0; + Velocidad_actual_percentual = 0; + Diametro_botella = 0.1f; + Botellas_hora = 10000; + } + + public override void UpdateGeometryStart() + { + // Se llama antes de la simulacion + + } + public override void UpdateGeometryStep() + { + } + public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds) + { + Consenso = LeerBitTag(plc, Tag_consenso); + } + + public override void UpdateControl(int elapsedMilliseconds) + { + if (Consenso && Velocidad_actual_percentual>0) + { + TiempoRestante -= elapsedMilliseconds / 1000.0f; + if (TiempoRestante <= 0) + { + TiempoRestante = Botellas_hora * (Velocidad_actual_percentual / 100.0f) / 3600.0f; + var PosSalida = new Vector2(Left, Top); + + var UltimaBotellla = GetLastElement(Botellas); + if (UltimaBotellla != null && UltimaBotellla.Center != PosSalida) + Botellas.Add(simulationManager.AddCircle(Diametro_botella, PosSalida, 1)); + } + } + } + public override void ucLoaded() + { + // El UserControl ya se ha cargado y podemos obtener las coordenadas para + // crear el objeto de simulacion + } + + } + + public partial class ucFiller : UserControl, IDataContainer + { + public osBase? Datos { get; set; } + + public ucFiller() + { + InitializeComponent(); + this.Loaded += OnLoaded; + } + private void OnLoaded(object sender, RoutedEventArgs e) + { + Datos?.ucLoaded(); + } + 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; + } + } + +} diff --git a/ObjetosSim/ucTanque.xaml.cs b/ObjetosSim/ucTanque.xaml.cs index 0bb4d24..1b431fc 100644 --- a/ObjetosSim/ucTanque.xaml.cs +++ b/ObjetosSim/ucTanque.xaml.cs @@ -1,7 +1,6 @@ using CtrEditor.Convertidores; using CtrEditor.Siemens; using Newtonsoft.Json.Linq; -using OpenCvSharp.Flann; using Siemens.Simatic.Simulation.Runtime; using System; using System.Collections.Generic; @@ -46,7 +45,6 @@ namespace CtrEditor.ObjetosSim private bool _SalidaAbierta_Bool; private float _capacidadLitros; - public float Capacidad_Litros { get => _capacidadLitros; diff --git a/ObjetosSim/ucVMmotorSim.xaml b/ObjetosSim/ucVMmotorSim.xaml index 0379f7e..6297680 100644 --- a/ObjetosSim/ucVMmotorSim.xaml +++ b/ObjetosSim/ucVMmotorSim.xaml @@ -12,7 +12,7 @@ - diff --git a/ObjetosSim/ucVMmotorSim.xaml.cs b/ObjetosSim/ucVMmotorSim.xaml.cs index edad0f9..438a43d 100644 --- a/ObjetosSim/ucVMmotorSim.xaml.cs +++ b/ObjetosSim/ucVMmotorSim.xaml.cs @@ -15,6 +15,7 @@ using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using CtrEditor.Simulacion; +using Newtonsoft.Json; namespace CtrEditor.ObjetosSim { @@ -22,25 +23,10 @@ namespace CtrEditor.ObjetosSim /// Interaction logic for ucVMmotorSim.xaml /// /// - 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 class osVMmotorSim : osBase { - // Otros datos y métodos relevantes para la simulación private string _nombre = "VetroMeccanica Motor"; @@ -53,9 +39,21 @@ namespace CtrEditor.ObjetosSim private bool _encendido; private float _rampaSegundos; private float _maxHz; + private ImageSource _imageSource; + + private VMSimMotor motState = new VMSimMotor(); - public VMSimMotor motState = new VMSimMotor(); - + [Hidden] + [JsonIgnore] + public ImageSource ImageSource + { + get { return _imageSource; } + set + { + _imageSource = value; + OnPropertyChanged(nameof(ImageSource)); + } + } public float Tamano { @@ -89,13 +87,12 @@ namespace CtrEditor.ObjetosSim } } - public bool Encendido { get => _encendido; set { - _encendido = value; + _encendido = value; OnPropertyChanged(nameof(Encendido)); } } @@ -156,7 +153,12 @@ namespace CtrEditor.ObjetosSim public float Velocidad { get => _velocidad; set { - _velocidad = value; + _velocidad = value; + if (value > 0) + ImageSource = ImageFromPath("/imagenes/motorVerde.png"); + else + ImageSource = ImageFromPath("/imagenes/motorNegro.png"); + OnPropertyChanged(nameof(Velocidad)); } } @@ -167,7 +169,8 @@ namespace CtrEditor.ObjetosSim PLC_NumeroMotor = 31; MaxRatedHz = 100; TiempoRampa = 3; - } + ImageSource = ImageFromPath("/imagenes/motor2.png"); + } public override void UpdateGeometryStart() { @@ -178,68 +181,19 @@ namespace CtrEditor.ObjetosSim { } public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds) { - var index = 0; - switch (PLC_NumeroMotor) - { - case < 100: - index = (int)PLC_NumeroMotor-30+300; - break; - } - motState.OUT_Run = plc.LeerTagBool($"\"DB MotorSimulate\".Motors[{index}].OUT.Run"); - 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"); + motState.UpdatePLC(plc,PLC_NumeroMotor,Encendido, elapsedMilliseconds); + Velocidad = motState.STATUS_VFD_ACT_Speed_Hz / 10; - - - if (Encendido) - { - motState._STATUS_VFD_Ready = true; - motState.Motor_Running = true; - motState.STATUS_VFD_Trip = false; - motState.STATUS_VFD_Warning = false; - motState.STATUS_VFD_Coasting = false; - } - 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; - } - - // 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); - plc.EscribirTagBool($"\"DB MotorSimulate\".Motors[{index}].STATUS_VFD_Warning", motState.STATUS_VFD_Warning); - plc.EscribirTagBool($"\"DB MotorSimulate\".Motors[{index}].STATUS_VFD_Coasting", motState.STATUS_VFD_Coasting); - - plc.EscribirTagInt16($"\"DB MotorSimulate\".Motors[{index}].STATUS_VFD_ACT_Speed_Hz",(int)motState.STATUS_VFD_ACT_Speed_Hz); - - 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(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 @@ -277,4 +231,82 @@ namespace CtrEditor.ObjetosSim 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); + + } + } + } diff --git a/imagenes/filler.png b/imagenes/filler.png new file mode 100644 index 0000000000000000000000000000000000000000..53f6d7f370437a152fe71900702ab7878782316d GIT binary patch literal 16708 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 OpsJ*)SRro_`ab|V!5Ulu literal 0 HcmV?d00001 diff --git a/imagenes/motor2.png b/imagenes/motorNegro.png similarity index 100% rename from imagenes/motor2.png rename to imagenes/motorNegro.png diff --git a/imagenes/motorVerde.png b/imagenes/motorVerde.png new file mode 100644 index 0000000000000000000000000000000000000000..9703d8ffa54e8b00fecb54dafe7f4629a0d0d8ed GIT binary patch literal 19454 zcmce-1yq~swkTSsEyarzcZz#}V5_*(0!4}j5~N6Q546SI-HMk21ri7j#ogVZxJ!`Y zH>|byI{V#s_BrRhcgJIl@Z0>R&-vN>g5IjgVm&8)e(&BrEO|L8wR`vOv!Q>Fo<2dp ziT?781ATdBE2rad?;dvR-S2)JD>fPW9n3;Q+fiFt31|Yf<^X}A#t;ryYg;t*y?Y|! zuC^c(ONb-2F~rQmMwE8Hwvm?F0xU|a#jnhzY%2*dw~%wUhp4-&XqdQLnh1eu#l@&a zT!Cl=)(}S!wX3z2jRVkCl=e@&K=l3HYff6~KS3NVMQPvM5v0~ueoHM0wTDpia{$;) zxcLOB1%x=b0eoD%05)nKE^Z-CEBDA8JA?jMEil%gN2bbtlrFfMAoq;cT7kt^Onq zHsOR=L98J*jt*#8?!RHtT2fa28~i^-Yi<2Ew1cCxGn%7+$o8K?J7~DsLO9hR4p1k1 z6Nt1knj+ml8FO${gZzhT{s+y`;D0x^HHSJv9n7KsMhSm^{#zBcKuLQD$PsF<0fk!q z)5>rEiG`X6tupG@S{627sEY&h9gF{H0FeSYLPTkKxOn*3x%k<+xiz=}KwbeL7dI;x z50H!NFHmK)6M;dFp#KdRpusH!l&nL#*h zZOs0prmPH&PkN~$Sub>b+4==AVJ3ojIaEFbH zU4R$D#mgte!^H>T`zJP4dkb_hf~@`(>m65MG)8_d5RU*@2+R&L5j18OG~t7=3xPlY zc7PBU9}n2rgrAoSLQDN0bS0ryPOe|;}6Df!kOYHDGH{@|b{D?u$UEeYTj z0sz=~IJoa(=1<=R%2_y|ZS3}sC{%~o{o~Hcg8EN)1%gcOB1)9jhpQ#YLpfMkxiGV4O06(ALKSlpHtaA(f(`WzRu>KFVnwW!Z%pmCW%t`xi zg)xEJI796J#TK?8dk{J+L+l+yX-(~+*3=+dTPq6_(4FBpoo&GXNOphw47DSa`adG% z-_ij?>@EI9ZU0K;Zx*Qk57PT@jl?Hl%rC^x4QA)z<41QgU_K#sV_vW^JCBeMzlkXT zg8qjNjsMcW{(m#le=p4cDEs`U!u;Q}sjhAw~)DWv_HEf z;D442{-*(j?xTR{RQh*v{ePAQ|Dq55|C&zkLf}uq|78sE&sXSy$=&VWBO>&}-(xC> z4LVZo(Icj^L+RXm_avC)rQT?`rtZQlBN@8h&v(^S=9^LR)(Z3DlT3{pQ$}0Sg~)cw zZLrgu&^?{;e)|X)sV%=o*sk3^i9LlKU}t0^ic4bU>$@BI4D;3&mj(~?=>C%&{j$J6 zBK}~d*O8NZAnI)DY-Z|`m%m?p9I@wcIqK3F6Q?sLS_z%Iq6OC2Xs&g|=p-4Yi^L@n zrXM3YxHa3XH$@K4Od^#c!_bHVEj;-v0%UIfVgd8=O0pFBGVb-JmS59Fz?}$LK1=ReooD} z#)(uDe5_GsWRj`icG01W_n~BC%|KAq82D}Sy8J|m@$5IO%&aV9>$lGe|LuiKzv2O5 zi>;Z5nP#13UxmrI?{dzzR$2Nt--`C%O|0Vd-0issxL*PTuksPlnlToqgNG3}lFw+~ zQb5?Z}%M>eDAv4asp~Y>ifw@g$mnvJT_e=}BhHJZfbxt@W&_0Sj3p5&2Y$%#+ zTKCP@N$F0pA&8KfF^;N}KpFtC8F8cCElH|7Uti$c2@u6Yn-e@XLh=*y(g*_66Dl=5|*3)V#8^m z;!eaQu&^v0TQzBDUQ+LY)6>E_gfMnO>(sc8@n)p0;E6l@vyBmwiEIP9-x!vk66Ta$ zHs@Jnu~QKj1-!m5pqbrILWSyiocBgl^63%qJcu@laQP30B0}2ECJW3o^%uQmZ1J(j zD-S*NFrz=GM;ggLT-KE^l5OPr@ zDRfhbd|x8x4-EE;OmGTeenQ>OC(UoC%pp2Lz0Jm(Nf}{LbTKV#!kqQ&n1!{$eUf() z`aaAm8}FL0hu9C>VmdmTd|<@6HU@;RQ=bLK;o*%iy+_8jZR92ZQhkY22rEhAN;O}U zyGzBx_{W#RiF&|1I<4|u5Oq#vOVoQE<|AW^ndmjI+oCb2VfEw=ISt=bT7-`33!!k!fg1Q3 zTlLqN3>}-%0eXw*qhnMaLh<4~)p&a*{`j*`vW&DnH#taaVXwlM)m&830&xgCk?oGH zt@tZhoHSmigjpsSmT5U&}Iude? zEX(VTxV%ZbCnMMiQvwvUS*7UoGc@)b`8s{)9R`WZFGFO$^VYmgDvI2x?n?92eF~47?b>)t8 zEeBJ4F)vGfHu*~9{OfBVzK}{+3tim+OXKMH61gy zA50m=+Nzte&kc_<#|H4M{&bW#&C&0d*gMN}avFC<0e;Qp^H8#WaG?RMRC648Xv#A9MpK9^Qn??uKwOjk@(mJJNQm3$EG|UPVFP zieL#Abu)q|q6WW>x|cW>2Os9r$9}U~SJ7G}s4sne+WZ~IlHiW}L5}rewE>+=@IDPq znFZ)$%|>nuGc}#q+WL{>S)LoEMV1ZhLLS8#S=o% z{T;lXd5Qj)I&SNehyrR>7ly*VJ6D%5?8_$#Y_GZsat-T<7iR;X6(v{Nk?3TJ37)9q zmxnv8QK=`TQ@V!veq46Eay(&Qx(ibUycH(Oo3ldAtb5}wITNRiiI;3D;Z~eBXI}aN z6-jq);(5@Hp;;^;-S#1KdW?{e(9%ai`@gz$e8QESp(^~P`87xxMnosUP-OD+t}FlX zAs|?E7U4ADp;au|Hbf(`X>C~ZkwPZOBGn+nZ))@Xc3Ib7z55*UqbN%_HoA9a>}}pb zVvbEs)hBf~n&b8c_FzV`ikT3d3ZqQZTVqh-alHJ*x>86|Ubm~b7Fi~7E^ z`F#G_(-*F%aV|S{3hDlhH;CKAw~)ebxCOraEPR<=H}MORbRQ<&_F+dvOzfis60G6( z;!0%#N4vfXjpGT&;@})2G*jyLyMCByzbAjp0t$PoY_7MBp^8~?5H76}6^c)KGvTOc zX7s*NqYdvX{&;63upkC6BrE~giPN%soyxpUuN0o;N=O#2piS4Ti5T-(;l4RjA%`$! zG+)l!bsEgmj%^y)@qP2<=gH|;oVcBT`F7uf zDEcg|Db}w&ur$l~^&K}crnKTGNf}DA#DISw zu2~-yEphi;KNQ&UO%v8CR+N^OCe2^Aj+jNlW*TceQNyY@ab@x8gCj8`3wFrr?PYXZ zF9(Lg;NX*=VYb(MJp!E9qkJkZ=~XAKF4$3J&c05U$EOLEh|^-NY1u~Ou^J5xjaU;R z)BJ|gOFw8DdZ^+sv)kjYPmWaA~@)5ULzC{sDKqFGaxHehi4_j$GV0 zuy1UR^+&GLuV>rMrvwJN2^<<4Rxg|T`zUHOw^xKq*N>v3H_VpxkFq%3%2Gw0G(RM9 z&OyQOmtxNdk~&ta0&fqKDj}N(`rbFS(o*y?cyp#^C3wz-`cFkD6eBh#MTz13^TSXo$%a=}1}RtlOfZ4C zd|`gIuTQgT4cS!1JW4zV467n2BWP(A&0;;6aEzuiZbwrPPBviLl7=%X z$L2b9{K@OlCb!oe&FPPlSrWPrjh2@SvUh%y?B`&V^a5sM%zC0RYY#~W5FLW2;^{xW zAJ!c-tot}cJA``$>9q~Ow!~`>Up1V~?>nHjEE53XwD03ZuWv$>Qi4PrH)-4s$`;s% z-%G7Toklgf+gx6q8BSOKMvyxHdS)r>+`rN)Otv4*lu@r`aK5576oXMm@Yt%QHRkzF zljGxS8OAV*g5MyQP`RlxHqr@y8k(uPNszlXv4KyUyOHeZw=1M|fgT%`<&Gw!@Y}aT ze0^UW>+HYYBnEW9wLjj%K(*ah>9OjW;8QmHWIm(G7}F(TC}KzzG%T!47w}E;sbaj^ z^O|!Jr(_JWqA5|O9!CPa^9IfZU4o0X-wsFR4yrMRm%X(ss>>Os+ibMv^8;HkACGIK8+uf{w`*fhA>;ivXV0*@wetWnYBHDpER>mRd&n0g6VuCJ0%!r zB*F7&+_hRZiSSg~YrIIesy726uYHS|8kR$94Sxfo_^FHZW?>gLc!xN#hyx&#%EOwl zVkN4QWsjWZ1~~$g*fRsKKi2FT1SFnBxz+Bgny-f(hw<@5pmII=(!+m{=1Yzav#sQ@ zab35ALhY7NDSR<)hrHHS#G8B^S&U*nQ(Rb?-s9K<#*p~c{FOx9~ERIpd)`J4X2S#_Y9pj z>NVyhvT4}xY$`S?uJ?1(l;X)gb+ZX^tBXIOX%^OBkh@I}*^g(PDCy)BJ$63tJf46#B@g)~vf{z-plnD%L8YS0C=|JGtOM|6h6r%V?y@9G#)#3B=6*m!Pf@3j6gtMBkM%bOv;N~JfJOSU0D zB#WC!m!%=BlH@8yX3yiOa7Fj#*Fx|3t$UT%KvFU9S(tz6%U;ilN2be@d`M${rIp2| z7Z&7{b|0!WGjPAuUANpi*iNyI<1`*GRk}34W_y{@sM(5Yr4pZPJSx|};L zII^JP%>-f@5JM?XF;vx8FHfPZ+}w9ae)$meKqrR8z% z4{aAu*Q0==+Jt#Uj1P}5i#gxhb3faBGlQRLeF@;ePms>=3p|p1YQK=ssFr@%TGGeS zvS)R+nSBTfI>>z__}%r)4hyfFt*kV!&sXfNKBn)5Bb}+4I`z7}72^ zBCWQyriS;XnnC^{j)wEGUO}CR^inFL^!eVewWT#P`E_1qBdsK>glAq6BG)@3h~q|P zfqil;kl#=FtP;3%4G#YD=B*l4e7Cd^vqNobYY7PsV>_+c2xmmAf4O|>IQP^fab42{ znQMI#b`O&;wW{dtXc13&W?HDJ!enCZBigxcNC z@~4{Ti4}d*dDg1cZ}AMojS4SyZaX(wC(japXrAiNxIhw2_ts%Y zOE2X)HO>P0XQh;qndicnck2c(M;?UC{&37cOOcs(**ueSZ-kRk_qIobq>%&zkVm~J zZ`eCZ-G(+JgI`qr;cm^`7I}&<7S><126RxaH8-=ElTSw~$S~kV<$WSqxahsDshXc5 za-gP@F8$t9XAK$V3n?d%EOp1ZovCANH?}g->-;Ezpcj6$nNxODWKis8@LIJe&rXAr z%+Nb~j|iZ&{c(Ouk3B@>Jyca}q~+Jxe(eAZ`{_=rdz$#>7PQh>A>U#K+s;uL>v&q95tja?TNJxw(pG(}Uv!+X2EcYcx6l|GFburzM~EOgi# zrH%_A7GUUEOG54Nm1v}S)U3R4Oy8p+w~{@-R^0E22UYfcBaef$9C!`E{Y|4ie|+zF zC)pG4ZX@0P^Bpttqf#^6M|h0eS`YaiV_)@oXX{q;X;4|gMmi2_#>>OL=&`vUuaQS( zp6+R+Z4O=W_fHgclIHo+S9dpa(q`uo>fajV` zJCgi#YJ;?l%}us&e(NhECnASUn|z-=3yZU&v@R-CNV9^5Fs+&)%yTgc$FW%iv+V|H zr>?2A_Yx0MX~*&A6lZ=$ynmuQc$lL(OX!PC&ku_o?>Ms|#_)3F$+ykobeM(pJkcA{ zpy7%|4QKj}&6TWy<_=qex3M^Mtfxnt-|SvKEH~!!WoXU^(B9y23O1L9yB#!wkCzy^ z0MGdn6lGQ$(W3#D!TewJlEoPfZ;19)G1&;V^A3t;ZC@us}NbSKaSGJZBkemmW#kTs!w<0X+Pu;}H__sx0;*b?4I_-hy2N16aFC7jVc9 zA|Rb82*%cW>|q&j#;@N+E9wEz+KRhTGw4{6{3f`$n9n_FxpVBXoq)aHvQ;M}8lOhw zc_Ox!7J}|I#=$-O8B@uxvGzSY%Aax$LKKL~cxIo+)csn?em1+#+$_KLb6p3X=%#?7 za=c2(zXN1T31Ei?aoV6`_&|&nG^F*J%|X(21*+ z=Ut8-JuNNNd+&DolB-K>x=nXZ(|DlNdZFmav$Wr`tGr|sAm$y7LH{M#I7yx0l$)eM z4QkG7qCe}Rl;!1gnJ#<|V+I-pF)s32!@;EK9N9m&47mg}YEW(H%Cvqu1PnJDQ+aI^ z{y~JZV6?FxE$!}V-%2|>wdlU+S(H-|WE8%cD$pxwRTMjBy6md1P86Y|+*{!N@*&Fn zspT{x{xPU;slorKT_Op3s z6K#d?Hkl9F?=A#t^410g@r|x%GBgzU0c!~Jt@~bN&-SZ1vCNYmEjA&(B(m+`P-gOY z+kO0roZP7N{g{@+VgNpu!r;XAdIWF17+Ov)e%33)u#W}7_Bxl|wZh4I)XhV<@p3Mln%&*T+4YaNt8y(3-cpqeIUTcz9Pchqh#ByyxzUk3Ys#px zWgydB$lE8oG~z|qXSywBs+AZ=R79dgEsf^;bDt?Q;fV4~_28^>9!;rEv}-adpH-2u z=7$x_g(|8XAk#v8eTX>$#bX6C?azxcy54v+-5-09F@S5yH8w7Ov>{uAJ%~$m^4Tfd zNeH^$Vok*lD+FpE5pd zyx+k_D5JvQ6%l;+6>Li&R5}}AMU1cyPys2T%5K%|?75ODqb=KBtBsVCpG%&UQGJuL zwE0E{H?ALgDqc$w)Y~qkr29_j_tN6>ACmE8nTB>I>}+@oOS|`rd15mg1CkfUyDXd} zFnBCW2~$4b<8-nTHe&7Jugj?~joaH85yi%&qBtha^I%Y%xiV*Cd)AZIP{q@^UQIml zusUbJ5aPrk6xwJkGRR4tNeIce?rmmyB}JINy{xH(6%|;YAjHh}bQ`@U^N_~BI1-C9BU(nUzqHP~vd2!0b1m(Cb+>mYT5P5X{BB@)s3lH%6RuJjWU-~uWq~uq< z?}%W%ot?9tN7~uEie%;Be0;*_Xhc+EGF&>2v<3>kNZlnhfDhLjiyQ#NJvhUOcB@Mp zv)bB^1+kAwA3ip}+w|eWMC@j(*^C*D$2#$3sdnX=sMua!muI?9E)+x|fO7%_SQbpQ zb55S_sx6XF;bT^i^)mjc4>@gs`hx<>mhwp0;^YWB#)x8VLV9(7e4H`xu$UB+p4*ht z<5tn}O;S2GTb(iDXX;XKha1Up{U_iLWI#?QTW9p=njo6+_QUk;7Kx1a-P~T27)kKo z@oYccrH*7CvKMO1u+DLqV!UG|D@HDVn#k}cd6ezJ9`_x0J4T_cZC8XQcGpEs2p~Ri zfiloNTdFiTwGmjzEhuhn8-IC{;Oeo#r?i}?y3~tATSyy1!dmqV-N&g@Y1R{msgl>gkrRk*@Xu z>2lmX3}as;lT4~XqP|JnI8om8cov1tuK4ARMmhMa_-tX5#=H4-yn)L>)w^mG;?2CD|$+bnXm!H)~;R`J~Sw8%oBcD9exNUCe_&Zj z>e1eQThhMp$|;h>S9+3O=?tQ>vfr=`HT$@sVMYLoF>$IE;%O6FwNd6=7X#Yj2ij~m}VI&mch-hA|)H6@B1MduC#iB4X?fo?T8 z3WvW%N%bE9^5mgNc~St{ujc>wLcc7U-vDSo zI_Vrl)J@*|w7NG_Wx(^B&vaSL15nj_zj%xMThmty+k^7kcK~4nGK>eK8Dqs~df_L$ zhPjM-881rIDKwoka^tpo1)Ir<*bL|2_^+?(gd$(SAV*K|N}Alp@kTnfrUXh|-Q3(y zuz-oskKOUSomdPbVx^xk?{+B4ag=V~W|FU3eTWqgrT278S1EXIfXzA7 zRYJ$M@Gi#byTR1{(h2Nd#O&phZ=;CKn5f_c^_$tcy710v#Y&Ez?U$Fb9|uLA+beaE z(u#LH5NCrwv!q;ibO){1h5j@S9Yz+HTW>pSD~G7`>C~p^N9qXc=9qXh>#ODPw~oK|E*S_xg0i*o&rU2|eE z|3*jOc&g^G`^h`ab{ZeEy-z>4R!e=HqHualH?(MFgF(AKAZUR7VwfvKQO3t|gai*L zt^Ee+n^zMXI}=St#-|alUsmik?O8@?yNw@Xjbn&<`gUZ--b3?Y55clW80Ge-G09*# zYjR!jBK90PyltJzoE1=Yn(N3hhdfbvg5dr!chrwtX8wEJJ)9?b$bsh-*COpz+VYV; zR?P}d4dn1{&`66m|K#~__5%DIre}1HXcsK+O`ZpEj6W5Hmjd00FxTREZOfWEdRj)( zqKv<%&hxf6-CthAC3~>I2Tzjan4aW_dmMplx{J)ow*0`VRq`Hi`IDp2qYlY4Nxe~_ z&ZdeT%+7W_piM0Nn2b<#oHDB}-%zD$!omS^y2Y&3e7XT?WBCYOk{nYmDMYN|x7gM5N(vEBIpmtK}XzMPAzJ0SpSNUWO!v%MH#W6JGxDndSV3}#>j{-=Q9Ea)=V#Uv1X-iLXVB+O&w)m!sN+F-`wW=3~U zII;-lx_PicaU8O;(VWNFdr^tr2+C7dcH!$C@yoTu1pb;W@J8oQ)h_DqPh@}acNMV# z{ABB`^tsZwVNWx^kxTcGvW=XEv1!s!Y_6exw1H&_Wxg;FY4%IXh0#0nwO{6n1--6Q zv7HZh(v}!Z4$xu*P?#OsZ^?bueAW&cc(+a168UZNQ#|~&w(clbA=3!P2uN7TXe&_X(P`VS={vK;ooEY4ToLeloq5p_^0la0cIZ1)4f zxpE*P;j!${K@qkRBUMFDeme8Ee_PA8hW3I6H^kx?FCr{O{Z`0FVaw~|^RD5XW_w3w zxA>YzSvexK5MJDEEeKD&$brEE;<#CDQ=vObS-TfpNk)^@jHGdIT?|{xm37?Ns6RszVO_s#z{jjC#^QEuRF^u^)b?eqG^-ZNXQ@$0#3;OQn^FXp9D@9TOd8S3h zABbot#;BOcVsf(a32Ix%d3GHqDjdzZ{3G1WAC=5@qsX#t#j;xmQdsv^D_1ONd(a=` zQ_gdl=VBf-eF@9pKVxHx&2=xsYJdUr^3tphNRHU*r$V+w9N+QB4|Vs^$UFgb((0@p zpACCrP%I`C27VE>;_L&lQ;dJJcSN2Y)P17?uCC?3h7lB~xQI0NjNFf+WhD#nb0f-} zSP~U-U5I|(ZUSj`_Cws`ET))Ld&snA2H`yiz&0^oj)vf|Z)qII<31UB`38afY{4aJ zDVzS-Z$(Ef+^TtaA}hGzNjlF+{jK)5sk)ttV&$HrQLZO|U(R&k!r`GAvidSS-?t5+ zCV}$BZl`{rY=zu5524mgJa-oBjBo~D{Va7A1K9a0Ex#P4jF;62bR|Hc!@@zBCYc2~j=-xJKX;+?B#vhVeD&_Q*X7@)Me%Tj%>UhhEHbbkWP zMtWi^Vn>n`t|)};t$=<^3yRxw9j7sHV4|1xXF8n>$j3hrH5=D%O`&clYWhd= zFT=m4>!h1F9Bw86ue%+kB)zf5(gNhHfLO6);7wf5KW*cM~}8Y0zf-3hm^~ zA0i(gk04Z*t4Y*|G!}lg4kLJYqg7YaE{e@D$QRc)znqotNMgK;?MkQER%Mnz-OQY> zP?)K!g`=mKFzQo&u4c+K=E49x({Reb>O+sk(a@;aumoC@8Qb01MCuVxSX2j>~M;D8Q z-bW5hCe?WLj#$Jn*3XR1L23Ze;N`R<+x}W4xU91TeAf zgDk2}^e34nF+Q)XP$jWVdxrR?R${pet;5dre8=Qr+k0-+GVPq0jWn*$ga%AKhmq?A ze7LGQNa?CL!?ik%nx2jsj_L#052elkwT|3Y32@qHU9DS=2ZTSXc@tA`nkYH(74BwW z&GdLrzWGeJ9Y&%%tx~Q$eVgX+^ZAo^q7KhzDy#!E8;E7;MXwPSla9EM3Txf9mKwp@ z-$nnZG}zH#!jkDqxo5Ozsx~7XO`C9WpJ;7EP8x3Rgt$x{y z8XB%RpNjG1b47#u1}1T%M!UrtbhwFJ86p|Fw#ybZZzR{MV73YPeUlKa{nm!&8&YpPy78U zOiwV_OtYg?V17Cn4!T(I_=91)v`C3DVU<{7iaCyO`AzI90e+%l=D>rV?vPFsA{^m& zzMJi*7+d^^Ne!fb9HpKRX|q~KZdM78h`j{9j!5VO&iKmjiAfXVT_4JljC_ibShHEY zjtY>*l@+Y?=c)a3hu$Y39QI*(os^2#QS4Cbj?%XhvZOK5}!{`5RO?i|>* z9=K_1b0Q#3sw`GYHWi0P1qlJCfG8$WsIw~N>-DLJ5*1@e% zyRut~9?}FZpF{12(fI<@bv7_#cw2UY@Fa*!bN`opuKQtXxv2o~tP~ z!zVaxe5HZWW~*tM(#}duC(o*gLHGhSpK38*aLL?SbtNLI1B@&q9=CcE4HEoz_Qo60b>=vGz|Jj2evsU7Y5sw@F?Of zcqc$fcv3mKLRQ}+c@-uPhDS)*E@a5Ya*to+E;JrDgd1X9D+Sk=z7D(CGiIfIlWcon z3ZH`3p^$Sp3n9GRR8NAqzogce$K>g3ctzf(-^!xqQI9REmnHTg^-_#|IVh2}-PHK| zun(tVbge!YK_T{;Y)TXD&Y2O#RJ{}NUE|6eY7fWX_OLT$#7ehyhP-;*Tk$inctD}A z$^5CJudV{E1!{yhC?TJifv|T1=9VXb;yhVuz%YE5n)eHAJnuXKVz&L%)^G4jD@}O2 z^rC3Fp&&D`@zI0gkFB`Fq*$Q7>6>ehjmZ9_U@Um)B70_BoC#3Nis-of$-tR2E>2J( zU&fCeG4bfW0n35Y8dbx*mib=ehYR?@w(9X=+P~cslBxmL+?j$S??wq_2Zq}U(ABH4 zH?aJ)-*!L!JxK<2W@Nun&SMhStLT8(UKE%1c`BMyKBapYk;)e;WNkeQSG7Gvm0ldN z++_~F#6B|$;#N;FL%~+UfQsg+`w8jdEUOO2>f#Ysk-nm=MUS#+iZtW3FlV+&u1{1? zr^Rw;SnbZhIaIzAwk#lPMm2iS%v1cIkvLc$P<4mA9MbUHDMfltC_s6(v(i@q-}%wO zWU3H%((+KdCFHljVl11}%aeuyO_)x~Gta%>)c{8uOKo|Fp{meNBKF-BrRwTh`)7kl zF?0rqK2XwF1CL^?ioPTh11oHtk4^$}wmcW~7R9vv&afU_>l-I6zIZf|S%&kArx}Ft z{HqetkIgCkz_9XCdJbK-=WFI22L|46mTcQKGnOq4=!?-yZKe{HaZ5FfQQ2gJrlsdX zE`~Jzh`C3Cg-wpI@ZZtK=&kIvOV&nd!E-K*tO=4yISnMgSwn1wnf;82A`G8!`Ck7E zKBXmgKV}_lBS_wb{P#YdkWfFwD@^>=c3th?ru}s6FHE4g)fMl>YTSG`E*^jXToGjs zN|0GNY$nw#=g&`bk;9W8Y$m%a?OKpfI~wXVv zYbdD2Dtm&5Z^C7kQX6zrvnmWIYhD+58*!69v~y!D<%cTve^*nKC%%`bBJ);X^0lFs51sLk_Z@6< zP_yWyx)9` zO7CmgdUtfN^Vv@bE69mu#z>*pL59VEQV2YmFWxgVlC5g?$V0b%I`DcyU<{8rWxf(F zGO3ny!1%*4lN-AqCdOE(zEzw>;kcWw|5!yR>Pn#-J=8N!$*Au}$+##D$mKl>eWaZ| zbP$a}Y!bUr{d?v|xKIs3u(#Pry|XQUfI|FzWIf$S1*br}{Y0WTOH>z(QmIlb;{%JR zqGgK$n0FF_HBaM9gdVWc>+L@zNNmKg?lGv-NU|~@t4`p0v`{p@KqMsFIL*f}QjuhG z?Mg(rmF(kvGZ|d>O*k4DC@b z5xbQzY}3(^9G5#$<~^Vp_sdRsR$f#r9sRLIazQUYuR}=P!>U#(uU+T&r?8C3&3N?Y zWc&%pjj`OogNV@MX$ao8Pl{`H@sR0(b<_%3YjEEK%gPPKtq<}H@Puo%WQ=W0*oB*A z72Bjc&%8JmL(4?LFF{^t7LOsN&YG_LUSdv5C+-xza%#+XxIP2rj;^Nb3UvOz|0Cobagk zPtjH2{>?L2smbjGJBm{Pa(XL5sIT_MluPr6j;B|_1)UR46DTX{5lH7b}cXSXtm)~7^lbg`B zSv+#|#lxU#l*5)HUfK*do+n>;X1Ox5;+8C7sx71NKmT?``RK6Y z)-Zn4Q9(tY3=32N76AV6>vppQ4Mp)Ujx5WmFdLVHIP2D>X=&|0+Or6&;&@=mp+ui9 z%)GL-h2h=~>ZqA}C2K{RZYqwsM{IsQJl^hpH|_VLzA*t=drxa*YL_=nq^*cU;3$bs zP!9K5Sq$e^=g*af-?IXo$75HQfe&WXrp%TEr_ASBh>2a}U(~%$xS#NrFEgqmr30>D z+c8c-MS%yZ*z-9-E`43NH_;eK;^6tyRMYBH6}F5los1F9Be$hF&JTL|pMVIsYffJz z{GK&AYXH9iCnsY>8;Gjz1W15B`LM_8&B9&_Rb?8ygq57BZx^cZNL8r*G=R17DnH%`(#H4j~jCfBN?9Q1ko!E&1_2jC3F|mAt zfu1#3m1uk?pnEeZ(;WEKd?7vu=eOAgojf48OhAsLWdDS({UmT^tp%84MzIq6Drj(I z0;QM-Ow)R@#%W9+@yK|UPAD%=LJs^I<7AK3GW1=r0f9`kK~20BRZ+|wwhb-(ShvTI z{>SaY@uoocC8d`k@}jcKI-z9G0Jd%ZG$g4mI2BINQ4}s0uY^JW zs+KA$tWz^yAWdP+4;>9n)SFX0+=UoD;cin+EgmhwPTOH?+BlSlTxkw_2)L1`>!YM( z0K(aLpK5K=2DALAtZzjdBq&#QF?1l{x#e_Z7IB;8`9>_G_||%+vhnny|Kza95h{Vu zcms2=!6}NunN!@*!7=bTE+Qxy`ug}M@cy=xwpd|ILc~N;naF(kr(#d!SyKPTqtn2j zTm5n`i=TQDbZj-TGR0$Vch{P(9&}E}Sa3U9h6ftj26mGhziZ2we;>HY2pq_zuh+~( z^mv4GOvj1q=m^x>v2}kW+h)h9vF~9C;r!PacWzMA78R-tBGN1Snoa(@(pt|En~Sb0 z^Fn@LCz%)uIQ0>^a-DGdN5gBw-fh_axsS&%KNFl@?)d?VQ42JDarz4i@Xb-WaI5&m>^uI~Ll4E_@fm?iZ_DW*Z`aewQW?r1y6gKo4~OCPSD`7JG-6R;cAvhKa4D&Q=|a1 zIw5!=AaewndK~}O3l+7{8GZJp)}SgjUZ*piFjY8i_9GHpITtrzc$<247$zQ1rhFsz zrd2e?L(I|5>wFWivqCU_>Ng=)hdOA~ub@Pq@~Fg~vAdjBnH2I_V)8- z?ha_Mu-M=k2aXhP;=py$#*mUyNdullXckMNkNzFfwGO5WIjwn6rih*{OkGz{7ZuiO zEx2nrOmt`#%iclIwBin=0v#66HS@Q9AqXO023k0Q^UZ2ORa#|nE&2dvaVbS*$y|fe zCF>9RP6aSlJpXH(Ug*{Eu5M+(`QC~(iYwirj)et4aRIP3LynNSShKO=MRk}(G{QVA zFKY{P-@hxnxjRsTE*SQ{m%x!zs_DxBm;KPkH>(BvKuEZaK6o;J6|HV~6t0svm*?@w z0e$X(0WKiwSq?ic9I#4*wV@@g`3OTxdSRC`>wG?YdQOEtS%93gQU5~FXv>G8DstWG zJ=8y)U@%jES|JQ~U^LqY-3gZ&)OQt|TH}1zd8DZz#g1JsC$F_!N*Yc|?PcF+TZR_Z zlMM)7S9lOJ#rl(3#H%2*r@xHGSC&M5Vpk}*;!+KTq9FDQ-q$#@ zM-NptVnp4(L1)JAzgdyxxY}qM2!-iaA<}mVC0*^0DQCRLUBnqMOOiOON>YdiW>E1S zoMt!_57Y5!XpaaCf1lNFvUH=*Az>LcKJ{f{a9uHKnSA!BKsC*+qs&z{$F=sR*otvr zB&@AzW1TquAA zd1x0s<|T`K2%qi^D|33QO-?b z+xJygB9?f4!Qvn-#sVt7ucpo!8eoy=6bCQ22lHC8=Q3e-STIE!Qzf|>0=knUUC>Gs(f#>`)C^RTt%q&o!3X;#~_TfrQTtcDH7AhuBn>QAHPtJZvdacY!{O#Az2Ay6Mnti(KE#i!Fef>8KS%H&j#(ad@tCo=QU9+2w^SAa_Y2K>U~lv3arbMD zwJl%ogoXwdYJ?nmb@1oQpX_>XK}N3OVGJ04zWC>b{?-dq(-3HufU0C)}dU;_LXlqUQ+Vg=6&uPodnLn>S0l0yB z^|i%|9vOgk(f~(1e}}I<9=I^Ct_--1dVP^pL||bEhv62(BPUmVwcxtK_j19c(4!Fs z%eL9fY65PC>5-qh0hnv9v0{j@a1)$m-wxjFd0wa5|(p4=wvqGf6k-EVqL1>?xr<2+)S+Rxl>}5&26sKuX9Bj zzkkqK9a0xpHQRi0yZfn#gkLg?j_wh3eVf3{-vTn}MdpHvBV1(%mSu{$vluTrWogf8 zEy{hkFN1%{WrH^cZ)VKew|Uu~FVX#qMclp{ zD<>M<+!+*Rrz~2%OzfDmt!>lUjVt!-s4$z>mRUK|XPNq|?v%z)Hjk357SHq8aAuaX zlIY8Ay4?c6J$zk735!3T;CQcCu;sg3WlQdlHO8;%y?y3geN_7D%9gXMi zX>+DQkMCKVpe^#3Hl!vzO-RhxyL63Dn7>6qf^0&RTiO|SvBzqcLRHrsPTn89NT^3hipCeQSGEtu_f z-WTX&XQi(9{Sik_O01N;_;L!{(~7>oGTttpN0vDrU&ossEIa#NTlTzbS(~4F?@c{y z6lG~4dg#-#HPQLEPU>vreN&Nd$J%bRxC?0usgvnKxhr{wedj4J|9K6bO^KkXh? zUD0Ioki)2}yLEx6sOpbJHHPM|G^8T7Zs6-|VhauoEcCdtw*}ueOQ;;56VL4JJv)-7 zWF#5S+7`ok`XFqtW!K^Zw$b1BlwY-y5q)*&BFnPEs#>4>C=Bk;R~;}i!O=+p76!M;OXk;vd$@? F2>_(jRMr3h literal 0 HcmV?d00001