Creado Encoder Lineal. Corregido error de inicio de tiempo en simulacion. Creado Frame Plate para que se muevan los objetos con un encoder lineal. Agregado a los transportes la actualizacion de geometrias en caso de que sean movidos por la interfaz.

This commit is contained in:
Miguel 2025-01-04 10:34:19 +01:00
parent 353b4b99e6
commit 9f41401e40
19 changed files with 668 additions and 16 deletions

View File

@ -75,14 +75,14 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Aether.Physics2D" Version="2.1.0" /> <PackageReference Include="Aether.Physics2D" Version="2.1.0" />
<PackageReference Include="ClosedXML" Version="0.104.0-preview2" /> <PackageReference Include="ClosedXML" Version="0.104.2" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" /> <PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageReference Include="Emgu.CV" Version="4.9.0.5494" /> <PackageReference Include="Emgu.CV" Version="4.9.0.5494" />
<PackageReference Include="Emgu.CV.runtime.windows" Version="4.9.0.5494" /> <PackageReference Include="Emgu.CV.runtime.windows" Version="4.9.0.5494" />
<PackageReference Include="Emgu.CV.UI" Version="4.9.0.5494" /> <PackageReference Include="Emgu.CV.UI" Version="4.9.0.5494" />
<PackageReference Include="Extended.Wpf.Toolkit" Version="4.6.0" /> <PackageReference Include="Extended.Wpf.Toolkit" Version="4.6.1" />
<PackageReference Include="LiveChartsCore.SkiaSharpView.WPF" Version="2.0.0-rc2" /> <PackageReference Include="LiveChartsCore.SkiaSharpView.WPF" Version="2.0.0-rc4.5" />
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.122" /> <PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.135" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Ookii.Dialogs.Wpf" Version="5.0.1" /> <PackageReference Include="Ookii.Dialogs.Wpf" Version="5.0.1" />
<PackageReference Include="Tesseract" Version="5.2.0" /> <PackageReference Include="Tesseract" Version="5.2.0" />

View File

@ -704,6 +704,7 @@ namespace CtrEditor
Debug_SimulacionCreado = true; Debug_SimulacionCreado = true;
_timerSimulacion.Start(); _timerSimulacion.Start();
simulationManager.Start();
} }
private void StopSimulation() private void StopSimulation()

View File

@ -0,0 +1,28 @@
<UserControl x:Class="CtrEditor.ObjetosSim.ucFramePlate"
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:i="http://schemas.microsoft.com/xaml/behaviors" xmlns:vm="clr-namespace:CtrEditor.ObjetosSim"
mc:Ignorable="d">
<UserControl.DataContext>
<vm:osFramePlate Color_Titulo="Black" Alto_Titulo="0.1" Color="WhiteSmoke" />
</UserControl.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Viewbox Grid.Row="0" Height="{Binding Alto_Titulo, Converter={StaticResource MeterToPixelConverter}}">
<Label Content="{Binding Titulo}" VerticalAlignment="Center" HorizontalAlignment="Center" FontWeight="Bold"
Foreground="{Binding Color_Titulo, Converter={StaticResource ColorToBrushConverter}}" />
</Viewbox>
<Rectangle Grid.Row="1" Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}"
Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}}"
Fill="{Binding Color, Converter={StaticResource ColorToBrushConverter}}" Stroke="Blue"
StrokeThickness="0.2" />
</Grid>
</UserControl>

View File

