Files
ExperionCrawler/P&ID-재설계-plan.md
2026-05-08 17:22:10 +09:00

12 KiB

P&ID 재설계 계획 - Step 1: 현황 분석

작성일: 2026-05-04 상태: 완료


시스템의 본질 목적

ExperionCrawler의 핵심:

  • Honeywell Experion HS R530과 OPC UA 통신
  • 각 태그의 실시간 값 + 이력 데이터 저장/활용
  • P&ID 파싱은 부가 기능 (도면 ↔ 실시간 데이터 연결)

현재 문제

  • No-10_Plant_PID.dxf 처리 시 30분 타임아웃
  • 원인: 전체 DXF(28,819개 엔티티)를 한 번에 LLM에 전달
  • LLM 응답이 1800초를 초과

DXF 파일 분석 결과

파일: src/Web/uploads/pid/No-10_Plant_PID.dxf

파일 내 페이지 구성

  • PFD (PIPE & FLOW DIAGRAM) : 위치 PFD 텍스트 우측 최상단 2 페이지

    • ROLE : 플랜트의 장비 구성 및 성격, 처리해야할 물질 정보, 플랜트 유체 흐름 개략도 ,#10 PLANT 의 중요 정보를 표시한 도면,
    • Information : Full of Equipment Information & Role, Pump : Size,Type,Capacity,Material
  • SYMBOL & LEGEND : 1 페이지,

항목
총 엔티티 28,819개
LINE 20,868개
TEXT 3,562개
ARC 1,324개
CIRCLE 1,275개
LWPOLYLINE 865개
레이어 33개
전체 좌표 X: 1,5435,583, Y: 4,8226,756
  • #10 PLANT P&ID : 9 PAGES, (X:1630,Y54) 약 9개 도면이 수평으로 배치:
도면 X 범위 내용
#1 -176~2,000 SYMBOL & LEGEND (레전드)
#2 2,000~2,800 P&ID 도면
#3 2,800~3,600 P&ID 도면
#4~9 3,600~5,600 P&ID 도면

레전드 페이지 정점: X=1644.26, Y=5875.98

P&ID 재설계 계획 - Step 2: 성공 테스트 분석

작성일: 2026-05-04 상태: 완료


성공한 테스트 스크립트

파일 추출 대상 계측기 유형
test_dxf_extract_pid1.py Sensor FT, FIT, LT, PT, TE, PG, LG, TG
test_dxf_extract_pid2.py Valve FCV, TCV, LCV, PCV, XV, FV, LV, PV, TV
test_dxf_extract_pid3.py System LI, PI, TI, FIQ, FICQ, TICA, PICA, LICA, FIC, TIC, PIC, LIC
test_dxf_extract_pid-PG.py Gauge PG, TG, LG
Pump P-10101, VP-10117, DP-10101

성공 요인

  1. 계측기 유형별 분할 - 각 호출당 특정 태그만 추출
  2. 짧고 명확한 프롬프트 - "Extract valve tags only" 등
  3. max_tokens=65536 - 충분한 출력 공간
  4. 직접 vLLM 호출 - MCP 서버 경유 X (오버헤드 없음)
  5. 동시 실행 - 4개 스크립트 병렬 → GPU 최대 활용

현재 시스템과의 차이

테스트 (성공) 현재 (실패)
호출 경로 직접 vLLM C# → MCP → pid_worker → vLLM
프롬프트 유형별 분할 모든 태그 한 번에
max_tokens 65536 32768
병렬 처리 4개 동시 1개 순차

핵심 통찰

  • 분할 + 병렬 = 빠른 처리 + 높은 정확도
  • LLM이 처리해야 할 범위가 좁을수록 정확도 향상
  • vLLM이 여러 요청을 병렬로 처리하면 GPU 활용도 최대화

P&ID 재설계 계획 - Step 3: 레전드 페이지 분석

작성일: 2026-05-04 상태: 완료


레전드 페이지 위치

  • DXF 파일 내 첫 번째 도면 영역
  • 정점: X=1644.26, Y=5875.98
  • 영역: X: -176 ~ 2,000

INSTRUMENT IDENTIFICATION TABLE

ISA-5.1 표준 기반 계측기 식별 테이블:

첫 글자 의미 후속 글자 의미
A ANALYSIS, AUTO A ALARM
B BURNER COMBUSTION B BISTABLE
C CONDUCTIVITY C CONTROL
D DENSITY D DIFFERENTIAL, DETECTOR
E VOLTAGE, HEAT ENERGY E PRIMARY ELEMENT
F FLOW RATE F RATIO
G GAUGE, GAS G GAUGE, GLASS
H HAND H HIGH, HOLE
I CURRENT I INDICATOR
K TIME K CONTROL STATION
L LEVEL L LOW, PILOT LIGHT
M MAN, MOTOR M MOTOR OPERATOR
P PRESSURE
T TEMPERATURE

