feat: Enhance hydraulic simulation components and integrate new object management

- Updated UserControlFactory to support hydraulic simulation manager in AssignDatos method.
- Introduced new classes for hydraulic simulation objects: simHydraulicBase, simHydraulicPump, simHydraulicTank, and simHydraulicPipe.
- Implemented methods for updating properties and applying simulation results in hydraulic components.
- Enhanced osHydPipe, osHydPump, and osHydTank classes to manage hydraulic simulation objects and their properties.
- Added methods for creating, removing, and clearing hydraulic simulation objects in HydraulicSimulationManager.
- Improved error handling and logging in various components to facilitate debugging.
- Refactored XAML and code-behind files to align with new namespace structure for hydraulic components.
This commit is contained in:
Miguel 2025-09-06 02:58:18 +02:00
parent 8e6d457047
commit e6a8bb8cbe
16 changed files with 650 additions and 85 deletions

View File

@ -275,7 +275,7 @@ namespace CtrEditor
if (userControl != null)
{
UserControlFactory.AssignDatos(userControl, osObjeto, _mainViewModel.simulationManager);
UserControlFactory.AssignDatos(userControl, osObjeto, _mainViewModel.simulationManager, _mainViewModel.hydraulicSimulationManager);
osObjeto._mainViewModel = _mainViewModel;
if (osObjeto.Id == null)

View File

@ -451,6 +451,13 @@ namespace CtrEditor.HydraulicSimulator
/// </summary>
private void UpdateObjectProperties()
{
// Actualizar objetos hidráulicos específicos
foreach (var simObj in HydraulicSimObjects)
{
simObj.UpdateProperties();
}
// Actualizar objetos hidráulicos generales (legacy)
foreach (var obj in HydraulicObjects)
{
// Aquí se implementará la actualización de propiedades específicas
@ -548,6 +555,14 @@ namespace CtrEditor.HydraulicSimulator
/// </summary>
private void ApplyResultsToObjects()
{
// Aplicar resultados a objetos hidráulicos específicos
foreach (var simObj in HydraulicSimObjects)
{
ApplyResultsToSimObject(simObj);
simObj.ApplySimulationResults();
}
// Aplicar resultados a objetos hidráulicos generales (legacy)
foreach (var obj in HydraulicObjects)
{
// Aplicar caudales y presiones calculados al objeto
@ -555,6 +570,58 @@ namespace CtrEditor.HydraulicSimulator
}
}
/// <summary>
/// Aplica los resultados de la simulación a un objeto específico
/// </summary>
private void ApplyResultsToSimObject(simHydraulicBase simObj)
{
// Aplicar resultados específicos según el tipo de objeto
if (simObj is simHydraulicPump pump)
{
// Buscar flujo y presión de la bomba en los resultados
string pumpBranchName = $"{pump.Nombre}_Pump";
if (LastSolutionResult.Flows.TryGetValue(pumpBranchName, out double flow))
{
pump.CurrentFlow = flow;
}
string inletNodeName = $"{pump.Nombre}_In";
if (LastSolutionResult.Pressures.TryGetValue(inletNodeName, out double pressure))
{
pump.CurrentPressure = pressure;
}
}
else if (simObj is simHydraulicTank tank)
{
// Buscar flujos de entrada y salida del tanque
// Esto requeriría mapeo de tuberías conectadas
// Por ahora, aplicar presión del tanque
string tankNodeName = $"{tank.Nombre}_Tank";
if (LastSolutionResult.Pressures.TryGetValue(tankNodeName, out double pressure))
{
tank.CurrentPressure = pressure;
}
}
else if (simObj is simHydraulicPipe pipe)
{
// Buscar flujo a través de la tubería
string pipeBranchName = $"{pipe.Nombre}_Pipe";
if (LastSolutionResult.Flows.TryGetValue(pipeBranchName, out double flow))
{
pipe.CurrentFlow = flow;
}
// Calcular pérdida de presión
string fromNode = pipe.ComponenteAId;
string toNode = pipe.ComponenteBId;
if (LastSolutionResult.Pressures.TryGetValue(fromNode, out double pressureA) &&
LastSolutionResult.Pressures.TryGetValue(toNode, out double pressureB))
{
pipe.PressureDrop = pressureA - pressureB;
}
}
}
/// <summary>
/// Aplica los resultados a un objeto específico
/// </summary>
@ -729,5 +796,103 @@ namespace CtrEditor.HydraulicSimulator
}
#endregion
#region Hydraulic Object Creation
/// <summary>
/// Lista de objetos hidráulicos específicos del simulador
/// </summary>
public List<simHydraulicBase> HydraulicSimObjects { get; private set; } = new List<simHydraulicBase>();
/// <summary>
/// Crea una bomba hidráulica en la simulación
/// </summary>
public simHydraulicPump AddPump(double pumpHead, double maxFlow, double speedRatio, bool isRunning, int direction)
{
var pump = new simHydraulicPump(this)
{
PumpHead = pumpHead,
MaxFlow = maxFlow,
SpeedRatio = speedRatio,
IsRunning = isRunning,
PumpDirection = direction
};
HydraulicSimObjects.Add(pump);
_networkNeedsRebuild = true;
Debug.WriteLine($"Bomba hidráulica creada: H={pumpHead}m, Q={maxFlow}m³/s");
return pump;
}
/// <summary>
/// Crea un tanque hidráulico en la simulación
/// </summary>
public simHydraulicTank AddTank(double pressure, double currentLevel, double maxLevel, double minLevel, double crossSectionalArea, bool isFixedPressure)
{
var tank = new simHydraulicTank(this)
{
TankPressure = pressure,
CurrentLevel = currentLevel,
MaxLevel = maxLevel,
MinLevel = minLevel,
CrossSectionalArea = crossSectionalArea,
IsFixedPressure = isFixedPressure
};
HydraulicSimObjects.Add(tank);
_networkNeedsRebuild = true;
Debug.WriteLine($"Tanque hidráulico creado: P={pressure}Pa, Nivel={currentLevel}m");
return tank;
}
/// <summary>
/// Crea una tubería hidráulica en la simulación
/// </summary>
public simHydraulicPipe AddPipe(double length, double diameter, double roughness, string componenteAId, string componenteBId)
{
var pipe = new simHydraulicPipe(this)
{
Length = length,
Diameter = diameter,
Roughness = roughness,
ComponenteAId = componenteAId,
ComponenteBId = componenteBId
};
HydraulicSimObjects.Add(pipe);
_networkNeedsRebuild = true;
Debug.WriteLine($"Tubería hidráulica creada: L={length}m, D={diameter}m");
return pipe;
}
/// <summary>
/// Remueve un objeto hidráulico de la simulación
/// </summary>
public void Remove(simHydraulicBase hydraulicObject)
{
if (hydraulicObject != null && HydraulicSimObjects.Contains(hydraulicObject))
{
HydraulicSimObjects.Remove(hydraulicObject);
_networkNeedsRebuild = true;
Debug.WriteLine($"Objeto hidráulico removido: {hydraulicObject.SimObjectType}");
}
}
/// <summary>
/// Limpia todos los objetos hidráulicos específicos
/// </summary>
public void ClearHydraulicSimObjects()
{
HydraulicSimObjects.Clear();
_networkNeedsRebuild = true;
Debug.WriteLine("Todos los objetos hidráulicos específicos han sido limpiados");
}
#endregion
}
}

