Compare commits

...

55 Commits

Author SHA1 Message Date
Miguel 304bdb06d4 Agregado posibilidad al Frame de moverse horizontal y verticalmente. Agregado TransporteDualInverter para que sea comandado por dos inverters seleccionables desde un tag. Agregada funcionalidad de cambio de tamaño a las curvas. 2025-03-07 11:00:27 +01:00
Miguel f264efd9ce Cambiado Path de teseract a absoluto en la carpeta de la aplicacion 2025-03-01 23:28:29 +01:00
Miguel 3d70992b1a Creado Control osVisFilter 2025-02-26 11:37:19 +01:00
Miguel b6b078f8ce Cambiado funcionamiento de BuscarCoincidencias para guardar el clip original. 2025-02-25 21:36:07 +01:00
Miguel 3fe845b02f Creado Panel de Edicion de Propiedades para multiples objetos. Multiinstancia. 2025-02-25 14:34:11 +01:00
Miguel e14c28920f Creada una UserControl para PanelEdicion que permita la edicion de los objetos 2025-02-25 11:10:58 +01:00
Miguel 8a5ebe6ac6 Creado un UserControl con los objetos en TreeView para simplificar la seleccion de los objetos 2025-02-24 21:39:15 +01:00
Miguel 621ee8be39 CustomImage con imagen por defecto. Creada visualizcion de tiempo de ciclo. Modificada logica de Preserve_Outside_Transport. Agregada opcion a osFramePlate de showPlate 2025-02-24 16:33:27 +01:00
Miguel 5e95459e3e Agregado un nuevo objeto CustomImage 2025-02-24 11:37:52 +01:00
Miguel 5f680b3a7a Mejorado de las curvas. Se creo un overlapPercentage para las curvas. 2025-02-23 21:22:42 +01:00
Miguel d06607ccc6 Rectangulo de seleccion funcionando para seleccionar multiples objetos 2025-02-21 22:25:14 +01:00
Miguel 061007158d Agregada clase de Serializacion para separar la logica del MainViewModel 2025-02-21 15:12:10 +01:00
Miguel 0a52c543e6 Marcado en purpura el objeto tomado de referencia 2025-02-20 13:17:03 +01:00
Miguel 38ca212d9f Agregado movimiento con flechas de los objetos seleccionados 2025-02-19 21:27:33 +01:00
Miguel 326c615887 Agregado EqualWidth, EqualHeight, EqualAngle, JoinHorizontally, JoinVertically 2025-02-19 14:57:15 +01:00
Miguel 5ee91dd26a Mejorado de panning y zoom 2025-02-18 21:52:27 +01:00
Miguel 67c6464ea1 Mejorada la seleccion de objetos multiples 2025-02-18 18:37:46 +01:00
Miguel 3dab570f5d Multiseleccion funcionando 2025-02-18 18:08:55 +01:00
Miguel 89745d15bf Mejorada la seleccion de objetos. 2025-02-17 15:16:40 +01:00
Miguel 633cd71d00 Mejorado la descripcion de las extracciones de Tag con Patrones 2025-02-17 13:04:21 +01:00
Miguel 8573d942c4 Agregado exportacion a Excel desde la ventana de Analizar la Matriz. Agregada la correccion de las columnas de descripcion. 2025-02-15 22:38:12 +01:00
Miguel 3a2e87cd75 Agregada funcion de analizar la matriz de exportacion de tags. Creada un submenu para cargar los ultimos directorios ustilizados. Cambiado intercambio de datos para los Motores simulados a DINT 2025-02-14 14:04:29 +01:00
Miguel dc164e96ef Agregadas las opciones de consultar si guardar luego de hacer modificaciones y agregado el uso de la tecla Delete para borrar elementos 2025-02-13 16:52:33 +01:00
Miguel e63577e5c3 Modificado Zindex para soportar los Frame 2025-02-13 14:00:47 +01:00
Miguel 9f41401e40 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. 2025-01-04 10:34:19 +01:00
Miguel 353b4b99e6 Agregada la funcion de crear el archivo base.png si no hay imagenes en el directorio elegido 2024-09-12 16:43:39 +02:00
Miguel 261fe679d8 Mejorado logica de Zindex para los Panel Plate y los ExtraccionTag. Las osGuia ahora al modificar la posicion se actualiza la simulacion en tiempo real. 2024-07-03 16:12:54 +02:00
Miguel c8abb98c7d Terminada las modificaciones de Movimiento / Angulo / Resize. Creado Enum para zindex 2024-06-30 18:17:44 +02:00
Miguel 8fddbb409b Cambios en la logica de Alto - Ancho y Angulo pasado a osBase 2024-06-30 14:32:32 +02:00
Miguel e09e4e710a Mejorada la implementacion de rotacion y redimensionado. 2024-06-28 19:47:08 +02:00
Miguel 2fe1af47dc Mejorado el Filtrado 2024-06-20 14:50:23 +02:00
Miguel f33273bbf6 Agregado de filtrado de objetossimulables. Hay mejorarlo. 2024-06-13 00:26:02 +02:00
Miguel dc01704da6 Si funcionar del todo con el TreeView 2024-06-11 19:43:12 +02:00
Miguel 51e70b706f Intenado usar TreeView 2024-06-11 13:29:00 +02:00
Miguel 759ee627e2 Mejorado de la exportacion a Excel 2024-06-11 12:30:27 +02:00
Miguel 922a46d616 Creando los botones de Seleccion multiple 2024-06-11 00:22:33 +02:00
Miguel 0f34e6cdaa Modificando BuscarCoincidencias 2024-06-10 11:07:25 +02:00
Miguel 77c9f3db5e Mejorado de Zoom y panning 2024-06-09 21:26:09 +02:00
Miguel 506ee16ae1 Terminado la logica de Groups y con un mecanismo antibucle basico. Faltaria controlar que los nombres de los objetos no puedan ser iguales. 2024-06-09 17:33:09 +02:00
Miguel 2187783fe2 Actualizados todos los objetos Transporte con la nueva logica 2024-06-09 10:55:21 +02:00
Miguel c58a264d38 Mejorando sistema de Links entre Objetos. Usando IItemsSource y suscribiendose al evento de cambio de Nombre. De esta forma se mantiene un enlace por string mas simple para serializar 2024-06-09 10:39:31 +02:00
Miguel 0305ae2506 Creado BuscarCoincidencias, implementando logica 2024-06-06 16:53:00 +02:00
Miguel 84725cc8d6 Separando los ObjetosSimulables para todas las paginas de los individuales 2024-06-05 22:27:53 +02:00
Miguel 84e7ac1c28 Agregado de ExtracionTag 2024-06-04 22:27:35 +02:00
Miguel 0410c87e93 Agregado del TextPlate 2024-06-04 17:33:00 +02:00
Miguel 1ce0371d18 Trabajando con los decimales 2024-06-02 19:14:35 +02:00
Miguel a1ecfca034 Con Float converter en el PropertyGrid 2024-06-02 17:04:58 +02:00
Miguel 47735ef00a Xceed PropetyGrid 2024-06-02 09:13:01 +02:00
Miguel 667cd18f5d Mejorado de CargarPropiedadesosDatos 2024-06-01 00:34:58 +02:00
Miguel c1ac20964e TransporteUnion Terminado 2024-05-31 19:25:24 +02:00
Miguel 268b66ad76 Creando GuiasUnion 2024-05-31 15:06:49 +02:00
Miguel c4892b1f36 Creado parametro para invertir direccion de marcha en los transportes. 2024-05-30 19:18:33 +02:00
Miguel 09980689fb Adaptacion con simCurve 2024-05-30 18:48:37 +02:00
Miguel 56f8630a65 Agregado Trace3 2024-05-27 10:34:20 +02:00
Miguel c66be28764 Creacion de la opcion EsFreno para los transportes 2024-05-26 11:50:25 +02:00
146 changed files with 17111 additions and 2620 deletions

View File

@ -1,9 +1,27 @@
<Application x:Class="CtrEditor.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CtrEditor"
StartupUri="MainWindow.xaml">
<Application x:Class="CtrEditor.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:CtrEditor"
xmlns:osExtraccion="clr-namespace:CtrEditor.ObjetosSim.Extraccion_Datos"
xmlns:os="clr-namespace:CtrEditor.ObjetosSim" StartupUri="MainWindow.xaml">
<Application.Resources>
<local:MeterToPixelConverter x:Key="MeterToPixelConverter" />
<local:LevelToHeightMultiConverter x:Key="LevelToHeightMultiConverter" />
<local:WidthPercentageConverter x:Key="WidthPercentageConverter" />
<local:DistanceToMarginConverter x:Key="DistanceToMarginConverter" />
<local:MarginConverter x:Key="MarginConverter" />
<local:FloatToFormattedStringConverter x:Key="floatFormatter" />
<local:DoubleToFormattedStringConverter x:Key="doubleFormatter" />
<local:BrushToColorNameConverter x:Key="BrushToColorNameConverter" />
<local:VerticalPositionConverter x:Key="VerticalPositionConverter" />
<local:SumConverter x:Key="SumConverter" />
<local:ColorToBrushConverter x:Key="ColorToBrushConverter" />
<local:StringToBrushConverter x:Key="StringToBrushConverter" />
<local:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
<local:SubclassFilterConverter x:Key="SubclassFilterConverterosBuscarCoincidencias"
TargetType="{x:Type osExtraccion:osBuscarCoincidencias}" />
<local:SubclassFilterConverter x:Key="SubclassFilterConverterosVMMotor" TargetType="{x:Type os:osVMmotorSim}" />
<local:UnsavedChangesConverter x:Key="UnsavedChangesConverter"/>
<local:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</Application.Resources>
</Application>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,668 @@
[
{
"$type": "CtrEditor.ObjetosSim.Extraccion_Datos.osExtraccionTag, CtrEditor",
"AutoCreated": false,
"RemoverDesdeSimulacion": false,
"Nombre": "Ingresos E0.0",
"Extraer": false,
"New_Row": false,
"Id_Search_Templates": "Ingresos Plate Group",
"Tag_extract": "E3.0\n",
"Collumn_name": "IO",
"Collumn_number": 2,
"Copy_Number": 0,
"Show_Debug_Window": false,
"Opacity_oculto": 0.1,
"Id": {
"Value": 1202
},
"Show_On_This_Page": false,
"Left": 2.0471327,
"Top": 8.451157,
"Ancho": 1.3239104,
"Alto": 0.19122289,
"Angulo": 0.0,
"Enable_On_All_Pages": true,
"ShowOnThisPagesList": [
"94102.80_SE00_Página_050.png",
"94102.80_SE00_Página_051.png",
"94102.80_SE00_Página_052.png",
"94102.80_SE00_Página_053.png",
"94102.80_SE00_Página_054.png",
"94102.80_SE00_Página_055.png",
"94102.80_SE00_Página_056.png",
"94102.80_SE00_Página_057.png",
"94102.80_SE00_Página_058.png",
"94102.80_SE00_Página_059.png",
"94102.80_SE00_Página_060.png",
"94102.80_SE00_Página_061.png",
"94102.80_SE00_Página_062.png",
"94102.80_SE00_Página_063.png",
"94102.80_SE00_Página_064.png",
"94102.80_SE00_Página_065.png",
"94102.80_SE00_Página_066.png",
"94102.80_SE00_Página_067.png",
"94102.80_SE00_Página_068.png",
"94102.80_SE00_Página_069.png"
],
"Cloned": false
},
{
"$type": "CtrEditor.ObjetosSim.Extraccion_Datos.osExtraccionTag, CtrEditor",
"AutoCreated": false,
"RemoverDesdeSimulacion": false,
"Nombre": "Ingresos Desc Italiano",
"Extraer": false,
"New_Row": false,
"Id_Search_Templates": "Ingresos Plate Group",
"Tag_extract": "MARCIA ASCIUGATORE\n",
"Collumn_name": "Description Italian",
"Collumn_number": 3,
"Copy_Number": 0,
"Show_Debug_Window": false,
"Opacity_oculto": 0.1,
"Id": {
"Value": 1203
},
"Show_On_This_Page": false,
"Left": 2.0339866,
"Top": 9.252491,
"Ancho": 1.3151709,
"Alto": 0.26124278,
"Angulo": 0.0,
"Enable_On_All_Pages": true,
"ShowOnThisPagesList": [
"94102.80_SE00_Página_050.png",
"94102.80_SE00_Página_051.png",
"94102.80_SE00_Página_052.png",
"94102.80_SE00_Página_053.png",
"94102.80_SE00_Página_054.png",
"94102.80_SE00_Página_055.png",
"94102.80_SE00_Página_056.png",
"94102.80_SE00_Página_057.png",
"94102.80_SE00_Página_058.png",
"94102.80_SE00_Página_059.png",
"94102.80_SE00_Página_060.png",
"94102.80_SE00_Página_061.png",
"94102.80_SE00_Página_062.png",
"94102.80_SE00_Página_063.png",
"94102.80_SE00_Página_064.png",
"94102.80_SE00_Página_065.png",
"94102.80_SE00_Página_066.png",
"94102.80_SE00_Página_067.png",
"94102.80_SE00_Página_068.png",
"94102.80_SE00_Página_069.png"
],
"Cloned": false
},
{
"$type": "CtrEditor.ObjetosSim.Extraccion_Datos.osExtraccionTag, CtrEditor",
"AutoCreated": false,
"RemoverDesdeSimulacion": false,
"Nombre": "Ingresos Desc English",
"Extraer": false,
"New_Row": false,
"Id_Search_Templates": "Ingresos Plate Group",
"Tag_extract": "EMERGENCY PRESSED\nELECTRICAL CABINET\n",
"Collumn_name": "Description English",
"Collumn_number": 4,
"Copy_Number": 0,
"Show_Debug_Window": false,
"Opacity_oculto": 0.1,
"Id": {
"Value": 1204
},
"Show_On_This_Page": false,
"Left": 2.0329113,
"Top": 9.509013,
"Ancho": 1.323141,
"Alto": 0.2408835,
"Angulo": 0.0,
"Enable_On_All_Pages": true,
"ShowOnThisPagesList": [
"94102.80_SE00_Página_050.png",
"94102.80_SE00_Página_051.png",
"94102.80_SE00_Página_052.png",
"94102.80_SE00_Página_053.png",
"94102.80_SE00_Página_054.png",
"94102.80_SE00_Página_055.png",
"94102.80_SE00_Página_056.png",
"94102.80_SE00_Página_057.png",
"94102.80_SE00_Página_058.png",
"94102.80_SE00_Página_059.png",
"94102.80_SE00_Página_060.png",
"94102.80_SE00_Página_061.png",
"94102.80_SE00_Página_062.png",
"94102.80_SE00_Página_063.png",
"94102.80_SE00_Página_064.png",
"94102.80_SE00_Página_065.png",
"94102.80_SE00_Página_066.png",
"94102.80_SE00_Página_067.png",
"94102.80_SE00_Página_068.png",
"94102.80_SE00_Página_069.png"
],
"Cloned": false
},
{
"$type": "CtrEditor.ObjetosSim.Extraccion_Datos.osExtraccionTag, CtrEditor",
"AutoCreated": false,
"RemoverDesdeSimulacion": false,
"Nombre": "Ingresos Card",
"Extraer": false,
"New_Row": false,
"Id_Search_Templates": "Ingresos Plate Group",
"Tag_extract": "A45311\n",
"Collumn_name": "Card",
"Collumn_number": 5,
"Copy_Number": 0,
"Show_Debug_Window": false,
"Opacity_oculto": 0.1,
"Id": {
"Value": 1260
},
"Show_On_This_Page": false,
"Left": 2.2931812,
"Top": 9.125617,
"Ancho": 0.3932013,
"Alto": 0.11076093,
"Angulo": 0.0,
"Enable_On_All_Pages": true,
"ShowOnThisPagesList": [
"94102.80_SE00_Página_050.png",
"94102.80_SE00_Página_051.png",
"94102.80_SE00_Página_052.png",
"94102.80_SE00_Página_053.png",
"94102.80_SE00_Página_054.png",
"94102.80_SE00_Página_055.png",
"94102.80_SE00_Página_056.png",
"94102.80_SE00_Página_057.png",
"94102.80_SE00_Página_058.png",
"94102.80_SE00_Página_059.png",
"94102.80_SE00_Página_060.png",
"94102.80_SE00_Página_061.png",
"94102.80_SE00_Página_062.png",
"94102.80_SE00_Página_063.png",
"94102.80_SE00_Página_064.png",
"94102.80_SE00_Página_065.png",
"94102.80_SE00_Página_066.png",
"94102.80_SE00_Página_067.png",
"94102.80_SE00_Página_068.png",
"94102.80_SE00_Página_069.png"
],
"Cloned": false
},
{
"$type": "CtrEditor.ObjetosSim.Extraccion_Datos.osExtraccionTag, CtrEditor",
"AutoCreated": false,
"RemoverDesdeSimulacion": false,
"Nombre": "Ingresos Pin",
"Extraer": false,
"New_Row": false,
"Id_Search_Templates": "Ingresos Plate Group",
"Tag_extract": "",
"Collumn_name": "Pin",
"Collumn_number": 7,
"Copy_Number": 0,
"Show_Debug_Window": false,
"Opacity_oculto": 0.1,
"Id": {
"Value": 1264
},
"Show_On_This_Page": false,
"Left": 2.6119895,
"Top": 8.204032,
"Ancho": 0.16785541,
"Alto": 0.15966734,
"Angulo": 0.0,
"Enable_On_All_Pages": true,
"ShowOnThisPagesList": [
"94102.80_SE00_Página_050.png",
"94102.80_SE00_Página_051.png",
"94102.80_SE00_Página_052.png",
"94102.80_SE00_Página_053.png",
"94102.80_SE00_Página_054.png",
"94102.80_SE00_Página_055.png",
"94102.80_SE00_Página_056.png",
"94102.80_SE00_Página_057.png",
"94102.80_SE00_Página_058.png",
"94102.80_SE00_Página_059.png",
"94102.80_SE00_Página_060.png",
"94102.80_SE00_Página_061.png",
"94102.80_SE00_Página_062.png",
"94102.80_SE00_Página_063.png",
"94102.80_SE00_Página_064.png",
"94102.80_SE00_Página_065.png",
"94102.80_SE00_Página_066.png",
"94102.80_SE00_Página_067.png",
"94102.80_SE00_Página_068.png",
"94102.80_SE00_Página_069.png"
],
"Cloned": false
},
{
"$type": "CtrEditor.ObjetosSim.Extraccion_Datos.osBuscarCoincidencias, CtrEditor",
"AutoCreated": false,
"RemoverDesdeSimulacion": false,
"Nombre": "Ingresos Plate Group",
"Search_rectangles": [
"200.6319122314453,815.1671752929688,137.59445190429688,161.43350219726562",
"350.22589111328125,815.4871215820312,137.59445190429688,161.43350219726562",
"499.9798583984375,815.4871215820312,137.59445190429688,161.43350219726562",
"649.7338256835938,815.4871215820312,137.59445190429688,161.43350219726562",
"799.6477661132812,815.4871215820312,137.59445190429688,161.43350219726562",
"949.2417602539062,815.4871215820312,137.59445190429688,161.43350219726562",
"1099.15576171875,815.4871215820312,137.59445190429688,161.43350219726562",
"1248.90966796875,815.4871215820312,137.59445190429688,161.43350219726562"
],
"Search_templates": false,
"Export_ocr": false,
"Opacity_oculto": 0.1,
"Show_debug_ocr": false,
"Threshold": 0.6,
"Coincidencias": 8,
"Id": {
"Value": 1201
},
"Show_On_This_Page": false,
"Left": 2.0069747,
"Top": 8.15955,
"Ancho": 1.3769451,
"Alto": 1.6148156,
"Angulo": 0.0,
"Enable_On_All_Pages": true,
"ShowOnThisPagesList": [
"94102.80_SE00_Página_050.png",
"94102.80_SE00_Página_051.png",
"94102.80_SE00_Página_052.png",
"94102.80_SE00_Página_053.png",
"94102.80_SE00_Página_054.png",
"94102.80_SE00_Página_055.png",
"94102.80_SE00_Página_056.png",
"94102.80_SE00_Página_057.png",
"94102.80_SE00_Página_058.png",
"94102.80_SE00_Página_059.png",
"94102.80_SE00_Página_060.png",
"94102.80_SE00_Página_061.png",
"94102.80_SE00_Página_062.png",
"94102.80_SE00_Página_063.png",
"94102.80_SE00_Página_064.png",
"94102.80_SE00_Página_065.png",
"94102.80_SE00_Página_066.png",
"94102.80_SE00_Página_067.png",
"94102.80_SE00_Página_068.png",
"94102.80_SE00_Página_069.png"
],
"Cloned": false
},
{
"$type": "CtrEditor.ObjetosSim.Extraccion_Datos.osExtraccionTag, CtrEditor",
"AutoCreated": false,
"RemoverDesdeSimulacion": false,
"Nombre": "Page",
"Extraer": false,
"New_Row": false,
"Tag_extract": "690\n",
"Collumn_name": "Extraccion Tags",
"Collumn_number": 1,
"Copy_Number": 0,
"Show_Debug_Window": false,
"Opacity_oculto": 0.1,
"Id": {
"Value": 1231
},
"Show_On_This_Page": false,
"Left": 13.666122,
"Top": 10.413534,
"Ancho": 0.81436056,
"Alto": 0.2296054,
"Angulo": 0.0,
"Enable_On_All_Pages": true,
"ShowOnThisPagesList": [
"94102.80_SE00_Página_050.png",
"94102.80_SE00_Página_051.png",
"94102.80_SE00_Página_052.png",
"94102.80_SE00_Página_053.png",
"94102.80_SE00_Página_054.png",
"94102.80_SE00_Página_055.png",
"94102.80_SE00_Página_056.png",
"94102.80_SE00_Página_057.png",
"94102.80_SE00_Página_058.png",
"94102.80_SE00_Página_059.png",
"94102.80_SE00_Página_060.png",
"94102.80_SE00_Página_061.png",
"94102.80_SE00_Página_062.png",
"94102.80_SE00_Página_063.png",
"94102.80_SE00_Página_064.png",
"94102.80_SE00_Página_065.png",
"94102.80_SE00_Página_066.png",
"94102.80_SE00_Página_067.png",
"94102.80_SE00_Página_068.png",
"94102.80_SE00_Página_069.png",
"94102.80_SE00_Página_070.png",
"94102.80_SE00_Página_071.png",
"94102.80_SE00_Página_072.png",
"94102.80_SE00_Página_073.png",
"94102.80_SE00_Página_074.png",
"94102.80_SE00_Página_075.png"
],
"Cloned": false
},
{
"$type": "CtrEditor.ObjetosSim.Extraccion_Datos.osExtraccionTag, CtrEditor",
"AutoCreated": false,
"RemoverDesdeSimulacion": false,
"Nombre": "Salidas A0.0",
"Extraer": false,
"New_Row": false,
"Id_Search_Templates": "Salidas Plate Group",
"Tag_extract": "E3.0\n",
"Collumn_name": "IO",
"Collumn_number": 2,
"Copy_Number": 0,
"Show_Debug_Window": false,
"Opacity_oculto": 0.1,
"Id": {
"Value": 63
},
"Show_On_This_Page": false,
"Left": 2.038601,
"Top": 2.4818933,
"Ancho": 1.3239104,
"Alto": 0.19122289,
"Angulo": 0.0,
"Enable_On_All_Pages": true,
"ShowOnThisPagesList": [
"94102.80_SE00_Página_070.png",
"94102.80_SE00_Página_071.png",
"94102.80_SE00_Página_072.png",
"94102.80_SE00_Página_073.png",
"94102.80_SE00_Página_074.png",
"94102.80_SE00_Página_075.png"
],
"Cloned": false
},
{
"$type": "CtrEditor.ObjetosSim.Extraccion_Datos.osExtraccionTag, CtrEditor",
"AutoCreated": false,
"RemoverDesdeSimulacion": false,
"Nombre": "Salidas Desc Italiano",
"Extraer": false,
"New_Row": false,
"Id_Search_Templates": "Salidas Plate Group",
"Tag_extract": "MARCIA ASCIUGATORE\n",
"Collumn_name": "Description Italian",
"Collumn_number": 3,
"Copy_Number": 0,
"Show_Debug_Window": false,
"Opacity_oculto": 0.1,
"Id": {
"Value": 64
},
"Show_On_This_Page": false,
"Left": 2.0427365,
"Top": 1.3802159,
"Ancho": 1.3151709,
"Alto": 0.26124278,
"Angulo": 0.0,
"Enable_On_All_Pages": true,
"ShowOnThisPagesList": [
"94102.80_SE00_Página_070.png",
"94102.80_SE00_Página_071.png",
"94102.80_SE00_Página_072.png",
"94102.80_SE00_Página_073.png",
"94102.80_SE00_Página_074.png",
"94102.80_SE00_Página_075.png"
],
"Cloned": false
},
{
"$type": "CtrEditor.ObjetosSim.Extraccion_Datos.osExtraccionTag, CtrEditor",
"AutoCreated": false,
"RemoverDesdeSimulacion": false,
"Nombre": "Salidas Desc English",
"Extraer": false,
"New_Row": false,
"Id_Search_Templates": "Salidas Plate Group",
"Tag_extract": "EMERGENCY PRESSED\nELECTRICAL CABINET\n",
"Collumn_name": "Description English",
"Collumn_number": 4,
"Copy_Number": 0,
"Show_Debug_Window": false,
"Opacity_oculto": 0.1,
"Id": {
"Value": 65
},
"Show_On_This_Page": false,
"Left": 2.0372825,
"Top": 1.6332868,
"Ancho": 1.323141,
"Alto": 0.2408835,
"Angulo": 0.0,
"Enable_On_All_Pages": true,
"ShowOnThisPagesList": [
"94102.80_SE00_Página_070.png",
"94102.80_SE00_Página_071.png",
"94102.80_SE00_Página_072.png",
"94102.80_SE00_Página_073.png",
"94102.80_SE00_Página_074.png",
"94102.80_SE00_Página_075.png"
],
"Cloned": false
},
{
"$type": "CtrEditor.ObjetosSim.Extraccion_Datos.osExtraccionTag, CtrEditor",
"AutoCreated": false,
"RemoverDesdeSimulacion": false,
"Nombre": "Salidas Card",
"Extraer": false,
"New_Row": false,
"Id_Search_Templates": "Salidas Plate Group",
"Tag_extract": "A45311\n",
"Collumn_name": "Card",
"Collumn_number": 5,
"Copy_Number": 0,
"Show_Debug_Window": false,
"Opacity_oculto": 0.1,
"Id": {
"Value": 66
},
"Show_On_This_Page": false,
"Left": 2.3019588,
"Top": 1.9661419,
"Ancho": 0.3932013,
"Alto": 0.11076093,
"Angulo": 0.0,
"Enable_On_All_Pages": true,
"ShowOnThisPagesList": [
"94102.80_SE00_Página_070.png",
"94102.80_SE00_Página_071.png",
"94102.80_SE00_Página_072.png",
"94102.80_SE00_Página_073.png",
"94102.80_SE00_Página_074.png",
"94102.80_SE00_Página_075.png"
],
"Cloned": false
},
{
"$type": "CtrEditor.ObjetosSim.Extraccion_Datos.osExtraccionTag, CtrEditor",
"AutoCreated": false,
"RemoverDesdeSimulacion": false,
"Nombre": "Salidas Pin",
"Extraer": false,
"New_Row": false,
"Id_Search_Templates": "Salidas Plate Group",
"Tag_extract": "",
"Collumn_name": "Pin",
"Collumn_number": 7,
"Copy_Number": 0,
"Show_Debug_Window": false,
"Opacity_oculto": 0.1,
"Id": {
"Value": 68
},
"Show_On_This_Page": false,
"Left": 2.613817,
"Top": 2.771892,
"Ancho": 0.16785541,
"Alto": 0.15966734,
"Angulo": 0.0,
"Enable_On_All_Pages": true,
"ShowOnThisPagesList": [
"94102.80_SE00_Página_070.png",
"94102.80_SE00_Página_071.png",
"94102.80_SE00_Página_072.png",
"94102.80_SE00_Página_073.png",
"94102.80_SE00_Página_074.png",
"94102.80_SE00_Página_075.png"
],
"Cloned": false
},
{
"$type": "CtrEditor.ObjetosSim.Extraccion_Datos.osBuscarCoincidencias, CtrEditor",
"AutoCreated": false,
"RemoverDesdeSimulacion": false,
"Nombre": "Salidas Plate Group",
"Search_rectangles": [
"200.471923828125,134.23458862304688,137.59445190429688,161.43350219726562",
"350.22589111328125,134.39459228515625,137.59445190429688,161.43350219726562",
"499.8198547363281,134.55458068847656,137.59445190429688,161.43350219726562",
"649.5738525390625,134.55458068847656,137.59445190429688,161.43350219726562",
"799.3278198242188,134.55458068847656,137.59445190429688,161.43350219726562",
"949.2417602539062,134.55458068847656,137.59445190429688,161.43350219726562",
"1098.9957275390625,134.55458068847656,137.59445190429688,161.43350219726562",
"1248.749755859375,134.55458068847656,137.59445190429688,161.43350219726562",
"1398.503662109375,134.87457275390625,137.59445190429688,161.43350219726562"
],
"Search_templates": false,
"Export_ocr": false,
"Opacity_oculto": 0.1,
"Show_debug_ocr": false,
"Threshold": 0.6,
"Coincidencias": 9,
"Id": {
"Value": 69
},
"Show_On_This_Page": false,
"Left": 2.0056293,
"Top": 1.3495445,
"Ancho": 1.3769451,
"Alto": 1.6148156,
"Angulo": 0.0,
"Enable_On_All_Pages": true,
"ShowOnThisPagesList": [
"94102.80_SE00_Página_070.png",
"94102.80_SE00_Página_071.png",
"94102.80_SE00_Página_072.png",
"94102.80_SE00_Página_073.png",
"94102.80_SE00_Página_074.png",
"94102.80_SE00_Página_075.png"
],
"Cloned": false
},
{
"$type": "CtrEditor.ObjetosSim.Extraccion_Datos.osExtraccionTag, CtrEditor",
"AutoCreated": false,
"RemoverDesdeSimulacion": false,
"Nombre": "Descrip Pagina",
"Extraer": false,
"New_Row": false,
"Tag_extract": "690\n",
"Collumn_name": "Descrip Pagina",
"Collumn_number": 1,
"Copy_Number": 0,
"Show_Debug_Window": false,
"Opacity_oculto": 0.1,
"Id": {
"Value": 70
},
"Show_On_This_Page": false,
"Left": 7.490359,
"Top": 10.734316,
"Ancho": 1.8668232,
"Alto": 0.17921503,
"Angulo": 0.0,
"Enable_On_All_Pages": true,
"ShowOnThisPagesList": [
"94102.80_SE00_Página_050.png",
"94102.80_SE00_Página_051.png",
"94102.80_SE00_Página_052.png",
"94102.80_SE00_Página_053.png",
"94102.80_SE00_Página_054.png",
"94102.80_SE00_Página_055.png",
"94102.80_SE00_Página_056.png",
"94102.80_SE00_Página_057.png",
"94102.80_SE00_Página_058.png",
"94102.80_SE00_Página_059.png",
"94102.80_SE00_Página_060.png",
"94102.80_SE00_Página_061.png",
"94102.80_SE00_Página_062.png",
"94102.80_SE00_Página_063.png",
"94102.80_SE00_Página_064.png",
"94102.80_SE00_Página_065.png",
"94102.80_SE00_Página_066.png",
"94102.80_SE00_Página_067.png",
"94102.80_SE00_Página_068.png",
"94102.80_SE00_Página_069.png",
"94102.80_SE00_Página_070.png",
"94102.80_SE00_Página_071.png",
"94102.80_SE00_Página_072.png",
"94102.80_SE00_Página_073.png",
"94102.80_SE00_Página_074.png",
"94102.80_SE00_Página_075.png"
],
"Cloned": false
},
{
"$type": "CtrEditor.ObjetosSim.Extraccion_Datos.osExtraccionTag, CtrEditor",
"AutoCreated": false,
"RemoverDesdeSimulacion": false,
"Nombre": "Comessa",
"Extraer": false,
"New_Row": false,
"Tag_extract": "690\n",
"Collumn_name": "Comessa",
"Collumn_number": 1,
"Copy_Number": 0,
"Show_Debug_Window": false,
"Opacity_oculto": 0.1,
"Id": {
"Value": 71
},
"Show_On_This_Page": false,
"Left": 11.119476,
"Top": 10.6297865,
"Ancho": 0.5749816,
"Alto": 0.14187858,
"Angulo": 0.0,
"Enable_On_All_Pages": true,
"ShowOnThisPagesList": [
"94102.80_SE00_Página_050.png",
"94102.80_SE00_Página_051.png",
"94102.80_SE00_Página_052.png",
"94102.80_SE00_Página_053.png",
"94102.80_SE00_Página_054.png",
"94102.80_SE00_Página_055.png",
"94102.80_SE00_Página_056.png",
"94102.80_SE00_Página_057.png",
"94102.80_SE00_Página_058.png",
"94102.80_SE00_Página_059.png",
"94102.80_SE00_Página_060.png",
"94102.80_SE00_Página_061.png",
"94102.80_SE00_Página_062.png",
"94102.80_SE00_Página_063.png",
"94102.80_SE00_Página_064.png",
"94102.80_SE00_Página_065.png",
"94102.80_SE00_Página_066.png",
"94102.80_SE00_Página_067.png",
"94102.80_SE00_Página_068.png",
"94102.80_SE00_Página_069.png",
"94102.80_SE00_Página_070.png",
"94102.80_SE00_Página_071.png",
"94102.80_SE00_Página_072.png",
"94102.80_SE00_Página_073.png",
"94102.80_SE00_Página_074.png",
"94102.80_SE00_Página_075.png"
],
"Cloned": false
}
]

View File

View File

@ -0,0 +1,22 @@
<UserControl x:Class="CtrEditor.Controls.ObjectHierarchyView"
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:controls="clr-namespace:CtrEditor.Controls"
mc:Ignorable="d">
<Grid>
<TreeView x:Name="ObjectsTreeView" ItemsSource="{Binding RootNodes}"
SelectedItemChanged="TreeView_SelectedItemChanged">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" />
<TextBlock Text="{Binding RelationshipType}" Margin="5,0,0,0" Foreground="Gray"
FontStyle="Italic" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Grid>
</UserControl>

View File

@ -0,0 +1,97 @@
using CtrEditor.ObjetosSim;
using System.Collections.ObjectModel;
using System.Windows.Controls;
using System.Windows;
namespace CtrEditor.Controls
{
public partial class ObjectHierarchyView : UserControl
{
private MainViewModel _mainViewModel;
public ObservableCollection<OsTreeNode> RootNodes { get; set; }
public ObjectHierarchyView()
{
InitializeComponent();
RootNodes = new ObservableCollection<OsTreeNode>();
this.DataContext = this;
}
public void Initialize(MainViewModel mainViewModel)
{
_mainViewModel = mainViewModel;
_mainViewModel.ObjetosSimulables.CollectionChanged += ObjetosSimulables_CollectionChanged;
UpdateTree();
}
private void ObjetosSimulables_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
UpdateTree();
}
public void UpdateTree()
{
RootNodes.Clear();
// Agrupar por tipos
var groupsByType = _mainViewModel.ObjetosSimulables
.GroupBy(obj => obj.GetType());
foreach (var group in groupsByType)
{
// Usar GetTypeDisplayName para obtener el nombre de la clase
var typeNode = new OsTreeNode(OsTreeNode.GetTypeDisplayName(group.Key));
RootNodes.Add(typeNode);
foreach (var obj in group)
{
var objNode = new OsTreeNode(obj.Nombre, obj);
typeNode.Children.Add(objNode);
// Agregar relaciones group_Panel
if (!string.IsNullOrEmpty(obj.Group_Panel))
{
var linkedPanel = _mainViewModel.ObjetosSimulables
.FirstOrDefault(o => o.Nombre == obj.Group_Panel);
if (linkedPanel != null)
{
objNode.Children.Add(new OsTreeNode(linkedPanel.Nombre, linkedPanel, "(group_Panel)"));
}
}
// Agregar relaciones group_FramePanel
if (!string.IsNullOrEmpty(obj.Group_FramePanel))
{
var linkedFrame = _mainViewModel.ObjetosSimulables
.FirstOrDefault(o => o.Nombre == obj.Group_FramePanel);
if (linkedFrame != null)
{
objNode.Children.Add(new OsTreeNode(linkedFrame.Nombre, linkedFrame, "(group_FramePanel)"));
}
}
// Agregar objetos que hacen referencia a este objeto
var referencingObjects = _mainViewModel.ObjetosSimulables
.Where(o => o.Group_Panel == obj.Nombre || o.Group_FramePanel == obj.Nombre);
foreach (var refObj in referencingObjects)
{
var relationType = refObj.Group_Panel == obj.Nombre ? "(referenced by group_Panel)" : "(referenced by group_FramePanel)";
objNode.Children.Add(new OsTreeNode(refObj.Nombre, refObj, relationType));
}
}
}
}
private void TreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
if (e.NewValue is OsTreeNode node && node.AssociatedObject != null)
{
// Actualizar la selección en el MainViewModel
_mainViewModel.SelectedItemOsList = node.AssociatedObject;
}
}
}
}

36
Controls/OsTreeNode.cs Normal file
View File

@ -0,0 +1,36 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CtrEditor.ObjetosSim;
using System.Collections.ObjectModel;
using System.Reflection;
namespace CtrEditor
{
public partial class OsTreeNode : ObservableObject
{
[ObservableProperty]
private string name;
[ObservableProperty]
private ObservableCollection<OsTreeNode> children;
[ObservableProperty]
private osBase associatedObject;
[ObservableProperty]
private string relationshipType;
public OsTreeNode(string name, osBase obj = null, string relationship = "")
{
this.Name = name;
this.Children = new ObservableCollection<OsTreeNode>();
this.AssociatedObject = obj;
this.RelationshipType = relationship;
}
public static string GetTypeDisplayName(Type type)
{
var methodInfo = type.GetMethod("NombreClase", BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy);
return methodInfo != null ? methodInfo.Invoke(null, null)?.ToString() : type.Name;
}
}
}

