Refactor hydraulic components: Enhance UI elements and add flow indicators

- Updated ucHydPipe.xaml to improve visual representation with drop shadows and flow indicators.
- Modified ucHydPump.xaml to include a background ellipse and improved status indicators.
- Simplified ucHydTank.xaml by removing unnecessary sections and enhancing the fluid level display.
- Added a new method CopyFrom in osBase.cs to facilitate property copying between objects.
This commit is contained in:
Miguel 2025-09-11 14:52:45 +02:00
parent 1df7a24140
commit 1b21f86886
9 changed files with 1107 additions and 2766 deletions

View File

@ -120,7 +120,7 @@ namespace CtrEditor.HydraulicSimulator.TSNet
foreach (var node in tankNodes)
{
var elevation = GetNodeElevation(node);
content.AppendLine($" {node.Name,-15}\t{elevation:F2} \t1.0 \t0.0 \t2.0 \t1.0 \t0 \t;");
content.AppendLine($" {node.Name,-15}\t{elevation:F2} \t1.0 \t0.0 \t2.0 \t1.0 \t0 \t");
}
content.AppendLine();
@ -158,8 +158,9 @@ namespace CtrEditor.HydraulicSimulator.TSNet
{
foreach (var element in branch.Elements.OfType<PumpHQ>())
{
var id = $"PUMP{pumpId++}";
var id = $"PUMP{pumpId}";
content.AppendLine($" {id,-15}\t{branch.N1,-15}\t{branch.N2,-15}\tHEAD CURVE{pumpId}");
pumpId++;
}
}

View File

@ -2046,6 +2046,29 @@ namespace CtrEditor
#endregion
#region Hydraulic Network Management
/// <summary>
/// Invalidates the hydraulic network to force recalculation
/// </summary>
public void InvalidateHydraulicNetwork()
{
try
{
if (hydraulicSimulationManager != null)
{
hydraulicSimulationManager.InvalidateNetwork();
Debug.WriteLine("Red hidráulica invalidada y marcada para recálculo");
}
}
catch (Exception ex)
{
Debug.WriteLine($"Error al invalidar red hidráulica: {ex.Message}");
}
}
#endregion
#region MCP Server Management
/// <summary>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -7,9 +7,7 @@
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim"
mc:Ignorable="d">
<UserControl.DataContext>
<vm:osHydPipe Ancho="2.0" Alto="0.2"/>
</UserControl.DataContext>
<!-- DataContext se establece desde el objeto padre -->
<Canvas RenderTransformOrigin="0,0">
<Canvas.RenderTransform>
@ -29,9 +27,13 @@
Stroke="Black"
StrokeThickness="2"
RadiusX="5"
RadiusY="5"/>
RadiusY="5">
<Rectangle.Effect>
<DropShadowEffect Color="Gray" ShadowDepth="2" Opacity="0.3"/>
</Rectangle.Effect>
</Rectangle>
<!-- Etiqueta con Viewbox para escalado -->
<!-- Etiqueta con nombre de la tubería -->
<Viewbox Width="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}}"
Height="{Binding Alto, Converter={StaticResource MeterToPixelConverter}}"
Stretch="Uniform">
@ -46,17 +48,43 @@
<!-- Indicador de flujo -->
<Border x:Name="FlowIndicator"
Background="Yellow"
CornerRadius="2"
Visibility="Collapsed"
Canvas.Top="-20"
Canvas.Left="{Binding Ancho, Converter={StaticResource MeterToPixelConverter}, ConverterParameter=0.5}">
<TextBlock x:Name="FlowText"
Text="0.00"
FontSize="6"
Foreground="Black"
Margin="2"/>
Background="#AA000000"
CornerRadius="3"
Padding="3,1"
Canvas.Top="-25"
Canvas.Left="5">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding CurrentFlowLMin, StringFormat='{}{0:F1} L/min'}"
FontSize="8"
Foreground="White"
Margin="0,0,3,0"/>
<TextBlock Text="{Binding PressureDropBar, StringFormat='{}{0:F2} bar'}"
FontSize="8"
Foreground="Yellow"/>
</StackPanel>
</Border>
<!-- Indicador de dirección de flujo -->
<Path x:Name="FlowArrow"
Stroke="Yellow"
StrokeThickness="2"
Fill="Yellow"
Visibility="{Binding HasFlow, Converter={StaticResource BooleanToVisibilityConverter}}">
<Path.Data>
<GeometryGroup>
<LineGeometry StartPoint="10,0" EndPoint="30,0"/>
<PathGeometry>
<PathFigure StartPoint="25,-5">
<LineSegment Point="35,0"/>
<LineSegment Point="25,5"/>
</PathFigure>
</PathGeometry>
</GeometryGroup>
</Path.Data>
<Path.RenderTransform>
<TranslateTransform X="10" Y="{Binding Alto, Converter={StaticResource MeterToPixelConverter}, ConverterParameter=0.5}"/>
</Path.RenderTransform>
</Path>
</Canvas>
</UserControl>

