Files
ExperionCrawler/mcp-server/pipeline/analyzer.py
windpacer f71ec310e4 feat: P&ID 그래프 파이프라인 및 MCP 서버 개선
- P&ID 그래프 파이프라인 구현 (py)
  - pid_geometric_extractor.py: 기하학적 특징 추출
  - pid_intelligent_mapper.py: 태그 매핑
  - pid_topology_builder.py: 위상 구축
  - test_pipeline_phase2.py, test_pipeline_phase3.py: 테스트

- MCP 서버 개선
  - server.py: 멀티프로세싱 지원
  - pipeline/: 분석, 추출, 매핑, 위상 모듈 추가

- C# P&ID 그래프 서비스
  - PidGraphDtos.cs: DTO 정의
  - PidGraphService.cs: 비즈니스 로직
  - PidGraphController.cs: API 컨트롤러

- OPC UA 서비스 개선
  - ExperionOpcServerService.cs
  - ExperionRealtimeService.cs
  - ExperionFastService.cs

- MCP 클라이언트 및 호스팅 서비스 개선
  - McpClient.cs
  - McpServerHostedService.cs

- 웹 UI 개선
  - pid_graph_view.html: P&ID 그래프 뷰어
  - pid-viewer.js: 뷰어 로직
  - app.js: 메인 앱
  - pid_graph.css: 스타일

- 프로젝트 설정 업데이트
  - ExperionCrawler.csproj
  - Program.cs
2026-05-03 03:50:20 +09:00

79 lines
3.0 KiB
Python

import networkx as nx
from typing import Dict, List, Optional
import json
import os
class PidAnalysisEngine:
def __init__(self, topology_file: str, mapping_file: str):
self.topology_file = topology_file
self.mapping_file = mapping_file
self.graph = nx.DiGraph()
self.tag_mapping = {}
self.load_data()
def load_data(self):
"""그래프 및 매핑 데이터 로드"""
try:
if os.path.exists(self.topology_file):
with open(self.topology_file, 'r', encoding='utf-8') as f:
data = json.load(f)
# NetworkX 그래프 생성 (node_link_data 형식 가정)
for node in data.get('nodes', []):
self.graph.add_node(node['id'], **node)
for edge in data.get('links', []): # node_link_data는 'links' 사용
self.graph.add_edge(edge['source'], edge['target'], **edge)
if os.path.exists(self.mapping_file):
with open(self.mapping_file, 'r', encoding='utf-8') as f:
self.tag_mapping = json.load(f)
except Exception as e:
print(f"Error loading analysis data: {e}")
def get_propagation_path_with_flow(self, start_node: str):
"""
엣지의 방향성(flow_direction)과 상태(valve_status)를 고려한 실제 영향 전파 경로 추출
"""
if start_node not in self.graph:
return {}
# 1. 유효한 엣지만 필터링 (방향이 forward이고 밸브가 open인 경로)
valid_edges = [
(u, v) for u, v, d in self.graph.edges(data=True)
if d.get('flow_direction', 'forward') == 'forward'
and d.get('valve_status', 'open') == 'open'
]
filtered_graph = nx.DiGraph()
filtered_graph.add_edges_from(valid_edges)
# 2. 전파 단계별 노드 추출 (BFS)
try:
propagation_levels = nx.single_source_shortest_path_length(filtered_graph, start_node)
return propagation_levels
except Exception:
return {}
def analyze_impact(self, node_id: str):
"""특정 노드 장애 시 영향도 분석 결과 반환"""
if node_id not in self.graph:
return {"success": False, "error": f"Node {node_id} not found in topology"}
impact_map = self.get_propagation_path_with_flow(node_id)
# 경로 추출 (시각화를 위해 모든 영향 노드로의 최단 경로 포함)
paths = []
for target in impact_map.keys():
if target != node_id:
try:
path = nx.shortest_path(self.graph, source=node_id, target=target)
paths.append(path)
except nx.NetworkXNoPath:
continue
return {
"success": True,
"startNode": node_id,
"impactedNodes": impact_map,
"paths": paths
}