View File

@ -0,0 +1,131 @@
<UserControl x:Class="CtrEditor.Controls.PanelEdicionControl"
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:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:ObjetosSim="clr-namespace:CtrEditor.ObjetosSim"
xmlns:ObjetosExtraccion="clr-namespace:CtrEditor.ObjetosSim.Extraccion_Datos"
mc:Ignorable="d">
<xctk:PropertyGrid x:Name="PropertyGridControl"
Margin="5"
AutoGenerateProperties="False"
ShowDescriptionByTooltip="True"
FocusManager.IsFocusScope="True">
<xctk:PropertyGrid.EditorDefinitions>
<!-- String -->
<xctk:EditorTemplateDefinition>
<xctk:EditorTemplateDefinition.TargetProperties>
<xctk:TargetPropertyType Type="{x:Type sys:String}" />
</xctk:EditorTemplateDefinition.TargetProperties>
<xctk:EditorTemplateDefinition.EditingTemplate>
<DataTemplate>
<TextBox Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"
Text="{Binding Value, UpdateSourceTrigger=LostFocus}" />
</DataTemplate>
</xctk:EditorTemplateDefinition.EditingTemplate>
</xctk:EditorTemplateDefinition>
<!-- Float -->
<xctk:EditorTemplateDefinition>
<xctk:EditorTemplateDefinition.TargetProperties>
<xctk:TargetPropertyType Type="{x:Type sys:Single}" />
</xctk:EditorTemplateDefinition.TargetProperties>
<xctk:EditorTemplateDefinition.EditingTemplate>
<DataTemplate>
<xctk:SingleUpDown Increment="0.01"
Text="{Binding Value, Converter={StaticResource floatFormatter}, UpdateSourceTrigger=LostFocus}" />
</DataTemplate>
</xctk:EditorTemplateDefinition.EditingTemplate>
</xctk:EditorTemplateDefinition>
<!-- Double -->
<xctk:EditorTemplateDefinition>
<xctk:EditorTemplateDefinition.TargetProperties>
<xctk:TargetPropertyType Type="{x:Type sys:Double}" />
</xctk:EditorTemplateDefinition.TargetProperties>
<xctk:EditorTemplateDefinition.EditingTemplate>
<DataTemplate>
<TextBox Text="{Binding Value, Converter={StaticResource doubleFormatter}, UpdateSourceTrigger=LostFocus}" />
</DataTemplate>
</xctk:EditorTemplateDefinition.EditingTemplate>
</xctk:EditorTemplateDefinition>
<!-- Velocidad -->
<xctk:EditorTemplateDefinition TargetProperties="VelocidadActual">
<xctk:EditorTemplateDefinition.EditingTemplate>
<DataTemplate>
<xctk:SingleUpDown Increment="0.01" Background="Beige"
Text="{Binding Value, Converter={StaticResource floatFormatter}}"
FontWeight="Bold" />
</DataTemplate>
</xctk:EditorTemplateDefinition.EditingTemplate>
</xctk:EditorTemplateDefinition>
<!-- File Dialog -->
<xctk:EditorTemplateDefinition TargetProperties="ImagePath">
<xctk:EditorTemplateDefinition.EditingTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBox Grid.Column="0"
Text="{Binding Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<Button Grid.Column="1" Content="..." Width="25" Margin="2,0,0,0"
Click="ImagePathButton_Click" />
</Grid>
</DataTemplate>
</xctk:EditorTemplateDefinition.EditingTemplate>
</xctk:EditorTemplateDefinition>
<!-- osBuscarCoincidencias -->
<xctk:EditorTemplateDefinition>
<xctk:EditorTemplateDefinition.TargetProperties>
<xctk:TargetPropertyType Type="{x:Type ObjetosExtraccion:osBuscarCoincidencias}" />
</xctk:EditorTemplateDefinition.TargetProperties>
<xctk:EditorTemplateDefinition.EditingTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding DataContext.ObjetosSimulables,
RelativeSource={RelativeSource AncestorType=Window},
Converter={StaticResource SubclassFilterConverterosBuscarCoincidencias}}"
SelectedItem="{Binding Value}" DisplayMemberPath="Nombre" IsEditable="True" />
</DataTemplate>
</xctk:EditorTemplateDefinition.EditingTemplate>
</xctk:EditorTemplateDefinition>
<!-- osVMmotorSim -->
<xctk:EditorTemplateDefinition>
<xctk:EditorTemplateDefinition.TargetProperties>
<xctk:TargetPropertyType Type="{x:Type ObjetosSim:osVMmotorSim}" />
</xctk:EditorTemplateDefinition.TargetProperties>
<xctk:EditorTemplateDefinition.EditingTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding DataContext.ObjetosSimulables,
RelativeSource={RelativeSource AncestorType=Window},
Converter={StaticResource SubclassFilterConverterosVMMotor}}"
SelectedItem="{Binding Value}" DisplayMemberPath="Nombre" />
</DataTemplate>
</xctk:EditorTemplateDefinition.EditingTemplate>
</xctk:EditorTemplateDefinition>
<!-- osBase -->
<xctk:EditorTemplateDefinition>
<xctk:EditorTemplateDefinition.TargetProperties>
<xctk:TargetPropertyType Type="{x:Type ObjetosSim:osBase}" />
</xctk:EditorTemplateDefinition.TargetProperties>
<xctk:EditorTemplateDefinition.EditingTemplate>
<DataTemplate>
<ComboBox
ItemsSource="{Binding DataContext.ObjetosSimulables, RelativeSource={RelativeSource AncestorType=Window}}"
SelectedItem="{Binding Value}" DisplayMemberPath="Nombre" />
</DataTemplate>
</xctk:EditorTemplateDefinition.EditingTemplate>
</xctk:EditorTemplateDefinition>
</xctk:PropertyGrid.EditorDefinitions>
</xctk:PropertyGrid>
</UserControl>

View File

@ -0,0 +1,40 @@
using CtrEditor.ObjetosSim;
using Ookii.Dialogs.Wpf;
using System.Windows;
using System.Windows.Controls;
using Xceed.Wpf.Toolkit.PropertyGrid;
namespace CtrEditor.Controls
{
public partial class PanelEdicionControl : UserControl
{
public PanelEdicionControl()
{
InitializeComponent();
}
public void CargarPropiedades(osBase selectedObject)
{
UserControlFactory.CargarPropiedadesosDatos(selectedObject, PropertyGridControl);
}
public bool IsKeyboardFocusWithin => PropertyGridControl.IsKeyboardFocusWithin;
private void ImagePathButton_Click(object sender, RoutedEventArgs e)
{
var dlg = new VistaOpenFileDialog
{
Filter = "Image files (*.png;*.jpg;*.bmp)|*.png;*.jpg;*.bmp|All files (*.*)|*.*"
};
if (dlg.ShowDialog() == true)
{
// El DataContext de este botón es el PropertyItem asociado
if ((sender as Button)?.DataContext is PropertyItem propertyItem)
{
propertyItem.Value = dlg.FileName;
}
}
}
}
}

52
Controls/osVisFilter.xaml Normal file
View File

@ -0,0 +1,52 @@
<UserControl x:Class="CtrEditor.Controls.osVisFilter" 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.Controls"
mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="200">
<UserControl.Resources>
<Style x:Key="FilterCheckBoxStyle" TargetType="CheckBox">
<Setter Property="Margin" Value="0,2,0,2" />
<Setter Property="FontSize" Value="12" />
</Style>
<Style x:Key="FilterHeaderStyle" TargetType="TextBlock">
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="Margin" Value="0,5,0,2" />
</Style>
<Style x:Key="FilterSeparatorStyle" TargetType="Separator">
<Setter Property="Margin" Value="0,5,0,5" />
</Style>
</UserControl.Resources>
<Border BorderBrush="LightGray" BorderThickness="1" Padding="5">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel>
<!-- Fixed Filter Options -->
<TextBlock Text="Filter Options" Style="{StaticResource FilterHeaderStyle}" />
<CheckBox Content="All" IsChecked="{Binding ShowAll, Mode=TwoWay}"
Style="{StaticResource FilterCheckBoxStyle}" />
<CheckBox Content="Cloned" IsChecked="{Binding ShowCloned, Mode=TwoWay}"
Style="{StaticResource FilterCheckBoxStyle}" />
<CheckBox Content="Auto Created" IsChecked="{Binding ShowAutoCreated, Mode=TwoWay}"
Style="{StaticResource FilterCheckBoxStyle}" />
<CheckBox Content="Enable On All Pages" IsChecked="{Binding ShowEnableOnAllPages, Mode=TwoWay}"
Style="{StaticResource FilterCheckBoxStyle}" />
<CheckBox Content="Show On This Page" IsChecked="{Binding ShowOnThisPage, Mode=TwoWay}"
Style="{StaticResource FilterCheckBoxStyle}" />
<!-- Separator between fixed and dynamic options -->
<Separator Style="{StaticResource FilterSeparatorStyle}" />
<!-- Type Filter Options -->
<TextBlock Text="Object Types" Style="{StaticResource FilterHeaderStyle}" />
<ItemsControl ItemsSource="{Binding TypeFilters}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding DisplayName}" IsChecked="{Binding IsSelected, Mode=TwoWay}"
Style="{StaticResource FilterCheckBoxStyle}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ScrollViewer>
</Border>
</UserControl>

View File

@ -0,0 +1,223 @@
using System.Collections.ObjectModel;
using System.ComponentModel; // Add this for PropertyChangedEventHandler and PropertyChangedEventArgs
using System.Reflection;
using System.Text.Json.Serialization;
using System.Windows;
using System.Windows.Controls;
using CommunityToolkit.Mvvm.ComponentModel;
using CtrEditor.ObjetosSim;
namespace CtrEditor.Controls
{
public class FilterChangedEventArgs : EventArgs
{
public osVisFilterViewModel FilterViewModel { get; private set; }
public FilterChangedEventArgs(osVisFilterViewModel viewModel)
{
FilterViewModel = viewModel;
}
}
/// <summary>
/// Interaction logic for osVisFilter.xaml
/// </summary>
public partial class osVisFilter : UserControl
{
public osVisFilter()
{
InitializeComponent();
DataContext = FilterViewModel = new osVisFilterViewModel { Parent = this }; // Set the Parent property
}
public osVisFilterViewModel FilterViewModel { get; private set; }
public event EventHandler<FilterChangedEventArgs> FilterChanged;
public virtual void OnFilterChanged() // Changed from protected to public
{
FilterChanged?.Invoke(this, new FilterChangedEventArgs(FilterViewModel));
}
/// <summary>
/// Updates the type filters based on the provided types
/// </summary>
public void UpdateAvailableTypes(IEnumerable<Type> types)
{
FilterViewModel.UpdateTypeFilters(types);
}
}
/// <summary>
/// ViewModel for the osVisFilter control
/// </summary>
public partial class osVisFilterViewModel : ObservableObject
{
[ObservableProperty]
private bool showAll = true;
[ObservableProperty]
private bool showCloned;
[ObservableProperty]
private bool showAutoCreated;
[ObservableProperty]
private bool showEnableOnAllPages;
[ObservableProperty]
private bool showOnThisPage;
[ObservableProperty]
private ObservableCollection<TypeFilterItem> typeFilters = new ObservableCollection<TypeFilterItem>();
partial void OnShowAllChanged(bool value)
{
NotifyFilterChanged();
}
partial void OnShowClonedChanged(bool value)
{
NotifyFilterChanged();
}
partial void OnShowAutoCreatedChanged(bool value)
{
NotifyFilterChanged();
}
partial void OnShowEnableOnAllPagesChanged(bool value)
{
NotifyFilterChanged();
}
partial void OnShowOnThisPageChanged(bool value)
{
NotifyFilterChanged();
}
partial void OnTypeFiltersChanged(ObservableCollection<TypeFilterItem> value)
{
if (value != null)
{
foreach (var filter in value)
{
filter.PropertyChanged += (s, e) =>
{
if (e.PropertyName == nameof(TypeFilterItem.IsSelected))
{
NotifyFilterChanged();
}
};
}
}
}
private void NotifyFilterChanged()
{
if (this.Parent is osVisFilter filter)
{
filter.OnFilterChanged();
}
}
[JsonIgnore]
public osVisFilter Parent { get; set; }
public osVisFilterViewModel()
{
// PropertyChanged listeners could be added here if needed
if (this.Parent is osVisFilter filter)
{
filter.OnFilterChanged();
}
}
/// <summary>
/// Updates the type filters based on the provided types
/// </summary>
public void UpdateTypeFilters(IEnumerable<Type> types)
{
// Remove types that are no longer present
var typesToRemove = TypeFilters
.Where(tf => !types.Contains(tf.Type))
.ToList();
foreach (var item in typesToRemove)
{
UnsubscribeFromTypeFilter(item);
TypeFilters.Remove(item);
}
// Add new types that aren't already in the list
foreach (var type in types)
{
if (!TypeFilters.Any(tf => tf.Type == type))
{
var newFilter = new TypeFilterItem(type)
{
DisplayName = GetTypeDisplayName(type),
IsSelected = true
};
SubscribeToTypeFilter(newFilter);
TypeFilters.Add(newFilter);
}
}
}
private void SubscribeToTypeFilter(TypeFilterItem filter)
{
filter.PropertyChanged += TypeFilter_PropertyChanged;
}
private void UnsubscribeFromTypeFilter(TypeFilterItem filter)
{
filter.PropertyChanged -= TypeFilter_PropertyChanged;
}
private void TypeFilter_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(TypeFilterItem.IsSelected))
{
NotifyFilterChanged();
}
}
/// <summary>
/// Gets the display name for a type, using the NombreClase static method if available
/// </summary>
private string GetTypeDisplayName(Type type)
{
var methodInfo = type.GetMethod("NombreClase",
BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy);
return methodInfo != null ?
methodInfo.Invoke(null, null)?.ToString() :
type.Name;
}
}
/// <summary>
/// Represents a type filter item with selection state
/// </summary>
public partial class TypeFilterItem : ObservableObject
{
[ObservableProperty]
private bool isSelected = true;
[ObservableProperty]
private string displayName;
public Type Type { get; }
public TypeFilterItem(Type type)
{
Type = type;
DisplayName = type?.Name ?? "Unknown Type";
}
partial void OnIsSelectedChanged(bool value)
{
OnPropertyChanged(nameof(IsSelected));
}
}
}

View File

View File

View File

@ -0,0 +1,27 @@
using System.Windows.Data;
using System.Windows;
namespace CtrEditor
{
public class UnsavedChangesConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is string path)
{
var viewModel = Application.Current.MainWindow?.DataContext as MainViewModel;
if (viewModel?.HasUnsavedChanges == true)
{
return $"{path} *";
}
return path;
}
return string.Empty;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

View File

@ -1,276 +0,0 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;
using System.Windows;
using System.Windows.Media;
namespace CtrEditor.Convertidores
{
public class HalfWidthConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is double width)
{
return (width / 2.0) - (double.Parse(parameter.ToString()) / 2.0);
}
return 0;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
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
{
// Instancia privada estática, parte del patrón Singleton
private static PixelToMeter? _instance;
public UnitConverter calc = new UnitConverter(0.01f);
// Propiedad pública estática para acceder a la instancia
public static PixelToMeter Instance
{
get
{
if (_instance == null)
{
_instance = new PixelToMeter();
}
return _instance;
}
}
}
public class MeterToPixelConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null) return 0; // Aseguramos que el valor no sea nulo
// Convertimos el valor de entrada en un número flotante
float meters = System.Convert.ToSingle(value);
float factor = 1; // Valor por defecto del factor
// Si el parámetro no es nulo, intentamos convertirlo a float
if (parameter != null)
{
string paramStr = parameter.ToString();
// Normalizamos el parámetro para asegurar que el punto sea el separador decimal
paramStr = paramStr.Replace(',', '.');
// Utilizamos CultureInfo.InvariantCulture para evitar problemas con el separador decimal
if (float.TryParse(paramStr, NumberStyles.Any, CultureInfo.InvariantCulture, out float parsedFactor))
{
factor = parsedFactor; // Asignamos el factor parseado si la conversión es exitosa
}
}
// Calculamos los píxeles llamando a la instancia de PixelToMeter y multiplicamos por el factor
return PixelToMeter.Instance.calc.MetersToPixels(meters) * factor;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null) return 0; // Aseguramos que el valor no sea nulo
// Convertimos el valor de entrada en un número flotante
float pixels = System.Convert.ToSingle(value);
float factor = 1; // Valor por defecto del factor
// Si el parámetro no es nulo, intentamos convertirlo a float
if (parameter != null)
{
string paramStr = parameter.ToString();
// Normalizamos el parámetro para asegurar que el punto sea el separador decimal
paramStr = paramStr.Replace(',', '.');
// Utilizamos CultureInfo.InvariantCulture para evitar problemas con el separador decimal
if (float.TryParse(paramStr, NumberStyles.Any, CultureInfo.InvariantCulture, out float parsedFactor))
{
factor = parsedFactor; // Asignamos el factor parseado si la conversión es exitosa
}
}
return PixelToMeter.Instance.calc.PixelsToMeters(pixels) * factor;
}
}
public class DistanceToMarginConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is double distance)
{
return new Thickness(0, 0, 0, PixelToMeter.Instance.calc.MetersToPixels((float)distance)); // Ajustar Bottom a 'distance'
}
return new Thickness();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException("ConvertBack is not supported.");
}
}
public class FloatToFormattedStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is float floatValue)
{
return floatValue.ToString("0.00", culture); // Formatear a dos decimales
}
return value; // Devolver el valor original si no es un float
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is string stringValue && float.TryParse(stringValue, NumberStyles.Float, culture, out float result))
{
return result;
}
return value; // Devolver el valor original si no se puede convertir
}
}
public class DoubleToFormattedStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is double floatValue)
{
return floatValue.ToString("0.00", culture); // Formatear a dos decimales
}
return value; // Devolver el valor original si no es un float
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is string stringValue && double.TryParse(stringValue, NumberStyles.Float, culture, out double result))
{
return result;
}
return value; // Devolver el valor original si no se puede convertir
}
}
public class UnitConverter
{
// La escala representa cuántos metros hay en un píxel
public float Scale { get; private set; }
public UnitConverter(float scale)
{
if (scale <= 0)
throw new ArgumentException("Scale must be greater than zero.");
Scale = scale;
}
// Convierte una distancia en metros a píxeles
public float MetersToPixels(float meters)
{
return meters / Scale;
}
// Convierte una distancia en píxeles a metros
public float PixelsToMeters(float pixels)
{
return pixels * Scale;
}
// Configurar o ajustar la escala
public void SetScale(float newScale)
{
if (newScale <= 0)
throw new ArgumentException("Scale must be greater than zero.");
Scale = newScale;
}
}
}

11
CtrEditor.code-workspace Normal file
View File

@ -0,0 +1,11 @@
{
"folders": [
{
"path": "."
},
{
"path": "../Libraries/LibS7Adv"
}
],
"settings": {}
}

View File

@ -6,9 +6,19 @@
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UseWPF>true</UseWPF>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<Optimize>True</Optimize>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<Optimize>True</Optimize>
</PropertyGroup>
<ItemGroup>
<Compile Remove="ObjetosSim\ucBasicExample.xaml.cs" />
<Compile Remove="ObjetosSim\ucTransporteCurva.xaml.cs" />
<Compile Remove="Simulacion\FPhysics.cs" />
<Compile Remove="Simulacion\GeometrySimulator.cs" />
@ -17,45 +27,74 @@
<ItemGroup>
<None Remove="app2.png" />
<None Remove="CtrEditorE.png" />
<None Remove="Icons\allselect.png" />
<None Remove="Icons\analyze.png" />
<None Remove="Icons\app.256x256.ico" />
<None Remove="Icons\app.png" />
<None Remove="Icons\app2.128x128.ico" />
<None Remove="Icons\app2.256x256.ico" />
<None Remove="Icons\app2.png" />
<None Remove="Icons\borrar.png" />
<None Remove="Icons\choose.png" />
<None Remove="Icons\connect.png" />
<None Remove="Icons\CtrEditorA.png" />
<None Remove="Icons\CtrEditorC.png" />
<None Remove="Icons\CtrEditorE.png" />
<None Remove="Icons\disconnect.png" />
<None Remove="Icons\duplicate.png" />
<None Remove="Icons\extract.png" />
<None Remove="Icons\fotocelula.png" />
<None Remove="Icons\match.png" />
<None Remove="Icons\ocr.png" />
<None Remove="Icons\rotation.cur" />
<None Remove="Icons\rotation32x32.cur" />
<None Remove="Icons\rotationRx.cur" />
<None Remove="Icons\rotationSx.cur" />
<None Remove="Icons\save.png" />
<None Remove="Icons\start.png" />
<None Remove="Icons\stop.png" />
<None Remove="Icons\unselect.png" />
<None Remove="imagenes\filler.png" />
<None Remove="imagenes\gear.png" />
<None Remove="imagenes\motorNegro.png" />
<None Remove="imagenes\motorVerde.png" />
<None Remove="Images\base.png" />
<None Remove="motor2.png" />
<None Remove="tank.png" />
<None Remove="IA\appsettings.json" />
</ItemGroup>
<ItemGroup>
<Page Remove="ObjetosSim\ucBasicExample.xaml" />
<Page Remove="ObjetosSim\ucTransporteCurva.xaml" />
</ItemGroup>
<ItemGroup>
<None Include="ObjetosSim\ucBasicExample.xaml" />
<None Include="ObjetosSim\ucBasicExample.xaml.cs" />
<None Include="Simulacion\FPhysics.cs" />
<None Include="Simulacion\GeometrySimulator.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Aether.Physics2D" Version="2.1.0" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" />
<PackageReference Include="FarseerPhysics" Version="3.5.0" />
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.77" />
<PackageReference Include="ClosedXML" Version="0.104.2" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageReference Include="Emgu.CV" 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="Extended.Wpf.Toolkit" Version="4.6.1" />
<PackageReference Include="LanguageDetection" Version="1.2.0" />
<PackageReference Include="LiveChartsCore.SkiaSharpView.WPF" Version="2.0.0-rc4.5" />
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.135" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Ookii.Dialogs.Wpf" Version="5.0.1" />
<PackageReference Include="Tesseract" Version="5.2.0" />
<PackageReference Include="Tesseract.Drawing" Version="5.2.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Libraries\LibS7Adv\LibS7Adv.csproj" />
</ItemGroup>
<ItemGroup>
@ -68,23 +107,69 @@
<ItemGroup>
<Resource Include="app2.png" />
<Resource Include="CtrEditorE.png" />
<Resource Include="Icons\allselect.png" />
<Resource Include="Icons\analyze.png" />
<Resource Include="Icons\app.png" />
<Resource Include="Icons\app2.png" />
<Resource Include="Icons\borrar.png" />
<Resource Include="Icons\choose.png" />
<Resource Include="Icons\connect.png" />
<Resource Include="Icons\CtrEditorA.png" />
<Resource Include="Icons\CtrEditorC.png" />
<Resource Include="Icons\CtrEditorE.png" />
<Resource Include="Icons\disconnect.png" />
<Resource Include="Icons\duplicate.png" />
<Resource Include="Icons\extract.png">
<CopyToOutputDirectory></CopyToOutputDirectory>
</Resource>
<Resource Include="Icons\fotocelula.png" />
<Resource Include="Icons\match.png" />
<Resource Include="Icons\ocr.png" />
<Resource Include="Icons\rotationRx.cur" />
<Resource Include="Icons\rotationSx.cur" />
<Resource Include="Icons\save.png" />
<Resource Include="Icons\start.png" />
<Resource Include="Icons\stop.png" />
<Resource Include="Icons\unselect.png" />
<Resource Include="imagenes\filler.png" />
<Resource Include="imagenes\gear.png" />
<Resource Include="imagenes\motorNegro.png" />
<Resource Include="imagenes\motorVerde.png" />
<Resource Include="imagenes\tank.png" />
<EmbeddedResource Include="Images\base.png" />
</ItemGroup>
<ItemGroup>
<Compile Update="Properties\Settings.Designer.cs">
<DesignTimeSharedInput>True</DesignTimeSharedInput>
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<None Update="IA\appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
<None Update="Tesseract\eng.traineddata">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Tesseract\ita.traineddata">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Tesseract\spa.traineddata">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<Content Include="IA\appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>

View File

@ -3,7 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.9.34723.18
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CtrEditor", "CtrEditor.csproj", "{A4DC96D9-6C55-42ED-8E29-DCBC8D7AB831}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CtrEditor", "CtrEditor.csproj", "{A4DC96D9-6C55-42ED-8E29-DCBC8D7AB831}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibS7Adv", "..\Libraries\LibS7Adv\LibS7Adv.csproj", "{86A7FED2-AEB1-4766-819F-C6256FA7DD38}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -15,6 +17,10 @@ Global
{A4DC96D9-6C55-42ED-8E29-DCBC8D7AB831}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A4DC96D9-6C55-42ED-8E29-DCBC8D7AB831}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A4DC96D9-6C55-42ED-8E29-DCBC8D7AB831}.Release|Any CPU.Build.0 = Release|Any CPU
{86A7FED2-AEB1-4766-819F-C6256FA7DD38}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{86A7FED2-AEB1-4766-819F-C6256FA7DD38}.Debug|Any CPU.Build.0 = Debug|Any CPU
{86A7FED2-AEB1-4766-819F-C6256FA7DD38}.Release|Any CPU.ActiveCfg = Release|Any CPU
{86A7FED2-AEB1-4766-819F-C6256FA7DD38}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

383
DataStates/StateManager.cs Normal file
View File

@ -0,0 +1,383 @@
using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Threading.Tasks;
using Newtonsoft.Json;
using System.Linq;
using System.Windows.Controls;
using System.Windows;
using CtrEditor.ObjetosSim;
using LibS7Adv;
namespace CtrEditor
{
/// <summary>
/// Metadata de una página, incluyendo información sobre la imagen y configuración
/// </summary>
public class PageMetadata
{
public string PageId { get; set; }
public string PageName { get; set; }
public string ImagePath { get; set; }
public DateTime LastModified { get; set; }
public Dictionary<string, object> CustomMetadata { get; set; } = new();
}
/// <summary>
/// Estado de un objeto dentro de una página específica
/// </summary>
public class PageObjectState
{
public string GlobalObjectId { get; set; }
public bool IsVisible { get; set; } = true;
public float Left { get; set; }
public float Top { get; set; }
public float Rotation { get; set; }
public Dictionary<string, object> CustomProperties { get; set; } = new();
}
/// <summary>
/// Estado de una página específica
/// </summary>
public class PageState
{
public string PageId { get; set; }
public string PageName { get; set; }
public List<osBase> LocalObjects { get; set; } = new();
public Dictionary<string, PageObjectState> GlobalObjectStates { get; set; } = new();
public Dictionary<string, object> PageMetadata { get; set; } = new();
}
/// <summary>
/// Estado global de la aplicación
/// </summary>
public class GlobalState
{
public List<osBase> SharedObjects { get; set; } = new();
public Dictionary<string, PageMetadata> PagesMetadata { get; set; } = new();
public PLCViewModel PLCConfiguration { get; set; }
public UnitConverter UnitConverter { get; set; }
public int LastUsedId { get; set; }
}
/// <summary>
/// Gestor principal de estados de la aplicación
/// </summary>
public class StateManager : IDisposable
{
private readonly string _basePath;
private GlobalState _globalState;
private Dictionary<string, PageState> _loadedPages;
private string _currentPageId;
private readonly object _lockObject = new();
private bool _hasUnsavedChanges;
private MainViewModel _mainViewModel;
public event EventHandler<string> PageStateChanged;
public event EventHandler GlobalStateChanged;
public StateManager(string basePath, MainViewModel mainViewModel)
{
_basePath = basePath;
_mainViewModel = mainViewModel;
_loadedPages = new Dictionary<string, PageState>();
Directory.CreateDirectory(basePath);
}
public async Task InitializeAsync()
{
await LoadGlobalStateAsync();
if (_mainViewModel.SelectedImage != null)
{
await LoadPageStateAsync(_mainViewModel.SelectedImage);
}
}
private JsonSerializerSettings GetSerializerSettings()
{
return new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto,
ObjectCreationHandling = ObjectCreationHandling.Replace,
Formatting = Formatting.Indented,
NullValueHandling = NullValueHandling.Ignore
};
}
public async Task LoadGlobalStateAsync()
{
var path = Path.Combine(_basePath, "global.json");
if (File.Exists(path))
{
try
{
var json = await File.ReadAllTextAsync(path);
_globalState = JsonConvert.DeserializeObject<GlobalState>(json, GetSerializerSettings());
// Restaurar configuración global
_mainViewModel.PLCViewModel = _globalState.PLCConfiguration ?? new PLCViewModel();
if (_globalState.UnitConverter != null)
PixelToMeter.Instance.calc = _globalState.UnitConverter;
else
PixelToMeter.Instance.calc = new UnitConverter(1.0f); // Valor por defecto
// Restaurar objetos globales
foreach (var obj in _globalState.SharedObjects)
{
await RestoreObjectAsync(obj);
}
}
catch (Exception ex)
{
// Log error y crear nuevo estado global
_globalState = new GlobalState();
}
}
else
{
_globalState = new GlobalState();
}
}
public async Task SaveGlobalStateAsync()
{
var path = Path.Combine(_basePath, "global.json");
var backupPath = Path.ChangeExtension(path, ".bak");
// Actualizar estado global
_globalState.PLCConfiguration = _mainViewModel.PLCViewModel;
_globalState.UnitConverter = PixelToMeter.Instance.calc;
// Crear backup
if (File.Exists(path))
{
File.Copy(path, backupPath, true);
}
try
{
// Preparar objetos para serialización
foreach (var obj in _globalState.SharedObjects)
{
obj.SalvarDatosNoSerializables();
}
var json = JsonConvert.SerializeObject(_globalState, GetSerializerSettings());
await File.WriteAllTextAsync(path, json);
_hasUnsavedChanges = false;
GlobalStateChanged?.Invoke(this, EventArgs.Empty);
}
finally
{
// Restaurar estado de objetos
foreach (var obj in _globalState.SharedObjects)
{
obj.RestaurarDatosNoSerializables();
}
}
}
public async Task<PageState> LoadPageStateAsync(string pageId)
{
// Retornar página cacheada si existe
if (_loadedPages.TryGetValue(pageId, out var cachedState))
{
return cachedState;
}
var path = Path.Combine(_basePath, $"page_{pageId}.json");
try
{
PageState pageState;
if (File.Exists(path))
{
var json = await File.ReadAllTextAsync(path);
pageState = JsonConvert.DeserializeObject<PageState>(json, GetSerializerSettings());
}
else
{
pageState = new PageState
{
PageId = pageId,
PageName = _globalState.PagesMetadata.GetValueOrDefault(pageId)?.PageName ?? pageId
};
}
_loadedPages[pageId] = pageState;
// Restaurar objetos locales
foreach (var obj in pageState.LocalObjects)
{
await RestoreObjectAsync(obj);
}
// Aplicar estados de objetos globales
ApplyGlobalObjectStates(pageState);
return pageState;
}
catch (Exception ex)
{
// Log error
throw;
}
}
private void ApplyGlobalObjectStates(PageState pageState)
{
foreach (var kvp in pageState.GlobalObjectStates)
{
var globalObj = _globalState.SharedObjects.FirstOrDefault(o => o.Id.Value.ToString() == kvp.Key);
if (globalObj != null)
{
var state = kvp.Value;
globalObj.Left = state.Left;
globalObj.Top = state.Top;
globalObj.Angulo = state.Rotation;
// Actualizar Show_On_This_Page que ahora maneja internamente showOnThisPagesList
if (state.IsVisible && !globalObj.Show_On_This_Page)
globalObj.Show_On_This_Page = true;
else if (!state.IsVisible && globalObj.Show_On_This_Page)
globalObj.Show_On_This_Page = false;
// Aplicar propiedades personalizadas
foreach (var prop in state.CustomProperties)
{
var property = globalObj.GetType().GetProperty(prop.Key);
if (property != null && property.CanWrite)
{
property.SetValue(globalObj, prop.Value);
}
}
}
}
}
private async Task RestoreObjectAsync(osBase obj)
{
if (obj != null)
{
obj.CheckData();
await Task.Run(() =>
{
Application.Current.Dispatcher.Invoke(() =>
{
CrearUserControlDesdeObjetoSimulable(obj);
});
});
}
}
private bool CrearUserControlDesdeObjetoSimulable(osBase osObjeto)
{
Type tipoObjeto = osObjeto.GetType();
UserControl userControl = UserControlFactory.GetControlForType(tipoObjeto);
if (userControl != null)
{
UserControlFactory.AssignDatos(userControl, osObjeto, _mainViewModel.simulationManager);
osObjeto._mainViewModel = _mainViewModel;
if (osObjeto.Id == null)
{
osObjeto.Id = new UniqueId().ObtenerNuevaID();
}
_mainViewModel.MainWindow.AgregarRegistrarUserControlCanvas(userControl);
return true;
}
return false;
}
public async Task SavePageStateAsync(string pageId)
{
if (!_loadedPages.TryGetValue(pageId, out var pageState))
return;
var path = Path.Combine(_basePath, $"page_{pageId}.json");
var backupPath = Path.ChangeExtension(path, ".bak");
// Crear backup
if (File.Exists(path))
{
File.Copy(path, backupPath, true);
}
// Actualizar estado de objetos globales
pageState.GlobalObjectStates.Clear();
foreach (var obj in _mainViewModel.ObjetosSimulables.Where(o => o.Enable_On_All_Pages))
{
var currentPageId = _mainViewModel.SelectedImage;
pageState.GlobalObjectStates[obj.Id.Value.ToString()] = new PageObjectState
{
GlobalObjectId = obj.Id.Value.ToString(),
IsVisible = obj.Show_On_This_Page,
Left = obj.Left,
Top = obj.Top,
Rotation = obj.Angulo,
CustomProperties = CaptureCustomProperties(obj)
};
}
try
{
// Preparar objetos para serialización
foreach (var obj in pageState.LocalObjects)
{
obj.SalvarDatosNoSerializables();
}
var json = JsonConvert.SerializeObject(pageState, GetSerializerSettings());
await File.WriteAllTextAsync(path, json);
_hasUnsavedChanges = false;
PageStateChanged?.Invoke(this, pageId);
}
finally
{
// Restaurar estado de objetos
foreach (var obj in pageState.LocalObjects)
{
obj.RestaurarDatosNoSerializables();
}
}
}
private Dictionary<string, object> CaptureCustomProperties(osBase obj)
{
var customProps = new Dictionary<string, object>();
var properties = obj.GetType().GetProperties()
.Where(p => p.GetCustomAttributes(typeof(SerializeAttribute), true).Any());
foreach (var prop in properties)
{
customProps[prop.Name] = prop.GetValue(obj);
}
return customProps;
}
public async Task SaveAllAsync()
{
foreach (var pageId in _loadedPages.Keys)
{
await SavePageStateAsync(pageId);
}
await SaveGlobalStateAsync();
}
public void Dispose()
{
if (_hasUnsavedChanges)
{
SaveAllAsync().Wait();
}
}
}
/// <summary>
/// Atributo para marcar propiedades que deben ser serializadas como propiedades personalizadas
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public class SerializeAttribute : Attribute { }
}

View File

