Refactor hydraulic components: Updated osHydPipe and osHydPump to improve code clarity by changing region comments to regular comments. Enhanced osHydTank with a new OnMove method and improved initialization in ucHydTank.xaml. Updated StateSerializer to include default values during serialization.

This commit is contained in:
Miguel 2025-09-06 00:38:07 +02:00
parent 0147e010d3
commit 8e6d457047
13 changed files with 441 additions and 902 deletions

View File

@ -193,7 +193,6 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Folder Include="ObjetosSim\HydraulicSimulator\" />
<Folder Include="paddleocr\cls\inference\" /> <Folder Include="paddleocr\cls\inference\" />
<Folder Include="paddleocr\det\inference\" /> <Folder Include="paddleocr\det\inference\" />
<Folder Include="paddleocr\keys\" /> <Folder Include="paddleocr\keys\" />

View File

@ -0,0 +1,374 @@
# Sistema de Componentes Hidráulicos - CtrEditor
## 📋 Resumen del Sistema
Este documento describe todos los componentes necesarios para implementar un sistema hidráulico completo en CtrEditor. El sistema está diseñado con una arquitectura modular que permite simular circuitos hidráulicos industriales complejos.
---
## 🎯 Componentes Existentes
### ✅ **Tanque Hidráulico** - `osHydTank`
- **Archivo**: `osHydTank.cs` / `ucHydTank.xaml`
- **Descripción**: Tanque hidráulico con gestión dinámica de nivel y presión configurable
- **Tipos**: Suction, Intermediate, Storage, Process
- **Características**:
- Múltiples tipos de tanque via enum `HydraulicTankType`
- Presión fija o variable (`IsFixedPressure`)
- Gestión completa de flujos entrada/salida
- Cálculos dinámicos de nivel y volumen
- Propiedades visuales para UI (colores, indicadores)
- Conexiones configurables de entrada y salida
### ✅ **Tubería Hidráulica** - `osHydPipe`
- **Archivo**: `osHydPipe.cs` / `ucHydPipe.xaml`
- **Descripción**: Tubería para transporte de fluido hidráulico
- **Características**:
- Cálculo de pérdidas de carga por fricción
- Diferentes materiales y rugosidades
- Diámetros configurables
- Longitud variable
- Resistencia hidráulica calculada
### ✅ **Bomba Hidráulica** - `osHydPump`
- **Archivo**: `osHydPump.cs` / `ucHydPump.xaml`
- **Descripción**: Bomba para generar presión y caudal en el sistema
- **Tipos**: Centrifugal, Positive Displacement, Variable Displacement
- **Características**:
- Curvas características configurables
- Control de velocidad variable
- Eficiencia energética
- Protección contra cavitación
- Control PID integrado
---
## 🔧 Componentes de Control (Por Implementar)
### **Válvulas** - `osHydValve`
- **Archivo**: `osHydValve.cs` / `ucHydValve.xaml`
- **Descripción**: Válvula general configurable para control de flujo
- **Tipos**:
- `Ball` - Válvula de bola (on/off)
- `Gate` - Válvula de compuerta
- `Globe` - Válvula de globo
- `Check` - Válvula de retención
- `Relief` - Válvula de alivio
- `Throttle` - Válvula de estrangulación
- **Estados**: `Open`, `Closed`, `Partial`
- **Propiedades**:
- Coeficiente de flujo (Cv)
- Pérdida de presión
- Posición del actuador
- Tiempo de operación
### **Actuadores Hidráulicos** - `osHydActuator`
- **Archivo**: `osHydActuator.cs` / `ucHydActuator.xaml`
- **Descripción**: Cilindros y actuadores hidráulicos
- **Tipos**:
- `SingleAction` - Cilindro de simple efecto
- `DoubleAction` - Cilindro de doble efecto
- `Rotary` - Actuador rotativo
- **Propiedades**:
- Diámetro del pistón
- Carrera (stroke)
- Fuerza desarrollada
- Velocidad de actuación
- Posición actual
---
## 📊 Instrumentación (Por Implementar)
### **Sensor de Presión** - `osHydPressureSensor`
- **Archivo**: `osHydPressureSensor.cs` / `ucHydPressureSensor.xaml`
- **Descripción**: Medición de presión en el sistema
- **Propiedades**:
- Rango de medición (0-1000 bar)
- Precisión (±0.1%)
- Tiempo de respuesta
- Señal de salida (4-20mA, 0-10V)
- Calibración automática
### **Sensor de Caudal** - `osHydFlowSensor`
- **Archivo**: `osHydFlowSensor.cs` / `ucHydFlowSensor.xaml`
- **Descripción**: Medición de caudal volumétrico
- **Tipos**: Electromagnetic, Turbine, Vortex, Ultrasonic
- **Propiedades**:
- Rango de caudal (L/min)
- Pérdida de presión
- Precisión de medición
- Compensación de temperatura
### **Sensor de Nivel** - `osHydLevelSensor`
- **Archivo**: `osHydLevelSensor.cs` / `ucHydLevelSensor.xaml`
- **Descripción**: Medición de nivel en tanques
- **Tipos**: Ultrasonic, Radar, Capacitive, Float
- **Propiedades**:
- Altura de medición
- Zona muerta
- Material del tanque
- Constante dieléctrica del fluido
### **Sensor de Temperatura** - `osHydTemperatureSensor`
- **Archivo**: `osHydTemperatureSensor.cs` / `ucHydTemperatureSensor.xaml`
- **Descripción**: Medición de temperatura del fluido
- **Tipos**: Thermocouple, RTD, Thermistor
- **Propiedades**:
- Rango de temperatura (-40°C a +200°C)
- Tiempo de respuesta térmica
- Exactitud (±0.1°C)
- Inmersión requerida
---
## 🔀 Conexiones y Distribución (Por Implementar)
### **Conexión en T** - `osHydTee`
- **Archivo**: `osHydTee.cs` / `ucHydTee.xaml`
- **Descripción**: Conexión de tres vías para dividir flujo
- **Propiedades**:
- Diámetro de entrada
- Diámetros de salida
- Coeficiente de pérdida
- Material de construcción
### **Codo 90°** - `osHydElbow`
- **Archivo**: `osHydElbow.cs` / `ucHydElbow.xaml`
- **Descripción**: Cambio de dirección a 90 grados
- **Propiedades**:
- Radio de curvatura
- Ángulo (45°, 90°, custom)
- Factor de pérdida K
- Diámetro interno
### **Conexión Cruzada** - `osHydCross`
- **Archivo**: `osHydCross.cs` / `ucHydCross.xaml`
- **Descripción**: Conexión de cuatro vías
- **Propiedades**:
- Configuración de flujos
- Pérdidas por turbulencia
- Diámetros de conexión
### **Reductor de Diámetro** - `osHydReducer`
- **Archivo**: `osHydReducer.cs` / `ucHydReducer.xaml`
- **Descripción**: Cambio gradual de diámetro de tubería
- **Tipos**: Concentric, Eccentric
- **Propiedades**:
- Diámetro de entrada
- Diámetro de salida
- Ángulo de reducción
- Pérdida por expansión/contracción
### **Distribuidor Multiple** - `osHydManifold`
- **Archivo**: `osHydManifold.cs` / `ucHydManifold.xaml`
- **Descripción**: Distribuidor de múltiples salidas
- **Propiedades**:
- Número de salidas (2-12)
- Distribución de caudal
- Válvulas integradas opcionales
- Presión de entrada común
---
## 🎛️ Control Avanzado (Por Implementar)
### **Regulador de Presión** - `osHydPressureRegulator`
- **Archivo**: `osHydPressureRegulator.cs` / `ucHydPressureRegulator.xaml`
- **Descripción**: Control automático de presión
- **Propiedades**:
- Presión de consigna (setpoint)
- Banda proporcional
- Control PID integrado
- Rango de regulación
- Precisión de control
### **Regulador de Caudal** - `osHydFlowRegulator`
- **Archivo**: `osHydFlowRegulator.cs` / `ucHydFlowRegulator.xaml`
- **Descripción**: Control automático de caudal
- **Propiedades**:
- Caudal de consigna
- Compensación de presión
- Tiempo de respuesta
- Estabilidad de flujo
### **Válvula de Alivio** - `osHydPressureRelief`
- **Archivo**: `osHydPressureRelief.cs` / `ucHydPressureRelief.xaml`
- **Descripción**: Protección contra sobrepresión
- **Propiedades**:
- Presión de apertura
- Presión de cierre
- Capacidad de descarga
- Tipo de pilotaje
---
## 🌡️ Gestión Térmica (Por Implementar)
### **Filtro Hidráulico** - `osHydFilter`
- **Archivo**: `osHydFilter.cs` / `ucHydFilter.xaml`
- **Descripción**: Filtración de partículas y contaminantes
- **Tipos**:
- `Suction` - Filtro de aspiración
- `Return` - Filtro de retorno
- `Pressure` - Filtro de presión
- `Bypass` - Filtro de bypass
- **Propiedades**:
- Grado de filtración (micrones)
- Capacidad de retención
- Pérdida de presión
- Indicador de saturación
### **Enfriador** - `osHydCooler`
- **Archivo**: `osHydCooler.cs` / `ucHydCooler.xaml`
- **Descripción**: Enfriamiento del fluido hidráulico
- **Tipos**: Air-cooled, Water-cooled
- **Propiedades**:
- Capacidad de enfriamiento (kW)
- Temperatura de entrada/salida
- Caudal de fluido refrigerante
- Eficiencia térmica
### **Calentador** - `osHydHeater`
- **Archivo**: `osHydHeater.cs` / `ucHydHeater.xaml`
- **Descripción**: Calentamiento del fluido hidráulico
- **Propiedades**:
- Potencia de calentamiento (kW)
- Temperatura objetivo
- Control de temperatura
- Protección contra sobrecalentamiento
### **Intercambiador de Calor** - `osHydHeatExchanger`
- **Archivo**: `osHydHeatExchanger.cs` / `ucHydHeatExchanger.xaml`
- **Descripción**: Intercambio térmico entre fluidos
- **Tipos**: Plate, Shell-and-tube, Coaxial
- **Propiedades**:
- Coeficiente de transferencia térmica
- Área de intercambio
- Configuración de flujos
- Eficiencia térmica
---
## 🔄 Componentes Especializados (Por Implementar)
### **Acumulador Hidráulico** - `osHydAccumulator`
- **Archivo**: `osHydAccumulator.cs` / `ucHydAccumulator.xaml`
- **Descripción**: Almacenamiento de energía hidráulica
- **Tipos**:
- `Bladder` - Membrana elastomérica
- `Piston` - Pistón separador
- `Diaphragm` - Diafragma metálico
- **Propiedades**:
- Volumen total
- Presión de precarga
- Volumen útil
- Tiempo de descarga
### **Motor Hidráulico** - `osHydMotor`
- **Archivo**: `osHydMotor.cs` / `ucHydMotor.xaml`
- **Descripción**: Conversión de energía hidráulica a mecánica rotativa
- **Tipos**:
- `Gear` - Motor de engranajes
- `Vane` - Motor de paletas
- `Piston` - Motor de pistones
- `Radial` - Motor radial
- **Propiedades**:
- Cilindrada (cm³/rev)
- Velocidad nominal (rpm)
- Par motor (Nm)
- Eficiencia volumétrica/mecánica
---
## 📋 Plan de Implementación
### **Fase 1 - Componentes Básicos** ⭐⭐⭐
1. `osHydValve` (Ball, Check, Relief)
2. `osHydPressureSensor`
3. `osHydFlowSensor`
4. `osHydTee`
### **Fase 2 - Control Intermedio** ⭐⭐
5. `osHydActuator`
6. `osHydPressureRegulator`
7. `osHydFilter`
8. `osHydManifold`
### **Fase 3 - Componentes Avanzados**
9. `osHydAccumulator`
10. `osHydMotor`
11. `osHydCooler`
12. `osHydHeatExchanger`
### **Fase 4 - Instrumentación Completa**
13. `osHydLevelSensor`
14. `osHydTemperatureSensor`
15. `osHydFlowRegulator`
16. Componentes de conexión restantes
---
## 💡 Arquitectura de Interfaces
### **Interfaces Base**
```csharp
// Control de flujo
public interface IHydraulicFlowController
{
double FlowCoefficient { get; set; }
double PressureDrop { get; }
ValveState CurrentState { get; set; }
}
// Actuadores
public interface IHydraulicActuator
{
double Force { get; }
double Stroke { get; set; }
double Velocity { get; }
ActuatorType Type { get; }
}
// Sensores
public interface IHydraulicSensor
{
double MeasuredValue { get; }
string Units { get; }
bool IsCalibrated { get; set; }
double Accuracy { get; }
}
// Componentes térmicos
public interface IHydraulicThermalComponent
{
double ThermalCapacity { get; }
double HeatTransferCoefficient { get; }
double OperatingTemperature { get; set; }
}
```
---
## 🎯 Objetivos del Sistema
- **Modularidad**: Cada componente es independiente y reutilizable
- **Realismo**: Cálculos basados en principios de ingeniería hidráulica
- **Flexibilidad**: Configuración adaptable a diferentes aplicaciones
- **Integración**: Compatible con el sistema de simulación existente
- **Escalabilidad**: Fácil adición de nuevos componentes
- **Documentación**: Cada componente bien documentado y ejemplificado
---
## 📚 Referencias Técnicas
- ISO 1219-1: Símbolos gráficos para esquemas de circuitos hidráulicos
- ISO 4413: Transmisiones hidráulicas - Reglas generales y requisitos de seguridad
- NFPA T3.5.17: Hidráulica industrial - Estándares de filtración
- API 610: Bombas centrífugas para servicios de refinería petroquímica
---
*Documento generado automáticamente - CtrEditor v2024*
*Última actualización: Septiembre 2025*

