Files
ExperionCrawler/plans/P&ID-추출-PREFIX-DB-수정플랜-byBigPickle.md
windpacer 95ec160e98 docs: P&ID PREFIX 분류 tag_dcs 컬럼 도입 플랜 작성
plans/P&ID-추출-PREFIX-DB-수정플랜-byBigPickle.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 11:46:21 +09:00

15 KiB

P&ID 추출 PREFIX 분류 — tag_dcs 컬럼 도입 플랜

작성일: 2026-05-27
작성자: BigPickle
목적: pid_prefix_rulespid_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 = TRUETagClass = "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.TagDcsrule.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 스키마 (선행 필수)

  1. ExperionDbContext.cs Boot DDL에 ALTER TABLE 추가
  2. 마이그레이션 SQL 실행 (직접 또는 재기동 시 자동 적용)

Phase 2: 도메인/DTO/서비스 (C# 코어)

  1. PidPrefixRule.csTagDcs 프로퍼티 추가
  2. PidEquipment.csTagDcs 프로퍼티 추가
  3. PidPrefixRuleDto.cs — 3개 record 수정
  4. PidExtractorService.cs — 추출/CRUD/export/import/backfill 수정

Phase 3: Web Controller

  1. PidController.cs — camelCase 직렬화 확인 (필요시 [JsonPropertyName])

Phase 4: Web UI

  1. pid.js — PREFIX 그룹 렌더링 + Add/Update 폼
  2. panes/pid.html — 열 헤더

Phase 5: MCP / LLM 경로

  1. server.py_classify_pid_tag + _DB_SCHEMA + upsert_pid_connection
  2. worker/sql_prompt.py — DB_SCHEMA pid_equipment 항목
  3. prompts/plant_context.md — tag_dcs 설명

Phase 6: 검증

  1. dotnet build — 경고 0/에러 0
  2. python3 -m py_compile mcp-server/server.py — OK
  3. 웹 UI: PREFIX 분류 탭에서 DCS/현장 배지 확인
  4. pid_equipment 추출 후 SELECT tag_dcs, COUNT(*) FROM pid_equipment GROUP BY tag_dcs 확인
  5. 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로 충분. 재추출 불필요.