@ -28,6 +28,13 @@ namespace CtrEditor
return null;
}
public string? ObtenerPathAllPages(string extension)
{
if (!string.IsNullOrEmpty(EstadoPersistente.Instance.directorio))
return Path.Combine(EstadoPersistente.Instance.directorio, "AllPages" + extension);
return null;
}
public void CargarImagenes()
{
Imagenes.Clear();

68
FuncionesBase/Idiomas.cs Normal file
View File

@ -0,0 +1,68 @@
using LanguageDetection;
using System.Collections.Generic;
using System.ComponentModel;
using Xceed.Wpf.Toolkit.PropertyGrid.Attributes;
namespace CtrEditor.FuncionesBase
{
public class Idiomas
{
public const string DEFAULT_LANGUAGE = "English";
private static readonly Dictionary<string, string> _languageMap = new Dictionary<string, string>
{
{ "en", "English" },
{ "es", "Spanish" },
{ "it", "Italian" },
{ "pt", "Portuguese" },
{ "fr", "French" },
{ "de", "German" }
};
private static readonly LanguageDetector _languageDetector;
static Idiomas()
{
_languageDetector = new LanguageDetector();
_languageDetector.AddLanguages("en", "es", "it", "pt", "fr", "de");
}
public static List<string> GetLanguageList()
{
return new List<string>(_languageMap.Values);
}
public static string DetectarIdioma(string texto)
{
if (string.IsNullOrEmpty(texto)) return DEFAULT_LANGUAGE;
try
{
string detectedLanguageCode = _languageDetector.Detect(texto);
return _languageMap.GetValueOrDefault(detectedLanguageCode, DEFAULT_LANGUAGE);
}
catch
{
return DEFAULT_LANGUAGE;
}
}
public static string GetLanguageCode(string languageName)
{
return _languageMap.FirstOrDefault(x => x.Value == languageName).Key ?? "en";
}
}
public class IdiomasItemsSource<T> : IItemsSource
{
public ItemCollection GetValues()
{
ItemCollection items = new ItemCollection();
foreach (string language in Idiomas.GetLanguageList())
{
items.Add(language);
}
return items;
}
}
}

View File

@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
namespace CtrEditor.FuncionesBase
{
public struct MutableRect
{
public float Left { get; set; }
public float Top { get; set; }
public float Right { get; set; }
public float Bottom { get; set; }
public MutableRect(float left, float top, float right, float bottom)
{
Left = left;
Top = top;
Right = right;
Bottom = bottom;
}
public MutableRect(Rect r)
{
Left = (float) r.Left;
Top = (float)r.Top;
Right = (float)r.Right;
Bottom = (float)r.Bottom;
}
public float Width => Right - Left;
public float Height => Bottom - Top;
}
}

110
FuncionesBase/TagPattern.cs Normal file
View File

@ -0,0 +1,110 @@
using System;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using Xceed.Wpf.Toolkit.PropertyGrid.Attributes;
namespace CtrEditor.FuncionesBase
{
public class TagPattern
{
public const string DEFAULT_PATTERN = "DESCRIPCION";
private static readonly Dictionary<string, string> _patternDescriptions = new Dictionary<string, string>
{
{ "Descripcion", "First letter capitalized" },
{ "DESCRIPCION", "All uppercase text" },
{ "Siemens IO Input", "Format: Exxxxx.y (x: 0-65535, y: 0-7)" },
{ "Siemens IO Output", "Format: Axxxxx.y (x: 0-65535, y: 0-7)" },
{ "LETRASNUMEROS", "Format: ABC...123... (1-10 letters + numbers)" },
{ "LETRASNUMEROSESPACIOS", "Format: ABC...123... (letters, numbers and spaces allowed)" },
{ "Numero", "Numeric value" }
};
public static List<string> GetPatternList()
{
return new List<string>(_patternDescriptions.Keys);
}
public static string ApplyPattern(string text, string pattern)
{
if (string.IsNullOrEmpty(text)) return text;
return pattern switch
{
"Descripcion" => ApplyDescripcionPattern(text),
"DESCRIPCION" => text.ToUpper(),
"Siemens IO Input" => ApplySiemensPattern(text, "E"),
"Siemens IO Output" => ApplySiemensPattern(text, "A"),
"LETRASNUMEROS" => ApplyLetrasNumerosPattern(text),
"LETRASNUMEROSESPACIOS" => ApplyLetrasNumerosEspaciosPattern(text),
"Numero" => ApplyNumberPattern(text),
_ => text
};
}
private static string ApplyDescripcionPattern(string text)
{
if (string.IsNullOrEmpty(text)) return text;
return char.ToUpper(text[0]) + (text.Length > 1 ? text.Substring(1).ToLower() : "");
}
private static string ApplySiemensPattern(string text, string prefix)
{
var match = Regex.Match(text, $"{prefix}?(\\d{{1,5}})\\.?(\\d)?", RegexOptions.IgnoreCase);
if (match.Success)
{
int address = Math.Min(int.Parse(match.Groups[1].Value), 65535);
int bit = match.Groups[2].Success ?
Math.Min(int.Parse(match.Groups[2].Value), 7) : 0;
return $"{prefix}{address}.{bit}";
}
return $"{prefix}0.0"; // Default value if pattern doesn't match
}
private static string ApplyLetrasNumerosPattern(string text)
{
// Extract letters and numbers from the text
var letters = new string(text.Where(c => char.IsLetter(c)).Take(10).ToArray());
var numbers = new string(text.Where(c => char.IsDigit(c)).ToArray());
// If no letters found, return "A" as default
if (string.IsNullOrEmpty(letters))
letters = "A";
// If no numbers found, return "0" as default
if (string.IsNullOrEmpty(numbers))
numbers = "0";
// Combine letters (uppercase) and numbers
return $"{letters.ToUpper()}{numbers}";
}
private static string ApplyLetrasNumerosEspaciosPattern(string text)
{
// Keep only letters, numbers and spaces
var cleanedText = new string(text.Where(c => char.IsLetterOrDigit(c) || char.IsWhiteSpace(c)).ToArray());
// Convert to uppercase
return cleanedText.ToUpper();
}
private static string ApplyNumberPattern(string text)
{
var match = Regex.Match(text, @"-?\d+\.?\d*");
return match.Success ? match.Value : "0";
}
}
public class TagPatternItemsSource<T> : IItemsSource
{
public ItemCollection GetValues()
{
ItemCollection items = new ItemCollection();
foreach (string pattern in TagPattern.GetPatternList())
{
items.Add(pattern);
}
return items;
}
}
}

View File

@ -0,0 +1,16 @@
namespace CtrEditor.FuncionesBase
{
public enum ZIndexEnum
{
Decorativos = 1,
Estaticos = 2,
Generadores = 3,
Guias = 4,
ExtraccionTag = 5,
Dinamicos = 10,
Descarte = 11,
Fotocelula = 12,
Trace = 13,
RectangulosPropiead = 14
}
}

7
IA/appsettings.json Normal file
View File

@ -0,0 +1,7 @@
{
"ApiKeys": {
"OpenAI": "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",
"Groq": "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",
"Grok": "xai-yR7J4s9VIchdjdsaIMP3zzZJBPa98pySDbavh6hEosG3eII9OtQhoKsNUofKUsKrvNUIW15N11BD5cEa"
}
}

421
IA/gtpask.cs Normal file
View File

@ -0,0 +1,421 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using LanguageDetection;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Diagnostics;
using CtrEditor.FuncionesBase;
namespace GTPCorrgir
{
public class Opciones
{
public enum LLM_a_Usar
{
OpenAI,
Ollama,
Groq,
Grok
}
public enum modoDeUso
{
Corregir,
Ortografia,
Traducir_a_Ingles,
Traducir_a_Italiano,
Traducir_a_Espanol,
}
public Dictionary<LLM_a_Usar, string> nombreLLM = new Dictionary<LLM_a_Usar, string>
{
{ LLM_a_Usar.Ollama, "Ollama" },
{ LLM_a_Usar.Groq, "Groq" },
{ LLM_a_Usar.Grok, "Grok" },
{ LLM_a_Usar.OpenAI, "OpenAI" },
};
public LLM_a_Usar LLM { get; set; }
public modoDeUso modo { get; set; }
public string nombreDeLLM()
{
return nombreLLM[LLM];
}
public Opciones() { } // Changed from private to public
}
public class ApiSettings
{
public class ApiKeySection
{
public string OpenAI { get; set; }
public string Groq { get; set; }
public string Grok { get; set; }
}
public ApiKeySection ApiKeys { get; set; }
}
public class gtpask : IDisposable
{
private string _openAiApiKey;
private string _groqApiKey;
private string _grokApiKey;
private readonly HttpClient _httpClient;
private bool _disposed;
public string IdiomaDetectado { get; private set; }
public string TextoACorregir { get; set; }
public string TextoCorregido { get; private set; }
public string TextodeSistema { get; set; }
public gtpask()
{
try
{
_httpClient = new HttpClient();
LoadApiKeys();
InitializeHttpClient();
}
catch (Exception ex)
{
throw new ApplicationException("Failed to initialize gtpask", ex);
}
}
private void LoadApiKeys()
{
try
{
string configPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "IA/appsettings.json");
if (!File.Exists(configPath))
{
throw new FileNotFoundException("Configuration file (appsettings.json) not found.");
}
string jsonContent = File.ReadAllText(configPath);
var settings = JsonConvert.DeserializeObject<ApiSettings>(jsonContent);
_openAiApiKey = settings?.ApiKeys?.OpenAI;
_groqApiKey = settings?.ApiKeys?.Groq;
_grokApiKey = settings?.ApiKeys?.Grok;
ValidateApiKeys();
}
catch (Exception ex)
{
throw new ApplicationException("Failed to load API keys", ex);
}
}
private void ValidateApiKeys()
{
var missingKeys = new List<string>();
if (string.IsNullOrEmpty(_openAiApiKey)) missingKeys.Add("OpenAI");
if (string.IsNullOrEmpty(_groqApiKey)) missingKeys.Add("Groq");
if (string.IsNullOrEmpty(_grokApiKey)) missingKeys.Add("Grok");
if (missingKeys.Any())
{
string missingKeysStr = string.Join(", ", missingKeys);
throw new ApplicationException($"Missing API keys: {missingKeysStr}");
}
}
private void InitializeHttpClient()
{
_httpClient.Timeout = TimeSpan.FromSeconds(30);
_httpClient.DefaultRequestHeaders.Clear();
_httpClient.DefaultRequestHeaders.Add("Accept", "application/json");
}
private bool DetectarIdioma()
{
try
{
IdiomaDetectado = Idiomas.DetectarIdioma(TextoACorregir);
return IdiomaDetectado != "Unknown";
}
catch
{
return false;
}
}
private async Task ProcesarTextoConLLM( Opciones Modelo)
{
try
{
string respuestaLLM;
switch (Modelo.LLM)
{
case Opciones.LLM_a_Usar.OpenAI:
respuestaLLM = await CallOpenAiApi();
break;
case Opciones.LLM_a_Usar.Ollama:
respuestaLLM = await CallOllamaApi();
break;
case Opciones.LLM_a_Usar.Groq:
respuestaLLM = await CallGroqAiApi();
break;
case Opciones.LLM_a_Usar.Grok:
respuestaLLM = await CallGrokApi();
break;
default:
throw new ArgumentException("LLM no válido");
}
if (string.IsNullOrEmpty(respuestaLLM))
{
throw new ApplicationException("No se recibió respuesta del LLM");
}
TextoCorregido = respuestaLLM;
}
catch (Exception ex)
{
throw;
}
}
private async Task SimularCorreccion()
{
await Task.Delay(1000);
TextoCorregido = "Texto simulado de prueba";
}
private async Task<string> CallGrokApi()
{
try
{
_httpClient.DefaultRequestHeaders.Clear();
_httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_grokApiKey}");
var requestData = new
{
messages = new[]
{
new { role = "system", content = TextodeSistema },
new { role = "user", content = TextoACorregir }
},
model = "grok-beta",
stream = false,
temperature = 0
};
return await EnviarSolicitudLLM("https://api.x.ai/v1/chat/completions", requestData);
}
catch (Exception ex)
{
throw;
}
}
private async Task<string> CallOllamaApi()
{
try
{
var requestData = new
{
model = "llama3.2:latest",
messages = new[]
{
new { role = "system", content = TextodeSistema },
new { role = "user", content = TextoACorregir }
},
stream = false
};
return await EnviarSolicitudLLM("http://127.0.0.1:11434/api/chat", requestData);
}
catch (Exception ex)
{
throw;
}
}
private async Task<string> CallOpenAiApi()
{
try
{
_httpClient.DefaultRequestHeaders.Clear();
_httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_openAiApiKey}");
var requestData = new
{
model = "gpt-4o-mini",
messages = new[]
{
new { role = "system", content = TextodeSistema },
new { role = "user", content = TextoACorregir }
}
};
return await EnviarSolicitudLLM("https://api.openai.com/v1/chat/completions", requestData);
}
catch (Exception ex)
{
throw;
}
}
private async Task<string> CallGroqAiApi()
{
try
{
_httpClient.DefaultRequestHeaders.Clear();
_httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_groqApiKey}");
var requestData = new
{
model = "llama-3.2-3b-preview",
messages = new[]
{
new { role = "system", content = TextodeSistema },
new { role = "user", content = TextoACorregir }
},
max_tokens = 2048,
stream = false
};
return await EnviarSolicitudLLM("https://api.groq.com/openai/v1/chat/completions", requestData);
}
catch (Exception ex)
{
throw;
}
}
private async Task<string> EnviarSolicitudLLM(string endpoint, object requestData)
{
try
{
var content = new StringContent(
JsonConvert.SerializeObject(requestData),
Encoding.UTF8,
"application/json"
);
using var response = await _httpClient.PostAsync(endpoint, content);
var responseContent = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
{
throw new HttpRequestException(
$"Error en la solicitud HTTP: {response.StatusCode} - {responseContent}"
);
}
var data = JsonConvert.DeserializeObject<dynamic>(responseContent);
// Manejar diferentes formatos de respuesta según el LLM
if (endpoint.Contains("ollama"))
{
if (data.done == true && data.message != null)
{
return data.message.content;
}
throw new ApplicationException("Formato de respuesta de Ollama inválido");
}
else // OpenAI, Groq, Grok
{
if (data.choices != null && data.choices.Count > 0)
{
return data.choices[0].message.content;
}
throw new ApplicationException("No se encontró contenido en la respuesta del LLM");
}
}
catch (Exception ex)
{
throw;
}
}
public async Task CorregirTexto()
{
try
{
if (string.IsNullOrEmpty(TextoACorregir))
{
throw new ArgumentException("TextoACorregir cannot be null or empty");
}
if (string.IsNullOrEmpty(TextodeSistema))
{
throw new ArgumentException("TextodeSistema cannot be null or empty");
}
var opciones = new Opciones { LLM = Opciones.LLM_a_Usar.Grok };
await ProcesarTextoConLLM(opciones);
}
catch (Exception ex)
{
throw new LLMException("Error during text correction", ex);
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
_httpClient?.Dispose();
}
_disposed = true;
}
}
~gtpask()
{
Dispose(false);
}
}
// Clase auxiliar para manejar excepciones específicas de la aplicación
public class LLMException : Exception
{
public LLMException(string message) : base(message) { }
public LLMException(string message, Exception innerException) : base(message, innerException) { }
}
// Clase auxiliar para validación
public static class Validations
{
public static void ValidateNotNull(object value, string paramName)
{
if (value == null)
{
throw new ArgumentNullException(paramName);
}
}
public static void ValidateNotNullOrEmpty(string value, string paramName)
{
if (string.IsNullOrEmpty(value))
{
throw new ArgumentException("Value cannot be null or empty", paramName);
}
}
}
}

View File

@ -0,0 +1,147 @@
import pandas as pd
import os
import logging
import json
import PyLibrary.funciones_comunes as fc
from openai import OpenAI
from openai_api_key import openai_api_key
# Definir el logger a nivel de módulo
logger = None
def corregir_texto_batch(text_pairs, logger):
"""
Corrige errores de OCR en lotes de pares de texto usando GPT-4.
Cada par consiste en dos versiones del mismo texto en diferentes idiomas.
Args:
text_pairs: Lista de tuplas (texto1, texto2) donde cada texto es una lectura OCR
logger: Logger para registrar el proceso
"""
client = OpenAI(api_key=openai_api_key())
system_prompt = """You are an OCR correction specialist. For each pair of texts, which are OCR readings of the same text in different languages, analyze and correct any obvious OCR errors.
Pay special attention to:
- Incorrectly joined words (missing spaces)
- Wrong character recognition (0 vs O, 1 vs I, etc.)
- Extra or missing characters
Return the corrected versions maintaining the general structure and meaning.
Input format: List of text pairs
Expected output format: List of corrected pairs in the same order
Example input: [["PULSANTE DI STARTNASTRI", "START PUSHBUTTONTTOP"]]
Example output: [["PULSANTE DI START NASTRI", "START PUSH BUTTON TOP"]]"""
try:
# Convertir los pares de texto a formato JSON
request_payload = json.dumps({"pairs": text_pairs})
logger.info(f"Solicitando corrección para el lote de textos:\n{request_payload}")
response = client.chat.completions.create(
model="gpt-4",
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": request_payload},
],
max_tokens=2000,
temperature=0.3,
)
# Procesar la respuesta
corrections = json.loads(response.choices[0].message.content)
logger.info(f"Correcciones recibidas:\n{corrections}")
return corrections["pairs"]
except Exception as e:
logger.error(f"Error en la corrección por lotes: {str(e)}")
return None
def procesar_archivo(config, archivo_entrada):
"""
Procesa el archivo Excel con los textos OCR y genera un nuevo archivo con las correcciones.
"""
logger.info(f"Iniciando procesamiento de {archivo_entrada}")
try:
# Leer el archivo de entrada
df = fc.read_dataframe_with_cleanup_retries(archivo_entrada)
if config.columna_origen1 not in df.columns or config.columna_origen2 not in df.columns:
logger.error(f"Columnas requeridas no encontradas en el archivo")
print(f"Error: Las columnas {config.columna_origen1} y/o {config.columna_origen2} no existen en el archivo")
return
# Filtrar filas donde ambas columnas tienen contenido
df_filtered = df.dropna(subset=[config.columna_origen1, config.columna_origen2])
df_filtered = df_filtered[
(df_filtered[config.columna_origen1].astype(str).str.strip() != '') &
(df_filtered[config.columna_origen2].astype(str).str.strip() != '')
]
# Crear columnas para textos corregidos
df[f"{config.columna_origen1}_Corregido"] = df[config.columna_origen1]
df[f"{config.columna_origen2}_Corregido"] = df[config.columna_origen2]
# Procesar en lotes de 10 pares
batch_size = 10
total_rows = len(df_filtered)
progress_bar = fc.ProgressBar(
total_rows, prefix="Procesando textos:", suffix="Completado"
)
for i in range(0, total_rows, batch_size):
batch_df = df_filtered.iloc[i:i+batch_size]
# Preparar pares de textos para el lote
text_pairs = [
[str(row[config.columna_origen1]).strip(), str(row[config.columna_origen2]).strip()]
for _, row in batch_df.iterrows()
]
# Obtener correcciones para el lote
corrections = corregir_texto_batch(text_pairs, logger)
if corrections:
# Aplicar correcciones al DataFrame original
for j, correction in enumerate(corrections):
idx = batch_df.index[j]
df.at[idx, f"{config.columna_origen1}_Corregido"] = correction[0]
df.at[idx, f"{config.columna_origen2}_Corregido"] = correction[1]
progress_bar.update(min(i + batch_size, total_rows))
progress_bar.finish()
# Guardar resultados
output_path = config.get_output_path()
fc.save_dataframe_with_retries(df, output_path)
logger.info(f"Archivo procesado y guardado en: {output_path}")
print(f"\nArchivo procesado y guardado en: {output_path}")
print(f"Se procesaron {total_rows} pares de texto.")
except Exception as e:
logger.error(f"Error durante el procesamiento: {str(e)}")
print(f"Error durante el procesamiento: {str(e)}")
def run(config):
global logger
logger = fc.configurar_logger(config.work_dir)
script_name = os.path.basename(__file__)
print(f"\rIniciando: {script_name}\r")
# Solicitar archivo de entrada
from tkinter import filedialog
archivo_entrada = filedialog.askopenfilename(
title="Seleccione el archivo con textos OCR",
filetypes=[("Excel files", "*.xls*")]
)
if archivo_entrada:
procesar_archivo(config, archivo_entrada)
else:
print("No se seleccionó ningún archivo para procesar.")
if __name__ == "__main__":
import menu_ocr_correction
menu_ocr_correction.main()

BIN
Icons/allselect.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

BIN
Icons/analyze.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

BIN
Icons/choose.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
Icons/extract.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

BIN
Icons/match.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

BIN
Icons/ocr.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
Icons/rotationRx.cur Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
Icons/rotationSx.cur Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
Icons/unselect.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

BIN
Images/base.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

File diff suppressed because it is too large Load Diff

View File

@ -1,42 +1,53 @@
<Window
xmlns:ctreditor="clr-namespace:CtrEditor"
<Window xmlns:ctreditor="clr-namespace:CtrEditor"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Siemens="clr-namespace:CtrEditor.Siemens"
xmlns:convert="clr-namespace:CtrEditor.Convertidores"
xmlns:ObjetosSim="clr-namespace:CtrEditor.ObjetosSim" x:Class="CtrEditor.MainWindow"
Height="900" Width="1600" WindowState="Maximized"
ResizeMode="CanResize" Title="{Binding directorioTrabajo}" Icon="/app2.png">
xmlns:Siemens="clr-namespace:LibS7Adv;assembly=LibS7Adv"
xmlns:local="clr-namespace:CtrEditor"
xmlns:controls="clr-namespace:CtrEditor.Controls"
xmlns:uc="clr-namespace:CtrEditor.ObjetosSim.UserControls"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:ObjetosSim="clr-namespace:CtrEditor.ObjetosSim"
xmlns:ObjetosExtraccion="clr-namespace:CtrEditor.ObjetosSim.Extraccion_Datos"
x:Class="CtrEditor.MainWindow"
Height="900" Width="1600" WindowState="Maximized" ResizeMode="CanResize" Title="{Binding directorioTrabajo, Converter={StaticResource UnsavedChangesConverter}}"
Icon="/app2.png">
<Window.DataContext>
<ctreditor:MainViewModel/>
<ctreditor:MainViewModel />
</Window.DataContext>
<Window.Resources>
<convert:FloatToFormattedStringConverter x:Key="floatFormatter"/>
<convert:DoubleToFormattedStringConverter x:Key="doubleFormatter"/>
<convert:BrushToColorNameConverter x:Key="BrushToColorNameConverter"/>
<!-- Style for Start/Stop Button -->
<Style x:Key="StartStopButtonStyle" TargetType="Button">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Background" Value="Transparent" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsSimulationRunning}" Value="True">
<Setter Property="Background" Value="LightGreen"/>
<Setter Property="Background" Value="LightGreen" />
</DataTrigger>
</Style.Triggers>
</Style>
<!-- Style for Connect/Disconnect Button -->
<Style x:Key="ConnectDisconnectButtonStyle" TargetType="Button">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Background" Value="Transparent" />
<Setter Property="Padding" Value="5" />
<Setter Property="MinWidth" Value="80" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsConnected}" Value="True">
<Setter Property="Background" Value="LightGreen"/>
<Setter Property="Background" Value="#90EE90" />
</DataTrigger>
</Style.Triggers>
</Style>
<!-- Converter for PLC Connect/Disconnect button text -->
<local:ConnectStateToBtnTextConverter x:Key="ConnectStateToBtnTextConverter" />
<!-- Converter for PLC Connect/Disconnect image -->
<local:ConnectStateToImageConverter x:Key="ConnectStateToImageConverter" />
</Window.Resources>
<Grid>
@ -44,8 +55,17 @@
<Menu VerticalAlignment="Top" HorizontalAlignment="Stretch">
<MenuItem Header="Projecto">
<MenuItem Header="Abrir Directorio de trabajo" Command="{Binding OpenWorkDirectoryCommand}" />
<MenuItem Header="Ultimos Directorios Utilizados" ItemsSource="{Binding RecentDirectories}">
<MenuItem.ItemTemplate>
<DataTemplate>
<MenuItem Header="{Binding}" Command="{Binding DataContext.OpenRecentDirectoryCommand, RelativeSource={RelativeSource AncestorType=MenuItem, AncestorLevel=2}}"
CommandParameter="{Binding}"/>
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
<MenuItem Header="Iniciar Simulacion" Command="{Binding StartSimulationCommand}" />
<MenuItem Header="Detenet Simulacion" Command="{Binding StopSimulationCommand}" />
<MenuItem Header="Debug Window" Command="{Binding DebugWindowCommand}" />
<MenuItem Header="Guardar" Command="{Binding SaveCommand}" />
<MenuItem Header="Salir" Command="{Binding ExitCommand}" />
</MenuItem>
@ -54,27 +74,31 @@
<Grid Margin="0,20,0,0">
<!-- Margen superior para el menú -->
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" MinWidth="100"/>
<ColumnDefinition Width="8*" MinWidth="200"/>
<ColumnDefinition Width="2*" MinWidth="100"/>
<ColumnDefinition Width="1*" MinWidth="200" />
<ColumnDefinition Width="8*" MinWidth="200" />
<ColumnDefinition Width="2*" MinWidth="100" />
</Grid.ColumnDefinitions>
<!-- Primera Columna -->
<Grid Grid.Column="0">
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
<RowDefinition Height="2*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="1*" />
<RowDefinition Height="2*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListBox x:Name="ListaImagenes" Grid.Row="0" Margin="5" ItemsSource="{Binding ListaImagenes}" SelectedItem="{Binding SelectedImage}" />
<ListBox x:Name="ListaFunciones" Grid.Row="1" Margin="5" ItemsSource="{Binding ListaOsBase}" DisplayMemberPath="Nombre" SelectedItem="{Binding SelectedItem}">
<ListBox x:Name="ListaImagenes" Grid.Row="0" Margin="5" ItemsSource="{Binding ListaImagenes}"
SelectedItem="{Binding SelectedImage}" />
<ListBox x:Name="ListaFunciones" Grid.Row="1" Margin="5" ItemsSource="{Binding ListaOsBase}"
DisplayMemberPath="Nombre" SelectedItem="{Binding SelectedItem}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<i:InvokeCommandAction Command="{Binding ItemDoubleClickCommand}" CommandParameter="{Binding SelectedItem}"/>
<i:InvokeCommandAction Command="{Binding ItemDoubleClickCommand}"
CommandParameter="{Binding SelectedItem}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBox>
<Siemens:PLCControl x:Name="PLCSim" Grid.Row="2" Margin="5" DataContext="{Binding PLCViewModel}"/>
<Siemens:PLCControl x:Name="PLCSim" Grid.Row="2" Margin="5" DataContext="{Binding PLCViewModel}" />
</Grid>
<!-- GridSplitter -->
<GridSplitter Grid.Column="0" Grid.Row="0" HorizontalAlignment="Right" Width="5" Background="LightGray" />
@ -88,45 +112,100 @@
<ToolBarTray Grid.Row="0">
<ToolBar>
<Button Command="{Binding TBStartSimulationCommand}" ToolTip="Iniciar Simulación" Style="{StaticResource StartStopButtonStyle}">
<StackPanel>
<Image Source="Icons/start.png" Width="16" Height="16"/>
<TextBlock Text="Iniciar"/>
</StackPanel>
</Button>
<StackPanel Orientation="Horizontal">
<Button Command="{Binding TBStartSimulationCommand}" ToolTip="Iniciar Simulación"
Style="{StaticResource StartStopButtonStyle}">
<StackPanel>
<Image Source="Icons/start.png" Width="24" Height="24" />
<TextBlock Text="Iniciar" />
</StackPanel>
</Button>
<!-- Grid para el indicador de Simulación -->
<Grid Width="15" Margin="2,0">
<TextBlock Text="{Binding SimulationSpeed, StringFormat={}{0:F1}}"
RenderTransformOrigin="0.5,0.5" TextAlignment="Center"
Background="{DynamicResource {x:Static SystemColors.ActiveCaptionBrushKey}}">
<TextBlock.LayoutTransform>
<RotateTransform Angle="-90"/>
</TextBlock.LayoutTransform>
</TextBlock>
</Grid>
</StackPanel>
<Button Command="{Binding TBStopSimulationCommand}" ToolTip="Detener Simulación">
<StackPanel>
<Image Source="Icons/stop.png" Width="16" Height="16"/>
<TextBlock Text="Detener"/>
<Image Source="Icons/stop.png" Width="24" Height="24" />
<TextBlock Text="Detener" />
</StackPanel>
</Button>
<Button Command="{Binding TBSaveCommand}" ToolTip="Guardar">
<StackPanel>
<Image Source="Icons/save.png" Width="16" Height="16"/>
<TextBlock Text="Guardar"/>
<Image Source="Icons/save.png" Width="24" Height="24" />
<TextBlock Text="Guardar" />
</StackPanel>
</Button>
<Button Command="{Binding TBConnectPLCCommand}" ToolTip="Conectar PLC" Style="{StaticResource ConnectDisconnectButtonStyle}">
<StackPanel Orientation="Horizontal">
<Button Command="{Binding TBTogglePLCConnectionCommand}"
ToolTip="{Binding IsConnected, Converter={StaticResource ConnectStateToBtnTextConverter}}"
Style="{StaticResource ConnectDisconnectButtonStyle}">
<StackPanel>
<Image Source="{Binding IsConnected, Converter={StaticResource ConnectStateToImageConverter}}"
Width="24" Height="24" />
<TextBlock Text="{Binding IsConnected, Converter={StaticResource ConnectStateToBtnTextConverter}}" />
</StackPanel>
</Button>
<!-- Grid para el indicador de PLC -->
<Grid Width="15" Margin="2,0">
<TextBlock Text="{Binding PlcUpdateSpeed, StringFormat={}{0:F1}}"
RenderTransformOrigin="0.5,0.5" TextAlignment="Center"
Background="{DynamicResource {x:Static SystemColors.ActiveCaptionBrushKey}}">
<TextBlock.LayoutTransform>
<RotateTransform Angle="-90"/>
</TextBlock.LayoutTransform>
</TextBlock>
</Grid>
</StackPanel>
<Button Command="{Binding TBMultiPageAnalizeCommand}"
ToolTip="Analyze Tags in multiple pages.">
<StackPanel>
<Image Source="Icons/connect.png" Width="16" Height="16"/>
<TextBlock Text="Conectar"/>
<Image Source="Icons/match.png" Width="24" Height="24" />
<TextBlock Text="Multi Page Analyze" />
</StackPanel>
</Button>
<Button Command="{Binding TBDisconnectPLCCommand}" ToolTip="Desconectar PLC">
<Button Command="{Binding TBMultiPageMatrixCommand}" ToolTip="Analyze Matrix (Multiple Pages)">
<StackPanel>
<Image Source="Icons/disconnect.png" Width="16" Height="16"/>
<TextBlock Text="Desconectar"/>
<Image Source="Icons/ocr.png" Width="24" Height="24" />
<TextBlock Text="Exportar Tags a Excel" />
</StackPanel>
</Button>
</ToolBar>
</ToolBarTray>
<ScrollViewer Grid.Row="1" x:Name="ImagenEnTrabajoScrollViewer" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" PanningMode="Both">
<Canvas x:Name="ImagenEnTrabajoCanvas" Margin="400">
<!-- El Margin puede ser ajustado según el espacio adicional que quieras proporcionar -->
<Canvas.LayoutTransform>
<ScaleTransform ScaleX="1" ScaleY="1"/>
</Canvas.LayoutTransform>
<ScrollViewer Grid.Row="1" x:Name="ImagenEnTrabajoScrollViewer"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto"
PanningMode="Both"
PreviewKeyDown="ScrollViewer_PreviewKeyDown">
<Canvas x:Name="ImagenEnTrabajoCanvas"
Margin="0"
Background="Transparent"
Focusable="True"
FocusVisualStyle="{x:Null}"
KeyDown="Canvas_KeyDown">
<!-- Agregar Background="Transparent" para que capture los eventos del mouse y -->
<!-- asegurar que el Canvas reciba los eventos del botón derecho -->
<Canvas.RenderTransform>
<TransformGroup>
<ScaleTransform x:Name="CanvasScaleTransform" ScaleX="1" ScaleY="1" />
<TranslateTransform x:Name="CanvasTranslateTransform" />
</TransformGroup>
</Canvas.RenderTransform>
</Canvas>
</ScrollViewer>
</Grid>
@ -137,51 +216,114 @@
<!-- Tercera Columna -->
<Grid Grid.Column="2">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<!-- Altura ajustable para el ListBox -->
<RowDefinition Height="Auto" />
<!-- Espacio para el GridSplitter -->
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<!-- Altura ajustable para el PanelEdicion -->
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ToolBarTray Grid.Row="0">
<ToolBar>
<Button Command="{Binding TBEliminarTodosCommand}" ToolTip="Eliminar Todos">
<StackPanel>
<Image Source="Icons/borrar.png" Width="16" Height="16" />
<TextBlock Text="All Objects" />
</StackPanel>
</Button>
<Button Command="{Binding TBEliminarAutoCreatedCommand}" ToolTip="Eliminar Auto">
<StackPanel>
<Image Source="Icons/borrar.png" Width="16" Height="16" />
<TextBlock Text="Auto Created" />
</StackPanel>
</Button>
<Button Command="{Binding TBEliminarClonedCommand}" ToolTip="Eliminar Cloned for tag Extraction">
<StackPanel>
<Image Source="Icons/borrar.png" Width="16" Height="16" />
<TextBlock Text="Cloned" />
</StackPanel>
</Button>
<Button Command="{Binding TBAssingPagesCommand}" ToolTip="Assing Pages">
<StackPanel>
<Image Source="Icons/choose.png" Width="16" Height="16" />
<TextBlock Text="Assing Pages" />
</StackPanel>
</Button>
</ToolBar>
</ToolBarTray>
<!-- Filter Control -->
<Expander Grid.Row="1" Header="Filters" IsExpanded="False">
<controls:osVisFilter x:Name="VisFilter" Margin="5"/>
</Expander>
<!-- ListBox -->
<ListBox x:Name="ListaOs"
Grid.Row="0"
Margin="5"
ItemsSource="{Binding ObjetosSimulables}"
DisplayMemberPath="Nombre"
SelectedItem="{Binding SelectedItemOsList, Mode=TwoWay}"
SelectionChanged="ListaOs_SelectionChanged"/>
<Expander Grid.Row="2" Header="Objects List" IsExpanded="False">
<ListBox x:Name="ListaOs"
Margin="5"
MinHeight="100"
MaxHeight="300"
ItemsSource="{Binding ObjetosSimulables}"
SelectedItem="{Binding SelectedItemOsList, Mode=TwoWay}"
SelectionChanged="ListaOs_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Nombre}">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Enable_On_All_Pages}" Value="True">
<Setter Property="Foreground" Value="Blue" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=Enable_On_All_Pages}" Value="False">
<Setter Property="Foreground" Value="Black" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=IsVisFilter}" Value="False">
<Setter Property="Opacity" Value="0.5" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Expander>
<!-- Object Hierarchy -->
<Expander Grid.Row="3" Header="Object Hierarchy" IsExpanded="True">
<controls:ObjectHierarchyView x:Name="ObjectHierarchy"
Margin="5"
MinHeight="100"
MaxHeight="300"/>
</Expander>
<!-- GridSplitter -->
<GridSplitter Grid.Row="1"
Height="5"
HorizontalAlignment="Stretch"
Background="Gray"
ResizeDirection="Rows"
VerticalAlignment="Center"/>
<GridSplitter Grid.Row="4" Height="5"
HorizontalAlignment="Stretch"
Background="Gray"
ResizeDirection="Rows"
VerticalAlignment="Center" />
<!-- PanelEdicion -->
<ScrollViewer Grid.Row="2" Margin="5" VerticalScrollBarVisibility="Auto">
<StackPanel x:Name="PanelEdicion">
<!-- Aquí puedes agregar los controles para editar propiedades -->
</StackPanel>
</ScrollViewer>
<controls:PanelEdicionControl Grid.Row="5"
x:Name="PanelEdicion"
Margin="5"/>
<ToolBarTray Grid.Row="3">
<ToolBarTray Grid.Row="6">
<ToolBar>
<Button Command="{Binding TBEliminarUserControlCommand}" ToolTip="Eliminar Control">
<StackPanel>
<Image Source="Icons/borrar.png" Width="16" Height="16"/>
<TextBlock Text="Eliminar"/>
<Image Source="Icons/borrar.png" Width="16" Height="16" />
<TextBlock Text="Eliminar" />
</StackPanel>
</Button>
<Button Command="{Binding TBDuplicarUserControlCommand}" ToolTip="Duplicar Control">
<StackPanel>
<Image Source="Icons/duplicate.png" Width="16" Height="16"/>
<TextBlock Text="Duplicar"/>
<Image Source="Icons/duplicate.png" Width="16" Height="16" />
<TextBlock Text="Duplicar" />
</StackPanel>
</Button>
</ToolBar>

File diff suppressed because it is too large Load Diff

268
ObjectAlignment.cs Normal file
View File

@ -0,0 +1,268 @@
using CtrEditor.ObjetosSim;
using System.Collections.ObjectModel;
using System.Windows;
namespace CtrEditor
{
public class ObjectAlignment
{
private readonly ObservableCollection<osBase> _selectedObjects;
private readonly osBase _referenceObject;
public ObjectAlignment(ObservableCollection<osBase> selectedObjects, osBase referenceObject = null)
{
_selectedObjects = selectedObjects;
_referenceObject = referenceObject ?? selectedObjects.FirstOrDefault();
}
public void AlignLeft()
{
if (_selectedObjects.Count <= 1) return;
float leftMost = _selectedObjects.Min(obj => obj.Left);
foreach (var obj in _selectedObjects)
{
obj.Left = leftMost;
}
}
public void AlignRight()
{
if (_selectedObjects.Count <= 1) return;
float rightMost = _selectedObjects.Max(obj => obj.Left + obj.Ancho);
foreach (var obj in _selectedObjects)
{
obj.Left = rightMost - obj.Ancho;
}
}
public void AlignTop()
{
if (_selectedObjects.Count <= 1) return;
float topMost = _selectedObjects.Min(obj => obj.Top);
foreach (var obj in _selectedObjects)
{
obj.Top = topMost;
}
}
public void AlignBottom()
{
if (_selectedObjects.Count <= 1) return;
float bottomMost = _selectedObjects.Max(obj => obj.Top + obj.Alto);
foreach (var obj in _selectedObjects)
{
obj.Top = bottomMost - obj.Alto;
}
}
public void AlignCenterHorizontally()
{
if (_selectedObjects.Count <= 1) return;
float averageY = _selectedObjects.Average(obj => obj.Top + obj.Alto / 2);
foreach (var obj in _selectedObjects)
{
obj.Top = averageY - obj.Alto / 2;
}
}
public void AlignCenterVertically()
{
if (_selectedObjects.Count <= 1) return;
float averageX = _selectedObjects.Average(obj => obj.Left + obj.Ancho / 2);
foreach (var obj in _selectedObjects)
{
obj.Left = averageX - obj.Ancho / 2;
}
}
public void DistributeHorizontally()
{
if (_selectedObjects.Count <= 2) return;
var objectsWithCenters = _selectedObjects
.Select(obj => new
{
Object = obj,
Center = GetObjectCenter(obj),
Dimensions = GetEffectiveDimensions(obj)
})
.OrderBy(x => x.Center.X)
.ToList();
float leftMost = (float)objectsWithCenters.First().Center.X;
float rightMost = (float)objectsWithCenters.Last().Center.X;
float totalDistance = rightMost - leftMost;
float spacing = totalDistance / (_selectedObjects.Count - 1);
for (int i = 1; i < objectsWithCenters.Count - 1; i++)
{
var obj = objectsWithCenters[i];
var targetX = leftMost + (spacing * i);
float deltaX = (float)(targetX - obj.Center.X);
obj.Object.Left += deltaX;
}
}
public void DistributeVertically()
{
if (_selectedObjects.Count <= 2) return;
var objectsWithCenters = _selectedObjects
.Select(obj => new
{
Object = obj,
Center = GetObjectCenter(obj),
Dimensions = GetEffectiveDimensions(obj)
})
.OrderBy(x => x.Center.Y)
.ToList();
float topMost = (float)objectsWithCenters.First().Center.Y;
float bottomMost = (float)objectsWithCenters.Last().Center.Y;
float totalDistance = bottomMost - topMost;
float spacing = totalDistance / (_selectedObjects.Count - 1);
for (int i = 1; i < objectsWithCenters.Count - 1; i++)
{
var obj = objectsWithCenters[i];
var targetY = topMost + (spacing * i);
float deltaY = (float)(targetY - obj.Center.Y);
obj.Object.Top += deltaY;
}
}
public void EqualWidth()
{
if (_selectedObjects.Count <= 1) return;
float referenceWidth = GetEffectiveDimensions(_referenceObject).Width;
foreach (var obj in _selectedObjects.Where(o => o != _referenceObject))
{
var currentDims = GetEffectiveDimensions(obj);
SetEffectiveDimensions(obj, referenceWidth, currentDims.Height);
}
}
public void EqualHeight()
{
if (_selectedObjects.Count <= 1) return;
float referenceHeight = GetEffectiveDimensions(_referenceObject).Height;
foreach (var obj in _selectedObjects.Where(o => o != _referenceObject))
{
var currentDims = GetEffectiveDimensions(obj);
SetEffectiveDimensions(obj, currentDims.Width, referenceHeight);
}
}
public void EqualAngle()
{
if (_selectedObjects.Count <= 1) return;
float referenceAngle = _referenceObject.Angulo;
foreach (var obj in _selectedObjects.Where(o => o != _referenceObject))
{
obj.Angulo = referenceAngle;
}
}
public void JoinHorizontally()
{
if (_selectedObjects.Count <= 1) return;
var sortedObjects = _selectedObjects
.OrderBy(obj => GetObjectCenter(obj).X)
.ToList();
for (int i = 1; i < sortedObjects.Count; i++)
{
var previousObj = sortedObjects[i - 1];
var currentObj = sortedObjects[i];
var previousCenter = GetObjectCenter(previousObj);
var currentCenter = GetObjectCenter(currentObj);
var previousDims = GetEffectiveDimensions(previousObj);
float offset = previousDims.Width / 2;
float newX = (float)(previousCenter.X + offset);
float deltaX = (float)(newX - (currentCenter.X - GetEffectiveDimensions(currentObj).Width / 2));
currentObj.Left += deltaX;
}
}
public void JoinVertically()
{
if (_selectedObjects.Count <= 1) return;
var sortedObjects = _selectedObjects
.OrderBy(obj => GetObjectCenter(obj).Y)
.ToList();
for (int i = 1; i < sortedObjects.Count; i++)
{
var previousObj = sortedObjects[i - 1];
var currentObj = sortedObjects[i];
var previousCenter = GetObjectCenter(previousObj);
var currentCenter = GetObjectCenter(currentObj);
var previousDims = GetEffectiveDimensions(previousObj);
float offset = previousDims.Height / 2;
float newY = (float)(previousCenter.Y + offset);
float deltaY = (float)(newY - (currentCenter.Y - GetEffectiveDimensions(currentObj).Height / 2));
currentObj.Top += deltaY;
}
}
private Point GetObjectCenter(osBase obj)
{
double angleRad = obj.Angulo * Math.PI / 180.0;
float centerX = obj.Left + (obj.Ancho / 2);
float centerY = obj.Top + (obj.Alto / 2);
if (obj.Angulo != 0)
{
var rotatedX = obj.Left + (Math.Cos(angleRad) * obj.Ancho / 2 - Math.Sin(angleRad) * obj.Alto / 2);
var rotatedY = obj.Top + (Math.Sin(angleRad) * obj.Ancho / 2 + Math.Cos(angleRad) * obj.Alto / 2);
return new Point(rotatedX, rotatedY);
}
return new Point(centerX, centerY);
}
private bool IsObjectVertical(osBase obj)
{
double normalizedAngle = obj.Angulo % 180;
if (normalizedAngle < 0) normalizedAngle += 180;
return normalizedAngle >= 45 && normalizedAngle < 135;
}
private (float Width, float Height) GetEffectiveDimensions(osBase obj)
{
if (IsObjectVertical(obj))
{
return (obj.Alto, obj.Ancho);
}
return (obj.Ancho, obj.Alto);
}
private void SetEffectiveDimensions(osBase obj, float width, float height)
{
if (IsObjectVertical(obj))
{
obj.Alto = width;
obj.Ancho = height;
}
else
{
obj.Ancho = width;
obj.Alto = height;
}
}
}
}