View File

@ -0,0 +1,179 @@
using System.Numerics;
using System.ComponentModel;
using CtrEditor.ObjetosSim;
namespace CtrEditor.HydraulicSimulator
{
/// <summary>
/// Clase base para objetos hidráulicos en la simulación
/// Similar a simBase pero para el sistema hidráulico
/// </summary>
public class simHydraulicBase
{
public string SimObjectType { get; set; } = "";
public osBase WpfObject { get; set; }
public string Nombre { get; set; }
public bool Descartar { get; set; } = false;
protected HydraulicSimulationManager _hydraulicManager;
public simHydraulicBase(HydraulicSimulationManager hydraulicManager)
{
_hydraulicManager = hydraulicManager;
}
/// <summary>
/// Método virtual para actualizar propiedades específicas del objeto
/// </summary>
public virtual void UpdateProperties()
{
// Implementación base - puede ser sobrescrita
}
/// <summary>
/// Método virtual para aplicar resultados de la simulación al objeto WPF
/// </summary>
public virtual void ApplySimulationResults()
{
// Implementación base - puede ser sobrescrita
}
}
/// <summary>
/// Representa una bomba hidráulica en la simulación
/// </summary>
public class simHydraulicPump : simHydraulicBase
{
public double PumpHead { get; set; }
public double MaxFlow { get; set; }
public double SpeedRatio { get; set; }
public bool IsRunning { get; set; }
public int PumpDirection { get; set; }
// Resultados de la simulación
public double CurrentFlow { get; set; }
public double CurrentPressure { get; set; }
public simHydraulicPump(HydraulicSimulationManager hydraulicManager) : base(hydraulicManager)
{
SimObjectType = "HydraulicPump";
}
public override void UpdateProperties()
{
// Actualizar propiedades específicas de la bomba
if (WpfObject is IHydraulicPump pump)
{
PumpHead = pump.PumpHead;
MaxFlow = pump.MaxFlow;
SpeedRatio = pump.SpeedRatio;
IsRunning = pump.IsRunning;
PumpDirection = pump.PumpDirection;
}
}
public override void ApplySimulationResults()
{
// Aplicar resultados al objeto WPF
if (WpfObject is IHydraulicPump pump)
{
pump.SetFlow(CurrentFlow);
pump.SetPressure(CurrentPressure);
}
}
}
/// <summary>
/// Representa un tanque hidráulico en la simulación
/// </summary>
public class simHydraulicTank : simHydraulicBase
{
public double TankPressure { get; set; }
public double CurrentLevel { get; set; }
public double MaxLevel { get; set; }
public double MinLevel { get; set; }
public double CrossSectionalArea { get; set; }
public bool IsFixedPressure { get; set; }
// Resultados de la simulación
public double InletFlow { get; set; }
public double OutletFlow { get; set; }
public double CurrentPressure { get; set; }
public simHydraulicTank(HydraulicSimulationManager hydraulicManager) : base(hydraulicManager)
{
SimObjectType = "HydraulicTank";
}
public override void UpdateProperties()
{
// Actualizar propiedades específicas del tanque
if (WpfObject is osHydTank tank)
{
TankPressure = tank.TankPressure;
CurrentLevel = tank.CurrentLevel;
MaxLevel = tank.MaxLevel;
MinLevel = tank.MinLevel;
CrossSectionalArea = tank.CrossSectionalArea;
IsFixedPressure = tank.IsFixedPressure;
}
}
public override void ApplySimulationResults()
{
// Aplicar resultados al objeto WPF
if (WpfObject is IHydraulicFlowReceiver flowReceiver)
{
flowReceiver.SetFlow(InletFlow - OutletFlow); // Flujo neto
}
if (WpfObject is IHydraulicPressureReceiver pressureReceiver)
{
pressureReceiver.SetPressure(CurrentPressure);
}
}
}
/// <summary>
/// Representa una tubería hidráulica en la simulación
/// </summary>
public class simHydraulicPipe : simHydraulicBase
{
public double Length { get; set; }
public double Diameter { get; set; }
public double Roughness { get; set; }
public string ComponenteAId { get; set; }
public string ComponenteBId { get; set; }
// Resultados de la simulación
public double CurrentFlow { get; set; }
public double PressureDrop { get; set; }
public simHydraulicPipe(HydraulicSimulationManager hydraulicManager) : base(hydraulicManager)
{
SimObjectType = "HydraulicPipe";
}
public override void UpdateProperties()
{
// Actualizar propiedades específicas de la tubería
if (WpfObject is IHydraulicPipe pipe)
{
Length = pipe.Length;
Diameter = pipe.Diameter;
Roughness = pipe.Roughness;
// Obtener IDs de componentes conectados (esto se manejará en el manager)
}
}
public override void ApplySimulationResults()
{
// Aplicar resultados al objeto WPF
if (WpfObject is IHydraulicPipe pipe)
{
pipe.SetFlow(CurrentFlow);
pipe.SetPressure(PressureDrop); // En tuberías, "presión" es la pérdida de presión
}
}
}
}