View File

@ -1,43 +0,0 @@
using System;
using System.Linq;
using CtrEditor.ObjetosSim.HydraulicComponents;
namespace CtrEditor.Test
{
class FilterDebugTest
{
static void TestHydraulicComponentDetection()
{
// Test hydraulic component detection logic like in osVisFilter
var tankType = typeof(osHydTank);
var pipeType = typeof(osHydPipe);
Console.WriteLine("=== Tank Type Analysis ===");
Console.WriteLine($"Tank Type: {tankType.FullName}");
Console.WriteLine($"Tank Namespace: {tankType.Namespace}");
Console.WriteLine($"Has HydraulicComponents in namespace: {tankType.Namespace?.Contains("HydraulicComponents")}");
Console.WriteLine($"Tank Interfaces: {string.Join(", ", tankType.GetInterfaces().Select(i => i.Name))}");
Console.WriteLine($"Has Hydraulic interface: {tankType.GetInterfaces().Any(i => i.Name.Contains("Hydraulic"))}");
Console.WriteLine("\n=== Pipe Type Analysis ===");
Console.WriteLine($"Pipe Type: {pipeType.FullName}");
Console.WriteLine($"Pipe Namespace: {pipeType.Namespace}");
Console.WriteLine($"Has HydraulicComponents in namespace: {pipeType.Namespace?.Contains("HydraulicComponents")}");
Console.WriteLine($"Pipe Interfaces: {string.Join(", ", pipeType.GetInterfaces().Select(i => i.Name))}");
Console.WriteLine($"Has Hydraulic interface: {pipeType.GetInterfaces().Any(i => i.Name.Contains("Hydraulic"))}");
// Test the exact logic from IsHydraulicComponentType
Console.WriteLine("\n=== IsHydraulicComponentType Logic Test ===");
bool tankIsHydraulic = tankType.Namespace != null &&
(tankType.Namespace.Contains("HydraulicComponents") ||
tankType.GetInterfaces().Any(i => i.Name.Contains("Hydraulic")));
bool pipeIsHydraulic = pipeType.Namespace != null &&
(pipeType.Namespace.Contains("HydraulicComponents") ||
pipeType.GetInterfaces().Any(i => i.Name.Contains("Hydraulic")));
Console.WriteLine($"Tank IsHydraulicComponentType: {tankIsHydraulic}");
Console.WriteLine($"Pipe IsHydraulicComponentType: {pipeIsHydraulic}");
}
}
}