View File

@ -7,7 +7,7 @@
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim"
mc:Ignorable="d">
<!-- DataContext se establece desde el objeto padre, no aquí -->
<!-- DataContext se establece desde el objeto padre -->
<Grid RenderTransformOrigin="0,0">
<Grid.RenderTransform>
@ -22,18 +22,47 @@
</Grid.RowDefinitions>
<!-- Imagen de la bomba -->
<Image Grid.Row="0" Source="{Binding ImageSource_oculta}"
Width="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}}"
<Grid Grid.Row="0">
<!-- Círculo de fondo para la bomba -->
<Ellipse Width="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}}"
Height="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}}"
Fill="{Binding ColorButton_oculto}"
Stroke="Black"
StrokeThickness="2">
<Ellipse.Effect>
<DropShadowEffect Color="Gray" ShadowDepth="2" Opacity="0.5"/>
</Ellipse.Effect>
</Ellipse>
<!-- Imagen de la bomba si está disponible -->
<Image Source="{Binding ImageSource_oculta}"
Width="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}, ConverterParameter=0.8}"
Height="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}, ConverterParameter=0.8}"
Stretch="Uniform"/>
<!-- Indicador visual de estado si no hay imagen -->
<Grid>
<TextBlock Text="P" FontSize="18" FontWeight="Bold"
Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<!-- Indicador de funcionamiento -->
<Ellipse Width="8" Height="8"
Fill="{Binding IsRunning, Converter={StaticResource BoolToColorConverter}}"
HorizontalAlignment="Right" VerticalAlignment="Top"
Margin="0,2,2,0"/>
</Grid>
</Grid>
<!-- Panel de información con presión y caudal -->
<StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Center">
<Border Grid.Row="1" Background="#AA000000" CornerRadius="3" Padding="3" Margin="0,2,0,0">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<TextBlock Text="{Binding CurrentPressureBar, StringFormat='{}{0:F1} bar'}"
Foreground="White" Background="Blue" Padding="2" Margin="1" FontSize="8"/>
<TextBlock Text="{Binding CurrentFlowLMin, StringFormat='{}{0:F1} L/min'}"
Foreground="White" Background="Green" Padding="2" Margin="1" FontSize="8"/>
<TextBlock Text="{Binding CurrentHead, StringFormat='{}{0:F1} m'}"
Foreground="White" Background="Orange" Padding="2" Margin="1" FontSize="8"/>
</StackPanel>
</Border>
</Grid>
</UserControl>

View File