EQUIPMENT IDENTIFICATION

글자 장비 유형
C COLUMN
K COMPRESSOR
T TANK
D DRUM
E HEAT EXCHANGER

생성된 파일

  • mcp-server/pipeline/legend_parser.py
  • 계측기 유형별 그룹 정의
  • 태그에서 AREA 번호 추출 로직

P&ID 재설계 계획 - Step 4: 해결 방향

작성일: 2026-05-04 상태: 논의 중


목표

사용자가 "TI-6111C가 어디에 있나?"라고 물으면:

  • "6호 정제탑, 밑에서 3번째 온도 센서입니다. 현재 온도 105도"

핵심 전략

  1. 도면 분할 - 한 DXF 파일 내 여러 도면을 좌표 기반으로 분리
  2. 계측기 유형별 병렬 LLM 호출 - Sensor/Valve/System/Gauge/Pump 동시 처리
  3. 위치 정보 저장 - 태그의 DXF 상 좌표 + 근처 텍스트
  4. AREA 그룹핑 - 태그명에서 플랜트 번호 유추

구현 단계

Step 5: 도면 분할 로직

  • TITLE 레이어 LINE으로 도면 경계 감지
  • 각 도면별 X/Y 범위 추출
  • 레전드 페이지는 제외 (X < 2000)

Step 6: 계측기 유형별 병렬 LLM

  • pid_worker.py 수정
  • asyncio.gather()로 4개 동시 호출
  • max_tokens=65536 적용

Step 7: 위치 정보 추출

  • 태그 근처 텍스트 함께 추출
  • 상하좌우 위치 파악

Step 8: AREA 그룹핑

  • 태그명 파싱 (FICQ-6XXX → 6호)
  • DB 스키마 수정 또는 동적 그룹핑

Step 9: Experion 태그 매핑

  • 기존 match_pid_tags 활용
  • 실시간 값 연동

Step 10: UI 연동

  • 태그 검색 시 위치 + 현재 값 표시

유지할 것

  • topology.py의 배관-설비 연결 로직 (향후 활용)
  • 기존 DB 스키마 (pid_equipment 테이블)

변경할 것

  • pid_worker.py: 병렬 LLM 호출

  • server.py: 도면 분할 후 전달

  • extractor.py: 도면별 텍스트 추출


이렇게 해도 안된다면 나중에 고려할것,

  • ProcessPoolExecutor 사용: asyncio 대신 multiprocessing을 사용하여 5개의 CPU 프로세스에서 각각 요청을 날리게 합니다. (이것이 사실상 5개 프로그램을 띄우는 것과 가장 유사한 물리적 구조입니다.)
  • vLLM 서버 튜닝:--max-num-batched-tokens: 이 값을 Blackwell 메모리에 맞게 더 크게 키우세요 (예: 32768 이상).
  • --gpu-memory-utilization: 0.95 정도로 높여서 KV 캐시를 최대한 확보하세요.
  • HTTP 클라이언트 튜닝: httpx나 aiohttp 사용 시 limits 옵션을 수정하여 동시 연결 수를 강제로 늘려야 합니다. (기본값에 걸려있을 확률이 큽니다.)

아래는 C# 일때의 참고사항 -- 패스해

C#(.NET)에서는 파이썬의 multiprocessing과 유사하게 병렬 처리를 위한 여러 가지 방법을 제공합니다. 하지만 C#은 기본적으로 멀티스레딩에 매우 강력하며, CPU 집약적인 작업을 분산하기 위해 Task 기반의 병렬 처리나 별도의 프로세스 생성을 활용합니다.사용자님의 상황(LLM API에 5개의 요청을 동시에, 최대한 독립적으로 빠르게 던지기)에 가장 적합한 세 가지 구현 방식을 소개합니다.1. Parallel.ForEachAsync (가장 권장하는 방식).NET 6 이상에서 지원하며, 별도의 프로세스를 띄우지 않고도 내부적으로 스레드 풀을 활용해 최적의 병렬성을 유지하며 비동기 HTTP 요청을 보냅니다. 파이썬의 asyncio.gather보다 시스템 자원을 더 공격적으로 사용합니다.csharpusing System.Net.Http.Json;

var client = new HttpClient(); var prompts = new[] { "질문 1", "질문 2", "질문 3", "질문 4", "질문 5" };

// 동시에 실행할 최대 병렬 수 설정 (5개) var options = new ParallelOptions { MaxDegreeOfParallelism = 5 };

await Parallel.ForEachAsync(prompts, options, async (prompt, cancellationToken) => { var requestData = new { model = "Qwen2.5-27B-Instruct-FP8", messages = new[] { new { role = "user", content = prompt } } };

var response = await client.PostAsJsonAsync("http://localhost:8000/v1/chat/completions", requestData);
var result = await response.Content.ReadAsStringAsync();

Console.WriteLine($"응답 수신 완료: {result.Substring(0, 50)}...");

}); 코드를 사용할 때는 주의가 필요합니다.2. Task.Run을 이용한 물리적 스레드 분리파이썬의 멀티프로세싱과 가장 유사한 느낌으로, 각 요청을 완전히 별도의 스레드에 할당하여 실행합니다. CPU 오버헤드를 분산시키는 데 효과적입니다.csharpvar tasks = prompts.Select(prompt => Task.Run(async () => { // 여기서 API 호출 로직 수행 return await CallVllmApiAsync(prompt); }));

// 5개의 작업을 동시에 시작하고 모두 끝날 때까지 대기 var results = await Task.WhenAll(tasks); 코드를 사용할 때는 주의가 필요합니다.3. 실제 별도 프로세스 실행 (Process.Start)만약 파이썬에서 프로그램 5개를 따로 실행했던 것과 물리적으로 완전히 동일한 환경을 만들고 싶다면, C# 메인 프로그램에서 자기 자신(또는 별도 CLI)을 5번 실행하는 방식을 씁니다.csharpfor (int i = 0; i < 5; i++) { ProcessStartInfo startInfo = new ProcessStartInfo { FileName = "MyLlmClient.exe", // 실행할 프로그램명 Arguments = $"--prompt "질문{i}"", UseShellExecute = true // 별도 창에서 실행하여 독립성 확보 }; Process.Start(startInfo); } 코드를 사용할 때는 주의가 필요합니다.왜 C# 구현이 파이썬보다 유리할 수 있나요?No GIL: C#은 파이썬과 달리 GIL이 없어 하나의 프로세스 내에서도 여러 CPU 코어를 100% 독립적으로 활용합니다. 따라서 Parallel.ForEachAsync만 써도 파이썬에서 프로그램 5개를 띄운 것과 흡사한 성능이 나옵니다.HttpClient 커넥션 풀: .NET의 HttpClient는 내부적으로 소켓 연결을 효율적으로 관리하므로, 요청이 꽂히는 속도가 파이썬보다 훨씬 빠르고 정교합니다.팁: 성능 차이를 줄이려면C#에서 호출할 때 HttpClient 인스턴스를 하나만 생성(정적 또는 싱글톤)해서 재사용하세요. 매번 생성하면 소켓 고갈 및 핸드셰이크 지연으로 인해 토큰 생성 속도가 느려질 수 있습니다.위 방법 중 어떤 방식(단일 프로그램 내 병렬 vs 실제 별도 창 실행)이 현재 프로젝트 구조에 더 적합해 보이시나요? 상황에 맞는 더 디테일한 코드를 짜드릴 수 있습니다.

너는 어떤게 더 효율적이고 안전하다고 생각해? 안전성과 관리 효율성, 그리고 실제 성능을 종합해 볼 때 저는 1번 방식(Parallel.ForEachAsync)이 가장 우수하다고 생각합니다.그 이유를 세 가지 측면에서 분석해 드립니다.1. 효율성 (Resource Efficiency)프로세스 방식: 프로그램 5개를 따로 띄우면 런타임(Runtime) 메모리가 5배로 들고, OS 스케줄러가 5개의 프로세스를 왔다 갔다 하며 컨텍스트 스위칭(Context Switching) 비용이 발생합니다.Parallel 방식: 하나의 프로세스 안에서 .NET의 고성능 스레드 풀을 사용합니다. 불필요한 메모리 낭비 없이 CPU 코어를 가장 효율적으로 사용하여 vLLM에 요청을 전달합니다.2. 안전성 및 제어 (Control & Safety)병렬 수 제어: MaxDegreeOfParallelism 옵션을 통해 동시 요청 수를 코드 한 줄로 조절할 수 있습니다. (예: 5개에서 10개로 늘리거나, 서버 부하 시 2개로 줄이는 작업이 매우 쉽습니다.)예외 처리: 5개 중 하나가 실패했을 때, 개별 프로그램 방식은 로그를 일일이 확인해야 하지만, Parallel 방식은 try-catch 하나로 모든 요청의 성공/실패를 통합 관리할 수 있어 시스템이 훨씬 안정적입니다.3. 실제 성능 (Throughput)사용자께서 파이썬에서 경험하셨던 '속도 차이'는 파이썬의 구조적 한계(GIL) 때문일 가능성이 큽니다. C#의 Parallel은 물리적인 멀티코어를 직접 활용하기 때문에, 굳이 번거롭게 프로그램을 5개 띄우지 않아도 그에 못지않은(혹은 더 빠른) 성능을 낼 수 있습니다.