View File

@ -1,179 +0,0 @@
using System;
using System.Collections.Generic;
using HydraulicSimulator.Models;
using CtrEditor.HydraulicSimulator;
using CtrEditor.ObjetosSim.HydraulicComponents;
namespace CtrEditor.HydraulicSimulator.Examples
{
/// <summary>
/// Ejemplo de uso de los componentes hidráulicos según la documentación
/// Muestra cómo crear una red simple: Bomba -> Tubería -> Tanque de Descarga
/// </summary>
public class SimpleHydraulicSystemExample
{
/// <summary>
/// Crea un sistema hidráulico simple con bomba, tubería y tanque
/// </summary>
public static void RunExample()
{
Console.WriteLine("🚀 Iniciando ejemplo de sistema hidráulico simple");
Console.WriteLine("Sistema: Bomba -> Tubería -> Tanque de Descarga");
Console.WriteLine();
// 1. Crear la red hidráulica
var network = new HydraulicNetwork();
// 2. Agregar nodos según la documentación
network.AddNode("SUMINISTRO", 0); // Tanque de suministro a presión atmosférica
network.AddNode("BOMBA_OUT"); // Salida de bomba (presión calculada)
network.AddNode("TANQUE_DESCARGA", 0); // Tanque de descarga libre
// 3. Crear elementos hidráulicos
var pump = new PumpHQ(
h0: 80, // Cabeza a caudal cero: 80 metros
q0: 0.01, // Caudal a cabeza cero: 0.01 m³/s (36 m³/h)
speedRel: 1.0, // Velocidad nominal
direction: 1 // Dirección normal
);
var pipe = new Pipe(
length: 50, // 50 metros de longitud
diameter: 0.08, // 80mm de diámetro
roughness: 0.0015 // Rugosidad del acero comercial
);
// 4. Conectar elementos en ramas según la documentación
network.AddBranch("SUMINISTRO", "BOMBA_OUT",
new List<Element> { pump }, "Bomba_Principal");
network.AddBranch("BOMBA_OUT", "TANQUE_DESCARGA",
new List<Element> { pipe }, "Tuberia_Descarga");
// 5. Resolver la red
Console.WriteLine("⚙️ Resolviendo red hidráulica...");
var result = network.Solve(
maxIterations: 200,
tolerance: 1e-4,
verbose: true
);
// 6. Mostrar resultados
if (result.Converged)
{
Console.WriteLine("✅ Simulación exitosa!");
Console.WriteLine($"Convergió en {result.Iterations} iteraciones");
Console.WriteLine();
ShowResults(result);
ShowComponentAnalysis(result, pump, pipe);
}
else
{
Console.WriteLine($"❌ No convergió después de {result.Iterations} iteraciones");
Console.WriteLine($"Error final: {result.Residual:E6}");
}
}
private static void ShowResults(SolutionResult result)
{
Console.WriteLine("📊 RESULTADOS DE LA SIMULACIÓN:");
Console.WriteLine("═══════════════════════════════");
// Flujos
Console.WriteLine("🌊 Flujos:");
foreach (var flow in result.Flows)
{
double flowM3h = flow.Value * 3600; // Convertir a m³/h
double flowLmin = flow.Value * 60000; // Convertir a L/min
Console.WriteLine($" {flow.Key}:");
Console.WriteLine($" {flow.Value:F6} m³/s = {flowM3h:F2} m³/h = {flowLmin:F1} L/min");
}
Console.WriteLine();
// Presiones
Console.WriteLine("📈 Presiones:");
foreach (var pressure in result.Pressures)
{
double pressureBar = pressure.Value / 100000.0; // Convertir a bar
double pressureKPa = pressure.Value / 1000.0; // Convertir a kPa
Console.WriteLine($" {pressure.Key}:");
Console.WriteLine($" {pressure.Value:F0} Pa = {pressureKPa:F1} kPa = {pressureBar:F3} bar");
}
}
private static void ShowComponentAnalysis(SolutionResult result, PumpHQ pump, Pipe pipe)
{
Console.WriteLine();
Console.WriteLine("🔧 ANÁLISIS DE COMPONENTES:");
Console.WriteLine("═══════════════════════════");
// Análisis de la bomba
if (result.Flows.TryGetValue("Bomba_Principal", out double pumpFlow))
{
double pumpFlowM3h = pumpFlow * 3600;
double pumpHead = pump.Dp(pumpFlow, Fluid.Water20C) / (1000 * 9.81); // Convertir Pa a metros
double pumpPower = pumpFlow * pump.Dp(pumpFlow, Fluid.Water20C) / 1000; // Potencia hidráulica en kW
Console.WriteLine("💨 Bomba:");
Console.WriteLine($" Caudal: {pumpFlowM3h:F1} m³/h");
Console.WriteLine($" Cabeza: {pumpHead:F1} m");
Console.WriteLine($" Potencia hidráulica: {pumpPower:F2} kW");
}
// Análisis de la tubería
if (result.Flows.TryGetValue("Tuberia_Descarga", out double pipeFlow))
{
double pipePressureDrop = pipe.Dp(pipeFlow, Fluid.Water20C);
double pipeHeadLoss = pipePressureDrop / (1000 * 9.81); // Convertir Pa a metros
double velocity = Math.Abs(pipeFlow) / (Math.PI * (pipe.D * pipe.D) / 4.0);
Console.WriteLine();
Console.WriteLine("🚰 Tubería:");
Console.WriteLine($" Caudal: {pipeFlow * 3600:F1} m³/h");
Console.WriteLine($" Velocidad: {velocity:F2} m/s");
Console.WriteLine($" Pérdida de carga: {pipeHeadLoss:F2} m");
Console.WriteLine($" Pérdida de presión: {pipePressureDrop / 1000:F1} kPa");
}
}
/// <summary>
/// Ejemplo de cómo los objetos gráficos osHyd* se integrarían en la simulación
/// </summary>
public static void ShowGraphicObjectsIntegration()
{
Console.WriteLine();
Console.WriteLine("🎨 INTEGRACIÓN CON OBJETOS GRÁFICOS:");
Console.WriteLine("═══════════════════════════════════");
Console.WriteLine();
Console.WriteLine("Los objetos creados (osHydPump, osHydPipe, osHydDischargeTank)");
Console.WriteLine("se integran automáticamente en el sistema de la siguiente manera:");
Console.WriteLine();
Console.WriteLine("1. 📋 Registro automático:");
Console.WriteLine(" - Se registran por reflexión usando NombreCategoria() = \"Hidráulicos\"");
Console.WriteLine(" - Aparecen en la categoría \"Hidráulicos\" del editor");
Console.WriteLine();
Console.WriteLine("2. 🔗 Sistema de conexiones:");
Console.WriteLine(" - Usan ItemsSource<IHydraulicComponent> para filtrar componentes");
Console.WriteLine(" - Similar al sistema Motor/Transporte existente");
Console.WriteLine(" - Conexiones: Id_InletComponent, Id_OutletComponent");
Console.WriteLine();
Console.WriteLine("3. 🚫 No participan en Bepu:");
Console.WriteLine(" - Override Start() sin crear geometrías Bepu");
Console.WriteLine(" - Solo participan en el simulador hidráulico");
Console.WriteLine();
Console.WriteLine("4. 📊 Integración con HydraulicSimulationManager:");
Console.WriteLine(" - Implementan IHydraulicComponent para GetHydraulicNodes()");
Console.WriteLine(" - Implementan GetHydraulicElements() para el solver");
Console.WriteLine(" - Reciben resultados via ApplyHydraulicResults()");
Console.WriteLine();
Console.WriteLine("5. 🎯 Ejemplo de configuración:");
Console.WriteLine(" osHydPump (Nombre=\"Bomba_01\", H0=80m, Q0=36m³/h)");
Console.WriteLine(" ↓ (Id_OutletComponent=\"Tuberia_01\")");
Console.WriteLine(" osHydPipe (Nombre=\"Tuberia_01\", L=50m, D=80mm)");
Console.WriteLine(" ↓ (Id_OutletComponent=\"Tanque_01\")");
Console.WriteLine(" osHydDischargeTank (Nombre=\"Tanque_01\", Area=1m²)");
}
}
}