View File

@ -0,0 +1,985 @@
using CtrEditor.FuncionesBase;
using CtrEditor.ObjetosSim;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
using Color = System.Windows.Media.Color;
namespace CtrEditor
{
public enum AlignmentType
{
Left,
Right,
Top,
Bottom,
CenterHorizontally,
CenterVertically,
DistributeHorizontally,
DistributeVertically,
EqualWidth,
EqualHeight,
EqualAngle,
JoinHorizontally,
JoinVertically
}
public class ObjectManipulationManager
{
private enum ResizeMode
{
None,
Move,
Rotate,
ResizeWidth,
ResizeHeight,
ResizeBoth
}
private enum HandleMode
{
None,
Move,
ResizeWidth,
ResizeHeight,
ResizeBoth,
Rotate
}
private ResizeMode _currentResizeMode;
private readonly MainWindow _mainWindow;
private readonly Canvas _canvas;
private Point _lastMousePosition;
private bool _isDrawingCanvas = false;
private bool _isDraggingUserControl = false;
private bool _isMovingUserControl = false;
private UserControl _currentDraggingControl;
private Point _startPointUserControl;
private Point _transformedBoundingBoxCenter;
private float _lastAngle;
private List<Rectangle> _resizeRectangles = new List<Rectangle>();
private List<Rectangle> _highlightRectangles = new List<Rectangle>();
private System.Threading.Timer _timerRemoveHighlight = null;
private Rectangle _currentDraggingRectangle;
private dataDebug _dataDebug = new dataDebug();
private ObservableCollection<osBase> _selectedObjects = new ObservableCollection<osBase>();
private List<Rectangle> _selectionHighlightRectangles = new List<Rectangle>(); // Add this line
private List<(UserControl Control, Rectangle Highlight)> _selectionHighlightPairs = new List<(UserControl, Rectangle)>();
private Point _rectangleStart;
public Rectangle _selectionRectangle; // Cambiado a private
private ScrollViewer _scrollViewer; // Add this line
private Image _backgroundImage; // Add this line
internal bool IsDraggingCanvas { get; set; }
private bool _isRectangleSelectionActive;
public bool IsRectangleSelectionActive
{
get => _isRectangleSelectionActive;
set
{
_isRectangleSelectionActive = value;
if (value)
{
// Al activar la selección por rectángulo, desactivar el panning
IsDraggingCanvas = false;
}
}
}
// Propiedad pública para verificar si hay un rectángulo de selección activo
public bool HasActiveSelectionRectangle => _selectionRectangle != null;
public ObjectManipulationManager(MainWindow mainWindow, Canvas canvas)
{
_mainWindow = mainWindow;
_canvas = canvas;
_scrollViewer = mainWindow.ImagenEnTrabajoScrollViewer; // Add this line
_backgroundImage = mainWindow.ImagenDeFondo; // Add this line
}
private void PurgeDeletedObjects()
{
var deletedObjects = _selectedObjects.Where(obj =>
obj.VisualRepresentation == null ||
!_canvas.Children.Contains(obj.VisualRepresentation)).ToList();
foreach (var obj in deletedObjects)
{
DeselectObject(obj);
}
}
public ObservableCollection<osBase> SelectedObjects
{
get
{
PurgeDeletedObjects();
return _selectedObjects;
}
private set
{
_selectedObjects = value;
UpdateSelectionVisuals();
}
}
public void UpdateSelectionVisuals()
{
PurgeDeletedObjects();
// Asegurarse de que el canvas haya actualizado su layout
_canvas.UpdateLayout();
RemoveResizeRectangles();
if (_selectedObjects.Any())
{
// Verificar si la simulación está activa
if (_mainWindow.DataContext is MainViewModel viewModel && !viewModel.IsSimulationRunning)
{
AddResizeRectangles(_selectedObjects);
UpdateSelectionHighlights();
}
}
}
public void SuscribirEventos(UserControl userControl)
{
userControl.MouseEnter += UserControl_MouseEnter;
userControl.MouseLeave += UserControl_MouseLeave;
userControl.Cursor = Cursors.Hand;
userControl.MouseLeftButtonDown += UserControl_MouseLeftButtonDown;
userControl.MouseLeftButtonUp += UserControl_MouseLeftButtonUp;
userControl.MouseMove += UserControl_MouseMove;
}
private void UserControl_MouseEnter(object sender, MouseEventArgs e)
{
if (sender is UserControl userControl && userControl is IDataContainer dataContainer)
{
dataContainer.Highlight(true);
if (_mainWindow.DataContext is MainViewModel viewModel)
{
var selectedObject = viewModel.SelectedItemOsList;
if (selectedObject?.VisualRepresentation != userControl)
{
AddHighlightRectangles(userControl);
}
}
}
}
private void UserControl_MouseLeave(object sender, MouseEventArgs e)
{
if (sender is UserControl userControl && userControl is IDataContainer dataContainer)
dataContainer.Highlight(false);
}
public void AddResizeRectangles(IEnumerable<osBase> selectedObjects)
{
double rectHighlightSize = 1;
RemoveResizeRectangles();
// Calcular el bounding box que contenga todos los objetos seleccionados
Rect boundingBox = CalculateTotalBoundingBox(selectedObjects);
FuncionesBase.MutableRect rectBox = new FuncionesBase.MutableRect(boundingBox);
rectBox.Left -= (float)rectHighlightSize;
rectBox.Right += (float)rectHighlightSize;
rectBox.Top -= (float)rectHighlightSize;
rectBox.Bottom += (float)rectHighlightSize;
_transformedBoundingBoxCenter = new Point(
boundingBox.Left + boundingBox.Width / 2,
boundingBox.Top + boundingBox.Height / 2
);
// Selection rectangle
Rectangle selectionRect = CreateSelectionRectangle(rectBox, rectHighlightSize);
_resizeRectangles.Add(selectionRect);
_canvas.Children.Add(selectionRect);
// Load rotation cursors
Cursor rotationCursorRx = new Cursor(Application.GetResourceStream(
new Uri("pack://application:,,,/CtrEditor;component/Icons/rotationRx.cur")).Stream);
Cursor rotationCursorSx = new Cursor(Application.GetResourceStream(
new Uri("pack://application:,,,/CtrEditor;component/Icons/rotationSx.cur")).Stream);
// Add resize/rotation handles
AddResizeHandles(rectBox, 10, rotationCursorRx, rotationCursorSx);
}
private Rect CalculateTotalBoundingBox(IEnumerable<osBase> selectedObjects)
{
double left = double.MaxValue;
double top = double.MaxValue;
double right = double.MinValue;
double bottom = double.MinValue;
foreach (var obj in selectedObjects)
{
if (obj.VisualRepresentation != null)
{
// Obtener el bounding box del objeto actual
Rect objectBounds = VisualTreeHelper.GetDescendantBounds(obj.VisualRepresentation);
GeneralTransform transform = obj.VisualRepresentation.TransformToAncestor(_canvas);
Rect transformedBounds = transform.TransformBounds(objectBounds);
// Actualizar los límites del bounding box total
left = Math.Min(left, transformedBounds.Left);
top = Math.Min(top, transformedBounds.Top);
right = Math.Max(right, transformedBounds.Right);
bottom = Math.Max(bottom, transformedBounds.Bottom);
}
}
return new Rect(
new Point(left, top),
new Point(right, bottom)
);
}
private Rectangle CreateSelectionRectangle(FuncionesBase.MutableRect rectBox, double rectHighlightSize)
{
if (double.IsInfinity(rectBox.Width) || double.IsInfinity(rectBox.Height) ||
double.IsNaN(rectBox.Width) || double.IsNaN(rectBox.Height))
{
throw new ArgumentException("Invalid dimensions for selection rectangle.");
}
var rect = new Rectangle
{
Width = rectBox.Width + (rectHighlightSize * 2),
Height = rectBox.Height + (rectHighlightSize * 2),
Fill = Brushes.Transparent,
Stroke = new SolidColorBrush(Color.FromArgb(180, 0, 120, 215)),
StrokeThickness = 1.5,
Tag = "Selection",
IsHitTestVisible = false,
StrokeDashArray = new DoubleCollection(new double[] { 3, 3 })
};
Canvas.SetLeft(rect, rectBox.Left - rectHighlightSize);
Canvas.SetTop(rect, rectBox.Top - rectHighlightSize);
Canvas.SetZIndex(rect, ((int)ZIndexEnum.RectangulosPropiead - 1));
return rect;
}
private void AddResizeHandles(FuncionesBase.MutableRect rectBox, double defaultRectSize,
Cursor rotationCursorRx, Cursor rotationCursorSx)
{
// Calcular el tamaño apropiado para los manejadores basado en el tamaño del objeto
double minObjectDimension = Math.Min(rectBox.Width, rectBox.Height);
double rectSize = Math.Min(defaultRectSize, minObjectDimension / 3);
// Asegurar un tamaño mínimo para los manejadores
rectSize = Math.Max(rectSize, 6);
// Calcular el offset para posicionar los manejadores fuera del rectángulo
double offset = rectSize / 2;
var positions = new List<(Point Position, string Tag, Cursor Cursor, Brush Stroke)>
{
// Esquinas - mover hacia afuera del rectángulo
(new Point(rectBox.Left - offset, rectBox.Top - offset), "TopLeft", Cursors.Arrow, Brushes.Gray),
(new Point(rectBox.Right + offset, rectBox.Top - offset), "TopRight", rotationCursorRx, Brushes.Red),
(new Point(rectBox.Left - offset, rectBox.Bottom + offset), "BottomLeft", rotationCursorSx, Brushes.DarkRed),
(new Point(rectBox.Right + offset, rectBox.Bottom + offset), "BottomRight", Cursors.SizeNWSE, Brushes.Blue),
// Centros de los bordes - mover hacia afuera del rectángulo
(new Point(rectBox.Left + rectBox.Width / 2, rectBox.Top - offset), "TopCenter", Cursors.Arrow, Brushes.Gray),
(new Point(rectBox.Left + rectBox.Width / 2, rectBox.Bottom + offset), "BottomCenter", Cursors.SizeNS, Brushes.Blue),
(new Point(rectBox.Left - offset, rectBox.Top + rectBox.Height / 2), "CenterLeft", rotationCursorRx, Brushes.Red),
(new Point(rectBox.Right + offset, rectBox.Top + rectBox.Height / 2), "CenterRight", Cursors.SizeWE, Brushes.Blue)
};
foreach (var (Position, Tag, Cursor, Stroke) in positions)
{
var handle = CreateResizeHandle(rectSize, Tag, Cursor, Stroke);
SetHandlePosition(handle, Position, rectSize);
_resizeRectangles.Add(handle);
_canvas.Children.Add(handle);
}
}
private Rectangle CreateResizeHandle(double rectSize, string tag, Cursor cursor, Brush stroke)
{
var handle = new Rectangle
{
Width = rectSize,
Height = rectSize,
Fill = Brushes.Transparent,
Stroke = stroke,
StrokeThickness = 1,
Tag = tag,
Cursor = cursor
};
handle.MouseLeftButtonDown += UserControl_MouseLeftButtonDown;
handle.MouseMove += UserControl_MouseMove;
handle.MouseLeftButtonUp += UserControl_MouseLeftButtonUp;
handle.MouseLeave += ResizeRectangle_MouseLeave;
Canvas.SetZIndex(handle, (int)ZIndexEnum.RectangulosPropiead);
return handle;
}
private void SetHandlePosition(Rectangle handle, Point position, double rectSize)
{
if (!double.IsInfinity(position.X) && !double.IsNaN(position.X))
Canvas.SetLeft(handle, position.X - rectSize / 2);
if (!double.IsInfinity(position.Y) && !double.IsNaN(position.Y))
Canvas.SetTop(handle, position.Y - rectSize / 2);
}
public void RemoveResizeRectangles()
{
if (_resizeRectangles == null || _resizeRectangles.Count == 0)
return;
foreach (var rect in _resizeRectangles)
{
_canvas.Children.Remove(rect);
}
_resizeRectangles.Clear();
}
public void MakeResizeRectanglesTransparent()
{
if (_resizeRectangles == null || _resizeRectangles.Count == 0)
return;
foreach (var rect in _resizeRectangles)
{
rect.Opacity = 0;
}
}
public void MakeResizeRectanglesNormal()
{
if (_resizeRectangles == null || _resizeRectangles.Count == 0)
return;
foreach (var rect in _resizeRectangles)
{
rect.Opacity = 1;
}
}
private void HandleObjectSelection(UserControl userControl, osBase datos)
{
PurgeDeletedObjects();
var viewModel = _mainWindow.DataContext as MainViewModel;
if (viewModel == null) return;
bool isControlPressed = Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl);
if (viewModel.IsMultiSelectionActive)
{
if (isControlPressed)
{
// Deseleccionar objeto si está presionada la tecla Ctrl
if (_selectedObjects.Contains(datos))
{
DeselectObject(datos);
}
}
else if (!_selectedObjects.Contains(datos))
{
// Seleccionar objeto si no está ya seleccionado
SelectObject(datos);
}
}
else
{
// Modo selección única
ClearSelection();
SelectObject(datos);
}
viewModel.SelectedItemOsList = datos;
UpdateSelectionVisuals();
}
public void SelectObject(osBase obj)
{
if (_mainWindow.DataContext is MainViewModel vm)
{
// Add new object if not already selected
if (!_selectedObjects.Contains(obj))
{
// If not in multi-selection mode, clear existing selection first
if (!vm.IsMultiSelectionActive)
ClearSelection();
_selectedObjects.Add(obj);
obj.IsSelected = true;
// Add highlight visual only if in multi-selection mode
if (vm.IsMultiSelectionActive)
{
AddSelectionHighlight(obj.VisualRepresentation);
}
UpdateSelectionVisuals();
}
}
}
public void DeselectObject(osBase obj)
{
if (_selectedObjects.Contains(obj))
{
_selectedObjects.Remove(obj);
obj.IsSelected = false;
RemoveSelectionHighlight(obj.VisualRepresentation);
}
}
public void ClearSelection()
{
foreach (var obj in _selectedObjects.ToList())
{
DeselectObject(obj);
}
RemoveAllSelectionHighlights();
RemoveResizeRectangles();
if (_mainWindow.DataContext is MainViewModel viewModel)
{
viewModel.SelectedItemOsList = null;
}
}
public void OnMultiSelectionModeChanged(bool isActive)
{
if (!isActive)
{
// Mantener solo el último objeto seleccionado
if (_selectedObjects.Count > 1)
{
var lastSelected = _selectedObjects.Last();
ClearSelection();
SelectObject(lastSelected);
}
RemoveAllSelectionHighlights();
}
else
{
UpdateAllSelectionHighlights();
}
}
private void AddSelectionHighlight(UserControl userControl, bool isReference = false)
{
if (userControl == null) return;
RemoveSelectionHighlight(userControl);
Rect boundingBox = VisualTreeHelper.GetDescendantBounds(userControl);
GeneralTransform transform = userControl.TransformToAncestor(_canvas);
Rect transformedBoundingBox = transform.TransformBounds(boundingBox);
Rectangle highlightRect = new Rectangle
{
Width = transformedBoundingBox.Width,
Height = transformedBoundingBox.Height,
Fill = Brushes.Transparent,
Stroke = new SolidColorBrush(isReference ?
Color.FromArgb(180, 128, 0, 128) : // Purple for reference
Color.FromArgb(180, 255, 0, 0)), // Red for others
StrokeThickness = isReference ? 3 : 2, // Más grueso para el de referencia
Tag = "SelectionHighlight",
IsHitTestVisible = false
};
Canvas.SetLeft(highlightRect, transformedBoundingBox.Left);
Canvas.SetTop(highlightRect, transformedBoundingBox.Top);
Canvas.SetZIndex(highlightRect, (int)ZIndexEnum.RectangulosPropiead - 1);
_selectionHighlightPairs.Add((userControl, highlightRect));
_canvas.Children.Add(highlightRect);
}
private void RemoveSelectionHighlight(UserControl userControl)
{
var pairsToRemove = _selectionHighlightPairs.Where(p => p.Control == userControl).ToList();
foreach (var pair in pairsToRemove)
{
_canvas.Children.Remove(pair.Highlight);
_selectionHighlightPairs.Remove(pair);
}
}
public void RemoveAllSelectionHighlights()
{
foreach (var pair in _selectionHighlightPairs)
{
_canvas.Children.Remove(pair.Highlight);
}
_selectionHighlightPairs.Clear();
}
private void UpdateAllSelectionHighlights()
{
if (_mainWindow.DataContext is MainViewModel viewModel && viewModel.IsMultiSelectionActive)
{
RemoveAllSelectionHighlights();
bool isFirst = true;
foreach (var selectedObject in _selectedObjects)
{
if (selectedObject.VisualRepresentation != null)
{
AddSelectionHighlight(selectedObject.VisualRepresentation, isFirst);
isFirst = false;
}
}
}
}
private void UpdateSelectionHighlights()
{
UpdateAllSelectionHighlights();
}
private void UserControl_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (!_isDrawingCanvas)
{
if (_resizeRectangles != null && _resizeRectangles.Contains(sender))
{
_currentDraggingRectangle = sender as Rectangle;
_lastMousePosition = e.GetPosition(_canvas);
_currentDraggingRectangle.CaptureMouse();
_isMovingUserControl = true;
_startPointUserControl = e.GetPosition(_canvas);
e.Handled = true;
return;
}
var userControl = sender as UserControl;
if (userControl != null)
{
if (userControl.DataContext is osBase datos)
{
HandleObjectSelection(userControl, datos);
}
// Solo iniciar el arrastre si no se presionó Ctrl
if (!(Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)))
{
userControl.CaptureMouse();
_currentDraggingControl = userControl;
_isMovingUserControl = true;
InitializeDrag(e);
}
}
}
}
private void InitializeDrag(MouseButtonEventArgs e)
{
_isDraggingUserControl = true;
_startPointUserControl = e.GetPosition(_canvas);
}
private void UserControl_MouseMove(object sender, MouseEventArgs e)
{
if (_isMovingUserControl)
{
var currentPosition = e.GetPosition(_canvas);
MakeResizeRectanglesTransparent();
if (_isDraggingUserControl)
{
HandleDrag(currentPosition);
}
else if (_currentDraggingRectangle != null)
{
string resizeDirection = _currentDraggingRectangle.Tag as string;
if (resizeDirection != null)
{
var mode = DetermineHandleMode(resizeDirection);
switch (mode)
{
case HandleMode.Rotate:
HandleRotation(currentPosition);
break;
case HandleMode.ResizeWidth:
case HandleMode.ResizeHeight:
case HandleMode.ResizeBoth:
HandleResize(currentPosition, mode);
break;
}
}
}
}
}
private void HandleDrag(Point currentPosition)
{
PurgeDeletedObjects();
var dx = currentPosition.X - _startPointUserControl.X;
var dy = currentPosition.Y - _startPointUserControl.Y;
RemoveAllSelectionHighlights(); // Remover antes de mover
foreach (var selectedObject in _selectedObjects)
{
var newX = Canvas.GetLeft(selectedObject.VisualRepresentation) + dx;
var newY = Canvas.GetTop(selectedObject.VisualRepresentation) + dy;
selectedObject.Move((float)newX, (float)newY);
}
_startPointUserControl = currentPosition;
UpdateAllSelectionHighlights();
}
private void UserControl_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (_isMovingUserControl)
{
if (sender is UserControl userControl)
{
userControl.ReleaseMouseCapture();
}
else if (sender is Rectangle rectangle)
{
rectangle.ReleaseMouseCapture();
}
RemoveAllSelectionHighlights(); // Remover antes de actualizar
UpdateSelectionVisuals();
UpdateAllSelectionHighlights();
// Restaurar los rectángulos de selección si es necesario
if (_mainWindow.DataContext is MainViewModel viewModel && viewModel.IsMultiSelectionActive)
{
foreach (var selectedObject in _selectedObjects)
{
AddSelectionHighlight(selectedObject.VisualRepresentation);
}
}
_isDraggingUserControl = false;
_isMovingUserControl = false;
_currentDraggingRectangle = null;
_currentResizeMode = ResizeMode.None;
_lastAngle = 0;
}
MarkUnsavedChanges();
}
private void MarkUnsavedChanges()
{
if (_mainWindow.DataContext is MainViewModel viewModel)
{
if (_isMovingUserControl || _isDraggingUserControl)
{
viewModel.HasUnsavedChanges = true;
}
}
}
private void ResizeRectangle_MouseLeave(object sender, MouseEventArgs e)
{
var rect = sender as Rectangle;
if (rect != null)
{
rect.Fill = Brushes.Transparent;
}
}
public void AddHighlightRectangles(UserControl userControl)
{
double rectSize = 1;
RemoveHighlightRectangles();
if (userControl is IDataContainer dataContainer && dataContainer.Datos is osBase mvBase && mvBase.Show_On_This_Page)
{
Rect boundingBox = VisualTreeHelper.GetDescendantBounds(userControl);
GeneralTransform transform = userControl.TransformToAncestor(_canvas);
Rect transformedBoundingBox = transform.TransformBounds(boundingBox);
FuncionesBase.MutableRect rectBox = new FuncionesBase.MutableRect(transformedBoundingBox);
rectBox.Left -= (float)rectSize;
rectBox.Right += (float)rectSize;
rectBox.Top -= (float)rectSize;
rectBox.Bottom += (float)rectSize;
Rectangle highlightRect = new Rectangle
{
Width = rectBox.Width,
Height = rectBox.Height,
Fill = Brushes.Transparent,
Stroke = new SolidColorBrush(Color.FromArgb(128, 0, 0, 0)),
StrokeThickness = 1,
Tag = "Highlight",
IsHitTestVisible = false,
StrokeDashArray = new DoubleCollection(new double[] { 2, 2 })
};
Canvas.SetLeft(highlightRect, rectBox.Left);
Canvas.SetTop(highlightRect, rectBox.Top); // Corregido: usando rectBox.Top en lugar de highlightRect.Top
_highlightRectangles.Add(highlightRect);
_canvas.Children.Add(highlightRect);
Canvas.SetZIndex(highlightRect, ((int)ZIndexEnum.RectangulosPropiead));
if (_timerRemoveHighlight == null)
_timerRemoveHighlight = new System.Threading.Timer(TimerCallbackRemoveHighlight, null, Timeout.Infinite, Timeout.Infinite);
_timerRemoveHighlight.Change(2000, Timeout.Infinite);
}
}
private void TimerCallbackRemoveHighlight(object state)
{
if (Application.Current != null)
{
Application.Current.Dispatcher.Invoke(RemoveHighlightRectangles);
}
}
public void RemoveHighlightRectangles()
{
if (_highlightRectangles == null || _highlightRectangles.Count == 0)
return;
foreach (var rect in _highlightRectangles)
{
_canvas.Children.Remove(rect);
}
_highlightRectangles.Clear();
}
private HandleMode DetermineHandleMode(string resizeDirection)
{
return resizeDirection switch
{
"TopLeft" => HandleMode.None,
"TopRight" or "BottomLeft" or "CenterLeft" => HandleMode.Rotate,
"BottomRight" => HandleMode.ResizeBoth,
"TopCenter" => HandleMode.None,
"BottomCenter" => HandleMode.ResizeHeight,
"CenterRight" => HandleMode.ResizeWidth,
_ => HandleMode.None
};
}
private void HandleResize(Point currentPosition, HandleMode mode)
{
PurgeDeletedObjects();
RemoveAllSelectionHighlights(); // Remover antes de redimensionar
foreach (var selectedObject in _selectedObjects)
{
bool resizeWidth = mode == HandleMode.ResizeWidth || mode == HandleMode.ResizeBoth;
bool resizeHeight = mode == HandleMode.ResizeHeight || mode == HandleMode.ResizeBoth;
HandleResizeForObject(selectedObject, currentPosition, resizeWidth, resizeHeight);
}
_startPointUserControl = currentPosition;
UpdateAllSelectionHighlights();
}
private void HandleResizeForObject(osBase obj, Point currentPosition, bool activateSizeWidth, bool activateSizeHeight)
{
double widthChange = activateSizeWidth ? currentPosition.X - _startPointUserControl.X : 0;
double heightChange = activateSizeHeight ? currentPosition.Y - _startPointUserControl.Y : 0;
obj.Resize(widthChange, heightChange);
}
private void HandleRotation(Point currentPosition)
{
PurgeDeletedObjects();
RemoveAllSelectionHighlights(); // Remover antes de rotar
// Calcular el ángulo respecto al centro del bounding box que contiene todos los objetos seleccionados
double deltaX = currentPosition.X - _transformedBoundingBoxCenter.X;
double deltaY = currentPosition.Y - _transformedBoundingBoxCenter.Y;
double angle = Math.Atan2(deltaY, deltaX) * (180 / Math.PI);
if ((_lastAngle == 0 && angle != 0) || Math.Abs(angle - _lastAngle) > 45)
{
_lastAngle = (float)angle;
}
else
{
double deltaAngle = angle - _lastAngle;
foreach (var selectedObject in _selectedObjects)
{
selectedObject.Rotate(deltaAngle);
}
_lastAngle = (float)angle;
}
UpdateAllSelectionHighlights();
}
public void AlignObjects(AlignmentType alignmentType)
{
PurgeDeletedObjects();
if (_selectedObjects.Count <= 1) return;
var alignment = new ObjectAlignment(_selectedObjects, _selectedObjects.FirstOrDefault());
switch (alignmentType)
{
case AlignmentType.Left:
alignment.AlignLeft();
break;
case AlignmentType.Right:
alignment.AlignRight();
break;
case AlignmentType.Top:
alignment.AlignTop();
break;
case AlignmentType.Bottom:
alignment.AlignBottom();
break;
case AlignmentType.CenterHorizontally:
alignment.AlignCenterHorizontally();
break;
case AlignmentType.CenterVertically:
alignment.AlignCenterVertically();
break;
case AlignmentType.DistributeHorizontally:
alignment.DistributeHorizontally();
break;
case AlignmentType.DistributeVertically:
alignment.DistributeVertically();
break;
case AlignmentType.EqualWidth:
alignment.EqualWidth();
break;
case AlignmentType.EqualHeight:
alignment.EqualHeight();
break;
case AlignmentType.EqualAngle:
alignment.EqualAngle();
break;
case AlignmentType.JoinHorizontally:
alignment.JoinHorizontally();
break;
case AlignmentType.JoinVertically:
alignment.JoinVertically();
break;
}
// Update the selection visuals after alignment
UpdateSelectionVisuals();
}
public void EliminarObjetosSeleccionados()
{
if (_selectedObjects.Count == 0) return;
var viewModel = _mainWindow.DataContext as MainViewModel;
if (viewModel == null) return;
// Crear una copia de la lista para evitar modificaciones durante la iteración
var objectsToRemove = _selectedObjects.ToList();
foreach (var obj in objectsToRemove)
{
viewModel.RemoverObjetoSimulable(obj);
}
// Limpiar la selección y actualizar la interfaz
ClearSelection();
RemoveResizeRectangles();
RemoveAllSelectionHighlights();
// Actualizar el estado de cambios sin guardar
if (viewModel != null)
{
viewModel.HasUnsavedChanges = true;
}
}
public void StartRectangleSelection(Point startPoint)
{
_rectangleStart = startPoint;
_selectionRectangle = new Rectangle
{
Stroke = Brushes.Blue,
StrokeThickness = 1,
Fill = new SolidColorBrush(Color.FromArgb(50, 0, 0, 255))
};
Canvas.SetLeft(_selectionRectangle, startPoint.X);
Canvas.SetTop(_selectionRectangle, startPoint.Y);
Canvas.SetZIndex(_selectionRectangle, (int)ZIndexEnum.RectangulosPropiead);
_canvas.Children.Add(_selectionRectangle);
}
private void UpdateSelectionRectangle(Point currentPoint)
{
double left = Math.Min(_rectangleStart.X, currentPoint.X);
double top = Math.Min(_rectangleStart.Y, currentPoint.Y);
double width = Math.Abs(currentPoint.X - _rectangleStart.X);
double height = Math.Abs(currentPoint.Y - _rectangleStart.Y);
_selectionRectangle.Width = width;
_selectionRectangle.Height = height;
Canvas.SetLeft(_selectionRectangle, left);
Canvas.SetTop(_selectionRectangle, top);
}
public void FinishRectangleSelection(Point currentPoint)
{
if (_selectionRectangle != null)
{
var left = Canvas.GetLeft(_selectionRectangle);
var top = Canvas.GetTop(_selectionRectangle);
var right = left + _selectionRectangle.Width;
var bottom = top + _selectionRectangle.Height;
var selectionBounds = new Rect(new Point(left, top), new Point(right, bottom));
var itemsToProcess = _canvas.Children.OfType<UserControl>().Where(child => child is IDataContainer).ToList();
foreach (var child in itemsToProcess)
{
var childBounds = GetElementBounds(child);
if (selectionBounds.Contains(childBounds) || selectionBounds.IntersectsWith(childBounds))
{
if (child.DataContext is osBase osObject)
{
SelectObject(osObject);
}
}
}
_canvas.Children.Remove(_selectionRectangle);
_selectionRectangle = null;
UpdateSelectionVisuals();
}
}
private Rect GetElementBounds(FrameworkElement element)
{
var bounds = VisualTreeHelper.GetDescendantBounds(element);
var transform = element.TransformToAncestor(_canvas);
return transform.TransformBounds(bounds);
}
public void UpdateRectangleSelection(Point currentPoint)
{
if (_selectionRectangle != null)
{
UpdateSelectionRectangle(currentPoint);
}
}
}
}

View File

@ -0,0 +1,28 @@
<UserControl x:Class="CtrEditor.ObjetosSim.ucCustomImage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:vm="clr-namespace:CtrEditor.ObjetosSim">
<UserControl.DataContext>
<vm:osCustomImage />
</UserControl.DataContext>
<Grid RenderTransformOrigin="0.5,0.5">
<Grid.RenderTransform>
<TransformGroup>
<RotateTransform Angle="{Binding Angulo}"/>
</TransformGroup>
</Grid.RenderTransform>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Image Source="{Binding ImageSource_oculta}"
Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}"
Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}}"
Stretch="Fill"
RenderTransformOrigin="0.5,0.5" />
</Grid>
</UserControl>

View File

@ -0,0 +1,106 @@
using CommunityToolkit.Mvvm.ComponentModel;
using LibS7Adv;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using CtrEditor.FuncionesBase;
using System.ComponentModel;
using Newtonsoft.Json;
namespace CtrEditor.ObjetosSim
{
/// <summary>
/// A custom image control that can display an image from a file path
/// </summary>
public partial class osCustomImage : osBase, IosBase
{
public static string NombreClase()
{
return "Custom Image";
}
private string nombre = NombreClase();
public override string Nombre
{
get => nombre;
set => SetProperty(ref nombre, value);
}
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(ImageSource_oculta))]
[property: Description("Path to the image file")]
[property: Category("Image:")]
private string imagePath;
[JsonIgnore]
[ObservableProperty]
public ImageSource imageSource_oculta;
partial void OnImagePathChanged(string value)
{
if (!string.IsNullOrEmpty(value))
{
ImageSource_oculta = ImageFromPath(value);
}
else
{
// Si no hay path, usar la imagen por defecto
ImageSource_oculta = ImageFromPath("/Icons/unselect.png");
}
}
public osCustomImage()
{
Ancho = 0.30f;
Alto = 0.30f;
// Establecer la imagen por defecto al crear el objeto
ImageSource_oculta = ImageFromPath("/Icons/unselect.png");
}
public override void ucLoaded()
{
base.ucLoaded();
if (!string.IsNullOrEmpty(ImagePath))
{
ImageSource_oculta = ImageFromPath(ImagePath);
}
else
{
// Si no hay path al cargar, usar la imagen por defecto
ImageSource_oculta = ImageFromPath("/Icons/unselect.png");
}
}
}
public partial class ucCustomImage : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public int zIndex_fromFrames { get; set; }
public ucCustomImage()
{
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_Base()
{
return ZIndexEnum.Estaticos;
}
}
}

View File

@ -0,0 +1,29 @@
<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"
Visibility="{Binding ShowPlate, Converter={StaticResource BooleanToVisibilityConverter}}" />
</Grid>
</UserControl>

View File

@ -0,0 +1,268 @@
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]
[property: Description("Layer index to add to Objects on the Frame")]
[property: Category("Layer:")]
int zindex_FramePlate;
partial void OnZindex_FramePlateChanged(int value)
{
UpdateZIndex(value);
}
[ObservableProperty]
Color color;
[ObservableProperty]
Color color_Titulo;
[ObservableProperty]
string titulo;
[ObservableProperty]
public float alto_Titulo;
// Encoder X
[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;
// Encoder Y
[ObservableProperty]
[property: Description("This is a link to a Encoder for Y.")]
[property: Category("Encoders:")]
[property: ItemsSource(typeof(osBaseItemsSource<osEncoderMotorLineal>))]
private string encoder_Y;
[ObservableProperty]
[property: Description("K Pulses per meter for Moving")]
[property: Category("Encoders:")]
public float k_encoder_Y;
[ObservableProperty]
[property: Description("Y in meter offset Top. Position when the encoder is 0")]
[property: Category("Encoders:")]
public float offset_encoder_Y;
partial void OnK_encoder_YChanged(float value)
{
UpdatePosition();
}
partial void OnOffset_encoder_YChanged(float value)
{
UpdatePosition();
}
partial void OnK_encoder_XChanged(float value)
{
UpdatePosition();
}
partial void OnOffset_encoder_XChanged(float value)
{
UpdatePosition();
}
[JsonIgnore]
private osEncoderMotorLineal EncoderX;
[JsonIgnore]
private osEncoderMotorLineal EncoderY;
[JsonIgnore]
private bool isUpdatingFromEncoderX = false;
[JsonIgnore]
private bool isUpdatingFromEncoderY = false;
partial void OnEncoder_XChanged(string value)
{
if (EncoderX != null)
EncoderX.PropertyChanged -= OnEncoderXPropertyChanged;
if (_mainViewModel != null && value != null && value.Length > 0)
{
EncoderX = (osEncoderMotorLineal)_mainViewModel.ObjetosSimulables.FirstOrDefault(s => (s is osEncoderMotorLineal && s.Nombre == value), null);
if (EncoderX != null)
EncoderX.PropertyChanged += OnEncoderXPropertyChanged;
}
}
partial void OnEncoder_YChanged(string value)
{
if (EncoderY != null)
EncoderY.PropertyChanged -= OnEncoderYPropertyChanged;
if (_mainViewModel != null && value != null && value.Length > 0)
{
EncoderY = (osEncoderMotorLineal)_mainViewModel.ObjetosSimulables.FirstOrDefault(s => (s is osEncoderMotorLineal && s.Nombre == value), null);
if (EncoderY != null)
EncoderY.PropertyChanged += OnEncoderYPropertyChanged;
}
}
private void OnEncoderXPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (!isUpdatingFromEncoderX)
{
isUpdatingFromEncoderX = true;
// Actualizamos el nombre si este fue modificado
if (e.PropertyName == nameof(osEncoderMotorLineal.Nombre))
Encoder_X = ((osEncoderMotorLineal)sender).Nombre;
if (e.PropertyName == nameof(osEncoderMotorLineal.Valor_Actual))
{
UpdatePosition();
}
isUpdatingFromEncoderX = false;
}
}
private void OnEncoderYPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (!isUpdatingFromEncoderY)
{
isUpdatingFromEncoderY = true;
// Actualizamos el nombre si este fue modificado
if (e.PropertyName == nameof(osEncoderMotorLineal.Nombre))
Encoder_Y = ((osEncoderMotorLineal)sender).Nombre;
if (e.PropertyName == nameof(osEncoderMotorLineal.Valor_Actual))
{
UpdatePosition();
}
isUpdatingFromEncoderY = false;
}
}
public void UpdatePosition()
{
// Update X position if encoder is available
if (EncoderX != null && K_encoder_X != 0)
Left = (EncoderX.Valor_Actual / k_encoder_X) + offset_encoder_X;
// Update Y position if encoder is available
if (EncoderY != null && K_encoder_Y != 0)
Top = (EncoderY.Valor_Actual / k_encoder_Y) + offset_encoder_Y;
}
public override void TopChanging(float oldValue, float newValue)
{
offsetY = newValue - oldValue;
}
public override void LeftChanging(float oldValue, float newValue)
{
offsetX = newValue - oldValue;
}
[ObservableProperty]
[property: Description("Show/Hide the plate background")]
[property: Category("Appearance:")]
private bool showPlate;
public osFramePlate()
{
Ancho = 0.5f;
Alto = 0.5f;
Alto_Titulo = 0.2f;
Color = Colors.WhiteSmoke;
Titulo = "Frame";
Zindex_FramePlate = 0;
ShowPlate = true; // Default value
}
public override void ucLoaded()
{
base.ucLoaded();
// El UserControl se ha cargado
OnEncoder_XChanged(Encoder_X);
OnEncoder_YChanged(Encoder_Y);
UpdateZIndex(Zindex_FramePlate);
}
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 int zIndex_fromFrames { 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_Base()
{
return ZIndexEnum.Decorativos;
}
}
}

