Agregada base Hidraulica
This commit is contained in:
parent
1e6ad6377e
commit
3f21061524
|
@ -0,0 +1,681 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using HydraulicSimulator.Models;
|
||||||
|
using CtrEditor.ObjetosSim;
|
||||||
|
|
||||||
|
namespace CtrEditor.HydraulicSimulator
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gestiona la simulación hidráulica similar a SimulationManagerBEPU
|
||||||
|
/// </summary>
|
||||||
|
public class HydraulicSimulationManager : IDisposable
|
||||||
|
{
|
||||||
|
#region Properties and Fields
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Red hidráulica principal que contiene todos los nodos y ramas
|
||||||
|
/// </summary>
|
||||||
|
public HydraulicNetwork Network { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Lista de objetos simulables que tienen componentes hidráulicos
|
||||||
|
/// </summary>
|
||||||
|
public List<osBase> HydraulicObjects { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fluido utilizado en la simulación (agua por defecto)
|
||||||
|
/// </summary>
|
||||||
|
public Fluid SimulationFluid { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resultado de la última simulación
|
||||||
|
/// </summary>
|
||||||
|
public SolutionResult LastSolutionResult { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tiempo global de la simulación hidráulica
|
||||||
|
/// </summary>
|
||||||
|
public float GlobalTime { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indica si la simulación hidráulica está habilitada
|
||||||
|
/// </summary>
|
||||||
|
public bool IsHydraulicSimulationEnabled { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parámetros del solver
|
||||||
|
/// </summary>
|
||||||
|
public int MaxIterations { get; set; } = 100;
|
||||||
|
public double Tolerance { get; set; } = 1e-3;
|
||||||
|
public double RelaxationFactor { get; set; } = 0.1;
|
||||||
|
public bool VerboseOutput { get; set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Contador de pasos de simulación para optimizaciones
|
||||||
|
/// </summary>
|
||||||
|
private int _stepCount = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cronómetro para medir tiempo de ejecución
|
||||||
|
/// </summary>
|
||||||
|
private Stopwatch _stopwatch;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Diccionario para mapear IDs de objetos a elementos hidráulicos
|
||||||
|
/// </summary>
|
||||||
|
private Dictionary<string, osBase> _objectMapping;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indica si hay cambios pendientes en la red que requieren reconstrucción
|
||||||
|
/// </summary>
|
||||||
|
private bool _networkNeedsRebuild = true;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Constructor
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor del gestor de simulación hidráulica
|
||||||
|
/// </summary>
|
||||||
|
public HydraulicSimulationManager()
|
||||||
|
{
|
||||||
|
// Inicializar componentes
|
||||||
|
SimulationFluid = Fluid.Water20C; // Agua a 20°C por defecto
|
||||||
|
Network = new HydraulicNetwork(SimulationFluid);
|
||||||
|
HydraulicObjects = new List<osBase>();
|
||||||
|
_objectMapping = new Dictionary<string, osBase>();
|
||||||
|
_stopwatch = new Stopwatch();
|
||||||
|
|
||||||
|
// Inicializar resultado vacío
|
||||||
|
LastSolutionResult = new SolutionResult(false)
|
||||||
|
{
|
||||||
|
ErrorMessage = "Simulación no ejecutada"
|
||||||
|
};
|
||||||
|
|
||||||
|
GlobalTime = 0.0f;
|
||||||
|
|
||||||
|
Debug.WriteLine("HydraulicSimulationManager inicializado");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Object Management
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registra un objeto simulable que tiene componentes hidráulicos
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="hydraulicObject">Objeto con componentes hidráulicos</param>
|
||||||
|
public void RegisterHydraulicObject(osBase hydraulicObject)
|
||||||
|
{
|
||||||
|
if (hydraulicObject == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!HydraulicObjects.Contains(hydraulicObject))
|
||||||
|
{
|
||||||
|
HydraulicObjects.Add(hydraulicObject);
|
||||||
|
_objectMapping[hydraulicObject.Nombre] = hydraulicObject;
|
||||||
|
_networkNeedsRebuild = true;
|
||||||
|
|
||||||
|
Debug.WriteLine($"Objeto hidráulico registrado: {hydraulicObject.Nombre}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Desregistra un objeto simulable hidráulico
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="hydraulicObject">Objeto a desregistrar</param>
|
||||||
|
public void UnregisterHydraulicObject(osBase hydraulicObject)
|
||||||
|
{
|
||||||
|
if (hydraulicObject == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (HydraulicObjects.Remove(hydraulicObject))
|
||||||
|
{
|
||||||
|
_objectMapping.Remove(hydraulicObject.Nombre);
|
||||||
|
_networkNeedsRebuild = true;
|
||||||
|
|
||||||
|
Debug.WriteLine($"Objeto hidráulico desregistrado: {hydraulicObject.Nombre}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Limpia todos los objetos hidráulicos registrados
|
||||||
|
/// </summary>
|
||||||
|
public void ClearHydraulicObjects()
|
||||||
|
{
|
||||||
|
HydraulicObjects.Clear();
|
||||||
|
_objectMapping.Clear();
|
||||||
|
_networkNeedsRebuild = true;
|
||||||
|
|
||||||
|
Debug.WriteLine("Todos los objetos hidráulicos han sido limpiados");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Network Building
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reconstruye la red hidráulica basada en los objetos registrados
|
||||||
|
/// </summary>
|
||||||
|
private void RebuildNetwork()
|
||||||
|
{
|
||||||
|
if (!_networkNeedsRebuild)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Debug.WriteLine("Reconstruyendo red hidráulica...");
|
||||||
|
|
||||||
|
// Crear nueva red
|
||||||
|
Network = new HydraulicNetwork(SimulationFluid);
|
||||||
|
|
||||||
|
// Agregar nodos y elementos basados en los objetos hidráulicos
|
||||||
|
BuildNodesFromObjects();
|
||||||
|
BuildBranchesFromObjects();
|
||||||
|
|
||||||
|
_networkNeedsRebuild = false;
|
||||||
|
|
||||||
|
Debug.WriteLine($"Red reconstruida: {Network.Nodes.Count} nodos, {Network.Branches.Count} ramas");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Construye los nodos de la red basándose en los objetos hidráulicos
|
||||||
|
/// </summary>
|
||||||
|
private void BuildNodesFromObjects()
|
||||||
|
{
|
||||||
|
foreach (var obj in HydraulicObjects)
|
||||||
|
{
|
||||||
|
// Verificar si el objeto implementa la interfaz hidráulica
|
||||||
|
if (obj is IHydraulicComponent hydraulicComponent && hydraulicComponent.HasHydraulicComponents)
|
||||||
|
{
|
||||||
|
// Obtener nodos definidos por el objeto
|
||||||
|
var nodeDefinitions = hydraulicComponent.GetHydraulicNodes();
|
||||||
|
|
||||||
|
foreach (var nodeDef in nodeDefinitions)
|
||||||
|
{
|
||||||
|
// Agregar nodo a la red
|
||||||
|
Network.AddNode(nodeDef.Name, nodeDef.IsFixedPressure ? nodeDef.Pressure : null);
|
||||||
|
|
||||||
|
if (VerboseOutput)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Nodo agregado: {nodeDef.Name} " +
|
||||||
|
$"(Presión fija: {nodeDef.IsFixedPressure}, " +
|
||||||
|
$"Presión: {nodeDef.Pressure?.ToString() ?? "libre"})");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Objeto sin interfaz hidráulica - crear nodos básicos por compatibilidad
|
||||||
|
string nodeName = $"Node_{obj.Nombre}";
|
||||||
|
bool isFixedPressure = DetermineIfFixedPressure(obj);
|
||||||
|
double? pressure = isFixedPressure ? GetObjectPressure(obj) : null;
|
||||||
|
|
||||||
|
Network.AddNode(nodeName, pressure);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Construye las ramas (conexiones) entre nodos
|
||||||
|
/// </summary>
|
||||||
|
private void BuildBranchesFromObjects()
|
||||||
|
{
|
||||||
|
foreach (var obj in HydraulicObjects)
|
||||||
|
{
|
||||||
|
// Verificar si el objeto implementa la interfaz hidráulica
|
||||||
|
if (obj is IHydraulicComponent hydraulicComponent && hydraulicComponent.HasHydraulicComponents)
|
||||||
|
{
|
||||||
|
// Obtener elementos definidos por el objeto
|
||||||
|
var elementDefinitions = hydraulicComponent.GetHydraulicElements();
|
||||||
|
|
||||||
|
foreach (var elemDef in elementDefinitions)
|
||||||
|
{
|
||||||
|
// Crear rama con el elemento
|
||||||
|
var elements = new List<Element> { elemDef.Element };
|
||||||
|
Network.AddBranch(elemDef.FromNode, elemDef.ToNode, elements, elemDef.Name);
|
||||||
|
|
||||||
|
if (VerboseOutput)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Rama agregada: {elemDef.Name} " +
|
||||||
|
$"({elemDef.FromNode} -> {elemDef.ToNode})");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Objeto sin interfaz hidráulica - crear elementos básicos por compatibilidad
|
||||||
|
var elements = CreateHydraulicElementsFromObject(obj);
|
||||||
|
|
||||||
|
if (elements.Any())
|
||||||
|
{
|
||||||
|
string fromNode = GetSourceNode(obj);
|
||||||
|
string toNode = GetTargetNode(obj);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(fromNode) && !string.IsNullOrEmpty(toNode))
|
||||||
|
{
|
||||||
|
Network.AddBranch(fromNode, toNode, elements, $"Branch_{obj.Nombre}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Helper Methods for Object Analysis
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determina si un objeto debe tener presión fija
|
||||||
|
/// </summary>
|
||||||
|
private bool DetermineIfFixedPressure(osBase obj)
|
||||||
|
{
|
||||||
|
// Implementación que será extendida con tipos específicos
|
||||||
|
// Por ejemplo: tanques, descargas atmosféricas = presión fija
|
||||||
|
// Uniones, conexiones = presión libre
|
||||||
|
|
||||||
|
return false; // Por defecto, presión libre
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Obtiene la presión de un objeto (si tiene presión fija)
|
||||||
|
/// </summary>
|
||||||
|
private double GetObjectPressure(osBase obj)
|
||||||
|
{
|
||||||
|
// Implementación que será extendida
|
||||||
|
return 0.0; // Por defecto, presión atmosférica
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Crea elementos hidráulicos basándose en un objeto
|
||||||
|
/// </summary>
|
||||||
|
private List<Element> CreateHydraulicElementsFromObject(osBase obj)
|
||||||
|
{
|
||||||
|
var elements = new List<Element>();
|
||||||
|
|
||||||
|
// Implementación que será extendida con tipos específicos
|
||||||
|
// Por ejemplo:
|
||||||
|
// - Si es una bomba: crear PumpHQ
|
||||||
|
// - Si es una tubería: crear Pipe
|
||||||
|
// - Si es una válvula: crear ValveKv
|
||||||
|
|
||||||
|
return elements;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Obtiene el nodo fuente de un objeto
|
||||||
|
/// </summary>
|
||||||
|
private string GetSourceNode(osBase obj)
|
||||||
|
{
|
||||||
|
// Implementación que será extendida
|
||||||
|
return $"Node_{obj.Nombre}_In";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Obtiene el nodo destino de un objeto
|
||||||
|
/// </summary>
|
||||||
|
private string GetTargetNode(osBase obj)
|
||||||
|
{
|
||||||
|
// Implementación que será extendida
|
||||||
|
return $"Node_{obj.Nombre}_Out";
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Simulation Step
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ejecuta un paso de simulación hidráulica (equivalente al Step de BEPU)
|
||||||
|
/// </summary>
|
||||||
|
public void Step(float deltaTime = 0.016f) // ~60 FPS por defecto
|
||||||
|
{
|
||||||
|
if (!IsHydraulicSimulationEnabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_stopwatch.Restart();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Actualizar tiempo global
|
||||||
|
GlobalTime += deltaTime;
|
||||||
|
_stepCount++;
|
||||||
|
|
||||||
|
// Reconstruir red si es necesario
|
||||||
|
RebuildNetwork();
|
||||||
|
|
||||||
|
// Solo resolver si tenemos objetos hidráulicos
|
||||||
|
if (HydraulicObjects.Count > 0 && Network.Branches.Count > 0)
|
||||||
|
{
|
||||||
|
// Actualizar propiedades de objetos antes de resolver
|
||||||
|
UpdateObjectProperties();
|
||||||
|
|
||||||
|
// Resolver la red hidráulica
|
||||||
|
LastSolutionResult = Network.Solve(
|
||||||
|
MaxIterations,
|
||||||
|
Tolerance,
|
||||||
|
RelaxationFactor,
|
||||||
|
VerboseOutput
|
||||||
|
);
|
||||||
|
|
||||||
|
if (LastSolutionResult.Converged)
|
||||||
|
{
|
||||||
|
// Aplicar resultados a los objetos
|
||||||
|
ApplyResultsToObjects();
|
||||||
|
}
|
||||||
|
else if (VerboseOutput)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Simulación hidráulica no convergió: {LastSolutionResult.ErrorMessage}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Sin objetos hidráulicos, resultado exitoso pero vacío
|
||||||
|
LastSolutionResult = new SolutionResult(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LastSolutionResult = new SolutionResult(false)
|
||||||
|
{
|
||||||
|
ErrorMessage = $"Error en simulación hidráulica: {ex.Message}"
|
||||||
|
};
|
||||||
|
|
||||||
|
Debug.WriteLine($"Error en HydraulicSimulationManager.Step: {ex}");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_stopwatch.Stop();
|
||||||
|
|
||||||
|
if (VerboseOutput && _stepCount % 60 == 0) // Log cada segundo aprox
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Simulación hidráulica - Paso {_stepCount}: {_stopwatch.ElapsedMilliseconds}ms, " +
|
||||||
|
$"Objetos: {HydraulicObjects.Count}, Convergió: {LastSolutionResult.Converged}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Actualiza las propiedades de los objetos antes de resolver la red
|
||||||
|
/// </summary>
|
||||||
|
private void UpdateObjectProperties()
|
||||||
|
{
|
||||||
|
foreach (var obj in HydraulicObjects)
|
||||||
|
{
|
||||||
|
// Aquí se implementará la actualización de propiedades específicas
|
||||||
|
// Por ejemplo: estado de válvulas, velocidad de bombas, etc.
|
||||||
|
UpdateObjectHydraulicProperties(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Actualiza las propiedades hidráulicas específicas de un objeto
|
||||||
|
/// </summary>
|
||||||
|
private void UpdateObjectHydraulicProperties(osBase obj)
|
||||||
|
{
|
||||||
|
// Actualizar propiedades basándose en las interfaces implementadas
|
||||||
|
if (obj is IHydraulicComponent hydraulicComponent && hydraulicComponent.HasHydraulicComponents)
|
||||||
|
{
|
||||||
|
// Llamar al método de actualización del objeto
|
||||||
|
hydraulicComponent.UpdateHydraulicProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actualización específica por tipo de objeto
|
||||||
|
if (obj is IHydraulicPump pump)
|
||||||
|
{
|
||||||
|
// Verificar estado de la bomba, velocidad, etc.
|
||||||
|
UpdatePumpProperties(pump);
|
||||||
|
}
|
||||||
|
else if (obj is IHydraulicValve valve)
|
||||||
|
{
|
||||||
|
// Verificar apertura de la válvula
|
||||||
|
UpdateValveProperties(valve);
|
||||||
|
}
|
||||||
|
else if (obj is IHydraulicTank tank)
|
||||||
|
{
|
||||||
|
// Actualizar nivel y presión del tanque
|
||||||
|
UpdateTankProperties(tank);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Actualiza propiedades específicas de bombas
|
||||||
|
/// </summary>
|
||||||
|
private void UpdatePumpProperties(IHydraulicPump pump)
|
||||||
|
{
|
||||||
|
// Aquí se pueden hacer validaciones y ajustes específicos de bombas
|
||||||
|
// Por ejemplo, limitar velocidad, verificar estado de operación, etc.
|
||||||
|
|
||||||
|
if (pump.SpeedRatio < 0.0) pump.SpeedRatio = 0.0;
|
||||||
|
if (pump.SpeedRatio > 1.0) pump.SpeedRatio = 1.0;
|
||||||
|
|
||||||
|
if (VerboseOutput)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Bomba {pump.GetType().Name}: Velocidad={pump.SpeedRatio:F2}, " +
|
||||||
|
$"Funcionando={pump.IsRunning}, Dirección={pump.PumpDirection}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Actualiza propiedades específicas de válvulas
|
||||||
|
/// </summary>
|
||||||
|
private void UpdateValveProperties(IHydraulicValve valve)
|
||||||
|
{
|
||||||
|
// Validar apertura de válvula
|
||||||
|
if (valve.Opening < 0.0) valve.Opening = 0.0;
|
||||||
|
if (valve.Opening > 1.0) valve.Opening = 1.0;
|
||||||
|
|
||||||
|
if (VerboseOutput)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Válvula {valve.GetType().Name}: Apertura={valve.Opening:F2}, " +
|
||||||
|
$"Cerrada={valve.IsClosed}, Abierta={valve.IsFullyOpen}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Actualiza propiedades específicas de tanques
|
||||||
|
/// </summary>
|
||||||
|
private void UpdateTankProperties(IHydraulicTank tank)
|
||||||
|
{
|
||||||
|
// Calcular presión basada en nivel si no es presión fija
|
||||||
|
if (!tank.IsFixedPressure)
|
||||||
|
{
|
||||||
|
// P = ρgh + P_atmosferica
|
||||||
|
double pressureFromLevel = SimulationFluid.Rho * 9.80665 * tank.Level; // + presión atmosférica
|
||||||
|
tank.TankPressure = pressureFromLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (VerboseOutput)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Tanque {tank.GetType().Name}: Nivel={tank.Level:F2}m, " +
|
||||||
|
$"Presión={tank.TankPressure:F0}Pa, PresionFija={tank.IsFixedPressure}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Aplica los resultados de la simulación a los objetos
|
||||||
|
/// </summary>
|
||||||
|
private void ApplyResultsToObjects()
|
||||||
|
{
|
||||||
|
foreach (var obj in HydraulicObjects)
|
||||||
|
{
|
||||||
|
// Aplicar caudales y presiones calculados al objeto
|
||||||
|
ApplyResultsToObject(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Aplica los resultados a un objeto específico
|
||||||
|
/// </summary>
|
||||||
|
private void ApplyResultsToObject(osBase obj)
|
||||||
|
{
|
||||||
|
// Aplicar resultados usando las interfaces hidráulicas
|
||||||
|
if (obj is IHydraulicComponent hydraulicComponent && hydraulicComponent.HasHydraulicComponents)
|
||||||
|
{
|
||||||
|
// Llamar al método de aplicación de resultados del objeto
|
||||||
|
hydraulicComponent.ApplyHydraulicResults(LastSolutionResult.Flows, LastSolutionResult.Pressures);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aplicación específica por tipo de interfaz
|
||||||
|
if (obj is IHydraulicFlowReceiver flowReceiver)
|
||||||
|
{
|
||||||
|
// Buscar caudal asociado al objeto
|
||||||
|
string branchName = FindBranchNameForObject(obj);
|
||||||
|
if (!string.IsNullOrEmpty(branchName) && LastSolutionResult.Flows.ContainsKey(branchName))
|
||||||
|
{
|
||||||
|
double flow = LastSolutionResult.Flows[branchName];
|
||||||
|
flowReceiver.SetFlow(flow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj is IHydraulicPressureReceiver pressureReceiver)
|
||||||
|
{
|
||||||
|
// Buscar presión asociada al objeto
|
||||||
|
string nodeName = FindNodeNameForObject(obj);
|
||||||
|
if (!string.IsNullOrEmpty(nodeName) && LastSolutionResult.Pressures.ContainsKey(nodeName))
|
||||||
|
{
|
||||||
|
double pressure = LastSolutionResult.Pressures[nodeName];
|
||||||
|
pressureReceiver.SetPressure(pressure);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Encuentra el nombre de rama asociado a un objeto
|
||||||
|
/// </summary>
|
||||||
|
private string FindBranchNameForObject(osBase obj)
|
||||||
|
{
|
||||||
|
// Buscar en las ramas de la red
|
||||||
|
foreach (var branch in Network.Branches)
|
||||||
|
{
|
||||||
|
if (branch.Name.Contains(obj.Nombre))
|
||||||
|
{
|
||||||
|
return branch.Name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback a nombre por defecto
|
||||||
|
return $"Branch_{obj.Nombre}";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Encuentra el nombre de nodo asociado a un objeto
|
||||||
|
/// </summary>
|
||||||
|
private string FindNodeNameForObject(osBase obj)
|
||||||
|
{
|
||||||
|
// Buscar en los nodos de la red
|
||||||
|
foreach (var nodeKvp in Network.Nodes)
|
||||||
|
{
|
||||||
|
if (nodeKvp.Key.Contains(obj.Nombre))
|
||||||
|
{
|
||||||
|
return nodeKvp.Key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback a nombre por defecto
|
||||||
|
return $"Node_{obj.Nombre}";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Establece el caudal en un objeto
|
||||||
|
/// </summary>
|
||||||
|
private void SetObjectFlow(osBase obj, double flow)
|
||||||
|
{
|
||||||
|
if (obj is IHydraulicFlowReceiver flowReceiver)
|
||||||
|
{
|
||||||
|
flowReceiver.SetFlow(flow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Establece la presión en un objeto
|
||||||
|
/// </summary>
|
||||||
|
private void SetObjectPressure(osBase obj, double pressure)
|
||||||
|
{
|
||||||
|
if (obj is IHydraulicPressureReceiver pressureReceiver)
|
||||||
|
{
|
||||||
|
pressureReceiver.SetPressure(pressure);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Public Methods
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fuerza la reconstrucción de la red en el próximo paso
|
||||||
|
/// </summary>
|
||||||
|
public void InvalidateNetwork()
|
||||||
|
{
|
||||||
|
_networkNeedsRebuild = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Obtiene estadísticas de la simulación
|
||||||
|
/// </summary>
|
||||||
|
public string GetSimulationStats()
|
||||||
|
{
|
||||||
|
return $"Objetos hidráulicos: {HydraulicObjects.Count}\n" +
|
||||||
|
$"Nodos: {Network.Nodes.Count}\n" +
|
||||||
|
$"Ramas: {Network.Branches.Count}\n" +
|
||||||
|
$"Última simulación convergió: {LastSolutionResult.Converged}\n" +
|
||||||
|
$"Iteraciones: {LastSolutionResult.Iterations}\n" +
|
||||||
|
$"Residual: {LastSolutionResult.Residual:E3}\n" +
|
||||||
|
$"Tiempo global: {GlobalTime:F2}s\n" +
|
||||||
|
$"Pasos: {_stepCount}";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reinicia la simulación
|
||||||
|
/// </summary>
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
GlobalTime = 0.0f;
|
||||||
|
_stepCount = 0;
|
||||||
|
_networkNeedsRebuild = true;
|
||||||
|
|
||||||
|
// Reinicializar resultado
|
||||||
|
LastSolutionResult = new SolutionResult(false)
|
||||||
|
{
|
||||||
|
ErrorMessage = "Simulación reiniciada"
|
||||||
|
};
|
||||||
|
|
||||||
|
Debug.WriteLine("Simulación hidráulica reiniciada");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region IDisposable
|
||||||
|
|
||||||
|
private bool _disposed = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Libera recursos del gestor de simulación
|
||||||
|
/// </summary>
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (!_disposed)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
// Limpiar recursos gestionados
|
||||||
|
HydraulicObjects?.Clear();
|
||||||
|
_objectMapping?.Clear();
|
||||||
|
Network = null;
|
||||||
|
_stopwatch?.Stop();
|
||||||
|
|
||||||
|
Debug.WriteLine("HydraulicSimulationManager disposed");
|
||||||
|
}
|
||||||
|
|
||||||
|
_disposed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,301 @@
|
||||||
|
using HydraulicSimulator.Models;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace CtrEditor.HydraulicSimulator
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Interfaz base para objetos que tienen componentes hidráulicos
|
||||||
|
/// </summary>
|
||||||
|
public interface IHydraulicComponent
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Indica si este objeto tiene componentes hidráulicos activos
|
||||||
|
/// </summary>
|
||||||
|
bool HasHydraulicComponents { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Obtiene los nodos hidráulicos asociados a este objeto
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Lista de definiciones de nodos</returns>
|
||||||
|
List<HydraulicNodeDefinition> GetHydraulicNodes();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Obtiene los elementos hidráulicos asociados a este objeto
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Lista de definiciones de elementos hidráulicos</returns>
|
||||||
|
List<HydraulicElementDefinition> GetHydraulicElements();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Actualiza las propiedades hidráulicas del objeto antes de la simulación
|
||||||
|
/// </summary>
|
||||||
|
void UpdateHydraulicProperties();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Aplica los resultados de la simulación hidráulica al objeto
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="flows">Caudales calculados</param>
|
||||||
|
/// <param name="pressures">Presiones calculadas</param>
|
||||||
|
void ApplyHydraulicResults(Dictionary<string, double> flows, Dictionary<string, double> pressures);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Interfaz para objetos que pueden recibir información de caudal
|
||||||
|
/// </summary>
|
||||||
|
public interface IHydraulicFlowReceiver
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Establece el caudal actual del objeto
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="flow">Caudal en m³/s</param>
|
||||||
|
void SetFlow(double flow);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Obtiene el caudal actual del objeto
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Caudal en m³/s</returns>
|
||||||
|
double GetFlow();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Interfaz para objetos que pueden recibir información de presión
|
||||||
|
/// </summary>
|
||||||
|
public interface IHydraulicPressureReceiver
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Establece la presión actual del objeto
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pressure">Presión en Pa</param>
|
||||||
|
void SetPressure(double pressure);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Obtiene la presión actual del objeto
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Presión en Pa</returns>
|
||||||
|
double GetPressure();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Interfaz para objetos que son bombas hidráulicas
|
||||||
|
/// </summary>
|
||||||
|
public interface IHydraulicPump : IHydraulicComponent, IHydraulicFlowReceiver, IHydraulicPressureReceiver
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Cabeza de la bomba a caudal cero (m)
|
||||||
|
/// </summary>
|
||||||
|
double PumpHead { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Caudal máximo de la bomba (m³/s)
|
||||||
|
/// </summary>
|
||||||
|
double MaxFlow { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Velocidad relativa de la bomba (0.0 a 1.0)
|
||||||
|
/// </summary>
|
||||||
|
double SpeedRatio { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indica si la bomba está encendida
|
||||||
|
/// </summary>
|
||||||
|
bool IsRunning { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Dirección de la bomba (+1 o -1)
|
||||||
|
/// </summary>
|
||||||
|
int PumpDirection { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Interfaz para objetos que son válvulas hidráulicas
|
||||||
|
/// </summary>
|
||||||
|
public interface IHydraulicValve : IHydraulicComponent, IHydraulicFlowReceiver, IHydraulicPressureReceiver
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Coeficiente Kv de la válvula completamente abierta (m³/h/√bar)
|
||||||
|
/// </summary>
|
||||||
|
double KvFull { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Apertura actual de la válvula (0.0 a 1.0)
|
||||||
|
/// </summary>
|
||||||
|
double Opening { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indica si la válvula está completamente cerrada
|
||||||
|
/// </summary>
|
||||||
|
bool IsClosed { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indica si la válvula está completamente abierta
|
||||||
|
/// </summary>
|
||||||
|
bool IsFullyOpen { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Interfaz para objetos que son tuberías hidráulicas
|
||||||
|
/// </summary>
|
||||||
|
public interface IHydraulicPipe : IHydraulicComponent, IHydraulicFlowReceiver, IHydraulicPressureReceiver
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Longitud de la tubería (m)
|
||||||
|
/// </summary>
|
||||||
|
double Length { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Diámetro interno de la tubería (m)
|
||||||
|
/// </summary>
|
||||||
|
double Diameter { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rugosidad absoluta de la tubería (m)
|
||||||
|
/// </summary>
|
||||||
|
double Roughness { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Interfaz para objetos que son tanques hidráulicos
|
||||||
|
/// </summary>
|
||||||
|
public interface IHydraulicTank : IHydraulicComponent, IHydraulicPressureReceiver
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Presión del tanque (Pa)
|
||||||
|
/// </summary>
|
||||||
|
double TankPressure { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Nivel actual del tanque (m)
|
||||||
|
/// </summary>
|
||||||
|
double Level { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Área de la sección transversal del tanque (m²)
|
||||||
|
/// </summary>
|
||||||
|
double CrossSectionalArea { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indica si el tanque tiene presión fija
|
||||||
|
/// </summary>
|
||||||
|
bool IsFixedPressure { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Definición de un nodo hidráulico
|
||||||
|
/// </summary>
|
||||||
|
public class HydraulicNodeDefinition
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Nombre único del nodo
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indica si el nodo tiene presión fija
|
||||||
|
/// </summary>
|
||||||
|
public bool IsFixedPressure { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Presión del nodo (Pa), si es de presión fija
|
||||||
|
/// </summary>
|
||||||
|
public double? Pressure { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Descripción del nodo
|
||||||
|
/// </summary>
|
||||||
|
public string Description { get; set; } = "";
|
||||||
|
|
||||||
|
public HydraulicNodeDefinition(string name, bool isFixedPressure = false, double? pressure = null, string description = "")
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
IsFixedPressure = isFixedPressure;
|
||||||
|
Pressure = pressure;
|
||||||
|
Description = description;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Definición de un elemento hidráulico
|
||||||
|
/// </summary>
|
||||||
|
public class HydraulicElementDefinition
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Nombre único del elemento
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Nodo de origen
|
||||||
|
/// </summary>
|
||||||
|
public string FromNode { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Nodo de destino
|
||||||
|
/// </summary>
|
||||||
|
public string ToNode { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Elemento hidráulico
|
||||||
|
/// </summary>
|
||||||
|
public Element Element { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Descripción del elemento
|
||||||
|
/// </summary>
|
||||||
|
public string Description { get; set; } = "";
|
||||||
|
|
||||||
|
public HydraulicElementDefinition(string name, string fromNode, string toNode, Element element, string description = "")
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
FromNode = fromNode;
|
||||||
|
ToNode = toNode;
|
||||||
|
Element = element;
|
||||||
|
Description = description;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tipos de elementos hidráulicos
|
||||||
|
/// </summary>
|
||||||
|
public enum HydraulicElementType
|
||||||
|
{
|
||||||
|
Pipe,
|
||||||
|
Pump,
|
||||||
|
Valve,
|
||||||
|
MinorLoss,
|
||||||
|
Tank,
|
||||||
|
Junction
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Configuración de un elemento hidráulico
|
||||||
|
/// </summary>
|
||||||
|
public class HydraulicElementConfiguration
|
||||||
|
{
|
||||||
|
public HydraulicElementType Type { get; set; }
|
||||||
|
public Dictionary<string, object> Parameters { get; set; } = new Dictionary<string, object>();
|
||||||
|
|
||||||
|
public HydraulicElementConfiguration(HydraulicElementType type)
|
||||||
|
{
|
||||||
|
Type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Establece un parámetro de configuración
|
||||||
|
/// </summary>
|
||||||
|
public void SetParameter(string key, object value)
|
||||||
|
{
|
||||||
|
Parameters[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Obtiene un parámetro de configuración
|
||||||
|
/// </summary>
|
||||||
|
public T GetParameter<T>(string key, T defaultValue = default(T))
|
||||||
|
{
|
||||||
|
if (Parameters.ContainsKey(key) && Parameters[key] is T)
|
||||||
|
{
|
||||||
|
return (T)Parameters[key];
|
||||||
|
}
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,6 +11,7 @@ using System.IO;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using CtrEditor.Simulacion;
|
using CtrEditor.Simulacion;
|
||||||
|
using CtrEditor.HydraulicSimulator; // Nuevo using para el simulador hidráulico
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
@ -58,6 +59,7 @@ namespace CtrEditor
|
||||||
private bool Debug_SimulacionCreado = false;
|
private bool Debug_SimulacionCreado = false;
|
||||||
|
|
||||||
public SimulationManagerBEPU simulationManager = new SimulationManagerBEPU();
|
public SimulationManagerBEPU simulationManager = new SimulationManagerBEPU();
|
||||||
|
public HydraulicSimulationManager hydraulicSimulationManager = new HydraulicSimulationManager();
|
||||||
|
|
||||||
private readonly System.Timers.Timer _timerSimulacion; // Cambiado a System.Timers.Timer para mejor precisión
|
private readonly System.Timers.Timer _timerSimulacion; // Cambiado a System.Timers.Timer para mejor precisión
|
||||||
private readonly System.Timers.Timer _timerPLCUpdate; // Cambiado a System.Timers.Timer para mejor precisión
|
private readonly System.Timers.Timer _timerPLCUpdate; // Cambiado a System.Timers.Timer para mejor precisión
|
||||||
|
@ -737,6 +739,9 @@ namespace CtrEditor
|
||||||
{
|
{
|
||||||
_stateSerializer.LoadState(SelectedImage);
|
_stateSerializer.LoadState(SelectedImage);
|
||||||
|
|
||||||
|
// Registrar objetos hidráulicos cargados
|
||||||
|
RegisterLoadedHydraulicObjects();
|
||||||
|
|
||||||
// Aplicar los filtros actuales a los objetos recién cargados
|
// Aplicar los filtros actuales a los objetos recién cargados
|
||||||
if (MainWindow?.VisFilter?.FilterViewModel != null)
|
if (MainWindow?.VisFilter?.FilterViewModel != null)
|
||||||
{
|
{
|
||||||
|
@ -787,6 +792,10 @@ namespace CtrEditor
|
||||||
{
|
{
|
||||||
// Añadir el nuevo osBase a la colección de objetos simulables
|
// Añadir el nuevo osBase a la colección de objetos simulables
|
||||||
ObjetosSimulables.Add(NuevoOsBase);
|
ObjetosSimulables.Add(NuevoOsBase);
|
||||||
|
|
||||||
|
// Registrar en el simulador hidráulico si tiene componentes hidráulicos
|
||||||
|
RegisterHydraulicObjectIfNeeded(NuevoOsBase);
|
||||||
|
|
||||||
HasUnsavedChanges = true;
|
HasUnsavedChanges = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -820,6 +829,9 @@ namespace CtrEditor
|
||||||
{
|
{
|
||||||
if (osObjeto != null && ObjetosSimulables.Contains(osObjeto))
|
if (osObjeto != null && ObjetosSimulables.Contains(osObjeto))
|
||||||
{
|
{
|
||||||
|
// Desregistrar del simulador hidráulico si estaba registrado
|
||||||
|
UnregisterHydraulicObjectIfNeeded(osObjeto);
|
||||||
|
|
||||||
ObjetosSimulables.Remove(osObjeto);
|
ObjetosSimulables.Remove(osObjeto);
|
||||||
if (osObjeto.VisualRepresentation != null)
|
if (osObjeto.VisualRepresentation != null)
|
||||||
MainWindow.EliminarUserControlDelCanvas(osObjeto.VisualRepresentation);
|
MainWindow.EliminarUserControlDelCanvas(osObjeto.VisualRepresentation);
|
||||||
|
@ -925,6 +937,10 @@ namespace CtrEditor
|
||||||
NuevoObjetoDuplicado.Top += OffsetY;
|
NuevoObjetoDuplicado.Top += OffsetY;
|
||||||
ObjetosSimulables.Add(NuevoObjetoDuplicado);
|
ObjetosSimulables.Add(NuevoObjetoDuplicado);
|
||||||
CrearUserControlDesdeObjetoSimulable(NuevoObjetoDuplicado);
|
CrearUserControlDesdeObjetoSimulable(NuevoObjetoDuplicado);
|
||||||
|
|
||||||
|
// Registrar en el simulador hidráulico si tiene componentes hidráulicos
|
||||||
|
RegisterHydraulicObjectIfNeeded(NuevoObjetoDuplicado);
|
||||||
|
|
||||||
HasUnsavedChanges = true;
|
HasUnsavedChanges = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1113,6 +1129,9 @@ namespace CtrEditor
|
||||||
foreach (var objetoSimulable in ObjetosSimulables)
|
foreach (var objetoSimulable in ObjetosSimulables)
|
||||||
objetoSimulable.SimulationStop();
|
objetoSimulable.SimulationStop();
|
||||||
|
|
||||||
|
// Reiniciar simulador hidráulico al detener la simulación
|
||||||
|
ResetHydraulicSimulation();
|
||||||
|
|
||||||
if (Debug_SimulacionCreado)
|
if (Debug_SimulacionCreado)
|
||||||
{
|
{
|
||||||
Debug_SimulacionCreado = false;
|
Debug_SimulacionCreado = false;
|
||||||
|
@ -1158,7 +1177,11 @@ namespace CtrEditor
|
||||||
objetoSimulable.UpdateGeometryStep();
|
objetoSimulable.UpdateGeometryStep();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ejecutar simulación física BEPU
|
||||||
simulationManager.Step();
|
simulationManager.Step();
|
||||||
|
|
||||||
|
// Ejecutar simulación hidráulica
|
||||||
|
hydraulicSimulationManager.Step((float)(timeBetweenCalls / 1000.0)); // Convertir ms a segundos
|
||||||
|
|
||||||
// ✅ NUEVO: Solo crear la copia si hay objetos para procesar
|
// ✅ NUEVO: Solo crear la copia si hay objetos para procesar
|
||||||
if (ObjetosSimulables?.Count > 0)
|
if (ObjetosSimulables?.Count > 0)
|
||||||
|
@ -1642,6 +1665,76 @@ namespace CtrEditor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region Hydraulic Simulation Management
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registra un objeto en el simulador hidráulico si tiene componentes hidráulicos
|
||||||
|
/// </summary>
|
||||||
|
private void RegisterHydraulicObjectIfNeeded(osBase obj)
|
||||||
|
{
|
||||||
|
if (obj is IHydraulicComponent hydraulicComponent && hydraulicComponent.HasHydraulicComponents)
|
||||||
|
{
|
||||||
|
hydraulicSimulationManager.RegisterHydraulicObject(obj);
|
||||||
|
Debug.WriteLine($"Objeto hidráulico registrado: {obj.Nombre}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Desregistra un objeto del simulador hidráulico si estaba registrado
|
||||||
|
/// </summary>
|
||||||
|
private void UnregisterHydraulicObjectIfNeeded(osBase obj)
|
||||||
|
{
|
||||||
|
// Desregistrar independientemente de si implementa la interfaz actualmente
|
||||||
|
// (podría haber cambiado desde que se registró)
|
||||||
|
hydraulicSimulationManager.UnregisterHydraulicObject(obj);
|
||||||
|
Debug.WriteLine($"Objeto desregistrado del simulador hidráulico: {obj.Nombre}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reinicia el simulador hidráulico
|
||||||
|
/// </summary>
|
||||||
|
public void ResetHydraulicSimulation()
|
||||||
|
{
|
||||||
|
hydraulicSimulationManager.Reset();
|
||||||
|
Debug.WriteLine("Simulador hidráulico reiniciado");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Obtiene estadísticas del simulador hidráulico
|
||||||
|
/// </summary>
|
||||||
|
public string GetHydraulicSimulationStats()
|
||||||
|
{
|
||||||
|
return hydraulicSimulationManager.GetSimulationStats();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Habilita/deshabilita la simulación hidráulica
|
||||||
|
/// </summary>
|
||||||
|
public void SetHydraulicSimulationEnabled(bool enabled)
|
||||||
|
{
|
||||||
|
hydraulicSimulationManager.IsHydraulicSimulationEnabled = enabled;
|
||||||
|
Debug.WriteLine($"Simulación hidráulica {(enabled ? "habilitada" : "deshabilitada")}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registra todos los objetos hidráulicos existentes después de cargar un proyecto
|
||||||
|
/// </summary>
|
||||||
|
private void RegisterLoadedHydraulicObjects()
|
||||||
|
{
|
||||||
|
// Limpiar registros previos
|
||||||
|
hydraulicSimulationManager.ClearHydraulicObjects();
|
||||||
|
|
||||||
|
// Registrar todos los objetos cargados que tengan componentes hidráulicos
|
||||||
|
foreach (var obj in ObjetosSimulables)
|
||||||
|
{
|
||||||
|
RegisterHydraulicObjectIfNeeded(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.WriteLine($"Registrados {hydraulicSimulationManager.HydraulicObjects.Count} objetos hidráulicos tras cargar proyecto");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SimulationData
|
public class SimulationData
|
||||||
|
|
|
@ -0,0 +1,469 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Windows.Media;
|
||||||
|
using CtrEditor.HydraulicSimulator;
|
||||||
|
using CtrEditor.ObjetosSim;
|
||||||
|
using CtrEditor.FuncionesBase;
|
||||||
|
using CtrEditor.Simulacion;
|
||||||
|
using HydraulicSimulator.Models;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Xceed.Wpf.Toolkit.PropertyGrid.Attributes;
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
|
||||||
|
namespace CtrEditor.ObjetosSim.HydraulicComponents
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Ejemplo de bomba hidráulica que implementa las interfaces del simulador hidráulico
|
||||||
|
/// </summary>
|
||||||
|
public partial class osPumpExample : osBase, IHydraulicPump, IosBase
|
||||||
|
{
|
||||||
|
private simPumpExample SimGeometria;
|
||||||
|
#region Properties
|
||||||
|
|
||||||
|
private double _pumpHead = 50.0; // metros
|
||||||
|
private double _maxFlow = 0.01; // m³/s (36 m³/h)
|
||||||
|
private double _speedRatio = 1.0;
|
||||||
|
private bool _isRunning = true;
|
||||||
|
private int _pumpDirection = 1;
|
||||||
|
private double _currentFlow = 0.0;
|
||||||
|
private double _currentPressure = 0.0;
|
||||||
|
|
||||||
|
// Propiedades físicas y visuales
|
||||||
|
[ObservableProperty]
|
||||||
|
[property: Category("🎨 Apariencia")]
|
||||||
|
[property: Description("Ancho de la bomba en metros")]
|
||||||
|
[property: Name("Ancho")]
|
||||||
|
private float ancho = 0.15f;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
[property: Category("🎨 Apariencia")]
|
||||||
|
[property: Description("Alto de la bomba en metros")]
|
||||||
|
[property: Name("Alto")]
|
||||||
|
private float alto = 0.10f;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
[property: Category("🎨 Apariencia")]
|
||||||
|
[property: Description("Profundidad de la bomba en metros")]
|
||||||
|
[property: Name("Profundidad")]
|
||||||
|
private float profundidad = 0.08f;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
[property: Category("🎨 Apariencia")]
|
||||||
|
[property: Description("Color visual de la bomba")]
|
||||||
|
[property: Name("Color")]
|
||||||
|
private Brush colorButton_oculto = Brushes.Blue;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
[property: Category("⚖️ Física")]
|
||||||
|
[property: Description("Masa del objeto en kg")]
|
||||||
|
[property: Name("Masa")]
|
||||||
|
private float mass = 5.0f;
|
||||||
|
|
||||||
|
partial void OnAnchoChanged(float value)
|
||||||
|
{
|
||||||
|
SimGeometria?.SetDimensions(new Vector3(value, Alto, Profundidad));
|
||||||
|
}
|
||||||
|
|
||||||
|
partial void OnAltoChanged(float value)
|
||||||
|
{
|
||||||
|
SimGeometria?.SetDimensions(new Vector3(Ancho, value, Profundidad));
|
||||||
|
}
|
||||||
|
|
||||||
|
partial void OnProfundidadChanged(float value)
|
||||||
|
{
|
||||||
|
SimGeometria?.SetDimensions(new Vector3(Ancho, Alto, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
partial void OnMassChanged(float value)
|
||||||
|
{
|
||||||
|
SimGeometria?.SetMass(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Category("🔧 Bomba Hidráulica")]
|
||||||
|
[DisplayName("Cabeza de bomba")]
|
||||||
|
[Description("Cabeza de la bomba a caudal cero (m)")]
|
||||||
|
public double PumpHead
|
||||||
|
{
|
||||||
|
get => _pumpHead;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _pumpHead, Math.Max(0, value)))
|
||||||
|
{
|
||||||
|
InvalidateHydraulicNetwork();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Category("🔧 Bomba Hidráulica")]
|
||||||
|
[DisplayName("Caudal máximo")]
|
||||||
|
[Description("Caudal máximo de la bomba (m³/s)")]
|
||||||
|
public double MaxFlow
|
||||||
|
{
|
||||||
|
get => _maxFlow;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _maxFlow, Math.Max(0.001, value)))
|
||||||
|
{
|
||||||
|
InvalidateHydraulicNetwork();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Category("🔧 Bomba Hidráulica")]
|
||||||
|
[DisplayName("Velocidad relativa")]
|
||||||
|
[Description("Velocidad relativa de la bomba (0.0 a 1.0)")]
|
||||||
|
public double SpeedRatio
|
||||||
|
{
|
||||||
|
get => _speedRatio;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
SetProperty(ref _speedRatio, Math.Max(0.0, Math.Min(1.0, value)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Category("🔧 Bomba Hidráulica")]
|
||||||
|
[DisplayName("Funcionando")]
|
||||||
|
[Description("Indica si la bomba está encendida")]
|
||||||
|
public bool IsRunning
|
||||||
|
{
|
||||||
|
get => _isRunning;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
SetProperty(ref _isRunning, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Category("🔧 Bomba Hidráulica")]
|
||||||
|
[DisplayName("Dirección")]
|
||||||
|
[Description("Dirección de la bomba (+1 o -1)")]
|
||||||
|
[ItemsSource(typeof(PumpDirectionItemsSource))]
|
||||||
|
public int PumpDirection
|
||||||
|
{
|
||||||
|
get => _pumpDirection;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _pumpDirection, value == -1 ? -1 : 1))
|
||||||
|
{
|
||||||
|
InvalidateHydraulicNetwork();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Category("📊 Estado Actual")]
|
||||||
|
[DisplayName("Caudal actual")]
|
||||||
|
[Description("Caudal actual de la bomba (m³/s)")]
|
||||||
|
[JsonIgnore]
|
||||||
|
public double CurrentFlow
|
||||||
|
{
|
||||||
|
get => _currentFlow;
|
||||||
|
private set => SetProperty(ref _currentFlow, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Category("📊 Estado Actual")]
|
||||||
|
[DisplayName("Presión actual")]
|
||||||
|
[Description("Presión actual en la bomba (Pa)")]
|
||||||
|
[JsonIgnore]
|
||||||
|
public double CurrentPressure
|
||||||
|
{
|
||||||
|
get => _currentPressure;
|
||||||
|
private set => SetProperty(ref _currentPressure, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Category("📊 Estado Actual")]
|
||||||
|
[DisplayName("Presión (bar)")]
|
||||||
|
[Description("Presión actual en la bomba (bar)")]
|
||||||
|
[JsonIgnore]
|
||||||
|
public double CurrentPressureBar => CurrentPressure / 100000.0;
|
||||||
|
|
||||||
|
[Category("📊 Estado Actual")]
|
||||||
|
[DisplayName("Caudal (L/min)")]
|
||||||
|
[Description("Caudal actual en L/min")]
|
||||||
|
[JsonIgnore]
|
||||||
|
public double CurrentFlowLMin => CurrentFlow * 60000.0; // m³/s a L/min
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Constructor y Métodos Base
|
||||||
|
|
||||||
|
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 OnMove(float LeftPixels, float TopPixels)
|
||||||
|
{
|
||||||
|
UpdateAfterMove();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnResize(float Delta_Width, float Delta_Height)
|
||||||
|
{
|
||||||
|
Ancho += Delta_Width;
|
||||||
|
Alto += Delta_Height;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector2 GetCentro()
|
||||||
|
{
|
||||||
|
return new Vector2(Left + Ancho / 2, Top + Alto / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetCentro(float x, float y)
|
||||||
|
{
|
||||||
|
Left = x - Ancho / 2;
|
||||||
|
Top = y - Alto / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetCentro(Vector2 centro)
|
||||||
|
{
|
||||||
|
Left = centro.X - Ancho / 2;
|
||||||
|
Top = centro.Y - Alto / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ActualizarGeometrias()
|
||||||
|
{
|
||||||
|
if (SimGeometria != null && !RemoverDesdeSimulacion)
|
||||||
|
{
|
||||||
|
SimGeometria.SetPosition(GetCentro());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public osPumpExample()
|
||||||
|
{
|
||||||
|
Nombre = "Bomba Hidráulica";
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateAfterMove()
|
||||||
|
{
|
||||||
|
if (!RemoverDesdeSimulacion)
|
||||||
|
{
|
||||||
|
ActualizarGeometrias();
|
||||||
|
SimGeometria?.SetDimensions(new Vector3(Ancho, Alto, Profundidad));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void UpdateGeometryStart()
|
||||||
|
{
|
||||||
|
// Se llama cuando inicia la simulación - crear geometría si no existe
|
||||||
|
if (SimGeometria == null)
|
||||||
|
{
|
||||||
|
SimGeometria = simulationManager.AddPumpExample(new Vector3(Ancho, Alto, Profundidad), GetCentro(), Mass);
|
||||||
|
SimGeometria.SimObjectType = "PumpExample";
|
||||||
|
SimGeometria.WpfObject = this;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ActualizarGeometrias();
|
||||||
|
SimGeometria?.SetDimensions(new Vector3(Ancho, Alto, Profundidad));
|
||||||
|
SimGeometria?.SetMass(Mass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void UpdateGeometryStep()
|
||||||
|
{
|
||||||
|
// Se llama durante cada paso de la simulación
|
||||||
|
if (SimGeometria != null)
|
||||||
|
{
|
||||||
|
// Actualizar posición desde la simulación hacia WPF
|
||||||
|
SetCentro(SimGeometria.Center);
|
||||||
|
|
||||||
|
// Actualizar propiedades hidráulicas en la simulación
|
||||||
|
SimGeometria.UpdateHydraulicProperties(PumpHead, MaxFlow, SpeedRatio, IsRunning, PumpDirection);
|
||||||
|
|
||||||
|
// Obtener resultados de la simulación hidráulica
|
||||||
|
CurrentFlow = SimGeometria.GetCurrentFlow();
|
||||||
|
CurrentPressure = SimGeometria.GetCurrentPressure();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void UpdateControl(int elapsedMilliseconds)
|
||||||
|
{
|
||||||
|
if (SimGeometria != null)
|
||||||
|
{
|
||||||
|
SetCentro(SimGeometria.Center);
|
||||||
|
|
||||||
|
// Actualizar el color según el estado
|
||||||
|
if (IsRunning)
|
||||||
|
ColorButton_oculto = Brushes.Green;
|
||||||
|
else
|
||||||
|
ColorButton_oculto = Brushes.Gray;
|
||||||
|
|
||||||
|
// Actualizar valores actuales desde la simulación
|
||||||
|
CurrentFlow = SimGeometria.GetCurrentFlow();
|
||||||
|
CurrentPressure = SimGeometria.GetCurrentPressure();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.AddPumpExample(new Vector3(Ancho, Alto, Profundidad), GetCentro(), Mass);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ucUnLoaded()
|
||||||
|
{
|
||||||
|
// El UserControl se esta eliminando
|
||||||
|
// eliminar el objeto de simulacion
|
||||||
|
simulationManager.Remove(SimGeometria);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region IHydraulicComponent Implementation
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public bool HasHydraulicComponents => true;
|
||||||
|
|
||||||
|
public List<HydraulicNodeDefinition> GetHydraulicNodes()
|
||||||
|
{
|
||||||
|
var nodes = new List<HydraulicNodeDefinition>
|
||||||
|
{
|
||||||
|
new HydraulicNodeDefinition($"{Nombre}_In", false, null, "Entrada de la bomba"),
|
||||||
|
new HydraulicNodeDefinition($"{Nombre}_Out", false, null, "Salida de la bomba")
|
||||||
|
};
|
||||||
|
|
||||||
|
return nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<HydraulicElementDefinition> GetHydraulicElements()
|
||||||
|
{
|
||||||
|
var elements = new List<HydraulicElementDefinition>();
|
||||||
|
|
||||||
|
if (HasHydraulicComponents)
|
||||||
|
{
|
||||||
|
// Crear bomba con parámetros actuales
|
||||||
|
var pump = new PumpHQ(
|
||||||
|
h0: PumpHead,
|
||||||
|
q0: MaxFlow,
|
||||||
|
speedRel: IsRunning ? SpeedRatio : 0.0, // Si no está funcionando, velocidad = 0
|
||||||
|
direction: PumpDirection
|
||||||
|
);
|
||||||
|
|
||||||
|
var pumpElement = new HydraulicElementDefinition(
|
||||||
|
$"{Nombre}_Pump",
|
||||||
|
$"{Nombre}_In",
|
||||||
|
$"{Nombre}_Out",
|
||||||
|
pump,
|
||||||
|
$"Bomba {Nombre}"
|
||||||
|
);
|
||||||
|
|
||||||
|
elements.Add(pumpElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
return elements;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateHydraulicProperties()
|
||||||
|
{
|
||||||
|
// Aquí se pueden hacer validaciones o ajustes antes de la simulación
|
||||||
|
if (!IsRunning)
|
||||||
|
{
|
||||||
|
SpeedRatio = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SpeedRatio < 0.0) SpeedRatio = 0.0;
|
||||||
|
if (SpeedRatio > 1.0) SpeedRatio = 1.0;
|
||||||
|
|
||||||
|
Debug.WriteLine($"Bomba {Nombre}: Velocidad={SpeedRatio:F2}, Funcionando={IsRunning}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ApplyHydraulicResults(Dictionary<string, double> flows, Dictionary<string, double> pressures)
|
||||||
|
{
|
||||||
|
// Buscar resultados para esta bomba
|
||||||
|
string pumpBranchName = $"{Nombre}_Pump";
|
||||||
|
string inletNodeName = $"{Nombre}_In";
|
||||||
|
string outletNodeName = $"{Nombre}_Out";
|
||||||
|
|
||||||
|
if (flows.ContainsKey(pumpBranchName))
|
||||||
|
{
|
||||||
|
CurrentFlow = flows[pumpBranchName];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pressures.ContainsKey(inletNodeName))
|
||||||
|
{
|
||||||
|
CurrentPressure = pressures[inletNodeName];
|
||||||
|
}
|
||||||
|
else if (pressures.ContainsKey(outletNodeName))
|
||||||
|
{
|
||||||
|
CurrentPressure = pressures[outletNodeName];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region IHydraulicFlowReceiver Implementation
|
||||||
|
|
||||||
|
public void SetFlow(double flow)
|
||||||
|
{
|
||||||
|
CurrentFlow = flow;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double GetFlow()
|
||||||
|
{
|
||||||
|
return CurrentFlow;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region IHydraulicPressureReceiver Implementation
|
||||||
|
|
||||||
|
public void SetPressure(double pressure)
|
||||||
|
{
|
||||||
|
CurrentPressure = pressure;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double GetPressure()
|
||||||
|
{
|
||||||
|
return CurrentPressure;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Helper Methods
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invalida la red hidráulica para forzar reconstrucción
|
||||||
|
/// </summary>
|
||||||
|
private void InvalidateHydraulicNetwork()
|
||||||
|
{
|
||||||
|
// Si tenemos acceso al MainViewModel, invalidar la red
|
||||||
|
if (_mainViewModel?.hydraulicSimulationManager != null)
|
||||||
|
{
|
||||||
|
_mainViewModel.hydraulicSimulationManager.InvalidateNetwork();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Static Interface Implementation
|
||||||
|
|
||||||
|
public static string NombreClase() => "Bomba Hidráulica";
|
||||||
|
public static string NombreCategoria() => "Componentes Hidráulicos";
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Proveedor de items para la dirección de la bomba
|
||||||
|
/// </summary>
|
||||||
|
public class PumpDirectionItemsSource : IItemsSource
|
||||||
|
{
|
||||||
|
public Xceed.Wpf.Toolkit.PropertyGrid.Attributes.ItemCollection GetValues()
|
||||||
|
{
|
||||||
|
var items = new Xceed.Wpf.Toolkit.PropertyGrid.Attributes.ItemCollection();
|
||||||
|
items.Add(1, "Adelante (+1)");
|
||||||
|
items.Add(-1, "Atrás (-1)");
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
<UserControl x:Class="CtrEditor.ObjetosSim.HydraulicComponents.ucPumpExample"
|
||||||
|
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"
|
||||||
|
d:DesignHeight="60" d:DesignWidth="80">
|
||||||
|
<Grid>
|
||||||
|
<!-- Fondo de la bomba -->
|
||||||
|
<Ellipse x:Name="PumpBackground"
|
||||||
|
Fill="LightSteelBlue"
|
||||||
|
Stroke="DarkSlateGray"
|
||||||
|
StrokeThickness="2"/>
|
||||||
|
|
||||||
|
<!-- Indicador de dirección -->
|
||||||
|
<Polygon x:Name="DirectionArrow"
|
||||||
|
Fill="DarkBlue"
|
||||||
|
Points="30,25 50,35 30,45"
|
||||||
|
RenderTransformOrigin="0.5,0.5">
|
||||||
|
<Polygon.RenderTransform>
|
||||||
|
<RotateTransform x:Name="ArrowRotation" Angle="0"/>
|
||||||
|
</Polygon.RenderTransform>
|
||||||
|
</Polygon>
|
||||||
|
|
||||||
|
<!-- Texto de identificación -->
|
||||||
|
<TextBlock x:Name="PumpLabel"
|
||||||
|
Text="P"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
FontWeight="Bold"
|
||||||
|
FontSize="16"
|
||||||
|
Foreground="White"
|
||||||
|
Margin="0,0,0,0"/>
|
||||||
|
|
||||||
|
<!-- Indicador de estado (LED) -->
|
||||||
|
<Ellipse x:Name="StatusLED"
|
||||||
|
Width="8"
|
||||||
|
Height="8"
|
||||||
|
Fill="Red"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
VerticalAlignment="Top"
|
||||||
|
Margin="0,5,5,0"/>
|
||||||
|
|
||||||
|
<!-- Conexiones de entrada y salida -->
|
||||||
|
<Rectangle x:Name="InletConnection"
|
||||||
|
Width="10"
|
||||||
|
Height="4"
|
||||||
|
Fill="DarkSlateGray"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Margin="-5,0,0,0"/>
|
||||||
|
|
||||||
|
<Rectangle x:Name="OutletConnection"
|
||||||
|
Width="10"
|
||||||
|
Height="4"
|
||||||
|
Fill="DarkSlateGray"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Margin="0,0,-5,0"/>
|
||||||
|
|
||||||
|
<!-- Información de estado (opcional, visible en modo debug) -->
|
||||||
|
<Border x:Name="StatusInfo"
|
||||||
|
Background="Black"
|
||||||
|
Opacity="0.8"
|
||||||
|
CornerRadius="3"
|
||||||
|
Visibility="Collapsed"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Bottom"
|
||||||
|
Margin="5">
|
||||||
|
<StackPanel Orientation="Vertical" Margin="3">
|
||||||
|
<TextBlock x:Name="FlowText"
|
||||||
|
Text="0.0 L/min"
|
||||||
|
Foreground="White"
|
||||||
|
FontSize="8"
|
||||||
|
HorizontalAlignment="Center"/>
|
||||||
|
<TextBlock x:Name="PressureText"
|
||||||
|
Text="0.0 bar"
|
||||||
|
Foreground="White"
|
||||||
|
FontSize="8"
|
||||||
|
HorizontalAlignment="Center"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
|
@ -0,0 +1,225 @@
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Media;
|
||||||
|
using System.Windows.Media.Animation;
|
||||||
|
using CtrEditor.ObjetosSim;
|
||||||
|
using CtrEditor.FuncionesBase;
|
||||||
|
|
||||||
|
namespace CtrEditor.ObjetosSim.HydraulicComponents
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// UserControl para la bomba hidráulica de ejemplo
|
||||||
|
/// </summary>
|
||||||
|
public partial class ucPumpExample : UserControl, IDataContainer
|
||||||
|
{
|
||||||
|
public osBase? Datos { get; set; }
|
||||||
|
public int zIndex_fromFrames { get; set; } = 0;
|
||||||
|
|
||||||
|
private Storyboard? _rotationAnimation;
|
||||||
|
private bool _isHighlighted = false;
|
||||||
|
|
||||||
|
public ucPumpExample()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
this.Loaded += OnLoaded;
|
||||||
|
this.Unloaded += OnUnloaded;
|
||||||
|
DataContextChanged += OnDataContextChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnLoaded(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
Datos?.ucLoaded();
|
||||||
|
UpdateVisualState();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnUnloaded(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
Datos?.ucUnLoaded();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (DataContext is osPumpExample pump)
|
||||||
|
{
|
||||||
|
Datos = pump;
|
||||||
|
pump.PropertyChanged += OnPumpPropertyChanged;
|
||||||
|
UpdateVisualState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPumpPropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.PropertyName == nameof(osPumpExample.IsRunning) ||
|
||||||
|
e.PropertyName == nameof(osPumpExample.SpeedRatio) ||
|
||||||
|
e.PropertyName == nameof(osPumpExample.PumpDirection) ||
|
||||||
|
e.PropertyName == nameof(osPumpExample.CurrentFlow) ||
|
||||||
|
e.PropertyName == nameof(osPumpExample.CurrentPressure))
|
||||||
|
{
|
||||||
|
UpdateVisualState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Actualiza el estado visual de la bomba
|
||||||
|
/// </summary>
|
||||||
|
private void UpdateVisualState()
|
||||||
|
{
|
||||||
|
if (Datos is not osPumpExample pump) return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Dispatcher.BeginInvoke(() =>
|
||||||
|
{
|
||||||
|
UpdateStatusLED(pump);
|
||||||
|
UpdateRotationAnimation(pump);
|
||||||
|
UpdateDirectionArrow(pump);
|
||||||
|
UpdateStatusInfo(pump);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Error updating pump visual state: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Actualiza el LED de estado
|
||||||
|
/// </summary>
|
||||||
|
private void UpdateStatusLED(osPumpExample pump)
|
||||||
|
{
|
||||||
|
if (pump.IsRunning && pump.SpeedRatio > 0)
|
||||||
|
{
|
||||||
|
StatusLED.Fill = new SolidColorBrush(Colors.LimeGreen);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StatusLED.Fill = new SolidColorBrush(Colors.Red);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Actualiza la animación de rotación
|
||||||
|
/// </summary>
|
||||||
|
private void UpdateRotationAnimation(osPumpExample pump)
|
||||||
|
{
|
||||||
|
_rotationAnimation?.Stop();
|
||||||
|
|
||||||
|
if (pump.IsRunning && pump.SpeedRatio > 0)
|
||||||
|
{
|
||||||
|
// Crear animación de rotación
|
||||||
|
var rotateTransform = new RotateTransform();
|
||||||
|
PumpBackground.RenderTransform = rotateTransform;
|
||||||
|
PumpBackground.RenderTransformOrigin = new Point(0.5, 0.5);
|
||||||
|
|
||||||
|
var animation = new DoubleAnimation
|
||||||
|
{
|
||||||
|
From = 0,
|
||||||
|
To = 360,
|
||||||
|
Duration = TimeSpan.FromSeconds(2.0 / pump.SpeedRatio), // Más rápido con mayor velocidad
|
||||||
|
RepeatBehavior = RepeatBehavior.Forever
|
||||||
|
};
|
||||||
|
|
||||||
|
_rotationAnimation = new Storyboard();
|
||||||
|
Storyboard.SetTarget(animation, rotateTransform);
|
||||||
|
Storyboard.SetTargetProperty(animation, new PropertyPath(RotateTransform.AngleProperty));
|
||||||
|
_rotationAnimation.Children.Add(animation);
|
||||||
|
_rotationAnimation.Begin();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PumpBackground.RenderTransform = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Actualiza la flecha de dirección
|
||||||
|
/// </summary>
|
||||||
|
private void UpdateDirectionArrow(osPumpExample pump)
|
||||||
|
{
|
||||||
|
if (pump.PumpDirection == -1)
|
||||||
|
{
|
||||||
|
ArrowRotation.Angle = 180; // Invertir flecha
|
||||||
|
DirectionArrow.Fill = new SolidColorBrush(Colors.Orange);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ArrowRotation.Angle = 0;
|
||||||
|
DirectionArrow.Fill = new SolidColorBrush(Colors.DarkBlue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Actualiza la información de estado
|
||||||
|
/// </summary>
|
||||||
|
private void UpdateStatusInfo(osPumpExample pump)
|
||||||
|
{
|
||||||
|
FlowText.Text = $"{pump.CurrentFlowLMin:F1} L/min";
|
||||||
|
PressureText.Text = $"{pump.CurrentPressureBar:F2} bar";
|
||||||
|
|
||||||
|
// Mostrar información si hay datos relevantes
|
||||||
|
if (Math.Abs(pump.CurrentFlow) > 0.001 || Math.Abs(pump.CurrentPressure) > 1000)
|
||||||
|
{
|
||||||
|
StatusInfo.Visibility = Visibility.Visible;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StatusInfo.Visibility = Visibility.Collapsed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region IDataContainer Implementation
|
||||||
|
|
||||||
|
public void Highlight(bool state)
|
||||||
|
{
|
||||||
|
_isHighlighted = state;
|
||||||
|
|
||||||
|
if (state)
|
||||||
|
{
|
||||||
|
PumpBackground.Stroke = new SolidColorBrush(Colors.Yellow);
|
||||||
|
PumpBackground.StrokeThickness = 3;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PumpBackground.Stroke = new SolidColorBrush(Colors.DarkSlateGray);
|
||||||
|
PumpBackground.StrokeThickness = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
protected override void OnMouseEnter(System.Windows.Input.MouseEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnMouseEnter(e);
|
||||||
|
if (!_isHighlighted)
|
||||||
|
{
|
||||||
|
// Mostrar información al pasar el mouse
|
||||||
|
StatusInfo.Visibility = Visibility.Visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnMouseLeave(System.Windows.Input.MouseEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnMouseLeave(e);
|
||||||
|
if (!_isHighlighted && Datos is osPumpExample pump)
|
||||||
|
{
|
||||||
|
// Ocultar información si no hay datos relevantes
|
||||||
|
if (Math.Abs(pump.CurrentFlow) < 0.001 && Math.Abs(pump.CurrentPressure) < 1000)
|
||||||
|
{
|
||||||
|
StatusInfo.Visibility = Visibility.Collapsed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ZIndexEnum ZIndex_Base()
|
||||||
|
{
|
||||||
|
return ZIndexEnum.Estaticos;
|
||||||
|
}
|
||||||
|
|
||||||
|
~ucPumpExample()
|
||||||
|
{
|
||||||
|
_rotationAnimation?.Stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1168,6 +1168,14 @@ namespace CtrEditor.Simulacion
|
||||||
return botella;
|
return botella;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public simPumpExample AddPumpExample(Vector3 dimensions, Vector2 position, float mass)
|
||||||
|
{
|
||||||
|
var pump = new simPumpExample(simulation, _deferredActions, dimensions, position, mass);
|
||||||
|
Cuerpos.Add(pump);
|
||||||
|
RegisterObjectHandle(pump);
|
||||||
|
return pump;
|
||||||
|
}
|
||||||
|
|
||||||
public simTransporte AddRectangle(float width, float height, Vector2 position, float angle)
|
public simTransporte AddRectangle(float width, float height, Vector2 position, float angle)
|
||||||
{
|
{
|
||||||
var transporte = new simTransporte(simulation, _deferredActions, width, height, position, angle, this);
|
var transporte = new simTransporte(simulation, _deferredActions, width, height, position, angle, this);
|
||||||
|
|
|
@ -0,0 +1,197 @@
|
||||||
|
using BepuPhysics.Collidables;
|
||||||
|
using BepuPhysics.Constraints;
|
||||||
|
using BepuPhysics;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using CtrEditor.HydraulicSimulator;
|
||||||
|
|
||||||
|
namespace CtrEditor.Simulacion
|
||||||
|
{
|
||||||
|
public class simPumpExample : simBase
|
||||||
|
{
|
||||||
|
public float Width;
|
||||||
|
public float Height;
|
||||||
|
public float Depth;
|
||||||
|
private float _mass;
|
||||||
|
|
||||||
|
// Propiedades hidráulicas
|
||||||
|
public double PumpHead { get; set; } = 50.0;
|
||||||
|
public double MaxFlow { get; set; } = 0.01;
|
||||||
|
public double SpeedRatio { get; set; } = 1.0;
|
||||||
|
public bool IsRunning { get; set; } = true;
|
||||||
|
public int PumpDirection { get; set; } = 1;
|
||||||
|
|
||||||
|
// Estado actual de la simulación hidráulica
|
||||||
|
public double CurrentFlow { get; set; } = 0.0;
|
||||||
|
public double CurrentPressure { get; set; } = 0.0;
|
||||||
|
|
||||||
|
public simPumpExample(Simulation simulation, List<Action> deferredActions, Vector3 dimensions, Vector2 position, float mass)
|
||||||
|
{
|
||||||
|
_simulation = simulation;
|
||||||
|
Width = dimensions.X;
|
||||||
|
Height = dimensions.Y;
|
||||||
|
Depth = dimensions.Z;
|
||||||
|
_mass = mass;
|
||||||
|
|
||||||
|
// Usar COORDINATECONVERTER para conversión centralizada
|
||||||
|
var position3D = new Vector3(position.X, CoordinateConverter.WpfYToBepuY(position.Y), Depth / 2f + zPos_Transporte + zAltura_Transporte);
|
||||||
|
Create(position3D);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float CenterX
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return GetPosition().X;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
var pos = GetPosition();
|
||||||
|
SetPosition(value, pos.Y, pos.Z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public float CenterY
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
// Usar COORDINATECONVERTER para conversión centralizada
|
||||||
|
return CoordinateConverter.BepuYToWpfY(GetPosition().Y);
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
var pos = GetPosition();
|
||||||
|
// Usar COORDINATECONVERTER para conversión centralizada
|
||||||
|
SetPosition(pos.X, CoordinateConverter.WpfYToBepuY(value), pos.Z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector2 Center
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var pos3D = GetPosition();
|
||||||
|
// Usar COORDINATECONVERTER para conversión centralizada
|
||||||
|
return CoordinateConverter.BepuVector3ToWpfVector2(pos3D);
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
// Mantener la Z actual, solo cambiar X, Y
|
||||||
|
var currentPos = GetPosition();
|
||||||
|
// Usar COORDINATECONVERTER para conversión centralizada
|
||||||
|
SetPosition(value.X, CoordinateConverter.WpfYToBepuY(value.Y), currentPos.Z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public float Mass
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_simulation != null && _simulation.Bodies.BodyExists(BodyHandle))
|
||||||
|
{
|
||||||
|
var bodyReference = _simulation.Bodies.GetBodyReference(BodyHandle);
|
||||||
|
return 1f / bodyReference.LocalInertia.InverseMass;
|
||||||
|
}
|
||||||
|
return _mass;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_mass = value;
|
||||||
|
if (_simulation != null && _simulation.Bodies.BodyExists(BodyHandle))
|
||||||
|
{
|
||||||
|
var box = new Box(Width, Height, Depth);
|
||||||
|
var inertia = box.ComputeInertia(_mass);
|
||||||
|
_simulation.Bodies.SetLocalInertia(BodyHandle, inertia);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Create(Vector3 position)
|
||||||
|
{
|
||||||
|
RemoverBody();
|
||||||
|
|
||||||
|
var box = new Box(Width, Height, Depth);
|
||||||
|
var shapeIndex = _simulation.Shapes.Add(box);
|
||||||
|
|
||||||
|
// Crear el cuerpo estático (las bombas no se mueven físicamente)
|
||||||
|
var inertia = box.ComputeInertia(_mass);
|
||||||
|
var bodyDescription = BodyDescription.CreateKinematic(
|
||||||
|
new RigidPose(position),
|
||||||
|
new CollidableDescription(shapeIndex, 0),
|
||||||
|
new BodyActivityDescription(-1f)
|
||||||
|
);
|
||||||
|
|
||||||
|
BodyHandle = _simulation.Bodies.Add(bodyDescription);
|
||||||
|
_bodyCreated = true;
|
||||||
|
|
||||||
|
// Registrar en el diccionario del SimulationManager
|
||||||
|
if (_simulationManager != null)
|
||||||
|
_simulationManager.CollidableData[BodyHandle.Value] = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetDimensions(Vector3 dimensions)
|
||||||
|
{
|
||||||
|
Width = dimensions.X;
|
||||||
|
Height = dimensions.Y;
|
||||||
|
Depth = dimensions.Z;
|
||||||
|
|
||||||
|
if (_simulation != null && _simulation.Bodies.BodyExists(BodyHandle))
|
||||||
|
{
|
||||||
|
var box = new Box(Width, Height, Depth);
|
||||||
|
var shapeIndex = _simulation.Shapes.Add(box);
|
||||||
|
ChangeBodyShape(shapeIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetMass(float mass)
|
||||||
|
{
|
||||||
|
Mass = mass;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Actualiza las propiedades hidráulicas de la bomba
|
||||||
|
/// </summary>
|
||||||
|
public void UpdateHydraulicProperties(double pumpHead, double maxFlow, double speedRatio, bool isRunning, int direction)
|
||||||
|
{
|
||||||
|
PumpHead = pumpHead;
|
||||||
|
MaxFlow = maxFlow;
|
||||||
|
SpeedRatio = speedRatio;
|
||||||
|
IsRunning = isRunning;
|
||||||
|
PumpDirection = direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Aplica los resultados de la simulación hidráulica
|
||||||
|
/// </summary>
|
||||||
|
public void ApplyHydraulicResults(double flow, double pressure)
|
||||||
|
{
|
||||||
|
CurrentFlow = flow;
|
||||||
|
CurrentPressure = pressure;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Obtiene el caudal actual de la bomba
|
||||||
|
/// </summary>
|
||||||
|
public double GetCurrentFlow()
|
||||||
|
{
|
||||||
|
return CurrentFlow;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Obtiene la presión actual de la bomba
|
||||||
|
/// </summary>
|
||||||
|
public double GetCurrentPressure()
|
||||||
|
{
|
||||||
|
return CurrentPressure;
|
||||||
|
}
|
||||||
|
|
||||||
|
public new void RemoverBody()
|
||||||
|
{
|
||||||
|
base.RemoverBody();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue