# 🎨 Graph Pipeline Phase 4: ν™œμš© 및 μ‹œκ°ν™” (Application & Visualization) 이 λ¬Έμ„œλŠ” P&ID Graph Pipeline의 μ΅œμ’… 단계인 **ν™œμš© 및 μ‹œκ°ν™”**의 상세 κ΅¬ν˜„ κ³„νšμ„ λ‹€λ£Ήλ‹ˆλ‹€. μ•žμ„  λ‹¨κ³„μ—μ„œ κ΅¬μΆ•ν•œ [κΈ°ν•˜ν•™μ  데이터 $\rightarrow$ μœ„μƒ κ·Έλž˜ν”„ $\rightarrow$ μ‹œμŠ€ν…œ νƒœκ·Έ λ§€ν•‘] 결과물을 κ²°ν•©ν•˜μ—¬, μš΄μ˜μžκ°€ 도면 μƒμ—μ„œ μ‹€μ‹œκ°„ 곡정 μƒνƒœλ₯Ό νŒŒμ•…ν•˜κ³  μž₯μ•  영ν–₯도λ₯Ό 뢄석할 수 μžˆλŠ” μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•˜λŠ” 것이 λͺ©ν‘œμž…λ‹ˆλ‹€. --- ## πŸ” [Supervisor Diagnosis] ν”„λ‘œκ·Έλž¨ 진단 및 κ°œμ„  ꢌ고 **진단 일자:** 2026-05-02 **μ§„λ‹¨μž:** Roo (Software Engineer / Supervisor) ### 1. μ’…ν•© 진단 κ²°κ³Ό ν˜„μž¬ κ³„νšμ€ 기본적인 데이터 흐름(C# $\rightarrow$ Python $\rightarrow$ Frontend)을 잘 μ •μ˜ν•˜κ³  μžˆμœΌλ‚˜, **μ‹€μ œ μ‚°μ—… ν˜„μž₯의 λŒ€κ·œλͺ¨ P&ID 도면 적용 μ‹œ λ°œμƒν•  수 μžˆλŠ” μ„±λŠ₯ 및 μ•ˆμ •μ„± 문제**에 λŒ€ν•œ κ³ λ €κ°€ λΆ€μ‘±ν•©λ‹ˆλ‹€. 특히 μ‹€μ‹œκ°„ 데이터 μ˜€λ²„λ ˆμ΄μ˜ λΆ€ν•˜ 관리와 뢄석 결과의 μ‹ λ’°μ„± 검증 단계가 λˆ„λ½λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€. ### 2. μ£Όμš” 진단 ν•­λͺ© 및 μˆ˜μ • 이유 | ν•­λͺ© | 진단 κ²°κ³Ό | μœ„ν—˜λ„ | μˆ˜μ • 이유 및 κ°œμ„  λ°©ν–₯ | |---|---|---|---| | **데이터 전솑 효율** | WebSocket/API 폴링 λ°©μ‹μ˜ λ‹¨μˆœ λ‚˜μ—΄ | MED | 수천 개의 νƒœκ·Έκ°€ ν¬ν•¨λœ λ„λ©΄μ—μ„œ κ°œλ³„ 폴링/전솑 μ‹œ λ„€νŠΈμ›Œν¬ λΆ€ν•˜ 급증 $\rightarrow$ **νƒœκ·Έ κ·Έλ£Ήν™” 및 λ³€κ²½λΆ„ 기반(Delta) 전솑** λ„μž… ν•„μš” | | **ν”„λ‘ νŠΈμ—”λ“œ λ Œλ”λ§** | SVG/Canvas λ‹¨μˆœ μ˜€λ²„λ ˆμ΄ | HIGH | λ…Έλ“œ μˆ˜κ°€ λ§Žμ•„μ§ˆ 경우 DOM μš”μ†Œ μ¦κ°€λ‘œ μΈν•œ λΈŒλΌμš°μ € λž™ λ°œμƒ $\rightarrow$ **Canvas 기반 λ Œλ”λ§ μ΅œμ ν™” 및 Viewport 기반 κ°€μ‹œ μ˜μ—­ λ Œλ”λ§** μ „λž΅ ν•„μš” | | **뢄석 μ—”μ§„ μ‹ λ’°μ„±** | `nx.descendants` λ‹¨μˆœ ν™œμš© | MED | λ‹¨μˆœ μœ„μƒ μ „νŒŒλŠ” μ‹€μ œ κ³΅μ •μ˜ '흐름 λ°©ν–₯(Flow Direction)'κ³Ό '밸브 개폐 μƒνƒœ'λ₯Ό λ¬΄μ‹œν•¨ $\rightarrow$ **μ—£μ§€ 속성(λ°©ν–₯μ„±, μƒνƒœ)을 λ°˜μ˜ν•œ κ°€μ€‘μΉ˜ 경둜 뢄석**으둜 고도화 | | **μ—λŸ¬ 핸듀링** | Python λΈŒλ¦Ώμ§€ 톡신 μ‹œ μ˜ˆμ™Έ 처리 미흑 | LOW | 뢄석 μ—”μ§„ λ‹€μš΄ μ‹œ C# μ„œλ²„μ˜ λΈ”λ‘œν‚Ή κ°€λŠ₯μ„± $\rightarrow$ **Circuit Breaker νŒ¨ν„΄ 및 νƒ€μž„μ•„μ›ƒ μ„€μ •** λͺ…μ‹œ ν•„μš” | | **μ‚¬μš©μž κ²½ν—˜(UX)** | λ‹¨μˆœ ν•˜μ΄λΌμ΄νŠΈ ν‘œμ‹œ | LOW | 영ν–₯도 κ²°κ³Όκ°€ λ§Žμ„ 경우 도면이 λΉ¨κ°„μƒ‰μœΌλ‘œ 도배됨 $\rightarrow$ **단계별 영ν–₯도(1μ°¨, 2μ°¨...) 색상 ꡬ뢄 및 필터링** κΈ°λŠ₯ μΆ”κ°€ | --- ## πŸ“¦ 1. ν•„μˆ˜ νŒ¨ν‚€μ§€ 및 기술 μŠ€νƒ ### 1.1 ν”„λ‘ νŠΈμ—”λ“œ (Visualization) | 기술/라이브러리 | μš©λ„ | λΉ„κ³  | |---|---|---| | `SVG / Canvas API` | P&ID 도면 λ Œλ”λ§ 및 데이터 μ˜€λ²„λ ˆμ΄ | **Canvas API μš°μ„  ꢌμž₯ (λŒ€κ·œλͺ¨ λ…Έλ“œ μ„±λŠ₯ μ΅œμ ν™”)** | | `Cytoscape.js` / `D3.js` | μœ„μƒ κ·Έλž˜ν”„ μ‹œκ°ν™” 및 μΈν„°λž™ν‹°λΈŒ 탐색 | κ·Έλž˜ν”„ 뢄석 λ·°μ–΄ | | `Vue.js` / `React` | 전체 UI ν”„λ ˆμž„μ›Œν¬ 및 μƒνƒœ 관리 | `src/Web` ꡬ쑰와 톡합 | | `Axios` / `WebSocket` | μ‹€μ‹œκ°„ OPC UA 데이터 μˆ˜μ‹  및 API 톡신 | **SignalR (ASP.NET Core) λ„μž… ꢌμž₯ (μ‹€μ‹œκ°„ μ–‘λ°©ν–₯ 톡신 μ΅œμ ν™”)** | ### 1.2 λ°±μ—”λ“œ (API & Analysis) | 기술/라이브러리 | μš©λ„ | λΉ„κ³  | |---|---|---| | `ASP.NET Core` | Graph API 및 뢄석 μ—”λ“œν¬μΈνŠΈ 제곡 | `ExperionCrawler` 메인 μ„œλ²„ | | `NetworkX` (Python) | 영ν–₯도 뢄석 및 경둜 좔적 μ•Œκ³ λ¦¬μ¦˜ μ‹€ν–‰ | 뢄석 μ—”μ§„ (Phase 2 ν™œμš©) | | `FastAPI` / `Flask` | Python 뢄석 μ—”μ§„κ³Ό C# μ„œλ²„ κ°„μ˜ λΈŒλ¦Ώμ§€ | 뢄석 λ§ˆμ΄ν¬λ‘œμ„œλΉ„μŠ€ | --- ## πŸ“ 2. 상세 섀계 ꡬ쑰 ### 2.1 μ‹€μ‹œκ°„ 데이터 μ˜€λ²„λ ˆμ΄ (Real-time Overlay) λ„λ©΄μ˜ μ’Œν‘œ 정보와 λ§€ν•‘λœ μ‹œμŠ€ν…œ νƒœκ·Έλ₯Ό μ—°κ²°ν•˜μ—¬ μ‹€μ‹œκ°„ 값을 ν‘œμ‹œν•©λ‹ˆλ‹€. 1. **λ§€ν•‘ 데이터 λ‘œλ“œ:** `(λ„λ©΄λ…Έλ“œID, μ‹œμŠ€ν…œνƒœκ·Έ, μ’Œν‘œ)` 리슀트λ₯Ό ν”„λ‘ νŠΈμ—”λ“œλ‘œ 전달. 2. **μ‹€μ‹œκ°„ 슀트리밍:** `OPC UA` $\rightarrow$ `C# Server` $\rightarrow$ `SignalR Hub` $\rightarrow$ `Frontend`. (**κ°œμ„ : λ³€κ²½λœ κ°’λ§Œ μ „μ†‘ν•˜λŠ” Delta Update 방식 적용**) 3. **동적 λ Œλ”λ§:** νƒœκ·Έ 값이 λ³€κ²½λ˜λ©΄ ν•΄λ‹Ή μ’Œν‘œμ˜ Canvas μš”μ†Œλ₯Ό μ—…λ°μ΄νŠΈν•˜κ±°λ‚˜ νˆ΄νŒμ— ν˜„μž¬ 값을 ν‘œμ‹œ. (**κ°œμ„ : Viewport λ‚΄ μš”μ†Œλ§Œ μ—…λ°μ΄νŠΈν•˜μ—¬ CPU λΆ€ν•˜ κ°μ†Œ**) ### 2.2 영ν–₯도 뢄석 μ—”μ§„ (Impact Analysis Engine) νŠΉμ • μ„€λΉ„μ˜ 이상 λ°œμƒ μ‹œ ν•˜λ₯˜(Downstream) 영ν–₯을 κ³„μ‚°ν•©λ‹ˆλ‹€. 1. **뢄석 μš”μ²­:** μ‚¬μš©μžκ°€ λ„λ©΄μ—μ„œ νŠΉμ • λ…Έλ“œ(예: νŽŒν”„ P-101)λ₯Ό 클릭. 2. **κ·Έλž˜ν”„ 탐색:** Python 뢄석 μ—”μ§„μ—μ„œ `nx.descendants(G, 'P-101')` μ‹€ν–‰. (**κ°œμ„ : μ—£μ§€μ˜ `flow_direction` 속성을 ν™•μΈν•˜μ—¬ μ‹€μ œ 유체 흐름 λ°©ν–₯으둜만 μ „νŒŒ 계산**) 3. **κ²°κ³Ό λ°˜ν™˜:** 영ν–₯λ°›λŠ” λͺ¨λ“  λ…Έλ“œ ID 리슀트, 경둜(Path), 그리고 **영ν–₯ 단계(Depth)**λ₯Ό λ°˜ν™˜. 4. **μ‹œκ°μ  κ°•μ‘°:** 도면 μƒμ—μ„œ 영ν–₯ 경둜λ₯Ό 단계별 색상(예: 1μ°¨-μ§„ν•œ λΉ¨κ°•, 2μ°¨-μ—°ν•œ λΉ¨κ°•)으둜 ν•˜μ΄λΌμ΄νŠΈ 처리. --- ## πŸ’» 3. μ‹€μ œ κ΅¬ν˜„ μ½”λ”© κ°€μ΄λ“œ (Example) ### 3.1 [Backend] 영ν–₯도 뢄석 API (C# $\rightarrow$ Python Bridge) ```csharp // src/Web/Controllers/PidGraphController.cs // 1. 뢄석 μƒνƒœ 좔적을 μœ„ν•œ DTO public record AnalysisStatus(string taskId, double progress, string status, string message); // 2. μ‹€μ‹œκ°„ μ§„ν–‰ μƒνƒœ 쑰회 API (Phase 5 병렬 처리 반영) [HttpGet("status/{taskId}")] public async Task GetAnalysisStatus(string taskId) { // Orchestratorκ°€ κ΄€λ¦¬ν•˜λŠ” μž‘μ—… μƒνƒœ μ €μž₯μ†Œ(Redis/MemoryCache)μ—μ„œ 쑰회 var status = await _statusService.GetStatusAsync(taskId); if (status == null) return NotFound(); return Ok(new { taskId = status.TaskId, progress = status.Progress, // 0.0 ~ 1.0 status = status.Status, // "Processing", "Completed", "Failed" message = status.Message }); } [HttpGet("impact/{nodeId}")] public async Task GetImpactAnalysis(string nodeId) { try { // Python 뢄석 λ§ˆμ΄ν¬λ‘œμ„œλΉ„μŠ€μ— μš”μ²­ (Timeout 및 Circuit Breaker 적용 ꢌμž₯) var response = await _httpClient.GetAsync($"http://python-analysis-api/impact/{nodeId}"); response.EnsureSuccessStatusCode(); var result = await response.Content.ReadFromJsonAsync(); return Ok(result); } catch (HttpRequestException ex) { // 뢄석 μ—”μ§„ μ—°κ²° μ‹€νŒ¨ μ‹œ μ μ ˆν•œ μ—λŸ¬ λ©”μ‹œμ§€ λ°˜ν™˜ return StatusCode(503, new { error = "Analysis Engine is currently unavailable", details = ex.Message }); } } ``` ### 3.2 [Frontend] Canvas 기반 데이터 μ˜€λ²„λ ˆμ΄ 및 μ§„ν–‰λ₯  ν‘œμ‹œ (JavaScript) ```javascript // src/Web/wwwroot/js/pid-viewer.js // 1. μ‹€μ‹œκ°„ κ°’ μ—…λ°μ΄νŠΈ (Canvas μ΅œμ ν™” 버전) async function updateRealtimeValues(tagData) { // tagData: { "TAG_01": { value: 10.5, status: "OK" }, ... } const ctx = canvas.getContext('2d'); for (const [tag, data] of Object.entries(tagData)) { const node = nodeMap.get(tag); // μ’Œν‘œ 정보 λ§΅ if (node && isInViewport(node)) { // 뷰포트 내에 μžˆμ„ λ•Œλ§Œ λ Œλ”λ§ ctx.fillStyle = data.value > threshold ? 'red' : 'green'; ctx.beginPath(); ctx.arc(node.x, node.y, 5, 0, Math.PI * 2); ctx.fill(); // 툴팁 데이터 μ—…λ°μ΄νŠΈ updateTooltipData(tag, data.value); } } } // 2. 뢄석 μ§„ν–‰ μƒνƒœ ν‘œμ‹œ (Phase 5 병렬 처리 반영) async function trackAnalysisProgress(taskId) { const progressBar = document.getElementById('analysis-progress-bar'); const statusText = document.getElementById('analysis-status-text'); const pollStatus = async () => { try { const response = await fetch(`/api/pid/status/${taskId}`); const data = await response.json(); // ν”„λ‘œκ·Έλ ˆμŠ€ λ°” μ—…λ°μ΄νŠΈ progressBar.style.width = `${data.progress * 100}%`; statusText.innerText = `뢄석 쀑... ${Math.round(data.progress * 100)}% (${data.message})`; if (data.status !== 'Completed' && data.status !== 'Failed') { setTimeout(pollStatus, 1000); // 1초 간격 폴링 } else { statusText.innerText = data.status === 'Completed' ? '뢄석 μ™„λ£Œ!' : '뢄석 μ‹€νŒ¨'; } } catch (e) { statusText.innerText = 'μƒνƒœ 쑰회 쀑 였λ₯˜ λ°œμƒ'; } }; pollStatus(); } ``` ### 3.3 [Analysis] 흐름 λ°©ν–₯ 반영 경둜 좔적 (Python) ```python import networkx as nx def get_propagation_path_with_flow(graph, start_node): """ λ‹¨μˆœ descendantsκ°€ μ•„λ‹Œ, μ—£μ§€μ˜ λ°©ν–₯μ„±(flow_direction)κ³Ό μƒνƒœ(valve_open)λ₯Ό κ³ λ €ν•œ μ‹€μ œ 영ν–₯ μ „νŒŒ 경둜 μΆ”μΆœ """ # 1. μœ νš¨ν•œ μ—£μ§€λ§Œ 필터링 (λ°©ν–₯이 맞고 λ°ΈλΈŒκ°€ μ—΄λ €μžˆλŠ” 경둜) valid_edges = [ (u, v, d) for u, v, d in graph.edges(data=True) if d.get('flow_direction') == 'forward' and d.get('valve_status') == 'open' ] filtered_graph = nx.DiGraph() filtered_graph.add_edges_from(valid_edges) # 2. μ „νŒŒ 단계별 λ…Έλ“œ μΆ”μΆœ (BFS) propagation_levels = nx.single_source_shortest_path_length(filtered_graph, start_node) # { node_id: distance } ν˜•νƒœλ‘œ λ°˜ν™˜ν•˜μ—¬ ν”„λ‘ νŠΈμ—”λ“œμ—μ„œ 색상 ꡬ뢄 κ°€λŠ₯ν•˜κ²Œ 함 return propagation_levels # 예: P-101μ—μ„œ μ‹œμž‘λ˜λŠ” μ‹€μ œ 유체 흐름 기반 영ν–₯도 뢄석 impact_map = get_propagation_path_with_flow(topology_graph, "P-101") ``` --- ## πŸš€ 4. Phase 4 μ™„λ£Œ κΈ°μ€€ (Definition of Done) - [ ] P&ID 도면(Canvas) μœ„μ— **μ‹€μ‹œκ°„ OPC UA κ°’**이 μ •ν™•ν•œ μ’Œν‘œμ— ν‘œμ‹œλ˜λ©°, 뷰포트 μ΅œμ ν™”κ°€ μ μš©λ˜μ—ˆλŠ”κ°€? - [ ] **SignalR λ˜λŠ” Delta Update**λ₯Ό 톡해 λ„€νŠΈμ›Œν¬ λΆ€ν•˜λ₯Ό μ΅œμ†Œν™”ν•˜λ©° μ‹€μ‹œκ°„ 데이터λ₯Ό μˆ˜μ‹ ν•˜λŠ”κ°€? - [ ] 병렬 처리 쀑인 뢄석 μž‘μ—…μ˜ **μ§„ν–‰ μƒνƒœ(Progress Bar)**κ°€ UI에 μ‹€μ‹œκ°„μœΌλ‘œ λ°˜μ˜λ˜λŠ”κ°€? - [ ] νŠΉμ • λ…Έλ“œ 클릭 μ‹œ **유체 흐름 λ°©ν–₯이 반영된 영ν–₯도 뢄석** κ²°κ³Όκ°€ 단계별 μƒ‰μƒμœΌλ‘œ ν•˜μ΄λΌμ΄νŠΈ λ˜λŠ”κ°€? - [ ] C# μ„œλ²„μ™€ Python μ—”μ§„ κ°„ 톡신에 **νƒ€μž„μ•„μ›ƒ 및 μ˜ˆμ™Έ 처리**κ°€ μ μš©λ˜μ–΄ μ‹œμŠ€ν…œ μ•ˆμ •μ„±μ΄ ν™•λ³΄λ˜μ—ˆλŠ”κ°€? - [ ] 전체 νŒŒμ΄ν”„λΌμΈ(`μΆ”μΆœ $\rightarrow$ λͺ¨λΈλ§ $\rightarrow$ λ§€ν•‘ $\rightarrow$ μ‹œκ°ν™”`)이 ν†΅ν•©λ˜μ–΄ λ™μž‘ν•˜λŠ”κ°€?