@ -0,0 +1,153 @@
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using CommunityToolkit.Mvvm.ComponentModel;
using CtrEditor.FuncionesBase;
using DocumentFormat.OpenXml.Spreadsheet;
using Newtonsoft.Json;
using Xceed.Wpf.Toolkit.PropertyGrid.Attributes;
using Color = System.Windows.Media.Color;
using Colors = System.Windows.Media.Colors;
using JsonIgnoreAttribute = Newtonsoft.Json.JsonIgnoreAttribute;
namespace CtrEditor.ObjetosSim
{
/// <summary>
/// Interaction logic for ucFramePlate.xaml
/// </summary>
///
public partial class osFramePlate : osBase, IosBase
{
[JsonIgnore]
public float offsetY;
[JsonIgnore]
public float offsetX;
public static string NombreClase()
{
return "Frame Plate";
}
private string nombre = NombreClase();
public override string Nombre
{
get => nombre;
set => SetProperty(ref nombre, value);
}
[ObservableProperty]
Color color;
[ObservableProperty]
Color color_Titulo;
[ObservableProperty]
string titulo;
[ObservableProperty]
public float alto_Titulo;
// Encoder
[ObservableProperty]
[property: Description("This is a link to a Encoder for X.")]
[property: Category("Encoders:")]
[property: ItemsSource(typeof(osBaseItemsSource<osEncoderMotorLineal>))]
private string encoder_X;
[ObservableProperty]
[property: Description("K Pulses per meter for Moving")]
[property: Category("Encoders:")]
public float k_encoder_X;
[ObservableProperty]
[property: Description("X in meter offset Left. Position when the encoder is 0")]
[property: Category("Encoders:")]
public float offset_encoder_X;
[JsonIgnore]
private osEncoderMotorLineal EncoderX;
[JsonIgnore]
private float EncoderXValue;
partial void OnEncoder_XChanged(string value)
{
if (_mainViewModel != null && value != null && value.Length > 0)
{
EncoderX = (osEncoderMotorLineal)_mainViewModel.ObjetosSimulables.FirstOrDefault(s => (s is osEncoderMotorLineal && s.Nombre == value), null);
}
}
public override void UpdateControl(int elapsedMilliseconds)
{
if (EncoderX == null)
return;
if (K_encoder_X == 0)
return;
if (EncoderXValue != EncoderX.Valor_Actual)
Left = (EncoderX.Valor_Actual / k_encoder_X) + offset_encoder_X;
EncoderXValue = EncoderX.Valor_Actual;
}
public override void TopChanging(float oldValue, float newValue)
{
offsetY = newValue - oldValue;
}
public override void LeftChanging(float oldValue, float newValue)
{
offsetX = newValue - oldValue;
}
public osFramePlate()
{
Ancho = 0.5f;
Alto = 0.5f;
Alto_Titulo = 0.2f;
Color = Colors.WhiteSmoke;
Titulo = "Frame";
}
public override void ucLoaded()
{
base.ucLoaded();
// El UserControl se ha cargado
OnEncoder_XChanged(Encoder_X);
}
public override void ucUnLoaded()
{
base.ucUnLoaded();
// El UserControl se esta eliminando
// eliminar el objeto de simulacion
}
}
public partial class ucFramePlate : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public ucFramePlate()
{
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 Highlight(bool State) { }
public ZIndexEnum ZIndex()
{
return ZIndexEnum.Decorativos;
}
}
}

View File

@ -99,6 +99,7 @@ namespace CtrEditor.ObjetosSim
Diametro = 0.10f; Diametro = 0.10f;
Mass = 1; Mass = 1;
ColorButton_oculto = Brushes.Gray; ColorButton_oculto = Brushes.Gray;
Preserve_Outside_Transport = true;
} }
public void UpdateAfterMove() public void UpdateAfterMove()

View File