View File

@ -0,0 +1,29 @@
<UserControl x:Class="CtrEditor.ObjetosSim.ucTextPlate"
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:osTextPlate Color_Titulo="Black" Alto_Titulo="0.1"/>
</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}}" />
</Grid>
</UserControl>

View File

@ -0,0 +1,99 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using CommunityToolkit.Mvvm.ComponentModel;
using CtrEditor.FuncionesBase;
using Newtonsoft.Json;
namespace CtrEditor.ObjetosSim
{
/// <summary>
/// Interaction logic for ucTextPlate.xaml
/// </summary>
///
public partial class osTextPlate : osBase, IosBase
{
[JsonIgnore]
public float offsetY;
[JsonIgnore]
public float offsetX;
public static string NombreClase()
{
return "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;
public override void TopChanging(float oldValue, float newValue) {
offsetY = newValue - oldValue;
}
public override void LeftChanging(float oldValue, float newValue)
{
offsetX = newValue - oldValue;
}
public osTextPlate()
{
Ancho = 0.5f;
Alto = 0.5f;
Alto_Titulo = 0.2f;
Color = Colors.Gray;
Titulo = "Titulo";
}
public override void ucUnLoaded()
{
// El UserControl se esta eliminando
// eliminar el objeto de simulacion
}
}
public partial class ucTextPlate : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public int zIndex_fromFrames { get; set; }
public ucTextPlate()
{
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_Base()
{
return ZIndexEnum.Decorativos;
}
}
}

View File

@ -1,12 +1,15 @@
<UserControl x:Class="CtrEditor.ObjetosSim.ucBotella"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:convert="clr-namespace:CtrEditor.Convertidores">
<UserControl.Resources>
<convert:MeterToPixelConverter x:Key="MeterToPixelConverter"/>
</UserControl.Resources>
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim"
>
<UserControl.DataContext>
<vm:osBotella/>
</UserControl.DataContext>
<Ellipse Height="{Binding Diametro, Converter={StaticResource MeterToPixelConverter}}"
Stroke="red" Fill="Gray"
Width="{Binding Diametro, Converter={StaticResource MeterToPixelConverter}}"/>
Stroke="{Binding ColorButton_oculto}" Fill="Gray"
Width="{Binding Diametro, Converter={StaticResource MeterToPixelConverter}}" StrokeThickness="0.5"/>
</UserControl>

View File

@ -1,11 +1,13 @@
using System.Windows;
using System.Windows.Controls;
//using using Microsoft.Xna.Framework;
using CtrEditor.Convertidores;
using CtrEditor.Siemens;
using LibS7Adv;
using CtrEditor.Simulacion;
using CommunityToolkit.Mvvm.ComponentModel;
using nkast.Aether.Physics2D.Common;
using System.Windows.Media;
using CtrEditor.FuncionesBase;
namespace CtrEditor.ObjetosSim
{
@ -31,6 +33,19 @@ namespace CtrEditor.ObjetosSim
set => SetProperty(ref nombre, value);
}
public override void OnMove(float LeftPixels, float TopPixels)
{
UpdateAfterMove();
}
public override void OnResize(float Delta_Width, float Delta_Height)
{
Diametro += Delta_Width;
}
[ObservableProperty]
private Brush colorButton_oculto;
[ObservableProperty]
private float diametro;
@ -39,6 +54,17 @@ namespace CtrEditor.ObjetosSim
SimGeometria?.SetDiameter(Diametro);
}
[ObservableProperty]
private string velocidad_desde_simulacion;
[ObservableProperty]
private float inercia_desde_simulacion;
[ObservableProperty]
private bool preserve_Outside_Transport;
[ObservableProperty]
private float porcentaje_Traccion;
[ObservableProperty]
private float mass;
partial void OnMassChanged(float value)
@ -62,9 +88,8 @@ namespace CtrEditor.ObjetosSim
private void ActualizarGeometrias()
{
if (SimGeometria != null)
{
SimGeometria.SetDiameter(Diametro);
if (SimGeometria != null && !RemoverDesdeSimulacion)
{
SimGeometria.SetPosition(GetCentro());
}
}
@ -73,39 +98,61 @@ namespace CtrEditor.ObjetosSim
{
Diametro = 0.10f;
Mass = 1;
ColorButton_oculto = Brushes.Gray;
Preserve_Outside_Transport = true;
}
public void UpdateAfterMove()
{
if (!RemoverDesdeSimulacion)
{
ActualizarGeometrias();
SimGeometria?.SetDiameter(Diametro);
}
}
public override void UpdateGeometryStart()
{
// Se llama antes de la simulacion
ActualizarGeometrias();
SimGeometria?.SetDiameter(Diametro);
}
public override void SimulationStop()
{
// Se llama al detener la simulacion. Util para detener Storyboards
}
public override void UpdateGeometryStep()
{
// Se llama antes de la simulacion
ActualizarGeometrias();
}
public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds) { }
public override void UpdateControl(int elapsedMilliseconds)
{
SetCentro(SimGeometria.Center);
if (SimGeometria.isRestricted)
ColorButton_oculto = Brushes.Yellow;
else
{
if (SimGeometria.IsOnAnyTransport())
ColorButton_oculto = Brushes.Red;
else
ColorButton_oculto = Brushes.Gray;
}
if (SimGeometria.Descartar) // Ha sido marcada para remover
// Ha sido marcada para remover
if (SimGeometria.Descartar)
RemoverDesdeSimulacion = true;
// Eliminar la botella si esta fuera de un transporte
if (!Preserve_Outside_Transport && !SimGeometria.IsOnAnyTransport())
RemoverDesdeSimulacion = true;
Velocidad_desde_simulacion = SimGeometria.Body.LinearVelocity.ToString();
Inercia_desde_simulacion = SimGeometria.Body.Inertia;
Porcentaje_Traccion = SimGeometria.OverlapPercentage;
}
public override void ucLoaded()
{
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
ActualizarLeftTop();
base.ucLoaded();
SimGeometria = simulationManager.AddCircle(Diametro, GetCentro(), Mass);
}
public override void ucUnLoaded()
@ -120,6 +167,7 @@ namespace CtrEditor.ObjetosSim
public partial class ucBotella : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public int zIndex_fromFrames { get; set; }
public ucBotella()
{
@ -136,20 +184,10 @@ namespace CtrEditor.ObjetosSim
Datos?.ucUnLoaded();
}
public void Resize(float width, float 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()
public ZIndexEnum ZIndex_Base()
{
return 10;
return ZIndexEnum.Dinamicos;
}
}
}

View File

@ -0,0 +1,14 @@
<UserControl x:Class="CtrEditor.ObjetosSim.ucBotellaCuello"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim">
<UserControl.DataContext>
<vm:osBotellaCuello/>
</UserControl.DataContext>
<Ellipse Height="{Binding Diametro, Converter={StaticResource MeterToPixelConverter}}"
Stroke="{Binding ColorButton_oculto}" Fill="Gray"
Width="{Binding Diametro, Converter={StaticResource MeterToPixelConverter}}" StrokeThickness="0.5"/>
</UserControl>

View File

@ -0,0 +1,180 @@
using System.Windows;
using System.Windows.Controls;
//using using Microsoft.Xna.Framework;
using LibS7Adv;
using CtrEditor.Simulacion;
using CommunityToolkit.Mvvm.ComponentModel;
using nkast.Aether.Physics2D.Common;
using System.Windows.Media;
using CtrEditor.FuncionesBase;
namespace CtrEditor.ObjetosSim
{
/// <summary>
/// Interaction logic for ucBotellaCuelloCuello.xaml
/// </summary>
public partial class osBotellaCuello : osBase, IosBase
{
private simBotella SimGeometria;
// Otros datos y métodos relevantes para la simulación
public static string NombreClase()
{
return "Botella con Cuello";
}
private string nombre = NombreClase();
public override string Nombre
{
get => nombre;
set => SetProperty(ref nombre, value);
}
public override void TopChanged(float value)
{
UpdateAfterMove();
}
public override void LeftChanged(float value)
{
UpdateAfterMove();
}
public override void OnResize(float Delta_Width, float Delta_Height)
{
Diametro += Delta_Width;
}
[ObservableProperty]
private Brush colorButton_oculto;
[ObservableProperty]
private float diametro;
partial void OnDiametroChanged(float value)
{
SimGeometria?.SetDiameter(Diametro);
}
[ObservableProperty]
private string velocidad_desde_simulacion;
[ObservableProperty]
private float inercia_desde_simulacion;
[ObservableProperty]
private float mass;
partial void OnMassChanged(float value)
{
SimGeometria?.SetMass(value);
}
public Vector2 GetCentro()
{
return new Vector2(Left + Diametro / 2, Top + Diametro / 2);
}
public void SetCentro(float x, float y)
{ Left = x; Top = y; }
public void SetCentro(Vector2 centro)
{
Left = centro.X - Diametro / 2;
Top = centro.Y - Diametro / 2;
}
private void ActualizarGeometrias()
{
if (SimGeometria != null)
{
SimGeometria.SetPosition(GetCentro());
}
}
public osBotellaCuello()
{
Diametro = 0.10f;
Mass = 1;
ColorButton_oculto = Brushes.Gray;
}
public void UpdateAfterMove()
{
ActualizarGeometrias();
SimGeometria?.SetDiameter(Diametro);
}
public override void UpdateGeometryStart()
{
// Se llama antes de la simulacion
ActualizarGeometrias();
SimGeometria?.SetDiameter(Diametro);
}
public override void UpdateGeometryStep()
{
// Se llama antes de la simulacion
ActualizarGeometrias();
}
public override void UpdateControl(int elapsedMilliseconds)
{
SetCentro(SimGeometria.Center);
if (SimGeometria.isRestricted)
ColorButton_oculto = Brushes.Yellow;
else
{
if (SimGeometria.isOnTransports > 0)
ColorButton_oculto = Brushes.Red;
else
ColorButton_oculto = Brushes.Gray;
}
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()
{
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
base.ucLoaded();
SimGeometria = simulationManager.AddCircle(Diametro, GetCentro(), Mass);
}
public override void ucUnLoaded()
{
// El UserControl se esta eliminando
// eliminar el objeto de simulacion
simulationManager.Remove(SimGeometria);
}
}
public partial class ucBotellaCuello : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public int zIndex_fromFrames { get; set; }
public ucBotellaCuello()
{
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_Base()
{
return ZIndexEnum.Dinamicos;
}
}
}

View File

@ -0,0 +1,24 @@
<UserControl x:Class="CtrEditor.ObjetosSim.ucBottGenerator"
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"
mc:Ignorable="d"
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim">
<UserControl.DataContext>
<vm:osFiller/>
</UserControl.DataContext>
<Grid>
<Image Source="/imagenes/gear.png"
Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}"
Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}}"
Stretch="Uniform">
<Image.RenderTransform>
<RotateTransform Angle="{Binding Angulo}" />
</Image.RenderTransform>
</Image>
</Grid>
</UserControl>

View File

@ -0,0 +1,205 @@
using LibS7Adv;
using System.Windows;
using System.Windows.Controls;
using CommunityToolkit.Mvvm.ComponentModel;
using System.Diagnostics;
using CtrEditor.FuncionesBase;
using System.ComponentModel;
namespace CtrEditor.ObjetosSim
{
/// <summary>
/// Interaction logic for ucBottGenerator.xaml
/// </summary>
public partial class osBottGenerator : osBase, IosBase
{
TimerTON_TOFF _TON_TOFF = new TimerTON_TOFF();
private float TiempoRestante;
public static string NombreClase()
{
return "BottGenerator";
}
private string nombre = NombreClase();
public override string Nombre { get => nombre; set => SetProperty(ref nombre, value); }
[ObservableProperty]
private float offsetLeftSalida;
[ObservableProperty]
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]
[property: Description("PLC tag for consense to run. 1 => always")]
[property: Category("Enable to Run:")]
private string tag_consenso;
[ObservableProperty]
[property: Description("Consense to run.")]
[property: Category("Enable to Run:")]
private bool consenso;
partial void OnConsensoChanged(bool value)
{
_TON_TOFF.Senal = value;
}
[ObservableProperty]
[property: Description("Consense is Normally close.")]
[property: Category("Enable to Run:")]
private bool consenso_NC;
[ObservableProperty]
[property: Description("Enable filter.")]
[property: Category("Enable to Run:")]
private bool consenso_Filtrado;
[ObservableProperty]
[property: Description("Time ON in s.")]
[property: Category("Enable to Run:")]
float filtro_consenso_ON_s;
[ObservableProperty]
[property: Description("Time OFF in s.")]
[property: Category("Enable to Run:")]
float filtro_consenso_OFF_s;
[ObservableProperty]
[property: Description("Filter OUT signal.")]
[property: Category("Enable to Run:")]
bool filter_Output;
[ObservableProperty]
private float botellas_hora;
partial void OnBotellas_horaChanged(float value)
{
Botellas_segundo = value / 3600;
}
[ObservableProperty]
private float botellas_segundo;
partial void OnBotellas_segundoChanged(float value)
{
Botellas_hora = value * 3600;
}
[ObservableProperty]
private float velocidad_actual_percentual;
[ObservableProperty]
private float diametro_botella;
public override void UpdatePLC(PLCViewModel plc, int elapsedMilliseconds)
{
if (Consenso_NC)
Consenso = !LeerBitTag(Tag_consenso);
else
Consenso = LeerBitTag(Tag_consenso);
}
private bool HayEspacioParaNuevaBotella(float X, float Y)
{
float radioMinimo = Diametro_botella / 4; // Distancia mínima entre centros
float radioMinimoCuadrado = radioMinimo * radioMinimo;
// Buscar todas las botellas cercanas
foreach (var obj in _mainViewModel.ObjetosSimulables)
{
if (obj is osBotella)
{
float distanciaCuadrada = (float)(Math.Pow(obj.Left - X, 2) + Math.Pow(obj.Top - Y, 2));
if (distanciaCuadrada < radioMinimoCuadrado)
{
return false; // Hay una botella demasiado cerca
}
}
}
return true; // No hay botellas cercanas
}
public override void UpdateControl(int elapsedMilliseconds)
{
bool habilitado;
_TON_TOFF.Tiempo_ON_s = Filtro_consenso_ON_s;
_TON_TOFF.Tiempo_OFF_s = Filtro_consenso_OFF_s;
if (Consenso_Filtrado)
habilitado = _TON_TOFF.SenalFiltrada();
else
habilitado = Consenso;
Filter_Output = habilitado;
if (habilitado && Velocidad_actual_percentual > 0)
{
if (PrimeraActualizacion)
{
TiempoEntreBotellas = 3600 / (Botellas_hora * (Velocidad_actual_percentual / 100.0f));
TiempoRestante = TiempoEntreBotellas;
PrimeraActualizacion = false;
}
TiempoRestante -= elapsedMilliseconds / 1000.0f;
if (TiempoRestante <= 0)
{
var X = Left + OffsetLeftSalida;
var Y = Top + OffsetTopSalida;
if (HayEspacioParaNuevaBotella(X, Y))
{
var nuevaBotella = _mainViewModel.CrearObjetoSimulable(typeof(osBotella), X, Y);
((osBotella)nuevaBotella).Diametro = Diametro_botella;
((osBotella)nuevaBotella).Preserve_Outside_Transport = Preserve_Outside_Transport;
nuevaBotella.AutoCreated = true;
// Recalcular el tiempo entre botellas por si cambió la velocidad
TiempoEntreBotellas = 3600 / (Botellas_hora * (Velocidad_actual_percentual / 100.0f));
TiempoRestante = TiempoEntreBotellas;
}
}
}
else
{
PrimeraActualizacion = true;
}
}
public override void ucLoaded()
{
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
base.ucLoaded();
}
private float TiempoEntreBotellas; // Nuevo: almacena el intervalo calculado
private bool PrimeraActualizacion = true; // Nuevo: flag para la primera actualización
public override void SimulationStop()
{
PrimeraActualizacion = true; // Reset del flag al detener
base.SimulationStop();
}
}
public partial class ucBottGenerator : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public int zIndex_fromFrames { get; set; }
public ucBottGenerator()
{
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_Base()
{
return ZIndexEnum.Generadores;
}
}
}

View File

@ -3,14 +3,9 @@
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"
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim">
<UserControl.Resources>
<convert:MeterToPixelConverter x:Key="MeterToPixelConverter"/>
</UserControl.Resources>
<UserControl.DataContext>
<vm:osFiller/>
@ -20,7 +15,7 @@
<Image Source="/imagenes/filler.png"
Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}"
Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}}"
Stretch="Uniform">
Stretch="Fill">
<Image.RenderTransform>
<RotateTransform Angle="{Binding Angulo}" />
</Image.RenderTransform>

View File

@ -1,8 +1,10 @@
using CtrEditor.Convertidores;
using CtrEditor.Siemens;

using LibS7Adv;
using System.Windows;
using System.Windows.Controls;
using CommunityToolkit.Mvvm.ComponentModel;
using System.Diagnostics;
using CtrEditor.FuncionesBase;
namespace CtrEditor.ObjetosSim
{
@ -11,6 +13,7 @@ namespace CtrEditor.ObjetosSim
/// </summary>
public partial class osFiller : osBase, IosBase
{
TimerTON_TOFF _TON_TOFF = new TimerTON_TOFF();
// Otros datos y métodos relevantes para la simulación
private float TiempoRestante;
@ -31,8 +34,23 @@ namespace CtrEditor.ObjetosSim
private float offsetLeftSalida;
[ObservableProperty]
private float offsetTopSalida;
[ObservableProperty]
private string tag_consenso;
[ObservableProperty]
private bool consenso;
partial void OnConsensoChanged(bool value)
{
_TON_TOFF.Senal = value;
}
[ObservableProperty]
private bool consenso_NC;
[ObservableProperty]
private bool consenso_Filtrado;
[ObservableProperty]
float filtro_consenso_s;
[ObservableProperty]
private float botellas_hora;
@ -53,14 +71,6 @@ namespace CtrEditor.ObjetosSim
private float velocidad_actual_percentual;
[ObservableProperty]
private float diametro_botella;
[ObservableProperty]
private string tag_consenso;
[ObservableProperty]
private float ancho;
[ObservableProperty]
private float alto;
[ObservableProperty]
private float angulo;
public osFiller()
{
@ -70,16 +80,29 @@ namespace CtrEditor.ObjetosSim
Velocidad_actual_percentual = 0;
Diametro_botella = 0.1f;
Botellas_hora = 10000;
Filtro_consenso_s = 1;
}
public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds)
public override void UpdatePLC(PLCViewModel plc, int elapsedMilliseconds)
{
Consenso = LeerBitTag(Tag_consenso);
if (Consenso_NC)
Consenso = !LeerBitTag(Tag_consenso);
else
Consenso = LeerBitTag(Tag_consenso);
}
public override void UpdateControl(int elapsedMilliseconds)
{
if (Consenso && Velocidad_actual_percentual > 0)
bool habilitado;
_TON_TOFF.Tiempo_ON_s = _TON_TOFF.Tiempo_OFF_s = Filtro_consenso_s;
if (Consenso_Filtrado)
habilitado = _TON_TOFF.SenalFiltrada();
else
habilitado = Consenso;
if (habilitado && Velocidad_actual_percentual > 0)
{
TiempoRestante -= elapsedMilliseconds / 1000.0f;
if (TiempoRestante <= 0)
@ -108,10 +131,10 @@ namespace CtrEditor.ObjetosSim
if (distancia > distanciaMinima)
{
var nuevaBotella = _mainViewModel.CrearObjetoSimulable(typeof(osBotella), X, Y);
((osBotella)nuevaBotella).Diametro = Diametro_botella;
osBotella nuevaBotella = (osBotella)_mainViewModel.CrearObjetoSimulable(typeof(osBotella), X, Y);
nuevaBotella.Diametro = Diametro_botella;
nuevaBotella.AutoCreated = true;
UltimaBotella = (osBotella)nuevaBotella;
UltimaBotella = nuevaBotella;
}
}
}
@ -126,7 +149,7 @@ namespace CtrEditor.ObjetosSim
{
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
ActualizarLeftTop();
base.ucLoaded();
}
}
@ -134,6 +157,7 @@ namespace CtrEditor.ObjetosSim
public partial class ucFiller : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public int zIndex_fromFrames { get; set; }
public ucFiller()
{
@ -153,28 +177,14 @@ namespace CtrEditor.ObjetosSim
{
if (Datos is osFiller datos)
{
datos.Ancho = PixelToMeter.Instance.calc.PixelsToMeters(width);
datos.Alto = PixelToMeter.Instance.calc.PixelsToMeters(width);
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)
{
if (Datos != null)
if (Datos is osFiller datos)
datos.Angulo = Angle;
}
public void Highlight(bool State) { }
public int ZIndex()
public ZIndexEnum ZIndex_Base()
{
return 10;
return ZIndexEnum.Generadores;
}
}

View File

@ -3,16 +3,8 @@
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:vm="clr-namespace:CtrEditor.ObjetosSim"
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>
mc:Ignorable="d">
<UserControl.DataContext>
<vm:osTanque Alto="1" Ancho="1" Angulo="-4" />
@ -21,8 +13,8 @@
<Grid>
<Image x:Name="TankImage"
Source="/imagenes/tank.png"
Width="{Binding Alto, Converter={StaticResource MeterToPixelConverter}}"
Height="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}"
Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}"
Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}}"
Stretch="Uniform">
</Image>
<Slider

View File

@ -1,8 +1,8 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CtrEditor.Convertidores;
using CtrEditor.Siemens;
using LibS7Adv;
using System.Windows;
using System.Windows.Controls;
using CtrEditor.FuncionesBase;
namespace CtrEditor.ObjetosSim
{
@ -46,12 +46,6 @@ namespace CtrEditor.ObjetosSim
public float max_OUT_Scaled;
[ObservableProperty]
public float level;
[ObservableProperty]
public float ancho;
[ObservableProperty]
public float alto;
[ObservableProperty]
public float angulo;
public osTanque()
{
@ -61,7 +55,7 @@ namespace CtrEditor.ObjetosSim
Min_OUT_Scaled = 0;
}
public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds)
public override void UpdatePLC(PLCViewModel plc, int elapsedMilliseconds)
{
EscribirWordTagScaled(TagNivel_Word, Level, 0, 100, Min_OUT_Scaled, Max_OUT_Scaled);
Ingreso_Abierto = LeerBitTag(TagIngresoAbierto_Bool);
@ -87,7 +81,7 @@ namespace CtrEditor.ObjetosSim
{
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
ActualizarLeftTop();
base.ucLoaded();
}
}
@ -95,6 +89,7 @@ namespace CtrEditor.ObjetosSim
public partial class ucTanque : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public int zIndex_fromFrames { get; set; }
public ucTanque()
{
@ -110,32 +105,10 @@ namespace CtrEditor.ObjetosSim
{
Datos?.ucUnLoaded();
}
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()
public ZIndexEnum ZIndex_Base()
{
return 10;
return ZIndexEnum.Generadores;
}
}
}

View File

@ -4,11 +4,10 @@
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:vm="clr-namespace:CtrEditor.ObjetosSim"
xmlns:convert="clr-namespace:CtrEditor.Convertidores">
mc:Ignorable="d">
<UserControl.Resources>
<convert:MeterToPixelConverter x:Key="MeterToPixelConverter"/>
<Storyboard x:Key="PulsingStoryboard" RepeatBehavior="Forever">
<DoubleAnimation
Storyboard.TargetName="AnimatedEllipse"

View File

@ -1,11 +1,12 @@
using CtrEditor.Convertidores;
using CtrEditor.Siemens;

using LibS7Adv;
using CtrEditor.Simulacion;
using System.Windows;
using System.Windows.Controls;
using nkast.Aether.Physics2D.Common;
using System.Windows.Media.Animation;
using CommunityToolkit.Mvvm.ComponentModel;
using CtrEditor.FuncionesBase;
namespace CtrEditor.ObjetosSim
{
/// <summary>
@ -82,7 +83,7 @@ namespace CtrEditor.ObjetosSim
public override void UpdateGeometryStep()
{
}
public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds)
public override void UpdatePLC(PLCViewModel plc, int elapsedMilliseconds)
{
}
@ -94,7 +95,7 @@ namespace CtrEditor.ObjetosSim
{
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
ActualizarLeftTop();
base.ucLoaded();
SimGeometria = simulationManager.AddDescarte(Diametro, GetCentro());
}
public override void ucUnLoaded()
@ -109,6 +110,7 @@ namespace CtrEditor.ObjetosSim
public partial class ucDescarte : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public int zIndex_fromFrames { get; set; }
public ucDescarte()
{
@ -129,28 +131,10 @@ namespace CtrEditor.ObjetosSim
{
Datos?.ucUnLoaded();
}
public void Resize(float width, float height)
{
if (Datos is osDescarte datos)
{
datos.Diametro = 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)
{
}
public void Highlight(bool State) { }
public int ZIndex()
public ZIndexEnum ZIndex_Base()
{
return 10;
return ZIndexEnum.Descarte;
}
}

View File

@ -4,12 +4,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim"
xmlns:convert="clr-namespace:CtrEditor.Convertidores">
<UserControl.Resources>
<convert:MeterToPixelConverter x:Key="MeterToPixelConverter"/>
</UserControl.Resources>
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim">
<UserControl.DataContext>
<vm:osGuia/>

View File

@ -1,9 +1,10 @@
using System.Windows;
using System.Windows.Controls;
using CommunityToolkit.Mvvm.ComponentModel;
using CtrEditor.Convertidores;
using CtrEditor.Siemens;
using LibS7Adv;
using CtrEditor.Simulacion;
using CtrEditor.FuncionesBase;
namespace CtrEditor.ObjetosSim
{
@ -25,21 +26,20 @@ namespace CtrEditor.ObjetosSim
set => SetProperty(ref nombre, value);
}
[ObservableProperty]
public float ancho;
[ObservableProperty]
public float altoGuia;
[ObservableProperty]
public float angulo;
private void ActualizarGeometrias()
{
if (_visualRepresentation is ucGuia uc)
UpdateOrCreateLine(SimGeometria, uc.Guia);
}
public override void OnMoveResizeRotate()
{
ActualizarGeometrias();
}
public osGuia()
{
Ancho = 1;
@ -57,12 +57,12 @@ namespace CtrEditor.ObjetosSim
public override void UpdateGeometryStep()
{
}
public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds) { }
public override void UpdatePLC(PLCViewModel plc, int elapsedMilliseconds) { }
public override void ucLoaded()
{
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
ActualizarLeftTop();
base.ucLoaded();
if (_visualRepresentation is ucGuia uc)
SimGeometria = AddLine(simulationManager, uc.Guia);
@ -80,6 +80,7 @@ namespace CtrEditor.ObjetosSim
public partial class ucGuia : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public int zIndex_fromFrames { get; set; }
public ucGuia()
{
@ -95,29 +96,10 @@ namespace CtrEditor.ObjetosSim
{
Datos?.ucUnLoaded();
}
public void Resize(float width, float height)
{
if (Datos is osGuia datos)
datos.Ancho = 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 osGuia datos)
datos.Angulo = Angle;
}
public void Highlight(bool State) { }
public int ZIndex()
public ZIndexEnum ZIndex_Base()
{
return 1;
return ZIndexEnum.Guias;
}
}

View File

@ -3,13 +3,9 @@
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:localuc="clr-namespace:CtrEditor.ObjetosSim.UserControls"
xmlns:convert="clr-namespace:CtrEditor.Convertidores"
xmlns:localuc="clr-namespace:CtrEditor.ObjetosSim.UserControls"
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim"
mc:Ignorable="d">
<UserControl.Resources>
<convert:MeterToPixelConverter x:Key="MeterToPixelConverter"/>
</UserControl.Resources>
<UserControl.DataContext>
<vm:osTransporteCurva />

View File

@ -1,10 +1,12 @@
using System.Windows;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using CommunityToolkit.Mvvm.ComponentModel;
using CtrEditor.Convertidores;
using CtrEditor.Siemens;
using LibS7Adv;
using CtrEditor.Simulacion;
using Xceed.Wpf.Toolkit.PropertyGrid.Attributes;
using CtrEditor.FuncionesBase;
using System.Text.Json.Serialization;
namespace CtrEditor.ObjetosSim
{
@ -13,16 +15,12 @@ namespace CtrEditor.ObjetosSim
/// </summary>
public partial class osTransporteCurva : osBase, IosBase
{
private float frictionCoefficient;
private float velMax50hz; // en metros por minuto
private float tiempoRampa;
private bool esMarcha;
private float _velocidadActual;
private osBase _osMotor = null;
private osBase Motor = null;
private simCurve Simulation_TransporteCurva;
private float _velocidadActual;
public static string NombreClase()
{
return "Transporte Curva 90";
@ -35,15 +33,92 @@ namespace CtrEditor.ObjetosSim
}
[ObservableProperty]
private float radioExterno;
[ObservableProperty]
private float radioInterno;
[ObservableProperty]
private string motor;
public float velocidadActual;
partial void OnVelocidadActualChanged(float value)
{
SetSpeed();
}
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(AnguloFinal))]
public float angulo;
bool invertirDireccion;
partial void OnInvertirDireccionChanged(bool value)
{
SetSpeed();
}
void SetSpeed()
{
if (InvertirDireccion)
Simulation_TransporteCurva?.SetSpeed(-VelocidadActual);
else
Simulation_TransporteCurva?.SetSpeed(VelocidadActual);
}
[ObservableProperty]
private float radioExterno;
partial void OnRadioExternoChanged(float value)
{
// Update ancho and alto based on radioExterno
Ancho = value * 2;
Alto = value * 2;
// Ensure radioInterno maintains proper proportion if needed
if (RadioInterno >= value)
{
RadioInterno = value * 0.75f; // Default proportion
}
ActualizarGeometrias();
}
[ObservableProperty]
private float radioInterno;
[ObservableProperty]
[property: Description("Bit to enable Link to Motor")]
[property: Category("PLC link:")]
string tag_ReleActivatedMotor;
[ObservableProperty]
[property: Description("Link to Motor")]
[property: Category("PLC link:")]
[property: ItemsSource(typeof(osBaseItemsSource<osVMmotorSim>))]
string id_Motor;
[JsonIgnore]
private PropertyChangedEventHandler motorPropertyChangedHandler;
partial void OnId_MotorChanged(string value)
{
if (Motor != null && motorPropertyChangedHandler != null)
Motor.PropertyChanged -= motorPropertyChangedHandler;
if (_mainViewModel != null && !string.IsNullOrEmpty(value))
{
Motor = (osVMmotorSim)_mainViewModel.ObjetosSimulables.FirstOrDefault(s => s is osVMmotorSim motor && motor.Nombre == value);
if (Motor != null)
{
motorPropertyChangedHandler = (sender, e) =>
{
if (e.PropertyName == nameof(osVMmotorSim.Nombre))
{
Id_Motor = ((osVMmotorSim)sender).Nombre;
}
};
Motor.PropertyChanged += motorPropertyChangedHandler;
}
}
}
public override void AnguloChanged(float value)
{
OnPropertyChanged(nameof(AnguloFinal));
}
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(AnguloFinal))]
@ -55,59 +130,103 @@ namespace CtrEditor.ObjetosSim
get => Angulo+Arco_en_grados;
}
public float VelocidadActual
{
get => _velocidadActual;
set
{
_velocidadActual = value;
Simulation_TransporteCurva?.SetSpeed(value);
OnPropertyChanged(nameof(VelocidadActual));
}
}
private void ActualizarGeometrias()
{
if (_visualRepresentation is ucTransporteCurva uc)
{
UpdateCurve(Simulation_TransporteCurva, RadioInterno, RadioExterno, Angulo, Angulo+Arco_en_grados);
Simulation_TransporteCurva.Speed = VelocidadActual;
SetSpeed();
}
}
public float FrictionCoefficient { get => frictionCoefficient; set => frictionCoefficient = value; }
public float VelMax50hz { get => velMax50hz; set => velMax50hz = value; }
public float TiempoRampa { get => tiempoRampa; set => tiempoRampa = value; }
public bool EsMarcha { get => esMarcha; set => esMarcha = value; }
public override void OnMoveResizeRotate()
{
ActualizarGeometrias();
}
[ObservableProperty]
public float frictionCoefficient;
[ObservableProperty]
public float velMax50hz;
[ObservableProperty]
public float tiempoRampa;
[ObservableProperty]
public bool esMarcha;
public override void OnResize(float Delta_Width, float Delta_Height)
{
// Calculate the proportional change factor
float widthChangeFactor = (Ancho + Delta_Width) / Ancho;
float heightChangeFactor = (Alto + Delta_Height) / Alto;
// Use the average or minimum change factor to maintain aspect ratio
float changeFactor = Math.Min(widthChangeFactor, heightChangeFactor);
// Save the original radiuses for calculating position adjustments
float originalRadioExterno = RadioExterno;
// Apply the change factor to both radios
RadioExterno *= changeFactor;
RadioInterno *= changeFactor;
// Calculate position adjustment to keep the component centered
float radiusDifference = RadioExterno - originalRadioExterno;
// Adjust Left and Top to maintain center position
// We move by negative half the difference because the component expands outward
Left -= radiusDifference;
Top -= radiusDifference;
// Ensure minimums
if (RadioExterno < 0.1f)
RadioExterno = 0.1f;
if (RadioInterno < 0.05f)
RadioInterno = 0.05f;
// Ensure radioInterno is always less than radioExterno
if (RadioInterno >= RadioExterno)
RadioInterno = RadioExterno * 0.75f;
}
public osTransporteCurva()
{
RadioExterno = 1.3f;
RadioInterno = 1;
RadioInterno = 1f;
Ancho = RadioExterno * 2; // Set initial width based on external radius
Alto = RadioExterno * 2; // Set initial height based on external radius
Arco_en_grados = 90;
Tag_ReleActivatedMotor = "1";
}
public override void UpdateGeometryStart()
{
// Se llama antes de la simulacion
ActualizarGeometrias();
}
public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds)
public override void UpdatePLC(PLCViewModel plc, int elapsedMilliseconds)
{
if (_osMotor != null)
if (Motor != null)
{
if (_osMotor is osVMmotorSim motor)
VelocidadActual = motor.Velocidad;
if (Motor is osVMmotorSim motor)
if (LeerBitTag(Tag_ReleActivatedMotor))
VelocidadActual = motor.Velocidad;
else
VelocidadActual = 0;
}
else
_osMotor = ObtenerLink(Motor, typeof(osVMmotorSim));
}
public override void ucLoaded()
{
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
ActualizarLeftTop();
base.ucLoaded();
OnId_MotorChanged(Id_Motor); // Link Id_Motor = Motor
if (_visualRepresentation is ucTransporteCurva uc)
Simulation_TransporteCurva = AddCurve(RadioInterno,RadioExterno, Angulo, Angulo + Arco_en_grados);
@ -126,6 +245,7 @@ namespace CtrEditor.ObjetosSim
public partial class ucTransporteCurva : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public int zIndex_fromFrames { get; set; }
public ucTransporteCurva()
{
@ -141,35 +261,10 @@ namespace CtrEditor.ObjetosSim
{
Datos?.ucUnLoaded();
}
public void Resize(float RadioExterno, float RadioInterno)
{
if (Datos is osTransporteCurva datos)
{
if (RadioExterno > RadioInterno && RadioExterno > 0 && RadioInterno >= 0)
{
datos.RadioExterno = PixelToMeter.Instance.calc.PixelsToMeters(RadioExterno);
datos.RadioInterno = PixelToMeter.Instance.calc.PixelsToMeters(RadioInterno);
}
}
}
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 osTransporteCurva datos)
datos.Angulo = Angle;
}
public void Highlight(bool State) { }
public int ZIndex()
public ZIndexEnum ZIndex_Base()
{
return 1;
return ZIndexEnum.Estaticos;
}
}

View File

