diff --git a/DataStates/StateManager.cs b/DataStates/StateManager.cs index 372686d..884af89 100644 --- a/DataStates/StateManager.cs +++ b/DataStates/StateManager.cs @@ -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) diff --git a/HydraulicSimulator/HydraulicSimulationManager.cs b/HydraulicSimulator/HydraulicSimulationManager.cs index 3138473..fb4bd1b 100644 --- a/HydraulicSimulator/HydraulicSimulationManager.cs +++ b/HydraulicSimulator/HydraulicSimulationManager.cs @@ -451,6 +451,13 @@ namespace CtrEditor.HydraulicSimulator /// 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 /// 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 } } + /// + /// Aplica los resultados de la simulación a un objeto específico + /// + 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; + } + } + } + /// /// Aplica los resultados a un objeto específico /// @@ -729,5 +796,103 @@ namespace CtrEditor.HydraulicSimulator } #endregion + + #region Hydraulic Object Creation + + /// + /// Lista de objetos hidráulicos específicos del simulador + /// + public List HydraulicSimObjects { get; private set; } = new List(); + + /// + /// Crea una bomba hidráulica en la simulación + /// + 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; + } + + /// + /// Crea un tanque hidráulico en la simulación + /// + 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; + } + + /// + /// Crea una tubería hidráulica en la simulación + /// + 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; + } + + /// + /// Remueve un objeto hidráulico de la simulación + /// + public void Remove(simHydraulicBase hydraulicObject) + { + if (hydraulicObject != null && HydraulicSimObjects.Contains(hydraulicObject)) + { + HydraulicSimObjects.Remove(hydraulicObject); + _networkNeedsRebuild = true; + + Debug.WriteLine($"Objeto hidráulico removido: {hydraulicObject.SimObjectType}"); + } + } + + /// + /// Limpia todos los objetos hidráulicos específicos + /// + public void ClearHydraulicSimObjects() + { + HydraulicSimObjects.Clear(); + _networkNeedsRebuild = true; + + Debug.WriteLine("Todos los objetos hidráulicos específicos han sido limpiados"); + } + + #endregion } } diff --git a/HydraulicSimulator/simHydraulicBase.cs b/HydraulicSimulator/simHydraulicBase.cs new file mode 100644 index 0000000..c4ee0c1 --- /dev/null +++ b/HydraulicSimulator/simHydraulicBase.cs @@ -0,0 +1,179 @@ +using System.Numerics; +using System.ComponentModel; +using CtrEditor.ObjetosSim; + +namespace CtrEditor.HydraulicSimulator +{ + /// + /// Clase base para objetos hidráulicos en la simulación + /// Similar a simBase pero para el sistema hidráulico + /// + 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; + } + + /// + /// Método virtual para actualizar propiedades específicas del objeto + /// + public virtual void UpdateProperties() + { + // Implementación base - puede ser sobrescrita + } + + /// + /// Método virtual para aplicar resultados de la simulación al objeto WPF + /// + public virtual void ApplySimulationResults() + { + // Implementación base - puede ser sobrescrita + } + } + + /// + /// Representa una bomba hidráulica en la simulación + /// + 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); + } + } + } + + /// + /// Representa un tanque hidráulico en la simulación + /// + 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); + } + } + } + + /// + /// Representa una tubería hidráulica en la simulación + /// + 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 + } + } + } +} diff --git a/MainViewModel.cs b/MainViewModel.cs index 7db8adc..900e8d2 100644 --- a/MainViewModel.cs +++ b/MainViewModel.cs @@ -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(); diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs index 439d529..c47f914 100644 --- a/MainWindow.xaml.cs +++ b/MainWindow.xaml.cs @@ -971,7 +971,37 @@ namespace CtrEditor try { var serializedData = JsonConvert.SerializeObject(original, settings); + Debug.WriteLine($"CreateCopyUsingExistingLogic - Serialized data: {serializedData}"); + copy = JsonConvert.DeserializeObject(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 { diff --git a/ObjetosSim/HydraulicComponents/osHydPipe.cs b/ObjetosSim/HydraulicComponents/osHydPipe.cs index 3479c5a..12eda05 100644 --- a/ObjetosSim/HydraulicComponents/osHydPipe.cs +++ b/ObjetosSim/HydraulicComponents/osHydPipe.cs @@ -12,13 +12,15 @@ using Xceed.Wpf.Toolkit.PropertyGrid.Attributes; using CommunityToolkit.Mvvm.ComponentModel; using LibS7Adv; -namespace CtrEditor.ObjetosSim.HydraulicComponents +namespace CtrEditor.ObjetosSim { /// /// Tubería hidráulica que conecta componentes del sistema hidráulico /// - 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 diff --git a/ObjetosSim/HydraulicComponents/osHydPump.cs b/ObjetosSim/HydraulicComponents/osHydPump.cs index 183a7ad..b64e392 100644 --- a/ObjetosSim/HydraulicComponents/osHydPump.cs +++ b/ObjetosSim/HydraulicComponents/osHydPump.cs @@ -11,13 +11,16 @@ using Newtonsoft.Json; using Xceed.Wpf.Toolkit.PropertyGrid.Attributes; using CommunityToolkit.Mvvm.ComponentModel; -namespace CtrEditor.ObjetosSim.HydraulicComponents +namespace CtrEditor.ObjetosSim { /// /// Bomba hidráulica que implementa las interfaces del simulador hidráulico /// - 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 el color según el estado - if (IsRunning) - ColorButton_oculto = Brushes.Green; - else - ColorButton_oculto = Brushes.Gray; + // 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,23 +398,13 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents return CurrentPressure; } - - // Helper Methods - /// - /// Invalida la red hidráulica para forzar reconstrucción - /// private void InvalidateHydraulicNetwork() { - // Si tenemos acceso al MainViewModel, invalidar la red - if (_mainViewModel?.hydraulicSimulationManager != null) - { - _mainViewModel.hydraulicSimulationManager.InvalidateNetwork(); - } + hydraulicSimulationManager?.InvalidateNetwork(); } - } /// diff --git a/ObjetosSim/HydraulicComponents/osHydTank.cs b/ObjetosSim/HydraulicComponents/osHydTank.cs index ad1782c..6b4e25c 100644 --- a/ObjetosSim/HydraulicComponents/osHydTank.cs +++ b/ObjetosSim/HydraulicComponents/osHydTank.cs @@ -13,13 +13,15 @@ using Xceed.Wpf.Toolkit.PropertyGrid.Attributes; using CommunityToolkit.Mvvm.ComponentModel; using LibS7Adv; -namespace CtrEditor.ObjetosSim.HydraulicComponents +namespace CtrEditor.ObjetosSim { /// /// Tanque hidráulico con gestión dinámica de nivel y presión configurable /// - 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")] @@ -88,25 +83,37 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents // Constructor public osHydTank() { - Nombre = "Tanque Hidráulico"; - Tamano = 1.0f; // Usar un tamaño mayor para mayor visibilidad - // Inicializar dimensiones usando las propiedades de osBase - Ancho = 0.30f; - Alto = 0.40f; - 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 + // Inicializar el lock primero para thread safety EnsureLockInitialized(); - // 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}"); + try + { + Nombre = "Tanque Hidráulico"; + Tamano = 1.0f; // Usar un tamaño mayor para mayor visibilidad + // Inicializar dimensiones usando las propiedades de osBase + Ancho = 0.30f; + Alto = 0.40f; + Angulo = 0f; + // Asegurar que el movimiento esté habilitado + Lock_movement = false; + + // 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(); + } } @@ -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 flows, Dictionary 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; + } } diff --git a/ObjetosSim/HydraulicComponents/ucHydPipe.xaml b/ObjetosSim/HydraulicComponents/ucHydPipe.xaml index 728d2d8..7973eb9 100644 --- a/ObjetosSim/HydraulicComponents/ucHydPipe.xaml +++ b/ObjetosSim/HydraulicComponents/ucHydPipe.xaml @@ -1,10 +1,10 @@ - diff --git a/ObjetosSim/HydraulicComponents/ucHydPipe.xaml.cs b/ObjetosSim/HydraulicComponents/ucHydPipe.xaml.cs index 8e60b80..7f1b75b 100644 --- a/ObjetosSim/HydraulicComponents/ucHydPipe.xaml.cs +++ b/ObjetosSim/HydraulicComponents/ucHydPipe.xaml.cs @@ -4,7 +4,7 @@ using System.Windows; using CtrEditor.ObjetosSim; using CtrEditor.FuncionesBase; -namespace CtrEditor.ObjetosSim.HydraulicComponents +namespace CtrEditor.ObjetosSim { /// /// Interaction logic for ucHydPipe.xaml diff --git a/ObjetosSim/HydraulicComponents/ucHydPump.xaml b/ObjetosSim/HydraulicComponents/ucHydPump.xaml index 74a7ed6..9d73d66 100644 --- a/ObjetosSim/HydraulicComponents/ucHydPump.xaml +++ b/ObjetosSim/HydraulicComponents/ucHydPump.xaml @@ -1,10 +1,10 @@ - diff --git a/ObjetosSim/HydraulicComponents/ucHydPump.xaml.cs b/ObjetosSim/HydraulicComponents/ucHydPump.xaml.cs index 39ef95f..fb8e761 100644 --- a/ObjetosSim/HydraulicComponents/ucHydPump.xaml.cs +++ b/ObjetosSim/HydraulicComponents/ucHydPump.xaml.cs @@ -6,7 +6,7 @@ using System.Windows.Media; using CtrEditor.ObjetosSim; using CtrEditor.FuncionesBase; -namespace CtrEditor.ObjetosSim.HydraulicComponents +namespace CtrEditor.ObjetosSim { /// /// UserControl para la bomba hidráulica diff --git a/ObjetosSim/HydraulicComponents/ucHydTank.xaml b/ObjetosSim/HydraulicComponents/ucHydTank.xaml index 49cb02a..cbb6eec 100644 --- a/ObjetosSim/HydraulicComponents/ucHydTank.xaml +++ b/ObjetosSim/HydraulicComponents/ucHydTank.xaml @@ -1,10 +1,10 @@ - diff --git a/ObjetosSim/HydraulicComponents/ucHydTank.xaml.cs b/ObjetosSim/HydraulicComponents/ucHydTank.xaml.cs index 7e9aebf..eada20c 100644 --- a/ObjetosSim/HydraulicComponents/ucHydTank.xaml.cs +++ b/ObjetosSim/HydraulicComponents/ucHydTank.xaml.cs @@ -8,7 +8,7 @@ using System.Diagnostics; using CtrEditor.ObjetosSim; using CtrEditor.FuncionesBase; -namespace CtrEditor.ObjetosSim.HydraulicComponents +namespace CtrEditor.ObjetosSim { /// /// UserControl para el tanque hidráulico osHydTank diff --git a/ObjetosSim/UserControlFactory.cs b/ObjetosSim/UserControlFactory.cs index 271b6e5..4aab89e 100644 --- a/ObjetosSim/UserControlFactory.cs +++ b/ObjetosSim/UserControlFactory.cs @@ -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; } } diff --git a/ObjetosSim/osBase.cs b/ObjetosSim/osBase.cs index ba4b314..230fee4 100644 --- a/ObjetosSim/osBase.cs +++ b/ObjetosSim/osBase.cs @@ -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; + /// + /// Link al Simulador hidráulico. + /// + [JsonIgnore] + public HydraulicSimulationManager hydraulicSimulationManager; + /// /// 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 /// public void SalvarDatosNoSerializables() { - DataSave = new DataSaveToSerialize(_mainViewModel, _visualRepresentation, simulationManager); + DataSave = new DataSaveToSerialize(_mainViewModel, _visualRepresentation, simulationManager, hydraulicSimulationManager); _mainViewModel = null; _visualRepresentation = null; simulationManager = null; + hydraulicSimulationManager = null; } /// @@ -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); } /// /// Se llama una unica vez al conectar con el PLC.