View File

@ -1,408 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Windows.Media;
using CtrEditor.HydraulicSimulator;
using CtrEditor.ObjetosSim;
using CtrEditor.FuncionesBase;
using HydraulicSimulator.Models;
using Newtonsoft.Json;
using Xceed.Wpf.Toolkit.PropertyGrid.Attributes;
using CommunityToolkit.Mvvm.ComponentModel;
using LibS7Adv;
namespace CtrEditor.ObjetosSim.HydraulicComponents
{
/// <summary>
/// Tanque de descarga hidráulico con cálculo de nivel dinámico
/// </summary>
public partial class osHydDischargeTank : osBase, IHydraulicTank, IosBase
{
#region Properties
private double _area = 1.0; // m²
private double _currentVolume = 0.5; // m³
private double _maxVolume = 2.0; // m³
private double _minVolume = 0.0; // m³
private double _currentHeight = 0.0;
private double _currentPressure = 0.0;
// Propiedades visuales
[ObservableProperty]
[property: Category("🎨 Apariencia")]
[property: Description("Ancho visual del tanque en metros")]
[property: Name("Ancho")]
private float ancho = 0.5f;
[ObservableProperty]
[property: Category("🎨 Apariencia")]
[property: Description("Alto visual del tanque en metros")]
[property: Name("Alto")]
private float alto = 0.8f;
[ObservableProperty]
[property: Category("🎨 Apariencia")]
[property: Description("Color visual del tanque")]
[property: Name("Color")]
private Brush colorButton_oculto = Brushes.LightBlue;
[ObservableProperty]
[property: Category("🎨 Apariencia")]
[property: Description("Ángulo de rotación del tanque en grados")]
[property: Name("Ángulo")]
private float angulo = 0f;
[Category("🔧 Tanque Hidráulico")]
[Description("Área de la base del tanque en m² (alias para compatibilidad)")]
[Name("Área Base (m²)")]
public double Area
{
get => _area;
set => SetProperty(ref _area, Math.Max(0.1, value));
}
[Category("🔧 Tanque Hidráulico")]
[Description("Presión del tanque en Pa")]
[Name("Presión Tanque (Pa)")]
public double TankPressure
{
get => _currentPressure;
set => SetProperty(ref _currentPressure, value);
}
[Category("🔧 Tanque Hidráulico")]
[Description("Nivel actual del tanque en metros")]
[Name("Nivel (m)")]
public double Level
{
get => _currentHeight;
set
{
SetProperty(ref _currentHeight, value);
CurrentVolume = value * Area;
}
}
[Category("🔧 Tanque Hidráulico")]
[Description("Área de la sección transversal del tanque en m²")]
[Name("Área Sección (m²)")]
public double CrossSectionalArea
{
get => _area;
set => SetProperty(ref _area, Math.Max(0.1, value));
}
[Category("🔧 Tanque Hidráulico")]
[Description("Indica si el tanque tiene presión fija")]
[Name("Presión Fija")]
[ReadOnly(true)]
public bool IsFixedPressure => false; // Los tanques de descarga tienen presión calculada
[Category("🔧 Tanque Hidráulico")]
[Description("Volumen actual del tanque en m³")]
[Name("Volumen Actual (m³)")]
public double CurrentVolume
{
get => _currentVolume;
set => SetProperty(ref _currentVolume, Math.Max(MinVolume, Math.Min(MaxVolume, value)));
}
[Category("🔧 Tanque Hidráulico")]
[Description("Volumen máximo del tanque en m³")]
[Name("Volumen Máximo (m³)")]
public double MaxVolume
{
get => _maxVolume;
set => SetProperty(ref _maxVolume, Math.Max(CurrentVolume, value));
}
[Category("🔧 Tanque Hidráulico")]
[Description("Volumen mínimo del tanque en m³")]
[Name("Volumen Mínimo (m³)")]
public double MinVolume
{
get => _minVolume;
set => SetProperty(ref _minVolume, Math.Max(0.0, Math.Min(CurrentVolume, value)));
}
[ObservableProperty]
[property: Category("🔗 Conexiones")]
[property: Description("Componente conectado en la entrada")]
[property: Name("Componente Entrada")]
[property: ItemsSource(typeof(osBaseItemsSource<IHydraulicComponent>))]
private string id_InletComponent = "";
[Category("📊 Estado")]
[Description("Altura actual del líquido en metros")]
[Name("Altura Actual (m)")]
[ReadOnly(true)]
public double CurrentHeight
{
get => _currentHeight;
set => SetProperty(ref _currentHeight, value);
}
[Category("📊 Estado")]
[Description("Presión hidrostática en el fondo del tanque en Pa")]
[Name("Presión Fondo (Pa)")]
[ReadOnly(true)]
public double CurrentPressure
{
get => _currentPressure;
set => SetProperty(ref _currentPressure, value);
}
[Category("📊 Estado")]
[Description("Porcentaje de llenado del tanque")]
[Name("Nivel (%)")]
[ReadOnly(true)]
public double FillPercentage => MaxVolume > 0 ? (CurrentVolume / MaxVolume) * 100.0 : 0.0;
#endregion
#region Component References
[JsonIgnore]
private IHydraulicComponent InletComponent = null;
[JsonIgnore]
private PropertyChangedEventHandler inletPropertyChangedHandler;
[JsonIgnore]
public Action<float, float> ActualizarTamaño { get; set; }
#endregion
#region IHydraulicComponent Implementation
public bool HasHydraulicComponents => true;
public List<HydraulicNodeDefinition> GetHydraulicNodes()
{
var nodes = new List<HydraulicNodeDefinition>();
// El tanque de descarga debe ser un nodo de presión fija para servir como referencia del sistema
// Presión = presión atmosférica + presión hidrostática
double pressureReference = 101325.0 + TankPressure; // Pa (1 atm + presión hidrostática)
nodes.Add(new HydraulicNodeDefinition(Nombre, true, pressureReference, "Tanque de descarga (referencia)"));
Debug.WriteLine($"Tanque {Nombre}: Nodo de presión fija creado - {pressureReference:F0} Pa ({pressureReference/100000:F2} bar)");
return nodes;
}
public List<HydraulicElementDefinition> GetHydraulicElements()
{
// Los tanques no son elementos, son nodos
return new List<HydraulicElementDefinition>();
}
public void UpdateHydraulicProperties()
{
// Actualizar presión basada en el nivel actual
UpdateHeightAndPressure();
}
public void ApplyHydraulicResults(Dictionary<string, double> flows, Dictionary<string, double> pressures)
{
// Actualizar presión del tanque
if (pressures.TryGetValue(Nombre, out double pressure))
{
TankPressure = pressure;
// Calcular nivel basado en la presión hidrostática
Level = pressure / (1000.0 * 9.81); // Asumiendo densidad del agua
}
// Calcular flujo neto hacia el tanque
double netFlow = 0.0;
foreach (var flow in flows)
{
if (flow.Key.EndsWith($"->{Nombre}"))
netFlow += flow.Value; // Flujo entrante
else if (flow.Key.StartsWith($"{Nombre}->"))
netFlow -= flow.Value; // Flujo saliente
}
// Aquí podrías actualizar el volumen en función del tiempo
// Esto se haría en el update loop principal de la simulación
}
#endregion
#region IHydraulicPressureReceiver Implementation
public void SetPressure(double pressure)
{
TankPressure = pressure;
// Actualizar nivel basado en presión hidrostática
Level = pressure / (1000.0 * 9.81);
}
public double GetPressure()
{
return TankPressure;
}
#endregion
#region Connection Management
partial void OnId_InletComponentChanged(string value)
{
if (InletComponent != null && inletPropertyChangedHandler != null)
((INotifyPropertyChanged)InletComponent).PropertyChanged -= inletPropertyChangedHandler;
if (_mainViewModel != null && !string.IsNullOrEmpty(value))
{
InletComponent = (IHydraulicComponent)_mainViewModel.ObjetosSimulables
.FirstOrDefault(s => s is IHydraulicComponent comp &&
((osBase)comp).Nombre == value);
if (InletComponent != null)
{
inletPropertyChangedHandler = (sender, e) =>
{
if (e.PropertyName == nameof(osBase.Nombre))
{
Id_InletComponent = ((osBase)sender).Nombre;
}
};
((INotifyPropertyChanged)InletComponent).PropertyChanged += inletPropertyChangedHandler;
}
}
}
#endregion
#region Volume and Level Calculations
private void UpdateHeightAndPressure()
{
Level = Area > 0 ? CurrentVolume / Area : 0.0;
// Presión hidrostática en el fondo del tanque (densidad del agua = 1000 kg/m³)
TankPressure = 1000.0 * 9.81 * Level;
}
/// <summary>
/// Actualiza el volumen del tanque basado en flujos netos según la documentación
/// </summary>
/// <param name="flows">Diccionario de flujos de la simulación</param>
/// <param name="deltaTime">Intervalo de tiempo en segundos</param>
public void UpdateVolume(Dictionary<string, double> flows, double deltaTime)
{
double netFlow = 0.0;
// Sumar flujos entrantes, restar salientes
foreach (var flow in flows)
{
if (flow.Key.EndsWith($"->{Nombre}"))
netFlow += flow.Value; // Entrante
else if (flow.Key.StartsWith($"{Nombre}->"))
netFlow -= flow.Value; // Saliente
}
// Actualizar volumen
CurrentVolume += netFlow * deltaTime;
CurrentVolume = Math.Max(MinVolume, Math.Min(MaxVolume, CurrentVolume));
// Actualizar altura y presión
UpdateHeightAndPressure();
}
/// <summary>
/// Calcula la presión en el fondo del tanque según la documentación
/// </summary>
/// <param name="density">Densidad del fluido (kg/m³)</param>
/// <returns>Presión hidrostática en Pa</returns>
public double BottomPressure(double density = 1000.0)
{
return density * 9.81 * Level; // ρgh
}
#endregion
#region osBase Implementation
public static string NombreCategoria() => "Componentes Hidráulicos";
public static string NombreClase() => "Tanque de Descarga";
private string nombre = NombreClase();
[Category("Identificación")]
[Description("Nombre identificativo del objeto")]
[Name("Nombre")]
public override string Nombre
{
get => nombre;
set => SetProperty(ref nombre, value);
}
public override void AltoChanged(float value)
{
ActualizarGeometrias();
}
public override void AnchoChanged(float value)
{
ActualizarGeometrias();
}
public void Start()
{
// Los tanques no participan en la simulación física Bepu
// Solo en el sistema hidráulico
UpdateHeightAndPressure();
}
public override void UpdateGeometryStart()
{
ActualizarGeometrias();
}
public override void UpdatePLC(PLCViewModel plc, int elapsedMilliseconds)
{
// Los tanques pueden tener sensores de nivel
// Implementar aquí si es necesario
}
private void ActualizarGeometrias()
{
try
{
// Actualizar geometría visual
if (this.ActualizarTamaño != null)
this.ActualizarTamaño(Ancho, Alto);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"Error actualizando geometría: {ex.Message}");
}
}
public void Inicializar(int valorInicial)
{
UpdateHeightAndPressure();
OnId_InletComponentChanged(Id_InletComponent);
}
public void Disposing()
{
if (InletComponent != null && inletPropertyChangedHandler != null)
((INotifyPropertyChanged)InletComponent).PropertyChanged -= inletPropertyChangedHandler;
}
#endregion
#region Constructor
public osHydDischargeTank()
{
UpdateHeightAndPressure();
}
#endregion
}
}