@ -35,6 +35,10 @@ namespace CtrEditor.ObjetosSim
[ObservableProperty] [ObservableProperty]
private float offsetTopSalida; private float offsetTopSalida;
[ObservableProperty]
[property: Description("The bottle will be destroyed if fall outside transport.")]
[property: Category("Enable to Run:")]
private bool preserve_Outside_Transport;
[ObservableProperty] [ObservableProperty]
[property: Description("PLC tag for consense to run. 1 => always")] [property: Description("PLC tag for consense to run. 1 => always")]
@ -103,6 +107,7 @@ namespace CtrEditor.ObjetosSim
Botellas_hora = 10000; Botellas_hora = 10000;
Filtro_consenso_ON_s = 1; Filtro_consenso_ON_s = 1;
Filtro_consenso_OFF_s = 0.1f; Filtro_consenso_OFF_s = 0.1f;
Preserve_Outside_Transport = false;
} }
public override void UpdatePLC(PLCViewModel plc, int elapsedMilliseconds) public override void UpdatePLC(PLCViewModel plc, int elapsedMilliseconds)
@ -144,6 +149,8 @@ namespace CtrEditor.ObjetosSim
// No hay botellas, se puede crear una nueva directamente // No hay botellas, se puede crear una nueva directamente
var nuevaBotella = _mainViewModel.CrearObjetoSimulable(typeof(osBotella), X, Y); var nuevaBotella = _mainViewModel.CrearObjetoSimulable(typeof(osBotella), X, Y);
((osBotella)nuevaBotella).Diametro = Diametro_botella; ((osBotella)nuevaBotella).Diametro = Diametro_botella;
((osBotella)nuevaBotella).Preserve_Outside_Transport = Preserve_Outside_Transport;
((osBotella)nuevaBotella).AutoCreated = true;
nuevaBotella.AutoCreated = true; nuevaBotella.AutoCreated = true;
UltimaBotella = (osBotella)nuevaBotella; UltimaBotella = (osBotella)nuevaBotella;
BotellaCreada = true; BotellaCreada = true;
@ -158,6 +165,7 @@ namespace CtrEditor.ObjetosSim
{ {
osBotella nuevaBotella = (osBotella)_mainViewModel.CrearObjetoSimulable(typeof(osBotella), X, Y); osBotella nuevaBotella = (osBotella)_mainViewModel.CrearObjetoSimulable(typeof(osBotella), X, Y);
nuevaBotella.Diametro = Diametro_botella; nuevaBotella.Diametro = Diametro_botella;
((osBotella)nuevaBotella).Preserve_Outside_Transport = Preserve_Outside_Transport;
nuevaBotella.AutoCreated = true; nuevaBotella.AutoCreated = true;
UltimaBotella = nuevaBotella; UltimaBotella = nuevaBotella;
BotellaCreada = true; BotellaCreada = true;

View File

@ -110,6 +110,11 @@ namespace CtrEditor.ObjetosSim
} }
} }
public override void OnMoveResizeRotate()
{
ActualizarGeometrias();
}
[ObservableProperty] [ObservableProperty]
public float frictionCoefficient; public float frictionCoefficient;
[ObservableProperty] [ObservableProperty]

View File

@ -16,8 +16,8 @@
</VisualBrush.Transform> </VisualBrush.Transform>
<VisualBrush.Visual> <VisualBrush.Visual>
<Canvas> <Canvas>
<Rectangle Fill="#FFBFBFBF" Width="10" Height="10"/> <Rectangle Fill="LightGray" Width="10" Height="10" />
<Rectangle Fill="LightGray" Width="10" Height="10" Canvas.Left="10"/> <Rectangle Fill="GhostWhite" Width="10" Height="10" Canvas.Left="10" />
</Canvas> </Canvas>
</VisualBrush.Visual> </VisualBrush.Visual>
</VisualBrush> </VisualBrush>
@ -55,7 +55,7 @@
Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}" Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}"
Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}}" Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}}"
Stretch="Uniform"> Stretch="Uniform">
<Label Content="{Binding Nombre}" VerticalAlignment="Center" HorizontalAlignment="Center" FontWeight="Bold" FontSize="18" Opacity="0.5"/> <Label Content="{Binding Nombre}" VerticalAlignment="Center" HorizontalAlignment="Center" FontWeight="Bold" FontSize="18" Opacity="0.9"/>
</Viewbox> </Viewbox>
</Canvas> </Canvas>

View File

