Refactor TSNet Hydraulic Simulation Integration
- Implemented node name sanitization in TSNetINPGenerator to ensure compatibility with EPANET INP format. - Enhanced TSNetSimulationManager to manage hydraulic components and elements more robustly, including improved error handling and logging. - Replaced HydraulicSimulationManager with TSNetSimulationManager in MainViewModel, updating all relevant references and methods. - Improved handling of IOException tracking in MainViewModel for better debugging and stability during simulation runs. - Updated osHydPipe, osHydPump, and osHydTank classes to utilize sanitized node names for hydraulic nodes. - Added new methods for resetting and clearing hydraulic objects in TSNetSimulationManager. - Enhanced UserControlFactory to support the new TSNetSimulationManager without assigning the old hydraulic simulation manager.
This commit is contained in:
parent
1b21f86886
commit
51d0f36187
|
@ -272,7 +272,7 @@ namespace CtrEditor
|
||||||
|
|
||||||
if (userControl != null)
|
if (userControl != null)
|
||||||
{
|
{
|
||||||
UserControlFactory.AssignDatos(userControl, osObjeto, _mainViewModel.simulationManager, _mainViewModel.hydraulicSimulationManager);
|
UserControlFactory.AssignDatos(userControl, osObjeto, _mainViewModel.simulationManager, _mainViewModel.tsnetSimulationManager);
|
||||||
osObjeto._mainViewModel = _mainViewModel;
|
osObjeto._mainViewModel = _mainViewModel;
|
||||||
|
|
||||||
if (osObjeto.Id == null)
|
if (osObjeto.Id == null)
|
||||||
|
|
|
@ -228,14 +228,36 @@ namespace CtrEditor.HydraulicSimulator
|
||||||
|
|
||||||
foreach (var elemDef in elementDefinitions)
|
foreach (var elemDef in elementDefinitions)
|
||||||
{
|
{
|
||||||
// Crear rama con el elemento
|
try
|
||||||
var elements = new List<Element> { elemDef.Element };
|
|
||||||
Network.AddBranch(elemDef.FromNode, elemDef.ToNode, elements, elemDef.Name);
|
|
||||||
|
|
||||||
if (VerboseOutput)
|
|
||||||
{
|
{
|
||||||
Trace.WriteLine($"Rama agregada: {elemDef.Name} " +
|
// Validar que los nodos existan antes de agregar la rama
|
||||||
$"({elemDef.FromNode} -> {elemDef.ToNode})");
|
if (!Network.Nodes.ContainsKey(elemDef.FromNode))
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"ERROR: Nodo '{elemDef.FromNode}' no existe. Nodos disponibles: {string.Join(", ", Network.Nodes.Keys)}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Network.Nodes.ContainsKey(elemDef.ToNode))
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"ERROR: Nodo '{elemDef.ToNode}' no existe. Nodos disponibles: {string.Join(", ", Network.Nodes.Keys)}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Crear rama con el elemento
|
||||||
|
var elements = new List<Element> { elemDef.Element };
|
||||||
|
Network.AddBranch(elemDef.FromNode, elemDef.ToNode, elements, elemDef.Name);
|
||||||
|
|
||||||
|
if (VerboseOutput)
|
||||||
|
{
|
||||||
|
Trace.WriteLine($"Rama agregada: {elemDef.Name} " +
|
||||||
|
$"({elemDef.FromNode} -> {elemDef.ToNode})");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Error agregando rama {elemDef.Name}: {ex.Message}");
|
||||||
|
Debug.WriteLine($" FromNode: '{elemDef.FromNode}', ToNode: '{elemDef.ToNode}'");
|
||||||
|
Debug.WriteLine($" Nodos disponibles: {string.Join(", ", Network.Nodes.Keys)}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -298,17 +298,43 @@ try:
|
||||||
# Cargar el modelo usando WNTR (TSNet usa WNTR internamente)
|
# Cargar el modelo usando WNTR (TSNet usa WNTR internamente)
|
||||||
wn = wntr.network.WaterNetworkModel(r'{inpFilePath}')
|
wn = wntr.network.WaterNetworkModel(r'{inpFilePath}')
|
||||||
|
|
||||||
# Configurar simulación transitoria
|
# Configurar WNTR options para headloss formula
|
||||||
wn.set_time(duration=10.0, dt=0.01) # 10 segundos, dt=0.01s
|
wn.options.hydraulic.headloss = 'D-W' # Darcy-Weisbach
|
||||||
|
wn.options.time.duration = 10.0 # 10 segundos de duración
|
||||||
|
wn.options.time.hydraulic_timestep = 0.01 # Paso de tiempo de 0.01 segundos
|
||||||
|
wn.options.time.report_timestep = 0.01 # Reportar cada 0.01 segundos
|
||||||
|
|
||||||
# Ejecutar simulación
|
# CORRECCIÓN: TSNet.TransientModel necesita el archivo INP, no el objeto WaterNetworkModel
|
||||||
results = tsnet.simulation.run_transient_simulation(wn, results_dir=r'{outputDir}')
|
# Convertir a modelo transient de TSNet usando el archivo INP directamente
|
||||||
|
tm = tsnet.network.TransientModel(r'{inpFilePath}')
|
||||||
|
|
||||||
|
# Ejecutar simulación usando la API correcta de TSNet
|
||||||
|
try:
|
||||||
|
# Método correcto de TSNet
|
||||||
|
results = tsnet.simulation.MOCSimulator(tm, results_obj='results', friction='steady')
|
||||||
|
print('Simulación TSNet completada exitosamente')
|
||||||
|
print(f'Resultados generados en modelo transient')
|
||||||
|
|
||||||
|
# Guardar resultados si es necesario
|
||||||
|
print('Directorio de resultados: ' + r'{outputDir}')
|
||||||
|
|
||||||
|
except Exception as tsnet_error:
|
||||||
|
print('Error en TSNet: ' + str(tsnet_error))
|
||||||
|
# Fallback a simulación básica con WNTR
|
||||||
|
try:
|
||||||
|
import wntr.sim
|
||||||
|
sim = wntr.sim.EpanetSimulator(wn)
|
||||||
|
results = sim.run_sim()
|
||||||
|
print('Ejecutada simulación básica WNTR (fallback)')
|
||||||
|
except Exception as wntr_error:
|
||||||
|
print('Error en WNTR fallback: ' + str(wntr_error))
|
||||||
|
raise tsnet_error
|
||||||
|
|
||||||
print('Simulación completada exitosamente')
|
print('Simulación completada exitosamente')
|
||||||
print(r'Resultados guardados en: {outputDir}')
|
print('Resultados guardados en: ' + r'{outputDir}')
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f'Error en simulación TSNet: {{e}}')
|
print('Error en simulación TSNet: ' + str(e))
|
||||||
raise
|
raise
|
||||||
";
|
";
|
||||||
|
|
||||||
|
|
|
@ -90,7 +90,8 @@ namespace CtrEditor.HydraulicSimulator.TSNet
|
||||||
{
|
{
|
||||||
var elevation = GetNodeElevation(node);
|
var elevation = GetNodeElevation(node);
|
||||||
var demand = GetNodeDemand(node);
|
var demand = GetNodeDemand(node);
|
||||||
content.AppendLine($" {node.Name,-15}\t{elevation.ToString("F2", CultureInfo.InvariantCulture)} \t{demand.ToString("F2", CultureInfo.InvariantCulture)} \t;");
|
var sanitizedName = SanitizeNodeName(node.Name);
|
||||||
|
content.AppendLine($" {sanitizedName,-15}\t{elevation.ToString("F2", CultureInfo.InvariantCulture)} \t{demand.ToString("F2", CultureInfo.InvariantCulture)} \t;");
|
||||||
}
|
}
|
||||||
|
|
||||||
content.AppendLine();
|
content.AppendLine();
|
||||||
|
@ -104,7 +105,8 @@ namespace CtrEditor.HydraulicSimulator.TSNet
|
||||||
foreach (var node in _network.Nodes.Values.Where(n => n.FixedP && !IsTank(n)))
|
foreach (var node in _network.Nodes.Values.Where(n => n.FixedP && !IsTank(n)))
|
||||||
{
|
{
|
||||||
var head = PressureToHead(node.P);
|
var head = PressureToHead(node.P);
|
||||||
content.AppendLine($" {node.Name,-15}\t{head.ToString("F2", CultureInfo.InvariantCulture)} \t;");
|
var sanitizedName = SanitizeNodeName(node.Name);
|
||||||
|
content.AppendLine($" {sanitizedName,-15}\t{head.ToString("F2", CultureInfo.InvariantCulture)} \t;");
|
||||||
}
|
}
|
||||||
|
|
||||||
content.AppendLine();
|
content.AppendLine();
|
||||||
|
@ -120,7 +122,8 @@ namespace CtrEditor.HydraulicSimulator.TSNet
|
||||||
foreach (var node in tankNodes)
|
foreach (var node in tankNodes)
|
||||||
{
|
{
|
||||||
var elevation = GetNodeElevation(node);
|
var elevation = GetNodeElevation(node);
|
||||||
content.AppendLine($" {node.Name,-15}\t{elevation:F2} \t1.0 \t0.0 \t2.0 \t1.0 \t0 \t");
|
var sanitizedName = SanitizeNodeName(node.Name);
|
||||||
|
content.AppendLine($" {sanitizedName,-15}\t{elevation.ToString("F2", CultureInfo.InvariantCulture)} \t1.0 \t0.0 \t2.0 \t1.0 \t0 \t");
|
||||||
}
|
}
|
||||||
|
|
||||||
content.AppendLine();
|
content.AppendLine();
|
||||||
|
@ -141,7 +144,10 @@ namespace CtrEditor.HydraulicSimulator.TSNet
|
||||||
var diameter = element.D * 1000; // Usar D en lugar de Diameter, convertir a mm
|
var diameter = element.D * 1000; // Usar D en lugar de Diameter, convertir a mm
|
||||||
var roughness = element.Rough * 1000; // Usar Rough en lugar de Roughness, convertir a mm
|
var roughness = element.Rough * 1000; // Usar Rough en lugar de Roughness, convertir a mm
|
||||||
|
|
||||||
content.AppendLine($" {id,-15}\t{branch.N1,-15}\t{branch.N2,-15}\t{length.ToString("F2", CultureInfo.InvariantCulture)} \t{diameter.ToString("F1", CultureInfo.InvariantCulture)} \t{roughness.ToString("F4", CultureInfo.InvariantCulture)} \t0 \tOpen");
|
var sanitizedN1 = SanitizeNodeName(branch.N1);
|
||||||
|
var sanitizedN2 = SanitizeNodeName(branch.N2);
|
||||||
|
|
||||||
|
content.AppendLine($" {id,-15}\t{sanitizedN1,-15}\t{sanitizedN2,-15}\t{length.ToString("F2", CultureInfo.InvariantCulture)} \t{diameter.ToString("F1", CultureInfo.InvariantCulture)} \t{roughness.ToString("F4", CultureInfo.InvariantCulture)} \t0 \tOpen");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,7 +165,10 @@ namespace CtrEditor.HydraulicSimulator.TSNet
|
||||||
foreach (var element in branch.Elements.OfType<PumpHQ>())
|
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}");
|
var sanitizedN1 = SanitizeNodeName(branch.N1);
|
||||||
|
var sanitizedN2 = SanitizeNodeName(branch.N2);
|
||||||
|
|
||||||
|
content.AppendLine($" {id,-15}\t{sanitizedN1,-15}\t{sanitizedN2,-15}\tHEAD CURVE{pumpId}");
|
||||||
pumpId++;
|
pumpId++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -213,9 +222,9 @@ namespace CtrEditor.HydraulicSimulator.TSNet
|
||||||
var maxHead = element.H0;
|
var maxHead = element.H0;
|
||||||
var maxFlow = element.H0 / 10; // Estimación simple
|
var maxFlow = element.H0 / 10; // Estimación simple
|
||||||
|
|
||||||
content.AppendLine($" CURVE{curveId} \t0 \t{maxHead:F2}");
|
content.AppendLine($" CURVE{curveId} \t0 \t{maxHead.ToString("F2", CultureInfo.InvariantCulture)}");
|
||||||
content.AppendLine($" CURVE{curveId} \t{maxFlow/2:F2} \t{maxHead*0.8:F2}");
|
content.AppendLine($" CURVE{curveId} \t{(maxFlow/2).ToString("F2", CultureInfo.InvariantCulture)} \t{(maxHead*0.8).ToString("F2", CultureInfo.InvariantCulture)}");
|
||||||
content.AppendLine($" CURVE{curveId} \t{maxFlow:F2} \t{maxHead*0.5:F2}");
|
content.AppendLine($" CURVE{curveId} \t{maxFlow.ToString("F2", CultureInfo.InvariantCulture)} \t{(maxHead*0.5).ToString("F2", CultureInfo.InvariantCulture)}");
|
||||||
|
|
||||||
curveId++;
|
curveId++;
|
||||||
}
|
}
|
||||||
|
@ -231,7 +240,8 @@ namespace CtrEditor.HydraulicSimulator.TSNet
|
||||||
|
|
||||||
foreach (var node in _network.Nodes.Values)
|
foreach (var node in _network.Nodes.Values)
|
||||||
{
|
{
|
||||||
content.AppendLine($" {node.Name,-15}\t0.0");
|
var sanitizedName = SanitizeNodeName(node.Name);
|
||||||
|
content.AppendLine($" {sanitizedName,-15}\t0.0");
|
||||||
}
|
}
|
||||||
|
|
||||||
content.AppendLine();
|
content.AppendLine();
|
||||||
|
@ -292,7 +302,8 @@ namespace CtrEditor.HydraulicSimulator.TSNet
|
||||||
int x = 0, y = 0;
|
int x = 0, y = 0;
|
||||||
foreach (var node in _network.Nodes.Values)
|
foreach (var node in _network.Nodes.Values)
|
||||||
{
|
{
|
||||||
content.AppendLine($" {node.Name,-15}\t{x:F2} \t{y:F2}");
|
var sanitizedName = SanitizeNodeName(node.Name);
|
||||||
|
content.AppendLine($" {sanitizedName,-15}\t{x.ToString("F2", CultureInfo.InvariantCulture)} \t{y.ToString("F2", CultureInfo.InvariantCulture)}");
|
||||||
x += 1000;
|
x += 1000;
|
||||||
if (x > 5000)
|
if (x > 5000)
|
||||||
{
|
{
|
||||||
|
@ -341,6 +352,45 @@ namespace CtrEditor.HydraulicSimulator.TSNet
|
||||||
return Math.Sqrt(valve.KvFull / 100.0) * 0.1; // en metros
|
return Math.Sqrt(valve.KvFull / 100.0) * 0.1; // en metros
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sanitiza nombres de nodos para compatibilidad con EPANET INP
|
||||||
|
/// </summary>
|
||||||
|
private string SanitizeNodeName(string nodeName)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(nodeName))
|
||||||
|
return "UnknownNode";
|
||||||
|
|
||||||
|
// Reemplazar espacios y caracteres especiales con guiones bajos
|
||||||
|
var sanitized = nodeName
|
||||||
|
.Replace(" ", "_")
|
||||||
|
.Replace("á", "a")
|
||||||
|
.Replace("é", "e")
|
||||||
|
.Replace("í", "i")
|
||||||
|
.Replace("ó", "o")
|
||||||
|
.Replace("ú", "u")
|
||||||
|
.Replace("ü", "u")
|
||||||
|
.Replace("ñ", "n")
|
||||||
|
.Replace("Á", "A")
|
||||||
|
.Replace("É", "E")
|
||||||
|
.Replace("Í", "I")
|
||||||
|
.Replace("Ó", "O")
|
||||||
|
.Replace("Ú", "U")
|
||||||
|
.Replace("Ü", "U")
|
||||||
|
.Replace("Ñ", "N");
|
||||||
|
|
||||||
|
// Remover caracteres no válidos para EPANET
|
||||||
|
var validChars = sanitized.Where(c => char.IsLetterOrDigit(c) || c == '_' || c == '-').ToArray();
|
||||||
|
var result = new string(validChars);
|
||||||
|
|
||||||
|
// Asegurar que empiece con una letra
|
||||||
|
if (!string.IsNullOrEmpty(result) && !char.IsLetter(result[0]))
|
||||||
|
{
|
||||||
|
result = "Node_" + result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.IsNullOrEmpty(result) ? "UnknownNode" : result;
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,6 +52,11 @@ namespace CtrEditor.HydraulicSimulator.TSNet
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsRunning { get; private set; }
|
public bool IsRunning { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indica si la red necesita ser reconstruida
|
||||||
|
/// </summary>
|
||||||
|
public bool NetworkNeedsRebuild { get; set; } = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Evento disparado cuando se completa una simulación
|
/// Evento disparado cuando se completa una simulación
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -224,12 +229,73 @@ namespace CtrEditor.HydraulicSimulator.TSNet
|
||||||
{
|
{
|
||||||
Network = new HydraulicNetwork();
|
Network = new HydraulicNetwork();
|
||||||
|
|
||||||
// Procesar objetos IHydraulicComponent
|
var hydraulicComponents = HydraulicObjects.OfType<IHydraulicComponent>().ToList();
|
||||||
foreach (var obj in HydraulicObjects)
|
|
||||||
|
// Primera pasada: Agregar TODOS los nodos
|
||||||
|
Debug.WriteLine("TSNet: Primera pasada - agregando todos los nodos...");
|
||||||
|
foreach (var component in hydraulicComponents)
|
||||||
{
|
{
|
||||||
if (obj is IHydraulicComponent hydraulicComponent)
|
try
|
||||||
{
|
{
|
||||||
ProcessHydraulicComponent(hydraulicComponent);
|
var nodes = component.GetHydraulicNodes();
|
||||||
|
foreach (var nodeDefinition in nodes)
|
||||||
|
{
|
||||||
|
if (nodeDefinition.IsFixedPressure)
|
||||||
|
{
|
||||||
|
Network.AddNode(nodeDefinition.Name, nodeDefinition.Pressure);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Network.AddNode(nodeDefinition.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Error agregando nodos del componente {component}: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.WriteLine($"TSNet: Nodos agregados - Total: {Network.Nodes.Count}");
|
||||||
|
Debug.WriteLine($"TSNet: Nodos disponibles: {string.Join(", ", Network.Nodes.Keys)}");
|
||||||
|
|
||||||
|
// Segunda pasada: Agregar TODOS los elementos
|
||||||
|
Debug.WriteLine("TSNet: Segunda pasada - agregando todos los elementos...");
|
||||||
|
foreach (var component in hydraulicComponents)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var elements = component.GetHydraulicElements();
|
||||||
|
foreach (var elementDefinition in elements)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Verificar que los nodos existan antes de agregar el elemento
|
||||||
|
if (Network.Nodes.ContainsKey(elementDefinition.FromNode) &&
|
||||||
|
Network.Nodes.ContainsKey(elementDefinition.ToNode))
|
||||||
|
{
|
||||||
|
Network.AddElement(elementDefinition.Element,
|
||||||
|
elementDefinition.FromNode,
|
||||||
|
elementDefinition.ToNode,
|
||||||
|
elementDefinition.Name);
|
||||||
|
|
||||||
|
Debug.WriteLine($"Rama agregada: {elementDefinition.Name} ({elementDefinition.FromNode} -> {elementDefinition.ToNode})");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"ERROR: Nodo '{elementDefinition.FromNode}' o '{elementDefinition.ToNode}' no existe. " +
|
||||||
|
$"Nodos disponibles: {string.Join(", ", Network.Nodes.Keys)}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception elementEx)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Error agregando elemento {elementDefinition.Name}: {elementEx.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Error obteniendo elementos del componente {component}: {ex.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -616,8 +682,29 @@ namespace CtrEditor.HydraulicSimulator.TSNet
|
||||||
var elements = component.GetHydraulicElements();
|
var elements = component.GetHydraulicElements();
|
||||||
foreach (var elementDefinition in elements)
|
foreach (var elementDefinition in elements)
|
||||||
{
|
{
|
||||||
// TODO: Convertir elementDefinition a Element y agregar a la red
|
try
|
||||||
// Por ahora creamos elementos básicos
|
{
|
||||||
|
// Verificar que los nodos existan antes de agregar el elemento
|
||||||
|
if (Network.Nodes.ContainsKey(elementDefinition.FromNode) &&
|
||||||
|
Network.Nodes.ContainsKey(elementDefinition.ToNode))
|
||||||
|
{
|
||||||
|
Network.AddElement(elementDefinition.Element,
|
||||||
|
elementDefinition.FromNode,
|
||||||
|
elementDefinition.ToNode,
|
||||||
|
elementDefinition.Name);
|
||||||
|
|
||||||
|
Debug.WriteLine($"Rama agregada: {elementDefinition.Name} ({elementDefinition.FromNode} -> {elementDefinition.ToNode})");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"ERROR: Nodo '{elementDefinition.FromNode}' o '{elementDefinition.ToNode}' no existe. " +
|
||||||
|
$"Nodos disponibles: {string.Join(", ", Network.Nodes.Keys)}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception elementEx)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Error agregando elemento {elementDefinition.Name}: {elementEx.Message}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
@ -646,6 +733,55 @@ namespace CtrEditor.HydraulicSimulator.TSNet
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Additional Methods for Compatibility
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reinicia el simulador hidráulico (equivalente a Reset del HydraulicSimulationManager)
|
||||||
|
/// </summary>
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
ResetAllCalculatedValues();
|
||||||
|
Debug.WriteLine("TSNet: Simulador reiniciado");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Obtiene estadísticas de la simulación
|
||||||
|
/// </summary>
|
||||||
|
public string GetSimulationStats()
|
||||||
|
{
|
||||||
|
return $"TSNet - Objetos: {HydraulicObjects.Count}, Tanques: {_tankAdapters.Count}, Bombas: {_pumpAdapters.Count}, Tuberías: {_pipeAdapters.Count}";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Propiedad para habilitar/deshabilitar la simulación hidráulica
|
||||||
|
/// </summary>
|
||||||
|
public bool IsHydraulicSimulationEnabled { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Limpia todos los objetos hidráulicos
|
||||||
|
/// </summary>
|
||||||
|
public void ClearHydraulicObjects()
|
||||||
|
{
|
||||||
|
HydraulicObjects.Clear();
|
||||||
|
_objectMapping.Clear();
|
||||||
|
_tankAdapters.Clear();
|
||||||
|
_pumpAdapters.Clear();
|
||||||
|
_pipeAdapters.Clear();
|
||||||
|
Network = new HydraulicNetwork();
|
||||||
|
Debug.WriteLine("TSNet: Todos los objetos hidráulicos limpiados");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invalida la red para forzar reconstrucción
|
||||||
|
/// </summary>
|
||||||
|
public void InvalidateNetwork()
|
||||||
|
{
|
||||||
|
NetworkNeedsRebuild = true;
|
||||||
|
Debug.WriteLine("TSNet: Red invalidada y marcada para reconstrucción");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region IDisposable
|
#region IDisposable
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|
178
MainViewModel.cs
178
MainViewModel.cs
|
@ -77,7 +77,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();
|
// ELIMINADO: HydraulicSimulationManager - reemplazado por TSNetSimulationManager
|
||||||
public TSNetSimulationManager tsnetSimulationManager = new TSNetSimulationManager();
|
public TSNetSimulationManager tsnetSimulationManager = new TSNetSimulationManager();
|
||||||
|
|
||||||
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
|
||||||
|
@ -87,6 +87,12 @@ namespace CtrEditor
|
||||||
private readonly System.Timers.Timer _timerDebugFlush; // Timer para flush automático del buffer de debug
|
private readonly System.Timers.Timer _timerDebugFlush; // Timer para flush automático del buffer de debug
|
||||||
private readonly System.Timers.Timer _timerTSNet; // Timer para TSNet automático cada 100ms
|
private readonly System.Timers.Timer _timerTSNet; // Timer para TSNet automático cada 100ms
|
||||||
|
|
||||||
|
// Variables para tracking de IOException
|
||||||
|
private volatile bool _tsnetExecuting = false; // Flag para evitar ejecuciones simultáneas de TSNet
|
||||||
|
private DateTime _lastTSNetExecution = DateTime.MinValue; // Timestamp de última ejecución TSNet
|
||||||
|
private readonly TimeSpan _minTSNetInterval = TimeSpan.FromMilliseconds(50); // Intervalo mínimo entre ejecuciones
|
||||||
|
private int _ioExceptionCount = 0; // Contador de excepciones IOException para debugging
|
||||||
|
|
||||||
public Canvas MainCanvas;
|
public Canvas MainCanvas;
|
||||||
|
|
||||||
// Manager para la visualización 3D
|
// Manager para la visualización 3D
|
||||||
|
@ -525,10 +531,46 @@ namespace CtrEditor
|
||||||
// Inicializar configuración del workspace
|
// Inicializar configuración del workspace
|
||||||
WorkspaceConfig = new Models.WorkspaceConfiguration();
|
WorkspaceConfig = new Models.WorkspaceConfiguration();
|
||||||
|
|
||||||
|
// Configurar tracking de IOException
|
||||||
|
SetupIOExceptionTraking();
|
||||||
|
|
||||||
// Iniciar servidor MCP automáticamente
|
// Iniciar servidor MCP automáticamente
|
||||||
StartMcpServer();
|
StartMcpServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Configura el sistema de tracking de IOException mejorado para capturar excepciones de WPF
|
||||||
|
/// </summary>
|
||||||
|
private void SetupIOExceptionTraking()
|
||||||
|
{
|
||||||
|
// Manejador para excepciones de primera oportunidad (first-chance exceptions)
|
||||||
|
// Esto captura TODAS las IOException, incluso las que son manejadas internamente por WPF
|
||||||
|
AppDomain.CurrentDomain.FirstChanceException += (sender, e) =>
|
||||||
|
{
|
||||||
|
if (e.Exception is System.IO.IOException ioEx)
|
||||||
|
{
|
||||||
|
_ioExceptionCount++;
|
||||||
|
var timestamp = DateTime.Now.ToString("HH:mm:ss:fff");
|
||||||
|
System.Diagnostics.Debug.WriteLine($"[{timestamp}] 🔍 IOException #{_ioExceptionCount} detectada (First-Chance):");
|
||||||
|
System.Diagnostics.Debug.WriteLine($" Mensaje: {ioEx.Message}");
|
||||||
|
System.Diagnostics.Debug.WriteLine($" Source: {ioEx.Source}");
|
||||||
|
System.Diagnostics.Debug.WriteLine($" HResult: {ioEx.HResult}");
|
||||||
|
System.Diagnostics.Debug.WriteLine($" Simulación activa: {IsSimulationRunning}");
|
||||||
|
System.Diagnostics.Debug.WriteLine($" TSNet ejecutándose: {_tsnetExecuting}");
|
||||||
|
|
||||||
|
// Verificar si está relacionada con TSNet/Python
|
||||||
|
if (ioEx.StackTrace?.Contains("Python") == true ||
|
||||||
|
ioEx.StackTrace?.Contains("tsnet") == true ||
|
||||||
|
ioEx.Source?.Contains("Presentation") == true)
|
||||||
|
{
|
||||||
|
System.Diagnostics.Debug.WriteLine($" ⚠️ Posiblemente relacionada con TSNet/WPF");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
System.Diagnostics.Debug.WriteLine("IOException tracking configurado - se capturarán todas las excepciones de E/S");
|
||||||
|
}
|
||||||
|
|
||||||
#region Workspace Configuration Management
|
#region Workspace Configuration Management
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -869,8 +911,8 @@ namespace CtrEditor
|
||||||
|
|
||||||
if (userControl != null)
|
if (userControl != null)
|
||||||
{
|
{
|
||||||
// Asignar los datos al UserControl
|
// Asignar los datos al UserControl - Usando TSNetSimulationManager
|
||||||
UserControlFactory.AssignDatos(userControl, osObjeto, simulationManager, hydraulicSimulationManager);
|
UserControlFactory.AssignDatos(userControl, osObjeto, simulationManager, tsnetSimulationManager);
|
||||||
osObjeto._mainViewModel = this;
|
osObjeto._mainViewModel = this;
|
||||||
if (osObjeto.Id == null) // Para los objetos salvados antes de usar UniqueID
|
if (osObjeto.Id == null) // Para los objetos salvados antes de usar UniqueID
|
||||||
osObjeto.Id = new UniqueId().ObtenerNuevaID();
|
osObjeto.Id = new UniqueId().ObtenerNuevaID();
|
||||||
|
@ -1254,8 +1296,8 @@ namespace CtrEditor
|
||||||
// Ejecutar simulación física BEPU
|
// Ejecutar simulación física BEPU
|
||||||
simulationManager.Step();
|
simulationManager.Step();
|
||||||
|
|
||||||
// Ejecutar simulación hidráulica
|
// ELIMINADO: hydraulicSimulationManager.Step() - Ahora solo usamos TSNet
|
||||||
hydraulicSimulationManager.Step((float)(timeBetweenCalls / 1000.0)); // Convertir ms a segundos
|
// La simulación hidráulica se ejecuta por separado en _timerTSNet
|
||||||
|
|
||||||
// ✅ 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)
|
||||||
|
@ -1277,7 +1319,7 @@ namespace CtrEditor
|
||||||
AdaptSimulationTiming(executionStopwatch.Elapsed.TotalMilliseconds);
|
AdaptSimulationTiming(executionStopwatch.Elapsed.TotalMilliseconds);
|
||||||
|
|
||||||
//Debug.WriteLine($"OnTickSimulacion execution time: {stopwatch.ElapsedMilliseconds} ms");
|
//Debug.WriteLine($"OnTickSimulacion execution time: {stopwatch.ElapsedMilliseconds} ms");
|
||||||
//Debug.WriteLine($"OnTickSimulacion execution time: {executionStopwatch.Elapsed.TotalMilliseconds:F2}ms | Timer interval: {timeBetweenCalls:F2}ms | Objects: {ObjetosSimulables?.Count ?? 0}");
|
Debug.WriteLine($"OnTickSimulacion execution time: {executionStopwatch.Elapsed.TotalMilliseconds:F2}ms | Timer interval: {timeBetweenCalls:F2}ms | Objects: {ObjetosSimulables?.Count ?? 0}");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
@ -1293,15 +1335,32 @@ namespace CtrEditor
|
||||||
private async void OnTSNetTimerElapsed(object sender, System.Timers.ElapsedEventArgs e)
|
private async void OnTSNetTimerElapsed(object sender, System.Timers.ElapsedEventArgs e)
|
||||||
{
|
{
|
||||||
// Solo ejecutar si la simulación está corriendo y hay objetos hidráulicos
|
// Solo ejecutar si la simulación está corriendo y hay objetos hidráulicos
|
||||||
if (!IsSimulationRunning || tsnetSimulationManager.IsRunning)
|
if (!IsSimulationRunning)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// Verificar que tsnetSimulationManager esté inicializado
|
||||||
|
if (tsnetSimulationManager == null)
|
||||||
|
{
|
||||||
|
Debug.WriteLine("TSNet Auto: tsnetSimulationManager es null, omitiendo ejecución");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verificar si ya está ejecutándose para evitar concurrencia
|
||||||
|
if (tsnetSimulationManager.IsRunning)
|
||||||
|
{
|
||||||
|
Debug.WriteLine("TSNet Auto: Simulación ya en ejecución, omitiendo");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Contar objetos hidráulicos
|
// Contar objetos hidráulicos
|
||||||
var hydraulicObjects = ObjetosSimulables?.Where(obj => obj.GetType().Name.Contains("osHyd")).ToList();
|
var hydraulicObjects = ObjetosSimulables?.Where(obj => obj.GetType().Name.Contains("osHyd")).ToList();
|
||||||
if (hydraulicObjects == null || hydraulicObjects.Count == 0)
|
if (hydraulicObjects == null || hydraulicObjects.Count == 0)
|
||||||
|
{
|
||||||
|
Debug.WriteLine("TSNet Auto: No hay objetos hidráulicos, omitiendo ejecución");
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Resetear y registrar objetos hidráulicos
|
// Resetear y registrar objetos hidráulicos
|
||||||
tsnetSimulationManager.ResetAllCalculatedValues();
|
tsnetSimulationManager.ResetAllCalculatedValues();
|
||||||
|
@ -1325,18 +1384,43 @@ namespace CtrEditor
|
||||||
// Ejecutar simulación TSNet de forma asíncrona sin bloquear
|
// Ejecutar simulación TSNet de forma asíncrona sin bloquear
|
||||||
var result = await tsnetSimulationManager.RunSimulationAsync();
|
var result = await tsnetSimulationManager.RunSimulationAsync();
|
||||||
|
|
||||||
if (result.Success)
|
if (result != null)
|
||||||
{
|
{
|
||||||
Debug.WriteLine($"TSNet Auto: Simulación exitosa con {hydraulicObjects.Count} objetos hidráulicos");
|
if (result.Success)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"TSNet Auto: Simulación exitosa con {hydraulicObjects.Count} objetos hidráulicos");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"TSNet Auto: Error en simulación: {result.Message}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Debug.WriteLine($"TSNet Auto: Error en simulación: {result.Message}");
|
Debug.WriteLine("TSNet Auto: RunSimulationAsync devolvió null");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (NullReferenceException nullEx)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"TSNet Auto: Error de referencia nula: {nullEx.Message}");
|
||||||
|
Debug.WriteLine($"TSNet Auto: StackTrace: {nullEx.StackTrace}");
|
||||||
|
|
||||||
|
// Intentar reinicializar el simulador
|
||||||
|
try
|
||||||
|
{
|
||||||
|
tsnetSimulationManager = new TSNetSimulationManager();
|
||||||
|
Debug.WriteLine("TSNet Auto: tsnetSimulationManager reinicializado");
|
||||||
|
}
|
||||||
|
catch (Exception reinitEx)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"TSNet Auto: Error al reinicializar: {reinitEx.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Debug.WriteLine($"TSNet Auto: Excepción: {ex.Message}");
|
Debug.WriteLine($"TSNet Auto: Excepción general: {ex.Message}");
|
||||||
|
Debug.WriteLine($"TSNet Auto: Tipo: {ex.GetType().Name}");
|
||||||
|
Debug.WriteLine($"TSNet Auto: StackTrace: {ex.StackTrace}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1965,14 +2049,14 @@ namespace CtrEditor
|
||||||
if (obj is IHydraulicComponent hydraulicComponent && hydraulicComponent.HasHydraulicComponents)
|
if (obj is IHydraulicComponent hydraulicComponent && hydraulicComponent.HasHydraulicComponents)
|
||||||
{
|
{
|
||||||
// Evitar doble registro - solo registrar si no está ya registrado
|
// Evitar doble registro - solo registrar si no está ya registrado
|
||||||
if (!hydraulicSimulationManager.HydraulicObjects.Contains(obj))
|
if (!tsnetSimulationManager.HydraulicObjects.Contains(obj))
|
||||||
{
|
{
|
||||||
hydraulicSimulationManager.RegisterHydraulicObject(obj);
|
tsnetSimulationManager.RegisterHydraulicObject(obj);
|
||||||
Debug.WriteLine($"Objeto hidráulico registrado: {obj.Nombre}");
|
Debug.WriteLine($"Objeto hidráulico registrado en TSNet: {obj.Nombre}");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Debug.WriteLine($"Objeto hidráulico ya registrado (omitido): {obj.Nombre}");
|
Debug.WriteLine($"Objeto hidráulico ya registrado en TSNet (omitido): {obj.Nombre}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1984,8 +2068,8 @@ namespace CtrEditor
|
||||||
{
|
{
|
||||||
// Desregistrar independientemente de si implementa la interfaz actualmente
|
// Desregistrar independientemente de si implementa la interfaz actualmente
|
||||||
// (podría haber cambiado desde que se registró)
|
// (podría haber cambiado desde que se registró)
|
||||||
hydraulicSimulationManager.UnregisterHydraulicObject(obj);
|
tsnetSimulationManager.UnregisterHydraulicObject(obj);
|
||||||
Debug.WriteLine($"Objeto desregistrado del simulador hidráulico: {obj.Nombre}");
|
Debug.WriteLine($"Objeto desregistrado del simulador hidráulico TSNet: {obj.Nombre}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -1993,8 +2077,8 @@ namespace CtrEditor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void ResetHydraulicSimulation()
|
public void ResetHydraulicSimulation()
|
||||||
{
|
{
|
||||||
hydraulicSimulationManager.Reset();
|
tsnetSimulationManager.Reset();
|
||||||
Debug.WriteLine("Simulador hidráulico reiniciado");
|
Debug.WriteLine("Simulador hidráulico TSNet reiniciado");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -2002,7 +2086,7 @@ namespace CtrEditor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string GetHydraulicSimulationStats()
|
public string GetHydraulicSimulationStats()
|
||||||
{
|
{
|
||||||
return hydraulicSimulationManager.GetSimulationStats();
|
return tsnetSimulationManager.GetSimulationStats();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -2010,8 +2094,8 @@ namespace CtrEditor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void SetHydraulicSimulationEnabled(bool enabled)
|
public void SetHydraulicSimulationEnabled(bool enabled)
|
||||||
{
|
{
|
||||||
hydraulicSimulationManager.IsHydraulicSimulationEnabled = enabled;
|
tsnetSimulationManager.IsHydraulicSimulationEnabled = enabled;
|
||||||
Debug.WriteLine($"Simulación hidráulica {(enabled ? "habilitada" : "deshabilitada")}");
|
Debug.WriteLine($"Simulación hidráulica TSNet {(enabled ? "habilitada" : "deshabilitada")}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -2020,7 +2104,7 @@ namespace CtrEditor
|
||||||
private void RegisterLoadedHydraulicObjects()
|
private void RegisterLoadedHydraulicObjects()
|
||||||
{
|
{
|
||||||
// Limpiar registros previos
|
// Limpiar registros previos
|
||||||
hydraulicSimulationManager.ClearHydraulicObjects();
|
tsnetSimulationManager.ClearHydraulicObjects();
|
||||||
|
|
||||||
// Crear una lista temporal para evitar duplicados durante la carga
|
// Crear una lista temporal para evitar duplicados durante la carga
|
||||||
var objectsToRegister = new HashSet<osBase>();
|
var objectsToRegister = new HashSet<osBase>();
|
||||||
|
@ -2037,11 +2121,11 @@ namespace CtrEditor
|
||||||
// Registrar los objetos únicos
|
// Registrar los objetos únicos
|
||||||
foreach (var obj in objectsToRegister)
|
foreach (var obj in objectsToRegister)
|
||||||
{
|
{
|
||||||
hydraulicSimulationManager.RegisterHydraulicObject(obj);
|
tsnetSimulationManager.RegisterHydraulicObject(obj);
|
||||||
Debug.WriteLine($"Objeto hidráulico registrado en carga: {obj.Nombre}");
|
Debug.WriteLine($"Objeto hidráulico registrado en carga TSNet: {obj.Nombre}");
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug.WriteLine($"Registrados {hydraulicSimulationManager.HydraulicObjects.Count} objetos hidráulicos únicos tras cargar proyecto");
|
Debug.WriteLine($"Registrados {tsnetSimulationManager.HydraulicObjects.Count} objetos hidráulicos únicos tras cargar proyecto en TSNet");
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
@ -2055,10 +2139,10 @@ namespace CtrEditor
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (hydraulicSimulationManager != null)
|
if (tsnetSimulationManager != null)
|
||||||
{
|
{
|
||||||
hydraulicSimulationManager.InvalidateNetwork();
|
tsnetSimulationManager.InvalidateNetwork();
|
||||||
Debug.WriteLine("Red hidráulica invalidada y marcada para recálculo");
|
Debug.WriteLine("Red hidráulica TSNet invalidada y marcada para recálculo");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
@ -2333,6 +2417,41 @@ namespace CtrEditor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Método de debugging para generar un reporte detallado de IOException y estado del sistema
|
||||||
|
/// </summary>
|
||||||
|
public void GenerateIOExceptionReport()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var finalTimestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
|
||||||
|
Debug.WriteLine($"\n=== REPORTE DE ESTADO ({finalTimestamp}) ===");
|
||||||
|
Debug.WriteLine($"Simulación ejecutándose: {IsSimulationRunning}");
|
||||||
|
Debug.WriteLine($"Timer Simulación activo: {_timerSimulacion?.Enabled}");
|
||||||
|
Debug.WriteLine($"Timer TSNet activo: {_timerTSNet?.Enabled}");
|
||||||
|
Debug.WriteLine($"Timer Display activo: {_timerDisplayUpdate?.IsEnabled}");
|
||||||
|
Debug.WriteLine($"Timer 3D activo: {_timer3DUpdate?.IsEnabled}");
|
||||||
|
Debug.WriteLine($"TSNet Manager inicializado: {tsnetSimulationManager != null}");
|
||||||
|
|
||||||
|
if (tsnetSimulationManager != null)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"TSNet ejecutándose: {tsnetSimulationManager.IsRunning}");
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.WriteLine($"Objetos simulables: {ObjetosSimulables?.Count ?? 0}");
|
||||||
|
|
||||||
|
var hydraulicObjects = ObjetosSimulables?.Where(obj =>
|
||||||
|
obj.GetType().Name.Contains("osHyd"))?.ToList();
|
||||||
|
Debug.WriteLine($"Objetos hidráulicos: {hydraulicObjects?.Count ?? 0}");
|
||||||
|
|
||||||
|
Debug.WriteLine($"=== FIN REPORTE ===\n");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Error en GenerateIOExceptionReport: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2356,7 +2475,6 @@ namespace CtrEditor
|
||||||
[System.Obsolete("Use ImageDataDictionary instead")]
|
[System.Obsolete("Use ImageDataDictionary instead")]
|
||||||
public Dictionary<string, string>? ImageCustomNames { get; set; }
|
public Dictionary<string, string>? ImageCustomNames { get; set; }
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class TipoSimulable
|
public class TipoSimulable
|
||||||
|
|
|
@ -346,16 +346,40 @@ namespace CtrEditor.ObjetosSim
|
||||||
// Verificar que la tubería esté conectada a dos componentes
|
// Verificar que la tubería esté conectada a dos componentes
|
||||||
if (!string.IsNullOrEmpty(Id_ComponenteA) && !string.IsNullOrEmpty(Id_ComponenteB))
|
if (!string.IsNullOrEmpty(Id_ComponenteA) && !string.IsNullOrEmpty(Id_ComponenteB))
|
||||||
{
|
{
|
||||||
// Crear elemento pipe con propiedades correctas
|
try
|
||||||
var pipeElement = new Pipe(Length, Diameter, Roughness);
|
{
|
||||||
|
// Resolver los nombres de los componentes conectados
|
||||||
|
var nodeNameA = ResolveComponentNodeName(Id_ComponenteA);
|
||||||
|
var nodeNameB = ResolveComponentNodeName(Id_ComponenteB);
|
||||||
|
|
||||||
elements.Add(new HydraulicElementDefinition(
|
if (!string.IsNullOrEmpty(nodeNameA) && !string.IsNullOrEmpty(nodeNameB))
|
||||||
$"PIPE_{Nombre}",
|
{
|
||||||
Id_ComponenteA,
|
// Crear elemento pipe con propiedades correctas
|
||||||
Id_ComponenteB,
|
var pipeElement = new Pipe(Length, Diameter, Roughness);
|
||||||
pipeElement,
|
|
||||||
$"Tubería - L:{Length:F1}m, D:{Diameter*1000:F0}mm, Rough:{Roughness*1000:F2}mm"
|
elements.Add(new HydraulicElementDefinition(
|
||||||
));
|
$"PIPE_{Nombre}",
|
||||||
|
nodeNameA,
|
||||||
|
nodeNameB,
|
||||||
|
pipeElement,
|
||||||
|
$"Tubería - L:{Length:F1}m, D:{Diameter*1000:F0}mm, Rough:{Roughness*1000:F2}mm"
|
||||||
|
));
|
||||||
|
|
||||||
|
if (VerboseOutput)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Pipe {Nombre}: Conectando nodos '{nodeNameA}' -> '{nodeNameB}'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Warning: Pipe {Nombre} no pudo resolver nombres de nodos. " +
|
||||||
|
$"ComponenteA='{Id_ComponenteA}', ComponenteB='{Id_ComponenteB}'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Error en GetHydraulicElements para pipe {Nombre}: {ex.Message}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return elements;
|
return elements;
|
||||||
|
@ -371,6 +395,209 @@ namespace CtrEditor.ObjetosSim
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resuelve el ID de un componente al nombre del nodo hidráulico correspondiente
|
||||||
|
/// THREAD-SAFE: No accede a objetos WPF desde background threads
|
||||||
|
/// </summary>
|
||||||
|
private string ResolveComponentNodeName(string componentId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Debug: Log the original componentId
|
||||||
|
Debug.WriteLine($"Pipe {Nombre}: Resolviendo componentId '{componentId}'");
|
||||||
|
|
||||||
|
// Primero intentar usar el ID directamente como nombre (para compatibilidad)
|
||||||
|
if (!string.IsNullOrEmpty(componentId))
|
||||||
|
{
|
||||||
|
// Para evitar problemas de threading, usar el Dispatcher para acceder a objetos UI
|
||||||
|
MainViewModel mainViewModel = null;
|
||||||
|
|
||||||
|
// Solo acceder a la UI desde el thread principal
|
||||||
|
if (Application.Current?.Dispatcher?.CheckAccess() == true)
|
||||||
|
{
|
||||||
|
mainViewModel = Application.Current?.MainWindow?.DataContext as MainViewModel;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Si estamos en un background thread, usar Invoke para acceder de forma segura
|
||||||
|
Application.Current?.Dispatcher?.Invoke(() =>
|
||||||
|
{
|
||||||
|
mainViewModel = Application.Current?.MainWindow?.DataContext as MainViewModel;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mainViewModel?.ObjetosSimulables != null)
|
||||||
|
{
|
||||||
|
List<osBase> hydraulicComponents = null;
|
||||||
|
|
||||||
|
// Obtener componentes de forma thread-safe
|
||||||
|
if (Application.Current?.Dispatcher?.CheckAccess() == true)
|
||||||
|
{
|
||||||
|
hydraulicComponents = mainViewModel.ObjetosSimulables
|
||||||
|
.OfType<IHydraulicComponent>()
|
||||||
|
.Where(comp => comp is osBase)
|
||||||
|
.Cast<osBase>()
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Application.Current?.Dispatcher?.Invoke(() =>
|
||||||
|
{
|
||||||
|
hydraulicComponents = mainViewModel.ObjetosSimulables
|
||||||
|
.OfType<IHydraulicComponent>()
|
||||||
|
.Where(comp => comp is osBase)
|
||||||
|
.Cast<osBase>()
|
||||||
|
.ToList();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hydraulicComponents != null)
|
||||||
|
{
|
||||||
|
// Debug: Log available components
|
||||||
|
Debug.WriteLine($"Pipe {Nombre}: Componentes hidráulicos disponibles:");
|
||||||
|
foreach (var comp in hydraulicComponents)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($" - Nombre: '{comp.Nombre}', ID: '{comp.Id?.Value}'");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Buscar por nombre primero (caso más común)
|
||||||
|
var componentByName = hydraulicComponents
|
||||||
|
.FirstOrDefault(comp => comp.Nombre == componentId);
|
||||||
|
|
||||||
|
if (componentByName != null)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Pipe {Nombre}: Encontrado componente por nombre: '{componentByName.Nombre}'");
|
||||||
|
|
||||||
|
// Verificar si es una bomba (requiere resolución especial de nodos)
|
||||||
|
if (componentByName is osHydPump)
|
||||||
|
{
|
||||||
|
var sanitizedName = SanitizeNodeName(componentByName.Nombre);
|
||||||
|
|
||||||
|
// Determinar si esta tubería está conectada a la entrada o salida de la bomba
|
||||||
|
// Basándonos en si la bomba es origen (ComponenteA) o destino (ComponenteB) de esta tubería
|
||||||
|
if (componentId == Id_ComponenteA)
|
||||||
|
{
|
||||||
|
// La bomba es ComponenteA (origen de esta tubería)
|
||||||
|
// Por lo tanto, conectamos desde la salida de la bomba (NODE_B)
|
||||||
|
Debug.WriteLine($"Pipe {Nombre}: Bomba como origen - conectando desde salida: NODE_B_{sanitizedName}");
|
||||||
|
return $"NODE_B_{sanitizedName}";
|
||||||
|
}
|
||||||
|
else if (componentId == Id_ComponenteB)
|
||||||
|
{
|
||||||
|
// La bomba es ComponenteB (destino de esta tubería)
|
||||||
|
// Por lo tanto, conectamos hacia la entrada de la bomba (NODE_A)
|
||||||
|
Debug.WriteLine($"Pipe {Nombre}: Bomba como destino - conectando hacia entrada: NODE_A_{sanitizedName}");
|
||||||
|
return $"NODE_A_{sanitizedName}";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Fallback: usar salida por defecto
|
||||||
|
Debug.WriteLine($"Pipe {Nombre}: Usando salida por defecto de bomba: NODE_B_{sanitizedName}");
|
||||||
|
return $"NODE_B_{sanitizedName}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Para tanques y otras componentes, usar el nombre sanitizado
|
||||||
|
return SanitizeNodeName(componentByName.Nombre);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si no se encuentra por nombre, buscar por ID único (convertir string a int)
|
||||||
|
if (int.TryParse(componentId, out int idValue))
|
||||||
|
{
|
||||||
|
var componentById = hydraulicComponents
|
||||||
|
.FirstOrDefault(comp => comp.Id?.Value == idValue);
|
||||||
|
|
||||||
|
if (componentById != null)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Pipe {Nombre}: Encontrado componente por ID {idValue}: '{componentById.Nombre}'");
|
||||||
|
|
||||||
|
// Verificar si es una bomba (requiere resolución especial de nodos)
|
||||||
|
if (componentById is osHydPump)
|
||||||
|
{
|
||||||
|
var sanitizedName = SanitizeNodeName(componentById.Nombre);
|
||||||
|
|
||||||
|
// Determinar si esta tubería está conectada a la entrada o salida de la bomba
|
||||||
|
if (componentId == Id_ComponenteA)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Pipe {Nombre}: Bomba como origen (ID) - conectando desde salida: NODE_B_{sanitizedName}");
|
||||||
|
return $"NODE_B_{sanitizedName}";
|
||||||
|
}
|
||||||
|
else if (componentId == Id_ComponenteB)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Pipe {Nombre}: Bomba como destino (ID) - conectando hacia entrada: NODE_A_{sanitizedName}");
|
||||||
|
return $"NODE_A_{sanitizedName}";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Pipe {Nombre}: Usando salida por defecto de bomba (ID): NODE_B_{sanitizedName}");
|
||||||
|
return $"NODE_B_{sanitizedName}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return SanitizeNodeName(componentById.Nombre);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Último intento: usar el componentId directamente si es un nombre válido
|
||||||
|
Debug.WriteLine($"Pipe {Nombre}: No se encontró componente, usando '{componentId}' directamente");
|
||||||
|
return SanitizeNodeName(componentId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Pipe {Nombre}: MainViewModel o ObjetosSimulables es null");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Pipe {Nombre}: componentId está vacío");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Error resolviendo componente '{componentId}': {ex.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sanitiza nombres de nodos para compatibilidad con EPANET INP
|
||||||
|
/// </summary>
|
||||||
|
private string SanitizeNodeName(string nodeName)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(nodeName))
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
// Reemplazar espacios y caracteres especiales con guiones bajos
|
||||||
|
var sanitized = nodeName
|
||||||
|
.Replace(" ", "_")
|
||||||
|
.Replace("á", "a")
|
||||||
|
.Replace("é", "e")
|
||||||
|
.Replace("í", "i")
|
||||||
|
.Replace("ó", "o")
|
||||||
|
.Replace("ú", "u")
|
||||||
|
.Replace("ü", "u")
|
||||||
|
.Replace("ñ", "n")
|
||||||
|
.Replace("Á", "A")
|
||||||
|
.Replace("É", "E")
|
||||||
|
.Replace("Í", "I")
|
||||||
|
.Replace("Ó", "O")
|
||||||
|
.Replace("Ú", "U")
|
||||||
|
.Replace("Ü", "U")
|
||||||
|
.Replace("Ñ", "N");
|
||||||
|
|
||||||
|
// Remover caracteres no válidos para EPANET
|
||||||
|
var validChars = sanitized.Where(c => char.IsLetterOrDigit(c) || c == '_' || c == '-').ToArray();
|
||||||
|
return new string(validChars);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool VerboseOutput => true; // Habilitar salida detallada para debugging
|
||||||
|
|
||||||
// Implementación de IHydraulicFlowReceiver
|
// Implementación de IHydraulicFlowReceiver
|
||||||
public void SetFlow(double flow)
|
public void SetFlow(double flow)
|
||||||
{
|
{
|
||||||
|
|
|
@ -275,7 +275,7 @@ namespace CtrEditor.ObjetosSim
|
||||||
if (IsRunning && HasFlow)
|
if (IsRunning && HasFlow)
|
||||||
ImageSource_oculta = ImageFromPath("/imagenes/pump_run.png");
|
ImageSource_oculta = ImageFromPath("/imagenes/pump_run.png");
|
||||||
else if (IsRunning)
|
else if (IsRunning)
|
||||||
ImageSource_oculta = ImageFromPath("/imagenes/pump_idle.png");
|
ImageSource_oculta = ImageFromPath("/imagenes/pump_run.png"); // Usar pump_run como fallback para idle
|
||||||
else
|
else
|
||||||
ImageSource_oculta = ImageFromPath("/imagenes/pump_stop.png");
|
ImageSource_oculta = ImageFromPath("/imagenes/pump_stop.png");
|
||||||
}
|
}
|
||||||
|
@ -324,16 +324,75 @@ namespace CtrEditor.ObjetosSim
|
||||||
|
|
||||||
public List<HydraulicNodeDefinition> GetHydraulicNodes()
|
public List<HydraulicNodeDefinition> GetHydraulicNodes()
|
||||||
{
|
{
|
||||||
// Las bombas no crean nodos propios, se conectan entre nodos existentes
|
var nodes = new List<HydraulicNodeDefinition>();
|
||||||
return new List<HydraulicNodeDefinition>();
|
|
||||||
|
// Sanitizar el nombre para compatibilidad con EPANET
|
||||||
|
var sanitizedName = SanitizeNodeName(Nombre);
|
||||||
|
|
||||||
|
// Crear nodos de entrada y salida para la bomba
|
||||||
|
nodes.Add(new HydraulicNodeDefinition(
|
||||||
|
$"NODE_A_{sanitizedName}",
|
||||||
|
false,
|
||||||
|
null,
|
||||||
|
$"Nodo entrada bomba {sanitizedName}"));
|
||||||
|
|
||||||
|
nodes.Add(new HydraulicNodeDefinition(
|
||||||
|
$"NODE_B_{sanitizedName}",
|
||||||
|
false,
|
||||||
|
null,
|
||||||
|
$"Nodo salida bomba {sanitizedName}"));
|
||||||
|
|
||||||
|
return nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sanitiza nombres de nodos para compatibilidad con EPANET INP
|
||||||
|
/// </summary>
|
||||||
|
private string SanitizeNodeName(string nodeName)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(nodeName))
|
||||||
|
return "UnknownPump";
|
||||||
|
|
||||||
|
// Reemplazar espacios y caracteres especiales con guiones bajos
|
||||||
|
var sanitized = nodeName
|
||||||
|
.Replace(" ", "_")
|
||||||
|
.Replace("á", "a")
|
||||||
|
.Replace("é", "e")
|
||||||
|
.Replace("í", "i")
|
||||||
|
.Replace("ó", "o")
|
||||||
|
.Replace("ú", "u")
|
||||||
|
.Replace("ü", "u")
|
||||||
|
.Replace("ñ", "n")
|
||||||
|
.Replace("Á", "A")
|
||||||
|
.Replace("É", "E")
|
||||||
|
.Replace("Í", "I")
|
||||||
|
.Replace("Ó", "O")
|
||||||
|
.Replace("Ú", "U")
|
||||||
|
.Replace("Ü", "U")
|
||||||
|
.Replace("Ñ", "N");
|
||||||
|
|
||||||
|
// Remover caracteres no válidos para EPANET
|
||||||
|
var validChars = sanitized.Where(c => char.IsLetterOrDigit(c) || c == '_' || c == '-').ToArray();
|
||||||
|
var result = new string(validChars);
|
||||||
|
|
||||||
|
// Asegurar que empiece con una letra
|
||||||
|
if (!string.IsNullOrEmpty(result) && !char.IsLetter(result[0]))
|
||||||
|
{
|
||||||
|
result = "Pump_" + result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.IsNullOrEmpty(result) ? "UnknownPump" : result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ApplyHydraulicResults(Dictionary<string, double> flows, Dictionary<string, double> pressures)
|
public void ApplyHydraulicResults(Dictionary<string, double> flows, Dictionary<string, double> pressures)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// Sanitizar el nombre para compatibilidad con EPANET
|
||||||
|
var sanitizedName = SanitizeNodeName(Nombre);
|
||||||
|
|
||||||
// Buscar resultados de esta bomba en TSNet
|
// Buscar resultados de esta bomba en TSNet
|
||||||
var pumpElementName = $"PUMP_{Nombre}";
|
var pumpElementName = $"PUMP_{sanitizedName}";
|
||||||
if (flows.ContainsKey(pumpElementName))
|
if (flows.ContainsKey(pumpElementName))
|
||||||
{
|
{
|
||||||
var pumpFlow = flows[pumpElementName];
|
var pumpFlow = flows[pumpElementName];
|
||||||
|
@ -365,13 +424,16 @@ namespace CtrEditor.ObjetosSim
|
||||||
|
|
||||||
if (IsRunning)
|
if (IsRunning)
|
||||||
{
|
{
|
||||||
|
// Sanitizar el nombre para compatibilidad con EPANET
|
||||||
|
var sanitizedName = SanitizeNodeName(Nombre);
|
||||||
|
|
||||||
// Crear elemento de bomba para TSNet
|
// Crear elemento de bomba para TSNet
|
||||||
var pumpElement = new PumpHQ(PumpHead, MaxFlow);
|
var pumpElement = new PumpHQ(PumpHead, MaxFlow);
|
||||||
|
|
||||||
elements.Add(new HydraulicElementDefinition(
|
elements.Add(new HydraulicElementDefinition(
|
||||||
$"PUMP_{Nombre}",
|
$"PUMP_{sanitizedName}",
|
||||||
$"NODE_A_{Nombre}",
|
$"NODE_A_{sanitizedName}",
|
||||||
$"NODE_B_{Nombre}",
|
$"NODE_B_{sanitizedName}",
|
||||||
pumpElement,
|
pumpElement,
|
||||||
$"Bomba hidráulica - Head: {PumpHead:F1}m, Flow: {MaxFlow * 3600:F1} m³/h"
|
$"Bomba hidráulica - Head: {PumpHead:F1}m, Flow: {MaxFlow * 3600:F1} m³/h"
|
||||||
));
|
));
|
||||||
|
|
|
@ -432,19 +432,63 @@ namespace CtrEditor.ObjetosSim
|
||||||
public List<HydraulicNodeDefinition> GetHydraulicNodes()
|
public List<HydraulicNodeDefinition> GetHydraulicNodes()
|
||||||
{
|
{
|
||||||
var nodes = new List<HydraulicNodeDefinition>();
|
var nodes = new List<HydraulicNodeDefinition>();
|
||||||
|
// Sanitizar el nombre para compatibilidad con EPANET
|
||||||
|
var sanitizedName = SanitizeNodeName(Nombre);
|
||||||
// Tanque como nodo libre por defecto
|
// Tanque como nodo libre por defecto
|
||||||
nodes.Add(new HydraulicNodeDefinition(Nombre, false, null, $"Tanque hidráulico - {FluidDescription}"));
|
nodes.Add(new HydraulicNodeDefinition(sanitizedName, false, null, $"Tanque hidráulico - {FluidDescription}"));
|
||||||
return nodes;
|
return nodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sanitiza nombres de nodos para compatibilidad con EPANET INP
|
||||||
|
/// </summary>
|
||||||
|
private string SanitizeNodeName(string nodeName)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(nodeName))
|
||||||
|
return "UnknownTank";
|
||||||
|
|
||||||
|
// Reemplazar espacios y caracteres especiales con guiones bajos
|
||||||
|
var sanitized = nodeName
|
||||||
|
.Replace(" ", "_")
|
||||||
|
.Replace("á", "a")
|
||||||
|
.Replace("é", "e")
|
||||||
|
.Replace("í", "i")
|
||||||
|
.Replace("ó", "o")
|
||||||
|
.Replace("ú", "u")
|
||||||
|
.Replace("ü", "u")
|
||||||
|
.Replace("ñ", "n")
|
||||||
|
.Replace("Á", "A")
|
||||||
|
.Replace("É", "E")
|
||||||
|
.Replace("Í", "I")
|
||||||
|
.Replace("Ó", "O")
|
||||||
|
.Replace("Ú", "U")
|
||||||
|
.Replace("Ü", "U")
|
||||||
|
.Replace("Ñ", "N");
|
||||||
|
|
||||||
|
// Remover caracteres no válidos para EPANET
|
||||||
|
var validChars = sanitized.Where(c => char.IsLetterOrDigit(c) || c == '_' || c == '-').ToArray();
|
||||||
|
var result = new string(validChars);
|
||||||
|
|
||||||
|
// Asegurar que empiece con una letra
|
||||||
|
if (!string.IsNullOrEmpty(result) && !char.IsLetter(result[0]))
|
||||||
|
{
|
||||||
|
result = "Tank_" + result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.IsNullOrEmpty(result) ? "UnknownTank" : result;
|
||||||
|
}
|
||||||
|
|
||||||
public void ApplyHydraulicResults(Dictionary<string, double> flows, Dictionary<string, double> pressures)
|
public void ApplyHydraulicResults(Dictionary<string, double> flows, Dictionary<string, double> pressures)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// Sanitizar el nombre para compatibilidad con EPANET
|
||||||
|
var sanitizedName = SanitizeNodeName(Nombre);
|
||||||
|
|
||||||
// Actualizar presión desde TSNet
|
// Actualizar presión desde TSNet
|
||||||
if (pressures.ContainsKey(Nombre))
|
if (pressures.ContainsKey(sanitizedName))
|
||||||
{
|
{
|
||||||
var pressurePa = pressures[Nombre];
|
var pressurePa = pressures[sanitizedName];
|
||||||
CurrentPressure = pressurePa / 100000.0; // Convertir Pa a bar
|
CurrentPressure = pressurePa / 100000.0; // Convertir Pa a bar
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
using CtrEditor.HydraulicSimulator;
|
using CtrEditor.HydraulicSimulator;
|
||||||
|
using CtrEditor.HydraulicSimulator.TSNet;
|
||||||
using CtrEditor.Simulacion;
|
using CtrEditor.Simulacion;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System;
|
using System;
|
||||||
|
@ -90,6 +91,19 @@ namespace CtrEditor.ObjetosSim
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sobrecarga para TSNetSimulationManager - simplemente no asigna el hydraulicSimulationManager
|
||||||
|
public static void AssignDatos(UserControl userControl, osBase datos, SimulationManagerBEPU simulationManager, TSNetSimulationManager tsnetSimulationManager)
|
||||||
|
{
|
||||||
|
if (userControl is IDataContainer dataContainer)
|
||||||
|
{
|
||||||
|
dataContainer.Datos = datos;
|
||||||
|
userControl.DataContext = datos;
|
||||||
|
datos.VisualRepresentation = userControl;
|
||||||
|
datos.simulationManager = simulationManager;
|
||||||
|
// No asignamos hydraulicSimulationManager para TSNet - se maneja de manera diferente
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void LimpiarPropiedadesosDatos(PropertyGrid propertyGrid)
|
public static void LimpiarPropiedadesosDatos(PropertyGrid propertyGrid)
|
||||||
{
|
{
|
||||||
// Forzar la actualización de bindings pendientes antes de limpiar
|
// Forzar la actualización de bindings pendientes antes de limpiar
|
||||||
|
|
Loading…
Reference in New Issue