plans/P&ID-추출-PREFIX-DB-수정플랜-byBigPickle.md Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
15 KiB
P&ID 추출 PREFIX 분류 — tag_dcs 컬럼 도입 플랜
작성일: 2026-05-27
작성자: BigPickle
목적:pid_prefix_rules와pid_equipment두 테이블에tag_dcs BOOLEAN컬럼을 추가해,
P&ID 추출 시작 시점부터 현장 계기(field instrument)와 DCS 태그(DCS function block)를 구별한다.
0. 배경 및 문제
현재 구조의 문제
현재 pid_prefix_rules.category = 'instrument' 아래에 두 종류가 혼재:
| 종류 | 예시 prefix | 실제 의미 |
|---|---|---|
| 현장 계기 (field) | FT, PT, LT, TT, FCV, PCV, PSV, XV, FG, PG | 물리적 기기, 현장 설치 |
| DCS 함수블록 (system) | FIC, TIC, PIC, LIC, FY, TY, PY, LY | DCS/SCADA 내부 연산 블록, 물리 기기 없음 |
기존 tag_class = 'field'/'system' 컬럼이 이를 구별하려 했으나:
- 추출 후 후처리에서 판정 (ISA 후속문자 분석 + Experion 연결 여부)
- PREFIX 정의 UI에서는 전혀 보이지 않아 운전원이 구별 불가
- LLM이 pid_equipment 조회 시 instrument를 한꺼번에 가져와 혼동
목표
pid_prefix_rules 테이블에 tag_dcs BOOLEAN 추가 → PREFIX 분류 정의 시점부터 DCS 여부 명시.
pid_equipment 테이블에도 동일 컬럼 전파 → 추출 결과 전체에 flag 유지.
1. DCS vs Field 분류 기준
DCS 태그 (tag_dcs = TRUE) — DCS/Experion DB 포인트, 물리 기기 없음
| Prefix | 설명 | 비고 |
|---|---|---|
| FIC | Flow Indicator Controller | 제어루프 함수블록 |
| TIC | Temperature Indicator Controller | |
| PIC | Pressure Indicator Controller | |
| LIC | Level Indicator Controller | |
| FY | Flow Relay/Converter/Computing | DCS 연산요소 |
| TY | Temperature Relay/Converter | |
| PY | Pressure Relay/Converter | |
| LY | Level Relay/Converter | |
| FV | Flow Valve (function block) | DCS 출력 함수블록 (주의: 물리 FCV와 구별) |
| TV | Temperature Valve (function block) | |
| PV | Pressure Valve (function block) | |
| LV | Level Valve (function block) |
주의: FCV/PCV/LCV/TCV는 물리적 제어밸브 →
tag_dcs = FALSE(field 유지)
현장 계기 (tag_dcs = FALSE) — 물리 기기
| Prefix | 설명 |
|---|---|
| FT, TT, PT, LT | 1차 측정 전송기 (Transmitter) |
| FG, TG, PG, LG | 게이지류 (Gauge) |
| FCV, TCV, PCV, LCV | 제어밸브 (물리 기기) |
| PSV | 안전밸브 |
| XV | 차단밸브 |
| VIP, VIT | 진동 프로브/전송기 |
| DP | 차압계 |
| BV | 볼/버터플라이 밸브 |
2. 영향 범위 전체 목록
2.1 데이터베이스 (4곳)
| 대상 | 변경 내용 |
|---|---|
pid_prefix_rules 테이블 |
tag_dcs BOOLEAN NOT NULL DEFAULT FALSE 컬럼 추가 |
pid_prefix_rules 시드 |
DCS prefix에 tag_dcs = TRUE UPDATE |
pid_equipment 테이블 |
tag_dcs BOOLEAN NOT NULL DEFAULT FALSE 컬럼 추가 |
pid_equipment 기존 행 |
prefix rule로 backfill |
마이그레이션 SQL:
-- pid_prefix_rules 컬럼 추가
ALTER TABLE pid_prefix_rules ADD COLUMN IF NOT EXISTS tag_dcs BOOLEAN NOT NULL DEFAULT FALSE;
-- DCS prefix 마킹
UPDATE pid_prefix_rules
SET tag_dcs = TRUE
WHERE prefix IN ('FIC','TIC','PIC','LIC','FY','TY','PY','LY','FV','TV','PV','LV');
-- pid_equipment 컬럼 추가
ALTER TABLE pid_equipment ADD COLUMN IF NOT EXISTS tag_dcs BOOLEAN NOT NULL DEFAULT FALSE;
-- 기존 행 backfill (prefix rule 기반)
UPDATE pid_equipment pe
SET tag_dcs = pr.tag_dcs
FROM pid_prefix_rules pr
WHERE pr.prefix = pe.instrument_type
AND pr.tag_dcs = TRUE;
2.2 C# 도메인 엔티티 (2파일)
src/Core/Domain/Entities/PidPrefixRule.cs
// 추가
[Column("tag_dcs")]
public bool TagDcs { get; set; } = false;
src/Core/Domain/Entities/PidEquipment.cs
// 추가 (tag_class 아래)
[Column("tag_dcs")]
public bool TagDcs { get; set; } = false;
2.3 DTOs (1파일, 3개 record)
src/Core/Application/DTOs/PidPrefixRuleDto.cs
// 기존
public record PidPrefixRuleDto(int Id, string Prefix, string Category, string? Description, int SortOrder, DateTime CreatedAt);
public record CreatePidPrefixRuleRequest(string Prefix, string Category, string? Description, int SortOrder = 0);
public record UpdatePidPrefixRuleRequest(string Prefix, string Category, string? Description, int SortOrder = 0);
// 수정 후 (TagDcs 추가)
public record PidPrefixRuleDto(int Id, string Prefix, string Category, bool TagDcs, string? Description, int SortOrder, DateTime CreatedAt);
public record CreatePidPrefixRuleRequest(string Prefix, string Category, bool TagDcs = false, string? Description = null, int SortOrder = 0);
public record UpdatePidPrefixRuleRequest(string Prefix, string Category, bool TagDcs = false, string? Description = null, int SortOrder = 0);
2.4 Application Services (1파일)
src/Core/Application/Services/PidExtractorService.cs
변경 1: MatchCategoryAsync() → prefix rule에서 tag_dcs도 반환
현재는 string? category만 반환. (string? category, bool tagDcs) 튜플로 변경하거나,
별도 GetPrefixRuleByTagAsync(tagNo) 호출로 tag_dcs 획득.
변경 2: ClassifyTagClass() 단순화
기존 로직: hasExperionLink → system, ISA 후속문자 분석 → system/field
수정 후: tag_dcs = TRUE → TagClass = "system" (prefix rule이 ground truth)
Experion 연결 여부는 여전히 보완 신호로 유지 가능.
변경 3: 추출 저장 시 TagDcs 채우기
// 기존
item.Category = category;
item.TagClass = tagClass;
// 수정
item.Category = category;
item.TagDcs = tagDcs; // prefix rule에서 가져온 값
item.TagClass = tagDcs ? PidEquipment.TagClassSystem : tagClass; // 파생 또는 별도 로직
변경 4: CSV/Excel export에 TagDcs 열 추가
- CSV 헤더:
TagNo,...,TagClass,TagDcs - Excel 열 추가 (17번 열): "DCS태그" 불리언 → "DCS"/"현장" 표시
변경 5: Excel import에서 tag_dcs 처리
- Excel "DCS태그" 열 →
"DCS" → true, "현장" → false
변경 6: BackfillTagClassAsync() → BackfillTagDcsAsync() 추가
기존 backfill 로직에서 tag_dcs 미지정 행도 함께 backfill.
변경 7: CreatePrefixRuleAsync / UpdatePrefixRuleAsync
request.TagDcs → rule.TagDcs 저장.
2.5 인터페이스 (1파일)
src/Core/Application/Interfaces/IExperionServices.cs
IPidExtractorService 인터페이스 시그니처 수정:
CreatePrefixRuleAsync(CreatePidPrefixRuleRequest)— DTO 변경으로 자동 반영UpdatePrefixRuleAsync(int, UpdatePidPrefixRuleRequest)— 동일
2.6 EF Core DbContext (1파일)
src/Infrastructure/Database/ExperionDbContext.cs
변경 1: Boot DDL에 ALTER TABLE 추가
await _ctx.Database.ExecuteSqlRawAsync(
"ALTER TABLE pid_prefix_rules ADD COLUMN IF NOT EXISTS tag_dcs BOOLEAN NOT NULL DEFAULT FALSE");
await _ctx.Database.ExecuteSqlRawAsync(
"ALTER TABLE pid_equipment ADD COLUMN IF NOT EXISTS tag_dcs BOOLEAN NOT NULL DEFAULT FALSE");
변경 2: 시드 INSERT 수정
기존 INSERT는 ON CONFLICT DO NOTHING → 기존 행에 반영 안 됨.
마이그레이션 UPDATE 별도 실행 필요 (§2.1 마이그레이션 SQL).
변경 3: EF 모델 바인딩 (필요시)
modelBuilder.Entity<PidPrefixRule>() 블록에 tag_dcs 명시 없어도 Column attribute로 자동 매핑.
2.7 Web Controllers (1파일)
src/Web/Controllers/PidController.cs
변경 1: GetPrefixRules 응답
현재 PidPrefixRule 엔티티를 직접 직렬화 → TagDcs 필드 자동 포함 (Column attribute 추가로 충분).
변경 2: CreatePrefixRule / UpdatePrefixRule
request.TagDcs 가 DTO에 추가되므로 컨트롤러 수정 불필요 (서비스에서 처리).
변경 3: [JsonPropertyName("tagDcs")] 확인
익명객체 대신 DTO 반환 시 camelCase 보장 필요. (기존 패턴 확인 후 적용)
2.8 Web UI (2파일)
src/Web/wwwroot/js/pid.js
변경 1: CATEGORY_LABELS / CATEGORY_ORDER
// 기존
instrument: { label: 'Instrument', badge: 'ok' },
// 수정 — DCS는 별도 배지
// (카테고리가 'instrument'로 유지되고 tag_dcs로 구별하는 방식)
변경 2: PREFIX 그룹 렌더링 (pidRenderPrefixGroups)
각 prefix rule 행에 tag_dcs 체크박스/배지 추가:
// 테이블 열 추가
<td><span class="badge ${r.tagDcs ? 'warn' : 'ok'}">${r.tagDcs ? 'DCS' : '현장'}</span></td>
// 편집 행에도 tag_dcs 토글 추가
<input type="checkbox" ${r.tagDcs ? 'checked' : ''} data-field="tagDcs" />
변경 3: pidAddPrefixRule(category) 요청 body에 tagDcs 추가
변경 4: pidUpdatePrefixRule(id) 요청 body에 tagDcs 추가
변경 5: 장비 목록 테이블에 tag_dcs 배지 추가 (선택사항)
src/Web/wwwroot/panes/pid.html
- PREFIX 분류 정의 패널에 열 헤더 "DCS태그" 추가
- 도움말 텍스트 갱신
2.9 MCP Server Python (2파일)
mcp-server/server.py
변경 1: _classify_pid_tag() 반환에 tag_dcs 필드 추가
# 기존
return {"kind": "instrument", "prefix": prefix, "type": type_name}
# 수정 — DCS prefix 목록 상수 추가
_DCS_PREFIXES = {"FIC","TIC","PIC","LIC","FY","TY","PY","LY","FV","TV","PV","LV"}
return {
"kind": "instrument",
"prefix": prefix,
"type": type_name,
"tag_dcs": prefix in _DCS_PREFIXES
}
변경 2: _DB_SCHEMA 상수에 pid_equipment.tag_dcs 컬럼 설명 추가
_DB_SCHEMA = """
...
테이블: pid_equipment (P&ID 추출 태그/장비)
tag_no TEXT - 태그번호
category TEXT - 'instrument' / 'power_equipment' / ...
tag_dcs BOOL - TRUE=DCS 함수블록(FIC/TIC/PIC 등), FALSE=현장 물리 계기(FT/PT/FCV 등)
tag_class TEXT - 'field' / 'system' (tag_dcs 기반 + Experion 연결 보완)
instrument_type TEXT - prefix (FT/FIC/P 등)
...
"""
변경 3: upsert_pid_connection 함수
현재 허용 컬럼 목록: from_tag, to_tag, from_at, to_at, role, category, tag_class, connection_locked
→ tag_dcs 추가 허용 여부 검토 (운전원이 수동 override 가능하도록)
mcp-server/worker/sql_prompt.py
DB_SCHEMA 상수 — 현재 pid_equipment가 직접 언급되지 않으나,
향후 NL2SQL에서 "DCS 태그인지" 질문 처리를 위해 다음 추가:
테이블: pid_equipment(tag_no TEXT, category TEXT, tag_dcs BOOL, tag_class TEXT, instrument_type TEXT, from_tag TEXT, to_tag TEXT)
※ tag_dcs=TRUE: DCS 함수블록(FIC/TIC/PIC류), FALSE: 현장 물리 계기(FT/FCV류)
2.10 프롬프트 / 지식 파일 (1파일)
prompts/plant_context.md
현재 계기/태그 분류 설명에 다음 추가:
## pid_equipment.tag_dcs
- tag_dcs = TRUE: DCS 내부 함수블록 (FIC, TIC, PIC, LIC, FY, TY, PY, LY 등)
- 물리 기기 없음, Experion 데이터베이스 포인트로만 존재
- tag_dcs = FALSE: 현장 물리 계기 (FT, PT, LT, FCV, PSV, XV 등)
- P&ID 도면에 기기 심벌로 표시되는 실물
- "DCS 태그 몇 개?" → pid_equipment WHERE tag_dcs=TRUE COUNT
- "현장 계기 목록" → pid_equipment WHERE tag_dcs=FALSE AND category='instrument'
2.11 instrument_inference (검토 필요, 1파일)
mcp-server/instrument_inference/infer.py
현재 _dcs_internal_roles 집합으로 내부적으로 DCS/field 구별 중.
tag_dcs 컬럼 도입 후 이 로직이 중복될 수 있으나, infer.py는 추론 단계이므로
pid_prefix_rules.tag_dcs를 직접 참조할 수 없음 (독립 실행 모듈).
→ 변경 불필요 — _dcs_internal_roles 로직은 infer 내부 용도로 유지.
3. 단계별 구현 순서
Phase 1: DB 스키마 (선행 필수)
ExperionDbContext.csBoot DDL에ALTER TABLE추가- 마이그레이션 SQL 실행 (직접 또는 재기동 시 자동 적용)
Phase 2: 도메인/DTO/서비스 (C# 코어)
PidPrefixRule.cs—TagDcs프로퍼티 추가PidEquipment.cs—TagDcs프로퍼티 추가PidPrefixRuleDto.cs— 3개 record 수정PidExtractorService.cs— 추출/CRUD/export/import/backfill 수정
Phase 3: Web Controller
PidController.cs— camelCase 직렬화 확인 (필요시[JsonPropertyName])
Phase 4: Web UI
pid.js— PREFIX 그룹 렌더링 + Add/Update 폼panes/pid.html— 열 헤더
Phase 5: MCP / LLM 경로
server.py—_classify_pid_tag+_DB_SCHEMA+upsert_pid_connectionworker/sql_prompt.py— DB_SCHEMA pid_equipment 항목prompts/plant_context.md— tag_dcs 설명
Phase 6: 검증
dotnet build— 경고 0/에러 0python3 -m py_compile mcp-server/server.py— OK- 웹 UI: PREFIX 분류 탭에서 DCS/현장 배지 확인
- pid_equipment 추출 후
SELECT tag_dcs, COUNT(*) FROM pid_equipment GROUP BY tag_dcs확인 - LLM 채팅: "FIC-6113이 DCS 태그야?" 질문 → 정상 답변 확인
4. 설계 결정
| 항목 | 결정 | 이유 |
|---|---|---|
| 컬럼 타입 | tag_dcs BOOLEAN (별도 카테고리 X) |
카테고리 변경 시 하위 의존(뷰·필터) 전파 범위 과도. Boolean이 최소 침습적 |
tag_class 유지 |
유지 (deprecated 아님) | Experion 연결 ground truth 포함, 더 정밀. tag_dcs는 prefix 기반 빠른 flag |
tag_class 파생 |
tag_dcs=TRUE → TagClass='system' |
기존 ISA 분석 로직 보완이 아닌 override |
| FCV/PCV/LCV/TCV | tag_dcs = FALSE (현장 유지) |
물리 제어밸브. DCS가 제어하지만 기기 자체는 현장 |
| FV/TV/PV/LV | tag_dcs = TRUE |
ISA 표준상 "Valve(function block output)" — 물리 기기 아닌 DCS 출력 |
| UI 표시 | category 컬럼 유지, tag_dcs 배지 추가 | 카테고리 탭 구조(instrument/power_equipment…) 그대로 유지 |
| seed UPDATE 시점 | Boot DDL 이후 별도 UPDATE | INSERT ON CONFLICT DO NOTHING은 기존 행 미반영 |
| backfill | 재기동 시 자동 실행 (Boot DDL에 포함) | 수동 실행 의존성 제거 |
5. 잔여/고려사항
-
FICQ, TICQ 등 "Q" suffix prefix: 현재 시드에 없음. 추출 시 FIC의 변형으로 처리되므로
instrument_type = 'FICQ'로 저장되면 prefix rule 미매칭 →tag_dcs = FALSE(default) 오류 가능.
→ 시드에('FICQ','instrument','Flow IC Totalizer',10, TRUE)등 추가 필요 여부 검토. -
누락 prefix 처리:
pid_equipment.instrument_type이 prefix rule에 없으면 backfill 불가.
→tag_class = 'system'인 행으로 보조 매핑 가능. -
P&ID 도면 재추출 여부: 기존 추출 결과는 backfill SQL로 충분. 재추출 불필요.