diff --git a/CtrEditor.csproj b/CtrEditor.csproj index 98043c8..e5600a5 100644 --- a/CtrEditor.csproj +++ b/CtrEditor.csproj @@ -53,6 +53,7 @@ + diff --git a/MainViewModel.cs b/MainViewModel.cs index 83a3fac..68abed8 100644 --- a/MainViewModel.cs +++ b/MainViewModel.cs @@ -146,7 +146,7 @@ namespace CtrEditor [ObservableProperty] public ObservableCollection objetosSimulables; - + // // Constructor // @@ -205,10 +205,10 @@ namespace CtrEditor public void CrearObjetoSimulableEnCentroCanvas(Type tipoSimulable) { var CentroCanvas = MainWindow.ObtenerCentroCanvasMeters(); - CrearObjetoSimulable(tipoSimulable,CentroCanvas.X,CentroCanvas.Y); + CrearObjetoSimulable(tipoSimulable, CentroCanvas.X, CentroCanvas.Y); } - public osBase CrearObjetoSimulable(Type tipoSimulable,float Left,float Top) + public osBase CrearObjetoSimulable(Type tipoSimulable, float Left, float Top) { // Crear una nueva instancia del osBase correspondiente osBase? NuevoOsBase = UserControlFactory.GetInstanceForType(tipoSimulable); @@ -261,7 +261,7 @@ namespace CtrEditor if (SelectedItemOsList is osBase objDuplicar) { StopSimulation(); - DisconnectPLC(); + DisconnectPLC(); objDuplicar.SalvarDatosNoSerializables(); @@ -287,11 +287,12 @@ namespace CtrEditor CrearUserControlDesdeObjetoSimulable(NuevoObjetoDuplicado); } } - catch + catch { // Log error or handle it accordingly } - finally { + finally + { objDuplicar.RestaurarDatosNoSerializables(); } } @@ -409,8 +410,8 @@ namespace CtrEditor // Reiniciar el cronómetro para la próxima medición foreach (var objetoSimulable in ObjetosSimulables) - objetoSimulable.UpdatePLC(PLCViewModel.PLCInterface, (int) elapsedMilliseconds); - } + objetoSimulable.UpdatePLC(PLCViewModel.PLCInterface, (int)elapsedMilliseconds); + } } private void OpenWorkDirectory() @@ -425,7 +426,7 @@ namespace CtrEditor // // Lista de osBase // - + public void Save() { SaveStateObjetosSimulables(); @@ -528,7 +529,7 @@ namespace CtrEditor // Se cargan los datos de cada UserControl en el StackPanel public void CargarPropiedadesosDatos(osBase selectedObject, StackPanel PanelEdicion, ResourceDictionary Resources) { - UserControlFactory.CargarPropiedadesosDatos(selectedObject,PanelEdicion, Resources); + UserControlFactory.CargarPropiedadesosDatos(selectedObject, PanelEdicion, Resources); } private RelayCommand saveCommand; @@ -551,7 +552,7 @@ namespace CtrEditor { public ObservableCollection? ObjetosSimulables { get; set; } public UnitConverter? UnitConverter { get; set; } - public PLCViewModel? PLC_ConnectionData { get; set; } + public PLCViewModel? PLC_ConnectionData { get; set; } } public class TipoSimulable diff --git a/ObjetosSim/Dinamicos/ucBotella.xaml.cs b/ObjetosSim/Dinamicos/ucBotella.xaml.cs index 2720ca4..73388e7 100644 --- a/ObjetosSim/Dinamicos/ucBotella.xaml.cs +++ b/ObjetosSim/Dinamicos/ucBotella.xaml.cs @@ -43,6 +43,12 @@ namespace CtrEditor.ObjetosSim SimGeometria?.SetDiameter(Diametro); } + [ObservableProperty] + private string velocidad_desde_simulacion; + [ObservableProperty] + private float inercia_desde_simulacion; + + [ObservableProperty] private float mass; partial void OnMassChanged(float value) @@ -111,6 +117,8 @@ namespace CtrEditor.ObjetosSim } if (SimGeometria.Descartar) // Ha sido marcada para remover RemoverDesdeSimulacion = true; + Velocidad_desde_simulacion = SimGeometria.Body.LinearVelocity.ToString(); + Inercia_desde_simulacion = SimGeometria.Body.Inertia; } public override void ucLoaded() diff --git a/ObjetosSim/Emuladores/ucTanque.xaml b/ObjetosSim/Emuladores/ucTanque.xaml index 48a449e..becaf14 100644 --- a/ObjetosSim/Emuladores/ucTanque.xaml +++ b/ObjetosSim/Emuladores/ucTanque.xaml @@ -21,8 +21,8 @@ Math.Abs(delta)) @@ -209,10 +213,10 @@ namespace CtrEditor.ObjetosSim return hzIncrementsRamp; } - public void UpdateSpeed(float MaxRatedHz, float TiempoRampa, int elapsedMilliseconds) + public void UpdateSpeed(float max_Speed_for_Ramp, 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); + STATUS_VFD_ACT_Speed_Hz += CalcSpeedRamp(max_Speed_for_Ramp, TiempoRampa, STATUS_VFD_ACT_Speed_Hz, OUT_OUT_VFD_REQ_Speed_Hz, elapsedMilliseconds); } } diff --git a/ObjetosSim/SensoresComandos/ucBoton.xaml.cs b/ObjetosSim/SensoresComandos/ucBoton.xaml.cs index 0462d80..421980f 100644 --- a/ObjetosSim/SensoresComandos/ucBoton.xaml.cs +++ b/ObjetosSim/SensoresComandos/ucBoton.xaml.cs @@ -43,7 +43,7 @@ namespace CtrEditor.ObjetosSim ColorButton_oculto = Brushes.LightGreen; else ColorButton_oculto = Color; - if (!tipo_NC) + if (!Tipo_NC) EscribirBitTag(Tag, value); else EscribirBitTag(Tag, !value); @@ -76,8 +76,14 @@ namespace CtrEditor.ObjetosSim } public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds) - { - + { + } + + public override void UpdatePLCPrimerCiclo() { + // Escribimos el valor actual al iniciar la conexion + // Esto es util para NC principalmente + + OnEstadoChanged(Estado); } public override void UpdateControl(int elapsedMilliseconds) diff --git a/ObjetosSim/SensoresComandos/ucPhotocell.xaml.cs b/ObjetosSim/SensoresComandos/ucPhotocell.xaml.cs index 8d7c9ff..a06515c 100644 --- a/ObjetosSim/SensoresComandos/ucPhotocell.xaml.cs +++ b/ObjetosSim/SensoresComandos/ucPhotocell.xaml.cs @@ -5,6 +5,7 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Media; using CommunityToolkit.Mvvm.ComponentModel; +using System.Diagnostics; namespace CtrEditor.ObjetosSim @@ -15,6 +16,9 @@ namespace CtrEditor.ObjetosSim public partial class osPhotocell : osBase, IosBase { private simBarrera Simulation_Photocell; + Stopwatch timer; + double timer_lastPositive; + double timer_lastNegative; public static string NombreClase() { @@ -42,9 +46,46 @@ namespace CtrEditor.ObjetosSim EscribirBitTag(TagPhotocell_OUT, !LuzCortada); else EscribirBitTag(TagPhotocell_OUT, LuzCortada); + if (filter_Frecuency < 1) + { + Filter_Frecuency = 1; + Frecuency = 0; + } + + if (!value) { + Lenght_positive_pulse = (float) (timer.ElapsedMilliseconds- timer_lastPositive); + timer_lastNegative = timer.ElapsedMilliseconds; + } else + { + Lenght_negative_pulse = (float)(timer.ElapsedMilliseconds - timer_lastNegative); + timer_lastPositive = timer.ElapsedMilliseconds; + Lenght_FP_to_FP = Lenght_positive_pulse + Lenght_negative_pulse; + Frecuency = (Frecuency * (filter_Frecuency - 1) + (1000 / Lenght_FP_to_FP)) / filter_Frecuency; + } } + [ObservableProperty] + float filter_Frecuency; + + partial void OnFilter_FrecuencyChanged(float value) + { + if (value<=0) + Filter_Frecuency = 10; + } + + [ObservableProperty] + float frecuency; + + [ObservableProperty] + float lenght_positive_pulse; + + [ObservableProperty] + float lenght_negative_pulse; + + [ObservableProperty] + float lenght_FP_to_FP; + [ObservableProperty] public bool tipo_NC; @@ -96,6 +137,9 @@ namespace CtrEditor.ObjetosSim { Ancho = 1; Alto = 0.03f; + Frecuency = 0; + timer = new Stopwatch(); + timer.Start(); } public override void UpdateGeometryStart() @@ -105,11 +149,14 @@ namespace CtrEditor.ObjetosSim } public override void UpdateControl(int elapsedMilliseconds) { - LuzCortada = Simulation_Photocell.LuzCortada; + LuzCortada = Simulation_Photocell.LuzCortada > 0; } - public override void UpdateGeometryStep() + public override void UpdatePLCPrimerCiclo() { - Simulation_Photocell.LuzCortada = false; + // Escribimos el valor actual al iniciar la conexion + // Esto es util para NC principalmente + + OnLuzCortadaChanged(LuzCortada); } public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds) { } diff --git a/ObjetosSim/SensoresComandos/ucSensTemperatura.xaml.cs b/ObjetosSim/SensoresComandos/ucSensTemperatura.xaml.cs index b41c58f..8e7f6f4 100644 --- a/ObjetosSim/SensoresComandos/ucSensTemperatura.xaml.cs +++ b/ObjetosSim/SensoresComandos/ucSensTemperatura.xaml.cs @@ -57,6 +57,11 @@ namespace CtrEditor.ObjetosSim { } + public override void UpdatePLCPrimerCiclo() + { + // Escribimos el valor actual al iniciar la conexion + OnValueChanged(Value); + } public override void ucLoaded() { diff --git a/ObjetosSim/TagsSignals/ucAnalogTag.xaml.cs b/ObjetosSim/TagsSignals/ucAnalogTag.xaml.cs index cd14584..2152702 100644 --- a/ObjetosSim/TagsSignals/ucAnalogTag.xaml.cs +++ b/ObjetosSim/TagsSignals/ucAnalogTag.xaml.cs @@ -78,6 +78,12 @@ namespace CtrEditor.ObjetosSim { } + public override void UpdatePLCPrimerCiclo() + { + // Escribimos el valor actual al iniciar la conexion + OnValueChanged(Value); + } + public override void UpdateControl(int elapsedMilliseconds) { // Calculamos la velocidad diff --git a/ObjetosSim/TagsSignals/ucBoolTag.xaml.cs b/ObjetosSim/TagsSignals/ucBoolTag.xaml.cs index 419700f..7355f4a 100644 --- a/ObjetosSim/TagsSignals/ucBoolTag.xaml.cs +++ b/ObjetosSim/TagsSignals/ucBoolTag.xaml.cs @@ -60,6 +60,12 @@ namespace CtrEditor.ObjetosSim Descripcion = "Nombre del Tag"; } + public override void UpdatePLCPrimerCiclo() + { + // Escribimos el valor actual al iniciar la conexion + OnEstadoChanged(Estado); + } + public override void ucLoaded() { // El UserControl ya se ha cargado y podemos obtener las coordenadas para diff --git a/ObjetosSim/Traces/ucTrace3.xaml b/ObjetosSim/Traces/ucTrace3.xaml new file mode 100644 index 0000000..1d450a7 --- /dev/null +++ b/ObjetosSim/Traces/ucTrace3.xaml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ObjetosSim/Traces/ucTrace3.xaml.cs b/ObjetosSim/Traces/ucTrace3.xaml.cs new file mode 100644 index 0000000..9366711 --- /dev/null +++ b/ObjetosSim/Traces/ucTrace3.xaml.cs @@ -0,0 +1,231 @@ +using CtrEditor.Convertidores; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; +using CommunityToolkit.Mvvm.ComponentModel; +using System.Windows.Shapes; +using CtrEditor.Siemens; +using System.Runtime.Intrinsics; + +namespace CtrEditor.ObjetosSim.Traces +{ + /// + /// Interaction logic for ucTrace3.xaml + /// + public partial class osTrace3 : osBase, IosBase + { + private List _series1 = new List(); + private List _series2 = new List(); + private List _series3 = new List(); + + // Otros datos y métodos relevantes para la simulación + + public static string NombreClase() + { + return "Trace3"; + } + private string nombre = NombreClase(); + public override string Nombre + { + get => nombre; + set => SetProperty(ref nombre, value); + } + + [ObservableProperty] + private Brush color_Serie_1; + [ObservableProperty] + private Brush color_Serie_2; + [ObservableProperty] + private Brush color_Serie_3; + + [ObservableProperty] + float alto; + [ObservableProperty] + float ancho; + + [ObservableProperty] + string tag_Serie1; + [ObservableProperty] + string tag_Serie2; + [ObservableProperty] + string tag_Serie3; + + [ObservableProperty] + string titulo; + + [ObservableProperty] + string descripcion_Serie_1; + [ObservableProperty] + string descripcion_Serie_2; + [ObservableProperty] + string descripcion_Serie_3; + + [ObservableProperty] + float min_Serie_1; + [ObservableProperty] + float max_Serie_1; + [ObservableProperty] + float min_Serie_2; + [ObservableProperty] + float max_Serie_2; + [ObservableProperty] + float min_Serie_3; + [ObservableProperty] + float max_Serie_3; + + [ObservableProperty] + float y_offset_1; + [ObservableProperty] + float y_offset_2; + [ObservableProperty] + float y_offset_3; + + + [ObservableProperty] + float max_Cantidad_Elementos; + + public osTrace3() + { + Alto = 3; + Ancho = 5; + Titulo = "Datos"; + Max_Cantidad_Elementos = 10; + Color_Serie_1 = Brushes.Black; + Color_Serie_2 = Brushes.Red; + Color_Serie_3 = Brushes.Blue; + Max_Serie_1 = 2; + Max_Serie_2 = 2; + Max_Serie_3 = 2; + } + + public void AddData(float series1Value, float series2Value, float series3Value) + { + if (_series1.Count >= Max_Cantidad_Elementos) + { + _series1.RemoveAt(0); + _series2.RemoveAt(0); + _series3.RemoveAt(0); + } + + _series1.Add(series1Value); + _series2.Add(series2Value); + _series3.Add(series3Value); + + DrawChart(); + } + + private void DrawChart() + { + if (VisualRepresentation != null) + if (VisualRepresentation is ucTrace3 uc) + { + uc.ChartCanvas.Children.Clear(); + + DrawSeries(uc.ChartCanvas,_series1, Brushes.Red,Min_Serie_1, Max_Serie_1, Y_offset_1); + DrawSeries(uc.ChartCanvas,_series2, Brushes.Green, Min_Serie_2, Max_Serie_2, Y_offset_2); + DrawSeries(uc.ChartCanvas,_series3, Brushes.Blue, Min_Serie_3, Max_Serie_3, Y_offset_3); + } + + } + + private void DrawSeries(Canvas ChartCanvas, List series, Brush color, float minY, float maxY, float yoffset) + { + if (series.Count < 2) return; + + foreach (var value in series) + { + if (value < minY) minY = value; + if (value > maxY) maxY = value; + } + if (minY == maxY) return; + + float canvasHeight = (float) ChartCanvas.ActualHeight; + float canvasWidth = (float) ChartCanvas.ActualWidth; + float scaleX = canvasWidth / Max_Cantidad_Elementos; + float scaleY = canvasHeight / (maxY - minY + yoffset); + + for (int i = 1; i < series.Count; i++) + { + float x1 = (i - 1) * scaleX; + float y1 = canvasHeight - (series[i - 1] - minY + yoffset) * scaleY; + float x2 = i * scaleX; + float y2 = canvasHeight - (series[i] - minY + yoffset) * scaleY; + + var line = new Line + { + Stroke = color, + StrokeThickness = 2, + X1 = x1, + Y1 = y1, + X2 = x2, + Y2 = y2 + }; + + ChartCanvas.Children.Add(line); + } + } + + public override void UpdatePLCPrimerCiclo() + { + // Escribimos el valor actual al iniciar la conexion + } + public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds) + { + base.UpdatePLC(plc, elapsedMilliseconds); + + float v1 = LeerBitTag(Tag_Serie1) ? 0 : 1; + float v2 = LeerBitTag(Tag_Serie2) ? 0 : 1; + float v3 = LeerBitTag(Tag_Serie3) ? 0 : 1; + + AddData(v1, v2, v3); + } + public override void ucLoaded() + { + // El UserControl ya se ha cargado y podemos obtener las coordenadas para + // crear el objeto de simulacion + base.ucLoaded(); + + } + } + + public partial class ucTrace3 : UserControl, IDataContainer + { + public osBase? Datos { get; set; } + + public ucTrace3() + { + InitializeComponent(); + this.Loaded += OnLoaded; + this.Unloaded += OnUnloaded; + } + private void OnLoaded(object sender, RoutedEventArgs e) + { + Datos?.ucLoaded(); + } + private void OnUnloaded(object sender, RoutedEventArgs e) + { + Datos?.ucUnLoaded(); + } + public void Resize(float width, float height) { + if (Datos is osTrace3 datos) + { + datos.Ancho = PixelToMeter.Instance.calc.PixelsToMeters(width); + datos.Alto = PixelToMeter.Instance.calc.PixelsToMeters(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/ObjetosSim/Traces/ucTraceSimple.xaml b/ObjetosSim/Traces/ucTraceSimple.xaml new file mode 100644 index 0000000..5d9c50c --- /dev/null +++ b/ObjetosSim/Traces/ucTraceSimple.xaml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + diff --git a/ObjetosSim/Traces/ucTraceSimple.xaml.cs b/ObjetosSim/Traces/ucTraceSimple.xaml.cs new file mode 100644 index 0000000..876a561 --- /dev/null +++ b/ObjetosSim/Traces/ucTraceSimple.xaml.cs @@ -0,0 +1,168 @@ +using CtrEditor.Convertidores; +using CtrEditor.Siemens; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; +using Newtonsoft.Json; +using CommunityToolkit.Mvvm.ComponentModel; +using System.Collections.ObjectModel; +using LiveChartsCore; +using LiveChartsCore.SkiaSharpView; +using LiveChartsCore.Defaults; +using System.Diagnostics; + +namespace CtrEditor.ObjetosSim.Traces +{ + /// + /// Interaction logic for ucTraceSimple.xaml + /// + public partial class osTraceSimple : osBase, IosBase + { + + private List data; + Stopwatch stopwatch; + private double YScale { get; set; } + + // Otros datos y métodos relevantes para la simulación + + public static string NombreClase() + { + return "Trace"; + } + private string nombre = NombreClase(); + public override string Nombre + { + get => nombre; + set => SetProperty(ref nombre, value); + } + + [ObservableProperty] + float alto; + + [ObservableProperty] + float ancho; + + [ObservableProperty] + string tag_serie; + + [ObservableProperty] + string x_Series_Name; + + [ObservableProperty] + string y_Series_Name; + + private readonly ObservableCollection _observableValues; + + + public ObservableCollection Series { get; set; } + + public osTraceSimple() + { + // Use ObservableCollections to let the chart listen for changes (or any INotifyCollectionChanged). + _observableValues = new ObservableCollection(); + Series = new ObservableCollection + { + new LineSeries + { + Values = _observableValues, + Fill = null + } + }; + data = new List(); + stopwatch = new Stopwatch(); + + YScale = 2.0; + Alto = 1; + Ancho = 1; + } + + public override void UpdateGeometryStart() + { + // Se llama antes de la simulacion + + } + + public override void UpdatePLCPrimerCiclo() + { + stopwatch.Start(); + } + + public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds) + { + if (Tag_serie != null && Tag_serie.Length > 0) { + var value = LeerBitTag(Tag_serie) == false ? 0 : 1; + data.Add(value); + } + if (data.Count > 50) + data.RemoveAt(0); + if (stopwatch.ElapsedMilliseconds > 1000) + { + stopwatch.Reset(); + stopwatch.Start(); + foreach (var v in data) + { + _observableValues.Add(new(v)); + if (_observableValues.Count > 50) + _observableValues.RemoveAt(0); + } + + } + } + + public override void UpdateControl(int elapsedMilliseconds) + { + // Calculamos la velocidad + + } + + public override void ucLoaded() + { + // El UserControl ya se ha cargado y podemos obtener las coordenadas para + // crear el objeto de simulacion + ActualizarLeftTop(); + + } + } + + public partial class ucTraceSimple : UserControl, IDataContainer + { + public osBase? Datos { get; set; } + + public ucTraceSimple() + { + InitializeComponent(); + this.Loaded += OnLoaded; + this.Unloaded += OnUnloaded; + } + private void OnLoaded(object sender, RoutedEventArgs e) + { + Datos?.ucLoaded(); + } + private void OnUnloaded(object sender, RoutedEventArgs e) + { + Datos?.ucUnLoaded(); + } + public void Resize(float width, float height) { + if (Datos is osTraceSimple datos) + { + datos.Ancho = PixelToMeter.Instance.calc.PixelsToMeters(width); + datos.Alto = PixelToMeter.Instance.calc.PixelsToMeters(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/ObjetosSim/osBase.cs b/ObjetosSim/osBase.cs index 698e9f5..f0a6e0f 100644 --- a/ObjetosSim/osBase.cs +++ b/ObjetosSim/osBase.cs @@ -26,8 +26,6 @@ using System.Windows.Media.Animation; namespace CtrEditor.ObjetosSim { - - public interface IosBase { static abstract string NombreClase(); @@ -50,7 +48,7 @@ namespace CtrEditor.ObjetosSim private UserControl? VisualRepresentation; private SimulationManagerFP? simulationManager; - public DataSaveToSerialize(MainViewModel a, UserControl b, SimulationManagerFP c ) + public DataSaveToSerialize(MainViewModel a, UserControl b, SimulationManagerFP c) { _mainViewModel = a; VisualRepresentation = b; @@ -93,35 +91,62 @@ namespace CtrEditor.ObjetosSim public virtual void TopChanged(float value) { } - - public bool Inicializado = false; + /// + /// Usado para saber cuando un objeto fue creado manualmente o por algun generador + /// Mas adelante la idea es poder eliminar o tratar de manera diferente a estos objetos + /// public bool AutoCreated = false; + + /// + /// Son los objetos marcados para ser eliminados luego de se detectados por un sensor del mundo fisico + /// Se deben eliminar fuera de la simulacion + /// public bool RemoverDesdeSimulacion = false; // La simulacion indica que se debe remover [JsonIgnore] private DataSaveToSerialize DataSave; + /// + /// Link al UserControl. Inicializado en el evento OnLoaded + /// [JsonIgnore] protected UserControl? _visualRepresentation = null; + /// + /// Link al control PLC. + /// Inicializado en el metodo SetPLC + /// [JsonIgnore] protected PLCModel? _plc = null; /// - /// + /// Se llama luego de cada Step. Esto permite actualziar las posiciones graficas de cada objeto que se esta dibujando + /// con las nuevas posiciones calculadas luego de la simulacion fisica. /// /// public virtual void UpdateControl(int elapsedMilliseconds) { } + /// - /// Se llama antes de la simulacion + /// Se llama antes de comenzar la simulacion con el boton de Iniciar simulacion. + /// La idea es actualizar los objetos en el motor fisico antes de comenzar la simulacion fisica. + /// + public virtual void UpdateGeometryStart() { } + + /// + /// Se llama al terminar la simulacion con el boton de Detener simulacion. + /// Se puede utilizar para detener los storyboard o alguna otra simulacion independiente + /// + public virtual void SimulationStop() { } + + /// + /// Se llama antes de cada Step al World de la simulacion fisica. + /// Permite actualizar el estado de los objetos de la simulacion fisica /// - public virtual void UpdateGeometryStart() { } - public virtual void SimulationStop() { } public virtual void UpdateGeometryStep() { } /// /// Es llamada en cada Tick del reloj establecido del PLC - /// cuando la conexion con el PLC esta establecida + /// cuando la conexion con el PLC esta establecida. /// /// /// @@ -140,38 +165,70 @@ namespace CtrEditor.ObjetosSim /// El UserControl se esta eliminando /// eliminar el objeto de simulacion /// - public virtual void ucUnLoaded() { } + public virtual void ucUnLoaded() { } [JsonIgnore] public MainViewModel _mainViewModel; + /// + /// Link al UserControl. Inicializado en el evento OnLoaded + /// [JsonIgnore] public UserControl? VisualRepresentation { get => _visualRepresentation; set => _visualRepresentation = value; } + + /// + /// Link al Simualdor fisico. + /// [JsonIgnore] public SimulationManagerFP simulationManager; + + /// + /// Prepara la clase para ser serializable poniendo a null los objetos que tienen referencias circulares + /// Luego se debe llamar a RestaurarDatosNoSerializables para restaurar el estado original + /// public void SalvarDatosNoSerializables() { - DataSave = new DataSaveToSerialize(_mainViewModel,_visualRepresentation,simulationManager); + DataSave = new DataSaveToSerialize(_mainViewModel, _visualRepresentation, simulationManager); _mainViewModel = null; _visualRepresentation = null; simulationManager = null; } + + /// + /// Restaura los links de los objetos que se eliminaron temporalmente para serializar. + /// public void RestaurarDatosNoSerializables() { if (DataSave == null) return; - DataSave.DataRestoreAfterSerialize(out _mainViewModel,out _visualRepresentation,out simulationManager); + DataSave.DataRestoreAfterSerialize(out _mainViewModel, out _visualRepresentation, out simulationManager); } - + /// + /// Se llama una unica vez al conectar con el PLC. + /// + /// public void SetPLC(PLCModel plc) { _plc = plc; + UpdatePLCPrimerCiclo(); } + /// + /// Se llama una unica vez al conectar con el PLC + /// + public virtual void UpdatePLCPrimerCiclo() { } + + /// + /// Crea una conexion entre los osBase segun el NameLink sea igual al nombre de algun osBase siempre que se cumpla + /// con el requisito que el osBase encontrado sea de tipo tipoOsBase + /// + /// + /// + /// protected osBase ObtenerLink(string NameLink, Type tipoOsBase) { if (!string.IsNullOrEmpty(NameLink) && _mainViewModel != null) @@ -218,6 +275,9 @@ namespace CtrEditor.ObjetosSim _storyboard.SetSpeedRatio(velocidadActual); } + /// + /// Actualiza Left Top del objeto respecto al Canvas padre. + /// public void ActualizarLeftTop() { CanvasSetLeftinMeter(Left); @@ -237,6 +297,11 @@ namespace CtrEditor.ObjetosSim return false; } + /// + /// Si la conexion con el plc esta activa y el Tag no es nulo escribe el bit en el PLC + /// + /// + /// public void EscribirBitTag(string Tag, bool Value) { if (_plc == null) return; @@ -380,9 +445,9 @@ namespace CtrEditor.ObjetosSim simRect.Create(Ancho, Alto, GetRectangleCenter(wpfRect), Angulo); } - public void UpdateCurve(simCurve curva,float RadioInterno, float RadioExterno, float startAngle, float endAngle) + public void UpdateCurve(simCurve curva, float RadioInterno, float RadioExterno, float startAngle, float endAngle) { - curva.Create(RadioInterno, RadioExterno, startAngle, endAngle, GetCurveCenterInMeter(RadioExterno)); + curva.Create(RadioInterno, RadioExterno, startAngle, endAngle, GetCurveCenterInMeter(RadioExterno)); } public simCurve AddCurve(float RadioInterno, float RadioExterno, float startAngle, float endAngle) diff --git a/Simulacion/Aether.cs b/Simulacion/Aether.cs index 8ad34e0..e278b78 100644 --- a/Simulacion/Aether.cs +++ b/Simulacion/Aether.cs @@ -223,7 +223,7 @@ namespace CtrEditor.Simulacion public class simBarrera : simBase { - public bool LuzCortada = false; + public int LuzCortada; private List _deferredActions; public simBarrera(World world, List deferredActions, float width, float height, Vector2 position, float angle = 0) @@ -260,7 +260,7 @@ namespace CtrEditor.Simulacion Body.BodyType = BodyType.Static; Body.Rotation = Microsoft.Xna.Framework.MathHelper.ToRadians(angle); Body.Tag = this; // Importante para la identificación durante la colisión - LuzCortada = false; + LuzCortada = 0; } } @@ -295,20 +295,20 @@ namespace CtrEditor.Simulacion public bool Descartar = false; public int isOnTransports; + public List ListOnTransports; public bool isRestricted; public bool isNoMoreRestricted; public float OriginalMass; public simTransporte ConveyorRestrictedTo; public Fixture axisRestrictedBy; - public Vector2 Speed; - PrismaticJoint _activeJoint; private List _deferredActions; public simBotella(World world, List deferredActions, float diameter, Vector2 position, float mass) { _world = world; + ListOnTransports = new List(); _deferredActions = deferredActions; _radius = diameter / 2; _mass = mass; @@ -362,9 +362,9 @@ namespace CtrEditor.Simulacion Body.SetFriction(0.5f); // Configurar amortiguamiento - Body.LinearDamping = 4f; // Ajustar para controlar la reducción de la velocidad lineal + Body.LinearDamping = 1f; // Ajustar para controlar la reducción de la velocidad lineal Body.AngularDamping = 1f; // Ajustar para controlar la reducción de la velocidad angular - Body.SetRestitution(0.3f); // Baja restitución para menos rebote + Body.SetRestitution(0.1f); // Baja restitución para menos rebote Body.SleepingAllowed = false; Body.IsBullet = true; @@ -385,7 +385,7 @@ namespace CtrEditor.Simulacion { if (fixtureB.Body.Tag is simBarrera Sensor) { - Sensor.LuzCortada = true; + Sensor.LuzCortada += 1; return true; } else if (fixtureB.Body.Tag is simCurve curve) @@ -403,20 +403,16 @@ namespace CtrEditor.Simulacion simTransporte conveyor = fixtureB.Body.Tag as simTransporte; isOnTransports += 1; + ListOnTransports.Add(conveyor); - if (conveyor.Speed != 0) + // Aplicar el efecto del transportador usando el porcentaje calculado + if (conveyor.TransportWithGuides && conveyor.isBrake) { - // Aplicar el efecto del transportador usando el porcentaje calculado - if (conveyor.TransportWithGuides && conveyor.isBrake) - { - ConveyorRestrictedTo = conveyor; - axisRestrictedBy = fixtureB; - OriginalMass = Body.Mass; - isRestricted = true; - isNoMoreRestricted = false; - } - - ApplyConveyorEffect(conveyor, fixtureA, 1); + ConveyorRestrictedTo = conveyor; + axisRestrictedBy = fixtureB; + OriginalMass = Body.Mass; + isRestricted = true; + isNoMoreRestricted = false; } return true; // No aplicar respuestas físicas } @@ -443,21 +439,35 @@ namespace CtrEditor.Simulacion private void HandleOnSeparation(Fixture sender, Fixture fixtureB, Contact contact) { - if (isOnTransports>0 && fixtureB.Body.Tag is simTransporte) + if (isOnTransports > 0 && fixtureB.Body.Tag is simTransporte) + { + if (fixtureB.Body.Tag is simTransporte transport) + ListOnTransports.Remove(transport); isOnTransports -= 1; + } if (isRestricted && fixtureB == axisRestrictedBy) { isRestricted = false; isNoMoreRestricted = true; } + if (fixtureB.Body.Tag is simBarrera Sensor) + Sensor.LuzCortada -= 1; } - private void ApplyConveyorEffect(simTransporte conveyor, Fixture circleFixture, float porcentajeCompartido) + public void ApplyConveyorSpeed() + { + Body.LinearVelocity = new Vector2(0.0f, 0.0f); + foreach (var conveyor in ListOnTransports) + { + ApplyConveyorEffect(conveyor); + } + } + + private void ApplyConveyorEffect(simTransporte conveyor) { float speedMetersPerSecond = conveyor.Speed / 60.0f; Vector2 desiredVelocity = new Vector2((float)Math.Cos(conveyor.Body.Rotation), (float)Math.Sin(conveyor.Body.Rotation)) * speedMetersPerSecond; - // circleFixture.Body.ApplyForce(desiredVelocity * porcentajeCompartido); - Speed = desiredVelocity; + Body.LinearVelocity += desiredVelocity; } public void CenterFixtureOnConveyor() @@ -505,6 +515,7 @@ namespace CtrEditor.Simulacion private Canvas simulationCanvas; public List Cuerpos; public List _deferredActions; + SolverIterations Iterations; private Stopwatch stopwatch; private double stopwatch_last; @@ -514,6 +525,11 @@ namespace CtrEditor.Simulacion public SimulationManagerFP() { world = new World(new Vector2(0, 0)); // Vector2.Zero + + SolverIterations Iterations = new SolverIterations(); + Iterations.PositionIterations = 1; + Iterations.VelocityIterations = 1; + Cuerpos = new List(); _deferredActions = new List(); stopwatch = new Stopwatch(); @@ -538,14 +554,13 @@ namespace CtrEditor.Simulacion // Pasar el tiempo transcurrido al método Step world.Step(elapsedMilliseconds / 1000.0f); - - + foreach (var cuerpo in Cuerpos) { if (cuerpo is simBotella botella) { - if (botella.isOnTransports > 0) - botella.Body.LinearVelocity = botella.Speed; + botella.ApplyConveyorSpeed(); + if (botella.isRestricted) { botella.CenterFixtureOnConveyor();