@ -4,13 +4,9 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim"
xmlns:convert="clr-namespace:CtrEditor.Convertidores"
mc:Ignorable="d">
<UserControl.Resources>
<convert:MeterToPixelConverter x:Key="MeterToPixelConverter"/>
<convert:DistanceToMarginConverter x:Key="DistanceToMarginConverter"/>
<!-- Define the VisualBrush for the conveyor belt pattern -->
<VisualBrush x:Key="BeltBrush" TileMode="Tile" Viewport="0,0,20,10" ViewportUnits="Absolute" Viewbox="0,0,20,10" ViewboxUnits="Absolute">
<VisualBrush.Transform>
@ -20,8 +16,8 @@
</VisualBrush.Transform>
<VisualBrush.Visual>
<Canvas>
<Rectangle Fill="#FFBFBFBF" Width="10" Height="10"/>
<Rectangle Fill="LightGray" Width="10" Height="10" Canvas.Left="10"/>
<Rectangle Fill="LightGray" Width="10" Height="10" />
<Rectangle Fill="GhostWhite" Width="10" Height="10" Canvas.Left="10" />
</Canvas>
</VisualBrush.Visual>
</VisualBrush>
@ -32,18 +28,37 @@
</UserControl.DataContext>
<Grid>
<Canvas>
<StackPanel x:Name="RectanglesContainer">
<StackPanel.RenderTransform>
<Canvas RenderTransformOrigin="0,0">
<Canvas.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform Angle="{Binding Angulo}"/>
</StackPanel.RenderTransform>
<Rectangle x:Name="GuiaSuperior" Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}" Height="{Binding AltoGuia, Converter={StaticResource MeterToPixelConverter}}" Fill="Blue"
Margin="{Binding Distance, Converter={StaticResource DistanceToMarginConverter}}"/>
<Rectangle x:Name="Transporte" Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}" Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}}"
Margin="{Binding Distance, Converter={StaticResource DistanceToMarginConverter}}"
Fill="{StaticResource BeltBrush}"/>
<Rectangle x:Name="GuiaInferior" Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}" Height="{Binding AltoGuia, Converter={StaticResource MeterToPixelConverter}}" Fill="Blue"/>
<TranslateTransform/>
</TransformGroup>
</Canvas.RenderTransform>
<StackPanel x:Name="RectanglesContainer">
<Rectangle x:Name="GuiaSuperior" Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}"
Height="{Binding AltoGuia, Converter={StaticResource MeterToPixelConverter}}"
Fill="{Binding Color, Converter={StaticResource ColorToBrushConverter}}"
Margin="{Binding Distance, Converter={StaticResource DistanceToMarginConverter}}"/>
<Rectangle x:Name="Transporte" Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}"
Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}}"
Margin="{Binding Distance, Converter={StaticResource DistanceToMarginConverter}}"
Fill="{StaticResource BeltBrush}"/>
<Rectangle x:Name="GuiaInferior" Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}"
Height="{Binding AltoGuia, Converter={StaticResource MeterToPixelConverter}}"
Fill="{Binding Color, Converter={StaticResource ColorToBrushConverter}}"/>
</StackPanel>
<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>
</Grid>
</UserControl>

View File

@ -1,9 +1,13 @@
using System.Windows;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using CommunityToolkit.Mvvm.ComponentModel;
using CtrEditor.Convertidores;
using CtrEditor.Siemens;
using LibS7Adv;
using CtrEditor.Simulacion;
using Xceed.Wpf.Toolkit.PropertyGrid.Attributes;
using CtrEditor.FuncionesBase;
using System.Text.Json.Serialization;
namespace CtrEditor.ObjetosSim
{
@ -12,7 +16,7 @@ namespace CtrEditor.ObjetosSim
/// </summary>
public partial class osTransporteGuias : osBase, IosBase
{
private osBase _osMotor = null;
private osBase Motor = null;
private simTransporte? SimGeometria;
private simGuia? Guia_Superior;
@ -24,6 +28,7 @@ namespace CtrEditor.ObjetosSim
return "Transporte Guias";
}
private string nombre = NombreClase();
public override string Nombre
{
get => nombre;
@ -31,34 +36,89 @@ namespace CtrEditor.ObjetosSim
}
[ObservableProperty]
private float velocidadActual;
public float velocidadActual;
partial void OnVelocidadActualChanged(float value)
{
SimGeometria?.SetSpeed(value);
SetSpeed();
}
[ObservableProperty]
bool invertirDireccion;
partial void OnInvertirDireccionChanged(bool value)
{
SetSpeed();
if (_visualRepresentation is ucTransporteGuias uc)
{
CrearAnimacionStoryBoardTrasnporte(uc.Transporte, InvertirDireccion);
ActualizarAnimacionStoryBoardTransporte(VelocidadActual);
}
}
void SetSpeed()
{
if (InvertirDireccion)
SimGeometria?.SetSpeed(-VelocidadActual);
else
SimGeometria?.SetSpeed(VelocidadActual);
ActualizarAnimacionStoryBoardTransporte(VelocidadActual);
}
[ObservableProperty]
public string motor;
partial void OnMotorChanged(string value)
[ObservableProperty]
Color color = Colors.Blue;
[ObservableProperty]
[property: Description("Bit to enable Link to Motor")]
[property: Category("PLC link:")]
string tag_ReleActivatedMotor;
[ObservableProperty]
[property: Description("Link to Motor")]
[property: Category("PLC link:")]
[property: ItemsSource(typeof(osBaseItemsSource<osVMmotorSim>))]
string id_Motor;
[JsonIgnore]
private PropertyChangedEventHandler motorPropertyChangedHandler;
partial void OnId_MotorChanged(string value)
{
_osMotor = ObtenerLink(Motor, typeof(osVMmotorSim));
if (Motor != null && motorPropertyChangedHandler != null)
Motor.PropertyChanged -= motorPropertyChangedHandler;
if (_mainViewModel != null && !string.IsNullOrEmpty(value))
{
Motor = (osVMmotorSim)_mainViewModel.ObjetosSimulables.FirstOrDefault(s => s is osVMmotorSim motor && motor.Nombre == value);
if (Motor != null)
{
motorPropertyChangedHandler = (sender, e) =>
{
if (e.PropertyName == nameof(osVMmotorSim.Nombre))
{
Id_Motor = ((osVMmotorSim)sender).Nombre;
}
};
Motor.PropertyChanged += motorPropertyChangedHandler;
}
}
}
[ObservableProperty]
public float ancho;
[ObservableProperty]
public float alto;
partial void OnAltoChanged(float value)
public override void AltoChanged(float value)
{
ActualizarGeometrias();
}
[ObservableProperty]
public float angulo;
public bool esFreno;
partial void OnEsFrenoChanged(bool value)
{
if (SimGeometria != null)
SimGeometria.isBrake = value;
}
[ObservableProperty]
public float frictionCoefficient;
[ObservableProperty]
@ -78,14 +138,18 @@ namespace CtrEditor.ObjetosSim
{
UpdateRectangle(SimGeometria, uc.Transporte, Alto, Ancho, Angulo);
UpdateOrCreateLine(Guia_Superior, uc.GuiaSuperior);
UpdateOrCreateLine(Guia_Inferior, uc.GuiaInferior) ;
UpdateOrCreateLine(Guia_Inferior, uc.GuiaInferior);
SimGeometria.DistanceGuide2Guide = Alto;
SimGeometria.Speed = VelocidadActual;
ActualizarAnimacionStoryBoardTransporte(VelocidadActual);
SimGeometria.isBrake = esFreno;
SetSpeed();
}
}
public override void OnMoveResizeRotate()
{
ActualizarGeometrias();
}
public osTransporteGuias()
{
@ -93,6 +157,7 @@ namespace CtrEditor.ObjetosSim
Alto = 0.10f;
AltoGuia = 0.03f;
Distance = 0.01f;
Tag_ReleActivatedMotor = "1";
}
public override void UpdateGeometryStart()
@ -100,26 +165,28 @@ namespace CtrEditor.ObjetosSim
// Se llama antes de la simulacion
ActualizarGeometrias();
}
public override void SimulationStop()
{
// Se llama al detener la simulacion
ActualizarAnimacionStoryBoardTransporte(VelocidadActual);
}
public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds)
public override void UpdatePLC(PLCViewModel plc, int elapsedMilliseconds)
{
if (_osMotor != null)
{
if (_osMotor is osVMmotorSim motor)
VelocidadActual = motor.Velocidad;
} else if (Motor.Length > 0)
_osMotor = ObtenerLink(Motor, typeof(osVMmotorSim));
if (Motor != null)
if (Motor is osVMmotorSim id_motor)
if (LeerBitTag(Tag_ReleActivatedMotor))
VelocidadActual = id_motor.Velocidad;
else
VelocidadActual = 0;
}
public override void ucLoaded()
{
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
ActualizarLeftTop();
base.ucLoaded();
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
@ -131,9 +198,9 @@ namespace CtrEditor.ObjetosSim
Guia_Superior = AddLine(simulationManager, uc.GuiaSuperior);
Guia_Inferior = AddLine(simulationManager, uc.GuiaInferior);
CrearAnimacionStoryBoardTrasnporte(uc.Transporte);
CrearAnimacionStoryBoardTrasnporte(uc.Transporte, InvertirDireccion);
}
Motor = Motor; // Forzar la busqueda
OnId_MotorChanged(Id_Motor); // Link Id_Motor = Motor
}
public override void ucUnLoaded()
{
@ -150,6 +217,7 @@ namespace CtrEditor.ObjetosSim
public partial class ucTransporteGuias : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public int zIndex_fromFrames { get; set; }
public ucTransporteGuias()
{
@ -165,30 +233,12 @@ namespace CtrEditor.ObjetosSim
{
Datos?.ucUnLoaded();
}
public void Resize(float width, float height)
{
if (Datos is osTransporteGuias datos)
datos.Ancho = 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 osTransporteGuias datos)
datos.Angulo = Angle;
}
public void Highlight(bool State) { }
public int ZIndex()
public ZIndexEnum ZIndex_Base()
{
return 1;
return ZIndexEnum.Estaticos;
}
}
}

View File

@ -0,0 +1,87 @@
<UserControl x:Class="CtrEditor.ObjetosSim.ucTransporteGuiasUnion"
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"
xmlns:uc="clr-namespace:CtrEditor.ObjetosSim.UserControls"
mc:Ignorable="d">
<UserControl.Resources>
<!-- Define the VisualBrush for the conveyor belt pattern -->
<VisualBrush x:Key="BeltBrushA" TileMode="Tile" Viewport="0,0,20,10" ViewportUnits="Absolute" Viewbox="0,0,20,10" ViewboxUnits="Absolute">
<VisualBrush.Transform>
<TransformGroup>
<TranslateTransform/>
</TransformGroup>
</VisualBrush.Transform>
<VisualBrush.Visual>
<Canvas>
<Rectangle Fill="#FFBFBFBF" Width="10" Height="10"/>
<Rectangle Fill="LightGray" Width="10" Height="10" Canvas.Left="10"/>
</Canvas>
</VisualBrush.Visual>
</VisualBrush>
<VisualBrush x:Key="BeltBrushB" TileMode="Tile" Viewport="0,0,20,10" ViewportUnits="Absolute" Viewbox="0,0,20,10" ViewboxUnits="Absolute">
<VisualBrush.Transform>
<TransformGroup>
<TranslateTransform/>
</TransformGroup>
</VisualBrush.Transform>
<VisualBrush.Visual>
<Canvas>
<Rectangle Fill="#FFBFBFBF" Width="10" Height="10"/>
<Rectangle Fill="LightGray" Width="10" Height="10" Canvas.Left="10"/>
</Canvas>
</VisualBrush.Visual>
</VisualBrush>
</UserControl.Resources>
<UserControl.DataContext>
<vm:osTransporteGuiasUnion Color="Red"/>
</UserControl.DataContext>
<Grid>
<Canvas x:Name="Canvas" RenderTransformOrigin="0,0">
<Canvas.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform Angle="{Binding Angulo}"/>
<TranslateTransform/>
</TransformGroup>
</Canvas.RenderTransform>
<Rectangle x:Name="TransporteA" Width="{Binding AnchoTransporte_oculto, Converter={StaticResource MeterToPixelConverter},ConverterParameter=1}"
Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}}"
Fill="{StaticResource BeltBrushA}"
/>
<Rectangle x:Name="TransporteB" Width="{Binding AnchoTransporte_oculto, Converter={StaticResource MeterToPixelConverter},ConverterParameter=1}"
Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}}"
Fill="{StaticResource BeltBrushB}"
Canvas.Top="{Binding Alto, Converter={StaticResource MeterToPixelConverter},ConverterParameter=1.05}"
Canvas.Left="{Binding AnchoRecto, Converter={StaticResource MeterToPixelConverter},ConverterParameter=1}"
/>
<uc:ThreeLinesControl x:Name="GuiaSuperior" AnchoRecto="{Binding AnchoRecto, Converter={StaticResource MeterToPixelConverter},ConverterParameter=1}"
AnchoCentro="{Binding AnchoCentral, Converter={StaticResource MeterToPixelConverter},ConverterParameter=1}"
Altura="{Binding Alto, Converter={StaticResource MeterToPixelConverter},ConverterParameter=1}"
AltoGuia="{Binding AltoGuia, Converter={StaticResource MeterToPixelConverter},ConverterParameter=1}"
Canvas.Top="{Binding Distance, Converter={StaticResource MeterToPixelConverter},ConverterParameter=-1}" Color="{Binding Color}"/>
<uc:ThreeLinesControl x:Name="GuiaInferior" AnchoRecto="{Binding AnchoRecto, Converter={StaticResource MeterToPixelConverter},ConverterParameter=1}"
AnchoCentro="{Binding AnchoCentral, Converter={StaticResource MeterToPixelConverter},ConverterParameter=1}"
Altura="{Binding Alto, Converter={StaticResource MeterToPixelConverter},ConverterParameter=1}"
AltoGuia="{Binding AltoGuia, Converter={StaticResource MeterToPixelConverter},ConverterParameter=1}"
Color="{Binding Color}">
<Canvas.Top>
<MultiBinding Converter="{StaticResource SumConverter}">
<Binding Path="Alto" Converter="{StaticResource MeterToPixelConverter}" ConverterParameter="1.0" />
<Binding Path="Distance" Converter="{StaticResource MeterToPixelConverter}" ConverterParameter="1.0" />
</MultiBinding>
</Canvas.Top>
</uc:ThreeLinesControl>
</Canvas>
</Grid>
</UserControl>

View File

@ -0,0 +1,408 @@
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using CommunityToolkit.Mvvm.ComponentModel;
using LibS7Adv;
using CtrEditor.Simulacion;
using Xceed.Wpf.Toolkit.PropertyGrid.Attributes;
using CtrEditor.FuncionesBase;
namespace CtrEditor.ObjetosSim
{
/// <summary>
/// Interaction logic for ucTransporteGuiasUnion.xaml
/// </summary>
public partial class osTransporteGuiasUnion : osBase, IosBase
{
private osBase _osMotorA = null;
private osBase _osMotorB = null;
Dictionary<Rectangle, simTransporte> SimGeometriaT;
Dictionary<Rectangle, simGuia> SimGeometriaG;
Dictionary<Rectangle, Storyboard> Storyboards;
Dictionary<Rectangle, BoolReference> TransportsDirection;
Dictionary<Rectangle, FloatReference> TransportsVelocidad;
public static string NombreClase()
{
return "Transporte Guias Union";
}
private string nombre = NombreClase();
public override string Nombre
{
get => nombre;
set => SetProperty(ref nombre, value);
}
[ObservableProperty]
Color color;
[ObservableProperty]
[property: Description("Bit to enable Link to Motor")]
[property: Category("PLC link:")]
string tag_ReleActivatedMotor;
[ObservableProperty]
[property: Description("Link to Motor A")]
[property: Category("PLC link:")]
[property: ItemsSource(typeof(osBaseItemsSource<osVMmotorSim>))]
string id_MotorA;
partial void OnId_MotorAChanged(string value)
{
if (_osMotorA != null)
_osMotorA.PropertyChanged -= OnMotorPropertyChangedA;
if (_mainViewModel != null && value != null && value.Length > 0)
{
_osMotorA = (osVMmotorSim)_mainViewModel.ObjetosSimulables.FirstOrDefault(s => (s is osVMmotorSim && s.Nombre == value), null);
if (_osMotorA != null)
_osMotorA.PropertyChanged += OnMotorPropertyChangedA;
}
}
private void OnMotorPropertyChangedA(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(osVMmotorSim.Nombre))
{
Id_MotorA = ((osVMmotorSim)sender).Nombre;
}
}
[ObservableProperty]
[property: Description("Link to Motor B")]
[property: Category("PLC link:")]
[property: ItemsSource(typeof(osBaseItemsSource<osVMmotorSim>))]
string id_MotorB;
partial void OnId_MotorBChanged(string value)
{
if (_osMotorB != null)
_osMotorB.PropertyChanged -= OnMotorPropertyChangedB;
if (_mainViewModel != null && value != null && value.Length > 0)
{
_osMotorB = (osVMmotorSim)_mainViewModel.ObjetosSimulables.FirstOrDefault(s => (s is osVMmotorSim && s.Nombre == value), null);
if (_osMotorB != null)
_osMotorB.PropertyChanged += OnMotorPropertyChangedB;
}
}
private void OnMotorPropertyChangedB(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(osVMmotorSim.Nombre))
{
Id_MotorB = ((osVMmotorSim)sender).Nombre;
}
}
[ObservableProperty]
public float velocidadActualA;
partial void OnVelocidadActualAChanged(float value)
{
if (_visualRepresentation is ucTransporteGuiasUnion uc)
{
var transporte = uc.TransporteA;
SetSpeed(transporte);
}
}
[ObservableProperty]
bool invertirDireccionA;
partial void OnInvertirDireccionAChanged(bool value)
{
if (_visualRepresentation is ucTransporteGuiasUnion uc)
{
var transporte = uc.TransporteA;
SetSpeed(transporte);
ActualizarStoryboards(transporte);
}
}
[ObservableProperty]
public float velocidadActualB;
partial void OnVelocidadActualBChanged(float value)
{
if (_visualRepresentation is ucTransporteGuiasUnion uc)
{
var transporte = uc.TransporteB;
SetSpeed(transporte);
}
}
[ObservableProperty]
bool invertirDireccionB;
partial void OnInvertirDireccionBChanged(bool value)
{
if (_visualRepresentation is ucTransporteGuiasUnion uc)
{
var transporte = uc.TransporteB;
SetSpeed(transporte);
ActualizarStoryboards(transporte);
}
}
public override void OnResize(float Delta_Width, float Delta_Height)
{
AnchoRecto += Delta_Width;
}
[ObservableProperty]
public float anchoRecto;
[ObservableProperty]
public float anchoCentral;
partial void OnAnchoRectoChanged(float value)
{
AnchoTransporte_oculto = anchoRecto + anchoCentral;
}
partial void OnAnchoCentralChanged(float value)
{
AnchoTransporte_oculto = anchoRecto + anchoCentral;
}
[ObservableProperty]
public float anchoTransporte_oculto;
[ObservableProperty]
public float frictionCoefficient;
[ObservableProperty]
public float velMax50hz;
[ObservableProperty]
public float tiempoRampa;
[ObservableProperty]
public bool esMarcha;
[ObservableProperty]
private float distance;
[ObservableProperty]
private float altoGuia;
void ActualizarStoryboards(Rectangle transporte)
{
if (!Storyboards.Keys.Contains(transporte)) return;
var direccion = TransportsDirection[transporte].Value;
var velocidad = TransportsVelocidad[transporte].Value;
Storyboards[transporte] = CrearAnimacionMultiStoryBoardTrasnporte(Storyboards[transporte], transporte, direccion);
ActualizarAnimacionMultiStoryBoardTransporte(Storyboards[transporte], velocidad);
}
void SetSpeed(Rectangle transporte)
{
if (!Storyboards.Keys.Contains(transporte)) return;
var invertirDireccion = TransportsDirection[transporte].Value;
var velocidad = TransportsVelocidad[transporte].Value;
if (invertirDireccion)
SimGeometriaT[transporte]?.SetSpeed(-velocidad);
else
SimGeometriaT[transporte]?.SetSpeed(velocidad);
ActualizarAnimacionMultiStoryBoardTransporte(Storyboards[transporte], velocidad);
}
private void ActualizarGeometrias()
{
if (_visualRepresentation is ucTransporteGuiasUnion uc)
{
foreach (var transporte in SimGeometriaT)
{
UpdateRectangle(transporte.Value, transporte.Key, Alto, AnchoTransporte_oculto, Angulo);
ActualizarStoryboards(transporte.Key);
SetSpeed(transporte.Key);
}
foreach (var l in SimGeometriaG)
UpdateOrCreateLine(l.Value, l.Key);
}
}
public override void OnMoveResizeRotate()
{
ActualizarGeometrias();
}
public osTransporteGuiasUnion()
{
AnchoRecto = 0.5f;
AnchoCentral = 0.5f;
Alto = 0.10f;
AltoGuia = 0.03f;
Distance = 0.02f;
SimGeometriaT = new Dictionary<Rectangle, simTransporte>();
SimGeometriaG = new Dictionary<Rectangle, simGuia>();
Storyboards = new Dictionary<Rectangle, Storyboard>();
TransportsDirection = new Dictionary<Rectangle, BoolReference>();
TransportsVelocidad = new Dictionary<Rectangle, FloatReference>();
Tag_ReleActivatedMotor = "1";
}
public override void UpdateGeometryStart()
{
// Se llama antes de la simulacion
ActualizarGeometrias();
}
public override void SimulationStop()
{
// Se llama al detener la simulacion
if (_visualRepresentation is ucTransporteGuiasUnion uc)
{
SetSpeed(uc.TransporteB);
SetSpeed(uc.TransporteA);
}
}
public override void UpdatePLC(PLCViewModel plc, int elapsedMilliseconds)
{
if (_osMotorA != null)
{
if (_osMotorA is osVMmotorSim motor)
if (LeerBitTag(Tag_ReleActivatedMotor))
VelocidadActualA = motor.Velocidad;
else
VelocidadActualA = 0;
}
if (_osMotorB != null)
{
if (_osMotorB is osVMmotorSim motor)
if (LeerBitTag(Tag_ReleActivatedMotor))
VelocidadActualB = motor.Velocidad;
else
VelocidadActualB = 0;
}
}
public override void ucLoaded()
{
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
base.ucLoaded();
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
if (_visualRepresentation is ucTransporteGuiasUnion uc)
{
foreach (var child in uc.Canvas.Children)
if (child is Rectangle rect)
if (rect.Name.StartsWith("Transporte"))
{
SimGeometriaT.Add(rect,AddRectangle(simulationManager, rect, Alto, AnchoTransporte_oculto, Angulo));
Storyboards.Add(rect,CrearAnimacionMultiStoryBoardTrasnporte(rect,false));
}
foreach (var child in uc.GuiaSuperior.Canvas.Children)
if (child is Rectangle rect)
SimGeometriaG.Add(rect, AddLine(simulationManager, rect));
foreach (var child in uc.GuiaInferior.Canvas.Children)
if (child is Rectangle rect)
SimGeometriaG.Add(rect, AddLine(simulationManager, rect));
TransportsDirection.Add(uc.TransporteA, new BoolReference(() => InvertirDireccionA, value => InvertirDireccionA = value));
TransportsDirection.Add(uc.TransporteB, new BoolReference(() => InvertirDireccionB, value => InvertirDireccionB = value));
TransportsVelocidad.Add(uc.TransporteA, new FloatReference(() => VelocidadActualA, value => VelocidadActualA = value));
TransportsVelocidad.Add(uc.TransporteB, new FloatReference(() => VelocidadActualB, value => VelocidadActualB = value));
}
OnId_MotorAChanged(Id_MotorA); // Link Id_Motor = Motor
OnId_MotorBChanged(Id_MotorB); // Link Id_Motor = Motor
}
public override void ucUnLoaded()
{
// El UserControl se esta eliminando
// eliminar el objeto de simulacion
foreach (var s in SimGeometriaT)
simulationManager.Remove(s.Value);
foreach (var s in SimGeometriaG)
simulationManager.Remove(s.Value);
}
}
public class BoolReference
{
private Func<bool> getter;
private Action<bool> setter;
public BoolReference(Func<bool> getter, Action<bool> setter)
{
this.getter = getter;
this.setter = setter;
}
public bool Value
{
get => getter();
set => setter(value);
}
}
public class FloatReference
{
private Func<float> getter;
private Action<float> setter;
public FloatReference(Func<float> getter, Action<float> setter)
{
this.getter = getter;
this.setter = setter;
}
public float Value
{
get => getter();
set => setter(value);
}
}
public partial class ucTransporteGuiasUnion : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public int zIndex_fromFrames { get; set; }
public ucTransporteGuiasUnion()
{
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_Base()
{
return ZIndexEnum.Estaticos;
}
}
}

View File

@ -4,13 +4,10 @@
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"
mc:Ignorable="d"
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim"
xmlns:convert="clr-namespace:CtrEditor.Convertidores">
mc:Ignorable="d">
<UserControl.Resources>
<convert:MeterToPixelConverter x:Key="MeterToPixelConverter"/>
<!-- Define the VisualBrush for the conveyor belt pattern -->
<VisualBrush x:Key="BeltBrush" TileMode="Tile" Viewport="0,0,20,10" ViewportUnits="Absolute" Viewbox="0,0,20,10" ViewboxUnits="Absolute">
<VisualBrush.Transform>
@ -20,8 +17,8 @@
</VisualBrush.Transform>
<VisualBrush.Visual>
<Canvas>
<Rectangle Fill="Gray" Width="10" Height="10"/>
<Rectangle Fill="DarkGray" Width="10" Height="10" Canvas.Left="10"/>
<Rectangle Fill="LightGray" Width="10" Height="10"/>
<Rectangle Fill="GhostWhite" Width="10" Height="10" Canvas.Left="10"/>
</Canvas>
</VisualBrush.Visual>
</VisualBrush>
@ -31,14 +28,25 @@
<vm:osTransporteTTop Ancho="2"/>
</UserControl.DataContext>
<Canvas>
<Rectangle x:Name="Transporte"
<Canvas RenderTransformOrigin="0,0">
<Canvas.RenderTransform>
<TransformGroup>
<ScaleTransform />
<SkewTransform />
<RotateTransform Angle="{Binding Angulo}" />
<TranslateTransform />
</TransformGroup>
</Canvas.RenderTransform>
<Rectangle x:Name="Transporte"
Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}"
Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}}"
Fill="{StaticResource BeltBrush}">
<Rectangle.RenderTransform>
<RotateTransform Angle="{Binding Angulo}"/>
</Rectangle.RenderTransform>
</Rectangle>
<Viewbox
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>
</UserControl>

View File

@ -1,13 +1,12 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Animation;
using System.Windows.Media;
using CommunityToolkit.Mvvm.ComponentModel;
using CtrEditor.Convertidores;
using CtrEditor.Siemens;
using LibS7Adv;
using CtrEditor.Simulacion;
using System.Windows.Input;
using Xceed.Wpf.Toolkit.PropertyGrid.Attributes;
using System.ComponentModel;
using CtrEditor.FuncionesBase;
using System.Text.Json.Serialization;
namespace CtrEditor.ObjetosSim
{
@ -18,58 +17,103 @@ namespace CtrEditor.ObjetosSim
public partial class osTransporteTTop : osBase, IosBase
{
private osBase _osMotor = null;
private simTransporte SimGeometria;
private osVMmotorSim Motor;
public static string NombreClase()
{
return "Transporte";
}
private string nombre = "Transporte TTOP";
[property: Category("Id:")]
public override string Nombre
{
get => nombre;
set => SetProperty(ref nombre, value);
}
private float velocidadActual;
public float VelocidadActual
[ObservableProperty]
[property: Category("Simulation:")]
public float velocidadActual;
partial void OnVelocidadActualChanged(float value)
{
get => velocidadActual;
set
SetSpeed();
}
[ObservableProperty]
[property: Category("Simulation:")]
bool invertirDireccion;
partial void OnInvertirDireccionChanged(bool value)
{
SetSpeed();
if (_visualRepresentation is ucTransporteTTop uc)
{
if (value != velocidadActual)
CrearAnimacionStoryBoardTrasnporte(uc.Transporte, InvertirDireccion);
ActualizarAnimacionStoryBoardTransporte(VelocidadActual);
}
}
void SetSpeed()
{
if (InvertirDireccion)
SimGeometria?.SetSpeed(-VelocidadActual);
else
SimGeometria?.SetSpeed(VelocidadActual);
ActualizarAnimacionStoryBoardTransporte(VelocidadActual);
}
[ObservableProperty]
[property: Description("Bit to enable Link to Motor")]
[property: Category("PLC link:")]
string tag_ReleActivatedMotor;
[ObservableProperty]
[property: Description("Link to Motor")]
[property: Category("PLC link:")]
[property: ItemsSource(typeof(osBaseItemsSource<osVMmotorSim>))]
string id_Motor;
[JsonIgnore]
private PropertyChangedEventHandler motorPropertyChangedHandler;
partial void OnId_MotorChanged(string value)
{
if (Motor != null && motorPropertyChangedHandler != null)
Motor.PropertyChanged -= motorPropertyChangedHandler;
if (_mainViewModel != null && !string.IsNullOrEmpty(value))
{
Motor = (osVMmotorSim)_mainViewModel.ObjetosSimulables.FirstOrDefault(s => s is osVMmotorSim motor && motor.Nombre == value);
if (Motor != null)
{
velocidadActual = value;
SimGeometria?.SetSpeed(value);
SetProperty(ref velocidadActual, value);
ActualizarAnimacionStoryBoardTransporte(VelocidadActual);
motorPropertyChangedHandler = (sender, e) =>
{
if (e.PropertyName == nameof(osVMmotorSim.Nombre))
{
Id_Motor = ((osVMmotorSim)sender).Nombre;
}
};
Motor.PropertyChanged += motorPropertyChangedHandler;
}
}
}
[ObservableProperty]
public string motor;
partial void OnMotorChanged(string value)
{
_osMotor = ObtenerLink(Motor, typeof(osVMmotorSim));
}
[ObservableProperty]
public float ancho;
[ObservableProperty]
public float alto;
[ObservableProperty]
public float angulo;
[ObservableProperty]
[property: Category("Setup:")]
public float frictionCoefficient;
[ObservableProperty]
[property: Category("Setup:")]
public float velMax50hz;
[ObservableProperty]
[property: Category("Setup:")]
public float tiempoRampa;
[ObservableProperty]
[property: Category("Setup:")]
public bool esMarcha;
@ -77,16 +121,22 @@ namespace CtrEditor.ObjetosSim
{
if (_visualRepresentation is ucTransporteTTop uc)
{
UpdateRectangle(SimGeometria, uc.Transporte,Alto,Ancho,Angulo);
SimGeometria.Speed = VelocidadActual;
UpdateRectangle(SimGeometria, uc.Transporte, Alto, Ancho, Angulo);
SetSpeed();
}
ActualizarAnimacionStoryBoardTransporte(VelocidadActual);
}
public override void OnMoveResizeRotate()
{
ActualizarGeometrias();
}
public osTransporteTTop()
{
{
Ancho = 1;
Alto = 0.10f;
Tag_ReleActivatedMotor = "1";
}
public override void SimulationStop()
@ -100,27 +150,27 @@ namespace CtrEditor.ObjetosSim
ActualizarGeometrias();
}
public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds)
public override void UpdatePLC(PLCViewModel plc, int elapsedMilliseconds)
{
if (_osMotor != null)
{
if (_osMotor is osVMmotorSim motor)
VelocidadActual = motor.Velocidad;
}
else if (Motor.Length > 0)
_osMotor = ObtenerLink(Motor, typeof(osVMmotorSim));
if (Motor != null)
if (Motor is osVMmotorSim motor)
if (LeerBitTag(Tag_ReleActivatedMotor))
VelocidadActual = motor.Velocidad;
else
VelocidadActual = 0;
}
public override void ucLoaded()
{
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
ActualizarLeftTop();
base.ucLoaded();
OnId_MotorChanged(Id_Motor); // Link Id_Motor = Motor
if (_visualRepresentation is ucTransporteTTop uc)
{
SimGeometria = AddRectangle(simulationManager, uc.Transporte, Alto, Ancho, Angulo);
CrearAnimacionStoryBoardTrasnporte(uc.Transporte);
CrearAnimacionStoryBoardTrasnporte(uc.Transporte, InvertirDireccion);
}
}
public override void ucUnLoaded()
@ -135,6 +185,7 @@ namespace CtrEditor.ObjetosSim
public partial class ucTransporteTTop : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public int zIndex_fromFrames { get; set; }
public ucTransporteTTop()
{
@ -150,31 +201,16 @@ namespace CtrEditor.ObjetosSim
{
Datos?.ucUnLoaded();
}
public void Resize(float width, float height)
{
if (Datos is osTransporteTTop datos)
datos.Ancho = 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 osTransporteTTop datos)
datos.Angulo = Angle;
}
public void Highlight(bool State) { }
public int ZIndex()
public ZIndexEnum ZIndex_Base()
{
return 1;
return ZIndexEnum.Estaticos;
}
}
}

View File

@ -0,0 +1,50 @@
<UserControl x:Class="CtrEditor.ObjetosSim.ucTransporteTTopDualInverter"
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.Resources>
<!-- Define the VisualBrush for the conveyor belt pattern -->
<VisualBrush x:Key="BeltBrush" TileMode="Tile" Viewport="0,0,20,10" ViewportUnits="Absolute" Viewbox="0,0,20,10"
ViewboxUnits="Absolute">
<VisualBrush.Transform>
<TransformGroup>
<TranslateTransform />
</TransformGroup>
</VisualBrush.Transform>
<VisualBrush.Visual>
<Canvas>
<Rectangle Fill="LightGray" Width="10" Height="10" />
<Rectangle Fill="GhostWhite" Width="10" Height="10" Canvas.Left="10" />
</Canvas>
</VisualBrush.Visual>
</VisualBrush>
</UserControl.Resources>
<UserControl.DataContext>
<vm:osTransporteTTop Ancho="2" />
</UserControl.DataContext>
<Canvas RenderTransformOrigin="0,0">
<Canvas.RenderTransform>
<TransformGroup>
<ScaleTransform />
<SkewTransform />
<RotateTransform Angle="{Binding Angulo}" />
<TranslateTransform />
</TransformGroup>
</Canvas.RenderTransform>
<Rectangle x:Name="Transporte" Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}"
Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}}"
Fill="{StaticResource BeltBrush}">
</Rectangle>
<Viewbox 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>
</UserControl>

View File

@ -0,0 +1,267 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CtrEditor.FuncionesBase;
using System.Windows;
using System.Windows.Controls;
using CommunityToolkit.Mvvm.ComponentModel;
using LibS7Adv;
using CtrEditor.Simulacion;
using Xceed.Wpf.Toolkit.PropertyGrid.Attributes;
using System.ComponentModel;
using CtrEditor.FuncionesBase;
using System.Text.Json.Serialization;
namespace CtrEditor.ObjetosSim
{
/// <summary>
/// Interaction logic for ucTransporteTTop.xaml
/// </summary>
///
public partial class osTransporteTTopDualInverter : osBase, IosBase
{
private simTransporte SimGeometria;
private osVMmotorSim MotorA;
private osVMmotorSim MotorB;
public static string NombreClase()
{
return "Transporte Dual Inverter";
}
private string nombre = "Transporte TTOP Dual Inverter";
[property: Category("Id:")]
public override string Nombre
{
get => nombre;
set => SetProperty(ref nombre, value);
}
[ObservableProperty]
[property: Category("Simulation:")]
public float velocidadActual;
partial void OnVelocidadActualChanged(float value)
{
SetSpeed();
}
[ObservableProperty]
[property: Category("Simulation:")]
bool invertirDireccion;
partial void OnInvertirDireccionChanged(bool value)
{
SetSpeed();
if (_visualRepresentation is ucTransporteTTop uc)
{
CrearAnimacionStoryBoardTrasnporte(uc.Transporte, InvertirDireccion);
ActualizarAnimacionStoryBoardTransporte(VelocidadActual);
}
}
void SetSpeed()
{
if (InvertirDireccion)
SimGeometria?.SetSpeed(-VelocidadActual);
else
SimGeometria?.SetSpeed(VelocidadActual);
ActualizarAnimacionStoryBoardTransporte(VelocidadActual);
}
[ObservableProperty]
[property: Description("Bit to enable Link to Inverter A")]
[property: Category("PLC link:")]
string tag_ReleActivatedMotor_A;
[ObservableProperty]
[property: Description("Bit to enable Link to Inverter B")]
[property: Category("PLC link:")]
string tag_ReleActivatedMotor_B;
[ObservableProperty]
[property: Description("Link to Inverter A")]
[property: Category("PLC link:")]
[property: ItemsSource(typeof(osBaseItemsSource<osVMmotorSim>))]
string id_Motor_A;
[ObservableProperty]
[property: Description("Link to Inverter B")]
[property: Category("PLC link:")]
[property: ItemsSource(typeof(osBaseItemsSource<osVMmotorSim>))]
string id_Motor_B;
[JsonIgnore]
private PropertyChangedEventHandler motorPropertyChangedHandler;
partial void OnId_Motor_AChanged(string value)
{
if (MotorA != null && motorPropertyChangedHandler != null)
MotorA.PropertyChanged -= motorPropertyChangedHandler;
if (_mainViewModel != null && !string.IsNullOrEmpty(value))
{
MotorA = (osVMmotorSim)_mainViewModel.ObjetosSimulables.FirstOrDefault(s => s is osVMmotorSim motor && motor.Nombre == value);
if (MotorA != null)
{
motorPropertyChangedHandler = (sender, e) =>
{
if (e.PropertyName == nameof(osVMmotorSim.Nombre))
{
Id_Motor_A = ((osVMmotorSim)sender).Nombre;
}
};
MotorA.PropertyChanged += motorPropertyChangedHandler;
}
}
}
partial void OnId_Motor_BChanged(string value)
{
if (MotorB != null && motorPropertyChangedHandler != null)
MotorB.PropertyChanged -= motorPropertyChangedHandler;
if (_mainViewModel != null && !string.IsNullOrEmpty(value))
{
MotorB = (osVMmotorSim)_mainViewModel.ObjetosSimulables.FirstOrDefault(s => s is osVMmotorSim motor && motor.Nombre == value);
if (MotorB != null)
{
motorPropertyChangedHandler = (sender, e) =>
{
if (e.PropertyName == nameof(osVMmotorSim.Nombre))
{
Id_Motor_B = ((osVMmotorSim)sender).Nombre;
}
};
MotorB.PropertyChanged += motorPropertyChangedHandler;
}
}
}
[ObservableProperty]
[property: Category("Setup:")]
public float frictionCoefficient;
[ObservableProperty]
[property: Category("Setup:")]
public float velMax50hz;
[ObservableProperty]
[property: Category("Setup:")]
public float tiempoRampa;
[ObservableProperty]
[property: Category("Setup:")]
public bool esMarcha;
private void ActualizarGeometrias()
{
if (_visualRepresentation is ucTransporteTTop uc)
{
UpdateRectangle(SimGeometria, uc.Transporte, Alto, Ancho, Angulo);
SetSpeed();
}
ActualizarAnimacionStoryBoardTransporte(VelocidadActual);
}
public override void OnMoveResizeRotate()
{
ActualizarGeometrias();
}
public osTransporteTTopDualInverter()
{
Ancho = 1;
Alto = 0.10f;
Tag_ReleActivatedMotor_A = "1";
Tag_ReleActivatedMotor_B = "1";
}
public override void SimulationStop()
{
// Se llama al detener la simulacion
ActualizarAnimacionStoryBoardTransporte(VelocidadActual);
}
public override void UpdateGeometryStart()
{
// Se llama antes de la simulacion
ActualizarGeometrias();
}
public override void UpdatePLC(PLCViewModel plc, int elapsedMilliseconds)
{
if (LeerBitTag(Tag_ReleActivatedMotor_A))
{
if (MotorA != null)
if (MotorA is osVMmotorSim motor)
VelocidadActual = motor.Velocidad;
else
VelocidadActual = 0;
}
else if (LeerBitTag(Tag_ReleActivatedMotor_B))
{
if (MotorB != null)
if (MotorB is osVMmotorSim motor)
VelocidadActual = motor.Velocidad;
else
VelocidadActual = 0;
}
}
public override void ucLoaded()
{
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
base.ucLoaded();
OnId_Motor_AChanged(Id_Motor_A); // Link Id_Motor = Motor
OnId_Motor_BChanged(Id_Motor_B); // Link Id_Motor = Motor
if (_visualRepresentation is ucTransporteTTop uc)
{
SimGeometria = AddRectangle(simulationManager, uc.Transporte, Alto, Ancho, Angulo);
CrearAnimacionStoryBoardTrasnporte(uc.Transporte, InvertirDireccion);
}
}
public override void ucUnLoaded()
{
// El UserControl se esta eliminando
// eliminar el objeto de simulacion
simulationManager.Remove(SimGeometria);
}
}
public partial class ucTransporteTTopDualInverter : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public int zIndex_fromFrames { get; set; }
public ucTransporteTTopDualInverter()
{
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_Base()
{
return ZIndexEnum.Estaticos;
}
}
}