@ -135,6 +135,10 @@ namespace CtrEditor.ObjetosSim
} }
} }
public override void OnMoveResizeRotate()
{
ActualizarGeometrias();
}
public osTransporteGuias() public osTransporteGuias()
{ {

View File

@ -227,6 +227,11 @@ namespace CtrEditor.ObjetosSim
} }
} }
public override void OnMoveResizeRotate()
{
ActualizarGeometrias();
}
public osTransporteGuiasUnion() public osTransporteGuiasUnion()
{ {

View File

@ -17,8 +17,8 @@
</VisualBrush.Transform> </VisualBrush.Transform>
<VisualBrush.Visual> <VisualBrush.Visual>
<Canvas> <Canvas>
<Rectangle Fill="Gray" Width="10" Height="10"/> <Rectangle Fill="LightGray" Width="10" Height="10"/>
<Rectangle Fill="DarkGray" Width="10" Height="10" Canvas.Left="10"/> <Rectangle Fill="GhostWhite" Width="10" Height="10" Canvas.Left="10"/>
</Canvas> </Canvas>
</VisualBrush.Visual> </VisualBrush.Visual>
</VisualBrush> </VisualBrush>
@ -28,14 +28,25 @@
<vm:osTransporteTTop Ancho="2"/> <vm:osTransporteTTop Ancho="2"/>
</UserControl.DataContext> </UserControl.DataContext>
<Canvas> <Canvas RenderTransformOrigin="0,0">
<Canvas.RenderTransform>
<TransformGroup>
<ScaleTransform />
<SkewTransform />
<RotateTransform Angle="{Binding Angulo}" />
<TranslateTransform />
</TransformGroup>
</Canvas.RenderTransform>
<Rectangle x:Name="Transporte" <Rectangle x:Name="Transporte"
Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}" Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}"
Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}}" Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}}"
Fill="{StaticResource BeltBrush}"> Fill="{StaticResource BeltBrush}">
<Rectangle.RenderTransform>
<RotateTransform Angle="{Binding Angulo}"/>
</Rectangle.RenderTransform>
</Rectangle> </Rectangle>
<Viewbox Canvas.Top="{Binding AltoGuia, Converter={StaticResource MeterToPixelConverter}}"
Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}"
Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}}" Stretch="Uniform">
<Label Content="{Binding Nombre}" VerticalAlignment="Center" HorizontalAlignment="Center" FontWeight="Bold"
FontSize="18" Opacity="0.9" />
</Viewbox>
</Canvas> </Canvas>
</UserControl> </UserControl>

View File