@ -7,10 +7,7 @@
xmlns:vm="clr-namespace:CtrEditor.ObjetosSim"
mc:Ignorable="d">
<!-- DataContext se establece desde el objeto padre, no aquí -->
<UserControl.DataContext>
<vm:osHydTank />
</UserControl.DataContext>
<!-- DataContext se establece desde el objeto padre -->
<Canvas RenderTransformOrigin="0,0">
<Canvas.RenderTransform>
@ -22,18 +19,21 @@
</TransformGroup>
</Canvas.RenderTransform>
<!-- Contenedor principal del tanque con diseño mejorado -->
<!-- Contenedor principal del tanque -->
<Grid Width="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}}"
Height="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}, ConverterParameter=1.4}">
<!-- Fondo del tanque (contenedor vacío) con diseño 3D -->
<!-- Fondo del tanque (contenedor vacío) -->
<Rectangle x:Name="rectTankContainer"
Fill="#FFE6E6E6"
Stroke="#FF606060"
StrokeThickness="3"
RadiusX="8"
RadiusY="8"
Effect="{StaticResource DropShadowEffect}"/>
RadiusY="8">
<Rectangle.Effect>
<DropShadowEffect Color="Gray" ShadowDepth="3" Opacity="0.5"/>
</Rectangle.Effect>
</Rectangle>
<!-- Marco interno del tanque -->
<Rectangle Fill="Transparent"
@ -43,132 +43,40 @@
RadiusY="6"
Margin="6"/>
<!-- Sistema de tres secciones (solo si está habilitado) -->
<Grid x:Name="ThreeSectionContainer"
Margin="8"
Visibility="{Binding EnableThreeSection, Converter={StaticResource BooleanToVisibilityConverter}}">
<!-- Sección secundaria (abajo) -->
<Rectangle x:Name="rectSecondaryLevel"
Fill="{Binding SecondaryLevelColor}"
Stroke="#FF1E90FF"
StrokeThickness="1.5"
RadiusX="4"
RadiusY="4"
VerticalAlignment="Bottom"
Opacity="0.85">
<Rectangle.Height>
<MultiBinding Converter="{StaticResource TankLevelToHeightConverter}">
<Binding Path="SecondaryPercentage"/>
<Binding Path="Tamano"/>
<Binding Source="0.33"/>
</MultiBinding>
</Rectangle.Height>
</Rectangle>
<!-- Sección de mezcla (medio) -->
<Rectangle x:Name="rectMixLevel"
Fill="{Binding MixLevelColor}"
Stroke="#FFFF6347"
StrokeThickness="1.5"
RadiusX="4"
RadiusY="4"
VerticalAlignment="Bottom"
Opacity="0.85">
<Rectangle.Height>
<MultiBinding Converter="{StaticResource TankLevelToHeightConverter}">
<Binding Path="PrimaryPercentage"/>
<Binding Path="Tamano"/>
<Binding Source="0.33"/>
</MultiBinding>
</Rectangle.Height>
<Rectangle.Margin>
<MultiBinding Converter="{StaticResource TankSectionMarginConverter}">
<Binding Path="SecondaryPercentage"/>
<Binding Path="Tamano"/>
<Binding Source="0.33"/>
</MultiBinding>
</Rectangle.Margin>
</Rectangle>
<!-- Etiquetas de sección -->
<Canvas>
<!-- Etiqueta sección secundaria -->
<Border Background="#AA1E90FF" CornerRadius="2" Padding="2,1"
Canvas.Left="2" Canvas.Bottom="2">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Sec:" Foreground="White" FontSize="6" FontWeight="Bold"/>
<TextBlock Text="{Binding SecondaryPercentage, StringFormat='{}{0:F0}%'}"
Foreground="White" FontSize="6" Margin="1,0,0,0"/>
<TextBlock Text="{Binding SecondaryVolumeL, StringFormat=' ({0:F0}L)'}"
Foreground="White" FontSize="5" Opacity="0.9"/>
</StackPanel>
</Border>
<!-- Etiqueta sección primaria -->
<Border Background="#AAFF6347" CornerRadius="2" Padding="2,1"
Canvas.Right="2" Canvas.Top="2">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Pri:" Foreground="White" FontSize="6" FontWeight="Bold"/>
<TextBlock Text="{Binding PrimaryPercentage, StringFormat='{}{0:F0}%'}"
Foreground="White" FontSize="6" Margin="1,0,0,0"/>
<TextBlock Text="{Binding PrimaryVolumeL, StringFormat=' ({0:F0}L)'}"
Foreground="White" FontSize="5" Opacity="0.9"/>
</StackPanel>
</Border>
</Canvas>
</Grid>
<!-- Sistema de una sección (clásico) -->
<Grid x:Name="SingleSectionContainer"
Margin="8"
Visibility="{Binding EnableThreeSection, Converter={StaticResource InverseBooleanToVisibilityConverter}}">
<!-- Nivel del líquido tradicional -->
<!-- Nivel del líquido -->
<Rectangle x:Name="rectFluidLevel"
Fill="{Binding LevelColor}"
Stroke="{Binding LevelBorderColor}"
Fill="{Binding ColorButton_oculto}"
Stroke="DarkBlue"
StrokeThickness="1.5"
RadiusX="4"
RadiusY="4"
VerticalAlignment="Bottom"
Opacity="0.85">
Opacity="0.85"
Margin="8">
<Rectangle.Height>
<MultiBinding Converter="{StaticResource TankLevelToHeightConverter}">
<Binding Path="FillPercentage"/>
<Binding Path="Tamano"/>
<Binding Source="1.0"/>
<Binding Path="Tamano" Converter="{StaticResource MeterToPixelConverter}" ConverterParameter="1.2"/>
</MultiBinding>
</Rectangle.Height>
</Rectangle>
</Grid>
<!-- Información central del tanque -->
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<!-- Nivel total y volumen -->
<Border Background="#AA000000" CornerRadius="4" Padding="6,4">
<StackPanel HorizontalAlignment="Center">
<!-- Porcentaje principal -->
<TextBlock Text="{Binding FillPercentage, StringFormat='{}{0:F0}%'}"
HorizontalAlignment="Center"
Foreground="White"
FontWeight="Bold"
FontSize="14"/>
<!-- Volumen actual -->
<TextBlock Text="{Binding CurrentVolumeL, StringFormat='{}{0:F0} L'}"
HorizontalAlignment="Center"
Foreground="LightCyan"
FontSize="8"
Margin="0,1,0,0"/>
<!-- Nivel sobre el mar -->
<TextBlock Text="{Binding CurrentLevelM, StringFormat='⌂ {0:F2} m'}"
HorizontalAlignment="Center"
Foreground="LightGreen"
FontSize="7"
Margin="0,1,0,0"/>
<!-- Nivel y porcentaje -->
<TextBlock Foreground="White" FontSize="10" HorizontalAlignment="Center">
<TextBlock.Text>
<MultiBinding StringFormat="{}{0:F2}m ({1:F0}%)">
<Binding Path="CurrentLevel"/>
<Binding Path="FillPercentage"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<!-- Volumen -->
<TextBlock Text="{Binding CurrentVolume, StringFormat='{}{0:F0}L'}"
Foreground="LightCyan" FontSize="8" HorizontalAlignment="Center"/>
</StackPanel>
</Border>
</Grid>
@ -178,29 +86,18 @@
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- Tipo de fluido actual -->
<Border Grid.Row="1" Background="#FF006400" CornerRadius="3" Padding="3,2" Margin="0,2,0,0">
<TextBlock Text="{Binding CurrentFluidDescription}"
Foreground="White"
FontSize="7"
FontWeight="SemiBold"
TextTrimming="CharacterEllipsis"
MaxWidth="60"/>
<!-- Nombre del tanque -->
<Border Grid.Row="0" Background="#FF4169E1" CornerRadius="3" Padding="3,2">
<TextBlock Text="{Binding Nombre}"
Foreground="White" FontSize="8" FontWeight="Bold"/>
</Border>
<!-- Estado de mezcla -->
<Border Grid.Row="2" Background="#FF8B4513" CornerRadius="3" Padding="3,2" Margin="0,2,0,0"
Visibility="{Binding EnableThreeSection, Converter={StaticResource BooleanToVisibilityConverter}}">
<TextBlock Text="{Binding MixingState}"
Foreground="White"
FontSize="6"
FontWeight="SemiBold"
TextTrimming="CharacterEllipsis"
MaxWidth="60"/>
<!-- Tipo de fluido -->
<Border Grid.Row="1" Background="#FF006400" CornerRadius="3" Padding="3,2" Margin="0,2,0,0">
<TextBlock Text="{Binding FluidDescription}"
Foreground="White" FontSize="7" FontWeight="Bold"/>
</Border>
</Grid>
</Grid>
@ -209,69 +106,15 @@
<Grid Canvas.Top="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}, ConverterParameter=1.5}"
Width="{Binding Tamano, Converter={StaticResource MeterToPixelConverter}}">
<!-- Fondo del panel con gradiente -->
<Border CornerRadius="5" Margin="0,4,0,0" Padding="4">
<Border Background="#AA000000" CornerRadius="5" Margin="0,4,0,0" Padding="4">
<StackPanel Orientation="Vertical" HorizontalAlignment="Center">
<!-- Fila 1: Presión y Nivel absoluto -->
<!-- Fila 1: Presión y Flujo -->
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,2">
<!-- Presión actual -->
<Border Background="#FF1E90FF" CornerRadius="3" Padding="4,3" Margin="2">
<StackPanel Orientation="Horizontal">
<TextBlock Text="⚡" Foreground="White" FontSize="9" FontWeight="Bold"/>
<TextBlock Text="{Binding CurrentPressure, StringFormat='{}{0:F2}'}"
Foreground="White" FontSize="8" FontWeight="SemiBold" Margin="2,0,0,0"/>
<TextBlock Text="bar" Foreground="LightCyan" FontSize="7" Opacity="0.9" Margin="1,0,0,0"/>
<TextBlock Text="{Binding CurrentPressure, StringFormat='{}{0:F2} bar'}"
Foreground="White" Background="Blue" Padding="2" Margin="1" FontSize="7"/>
<TextBlock Text="{Binding CurrentFlow, StringFormat='{}{0:F3} m³/s'}"
Foreground="White" Background="Green" Padding="2" Margin="1" FontSize="7"/>
</StackPanel>
</Border>
<!-- Nivel absoluto -->
<Border Background="#FF32CD32" CornerRadius="3" Padding="4,3" Margin="2">
<StackPanel Orientation="Horizontal">
<TextBlock Text="🌊" Foreground="White" FontSize="9" FontWeight="Bold"/>
<TextBlock Text="{Binding CurrentLevelM, StringFormat='{}{0:F3}'}"
Foreground="White" FontSize="8" FontWeight="SemiBold" Margin="2,0,0,0"/>
<TextBlock Text="m" Foreground="LightGreen" FontSize="7" Opacity="0.9" Margin="1,0,0,0"/>
</StackPanel>
</Border>
</StackPanel>
<!-- Fila 2: Flujo neto -->
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,2">
<!-- Flujo neto -->
<Border Background="{Binding FlowBalanceColor}" CornerRadius="3" Padding="4,3" Margin="2">
<StackPanel Orientation="Horizontal">
<TextBlock Text="⇄" Foreground="White" FontSize="9" FontWeight="Bold"/>
<TextBlock Text="{Binding NetFlow, StringFormat='{}{0:F1}'}"
Foreground="White" FontSize="8" FontWeight="SemiBold" Margin="2,0,0,0"/>
<TextBlock Text="L/min" Foreground="White" FontSize="6" Opacity="0.9" Margin="1,0,0,0"/>
</StackPanel>
</Border>
</StackPanel>
<!-- Fila 3: Volumen secundario y mix (simplificado) -->
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,2">
<!-- Volumen secundario -->
<Border Background="#AA1E90FF" CornerRadius="3" Padding="3,2" Margin="1">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Sec:" Foreground="White" FontSize="7" FontWeight="Bold"/>
<TextBlock Text="{Binding SecondaryVolumeL, StringFormat='{}{0:F0}L'}"
Foreground="White" FontSize="7" Margin="1,0,0,0"/>
</StackPanel>
</Border>
<!-- Volumen de mezcla -->
<Border Background="#AA8B4513" CornerRadius="3" Padding="3,2" Margin="1">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Mix:" Foreground="White" FontSize="7" FontWeight="Bold"/>
<TextBlock Text="{Binding MixingVolumeL, StringFormat='{}{0:F0}L'}"
Foreground="White" FontSize="7" Margin="1,0,0,0"/>
</StackPanel>
</Border>
</StackPanel>
</StackPanel>
</Border>
</Grid>

View File

@ -1867,6 +1867,28 @@ namespace CtrEditor.ObjetosSim
System.Diagnostics.Debug.WriteLine($"[Dimensions] Cleared ALL stored dimensions");
}
/// <summary>
/// Copia propiedades comunes desde otro objeto osBase
/// </summary>
/// <param name="source">Objeto fuente para copiar</param>
public virtual void CopyFrom(osBase source)
{
if (source == null) return;
try
{
// Copiar propiedades básicas comunes que están disponibles en osBase
Nombre = source.Nombre;
// Nota: X, Y, Width, Height no están definidas en osBase base
// Las clases derivadas pueden override este método para copiar sus propiedades específicas
}
catch (Exception ex)
{
Debug.WriteLine($"Error en CopyFrom: {ex.Message}");
}
}
}
public class UniqueId
{