View File

@ -4,34 +4,40 @@
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:vm="clr-namespace:CtrEditor.ObjetosSim"
xmlns:convert="clr-namespace:CtrEditor.Convertidores">
mc:Ignorable="d">
<UserControl.Resources>
<convert:MeterToPixelConverter x:Key="MeterToPixelConverter"/>
</UserControl.Resources>
<UserControl.DataContext>
<vm:osVMmotorSim ImageSource_oculta="/imagenes/motorNegro.png" />
<vm:osVMmotorSim ImageSource_oculta="/imagenes/motorNegro.png"/>
</UserControl.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid RenderTransformOrigin="0,0">
<Grid.RenderTransform>
<TransformGroup>
<RotateTransform Angle="{Binding Angulo}"/>
</TransformGroup>
</Grid.RenderTransform>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Content="{Binding Nombre}"
<Image Grid.Column="0" Source="{Binding ImageSource_oculta}"
Width="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}}"
Height="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}}"
Stretch="Uniform"/>
<Viewbox Grid.Column="1" Stretch="Uniform">
<Label Content="{Binding Nombre}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Background="Transparent"
Foreground="Black"/>
<Image Grid.Row="1" Source="{Binding ImageSource_oculta}"
Width="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}}"
Height="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}}"
Stretch="Uniform"/>
Foreground="Black"
Opacity="0.5"/>
</Viewbox>
</Grid>
</UserControl>

View File

@ -1,10 +1,11 @@
using CtrEditor.Convertidores;
using CtrEditor.Siemens;
using CommunityToolkit.Mvvm.ComponentModel;
using CtrEditor.FuncionesBase;
using LibS7Adv;
using Newtonsoft.Json;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using Newtonsoft.Json;
using CommunityToolkit.Mvvm.ComponentModel;
namespace CtrEditor.ObjetosSim
{
@ -17,7 +18,7 @@ namespace CtrEditor.ObjetosSim
{
// Otros datos y métodos relevantes para la simulación
private VMSimMotor motState = new VMSimMotor();
public static string NombreClase()
@ -37,8 +38,23 @@ namespace CtrEditor.ObjetosSim
[ObservableProperty]
public float tamano;
public override void OnResize(float Delta_Width, float Delta_Height)
{
Tamano += Delta_Width + Delta_Height;
}
[ObservableProperty]
public float maxRatedHz;
public float refresh_Time_ms;
[ObservableProperty]
public float proporcional_Speed;
[ObservableProperty]
public float max_Speed_for_Ramp;
[ObservableProperty]
bool vFD_Trip_NC;
[ObservableProperty]
public float tiempoRampa;
@ -51,15 +67,32 @@ namespace CtrEditor.ObjetosSim
}
[ObservableProperty]
public bool encendido;
bool encendido;
[ObservableProperty]
public int pLC_NumeroMotor;
int pLC_NumeroMotor;
partial void OnPLC_NumeroMotorChanged(int value)
{
if (PLC_DB_Motor == 0)
{
PLC_DB_Motor = PLC_NumeroMotor - 30 + 300;
}
}
[ObservableProperty]
int pLC_DB_Motor;
[ObservableProperty]
public float ratio;
[ObservableProperty]
public float velocidad;
[ObservableProperty]
public bool sentido_contrario;
partial void OnVelocidadChanged(float value)
{
if (value > 0)
@ -72,27 +105,43 @@ namespace CtrEditor.ObjetosSim
{
Tamano = 0.30f;
PLC_NumeroMotor = 31;
MaxRatedHz = 100;
Proporcional_Speed = 100;
Max_Speed_for_Ramp = 100;
TiempoRampa = 3;
ImageSource_oculta = ImageFromPath("/imagenes/motor2.png");
}
Refresh_Time_ms = 500;
}
public override void UpdateGeometryStart()
{
// Se llama antes de la simulacion
}
public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds) {
motState.UpdatePLC(plc,PLC_NumeroMotor,Encendido, elapsedMilliseconds);
Velocidad = motState.STATUS_VFD_ACT_Speed_Hz / 10;
[JsonIgnore]
private float elapsedTimeAccumulator = 0;
public override void UpdatePLC(PLCViewModel plc, int TotalMilliseconds)
{
elapsedTimeAccumulator += TotalMilliseconds;
float randomFactor = (float)(new Random().NextDouble() * 0.1); // 10% random factor
float adjustedRefreshTime = Refresh_Time_ms * (1 + randomFactor);
if (elapsedTimeAccumulator >= adjustedRefreshTime)
{
motState.UpdatePLC(plc, this, TotalMilliseconds);
elapsedTimeAccumulator = 0;
}
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 TotalMilliseconds)
{
// Calculamos la velocidad
motState.UpdateSpeed(MaxRatedHz,TiempoRampa, elapsedMilliseconds);
motState.UpdateSpeed(Max_Speed_for_Ramp, TiempoRampa, TotalMilliseconds);
}
@ -100,7 +149,8 @@ namespace CtrEditor.ObjetosSim
{
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
ActualizarLeftTop();
base.ucLoaded();
OnVelocidadChanged(Velocidad);
}
}
@ -108,6 +158,7 @@ namespace CtrEditor.ObjetosSim
public partial class ucVMmotorSim : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public int zIndex_fromFrames { get; set; }
public ucVMmotorSim()
{
@ -123,21 +174,12 @@ namespace CtrEditor.ObjetosSim
{
Datos?.ucUnLoaded();
}
public void Resize(float width, float 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()
public ZIndexEnum ZIndex_Base()
{
return 10;
return ZIndexEnum.Estaticos;
}
}
public class VMSimMotor
@ -153,26 +195,33 @@ namespace CtrEditor.ObjetosSim
public bool OUT_Reversal;
public float OUT_OUT_VFD_REQ_Speed_Hz;
public void UpdatePLC(PLCModel plc, int NumeroMotor, bool Encendido, int elapsedMilliseconds)
public void UpdatePLC(PLCViewModel plc, osVMmotorSim Data, int TotalMilliseconds)
{
var index = 0;
switch (NumeroMotor)
{
case < 100:
index = (int)NumeroMotor - 30 + 300;
break;
}
var DB_Motor = Data.PLC_DB_Motor;
OUT_Run = plc.LeerTagBool($"\"DB MotorSimulate\".Motors[{index}].OUT.Run");
OUT_Reversal = plc.LeerTagBool($"\"DB MotorSimulate\".Motors[{index}].OUT.\"Reversal Direction\"");
OUT_OUT_VFD_REQ_Speed_Hz = (float)plc.LeerTagInt16($"\"DB MotorSimulate\".Motors[{index}].OUT.OUT_VFD_REQ_Speed_Hz");
if (DB_Motor == 0)
return;
// Add timestamp to trace when the read occurs
var timestamp = DateTime.Now;
// Read ControlWord and track the raw response
var rawResponse = plc.LeerTagDInt($"\"DB MotorSimulate\".Motors[{DB_Motor}].ControlWord");
int controlWord = rawResponse ?? 0;
var control = VMMotorBitPacker.UnpackControlWord(controlWord);
// Update local state from ControlWord
OUT_Run = control.run;
OUT_Stop = control.stop;
OUT_Reversal = control.reversal;
OUT_OUT_VFD_REQ_Speed_Hz = control.reqSpeedHz;
if (Encendido)
// Update motor state based on enable status
if (Data.Encendido)
{
_STATUS_VFD_Ready = true;
Motor_Running = true;
Motor_Running = OUT_Run && !OUT_Stop;
STATUS_VFD_Trip = false;
STATUS_VFD_Warning = false;
STATUS_VFD_Coasting = false;
@ -186,19 +235,27 @@ namespace CtrEditor.ObjetosSim
STATUS_VFD_Coasting = false;
}
plc.EscribirTagBool($"\"DB MotorSimulate\".Motors[{index}].STATUS_VFD_Ready", _STATUS_VFD_Ready);
plc.EscribirTagBool($"\"DB MotorSimulate\".Motors[{index}].Motor_Running", Motor_Running);
plc.EscribirTagBool($"\"DB MotorSimulate\".Motors[{index}].STATUS_VFD_Trip", STATUS_VFD_Trip);
plc.EscribirTagBool($"\"DB MotorSimulate\".Motors[{index}].STATUS_VFD_Warning", STATUS_VFD_Warning);
plc.EscribirTagBool($"\"DB MotorSimulate\".Motors[{index}].STATUS_VFD_Coasting", STATUS_VFD_Coasting);
if (Data.VFD_Trip_NC)
STATUS_VFD_Trip = !STATUS_VFD_Trip;
plc.EscribirTagInt16($"\"DB MotorSimulate\".Motors[{index}].STATUS_VFD_ACT_Speed_Hz", (int)STATUS_VFD_ACT_Speed_Hz);
// Pack all status bits and speed into StatusWord
int statusWord = VMMotorBitPacker.PackStatusWord(
_STATUS_VFD_Ready,
Motor_Running,
STATUS_VFD_Trip,
STATUS_VFD_Warning,
STATUS_VFD_Coasting,
(short)STATUS_VFD_ACT_Speed_Hz
);
// Write StatusWord in one operation
plc.EscribirTagDInt($"\"DB MotorSimulate\".Motors[{DB_Motor}].StatusWord", statusWord);
}
private float CalcSpeedRamp(float MaxRatedHz, float TiempoRampa, float actual, float expected, int elapsedMilliseconds)
private float CalcSpeedRamp(float max_Speed_for_Ramp, float TiempoRampa, float actual, float expected, int TotalMilliseconds)
{
float hzIncrementsRamp = (MaxRatedHz * 10) / (TiempoRampa * (1000.0f / elapsedMilliseconds));
float hzIncrementsRamp = (max_Speed_for_Ramp * 10) / (TiempoRampa * (1000.0f / TotalMilliseconds));
float delta = expected - actual;
// Conrtolar si la diferencia no es mayor de lo que falta
if (Math.Abs(hzIncrementsRamp) > Math.Abs(delta))
@ -209,12 +266,14 @@ 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 TotalMilliseconds)
{
// 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, TotalMilliseconds);
}
}
}

View File

@ -0,0 +1,23 @@
<UserControl x:Class="CtrEditor.ObjetosSim.Extraccion_Datos.ucBuscarCoincidencias"
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.Extraccion_Datos" mc:Ignorable="d"
Visibility="{Binding Show_On_This_Page, Converter={StaticResource BoolToVisibilityConverter}}">
<UserControl.DataContext>
<vm:osBuscarCoincidencias />
</UserControl.DataContext>
<Canvas>
<Rectangle x:Name="Area" Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}"
Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}}"
Opacity="{Binding Opacity_oculto}" Fill="Yellow" Stroke="Black">
<Rectangle.RenderTransform>
<RotateTransform Angle="{Binding Angulo}" />
</Rectangle.RenderTransform>
</Rectangle>
</Canvas>
</UserControl>

View File

@ -0,0 +1,733 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using CommunityToolkit.Mvvm.ComponentModel;
using System.IO;
using System.Windows.Media.Imaging;
using Emgu.CV.CvEnum;
using Emgu.CV;
using System.Drawing;
using Image = System.Windows.Controls.Image;
using Rectangle = System.Windows.Shapes.Rectangle;
using Size = System.Drawing.Size;
using Ookii.Dialogs.Wpf;
using Rect = System.Windows.Rect;
using System.ComponentModel;
using Newtonsoft.Json;
using Xceed.Wpf.Toolkit.PropertyGrid.Attributes;
using System.ComponentModel;
using ClosedXML.Excel;
using Colors = System.Windows.Media.Colors;
using CtrEditor.FuncionesBase;
using System.Drawing;
using System.Windows.Shapes;
using System.Drawing.Imaging;
using Emgu.CV.Structure;
namespace CtrEditor.ObjetosSim.Extraccion_Datos
{
/// <summary>
/// Represents a template search control that identifies similar patterns in images and creates tag extraction clones.
/// This class is designed to work with OCR extraction by finding visual patterns and creating copies of extraction tags
/// at each matching location.
/// </summary>
/// <remarks>
/// Key functionalities:
/// - Template matching using OpenCV
/// - Automatic tag cloning at found locations
/// - OCR text extraction from matched regions
/// - Export capabilities to Excel
///
/// Workflow:
/// 1. User creates a search template by positioning and sizing the control over a pattern
/// 2. Links extraction tags to this template using Id_Search_Templates
/// 3. Activates search_templates to find similar patterns
/// 4. The system automatically:
/// - Searches for visual matches in the image
/// - Creates clones of linked extraction tags at each match
/// - Assigns incremental copy_Number to organize rows in exports
/// - Performs OCR on each cloned tag location
///
/// Properties:
/// - search_templates: Triggers the pattern search process
/// - threshold: Minimum similarity threshold for pattern matching
/// - coincidencias: Number of matches found (readonly)
/// - show_debug_ocr: Shows debug windows during OCR process
/// - export_ocr: Triggers OCR text export for all matches
///
/// Usage example:
/// 1. Position the search template over a repeating pattern
/// 2. Create extraction tags and link them to this template
/// 3. Set threshold value (default usually works well)
/// 4. Activate search_templates to find matches and create clones
/// </remarks>
public partial class osBuscarCoincidencias : osBase, IosBase
{
[JsonIgnore]
public float offsetY;
[JsonIgnore]
public float offsetX;
[ObservableProperty]
List<Rect> search_rectangles;
public static string NombreClase()
{
return "Search Templates";
}
private string nombre = NombreClase();
public override string Nombre
{
get => nombre;
set => SetProperty(ref nombre, value);
}
[ObservableProperty]
[property: Category("Tag Extraction:")]
bool search_templates;
partial void OnSearch_templatesChanged(bool oldValue, bool newValue)
{
if (Search_templates)
BuscarCoincidencias();
Search_templates = false;
}
[ObservableProperty]
[property: Category("Tag Extraction:")]
bool tomarClip;
// En lugar de almacenar Mat directamente, guardaremos una representación serializable
[ObservableProperty]
[property: Category("Tag Extraction:")]
byte[] capturedRegionData;
[ObservableProperty]
[property: Category("Tag Extraction:")]
[property: ReadOnly(true)]
bool regionCapturada;
// Para uso interno (no serializado)
[JsonIgnore]
private Mat _capturedRegion;
// Propiedades para almacenar las dimensiones de la captura
[ObservableProperty]
[property: Category("Tag Extraction:")]
int capturedWidth;
[ObservableProperty]
[property: Category("Tag Extraction:")]
int capturedHeight;
[ObservableProperty]
[property: Category("Tag Extraction:")]
bool export_ocr;
[ObservableProperty]
[property: Category("Tag Extraction:")]
string text_export_ocr;
// Esta propiedad manejará la conversión entre Mat y datos serializables
[JsonIgnore]
public Mat CapturedRegion
{
get
{
if (_capturedRegion == null && CapturedRegionData != null && CapturedRegionData.Length > 0)
{
// Recrear Mat desde los datos almacenados
_capturedRegion = BytesToMat(CapturedRegionData, CapturedWidth, CapturedHeight);
}
return _capturedRegion;
}
set
{
if (value != null)
{
// Convertir Mat a bytes para serialización
CapturedRegionData = MatToBytes(value);
CapturedWidth = value.Width;
CapturedHeight = value.Height;
_capturedRegion = value;
}
else
{
CapturedRegionData = null;
CapturedWidth = 0;
CapturedHeight = 0;
_capturedRegion = null;
}
}
}
partial void OnTomarClipChanged(bool oldValue, bool newValue)
{
if (tomarClip)
{
CapturarRegionActual();
TomarClip = false; // Resetear el flag después de la captura
}
}
// Método para capturar la región actual
private void CapturarRegionActual()
{
Application.Current.Dispatcher.Invoke(() =>
{
if (_mainViewModel?.MainCanvas.Children[0] is Image imagenDeFondo)
{
if (imagenDeFondo.Source is BitmapSource bitmapSource)
{
// Obtener los DPI de la imagen original
float originalDpiX = (float)bitmapSource.DpiX;
float originalDpiY = (float)bitmapSource.DpiY;
// Estándar DPI en el que el Canvas renderiza la imagen
float canvasDpiX = 96;
float canvasDpiY = 96;
// Calcular el ratio de escala entre el Canvas y la imagen original
float scaleFactorX = originalDpiX / canvasDpiX;
float scaleFactorY = originalDpiY / canvasDpiY;
// Ajustar las coordenadas de recorte en función del ratio de escala
int x = (int)MeterToPixels(Left * scaleFactorX);
int y = (int)MeterToPixels(Top * scaleFactorY);
int width = (int)MeterToPixels(Ancho * scaleFactorX);
int height = (int)MeterToPixels(Alto * scaleFactorY);
// Validar y ajustar el tamaño del recorte para que se mantenga dentro de los límites de la imagen
if (x < 0) x = 0;
if (y < 0) y = 0;
if (x + width > bitmapSource.PixelWidth) width = bitmapSource.PixelWidth - x;
if (y + height > bitmapSource.PixelHeight) height = bitmapSource.PixelHeight - y;
// Recortar el área deseada utilizando las coordenadas ajustadas
CroppedBitmap croppedBitmap = new CroppedBitmap(bitmapSource, new Int32Rect(x, y, width, height));
// Capturar la región y almacenarla
if (_capturedRegion != null)
{
_capturedRegion.Dispose(); // Liberar recursos previos
}
// Usar la propiedad que maneja la serialización
CapturedRegion = BitmapSourceToMat(croppedBitmap);
// Actualizar el estado
RegionCapturada = true;
MessageBox.Show("Región capturada correctamente.", "Información", MessageBoxButton.OK, MessageBoxImage.Information);
}
}
});
}
// Métodos para convertir entre Mat y bytes
private byte[] MatToBytes(Mat mat)
{
if (mat == null)
return null;
// Asegurar que tenemos un formato consistente para serialización
Mat bgr = new Mat();
if (mat.NumberOfChannels != 3)
{
CvInvoke.CvtColor(mat, bgr, mat.NumberOfChannels == 1 ?
ColorConversion.Gray2Bgr : ColorConversion.Bgra2Bgr);
}
else
{
bgr = mat.Clone();
}
// Convertir a un formato que pueda ser serializado
using (MemoryStream ms = new MemoryStream())
{
// Convertir Mat a Bitmap
Bitmap bitmap = bgr.ToBitmap();
// Guardar como PNG (sin pérdida de calidad)
bitmap.Save(ms, ImageFormat.Png);
// Liberar recursos
bitmap.Dispose();
if (bgr != mat)
bgr.Dispose();
return ms.ToArray();
}
}
private Mat BytesToMat(byte[] bytes, int width, int height)
{
if (bytes == null || bytes.Length == 0)
return null;
try
{
using (MemoryStream ms = new MemoryStream(bytes))
{
// Cargar imagen desde bytes
Bitmap bitmap = (Bitmap)System.Drawing.Image.FromStream(ms);
// Convertir Bitmap a Mat
Image<Bgr, byte> img = bitmap.ToImage<Bgr, byte>();
// Liberar recursos
bitmap.Dispose();
// Si las dimensiones no coinciden, redimensionar
if (img.Width != width || img.Height != height)
{
CvInvoke.Resize(img.Mat, img.Mat, new Size(width, height));
}
return img.Mat;
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"Error al reconstruir Mat: {ex.Message}");
return null;
}
}
// Sobrescribir los métodos de cambio de tamaño para limpiar la región capturada
public override void AnchoChanged(float newValue)
{
base.AnchoChanged(newValue);
//LimpiarRegionCapturada();
}
public override void AltoChanged(float newValue)
{
base.AnchoChanged(newValue);
// LimpiarRegionCapturada();
}
private void LimpiarRegionCapturada()
{
if (_capturedRegion != null)
{
_capturedRegion.Dispose();
_capturedRegion = null;
}
// Usar la propiedad para manejar la serialización
CapturedRegion = null;
RegionCapturada = false;
}
partial void OnExport_ocrChanged(bool value)
{
if (Export_ocr)
{
Text_export_ocr = "";
if (!string.IsNullOrEmpty(Nombre) && _mainViewModel != null)
{
foreach (var objetoSimulable in _mainViewModel.ObjetosSimulables)
{
if (objetoSimulable != this && objetoSimulable.Group_Panel == Nombre)
{
if (objetoSimulable is osExtraccionTag osExtraccionTag)
{
osExtraccionTag.CaptureImageAreaAndDoOCR();
Text_export_ocr += osExtraccionTag.Tag_extract;
}
}
}
}
}
Export_ocr = false;
}
public override void TopChanging(float oldValue, float newValue)
{
offsetY = newValue - oldValue;
}
public override void LeftChanging(float oldValue, float newValue)
{
offsetX = newValue - oldValue;
}
[ObservableProperty]
[property: Category("Tag Extraction:")]
string tag_extract;
[ObservableProperty]
[property: Category("Tag Extraction:")]
string clase;
[ObservableProperty]
[property: Category("Tag Extraction:")]
string tag_name;
[ObservableProperty]
float opacity_oculto;
[ObservableProperty]
[property: Category("Tag Extraction:")]
bool show_debug_ocr;
[ObservableProperty]
[property: Category("Tag Extraction:")]
float threshold;
[ObservableProperty]
[property: Category("Tag Extraction:")]
[property: ReadOnly(true)]
int coincidencias;
public osBuscarCoincidencias()
{
Ancho = 1;
Alto = 1;
Angulo = 0;
Opacity_oculto = 0.1f;
Threshold = 0.6f;
}
private void ShowPreviewWindow(Stream imageStream)
{
// Create a new window for preview
Window previewWindow = new Window
{
Title = "Preview Captured Image",
Width = 500,
Height = 500,
Content = new Image
{
Source = BitmapFrame.Create(imageStream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad),
Stretch = Stretch.Uniform
}
};
previewWindow.ShowDialog();
}
public async void BuscarCoincidencias()
{
var progressDialog = new ProgressDialog
{
WindowTitle = "Procesando",
Text = "Buscando coincidencias...",
ShowTimeRemaining = true,
ShowCancelButton = false
};
progressDialog.DoWork += (sender, e) => BuscarCoincidenciasAsync(progressDialog);
progressDialog.RunWorkerCompleted += (sender, e) =>
{
if (e.Error != null)
{
MessageBox.Show(e.Error.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
};
progressDialog.Show();
}
// Modificar el método BuscarCoincidenciasAsync para usar la región capturada si está disponible
private void BuscarCoincidenciasAsync(ProgressDialog progressDialog)
{
// Reset the Canvas children
Application.Current.Dispatcher.Invoke(() =>
{
var clearShapes = _mainViewModel.MainCanvas.Children.OfType<System.Windows.Shapes.Shape>().Where(s => s.Tag as string == "BuscarCoincidencias").ToList();
foreach (var shape in clearShapes)
{
_mainViewModel.MainCanvas.Children.Remove(shape);
}
if (_mainViewModel?.MainCanvas.Children[0] is Image imagenDeFondo)
{
// Asegurarse de que la imagen origen está disponible
if (imagenDeFondo.Source is BitmapSource bitmapSource)
{
progressDialog.ReportProgress(10);
Mat templateMat;
int width, height;
float scaleFactorX, scaleFactorY;
bool deleteTemplateMat = false;
// Usar la región capturada si existe, de lo contrario capturar la región actual
if (CapturedRegion != null && RegionCapturada)
{
// Usar la región almacenada (ya deserializada)
templateMat = CapturedRegion.Clone();
deleteTemplateMat = true;
width = templateMat.Width;
height = templateMat.Height;
// Para mantener la relación con la imagen original
float originalDpiX = (float)bitmapSource.DpiX;
float originalDpiY = (float)bitmapSource.DpiY;
float canvasDpiX = 96;
float canvasDpiY = 96;
scaleFactorX = originalDpiX / canvasDpiX;
scaleFactorY = originalDpiY / canvasDpiY;
}
else
{
// Obtener los DPI de la imagen original
float originalDpiX = (float)bitmapSource.DpiX;
float originalDpiY = (float)bitmapSource.DpiY;
// Estándar DPI en el que el Canvas renderiza la imagen
float canvasDpiX = 96;
float canvasDpiY = 96;
// Calcular el ratio de escala entre el Canvas y la imagen original
scaleFactorX = originalDpiX / canvasDpiX;
scaleFactorY = originalDpiY / canvasDpiY;
// Ajustar las coordenadas de recorte en función del ratio de escala
int x = (int)MeterToPixels(Left * scaleFactorX);
int y = (int)MeterToPixels(Top * scaleFactorY);
width = (int)MeterToPixels(Ancho * scaleFactorX);
height = (int)MeterToPixels(Alto * scaleFactorY);
// Validar y ajustar el tamaño del recorte para que se mantenga dentro de los límites de la imagen
if (x < 0) x = 0;
if (y < 0) y = 0;
if (x + width > bitmapSource.PixelWidth) width = bitmapSource.PixelWidth - x;
if (y + height > bitmapSource.PixelHeight) height = bitmapSource.PixelHeight - y;
// Recortar el área deseada utilizando las coordenadas ajustadas
CroppedBitmap croppedBitmap = new CroppedBitmap(bitmapSource, new Int32Rect(x, y, width, height));
// Convertir CroppedBitmap a Mat directamente
templateMat = BitmapSourceToMat(croppedBitmap);
deleteTemplateMat = true;
}
int scale = 4;
// Convertir la plantilla a escala de grises y redimensionarla
Mat templateGray = new Mat();
CvInvoke.CvtColor(templateMat, templateGray, ColorConversion.Bgr2Gray);
Mat templateGrayResized = new Mat();
CvInvoke.Resize(templateGray, templateGrayResized, new Size(templateGray.Width / scale, templateGray.Height / scale), 0, 0, Inter.Linear);
progressDialog.ReportProgress(20);
// El resto del código permanece igual...
// Cargar la imagen principal completa en un Mat
Mat mainImageMat = BitmapSourceToMat(bitmapSource);
// Convertir la imagen principal a escala de grises y redimensionarla
Mat mainImageGray = new Mat();
CvInvoke.CvtColor(mainImageMat, mainImageGray, ColorConversion.Bgr2Gray);
Mat mainImageGrayResized = new Mat();
CvInvoke.Resize(mainImageGray, mainImageGrayResized, new Size(mainImageGray.Width / scale, mainImageGray.Height / scale), 0, 0, Inter.Linear);
progressDialog.ReportProgress(50);
// Realizar la coincidencia de plantillas
Mat result = new Mat();
CvInvoke.MatchTemplate(mainImageGray, templateGray, result, TemplateMatchingType.CcoeffNormed);
// Establecer un umbral de coincidencia
if (Threshold < 0.4)
Threshold = 0.4f;
double threshold = Threshold;
int ConteoPositivos = 0;
// Lista para mantener áreas ya aceptadas
if (search_rectangles != null)
search_rectangles.Clear();
else
search_rectangles = new List<Rect>();
// Obtener los puntos que superan el umbral
float[] resultArray = result.GetData(false) as float[];
if (resultArray != null)
{
for (int i = 0; i < resultArray.Length; i++)
{
if (resultArray[i] >= threshold)
{
int row = i / result.Cols;
int col = i % result.Cols;
Rect newRect = new Rect();
newRect.X = col / scaleFactorX;
newRect.Y = row / scaleFactorY;
newRect.Width = width / scaleFactorX;
newRect.Height = height / scaleFactorY;
// Crear un rectángulo para la coincidencia actual
Rectangle matchRect = new Rectangle
{
Stroke = new SolidColorBrush(Colors.Red),
StrokeThickness = 2,
Width = newRect.Width,
Height = newRect.Height,
Tag = "BuscarCoincidencias"
};
Canvas.SetLeft(matchRect, newRect.X);
Canvas.SetTop(matchRect, newRect.Y);
// Verificar si la coincidencia actual está dentro de algún rectángulo aceptado
bool isOverlap = search_rectangles.Any(r =>
r.IntersectsWith(newRect)
);
// Si no hay superposición, agregar el rectángulo al Canvas y a la lista de aceptados
if (!isOverlap)
{
Canvas.SetZIndex(matchRect, 40);
_mainViewModel.MainCanvas.Children.Add(matchRect);
search_rectangles.Add(newRect);
ConteoPositivos++;
Coincidencias = ConteoPositivos;
progressDialog.ReportProgress(90);
if (ConteoPositivos > 20)
{
// Liberar recursos antes de salir
if (deleteTemplateMat) templateMat.Dispose();
templateGray.Dispose();
templateGrayResized.Dispose();
mainImageMat.Dispose();
mainImageGray.Dispose();
mainImageGrayResized.Dispose();
result.Dispose();
return;
}
}
}
}
PopularTagExtraction();
}
// Limpiar recursos
if (deleteTemplateMat) templateMat.Dispose();
templateGray.Dispose();
templateGrayResized.Dispose();
mainImageMat.Dispose();
mainImageGray.Dispose();
mainImageGrayResized.Dispose();
result.Dispose();
}
}
});
}
public void PopularTagExtraction()
{
var objetosSimulablesCopy = new List<osBase>(_mainViewModel.ObjetosSimulables);
foreach (var obj in objetosSimulablesCopy)
if (obj is osExtraccionTag objExtraccionTag)
if (objExtraccionTag.Id_Search_Templates == this.Nombre && objExtraccionTag.Cloned)
_mainViewModel.RemoverObjetoSimulable(objExtraccionTag);
var objetosSimulables2Copy = new List<osBase>(_mainViewModel.ObjetosSimulables);
// Saltar el primer rectángulo en el foreach
int Row = 0;
foreach (var rectangle in search_rectangles) //.Skip(1))
{
float offsetX = PixelsToMeters((float)rectangle.X) - Left;
float offsetY = PixelsToMeters((float)rectangle.Y) - Top;
osExtraccionTag newObj = null;
foreach (var eTag in objetosSimulables2Copy)
{
if (eTag is osExtraccionTag objExtraccionTag)
{
if (objExtraccionTag.Id_Search_Templates == this.Nombre)
{
newObj = (osExtraccionTag)_mainViewModel.DuplicarObjeto(objExtraccionTag, offsetX, offsetY);
if (newObj != null)
{
newObj.Cloned = true;
newObj.Cloned_from = objExtraccionTag.Id;
newObj.Copy_Number = Row;
newObj.Enable_On_All_Pages = false;
if (newObj.Extraer)
objExtraccionTag.CaptureImageAreaAndDoOCR();
}
}
}
}
Row++;
}
}
public static int FindFirstEmptyRow(IXLWorksheet worksheet)
{
var lastRowUsed = worksheet.LastRowUsed();
return lastRowUsed == null ? 1 : lastRowUsed.RowNumber() + 1;
}
// Método para convertir BitmapSource a Mat
private Mat BitmapSourceToMat(BitmapSource bitmapSource)
{
if (bitmapSource == null)
throw new ArgumentNullException(nameof(bitmapSource));
// Convierte BitmapSource a Bitmap
Bitmap bitmap;
using (MemoryStream outStream = new MemoryStream())
{
BitmapEncoder enc = new BmpBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create(bitmapSource));
enc.Save(outStream);
bitmap = new Bitmap(outStream);
}
// Convierte directamente a Mat usando Image<Bgr, byte>
Image<Bgr, byte> image = bitmap.ToImage<Bgr, byte>();
return image.Mat;
}
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 ucBuscarCoincidencias : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public int zIndex_fromFrames { get; set; }
public ucBuscarCoincidencias()
{
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_Base()
{
return ZIndexEnum.Estaticos;
}
}
}

View File

@ -0,0 +1,35 @@
<UserControl x:Class="CtrEditor.ObjetosSim.Extraccion_Datos.ucExtraccionTag"
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.Extraccion_Datos"
mc:Ignorable="d"
Visibility="{Binding Show_On_This_Page, Converter={StaticResource BoolToVisibilityConverter}}">
<UserControl.DataContext>
<vm:osExtraccionTag/>
</UserControl.DataContext>
<Canvas>
<Rectangle x:Name="Area" Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}"
Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}}"
Opacity="{Binding Opacity_oculto}" Fill="Green" Stroke="Black">
</Rectangle>
<Viewbox Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}}"
Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}">
<Label Content="{Binding Angulo}" VerticalAlignment="Top" HorizontalAlignment="Center"
RenderTransformOrigin="0.5,0.5" Opacity="0.1">
<Label.RenderTransform>
<TransformGroup>
<ScaleTransform />
<SkewTransform />
<RotateTransform Angle="{Binding Angulo}" />
<TranslateTransform />
</TransformGroup>
</Label.RenderTransform>
</Label>
</Viewbox>
</Canvas>
</UserControl>

View File

@ -0,0 +1,274 @@
using ClosedXML.Excel;
using CommunityToolkit.Mvvm.ComponentModel;
using CtrEditor.FuncionesBase;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using Xceed.Wpf.Toolkit.PropertyGrid.Attributes;
namespace CtrEditor.ObjetosSim.Extraccion_Datos
{
/// <summary>
/// Interaction logic for ucExtraccionTag.xaml
/// </summary>
///
public partial class osExtraccionTag : osBase, IosBase
{
public static string NombreClase()
{
return "Extraccion Tags";
}
private string nombre = NombreClase();
public override string Nombre
{
get => nombre;
set
{
if (Collumn_name == null || Collumn_name.Length == 0)
Collumn_name = value;
SetProperty(ref nombre, value);
}
}
[ObservableProperty]
[property: Category("Tag Extraction:")]
bool extraer;
[ObservableProperty]
[property: Category("Tag Extraction:")]
bool eliminar_enters;
[ObservableProperty]
[property: Category("Tag Extraction:")]
[property: ItemsSource(typeof(IdiomasItemsSource<Idiomas>))]
string idioma_Extraccion;
[ObservableProperty]
[property: Category("Tag Extraction:")]
[property: ItemsSource(typeof(TagPatternItemsSource<TagPattern>))]
string pattern_Type;
public override void TopChanged(float value)
{
base.TopChanged(value);
if (Extraer) ResetTimer();
}
public override void LeftChanged(float value)
{
base.LeftChanged(value);
if (Extraer) ResetTimer();
}
partial void OnExtraerChanged(bool value)
{
if (Extraer)
ResetTimer();
}
public override void OnResize(float Delta_Width, float Delta_Height)
{
if (Extraer)
ResetTimer();
}
public override void OnMove(float LeftPixels, float TopPixels)
{
if (Extraer)
ResetTimer();
}
public override void OnRotate(float Angle)
{
if (Extraer)
ResetTimer();
}
public override void OnTimerAfterMovement()
{
Angulo = (float)Math.Round(Angulo / 90) * 90;
if (Extraer)
CaptureImageAreaAndDoOCR();
Extraer = false;
}
private osBuscarCoincidencias Search_Templates;
[ObservableProperty]
[property: Description("Link to Search Templates")]
[property: Category("Tag Extraction:")]
[property: ItemsSource(typeof(osBaseItemsSource<osBuscarCoincidencias>))]
string id_Search_Templates;
partial void OnId_Search_TemplatesChanged(string value)
{
if (Search_Templates != null)
Search_Templates.PropertyChanged -= OnMotorPropertyChanged;
if (_mainViewModel != null && value != null && value.Length > 0)
{
Search_Templates = (osBuscarCoincidencias)_mainViewModel.ObjetosSimulables.FirstOrDefault(s => (s is osBuscarCoincidencias && s.Nombre == value), null);
if (Search_Templates != null)
Search_Templates.PropertyChanged += OnMotorPropertyChanged;
}
}
private void OnMotorPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(osBuscarCoincidencias.Nombre))
{
Id_Search_Templates = ((osBuscarCoincidencias)sender).Nombre;
}
}
[ObservableProperty]
[property: Category("Tag Extraction:")]
string tag_extract;
[ObservableProperty]
[property: Category("Export:")]
string clase;
[ObservableProperty]
[property: Category("Export:")]
string collumn_name;
[ObservableProperty]
[property: Description("Excel collumn.")]
[property: Category("Export:")]
int collumn_number;
[ObservableProperty]
[property: Category("Tag Extraction:")]
[property: ReadOnly(true)]
int copy_Number;
[ObservableProperty]
[property: Category("Tag Extraction:")]
bool show_Debug_Window;
[ObservableProperty]
float opacity_oculto;
public osExtraccionTag()
{
Ancho = 1;
Alto = 1;
Angulo = 0;
Opacity_oculto = 0.1f;
Idioma_Extraccion = Idiomas.DEFAULT_LANGUAGE;
Pattern_Type = TagPattern.DEFAULT_PATTERN;
Eliminar_enters = true;
}
public void CaptureImageAreaAndDoOCR()
{
string extractedText = CaptureImageAreaAndDoOCR(Left, Top, Ancho, Alto, Angulo, Show_Debug_Window);
// Clean up the extracted text if eliminar_enters is true
if (Eliminar_enters && !string.IsNullOrEmpty(extractedText))
{
// Replace all types of line endings with spaces
extractedText = extractedText.Replace("\r\n", " ")
.Replace("\n", " ")
.Replace("\r", " ");
// Replace multiple consecutive spaces with a single space
extractedText = System.Text.RegularExpressions.Regex.Replace(extractedText, @"\s+", " ");
// Trim spaces at the beginning and end
extractedText = extractedText.Trim();
}
// Apply the selected pattern
extractedText = TagPattern.ApplyPattern(extractedText, Pattern_Type);
Tag_extract = extractedText;
// Set default language to English if not set
if (string.IsNullOrEmpty(Idioma_Extraccion))
{
Idioma_Extraccion = Idiomas.DEFAULT_LANGUAGE;
}
}
public int ExportToExcel(IXLWorksheet worksheet, int row, int colBase)
{
// Agregar Tag
worksheet.Cell(row + 2, Collumn_number + colBase).Value = Tag_extract;
return Collumn_number + colBase;
}
public override void ucLoaded()
{
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
base.ucLoaded();
OnId_Search_TemplatesChanged(Id_Search_Templates); // Actualizar Link
}
}
public partial class ucExtraccionTag : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public int zIndex_fromFrames { get; set; }
public ucExtraccionTag()
{
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 osExtraccionTag 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 float Angle()
{
if (Datos != null)
if (Datos is osExtraccionTag datos)
return datos.Angulo;
return 0f;
}
public void Rotate(float Angle)
{
if (Datos != null)
if (Datos is osExtraccionTag datos)
datos.Angulo += Angle;
}
public void Highlight(bool State) { }
public ZIndexEnum ZIndex_Base()
{
return ZIndexEnum.ExtraccionTag;
}
}
}