@ -116,6 +116,11 @@ namespace CtrEditor.ObjetosSim
ActualizarAnimacionStoryBoardTransporte(VelocidadActual); ActualizarAnimacionStoryBoardTransporte(VelocidadActual);
} }
public override void OnMoveResizeRotate()
{
ActualizarGeometrias();
}
public osTransporteTTop() public osTransporteTTop()
{ {
Ancho = 1; Ancho = 1;

View File

@ -86,6 +86,10 @@ namespace CtrEditor.ObjetosSim
[ObservableProperty] [ObservableProperty]
public float velocidad; public float velocidad;
[ObservableProperty]
public bool sentido_contrario;
partial void OnVelocidadChanged(float value) partial void OnVelocidadChanged(float value)
{ {
if (value > 0) if (value > 0)
@ -113,6 +117,7 @@ namespace CtrEditor.ObjetosSim
{ {
motState.UpdatePLC(plc, this, elapsedMilliseconds); motState.UpdatePLC(plc, this, elapsedMilliseconds);
Velocidad = (Proporcional_Speed / 100) * (motState.STATUS_VFD_ACT_Speed_Hz / 10); Velocidad = (Proporcional_Speed / 100) * (motState.STATUS_VFD_ACT_Speed_Hz / 10);
Sentido_contrario = motState.OUT_Reversal;
} }
public override void UpdateControl(int elapsedMilliseconds) public override void UpdateControl(int elapsedMilliseconds)

View File

@ -0,0 +1,16 @@
<UserControl x:Class="CtrEditor.ObjetosSim.ucEncoderMotor"
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:vm="clr-namespace:CtrEditor.ObjetosSim"
mc:Ignorable="d">
<UserControl.DataContext>
<vm:osEncoderMotor />
</UserControl.DataContext>
<Grid Width="50" Height="50">
<Ellipse Fill="{Binding Color_oculto}" Stroke="DarkGray" StrokeThickness="2" Width="30" Height="30" />
</Grid>
</UserControl>

View File

@ -0,0 +1,160 @@
// EncoderMotorViewModel.cs
using CommunityToolkit.Mvvm.ComponentModel;
using System.Windows.Media;
using System.ComponentModel;
using LibS7Adv;
using System.Diagnostics;
using Xceed.Wpf.Toolkit.PropertyGrid.Attributes;
using CtrEditor.FuncionesBase;
using System.Windows;
using System.Windows.Controls;
namespace CtrEditor.ObjetosSim
{
public partial class osEncoderMotor : osBase, IosBase
{
private osBase Motor = null;
private Stopwatch Stopwatch = new Stopwatch();
private double stopwatch_last = 0;
public static string NombreClase()
{
return "Encoder Motor";
}
private string nombre = NombreClase();
public override string Nombre
{
get => nombre;
set => SetProperty(ref nombre, value);
}
[ObservableProperty]
private Brush color_oculto;
[ObservableProperty]
public float velocidadActual;
[ObservableProperty]
[property: Description("Pulsos por vuelta del encoder")]
[property: Category("Encoder Config:")]
public float pulsos_Por_Vuelta;
[ObservableProperty]
[property: Description("Ratio de giros por 50Hz")]
[property: Category("Encoder Config:")]
public float ratio_Giros_50hz;
[ObservableProperty]
[property: Description("Valor actual del encoder")]
[property: Category("Encoder Status:")]
public float valor_Actual;
[ObservableProperty]
[property: Description("Link to Motor")]
[property: Category("PLC link:")]
[property: ItemsSource(typeof(osBaseItemsSource<osVMmotorSim>))]
string id_Motor;
[ObservableProperty]
[property: Description("Tag para escribir el valor del encoder")]
[property: Category("PLC link:")]
string tag_Valor;
partial void OnId_MotorChanged(string value)
{
if (Motor != null)
Motor.PropertyChanged -= OnMotorPropertyChanged;
if (_mainViewModel != null && value != null && value.Length > 0)
{
Motor = (osVMmotorSim)_mainViewModel.ObjetosSimulables.FirstOrDefault(s => (s is osVMmotorSim && s.Nombre == value), null);
if (Motor != null)
Motor.PropertyChanged += OnMotorPropertyChanged;
}
}
private void OnMotorPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(osVMmotorSim.Nombre))
{
Id_Motor = ((osVMmotorSim)sender).Nombre;
}
}
public osEncoderMotor()
{
Pulsos_Por_Vuelta = 360; // Por defecto, un pulso por grado
Ratio_Giros_50hz = 1; // Por defecto, 1 giro por cada 50Hz
Color_oculto = Brushes.Gray;
Stopwatch.Start();
}
public override void UpdatePLC(PLCViewModel plc, int elapsedMilliseconds)
{
if (Motor != null && Motor is osVMmotorSim motor)
{
VelocidadActual = motor.Velocidad;
// Calcular giros por segundo basado en la velocidad actual
// velocidad * ratio_giros_50hz / 100 nos da los giros por segundo a esa velocidad
float girosPorSegundo = (VelocidadActual * Ratio_Giros_50hz) / 100f;
// Considerar el sentido de giro
if (motor.Sentido_contrario)
girosPorSegundo = -girosPorSegundo;
// Calcular incremento de pulsos para este ciclo
float segundosTranscurridos = elapsedMilliseconds / 1000f;
float incrementoPulsos = girosPorSegundo * Pulsos_Por_Vuelta * segundosTranscurridos;
// Actualizar valor del encoder
Valor_Actual = (Valor_Actual + incrementoPulsos) % Pulsos_Por_Vuelta;
if (Valor_Actual < 0) Valor_Actual += Pulsos_Por_Vuelta;
// Actualizar color basado en si está girando
Color_oculto = Math.Abs(girosPorSegundo) > 0.01f ? Brushes.LightGreen : Brushes.Gray;
// Escribir valor al PLC si hay tag configurado
if (!string.IsNullOrEmpty(Tag_Valor))
{
EscribirWordTagScaled(Tag_Valor, Valor_Actual, 0, Pulsos_Por_Vuelta, 0, 27648);
}
}
}
public override void ucLoaded()
{
base.ucLoaded();
OnId_MotorChanged(Id_Motor);
}
}
public partial class ucEncoderMotor : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public ucEncoderMotor()
{
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 Highlight(bool State) { }
public ZIndexEnum ZIndex()
{
return ZIndexEnum.Estaticos;
}
}
}

View File

@ -0,0 +1,16 @@
<UserControl x:Class="CtrEditor.ObjetosSim.ucEncoderMotorLineal"
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:vm="clr-namespace:CtrEditor.ObjetosSim"
mc:Ignorable="d">
<UserControl.DataContext>
<vm:osEncoderMotorLineal />
</UserControl.DataContext>
<Grid Width="50" Height="50">
<Ellipse Fill="{Binding Color_oculto}" Stroke="DarkGray" StrokeThickness="2" Width="30" Height="30" />
</Grid>
</UserControl>

View File

@ -0,0 +1,152 @@
// EncoderMotorLinealViewModel.cs
using CommunityToolkit.Mvvm.ComponentModel;
using System.Windows.Media;
using System.ComponentModel;
using LibS7Adv;
using System.Diagnostics;
using Xceed.Wpf.Toolkit.PropertyGrid.Attributes;
using CtrEditor.FuncionesBase;
using System.Windows;
using System.Windows.Controls;
namespace CtrEditor.ObjetosSim
{
public partial class osEncoderMotorLineal : osBase, IosBase
{
private osBase Motor = null;
private Stopwatch Stopwatch = new Stopwatch();
private double stopwatch_last = 0;
public static string NombreClase()
{
return "Encoder Motor Lineal";
}
private string nombre = NombreClase();
public override string Nombre
{
get => nombre;
set => SetProperty(ref nombre, value);
}
[ObservableProperty]
private Brush color_oculto;
[ObservableProperty]
public float velocidadActual;
[ObservableProperty]
[property: Description("Pulsos por Hz por segundo : K")]
[property: Category("Encoder Config:")]
public float pulsos_Por_Hz;
[ObservableProperty]
[property: Description("Valor actual del encoder")]
[property: Category("Encoder Status:")]
public float valor_Actual;
[ObservableProperty]
[property: Description("Link to Motor")]
[property: Category("PLC link:")]
[property: ItemsSource(typeof(osBaseItemsSource<osVMmotorSim>))]
string id_Motor;
[ObservableProperty]
[property: Description("Tag para escribir el valor del encoder")]
[property: Category("PLC link:")]
string tag_Valor;
partial void OnId_MotorChanged(string value)
{
if (Motor != null)
Motor.PropertyChanged -= OnMotorPropertyChanged;
if (_mainViewModel != null && value != null && value.Length > 0)
{
Motor = (osVMmotorSim)_mainViewModel.ObjetosSimulables.FirstOrDefault(s => (s is osVMmotorSim && s.Nombre == value), null);
if (Motor != null)
Motor.PropertyChanged += OnMotorPropertyChanged;
}
}
private void OnMotorPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(osVMmotorSim.Nombre))
{
Id_Motor = ((osVMmotorSim)sender).Nombre;
}
}
public osEncoderMotorLineal()
{
Pulsos_Por_Hz = 3000; // Por defecto, un pulso por grado
Color_oculto = Brushes.Gray;
Stopwatch.Start();
}
public override void UpdatePLC(PLCViewModel plc, int elapsedMilliseconds)
{
if (Motor != null && Motor is osVMmotorSim motor)
{
VelocidadActual = motor.Velocidad;
// Calcular giros por segundo basado en la velocidad actual
float pulsosPorHz = (VelocidadActual * Pulsos_Por_Hz) / 100f;
// Considerar el sentido de giro
if (motor.Sentido_contrario)
pulsosPorHz = -pulsosPorHz;
// Calcular incremento de pulsos para este ciclo
float segundosTranscurridos = elapsedMilliseconds / 1000f;
float incrementoPulsos = pulsosPorHz * segundosTranscurridos;
// Actualizar valor del encoder
Valor_Actual = (Valor_Actual + incrementoPulsos);
// Actualizar color basado en si está girando
Color_oculto = Math.Abs(pulsosPorHz) > 0.01f ? Brushes.LightGreen : Brushes.Gray;
// Escribir valor al PLC si hay tag configurado
if (!string.IsNullOrEmpty(Tag_Valor))
{
EscribirDINTTag(tag_Valor, (int)Valor_Actual);
}
}
}
public override void ucLoaded()
{
base.ucLoaded();
OnId_MotorChanged(Id_Motor);
}
}
public partial class ucEncoderMotorLineal : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public ucEncoderMotorLineal()
{
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 Highlight(bool State) { }
public ZIndexEnum ZIndex()
{
return ZIndexEnum.Estaticos;
}
}
}