View File

@ -19,7 +19,7 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents
/// </summary> /// </summary>
public partial class osHydPipe : osBase, IHydraulicPipe, IosBase public partial class osHydPipe : osBase, IHydraulicPipe, IosBase
{ {
#region Properties // Properties
private double _length = 1.0; // metros private double _length = 1.0; // metros
private double _diameter = 0.05; // metros (50mm) private double _diameter = 0.05; // metros (50mm)
@ -120,9 +120,9 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents
set => SetProperty(ref pipeId, value); set => SetProperty(ref pipeId, value);
} }
#endregion
#region Component References
// Component References
[JsonIgnore] [JsonIgnore]
private IHydraulicComponent ComponenteA = null; private IHydraulicComponent ComponenteA = null;
@ -139,15 +139,15 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents
[JsonIgnore] [JsonIgnore]
public Action<float, float> ActualizarTamaño { get; set; } public Action<float, float> ActualizarTamaño { get; set; }
#endregion
#region IHydraulicPipe Implementation
// IHydraulicPipe Implementation
// Ya implementadas las propiedades Length, Diameter, Roughness arriba // Ya implementadas las propiedades Length, Diameter, Roughness arriba
#endregion
#region IHydraulicComponent Implementation
// IHydraulicComponent Implementation
public bool HasHydraulicComponents => true; public bool HasHydraulicComponents => true;
@ -277,9 +277,9 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents
} }
} }
#endregion
#region IHydraulicFlowReceiver Implementation
// IHydraulicFlowReceiver Implementation
public void SetFlow(double flow) public void SetFlow(double flow)
{ {
@ -291,9 +291,9 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents
return CurrentFlow; return CurrentFlow;
} }
#endregion
#region IHydraulicPressureReceiver Implementation
// IHydraulicPressureReceiver Implementation
public void SetPressure(double pressure) public void SetPressure(double pressure)
{ {
@ -305,9 +305,9 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents
return PressureDrop; return PressureDrop;
} }
#endregion
#region Connection Management
// Connection Management
partial void OnId_ComponenteAChanged(string value) partial void OnId_ComponenteAChanged(string value)
{ {
@ -359,9 +359,9 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents
} }
} }
#endregion
#region osBase Implementation
// osBase Implementation
public static string NombreCategoria() => "Componentes Hidráulicos"; public static string NombreCategoria() => "Componentes Hidráulicos";
@ -434,9 +434,9 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents
((INotifyPropertyChanged)ComponenteB).PropertyChanged -= componenteBPropertyChangedHandler; ((INotifyPropertyChanged)ComponenteB).PropertyChanged -= componenteBPropertyChangedHandler;
} }
#endregion
#region Constructor
// Constructor
public osHydPipe() public osHydPipe()
{ {
@ -444,6 +444,6 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents
IsVisFilter = true; // Asegurar que el componente hidráulico sea visible en filtros IsVisFilter = true; // Asegurar que el componente hidráulico sea visible en filtros
} }
#endregion
} }
} }