View File

@ -813,7 +813,7 @@ namespace CtrEditor
if (userControl != null)
{
// Asignar los datos al UserControl
UserControlFactory.AssignDatos(userControl, osObjeto, simulationManager);
UserControlFactory.AssignDatos(userControl, osObjeto, simulationManager, hydraulicSimulationManager);
osObjeto._mainViewModel = this;
if (osObjeto.Id == null) // Para los objetos salvados antes de usar UniqueID
osObjeto.Id = new UniqueId().ObtenerNuevaID();

View File

@ -971,7 +971,37 @@ namespace CtrEditor
try
{
var serializedData = JsonConvert.SerializeObject(original, settings);
Debug.WriteLine($"CreateCopyUsingExistingLogic - Serialized data: {serializedData}");
copy = JsonConvert.DeserializeObject<osBase>(serializedData, settings);
if (copy == null)
{
Debug.WriteLine($"CreateCopyUsingExistingLogic - Deserialización devolvió null para tipo: {original.GetType().Name}");
}
else
{
Debug.WriteLine($"CreateCopyUsingExistingLogic - Copia exitosa de tipo: {copy.GetType().Name}");
}
}
catch (JsonSerializationException jsonEx)
{
Debug.WriteLine($"CreateCopyUsingExistingLogic - Error de serialización JSON: {jsonEx.Message}");
Debug.WriteLine($"CreateCopyUsingExistingLogic - StackTrace: {jsonEx.StackTrace}");
if (jsonEx.InnerException != null)
{
Debug.WriteLine($"CreateCopyUsingExistingLogic - Inner Exception: {jsonEx.InnerException.Message}");
}
}
catch (Exception ex)
{
Debug.WriteLine($"CreateCopyUsingExistingLogic - Error general: {ex.Message}");
Debug.WriteLine($"CreateCopyUsingExistingLogic - Tipo de excepción: {ex.GetType().Name}");
Debug.WriteLine($"CreateCopyUsingExistingLogic - StackTrace: {ex.StackTrace}");
if (ex.InnerException != null)
{
Debug.WriteLine($"CreateCopyUsingExistingLogic - Inner Exception: {ex.InnerException.Message}");
}
}
finally
{

View File

@ -12,13 +12,15 @@ using Xceed.Wpf.Toolkit.PropertyGrid.Attributes;
using CommunityToolkit.Mvvm.ComponentModel;
using LibS7Adv;
namespace CtrEditor.ObjetosSim.HydraulicComponents
namespace CtrEditor.ObjetosSim
{
/// <summary>
/// Tubería hidráulica que conecta componentes del sistema hidráulico
/// </summary>
public partial class osHydPipe : osBase, IHydraulicPipe, IosBase
public partial class osHydPipe : osBase, IHydraulicFlowReceiver, IHydraulicPressureReceiver, IosBase
{
// Referencia al objeto de simulación hidráulica específico
private simHydraulicPipe SimHydraulicPipe;
// Properties
private double _length = 1.0; // metros
@ -396,9 +398,59 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents
public override void UpdateGeometryStart()
{
// Se llama cuando inicia la simulación - crear objeto hidráulico si no existe
if (SimHydraulicPipe == null && !string.IsNullOrEmpty(Id_ComponenteA) && !string.IsNullOrEmpty(Id_ComponenteB))
{
SimHydraulicPipe = hydraulicSimulationManager.AddPipe(Length, Diameter, Roughness, Id_ComponenteA, Id_ComponenteB);
SimHydraulicPipe.SimObjectType = "HydraulicPipe";
SimHydraulicPipe.WpfObject = this;
SimHydraulicPipe.Nombre = Nombre;
}
else if (SimHydraulicPipe != null)
{
// Actualizar propiedades si el objeto ya existe
SimHydraulicPipe.Length = Length;
SimHydraulicPipe.Diameter = Diameter;
SimHydraulicPipe.Roughness = Roughness;
SimHydraulicPipe.ComponenteAId = Id_ComponenteA;
SimHydraulicPipe.ComponenteBId = Id_ComponenteB;
}
ActualizarGeometrias();
}
public override void UpdateGeometryStep()
{
// Los objetos hidráulicos actualizan sus resultados
// a través de ApplySimulationResults() desde HydraulicSimulationManager
}
public override void UpdateControl(int elapsedMilliseconds)
{
// Actualizar propiedades desde la simulación hidráulica
if (SimHydraulicPipe != null)
{
CurrentFlow = SimHydraulicPipe.CurrentFlow;
PressureDrop = SimHydraulicPipe.PressureDrop;
}
}
public override void ucLoaded()
{
// Objeto hidráulico se crea en UpdateGeometryStart cuando inicia la simulación
base.ucLoaded();
}
public override void ucUnLoaded()
{
// Remover objeto hidráulico de la simulación
if (SimHydraulicPipe != null)
{
hydraulicSimulationManager.Remove(SimHydraulicPipe);
SimHydraulicPipe = null;
}
}
public override void UpdatePLC(PLCViewModel plc, int elapsedMilliseconds)
{
// Las tuberías no tienen control PLC directo

View File

@ -11,13 +11,16 @@ using Newtonsoft.Json;
using Xceed.Wpf.Toolkit.PropertyGrid.Attributes;
using CommunityToolkit.Mvvm.ComponentModel;
namespace CtrEditor.ObjetosSim.HydraulicComponents
namespace CtrEditor.ObjetosSim
{
/// <summary>
/// Bomba hidráulica que implementa las interfaces del simulador hidráulico
/// </summary>
public partial class osHydPump : osBase, IHydraulicPump, IosBase
public partial class osHydPump : osBase, IHydraulicFlowReceiver, IHydraulicPressureReceiver, IosBase
{
// Referencia al objeto de simulación hidráulica específico
private simHydraulicPump SimHydraulicPump;
public static string NombreCategoria() => "Componentes Hidráulicos";
public static string NombreClase()
@ -206,35 +209,62 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents
public override void UpdateGeometryStart()
{
// Los objetos hidráulicos se registran automáticamente
// cuando inicia la simulación a través de las interfaces
// Se llama cuando inicia la simulación - crear objeto hidráulico si no existe
if (SimHydraulicPump == null)
{
SimHydraulicPump = hydraulicSimulationManager.AddPump(PumpHead, MaxFlow, SpeedRatio, IsRunning, PumpDirection);
SimHydraulicPump.SimObjectType = "HydraulicPump";
SimHydraulicPump.WpfObject = this;
SimHydraulicPump.Nombre = Nombre;
}
else
{
// Actualizar propiedades si el objeto ya existe
SimHydraulicPump.PumpHead = PumpHead;
SimHydraulicPump.MaxFlow = MaxFlow;
SimHydraulicPump.SpeedRatio = SpeedRatio;
SimHydraulicPump.IsRunning = IsRunning;
SimHydraulicPump.PumpDirection = PumpDirection;
}
}
public override void UpdateGeometryStep()
{
// Los objetos hidráulicos actualizan sus resultados
// a través de ApplyHydraulicResults()
// a través de ApplySimulationResults() desde HydraulicSimulationManager
}
public override void UpdateControl(int elapsedMilliseconds)
{
// Actualizar propiedades desde la simulación hidráulica
if (SimHydraulicPump != null)
{
CurrentFlow = SimHydraulicPump.CurrentFlow;
CurrentPressure = SimHydraulicPump.CurrentPressure;
// Actualizar el color según el estado
if (IsRunning)
ColorButton_oculto = Brushes.Green;
else
ColorButton_oculto = Brushes.Gray;
}
}
public override void ucLoaded()
{
// Los objetos hidráulicos no necesitan geometría física
// Objeto hidráulico se crea en UpdateGeometryStart cuando inicia la simulación
base.ucLoaded();
UpdatePumpImage();
}
public override void ucUnLoaded()
{
// Los objetos hidráulicos no tienen geometría que limpiar
// Remover objeto hidráulico de la simulación
if (SimHydraulicPump != null)
{
hydraulicSimulationManager.Remove(SimHydraulicPump);
SimHydraulicPump = null;
}
}
@ -368,22 +398,12 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents
return CurrentPressure;
}
// 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();
hydraulicSimulationManager?.InvalidateNetwork();
}
}
}

View File

@ -13,13 +13,15 @@ using Xceed.Wpf.Toolkit.PropertyGrid.Attributes;
using CommunityToolkit.Mvvm.ComponentModel;
using LibS7Adv;
namespace CtrEditor.ObjetosSim.HydraulicComponents
namespace CtrEditor.ObjetosSim
{
/// <summary>
/// Tanque hidráulico con gestión dinámica de nivel y presión configurable
/// </summary>
public partial class osHydTank : osBase, IHydraulicComponent, IHydraulicFlowReceiver, IHydraulicPressureReceiver, IosBase
public partial class osHydTank : osBase, IHydraulicFlowReceiver, IHydraulicPressureReceiver, IosBase
{
// Referencia al objeto de simulación hidráulica específico
private simHydraulicTank SimHydraulicTank;
public static string NombreCategoria() => "Componentes Hidráulicos";
public static string NombreClase() => "Tanque Hidráulico";
@ -63,13 +65,6 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents
// Properties
[ObservableProperty]
[property: JsonIgnore]
[property: Category("Apariencia")]
[property: Description("Imagen visual")]
[property: Name("Imagen")]
public ImageSource imageSource_oculta;
[ObservableProperty]
[property: Category("🎨 Apariencia")]
[property: Description("Tamaño visual del tanque")]
@ -87,6 +82,11 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents
// Constructor
public osHydTank()
{
// Inicializar el lock primero para thread safety
EnsureLockInitialized();
try
{
Nombre = "Tanque Hidráulico";
Tamano = 1.0f; // Usar un tamaño mayor para mayor visibilidad
@ -96,18 +96,25 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents
Angulo = 0f;
// Asegurar que el movimiento esté habilitado
Lock_movement = false;
ImageSource_oculta = ImageFromPath("/imagenes/tank.png");
_currentVolume = _currentLevel * _crossSectionalArea;
_maxVolume = _maxLevel * _crossSectionalArea;
IsVisFilter = true; // Asegurar que el componente hidráulico sea visible en filtros
UpdateTankPressure();
// Inicializar el lock para thread safety
EnsureLockInitialized();
// No cargar imagen aquí - se carga en ucLoaded()
// para evitar problemas de serialización
// Cálculos seguros
SafeUpdateVolumeCalculations();
IsVisFilter = true; // Asegurar que el componente hidráulico sea visible en filtros
SafeUpdateTankPressure();
// 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}");
}
catch (Exception ex)
{
Debug.WriteLine($"Error in osHydTank constructor: {ex.Message}");
// Inicializar valores por defecto mínimos
InitializeDefaults();
}
}
// Constructor y Métodos Base
@ -137,18 +144,49 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents
public override void UpdateGeometryStart()
{
// Los objetos hidráulicos se registran automáticamente
// cuando inicia la simulación a través de las interfaces
// Se llama cuando inicia la simulación - crear objeto hidráulico si no existe
if (SimHydraulicTank == null)
{
SimHydraulicTank = hydraulicSimulationManager.AddTank(TankPressure, CurrentLevel, MaxLevel, MinLevel, CrossSectionalArea, IsFixedPressure);
SimHydraulicTank.SimObjectType = "HydraulicTank";
SimHydraulicTank.WpfObject = this;
SimHydraulicTank.Nombre = Nombre;
}
else
{
// Actualizar propiedades si el objeto ya existe
SimHydraulicTank.TankPressure = TankPressure;
SimHydraulicTank.CurrentLevel = CurrentLevel;
SimHydraulicTank.MaxLevel = MaxLevel;
SimHydraulicTank.MinLevel = MinLevel;
SimHydraulicTank.CrossSectionalArea = CrossSectionalArea;
SimHydraulicTank.IsFixedPressure = IsFixedPressure;
}
}
public override void UpdateGeometryStep()
{
// Los objetos hidráulicos actualizan sus resultados
// a través de ApplyHydraulicResults()
// a través de ApplySimulationResults() desde HydraulicSimulationManager
}
public override void UpdateControl(int elapsedMilliseconds)
{
// Actualizar propiedades desde la simulación hidráulica
if (SimHydraulicTank != null)
{
InletFlow = SimHydraulicTank.InletFlow;
OutletFlow = SimHydraulicTank.OutletFlow;
CurrentPressure = SimHydraulicTank.CurrentPressure;
// Actualizar nivel del tanque basado en el balance de flujo
double deltaTime = elapsedMilliseconds / 1000.0; // Convertir a segundos
if (deltaTime > 0)
{
UpdateLevelFromFlowBalance(deltaTime);
}
}
// Actualizar el color según el estado del tanque
var percentage = FillPercentage;
if (percentage < 20)
@ -191,7 +229,7 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents
{
if (SetProperty(ref _crossSectionalArea, Math.Max(0.1, value)))
{
UpdateVolumeCalculations();
SafeUpdateVolumeCalculations();
InvalidateHydraulicNetwork();
}
}
@ -209,7 +247,7 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents
{
if (_currentLevel > _maxLevel)
CurrentLevel = _maxLevel;
UpdateVolumeCalculations();
SafeUpdateVolumeCalculations();
}
}
}
@ -226,7 +264,7 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents
{
if (_currentLevel < _minLevel)
CurrentLevel = _minLevel;
UpdateVolumeCalculations();
SafeUpdateVolumeCalculations();
}
}
}
@ -245,7 +283,7 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents
{
if (SetProperty(ref _isFixedPressure, value))
{
UpdateTankPressure();
SafeUpdateTankPressure();
InvalidateHydraulicNetwork();
}
}
@ -261,7 +299,7 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents
{
if (SetProperty(ref _tankPressure, Math.Max(0, value)))
{
UpdateTankPressure();
SafeUpdateTankPressure();
InvalidateHydraulicNetwork();
}
}
@ -296,7 +334,7 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents
var clampedLevel = Math.Max(_minLevel, Math.Min(_maxLevel, value));
if (SetProperty(ref _currentLevel, clampedLevel))
{
UpdateVolumeCalculations();
SafeUpdateVolumeCalculations();
OnPropertyChanged(nameof(FillPercentage));
}
}
@ -442,8 +480,8 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents
public void UpdateHydraulicProperties()
{
// Actualizar propiedades antes de la simulación
UpdateTankPressure();
UpdateVolumeCalculations();
SafeUpdateTankPressure();
SafeUpdateVolumeCalculations();
}
public void ApplyHydraulicResults(Dictionary<string, double> flows, Dictionary<string, double> pressures)
@ -588,6 +626,67 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents
// Private Methods
private void InitializeDefaults()
{
// Valores mínimos para que el objeto sea funcional
try
{
if (string.IsNullOrEmpty(nombre))
nombre = "Tanque Hidráulico";
if (Tamano <= 0)
Tamano = 1.0f;
if (Ancho <= 0)
Ancho = 0.30f;
if (Alto <= 0)
Alto = 0.40f;
SafeUpdateVolumeCalculations();
SafeUpdateTankPressure();
Debug.WriteLine("osHydTank: Initialized with default values");
}
catch (Exception ex)
{
Debug.WriteLine($"Error in InitializeDefaults: {ex.Message}");
}
}
private void SafeUpdateVolumeCalculations()
{
try
{
_currentVolume = _currentLevel * _crossSectionalArea;
_maxVolume = _maxLevel * _crossSectionalArea;
}
catch (Exception ex)
{
Debug.WriteLine($"Error in SafeUpdateVolumeCalculations: {ex.Message}");
// Usar valores por defecto seguros
_currentVolume = 1.0;
_maxVolume = 2.0;
}
}
private void SafeUpdateTankPressure()
{
try
{
if (IsFixedPressure)
{
CurrentPressure = TankPressure;
}
}
catch (Exception ex)
{
Debug.WriteLine($"Error in SafeUpdateTankPressure: {ex.Message}");
// Usar presión atmosférica por defecto
_currentPressure = 101325.0;
}
}
private string GetTankDescription()
{
return TankType switch
@ -751,15 +850,15 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents
public void Start()
{
// Inicialización del tanque para la simulación
UpdateVolumeCalculations();
UpdateTankPressure();
SafeUpdateVolumeCalculations();
SafeUpdateTankPressure();
}
public void Inicializar(int valorInicial)
{
// Inicialización con valor inicial
UpdateVolumeCalculations();
UpdateTankPressure();
SafeUpdateVolumeCalculations();
SafeUpdateTankPressure();
}
public void Disposing()
@ -800,9 +899,11 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents
// crear el objeto de simulacion
base.ucLoaded();
Debug.WriteLine($"osHydTank.ucLoaded(): Componente hidráulico cargado correctamente");
// Inicialización específica del tanque hidráulico
UpdateVolumeCalculations();
UpdateTankPressure();
SafeUpdateVolumeCalculations();
SafeUpdateTankPressure();
// Debug: Confirmar que el UserControl se está cargando
Debug.WriteLine($"osHydTank.ucLoaded(): Tanque '{Nombre}' cargado - Tamaño: {Tamano}, ZIndex: {zIndex_fromFrames}");
@ -810,7 +911,12 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents
public override void ucUnLoaded()
{
// Los tanques hidráulicos no tienen geometría que limpiar
// Remover objeto hidráulico de la simulación
if (SimHydraulicTank != null)
{
hydraulicSimulationManager.Remove(SimHydraulicTank);
SimHydraulicTank = null;
}
}

View File

@ -1,10 +1,10 @@
<UserControl x:Class="CtrEditor.ObjetosSim.HydraulicComponents.ucHydPipe"
<UserControl x:Class="CtrEditor.ObjetosSim.ucHydPipe"
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"
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim"
mc:Ignorable="d">
<UserControl.DataContext>

View File

@ -4,7 +4,7 @@ using System.Windows;
using CtrEditor.ObjetosSim;
using CtrEditor.FuncionesBase;
namespace CtrEditor.ObjetosSim.HydraulicComponents
namespace CtrEditor.ObjetosSim
{
/// <summary>
/// Interaction logic for ucHydPipe.xaml

View File

@ -1,10 +1,10 @@
<UserControl x:Class="CtrEditor.ObjetosSim.HydraulicComponents.ucHydPump"
<UserControl x:Class="CtrEditor.ObjetosSim.ucHydPump"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:CtrEditor.ObjetosSim"
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim.HydraulicComponents"
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim"
mc:Ignorable="d">
<!-- DataContext se establece desde el objeto padre, no aquí -->

View File

@ -6,7 +6,7 @@ using System.Windows.Media;
using CtrEditor.ObjetosSim;
using CtrEditor.FuncionesBase;
namespace CtrEditor.ObjetosSim.HydraulicComponents
namespace CtrEditor.ObjetosSim
{
/// <summary>
/// UserControl para la bomba hidráulica

View File

@ -1,10 +1,10 @@
<UserControl x:Class="CtrEditor.ObjetosSim.HydraulicComponents.ucHydTank"
<UserControl x:Class="CtrEditor.ObjetosSim.ucHydTank"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:CtrEditor.ObjetosSim"
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim.HydraulicComponents"
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim"
mc:Ignorable="d">
<!-- DataContext se establece desde el objeto padre, no aquí -->

View File

@ -8,7 +8,7 @@ using System.Diagnostics;
using CtrEditor.ObjetosSim;
using CtrEditor.FuncionesBase;
namespace CtrEditor.ObjetosSim.HydraulicComponents
namespace CtrEditor.ObjetosSim
{
/// <summary>
/// UserControl para el tanque hidráulico osHydTank

View File

@ -1,5 +1,6 @@
using Newtonsoft.Json;
using System.Windows.Controls;
using CtrEditor.HydraulicSimulator;
using CtrEditor.Simulacion;
using System.Reflection;
using System;
@ -77,7 +78,7 @@ namespace CtrEditor.ObjetosSim
return instance;
}
public static void AssignDatos(UserControl userControl, osBase datos, SimulationManagerBEPU simulationManager)
public static void AssignDatos(UserControl userControl, osBase datos, SimulationManagerBEPU simulationManager, HydraulicSimulationManager hydraulicSimulationManager = null)
{
if (userControl is IDataContainer dataContainer)
{
@ -85,6 +86,7 @@ namespace CtrEditor.ObjetosSim
userControl.DataContext = datos;
datos.VisualRepresentation = userControl;
datos.simulationManager = simulationManager;
datos.hydraulicSimulationManager = hydraulicSimulationManager;
}
}

View File

@ -1,5 +1,6 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CtrEditor.FuncionesBase;
using CtrEditor.HydraulicSimulator;
using CtrEditor.Serialization;
using CtrEditor.Services;
using CtrEditor.Simulacion;
@ -45,19 +46,22 @@ namespace CtrEditor.ObjetosSim
private MainViewModel? _mainViewModel;
private UserControl? VisualRepresentation;
private SimulationManagerBEPU? simulationManager;
private HydraulicSimulationManager? hydraulicSimulationManager;
public DataSaveToSerialize(MainViewModel a, UserControl b, SimulationManagerBEPU c)
public DataSaveToSerialize(MainViewModel a, UserControl b, SimulationManagerBEPU c, HydraulicSimulationManager d = null)
{
_mainViewModel = a;
VisualRepresentation = b;
simulationManager = c;
hydraulicSimulationManager = d;
}
public void DataRestoreAfterSerialize(out MainViewModel a, out UserControl b, out SimulationManagerBEPU c)
public void DataRestoreAfterSerialize(out MainViewModel a, out UserControl b, out SimulationManagerBEPU c, out HydraulicSimulationManager d)
{
a = _mainViewModel;
b = VisualRepresentation;
c = simulationManager;
d = hydraulicSimulationManager;
}
}
@ -1054,16 +1058,23 @@ namespace CtrEditor.ObjetosSim
[JsonIgnore]
public SimulationManagerBEPU simulationManager;
/// <summary>
/// Link al Simulador hidráulico.
/// </summary>
[JsonIgnore]
public HydraulicSimulationManager hydraulicSimulationManager;
/// <summary>
/// Prepara la clase para ser serializable poniendo a null los objetos que tienen referencias circulares
/// Luego se debe llamar a RestaurarDatosNoSerializables para restaurar el estado original
/// </summary>
public void SalvarDatosNoSerializables()
{
DataSave = new DataSaveToSerialize(_mainViewModel, _visualRepresentation, simulationManager);
DataSave = new DataSaveToSerialize(_mainViewModel, _visualRepresentation, simulationManager, hydraulicSimulationManager);
_mainViewModel = null;
_visualRepresentation = null;
simulationManager = null;
hydraulicSimulationManager = null;
}
/// <summary>
@ -1072,7 +1083,7 @@ namespace CtrEditor.ObjetosSim
public void RestaurarDatosNoSerializables()
{
if (DataSave == null) return;
DataSave.DataRestoreAfterSerialize(out _mainViewModel, out _visualRepresentation, out simulationManager);
DataSave.DataRestoreAfterSerialize(out _mainViewModel, out _visualRepresentation, out simulationManager, out hydraulicSimulationManager);
}
/// <summary>
/// Se llama una unica vez al conectar con el PLC.