View File

@ -4,20 +4,23 @@
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:ei="http://schemas.microsoft.com/xaml/behaviors"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:convert="clr-namespace:CtrEditor.Convertidores"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim"
mc:Ignorable="d">
<UserControl.Resources>
<convert:MeterToPixelConverter x:Key="MeterToPixelConverter"/>
</UserControl.Resources>
<UserControl.DataContext>
<vm:osBoton Color="#FFADE6C0" ColorButton_oculto="#FFC72323"/>
<vm:osBoton Color="#FFADE6C0" ColorButton="#FFC72323" Color_Pressed="#FF9A9A9A" />
</UserControl.DataContext>
<Grid>
<Canvas RenderTransformOrigin="0.5,0.5">
<Canvas.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform Angle="{Binding Angulo}"/>
<TranslateTransform/>
</TransformGroup>
</Canvas.RenderTransform>
<Border x:Name="BackgroundRectangle"
BorderBrush="Black"
BorderThickness="2"
@ -26,16 +29,24 @@
Height="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}, ConverterParameter=1.5}"
VerticalAlignment="Top"
HorizontalAlignment="Left"
Background="Gray"/>
<Ellipse Fill="{Binding ColorButton_oculto}"
Background="{Binding Color_Pressed, Converter={StaticResource ColorToBrushConverter}}"/>
<Ellipse Fill="{Binding ColorButton, Converter={StaticResource ColorToBrushConverter}}"
Stroke="Black"
StrokeThickness="2"
Width="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}}"
Height="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}}"
Canvas.Top="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}, ConverterParameter=0.5}"
HorizontalAlignment="Left"
VerticalAlignment="Bottom"
MouseLeftButtonDown="Ellipse_MouseLeftButtonDown"
MouseLeftButtonUp="Ellipse_MouseLeftButtonUp">
</Ellipse>
</Grid>
<Viewbox Height="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}, ConverterParameter=0.5}"
Width="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}}">
<Label Content="{Binding Button_Name}"
VerticalAlignment="Top" HorizontalAlignment="Center"
Foreground="{Binding Color_Titulo, Converter={StaticResource ColorToBrushConverter}}"/>
</Viewbox>
</Canvas>
</UserControl>

View File

@ -1,10 +1,11 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CtrEditor.Convertidores;
using CtrEditor.Siemens;
using LibS7Adv;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using CtrEditor.FuncionesBase;
namespace CtrEditor.ObjetosSim
{
@ -26,24 +27,49 @@ namespace CtrEditor.ObjetosSim
set => SetProperty(ref nombre, value);
}
[ObservableProperty]
string button_Name;
[ObservableProperty]
public bool tipo_NC;
[ObservableProperty]
private Brush color;
[property: Hidden]
Color color_Pressed;
[ObservableProperty]
private Brush colorButton_oculto;
Color color_Titulo;
[ObservableProperty]
Color color;
partial void OnColorChanged(Color value)
{
OnEstadoChanged(Estado);
}
[ObservableProperty]
[property: Hidden]
private Color colorButton;
[ObservableProperty]
public float tamano;
public override void OnResize(float Delta_Width, float Delta_Height)
{
Tamano += Delta_Width + Delta_Height;
}
[ObservableProperty]
public bool estado;
partial void OnEstadoChanged(bool value)
{
if (value)
ColorButton_oculto = Brushes.LightGreen;
Color_Pressed = Colors.LightGreen;
else
ColorButton_oculto = Color;
if (!tipo_NC)
Color_Pressed = Colors.Gray;
if (!Tipo_NC)
EscribirBitTag(Tag, value);
else
EscribirBitTag(Tag, !value);
@ -52,6 +78,9 @@ namespace CtrEditor.ObjetosSim
[ObservableProperty]
public string tag;
[ObservableProperty]
public string tag_Luz;
public void ButtonDownCommand()
{
Estado = true;
@ -67,7 +96,10 @@ namespace CtrEditor.ObjetosSim
Estado = false;
Tag = "M50.0";
// Set initial color
Color = Brushes.LightBlue;
Color = Colors.LightBlue;
color_Titulo = Colors.Black;
button_Name = "TAG";
Color_Pressed = Colors.Gray;
}
public override void UpdateGeometryStart()
@ -75,9 +107,18 @@ namespace CtrEditor.ObjetosSim
// Se llama antes de la simulacion
}
public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds)
public override void UpdatePLC(PLCViewModel plc, int elapsedMilliseconds)
{
if (LeerBitTag(Tag_Luz))
ColorButton = ObtenerColorMasClaroYSaturado(Color, 0.3, 0.5);
else ColorButton = Color;
}
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)
@ -88,7 +129,8 @@ namespace CtrEditor.ObjetosSim
{
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
ActualizarLeftTop();
base.ucLoaded();
ColorButton = Color;
}
public override void ucUnLoaded()
{
@ -101,6 +143,7 @@ namespace CtrEditor.ObjetosSim
public partial class ucBoton : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public int zIndex_fromFrames { get; set; }
public ucBoton()
{
@ -140,21 +183,11 @@ namespace CtrEditor.ObjetosSim
e.Handled = true; // Evita que el evento se propague
}
}
public void Resize(float width, float 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()
public ZIndexEnum ZIndex_Base()
{
return 10;
return ZIndexEnum.Estaticos;
}
}
}

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,161 @@
// 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 int zIndex_fromFrames { 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_Base()
{
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,153 @@
// 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 int zIndex_fromFrames { 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_Base()
{
return ZIndexEnum.Estaticos;
}
}
}

View File

@ -4,13 +4,8 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:CtrEditor.ObjetosSim.UserControls"
mc:Ignorable="d"
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim"
xmlns:convert="clr-namespace:CtrEditor.Convertidores">
<UserControl.Resources>
<convert:MeterToPixelConverter x:Key="MeterToPixelConverter"/>
</UserControl.Resources>
mc:Ignorable="d">
<UserControl.DataContext>
<vm:osGearEncoder Dientes="9"/>

View File

@ -1,11 +1,13 @@
using CtrEditor.Convertidores;
using CtrEditor.Siemens;

using LibS7Adv;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using Newtonsoft.Json;
using CommunityToolkit.Mvvm.ComponentModel;
using System.Diagnostics;
using Xceed.Wpf.Toolkit.PropertyGrid.Attributes;
using System.ComponentModel;
using CtrEditor.FuncionesBase;
namespace CtrEditor.ObjetosSim
{
@ -14,7 +16,7 @@ namespace CtrEditor.ObjetosSim
/// </summary>
public partial class osGearEncoder : osBase, IosBase
{
private osBase _osMotor = null;
private osBase Motor = null;
private Stopwatch Stopwatch = new Stopwatch();
private double stopwatch_last = 0;
@ -41,8 +43,8 @@ namespace CtrEditor.ObjetosSim
{
if (value)
{
var dTime = Stopwatch.ElapsedMilliseconds - stopwatch_last;
stopwatch_last = Stopwatch.ElapsedMilliseconds;
var dTime = Stopwatch.Elapsed.TotalMilliseconds - stopwatch_last;
stopwatch_last = Stopwatch.Elapsed.TotalMilliseconds;
Tiempo_Pulso = (float)dTime;
}
@ -57,10 +59,8 @@ namespace CtrEditor.ObjetosSim
private Brush color_oculto;
[ObservableProperty]
public float velocidadActual;
[ObservableProperty]
public double angulo;
partial void OnAnguloChanged(double value)
public override void AnguloChanged(float value)
{
// Generar pulsos cuadrados en función del ángulo
Pulso = DetectarDiente();
@ -96,12 +96,37 @@ namespace CtrEditor.ObjetosSim
public float ancho_Dientes;
[ObservableProperty]
public float giros_segundo_a_100;
[ObservableProperty]
public string motor;
partial void OnMotorChanged(string value)
public override void OnResize(float Delta_Width, float Delta_Height)
{
_osMotor = ObtenerLink(Motor, typeof(osVMmotorSim));
Radio_Externo += Delta_Width;
Radio_Interno += Delta_Height;
}
[ObservableProperty]
[property: Description("Link to Motor")]
[property: Category("PLC link:")]
[property: ItemsSource(typeof(osBaseItemsSource<osVMmotorSim>))]
string id_Motor;
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 osGearEncoder()
@ -119,28 +144,9 @@ namespace CtrEditor.ObjetosSim
// Se llama antes de la simulacion
}
public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds)
public override void UpdatePLC(PLCViewModel plc, int elapsedMilliseconds)
{
if (_osMotor != null)
{
if (_osMotor is osVMmotorSim motor)
{
VelocidadActual = motor.Velocidad;
// Calcular la cantidad de giros por segundo
double girosPorSegundo = (VelocidadActual / 100.0) * Giros_segundo_a_100;
// Calcular la fracción del segundo que ha pasado
double segundosTranscurridos = elapsedMilliseconds / 1000.0;
// Calcular el incremento de ángulo
double incrementoAngulo = (girosPorSegundo * 360.0) * segundosTranscurridos;
// Actualizar el ángulo
Angulo = (Angulo + incrementoAngulo) % 360;
}
} else if (Motor.Length>0)
_osMotor = ObtenerLink(Motor, typeof(osVMmotorSim));
}
public bool DetectarDiente()
@ -183,21 +189,43 @@ namespace CtrEditor.ObjetosSim
public override void UpdateControl(int elapsedMilliseconds)
{
// Calculamos la velocidad
if (Motor != null)
{
if (Motor is osVMmotorSim motor)
{
if (motor.Sentido_contrario)
VelocidadActual = -motor.Velocidad;
else
VelocidadActual = motor.Velocidad;
// Calcular la cantidad de giros por segundo
double girosPorSegundo = (VelocidadActual / 100.0) * Giros_segundo_a_100;
// Calcular la fracción del segundo que ha pasado
double segundosTranscurridos = elapsedMilliseconds / 1000.0;
// Calcular el incremento de ángulo
double incrementoAngulo = (girosPorSegundo * 360.0) * segundosTranscurridos;
// Actualizar el ángulo
Angulo = (float)(Angulo + incrementoAngulo) % 360;
}
}
}
public override void ucLoaded()
{
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
ActualizarLeftTop();
base.ucLoaded();
OnId_MotorChanged(Id_Motor); // Link Id_Motor = Motor
}
}
public partial class ucGearEncoder : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public int zIndex_fromFrames { get; set; }
public ucGearEncoder()
{
@ -213,21 +241,12 @@ namespace CtrEditor.ObjetosSim
{
Datos?.ucUnLoaded();
}
public void Resize(float width, float 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()
public ZIndexEnum ZIndex_Base()
{
return 10;
return ZIndexEnum.Estaticos;
}
}
}

View File

@ -4,54 +4,55 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:CtrEditor.ObjetosSim.UserControls"
mc:Ignorable="d"
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim"
xmlns:convert="clr-namespace:CtrEditor.Convertidores">
mc:Ignorable="d">
<UserControl.Resources>
<convert:MeterToPixelConverter x:Key="MeterToPixelConverter"/>
</UserControl.Resources>
<UserControl.DataContext>
<vm:osPhotocell Color="#FFCA1C1C"/>
<vm:osPhotocell Color="#FFCA1C1C" Ancho="0.8" Nombre="PTH" Alto="0.04" />
</UserControl.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition
Width="{Binding Alto, Converter={StaticResource MeterToPixelConverter}, ConverterParameter=5}" />
<!-- Columna para el Label -->
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<!-- Columna para la Image -->
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<!-- Columna para el Rectangle -->
</Grid.ColumnDefinitions>
<Grid.RenderTransform>
<RotateTransform Angle="{Binding Angulo}" CenterX="0" CenterY="0"/>
</Grid.RenderTransform>
<!-- Label en la primera columna -->
<Label Content="{Binding Nombre}" VerticalAlignment="Center" HorizontalAlignment="Center" Grid.Column="0" Foreground="{Binding Color}" >
<Viewbox Grid.Column="0" Stretch="UniformToFill">
<Label Content="{Binding Nombre}"
VerticalAlignment="Center" HorizontalAlignment="Center" Opacity="0.8" FontWeight="Bold" Foreground="{Binding Color}"/>
</Viewbox>
</Label>
<Image Source="/Icons/fotocelula.png"
Width="{Binding Alto, Converter={StaticResource MeterToPixelConverter}, ConverterParameter=2}"
Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}, ConverterParameter=2}"
VerticalAlignment="Center" HorizontalAlignment="Left" Margin="2,2,2,2" Grid.Column="1"/>
<Image Source="/Icons/fotocelula.png"
Width="{Binding Alto, Converter={StaticResource MeterToPixelConverter}, ConverterParameter=3}"
Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}, ConverterParameter=3}"
VerticalAlignment="Center" HorizontalAlignment="Left" Margin="0,0,0,0" Grid.Column="1"/>
<Rectangle x:Name="Photocell" Grid.Column="2" Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}" Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}}">
<Rectangle x:Name="Photocell" Grid.Column="2" Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}, ConverterParameter=0.9}"
Height="{Binding Ancho_Haz_De_Luz, Converter={StaticResource MeterToPixelConverter}}">
<Rectangle.Fill>
<VisualBrush x:Name="MovingPattern" TileMode="Tile" Viewport="0,0,3,3" ViewportUnits="Absolute" Viewbox="0,0,3,3" ViewboxUnits="Absolute">
<VisualBrush.Visual>
<Canvas>
<Ellipse Width="2" Height="2" Fill="{Binding Color}"/>
<Ellipse
Width="1"
Height="1" Fill="{Binding Color}"/>
</Canvas>
</VisualBrush.Visual>
</VisualBrush>
</Rectangle.Fill>
<!-- No se aplica la transformación aquí -->
</Rectangle>
</Grid>
</UserControl>

View File

@ -1,11 +1,15 @@
using CtrEditor.Convertidores;

using CtrEditor.Simulacion;
using CtrEditor.Siemens;
using LibS7Adv;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using CommunityToolkit.Mvvm.ComponentModel;
using System.Diagnostics;
using CtrEditor.FuncionesBase;
using Xceed.Wpf.Toolkit.PropertyGrid.Attributes;
using JsonIgnoreAttribute = Newtonsoft.Json.JsonIgnoreAttribute;
using System.ComponentModel;
namespace CtrEditor.ObjetosSim
{
@ -15,7 +19,10 @@ 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()
{
return "Photocell";
@ -27,10 +34,15 @@ namespace CtrEditor.ObjetosSim
set => SetProperty(ref nombre, value);
}
[ObservableProperty]
[property: Description("Color")]
[property: Category("Layout:")]
Brush color;
[ObservableProperty]
public Brush color;
[ObservableProperty]
public bool luzCortada;
[property: Description("Light cut")]
[property: Category("Debug:")]
bool luzCortada;
partial void OnLuzCortadaChanged(bool value)
{
@ -42,12 +54,85 @@ 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.Elapsed.TotalMilliseconds - timer_lastPositive);
timer_lastNegative = timer.Elapsed.TotalMilliseconds;
} else
{
Lenght_negative_pulse = (float)(timer.Elapsed.TotalMilliseconds - timer_lastNegative);
timer_lastPositive = timer.Elapsed.TotalMilliseconds;
Lenght_FP_to_FP = Lenght_positive_pulse + Lenght_negative_pulse;
Frecuency = (Frecuency * (filter_Frecuency - 1) + (1000 / Lenght_FP_to_FP)) / filter_Frecuency;
}
}
[ObservableProperty]
[property: Description("Size of the Light.")]
[property: Category("Setup:")]
float ancho_Haz_De_Luz;
[ObservableProperty]
[property: Description("Distance to the neck.")]
[property: Category("Debug:")]
float distancia_cuello;
[ObservableProperty]
[property: Description("Type of detection: Neck of the bottle or Complete bottle.")]
[property: Category("Setup:")]
bool detectarCuello;
partial void OnDetectarCuelloChanged(bool value)
{
if (Simulation_Photocell == null) return;
Simulation_Photocell.DetectNeck = value;
}
[ObservableProperty]
[property: Description("Filter signal.")]
[property: Category("Setup:")]
float filter_Frecuency;
partial void OnFilter_FrecuencyChanged(float value)
{
if (value<=0)
Filter_Frecuency = 10;
}
[ObservableProperty]
[property: Category("Debug:")]
float frecuency;
[ObservableProperty]
[property: Category("Debug:")]
float lenght_positive_pulse;
[ObservableProperty]
[property: Category("Debug:")]
float lenght_negative_pulse;
[ObservableProperty]
[property: Category("Debug:")]
float lenght_FP_to_FP;
[ObservableProperty]
[property: Description("Type Filter signal.")]
[property: Category("Setup:")]
public bool tipo_NC;
partial void OnTipo_NCChanged(bool value)
{
OnLuzCortadaChanged(LuzCortada);
}
[ObservableProperty]
public string tagPhotocell_OUT;
@ -62,40 +147,36 @@ namespace CtrEditor.ObjetosSim
ActualizarGeometrias();
}
[ObservableProperty]
public float ancho;
partial void OnAnchoChanged(float value)
public override void AnguloChanged(float value)
{
ActualizarGeometrias();
}
[ObservableProperty]
public float alto;
partial void OnAltoChanged(float value)
public override void AltoChanged(float value)
{
ActualizarGeometrias();
}
[ObservableProperty]
public float angulo;
partial void OnAnguloChanged(float value)
public override void AnchoChanged(float value)
{
ActualizarGeometrias();
}
private void ActualizarGeometrias()
{
if (_visualRepresentation is ucPhotocell uc)
UpdateRectangle(Simulation_Photocell, uc.Photocell, Alto, Ancho, Angulo);
UpdateRectangle(Simulation_Photocell, uc.Photocell, Ancho_Haz_De_Luz, Ancho, Angulo);
}
public osPhotocell()
{
Ancho = 1;
Alto = 0.03f;
Ancho_Haz_De_Luz = 0.01f;
Frecuency = 0;
timer = new Stopwatch();
timer.Start();
}
public override void UpdateGeometryStart()
@ -105,22 +186,29 @@ namespace CtrEditor.ObjetosSim
}
public override void UpdateControl(int elapsedMilliseconds)
{
LuzCortada = Simulation_Photocell.LuzCortada;
Distancia_cuello = Simulation_Photocell.Distancia;
if (DetectarCuello)
LuzCortada = Simulation_Photocell.LuzCortadaNeck;
else
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) {
public override void UpdatePLC(PLCViewModel plc, int elapsedMilliseconds) {
}
public override void ucLoaded()
{
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
ActualizarLeftTop();
base.ucLoaded();
if (_visualRepresentation is ucPhotocell uc)
Simulation_Photocell = AddBarrera(simulationManager, uc.Photocell, Alto, Ancho, Angulo);
Simulation_Photocell = AddBarrera(simulationManager, uc.Photocell, Alto, Ancho, Angulo, DetectarCuello);
}
public override void ucUnLoaded()
{
@ -135,6 +223,7 @@ namespace CtrEditor.ObjetosSim
public partial class ucPhotocell : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public int zIndex_fromFrames { get; set; }
public ucPhotocell()
{
@ -150,31 +239,12 @@ namespace CtrEditor.ObjetosSim
{
Datos?.ucUnLoaded();
}
public void Resize(float width, float height)
{
if (width == 0) return;
if (Datos is osPhotocell datos)
datos.Ancho = 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 osPhotocell datos)
datos.Angulo = Angle;
}
public void Highlight(bool State) { }
public int ZIndex()
public ZIndexEnum ZIndex_Base()
{
return 16;
return ZIndexEnum.Fotocelula;
}
}
}

View File

@ -4,13 +4,8 @@
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:vm="clr-namespace:CtrEditor.ObjetosSim"
xmlns:convert="clr-namespace:CtrEditor.Convertidores">
<UserControl.Resources>
<convert:MeterToPixelConverter x:Key="MeterToPixelConverter"/>
</UserControl.Resources>
mc:Ignorable="d">
<UserControl.DataContext>
<vm:osSensTemperatura />

View File

@ -1,8 +1,10 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CtrEditor.Convertidores;
using CtrEditor.Siemens;
using LibS7Adv;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using CtrEditor.FuncionesBase;
namespace CtrEditor.ObjetosSim
{
@ -30,6 +32,7 @@ namespace CtrEditor.ObjetosSim
public float min_OUT_Scaled;
[ObservableProperty]
public float max_OUT_Scaled;
[ObservableProperty]
public float value;
@ -38,13 +41,6 @@ namespace CtrEditor.ObjetosSim
EscribirWordTagScaled(Tag, value, 0, 100, Min_OUT_Scaled, Max_OUT_Scaled);
}
[ObservableProperty]
public float ancho;
[ObservableProperty]
public float alto;
[ObservableProperty]
public float angulo;
public osSensTemperatura()
{
Ancho = 0.4f;
@ -53,16 +49,21 @@ namespace CtrEditor.ObjetosSim
Min_OUT_Scaled = 0;
}
public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds)
public override void UpdatePLC(PLCViewModel plc, int elapsedMilliseconds)
{
}
public override void UpdatePLCPrimerCiclo()
{
// Escribimos el valor actual al iniciar la conexion
OnValueChanged(Value);
}
public override void ucLoaded()
{
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
ActualizarLeftTop();
base.ucLoaded();
}
@ -71,6 +72,7 @@ namespace CtrEditor.ObjetosSim
public partial class ucSensTemperatura : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public int zIndex_fromFrames { get; set; }
public ucSensTemperatura()
{
@ -86,32 +88,11 @@ namespace CtrEditor.ObjetosSim
{
Datos?.ucUnLoaded();
}
public void Resize(float width, float height)
{
if (Datos is osSensTemperatura 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 osSensTemperatura datos)
datos.Angulo = Angle;
}
public void Highlight(bool State) { }
public int ZIndex()
public ZIndexEnum ZIndex_Base()
{
return 10;
return ZIndexEnum.Estaticos;
}
}
}

View File

@ -4,15 +4,10 @@
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:ei="http://schemas.microsoft.com/xaml/behaviors"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:convert="clr-namespace:CtrEditor.Convertidores"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim"
mc:Ignorable="d">
<UserControl.Resources>
<convert:MeterToPixelConverter x:Key="MeterToPixelConverter"/>
</UserControl.Resources>
<UserControl.DataContext>
<vm:osAnalogTag/>
</UserControl.DataContext>

View File

@ -1,10 +1,11 @@
using CtrEditor.Convertidores;
using CtrEditor.Siemens;

using LibS7Adv;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using CommunityToolkit.Mvvm.ComponentModel;
using Newtonsoft.Json.Linq;
using CtrEditor.FuncionesBase;
namespace CtrEditor.ObjetosSim
{
@ -31,6 +32,12 @@ namespace CtrEditor.ObjetosSim
[ObservableProperty]
public float tamano;
public override void OnResize(float Delta_Width, float Delta_Height)
{
Tamano += Delta_Width + Delta_Height;
}
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(Value))]
public string tag;
@ -74,10 +81,16 @@ namespace CtrEditor.ObjetosSim
// Se llama antes de la simulacion
}
public override void UpdatePLC(PLCModel plc, int elapsedMilliseconds)
public override void UpdatePLC(PLCViewModel plc, int elapsedMilliseconds)
{
}
public override void UpdatePLCPrimerCiclo()
{
// Escribimos el valor actual al iniciar la conexion
OnValueChanged(Value);
}
public override void UpdateControl(int elapsedMilliseconds)
{
// Calculamos la velocidad
@ -88,7 +101,7 @@ namespace CtrEditor.ObjetosSim
{
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
ActualizarLeftTop();
base.ucLoaded();
}
}
@ -96,6 +109,7 @@ namespace CtrEditor.ObjetosSim
public partial class ucAnalogTag : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public int zIndex_fromFrames { get; set; }
public ucAnalogTag()
{
@ -111,20 +125,10 @@ namespace CtrEditor.ObjetosSim
{
Datos?.ucUnLoaded();
}
public void Resize(float width, float 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()
public ZIndexEnum ZIndex_Base()
{
return 10;
return ZIndexEnum.Estaticos;
}
}
}

View File

@ -4,18 +4,22 @@
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:ei="http://schemas.microsoft.com/xaml/behaviors"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:convert="clr-namespace:CtrEditor.Convertidores"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim"
mc:Ignorable="d">
<UserControl.Resources>
<convert:MeterToPixelConverter x:Key="MeterToPixelConverter"/>
</UserControl.Resources>
<UserControl.DataContext>
<vm:osBoolTag/>
</UserControl.DataContext>
<Canvas RenderTransformOrigin="0.5,0.5">
<Canvas.RenderTransform>
<TransformGroup>
<ScaleTransform />
<SkewTransform />
<RotateTransform Angle="{Binding Angulo}" />
<TranslateTransform />
</TransformGroup>
</Canvas.RenderTransform>
<Grid Margin="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
@ -30,8 +34,10 @@
Height="40"
VerticalAlignment="Top"
HorizontalAlignment="Left"
Background="Gray"/>
Background="{Binding Color, Converter={StaticResource ColorToBrushConverter}}"/>
<Label Content="{Binding Descripcion}" Grid.Column="1" VerticalAlignment="Center" Background="{Binding Color}" />
<CheckBox Grid.Column="2" VerticalAlignment="Center" IsChecked="{Binding Estado}" />
<CheckBox Grid.Column="2" VerticalAlignment="Center" IsChecked="{Binding Estado}"
Background="{Binding Color_oculto}" />
</Grid>
</Canvas>
</UserControl>

View File

@ -1,10 +1,11 @@
using CtrEditor.Convertidores;
using CtrEditor.Siemens;

using LibS7Adv;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using CommunityToolkit.Mvvm.ComponentModel;
using Newtonsoft.Json.Linq;
using CtrEditor.FuncionesBase;
namespace CtrEditor.ObjetosSim
{
@ -32,6 +33,9 @@ namespace CtrEditor.ObjetosSim
[ObservableProperty]
private Brush color_oculto;
[ObservableProperty]
Color color;
[ObservableProperty]
public bool estado;
@ -43,9 +47,15 @@ namespace CtrEditor.ObjetosSim
else
Color_oculto = Brushes.Transparent;
}
[ObservableProperty]
public float tamano;
public override void OnResize(float Delta_Width, float Delta_Height)
{
Tamano += Delta_Width + Delta_Height;
}
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(Estado))]
public string tag;
@ -58,13 +68,20 @@ namespace CtrEditor.ObjetosSim
Tamano = 0.30f;
tag = "%M50.0";
Descripcion = "Nombre del Tag";
Color = Colors.LightBlue;
}
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
// crear el objeto de simulacion
ActualizarLeftTop();
base.ucLoaded();
}
}
@ -72,6 +89,7 @@ namespace CtrEditor.ObjetosSim
public partial class ucBoolTag : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public int zIndex_fromFrames { get; set; }
public ucBoolTag()
{
@ -87,20 +105,11 @@ namespace CtrEditor.ObjetosSim
{
Datos?.ucUnLoaded();
}
public void Resize(float width, float 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()
public ZIndexEnum ZIndex_Base()
{
return 10;
return ZIndexEnum.Estaticos;
}
}
}

View File

@ -3,10 +3,9 @@
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"
mc:Ignorable="d"
Background="LightGray"
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim"
xmlns:convert="clr-namespace:CtrEditor.Convertidores"
Background="LightGray">
mc:Ignorable="d">
<UserControl.DataContext>
<vm:osConsensGeneric />

View File

@ -1,8 +1,7 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CtrEditor.Convertidores;
using CtrEditor.Siemens;
using System.Windows;
using System.Windows.Controls;
using CtrEditor.FuncionesBase;
namespace CtrEditor.ObjetosSim
@ -34,14 +33,19 @@ namespace CtrEditor.ObjetosSim
[ObservableProperty]
public float tamano;
public override void OnResize(float Delta_Width, float Delta_Height)
{
Tamano += Delta_Width + Delta_Height;
}
public override void ucLoaded()
{
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
ActualizarLeftTop();
base.ucLoaded();
if (_visualRepresentation is ucConsensGeneric uc)
Tags = UserControlFactory.CargarPropiedadesosDatosTags(Consensos, uc.PanelEdicion, null);
//if (_visualRepresentation is ucConsensGeneric uc)
// Tags = UserControlFactory.CargarPropiedadesosDatosTags(Consensos, uc.PanelEdicion, null);
}
}
@ -49,6 +53,7 @@ namespace CtrEditor.ObjetosSim
public partial class ucConsensGeneric : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public int zIndex_fromFrames { get; set; }
public ucConsensGeneric()
{
@ -64,21 +69,12 @@ namespace CtrEditor.ObjetosSim
{
Datos?.ucUnLoaded();
}
public void Resize(float width, float 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()
public ZIndexEnum ZIndex_Base()
{
return 10;
return ZIndexEnum.Estaticos;
}
}
public class TagsConsensos

View File

@ -0,0 +1,39 @@
<UserControl x:Class="CtrEditor.ObjetosSim.ucTrace3"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:ei="http://schemas.microsoft.com/xaml/behaviors"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
Background="#FFECECEC"
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim"
mc:Ignorable="d">
<UserControl.DataContext>
<vm:osTrace3 Descripcion_Serie_1="Serie 1" Max_Cantidad_Elementos="100"/>
</UserControl.DataContext>
<Grid Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}}"
Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<!-- Title -->
<RowDefinition Height="*"/>
<!-- Chart Area -->
<RowDefinition Height="Auto"/>
<!-- Descriptions -->
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="{Binding Titulo}" FontSize="10" HorizontalAlignment="Center"/>
<Canvas Grid.Row="1" x:Name="ChartCanvas" Background="White" Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}" />
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Center" >
<TextBlock Text="{Binding Descripcion_Serie_1}" Margin="1" FontSize="8" Foreground="{Binding Color_Serie_1}"/>
<TextBlock Text="{Binding Descripcion_Serie_2}" Margin="1" FontSize="8" Foreground="{Binding Color_Serie_2}"/>
<TextBlock Text="{Binding Descripcion_Serie_3}" Margin="1" FontSize="8" Foreground="{Binding Color_Serie_3}"/>
</StackPanel>
</Grid>
</UserControl>

View File

@ -0,0 +1,305 @@

using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using CommunityToolkit.Mvvm.ComponentModel;
using System.Windows.Shapes;
using LibS7Adv;
using System.Runtime.Intrinsics;
using CtrEditor.FuncionesBase;
namespace CtrEditor.ObjetosSim
{
/// <summary>
/// Interaction logic for ucTrace3.xaml
/// </summary>
public partial class osTrace3 : osBase, IosBase
{
private List<float> _series1 = new List<float>();
private List<float> _series2 = new List<float>();
private List<float> _series3 = new List<float>();
// 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]
bool serie1_Tipo_Bool;
[ObservableProperty]
string tag_Serie1;
[ObservableProperty]
bool serie2_Tipo_Bool;
[ObservableProperty]
string tag_Serie2;
[ObservableProperty]
bool serie3_Tipo_Bool;
[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;
Serie1_Tipo_Bool = true;
Serie2_Tipo_Bool = true;
Serie3_Tipo_Bool = true;
}
public override void UpdatePLCPrimerCiclo()
{
// Escribimos el valor actual al iniciar la conexion
}
public override void UpdatePLC(PLCViewModel plc, int elapsedMilliseconds)
{
float v1, v2, v3;
base.UpdatePLC(plc, elapsedMilliseconds);
if (Serie1_Tipo_Bool)
v1 = LeerBitTag(Tag_Serie1) ? 0 : 1;
else
v1 = LeerWordTag(Tag_Serie1);
if (Serie2_Tipo_Bool)
v2 = LeerBitTag(Tag_Serie2) ? 0 : 1;
else
v2 = LeerWordTag(Tag_Serie2);
if (Serie3_Tipo_Bool)
v3 = LeerBitTag(Tag_Serie3) ? 0 : 1;
else
v3 = LeerWordTag(Tag_Serie3);
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 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<float> 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);
int startIndex = 0;
float lastX = 0;
float lastY = canvasHeight - (series[startIndex] - minY + yoffset) * scaleY;
while (startIndex < series.Count - 1)
{
// Buscar el próximo cambio en la serie
int endIndex = startIndex + 1;
while (endIndex < series.Count && series[endIndex] == series[startIndex])
{
endIndex++;
}
// Dibujar la línea horizontal desde startIndex hasta endIndex - 1
float x1 = lastX;
float y1 = lastY;
float x2 = (endIndex - 1) * scaleX;
float y2 = y1;
var horizontalLine = new Line
{
Stroke = color,
StrokeThickness = 0.4,
X1 = x1,
Y1 = y1,
X2 = x2,
Y2 = y2
};
ChartCanvas.Children.Add(horizontalLine);
// Dibujar la línea vertical en endIndex - 1 si no es el último punto
if (endIndex < series.Count)
{
float xVertical = x2;
float yVerticalStart = y2;
float yVerticalEnd = canvasHeight - (series[endIndex] - minY + yoffset) * scaleY;
var verticalLine = new Line
{
Stroke = color,
StrokeThickness = 0.4,
X1 = xVertical,
Y1 = yVerticalStart,
X2 = xVertical,
Y2 = yVerticalEnd
};
ChartCanvas.Children.Add(verticalLine);
// Actualizar la última posición dibujada
lastX = xVertical;
lastY = yVerticalEnd;
}
else
{
// Actualizar la última posición dibujada para la última línea horizontal
lastX = x2;
lastY = y2;
}
// Actualizar startIndex
startIndex = endIndex;
}
}
}
public partial class ucTrace3 : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public int zIndex_fromFrames { 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 float Angle() { return 0; } public void Rotate(float Angle) { }
public void Highlight(bool State) { }
public ZIndexEnum ZIndex_Base()
{
return ZIndexEnum.Trace;
}
}
}

View File

@ -0,0 +1,20 @@
<UserControl x:Class="CtrEditor.ObjetosSim.ucTraceSimple"
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:lvc="clr-namespace:LiveChartsCore.SkiaSharpView.WPF;assembly=LiveChartsCore.SkiaSharpView.WPF"
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim"
mc:Ignorable="d">
<UserControl.DataContext>
<vm:osTraceSimple Alto="3" Ancho="6"/>
</UserControl.DataContext>
<Grid>
<lvc:CartesianChart Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}"
Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}}"
Name="SignalChart" Series="{Binding Series}">
</lvc:CartesianChart>
</Grid>
</UserControl>

View File

@ -0,0 +1,171 @@

using LibS7Adv;
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;
using CtrEditor.FuncionesBase;
namespace CtrEditor.ObjetosSim
{
/// <summary>
/// Interaction logic for ucTraceSimple.xaml
/// </summary>
public partial class osTraceSimple : osBase, IosBase
{
private List<float> 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<ObservableValue> _observableValues;
public ObservableCollection<ISeries> Series { get; set; }
public osTraceSimple()
{
// Use ObservableCollections to let the chart listen for changes (or any INotifyCollectionChanged).
_observableValues = new ObservableCollection<ObservableValue>();
Series = new ObservableCollection<ISeries>
{
new LineSeries<ObservableValue>
{
Values = _observableValues,
Fill = null
}
};
data = new List<float>();
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(PLCViewModel 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.Elapsed.TotalMilliseconds > 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
base.ucLoaded();
}
}
public partial class ucTraceSimple : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public int zIndex_fromFrames { 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 float Angle() { return 0; } public void Rotate(float Angle) { }
public void Highlight(bool State) { }
public ZIndexEnum ZIndex_Base()
{
return ZIndexEnum.Trace;
}
}
}

Some files were not shown because too many files have changed in this diff Show More