View File

@ -25,7 +25,7 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents
return "Bomba Hidráulica"; return "Bomba Hidráulica";
} }
#region Properties // Properties
[ObservableProperty] [ObservableProperty]
[property: JsonIgnore] [property: JsonIgnore]
@ -176,9 +176,9 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents
[JsonIgnore] [JsonIgnore]
public double CurrentFlowLMin => CurrentFlow * 60000.0; // m³/s a L/min public double CurrentFlowLMin => CurrentFlow * 60000.0; // m³/s a L/min
#endregion
#region Constructor y Métodos Base
// Constructor y Métodos Base
private string nombre = NombreClase(); private string nombre = NombreClase();
@ -201,9 +201,6 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents
Angulo = 0f; Angulo = 0f;
// Asegurar que el movimiento esté habilitado // Asegurar que el movimiento esté habilitado
Lock_movement = false; Lock_movement = false;
// Inicializar posición por defecto
Left = 0.0f;
Top = 0.0f;
ImageSource_oculta = ImageFromPath("/imagenes/pump_stop.png"); ImageSource_oculta = ImageFromPath("/imagenes/pump_stop.png");
} }
@ -240,9 +237,9 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents
// Los objetos hidráulicos no tienen geometría que limpiar // Los objetos hidráulicos no tienen geometría que limpiar
} }
#endregion
#region IHydraulicComponent Implementation
// IHydraulicComponent Implementation
[JsonIgnore] [JsonIgnore]
public bool HasHydraulicComponents => true; public bool HasHydraulicComponents => true;
@ -343,9 +340,9 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents
OnPropertyChanged(nameof(CurrentPressureBar)); OnPropertyChanged(nameof(CurrentPressureBar));
} }
#endregion
#region IHydraulicFlowReceiver Implementation
// IHydraulicFlowReceiver Implementation
public void SetFlow(double flow) public void SetFlow(double flow)
{ {
@ -357,9 +354,9 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents
return CurrentFlow; return CurrentFlow;
} }
#endregion
#region IHydraulicPressureReceiver Implementation
// IHydraulicPressureReceiver Implementation
public void SetPressure(double pressure) public void SetPressure(double pressure)
{ {
@ -371,9 +368,9 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents
return CurrentPressure; return CurrentPressure;
} }
#endregion
#region Helper Methods
// Helper Methods
/// <summary> /// <summary>
/// Invalida la red hidráulica para forzar reconstrucción /// Invalida la red hidráulica para forzar reconstrucción
@ -387,7 +384,7 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents
} }
} }
#endregion
} }
/// <summary> /// <summary>

View File

@ -41,7 +41,8 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents
private string _connectedInletPipe = ""; private string _connectedInletPipe = "";
private string _connectedOutletPipe = ""; private string _connectedOutletPipe = "";
private double _lastUpdateTime = 0.0; private double _lastUpdateTime = 0.0;
private readonly object _levelLock = new object(); [JsonIgnore]
private object _levelLock = new object();
private PropertyChangedEventHandler? inletPropertyChangedHandler; private PropertyChangedEventHandler? inletPropertyChangedHandler;
private PropertyChangedEventHandler? outletPropertyChangedHandler; private PropertyChangedEventHandler? outletPropertyChangedHandler;
@ -95,13 +96,15 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents
Angulo = 0f; Angulo = 0f;
// Asegurar que el movimiento esté habilitado // Asegurar que el movimiento esté habilitado
Lock_movement = false; Lock_movement = false;
// Inicializar posición por defecto
ImageSource_oculta = ImageFromPath("/imagenes/tank.png"); ImageSource_oculta = ImageFromPath("/imagenes/tank.png");
_currentVolume = _currentLevel * _crossSectionalArea; _currentVolume = _currentLevel * _crossSectionalArea;
_maxVolume = _maxLevel * _crossSectionalArea; _maxVolume = _maxLevel * _crossSectionalArea;
IsVisFilter = true; // Asegurar que el componente hidráulico sea visible en filtros IsVisFilter = true; // Asegurar que el componente hidráulico sea visible en filtros
UpdateTankPressure(); UpdateTankPressure();
// Inicializar el lock para thread safety
EnsureLockInitialized();
// Debug: Confirmar que el constructor se ejecuta // Debug: Confirmar que el constructor se ejecuta
Debug.WriteLine($"osHydTank Constructor: Nombre='{Nombre}', Tamaño={Tamano}, ZIndex={zIndex_fromFrames}, IsVisFilter={IsVisFilter}, Lock_movement={Lock_movement}"); Debug.WriteLine($"osHydTank Constructor: Nombre='{Nombre}', Tamaño={Tamano}, ZIndex={zIndex_fromFrames}, IsVisFilter={IsVisFilter}, Lock_movement={Lock_movement}");
} }
@ -120,6 +123,13 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents
set => SetProperty(ref nombre, value); set => SetProperty(ref nombre, value);
} }
public override void OnMove(float LeftPixels, float TopPixels)
{
// Método básico de movimiento como otros objetos
// Debug para verificar que se llama
Debug.WriteLine($"osHydTank.OnMove: LeftPixels={LeftPixels}, TopPixels={TopPixels}, Left={Left}, Top={Top}");
}
public override void OnResize(float Delta_Width, float Delta_Height) public override void OnResize(float Delta_Width, float Delta_Height)
{ {
Tamano += Delta_Width + Delta_Height; Tamano += Delta_Width + Delta_Height;
@ -280,6 +290,7 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents
get => _currentLevel; get => _currentLevel;
private set private set
{ {
EnsureLockInitialized();
lock (_levelLock) lock (_levelLock)
{ {
var clampedLevel = Math.Max(_minLevel, Math.Min(_maxLevel, value)); var clampedLevel = Math.Max(_minLevel, Math.Min(_maxLevel, value));
@ -768,11 +779,28 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents
// Serialization Support
/// <summary>
/// Se llama antes de la serialización para asegurar que el lock esté disponible
/// </summary>
private void EnsureLockInitialized()
{
if (_levelLock == null)
_levelLock = new object();
}
// osBase Overrides // osBase Overrides
public override void ucLoaded() public override void ucLoaded()
{ {
// Inicialización cuando se carga el UserControl // El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
base.ucLoaded();
// Inicialización específica del tanque hidráulico
UpdateVolumeCalculations(); UpdateVolumeCalculations();
UpdateTankPressure(); UpdateTankPressure();

View File

@ -1,101 +0,0 @@
<UserControl x:Class="CtrEditor.ObjetosSim.HydraulicComponents.ucHydDischargeTank"
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.HydraulicComponents"
mc:Ignorable="d">
<UserControl.DataContext>
<vm:osHydDischargeTank Ancho="0.5" Alto="0.8"/>
</UserControl.DataContext>
<Canvas RenderTransformOrigin="0,0">
<Canvas.RenderTransform>
<TransformGroup>
<ScaleTransform />
<SkewTransform />
<RotateTransform Angle="{Binding Angulo}" />
<TranslateTransform />
</TransformGroup>
</Canvas.RenderTransform>
<!-- Cuerpo del tanque -->
<Rectangle x:Name="rectTank"
Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}"
Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}}"
Fill="{Binding ColorButton_oculto}"
Stroke="Black"
StrokeThickness="2"
RadiusX="5"
RadiusY="5"/>
<!-- Nivel del líquido con altura proporcional al nivel -->
<Rectangle x:Name="rectLevel"
Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}, ConverterParameter=6}"
Canvas.Left="3"
Canvas.Bottom="3">
<Rectangle.Height>
<MultiBinding Converter="{StaticResource LevelToHeightMultiConverter}">
<Binding Path="FillPercentage" />
<Binding Path="Alto" Converter="{StaticResource MeterToPixelConverter}" />
</MultiBinding>
</Rectangle.Height>
<Rectangle.Fill>Blue</Rectangle.Fill>
<Rectangle.Opacity>0.6</Rectangle.Opacity>
</Rectangle>
<!-- Etiqueta con Viewbox para escalado -->
<Viewbox Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}"
Height="20"
Stretch="Uniform"
Canvas.Top="2">
<Label Content="{Binding Nombre}"
VerticalAlignment="Center"
HorizontalAlignment="Center"
FontWeight="Bold"
FontSize="14"
Opacity="0.9"
Foreground="White"/>
</Viewbox>
<!-- Indicador de conexión entrada -->
<Ellipse x:Name="ConnectionIn"
Fill="Green"
Width="8"
Height="8"
Canvas.Left="-4"
Canvas.Bottom="10"/>
<!-- Indicador de nivel en porcentaje -->
<Border x:Name="LevelIndicator"
Background="White"
BorderBrush="Black"
BorderThickness="1"
CornerRadius="2"
Canvas.Right="2"
Canvas.Top="{Binding Alto, Converter={StaticResource MeterToPixelConverter}, ConverterParameter=0.5}">
<TextBlock x:Name="LevelText"
Text="{Binding FillPercentage, StringFormat='{}{0:F0}%'}"
FontSize="6"
Foreground="Black"
Margin="2"/>
</Border>
<!-- Indicador de volumen -->
<Border x:Name="VolumeIndicator"
Background="Yellow"
CornerRadius="2"
Visibility="Collapsed"
Canvas.Bottom="2"
Canvas.Left="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}, ConverterParameter=0.5}">
<TextBlock x:Name="VolumeText"
Text="{Binding CurrentVolume, StringFormat='{}{0:F1}m³'}"
FontSize="5"
Foreground="Black"
Margin="1"/>
</Border>
</Canvas>
</UserControl>

View File

@ -1,70 +0,0 @@
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows;
using CtrEditor.ObjetosSim;
using CtrEditor.FuncionesBase;
namespace CtrEditor.ObjetosSim.HydraulicComponents
{
/// <summary>
/// Interaction logic for ucHydDischargeTank.xaml
/// </summary>
public partial class ucHydDischargeTank : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public int zIndex_fromFrames { get; set; } = 0;
private bool _isHighlighted = false;
public ucHydDischargeTank()
{
InitializeComponent();
this.Loaded += OnLoaded;
this.Unloaded += OnUnloaded;
DataContextChanged += OnDataContextChanged;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
Datos?.ucLoaded();
}
private void OnUnloaded(object sender, RoutedEventArgs e)
{
Datos?.ucUnLoaded();
}
private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (DataContext is osHydDischargeTank tank)
{
Datos = tank;
}
}
#region IDataContainer Implementation
public void Highlight(bool state)
{
_isHighlighted = state;
if (state)
{
rectTank.Stroke = new SolidColorBrush(Colors.Yellow);
rectTank.StrokeThickness = 3;
}
else
{
rectTank.Stroke = new SolidColorBrush(Colors.DarkSlateGray);
rectTank.StrokeThickness = 2;
}
}
#endregion
public ZIndexEnum ZIndex_Base()
{
return ZIndexEnum.Estaticos;
}
}
}

View File

@ -8,6 +8,9 @@
mc:Ignorable="d"> mc:Ignorable="d">
<!-- DataContext se establece desde el objeto padre, no aquí --> <!-- DataContext se establece desde el objeto padre, no aquí -->
<UserControl.DataContext>
<vm:osHydTank />
</UserControl.DataContext>
<Canvas RenderTransformOrigin="0,0"> <Canvas RenderTransformOrigin="0,0">
<Canvas.RenderTransform> <Canvas.RenderTransform>
@ -21,8 +24,7 @@
<!-- Contenedor principal del tanque --> <!-- Contenedor principal del tanque -->
<Grid Width="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}}" <Grid Width="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}}"
Height="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}}" Height="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}}">
Canvas.Left="0" Canvas.Top="0">
<!-- Fondo del tanque (contenedor vacío) --> <!-- Fondo del tanque (contenedor vacío) -->
<Rectangle x:Name="rectTankContainer" <Rectangle x:Name="rectTankContainer"
@ -64,27 +66,6 @@
Stroke="Red" StrokeThickness="1" StrokeDashArray="2,2" Opacity="0.7"/> Stroke="Red" StrokeThickness="1" StrokeDashArray="2,2" Opacity="0.7"/>
</Canvas> </Canvas>
<!-- Indicadores de conexión -->
<!-- Entrada (parte superior) -->
<Ellipse Width="8" Height="8"
Fill="Green"
Stroke="DarkGreen"
StrokeThickness="1"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Margin="2,2,0,0"
Visibility="{Binding HasInletConnection, Converter={StaticResource BooleanToVisibilityConverter}}"/>
<!-- Salida (parte inferior) -->
<Ellipse Width="8" Height="8"
Fill="Blue"
Stroke="DarkBlue"
StrokeThickness="1"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Margin="0,0,2,2"
Visibility="{Binding HasOutletConnection, Converter={StaticResource BooleanToVisibilityConverter}}"/>
<!-- Texto del porcentaje de llenado --> <!-- Texto del porcentaje de llenado -->
<TextBlock Text="{Binding FillPercentage, StringFormat='{}{0:F0}%'}" <TextBlock Text="{Binding FillPercentage, StringFormat='{}{0:F0}%'}"
HorizontalAlignment="Center" HorizontalAlignment="Center"
@ -106,8 +87,7 @@
<!-- Panel de información --> <!-- Panel de información -->
<Grid Canvas.Top="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}, ConverterParameter=1.05}" <Grid Canvas.Top="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}, ConverterParameter=1.05}"
Width="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}}" Width="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}}">
Canvas.Left="0">
<StackPanel Orientation="Horizontal" <StackPanel Orientation="Horizontal"
HorizontalAlignment="Center" HorizontalAlignment="Center"
Margin="0,2,0,0"> Margin="0,2,0,0">

View File

@ -270,6 +270,7 @@ namespace CtrEditor.Serialization
{ {
Formatting = Formatting.Indented, Formatting = Formatting.Indented,
NullValueHandling = NullValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.Include, // Incluir valores por defecto
TypeNameHandling = TypeNameHandling.Auto, TypeNameHandling = TypeNameHandling.Auto,
ObjectCreationHandling = ObjectCreationHandling.Replace, ObjectCreationHandling = ObjectCreationHandling.Replace,
ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor, ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor,

View File

@ -1,39 +0,0 @@
using System;
using System.Collections.Generic;
using CtrEditor.ObjetosSim;
using CtrEditor.ObjetosSim.HydraulicComponents;
using CtrEditor.HydraulicSimulator;
// Este archivo es solo para verificar que la corrección funciona
// Se puede eliminar después de confirmar que todo funciona bien
namespace CtrEditor.Test
{
class TestItemsSource
{
static void TestTypeAssignability()
{
// Crear instancias de objetos hidráulicos
var pipe = new osHydPipe();
var tank = new osHydDischargeTank();
var pump = new osHydPump();
var hydTank = new osHydTank();
// Probar la corrección - debería ser true para todos
Console.WriteLine($"osHydPipe implements IHydraulicComponent: {typeof(IHydraulicComponent).IsAssignableFrom(pipe.GetType())}");
Console.WriteLine($"osHydDischargeTank implements IHydraulicComponent: {typeof(IHydraulicComponent).IsAssignableFrom(tank.GetType())}");
Console.WriteLine($"osHydPump implements IHydraulicComponent: {typeof(IHydraulicComponent).IsAssignableFrom(pump.GetType())}");
Console.WriteLine($"osHydTank implements IHydraulicComponent: {typeof(IHydraulicComponent).IsAssignableFrom(hydTank.GetType())}");
// Verificar NombreCategoria y NombreClase
Console.WriteLine($"osHydTank.NombreCategoria(): {osHydTank.NombreCategoria()}");
Console.WriteLine($"osHydTank.NombreClase(): {osHydTank.NombreClase()}");
// La comparación anterior que fallaba
Console.WriteLine($"osHydPipe.GetType() == typeof(IHydraulicComponent): {pipe.GetType() == typeof(IHydraulicComponent)}");
Console.WriteLine($"osHydDischargeTank.GetType() == typeof(IHydraulicComponent): {tank.GetType() == typeof(IHydraulicComponent)}");
Console.WriteLine($"osHydPump.GetType() == typeof(IHydraulicComponent): {pump.GetType() == typeof(IHydraulicComponent)}");
Console.WriteLine($"osHydTank.GetType() == typeof(IHydraulicComponent): {hydTank.GetType() == typeof(IHydraulicComponent)}");
}
}
}