View File

@ -53,12 +53,26 @@ namespace CtrEditor.ObjetosSim
b = VisualRepresentation; b = VisualRepresentation;
c = simulationManager; c = simulationManager;
} }
} }
public abstract partial class osBase : ObservableObject public abstract partial class osBase : ObservableObject
{ {
public virtual string Nombre { get; set; } = "osBase"; public virtual string Nombre { get; set; } = "osBase";
public osBase()
{
if (float.IsNaN(Left))
{
Left = 0;
}
if (float.IsNaN(Top))
{
Top = 0;
}
}
[JsonIgnore] [JsonIgnore]
private Storyboard _storyboard; private Storyboard _storyboard;
[JsonIgnore] [JsonIgnore]
@ -210,7 +224,7 @@ namespace CtrEditor.ObjetosSim
Id = new UniqueId().ObtenerNuevaID(); Id = new UniqueId().ObtenerNuevaID();
} }
// Group // Group as FacePlate
[ObservableProperty] [ObservableProperty]
[property: Description("This is a link to a faceplate. It works like an anchor.")] [property: Description("This is a link to a faceplate. It works like an anchor.")]
[property: Category("Group:")] [property: Category("Group:")]
@ -253,6 +267,56 @@ namespace CtrEditor.ObjetosSim
} }
} }
// Group as FacePlate
[ObservableProperty]
[property: Description("This is a link to a FromPlate that moves automatically. It works like an anchor.")]
[property: Category("Group:")]
[property: ItemsSource(typeof(osBaseItemsSource<osFramePlate>))]
private string group_FramePanel;
[JsonIgnore]
private osFramePlate FramePlate;
[JsonIgnore]
private bool isUpdatingFromFramePlate = false;
partial void OnGroup_FramePanelChanged(string value)
{
if (FramePlate != null)
FramePlate.PropertyChanged -= OnFramePlatePropertyChanged;
if (_mainViewModel != null && value != null && value.Length > 0)
{
FramePlate = (osFramePlate)_mainViewModel.ObjetosSimulables.FirstOrDefault(s => (s is osFramePlate && s.Nombre == value), null);
if (FramePlate != null)
FramePlate.PropertyChanged += OnFramePlatePropertyChanged;
}
}
private void OnFramePlatePropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (!isUpdatingFromFramePlate)
{
isUpdatingFromFramePlate = true;
if (e.PropertyName == nameof(osFramePlate.Nombre))
Group_Panel = ((osFramePlate)sender).Nombre;
if (e.PropertyName == nameof(osFramePlate.Top))
{
Top += ((osFramePlate)sender).offsetY;
OnMoveResizeRotate();
}
if (e.PropertyName == nameof(osFramePlate.Left))
{
Left += ((osFramePlate)sender).offsetX;
OnMoveResizeRotate();
}
isUpdatingFromFramePlate = false;
}
}
private void ShowPreviewWindow(Stream imageStream) private void ShowPreviewWindow(Stream imageStream)
{ {
// Create a new window for preview // Create a new window for preview
@ -535,6 +599,7 @@ namespace CtrEditor.ObjetosSim
{ {
ActualizarLeftTop(); ActualizarLeftTop();
OnGroup_PanelChanged(Group_Panel); // Establece el link y se suscribe a los eventos OnGroup_PanelChanged(Group_Panel); // Establece el link y se suscribe a los eventos
OnGroup_FramePanelChanged(Group_FramePanel); // Establece el link y se suscribe a los eventos
Show_On_This_Page = Show_On_This_Page; // Update data Show_On_This_Page = Show_On_This_Page; // Update data
} }
@ -687,6 +752,17 @@ namespace CtrEditor.ObjetosSim
_plc.EscribirBool(Tag, Value); _plc.EscribirBool(Tag, Value);
} }
public void EscribirDINTTag(string Tag, float Value)
{
if (_plc == null) return;
if (!string.IsNullOrEmpty(Tag))
{
SDataValue plcData = new SDataValue();
plcData.Int32 = (int)Value;
_plc.EscribirTag(Tag, plcData);
}
}
public void EscribirWordTagScaled(string Tag, float Value, float IN_scale_Min, float IN_scale_Max, float OUT_scale_Min, float OUT_scale_Max) public void EscribirWordTagScaled(string Tag, float Value, float IN_scale_Min, float IN_scale_Max, float OUT_scale_Min, float OUT_scale_Max)
{ {
if (_plc == null) return; if (_plc == null) return;

View File

@ -726,6 +726,12 @@ namespace CtrEditor.Simulacion
// ****************************************************************************************************************************************** // ******************************************************************************************************************************************
// ****************************************************************************************************************************************** // ******************************************************************************************************************************************
public void Start()
{
stopwatch.Start();
stopwatch_last = stopwatch.Elapsed.TotalMilliseconds;
}
public void Step() public void Step()
{ {
// Detener el cronómetro y obtener el tiempo transcurrido en milisegundos // Detener el cronómetro y obtener el tiempo transcurrido en milisegundos