Compare commits

...

2 Commits

20 changed files with 830 additions and 85 deletions

View File

@ -6,9 +6,83 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Data; using System.Windows.Data;
using System.Windows; using System.Windows;
using System.Windows.Media;
namespace CtrEditor.Convertidores namespace CtrEditor.Convertidores
{ {
public class WidthPercentageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is double width)
{
return width * 0.25;
}
return 0;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class LevelToHeightMultiConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values[0] is float level && values[1] is double containerHeight)
{
return containerHeight * (level / 100);
}
return 0;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class BrushToColorNameConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is SolidColorBrush brush)
{
if (brush == Brushes.Red) return "Rojo";
if (brush == Brushes.Blue) return "Azul";
if (brush == Brushes.Black) return "Negro";
if (brush == Brushes.Green) return "Verde";
if (brush == Brushes.Gray) return "Gris";
}
return "Unknown";
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is string colorName)
{
switch (colorName)
{
case "Rojo":
return Brushes.Red;
case "Azul":
return Brushes.Blue;
case "Negro":
return Brushes.Black;
case "Verde":
return Brushes.Green;
case "Gris":
return Brushes.Gray;
}
}
return Brushes.Transparent;
}
}
public class PixelToMeter public class PixelToMeter
{ {
// Instancia privada estática, parte del patrón Singleton // Instancia privada estática, parte del patrón Singleton

View File

@ -9,19 +9,24 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Remove="ObjetosSim\ucBasicExample.xaml.cs" />
<Compile Remove="ObjetosSim\ucTransporteCurva.xaml.cs" /> <Compile Remove="ObjetosSim\ucTransporteCurva.xaml.cs" />
<Compile Remove="Simulacion\GeometrySimulator.cs" /> <Compile Remove="Simulacion\GeometrySimulator.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Remove="motor2.png" /> <None Remove="motor2.png" />
<None Remove="tank.png" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Page Remove="ObjetosSim\ucBasicExample.xaml" />
<Page Remove="ObjetosSim\ucTransporteCurva.xaml" /> <Page Remove="ObjetosSim\ucTransporteCurva.xaml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="ObjetosSim\ucBasicExample.xaml" />
<None Include="ObjetosSim\ucBasicExample.xaml.cs" />
<None Include="ObjetosSim\ucTransporteCurva.xaml" /> <None Include="ObjetosSim\ucTransporteCurva.xaml" />
<None Include="ObjetosSim\ucTransporteCurva.xaml.cs" /> <None Include="ObjetosSim\ucTransporteCurva.xaml.cs" />
<None Include="Simulacion\GeometrySimulator.cs" /> <None Include="Simulacion\GeometrySimulator.cs" />
@ -44,6 +49,7 @@
<ItemGroup> <ItemGroup>
<Resource Include="motor2.png" /> <Resource Include="motor2.png" />
<Resource Include="tank.png" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -1,4 +1,5 @@
using System; using CtrEditor;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Linq; using System.Linq;
@ -25,6 +26,8 @@ using System.Windows;
using static System.Resources.ResXFileRef; using static System.Resources.ResXFileRef;
using CtrEditor.Convertidores; using CtrEditor.Convertidores;
using CtrEditor.Simulacion; using CtrEditor.Simulacion;
using System.Diagnostics;
using Newtonsoft.Json.Linq;
namespace CtrEditor namespace CtrEditor
{ {
@ -36,6 +39,7 @@ namespace CtrEditor
public ObservableCollection<TipoSimulable> ListaOsBase { get; } = new ObservableCollection<TipoSimulable>(); public ObservableCollection<TipoSimulable> ListaOsBase { get; } = new ObservableCollection<TipoSimulable>();
private ObservableCollection<osBase> _objetosSimulables = new ObservableCollection<osBase>(); private ObservableCollection<osBase> _objetosSimulables = new ObservableCollection<osBase>();
public PLCViewModel _plcViewModelData; public PLCViewModel _plcViewModelData;
public Stopwatch stopwatch;
public SimulationManagerFP simulationManager = new SimulationManagerFP(); public SimulationManagerFP simulationManager = new SimulationManagerFP();
@ -70,6 +74,8 @@ namespace CtrEditor
StartSimulationCommand = new RelayCommand(StartSimulation); StartSimulationCommand = new RelayCommand(StartSimulation);
StopSimulationCommand = new RelayCommand(StopSimulation); StopSimulationCommand = new RelayCommand(StopSimulation);
stopwatch = new Stopwatch();
} }
public void LoadInitialData() public void LoadInitialData()
@ -155,11 +161,21 @@ namespace CtrEditor
private void OnRefreshEvent(object sender, EventArgs e) private void OnRefreshEvent(object sender, EventArgs e)
{ {
foreach (var objetoSimulable in ObjetosSimulables) if (_plcViewModelData.IsConnected)
{ {
if (_plcViewModelData.IsConnected) // Detener el cronómetro y obtener el tiempo transcurrido en milisegundos
objetoSimulable.UpdatePLC(_plcViewModelData.PLCInterface); 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) //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() public void SaveStateObjetosSimulables()
{ {
if (_selectedImage != null) if (_selectedImage != null)
{ {
StopSimulation();
PLCViewModel.Disconnect();
var settings = new JsonSerializerSettings var settings = new JsonSerializerSettings
{ {
Formatting = Formatting.Indented, Formatting = Formatting.Indented,
@ -274,6 +299,7 @@ namespace CtrEditor
{ {
obj.VisualRepresentation = null; obj.VisualRepresentation = null;
obj.simulationManager = null; obj.simulationManager = null;
obj._mainViewModel = null;
} }
// Crear un objeto que incluya tanto los ObjetosSimulables como el UnitConverter // Crear un objeto que incluya tanto los ObjetosSimulables como el UnitConverter
var dataToSerialize = new SimulationData var dataToSerialize = new SimulationData
@ -292,6 +318,8 @@ namespace CtrEditor
{ {
try try
{ {
StopSimulation();
PLCViewModel.Disconnect();
ObjetosSimulables.Clear(); ObjetosSimulables.Clear();
simulationManager.Clear(); simulationManager.Clear();
if (_selectedImage != null) if (_selectedImage != null)
@ -321,6 +349,9 @@ namespace CtrEditor
PixelToMeter.Instance.calc = simulationData.UnitConverter; PixelToMeter.Instance.calc = simulationData.UnitConverter;
// Re-register to the events
_plcViewModelData.RefreshEvent += OnRefreshEvent;
// Recorrer la colección de objetos simulables // Recorrer la colección de objetos simulables
foreach (var objetoSimulable in ObjetosSimulables) foreach (var objetoSimulable in ObjetosSimulables)
CrearUsercontrol(objetoSimulable); CrearUsercontrol(objetoSimulable);
@ -343,6 +374,7 @@ namespace CtrEditor
{ {
// Asignar los datos al UserControl // Asignar los datos al UserControl
UserControlFactory.AssignDatos(userControl, osObjeto, simulationManager); UserControlFactory.AssignDatos(userControl, osObjeto, simulationManager);
osObjeto._mainViewModel = this;
OnUserControlSelected?.Invoke(userControl); OnUserControlSelected?.Invoke(userControl);
@ -353,7 +385,6 @@ namespace CtrEditor
// Implementación de INotifyPropertyChanged... // Implementación de INotifyPropertyChanged...
public event PropertyChangedEventHandler PropertyChanged; public event PropertyChangedEventHandler PropertyChanged;
@ -362,7 +393,21 @@ namespace CtrEditor
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 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 public class SimulationData
{ {

View File

@ -11,6 +11,7 @@
<Window.Resources> <Window.Resources>
<convert:FloatToFormattedStringConverter x:Key="floatFormatter"/> <convert:FloatToFormattedStringConverter x:Key="floatFormatter"/>
<convert:BrushToColorNameConverter x:Key="BrushToColorNameConverter"/>
</Window.Resources> </Window.Resources>
<Window.DataContext> <Window.DataContext>

View File

@ -21,6 +21,7 @@ using TextBox = System.Windows.Controls.TextBox;
using UserControl = System.Windows.Controls.UserControl; using UserControl = System.Windows.Controls.UserControl;
using CheckBox = System.Windows.Controls.CheckBox; using CheckBox = System.Windows.Controls.CheckBox;
using Orientation = System.Windows.Controls.Orientation; using Orientation = System.Windows.Controls.Orientation;
using ListBox = System.Windows.Controls.ListBox;
//using OpenCvSharp; //using OpenCvSharp;
@ -418,6 +419,11 @@ namespace CtrEditor
foreach (var property in properties) foreach (var property in properties)
{ {
if (Attribute.IsDefined(property, typeof(HiddenAttribute)))
{
continue;
}
var grid = new Grid(); var grid = new Grid();
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto }); grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) }); grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
@ -480,11 +486,61 @@ namespace CtrEditor
grid.Children.Add(label); grid.Children.Add(label);
grid.Children.Add(checkBox); grid.Children.Add(checkBox);
} }
else if (property.PropertyType == typeof(Brush))
{
var listBox = new ListBox
{
ItemsSource = new List<string> { "Rojo", "Azul", "Negro", "Verde", "Gris" },
Margin = new Thickness(0),
MinWidth = 200
};
listBox.SelectionChanged += (sender, e) =>
{
if (listBox.SelectedItem != null)
{
switch (listBox.SelectedItem.ToString())
{
case "Rojo":
property.SetValue(selectedObject, Brushes.Red);
break;
case "Azul":
property.SetValue(selectedObject, Brushes.Blue);
break;
case "Negro":
property.SetValue(selectedObject, Brushes.Black);
break;
case "Verde":
property.SetValue(selectedObject, Brushes.Green);
break;
case "Gris":
property.SetValue(selectedObject, Brushes.Gray);
break;
}
}
};
var binding = new Binding(property.Name)
{
Source = selectedObject,
Mode = BindingMode.TwoWay,
Converter = new BrushToColorNameConverter()
};
listBox.SetBinding(ListBox.SelectedItemProperty, binding);
Grid.SetColumn(label, 0);
Grid.SetColumn(listBox, 1);
grid.Children.Add(label);
grid.Children.Add(listBox);
}
PanelEdicion.Children.Add(grid); PanelEdicion.Children.Add(grid);
} }
} }
private void MainWindow_Closed(object sender, EventArgs e) private void MainWindow_Closed(object sender, EventArgs e)
{ {
if (DataContext is MainViewModel viewModel) if (DataContext is MainViewModel viewModel)

View File

@ -27,6 +27,8 @@ namespace CtrEditor.ObjetosSim
return new ucVMmotorSim(); return new ucVMmotorSim();
if (tipoObjeto == typeof(osBoton)) if (tipoObjeto == typeof(osBoton))
return new ucBoton(); return new ucBoton();
if (tipoObjeto == typeof(osTanque))
return new ucTanque();
// Puedes añadir más condiciones para otros tipos // Puedes añadir más condiciones para otros tipos
@ -49,6 +51,8 @@ namespace CtrEditor.ObjetosSim
return new osVMmotorSim(); return new osVMmotorSim();
if (tipoObjeto == typeof(osBoton)) if (tipoObjeto == typeof(osBoton))
return new osBoton(); return new osBoton();
if (tipoObjeto == typeof(osTanque))
return new osTanque();
// Puedes añadir más condiciones para otros tipos // Puedes añadir más condiciones para otros tipos

View File

@ -54,9 +54,12 @@ namespace CtrEditor.ObjetosSim
public abstract void UpdateControl(); public abstract void UpdateControl();
public abstract void UpdateGeometryStart(); public abstract void UpdateGeometryStart();
public abstract void UpdateGeometryStep(); public abstract void UpdateGeometryStep();
public abstract void UpdatePLC(PLCModel plc); public abstract void UpdatePLC(PLCModel plc, int elapsedMilliseconds);
public abstract void ucLoaded(); public abstract void ucLoaded();
[JsonIgnore]
public MainViewModel _mainViewModel;
[JsonIgnore] [JsonIgnore]
public UserControl? VisualRepresentation public UserControl? VisualRepresentation
{ {
@ -185,4 +188,8 @@ namespace CtrEditor.ObjetosSim
} }
} }
[AttributeUsage(AttributeTargets.Property)]
public class HiddenAttribute : Attribute
{
}
} }

View File

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

View File

@ -0,0 +1,167 @@
using CtrEditor.Convertidores;
using CtrEditor.Siemens;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace CtrEditor.ObjetosSim
{
/// <summary>
/// Interaction logic for ucBasicExample.xaml
/// </summary>
public class osBasicExample : osBase
{
// Otros datos y métodos relevantes para la simulación
private string _nombre = "Ejemplo";
private float _ancho;
private float _alto;
private float _left;
private float _top;
private float _angulo;
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 osBasicExample()
{
Ancho = 0.30f;
Alto = 0.30f;
}
public override void UpdateGeometryStart()
{
// Se llama antes de la simulacion
}
public override void UpdateGeometryStep()
{
}
public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds)
{
}
public override void UpdateControl()
{
}
public override void ucLoaded()
{
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
}
}
public partial class ucBasicExample : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public ucBasicExample()
{
InitializeComponent();
this.Loaded += OnLoaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
Datos?.ucLoaded();
}
public void Resize(float width, float height) {
if (Datos is osBasicExample 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 osBasicExample datos)
datos.Angulo = Angle;
}
public void Highlight(bool State) { }
public int ZIndex()
{
return 10;
}
}
}

View File

@ -139,7 +139,7 @@ namespace CtrEditor.ObjetosSim
ActualizarGeometrias(); ActualizarGeometrias();
} }
public override void UpdatePLC(PLCModel plc) { } public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds) { }
public override void UpdateControl() public override void UpdateControl()
{ {

View File

@ -13,37 +13,24 @@
</UserControl.Resources> </UserControl.Resources>
<Grid> <Grid>
<Rectangle x:Name="BackgroundRectangle" <Border x:Name="BackgroundRectangle"
Fill="LightGray" BorderBrush="Black"
Width="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}}" BorderThickness="2"
Height="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}, ConverterParameter=1.5}" CornerRadius="0,0,10,10"
VerticalAlignment="Top"
HorizontalAlignment="Left"
/>
<Button Content="Button"
Width="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}}" Width="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}}"
Height="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}}" Height="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}, ConverterParameter=1.5}"
Background="{Binding Color}" VerticalAlignment="Top"
HorizontalAlignment="Left" HorizontalAlignment="Left"
VerticalAlignment="Bottom" Background="Gray"/>
MouseLeftButtonDown="Button_MouseLeftButtonDown" <Ellipse Fill="{Binding ColorButton}"
MouseLeftButtonUp="Button_MouseLeftButtonUp"> Stroke="Black"
<i:Interaction.Triggers> StrokeThickness="2"
<i:EventTrigger EventName="MouseLeftButtonDown"> Width="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}}"
<ei:InvokeCommandAction Command="{Binding ButtonDownCommand}" /> Height="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}}"
</i:EventTrigger> HorizontalAlignment="Left"
<i:EventTrigger EventName="MouseLeftButtonUp"> VerticalAlignment="Bottom"
<ei:InvokeCommandAction Command="{Binding ButtonUpCommand}" /> MouseLeftButtonDown="Ellipse_MouseLeftButtonDown"
</i:EventTrigger> MouseLeftButtonUp="Ellipse_MouseLeftButtonUp">
</i:Interaction.Triggers> </Ellipse>
<Button.Template>
<ControlTemplate TargetType="Button">
<Grid>
<Ellipse Fill="{TemplateBinding Background}" Stroke="{TemplateBinding BorderBrush}" StrokeThickness="2"/>
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</ControlTemplate>
</Button.Template>
</Button>
</Grid> </Grid>
</UserControl> </UserControl>

View File

@ -32,6 +32,8 @@ namespace CtrEditor.ObjetosSim
private string _tag; private string _tag;
private Brush _color; private Brush _color;
private Brush _colorButton;
public Brush Color public Brush Color
{ {
get => _color; get => _color;
@ -42,9 +44,16 @@ namespace CtrEditor.ObjetosSim
} }
} }
public ICommand ButtonClickCommand { get; } [Hidden]
public ICommand ButtonDownCommand { get; } public Brush ColorButton
public ICommand ButtonUpCommand { get; } {
get => _colorButton;
set
{
_colorButton = value;
OnPropertyChanged(nameof(ColorButton));
}
}
public override float Left public override float Left
{ {
@ -81,6 +90,10 @@ namespace CtrEditor.ObjetosSim
set set
{ {
_estado = value; _estado = value;
if (value)
ColorButton = Brushes.LightGreen;
else
ColorButton = Color;
OnPropertyChanged(nameof(Estado)); OnPropertyChanged(nameof(Estado));
} }
} }
@ -110,20 +123,11 @@ namespace CtrEditor.ObjetosSim
} }
} }
private void OnButtonClick() public void ButtonDownCommand()
{
// Handle the click event here
// Example: Change color on click
Color = Brushes.LightGreen;
}
private void OnButtonDown()
{ {
Estado = true; Estado = true;
} }
public void ButtonUpCommand()
private void OnButtonUp()
{ {
Estado = false; Estado = false;
} }
@ -135,11 +139,6 @@ namespace CtrEditor.ObjetosSim
Tag = "M50.0"; Tag = "M50.0";
// Set initial color // Set initial color
Color = Brushes.LightBlue; Color = Brushes.LightBlue;
// Initialize the command
ButtonClickCommand = new RelayCommand(OnButtonClick);
ButtonDownCommand = new RelayCommand(OnButtonDown);
ButtonUpCommand = new RelayCommand(OnButtonUp);
} }
public override void UpdateGeometryStart() public override void UpdateGeometryStart()
@ -150,9 +149,9 @@ namespace CtrEditor.ObjetosSim
public override void UpdateGeometryStep() 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() public override void UpdateControl()
@ -175,28 +174,37 @@ namespace CtrEditor.ObjetosSim
{ {
InitializeComponent(); InitializeComponent();
this.Loaded += OnLoaded; this.Loaded += OnLoaded;
this.DataContextChanged += OnDataContextChanged;
} }
private void OnLoaded(object sender, RoutedEventArgs e) private void OnLoaded(object sender, RoutedEventArgs e)
{ {
Datos?.ucLoaded(); 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) if (Datos is osBoton osBotonData)
{ {
osBotonData.ButtonDownCommand.Execute(null); osBotonData.ButtonDownCommand();
Mouse.Capture((UIElement)sender); 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) if (Datos is osBoton osBotonData)
{ {
osBotonData.ButtonUpCommand.Execute(null); osBotonData.ButtonUpCommand();
Mouse.Capture(null); // Release mouse capture e.Handled = true; // Evita que el evento se propague
} }
} }
public void Resize(float width, float height) { } public void Resize(float width, float height) { }
public void Move(float LeftPixels, float TopPixels) public void Move(float LeftPixels, float TopPixels)
{ {

View File

@ -121,7 +121,7 @@ namespace CtrEditor.ObjetosSim
public override void UpdateGeometryStep() public override void UpdateGeometryStep()
{ {
} }
public override void UpdatePLC(PLCModel plc) { } public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds) { }
public override void ucLoaded() public override void ucLoaded()
{ {
// El UserControl ya se ha cargado y podemos obtener las coordenadas para // El UserControl ya se ha cargado y podemos obtener las coordenadas para

39
ObjetosSim/ucTanque.xaml Normal file
View File

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

201
ObjetosSim/ucTanque.xaml.cs Normal file
View File

@ -0,0 +1,201 @@
using CtrEditor.Convertidores;
using CtrEditor.Siemens;
using OpenCvSharp.Flann;
using Siemens.Simatic.Simulation.Runtime;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace CtrEditor.ObjetosSim
{
/// <summary>
/// Interaction logic for ucTanque.xaml
/// </summary>
public class osTanque : osBase
{
// Otros datos y métodos relevantes para la simulación
private string _nombre = "Tanque";
private float _ancho;
private float _alto;
private float _left;
private float _top;
private float _angulo;
private float _level;
private string _tag;
public string Tag
{
get => _tag;
set
{
if (_tag != value)
{
_tag = value;
OnPropertyChanged(nameof(Tag));
}
}
}
public float Level
{
get => _level;
set
{
_level = value;
OnPropertyChanged(nameof(Level));
}
}
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 osTanque()
{
Ancho = 0.30f;
Alto = 0.30f;
}
public override void UpdateGeometryStart()
{
// Se llama antes de la simulacion
}
public override void UpdateGeometryStep()
{
}
public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds)
{
if (Tag.Length > 0)
{
SDataValue s = new SDataValue();
s.UInt16 = (ushort)(Level / 100.0 * 26600);
plc.EscribirTag(Tag, s);
}
}
public override void UpdateControl()
{
}
public override void ucLoaded()
{
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
}
}
public partial class ucTanque : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public ucTanque()
{
InitializeComponent();
this.Loaded += OnLoaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
Datos?.ucLoaded();
}
public void Resize(float width, float height)
{
if (Datos is osTanque datos)
{
datos.Ancho = PixelToMeter.Instance.calc.PixelsToMeters(width);
datos.Alto = PixelToMeter.Instance.calc.PixelsToMeters(width);
}
}
public void Move(float LeftPixels, float TopPixels)
{
if (Datos != null)
{
Datos.Left = PixelToMeter.Instance.calc.PixelsToMeters(LeftPixels);
Datos.Top = PixelToMeter.Instance.calc.PixelsToMeters(TopPixels);
}
}
public void Rotate(float Angle)
{
if (Datos != null)
if (Datos is osTanque datos)
datos.Angulo = Angle;
}
public void Highlight(bool State) { }
public int ZIndex()
{
return 10;
}
}
}

View File

@ -15,6 +15,7 @@ using System.Windows.Shapes;
using CtrEditor.Convertidores; using CtrEditor.Convertidores;
using CtrEditor.Siemens; using CtrEditor.Siemens;
using CtrEditor.Simulacion; using CtrEditor.Simulacion;
using static System.Runtime.InteropServices.JavaScript.JSType;
namespace CtrEditor.ObjetosSim namespace CtrEditor.ObjetosSim
{ {
@ -39,12 +40,28 @@ namespace CtrEditor.ObjetosSim
private float _top; private float _top;
private float _angulo; private float _angulo;
private float _velocidadActual; private float _velocidadActual;
private osBase _osMotor = null;
private string _motor;
private simRectangle? TransporteCentral; private simRectangle? TransporteCentral;
private simLine? Guia_Superior; private simLine? Guia_Superior;
private simLine? Guia_Inferior; private simLine? Guia_Inferior;
public string Motor
{
get => _motor;
set
{
if (_motor != value)
{
_motor = value;
OnPropertyChanged(nameof(Motor));
}
}
}
public override float Left public override float Left
{ {
get => _left; get => _left;
@ -179,7 +196,24 @@ namespace CtrEditor.ObjetosSim
public override void UpdateControl() 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() public override void ucLoaded()
{ {
// El UserControl ya se ha cargado y podemos obtener las coordenadas para // 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_Superior = AddLine(simulationManager, uc.GuiaSuperior);
Guia_Inferior = AddLine(simulationManager, uc.GuiaInferior); Guia_Inferior = AddLine(simulationManager, uc.GuiaInferior);
} }
Motor = Motor; // Forzar la busqueda
} }

View File

@ -27,10 +27,23 @@ namespace CtrEditor.ObjetosSim
private float _top; private float _top;
private float _angulo; private float _angulo;
private float _velocidadActual; private float _velocidadActual;
private osBase _osMotor = null;
private string _motor;
private simRectangle Simulation_Transporte; private simRectangle Simulation_Transporte;
public string Motor
{
get => _motor;
set
{
if (_motor != value)
{
_motor = value;
OnPropertyChanged(nameof(Motor));
}
}
}
public override float Left public override float Left
{ {
get => _left; get => _left;
@ -132,8 +145,25 @@ namespace CtrEditor.ObjetosSim
public override void UpdateGeometryStep() public override void UpdateGeometryStep()
{ {
} }
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 UpdateControl() public override void UpdateControl()
{ {

View File

@ -51,6 +51,9 @@ namespace CtrEditor.ObjetosSim
private float _ratio; private float _ratio;
private float _velocidad; private float _velocidad;
private bool _encendido; private bool _encendido;
private float _rampaSegundos;
private float _maxHz;
public VMSimMotor motState = new VMSimMotor(); 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 public bool Encendido
{ {
get => _encendido; get => _encendido;
@ -139,6 +165,8 @@ namespace CtrEditor.ObjetosSim
{ {
Tamano = 0.30f; Tamano = 0.30f;
PLC_NumeroMotor = 31; PLC_NumeroMotor = 31;
MaxRatedHz = 100;
TiempoRampa = 3;
} }
public override void UpdateGeometryStart() public override void UpdateGeometryStart()
@ -149,7 +177,7 @@ namespace CtrEditor.ObjetosSim
public override void UpdateGeometryStep() public override void UpdateGeometryStep()
{ {
} }
public override void UpdatePLC(PLCModel plc) { public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds) {
var index = 0; var index = 0;
switch (PLC_NumeroMotor) switch (PLC_NumeroMotor)
{ {
@ -162,6 +190,8 @@ namespace CtrEditor.ObjetosSim
motState.OUT_Reversal = plc.LeerTagBool($"\"DB MotorSimulate\".Motors[{index}].OUT.\"Reversal Direction\""); 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.OUT_OUT_VFD_REQ_Speed_Hz = (float)plc.LeerTagInt16($"\"DB MotorSimulate\".Motors[{index}].OUT.OUT_VFD_REQ_Speed_Hz");
if (Encendido) if (Encendido)
{ {
motState._STATUS_VFD_Ready = true; motState._STATUS_VFD_Ready = true;
@ -169,19 +199,19 @@ namespace CtrEditor.ObjetosSim
motState.STATUS_VFD_Trip = false; motState.STATUS_VFD_Trip = false;
motState.STATUS_VFD_Warning = false; motState.STATUS_VFD_Warning = false;
motState.STATUS_VFD_Coasting = 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._STATUS_VFD_Ready = false;
motState.Motor_Running = false; motState.Motor_Running = false;
motState.STATUS_VFD_Trip = true; motState.STATUS_VFD_Trip = true;
motState.STATUS_VFD_Warning = false; motState.STATUS_VFD_Warning = false;
motState.STATUS_VFD_Coasting = 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}].STATUS_VFD_Ready", motState._STATUS_VFD_Ready);
plc.EscribirTagBool($"\"DB MotorSimulate\".Motors[{index}].Motor_Running", motState.Motor_Running); 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_Trip", motState.STATUS_VFD_Trip);
@ -193,6 +223,19 @@ namespace CtrEditor.ObjetosSim
Velocidad = motState.STATUS_VFD_ACT_Speed_Hz/10; 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() public override void UpdateControl()
{ {

View File

@ -42,7 +42,7 @@ namespace CtrEditor.Siemens
{ {
IsConnected = false; IsConnected = false;
PLCInterface = new PLCModel(); PLCInterface = new PLCModel();
_timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(1000) }; _timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(10) };
_timer.Tick += (s, e) => Refresh(); _timer.Tick += (s, e) => Refresh();
ConnectCommand = new RelayCommand(Connect, () => true); ConnectCommand = new RelayCommand(Connect, () => true);
@ -102,7 +102,7 @@ namespace CtrEditor.Siemens
} }
} }
private void Connect() public void Connect()
{ {
// Implementa la conexión utilizando PLCModel // Implementa la conexión utilizando PLCModel
PLCInterface.Instance = SimulationRuntimeManager.CreateInterface(Name); PLCInterface.Instance = SimulationRuntimeManager.CreateInterface(Name);
@ -123,7 +123,7 @@ namespace CtrEditor.Siemens
PLCInterface.UpdateTagList(); PLCInterface.UpdateTagList();
} }
private void Disconnect() public void Disconnect()
{ {
IsConnected = false; IsConnected = false;
_timer.Stop(); _timer.Stop();
@ -137,10 +137,10 @@ namespace CtrEditor.Siemens
{ {
CpuTime = PLCInterface.LeerTagInt16("\"DB HMI\".CPU_Scan_Time")?.ToString() ?? "N/A"; CpuTime = PLCInterface.LeerTagInt16("\"DB HMI\".CPU_Scan_Time")?.ToString() ?? "N/A";
LastError = PLCInterface.LastError; LastError = PLCInterface.LastError;
}
// Disparar el evento RefreshEvent // Disparar el evento RefreshEvent
RefreshEvent?.Invoke(this, EventArgs.Empty); RefreshEvent?.Invoke(this, EventArgs.Empty);
}
} }
private void OnPropertyChanged([CallerMemberName] string propertyName = null) private void OnPropertyChanged([CallerMemberName] string propertyName = null)
@ -193,7 +193,29 @@ namespace CtrEditor.Siemens
LastError = ex.Message; LastError = ex.Message;
} }
} }
public void EscribirTag(string pTag, SDataValue Value)
{
try
{
Instance?.Write(pTag, Value);
}
catch (Exception ex)
{
LastError = pTag + ":" + ex.Message;
}
}
public SDataValue LeerTag(string pTag)
{
try
{
return Instance.Read(pTag);
}
catch (Exception ex)
{
LastError = pTag + ":" + ex.Message;
return new SDataValue();
}
}
public void EscribirTagBool(string pTag, bool pValue) public void EscribirTagBool(string pTag, bool pValue)
{ {
try try

BIN
tank.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB