feat: Sub-Area(세부 Area) 분류 기능 + 포인트 삭제 시 메타데이터/이력 정리
하나의 area(P6)를 Column 단위 sub_area(P6-1/P6-2)로 분류. tag_metadata
attribute='sub_area'(EAV)에 저장, 공용 설비는 "P6-1,P6-2" 형식 + 토큰 매칭.
- 백엔드: GetSubAreaListByAreaAsync/UpdateSubAreaAsync/SeedSubAreaAsync,
SubAreaController(GET/PUT/POST seed), SubAreaDtos
- 포인트 삭제 개선: DeleteRealtimePointAsync(purgeHistory) — 잔여 0이면
고아 메타데이터(desc/area/sub_area) 정리, opt-in 시 history_table 영구 삭제
- MCP: find_tags(sub_area=...) + _area_or_subarea_filter('-' 포함 시 자동 토큰 매칭)
- 문서: prompts/plant_context.md, AGENTS.md, SubArea-추가플랜.md
- UI: 포인트빌더 Sub-Area 관리 카드(조회/수정/seed) + 행별 이력 삭제 체크박스
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -33,6 +33,47 @@ DB의 area 컬럼은 두 가지 표기가 혼재합니다 — 도구 호출 시
|
||||
|
||||
`unit-6`, `Unit 6`, `6번 유닛` 같은 표현은 area 코드가 아닙니다. 위 표의 정규 코드만 사용하세요.
|
||||
|
||||
## Sub-Area (세부 Area) 명명
|
||||
|
||||
하나의 area(P6 등)는 실제로는 **2개의 Column(증류탑)** 으로 나뉩니다. ExperionCrawler는
|
||||
태그 단위로 `sub_area`를 `tag_metadata`(attribute='sub_area')에 저장합니다 — OPC UA가 아니라
|
||||
ExperionCrawler가 결정한 값이 **Single Source of Truth** 입니다.
|
||||
|
||||
사용자가 "6-1차 플랜트", "6-1차"라고 부르면 → **sub_area = `P6-1`** 로 변환하세요.
|
||||
|
||||
| 운전원 호칭 | sub_area 코드 | area 코드 | Column | 번호 패턴 | 제품 |
|
||||
|---|---|---|---|---|---|
|
||||
| 6-1차 플랜트 | `P6-1` | `P6` | C-6111 | 61xx | PGMEA |
|
||||
| 6-2차 플랜트 | `P6-2` | `P6` | C-6211 | 62xx | HBM |
|
||||
| 9-1차 플랜트 | `P9-1` | `P9` | C-9111 | 91xx | |
|
||||
| 9-2차 플랜트 | `P9-2` | `P9` | C-9211 | 92xx | |
|
||||
| 10-1차 플랜트 | `P10-1` | `P10` | C-10111 | 101xx | |
|
||||
| 10-2차 플랜트 | `P10-2` | `P10` | C-10211 | 102xx | |
|
||||
| 1-1차 플랜트 | `P1-1` | `P1` | C-1111 | 11x | |
|
||||
| 1-2차 플랜트 | `P1-2` | `P1` | C-1211 | 12x, 13x | |
|
||||
| 2-1차 플랜트 | `P2-1` | `P2` | C-2111 | 211x | |
|
||||
| 2-2차 플랜트 | `P2-2` | `P2` | C-2121 | 212x, 213x | |
|
||||
|
||||
### sub_area 도구 호출 방법
|
||||
|
||||
- `active_alarms(area="P6-1")` — '-'가 있으면 server.py가 자동으로 sub_area 토큰 매칭
|
||||
- `find_tags(query="펌프", sub_area="P6-1")` — 전용 sub_area 파라미터 사용
|
||||
- `query_events(area="P6-1")` / `summarize_events(area="P6-1")` / `generate_status_report(area="P6-1")` — 동일
|
||||
- area 코드("P6")를 그대로 주면 sub_area 구분 없이 area 전체가 조회됩니다(기존 동작).
|
||||
|
||||
### ⚠️ 공용(shared) 태그
|
||||
|
||||
여러 sub_area가 함께 쓰는 설비는 `sub_area`에 **두 코드를 콤마로** 저장합니다 (예: `P6-1,P6-2`).
|
||||
이런 태그는 `P6-1` 필터에도, `P6-2` 필터에도 모두 잡힙니다. (토큰 매칭:
|
||||
`'P6-1' = ANY(string_to_array(sub_area, ','))`)
|
||||
|
||||
- P6 공용: `p-6201`(원료 투입 펌프), `ficq-6201`(공용 원료 유량 적산계) 등 → `P6-1,P6-2`
|
||||
- P2 공용: `vp-2127a/b`(진공 펌프), `p-2129a~d`(스크러버 순환 펌프), `li-2128a/b`(스크러버 레벨) → `P2-1,P2-2`
|
||||
|
||||
`sub_area`가 **NULL** 인 태그는 (1) 분류 규칙 미적용(예: 66xx/67xx/69xx cooling tower·steam·N2 같은
|
||||
area-level 유틸리티) 또는 (2) 아직 미분류 상태입니다. 공용 여부가 모호하면 `pid_equipment` 테이블에서
|
||||
같은 태그가 여러 Column에 연결되어 있는지 확인하세요.
|
||||
|
||||
## 계기 명명 약어
|
||||
|
||||
- `FIC` / `FT` — Flow Indicator Controller / Transmitter
|
||||
@@ -153,3 +194,66 @@ ORDER BY area_code;
|
||||
| "절차서/매뉴얼/설계서 검색" | `search_kb` |
|
||||
|
||||
도구 인자는 raw JSON으로 답변하지 말고, 표/요약으로 정리해 운전원이 바로 읽을 수 있게 답변하세요.
|
||||
|
||||
## P&ID 장비 연결 경로 (pid_equipment 테이블)
|
||||
|
||||
pid_equipment 테이블은 플랜트 장비의 유체 이동 경로를 저장합니다.
|
||||
|
||||
### ⚠️ 경로/흐름/공급/계통 질문 — 반드시 `trace_connections` 사용 (필수 규칙)
|
||||
|
||||
"원료 투입 경로", "스팀 흐름", "어디서 공급돼?", "C-6111로 뭐가 들어와?" 처럼 **유체/공급/계통
|
||||
경로**를 묻는 질문은 **절대 기억·추론으로 경로를 재구성하지 말고** `trace_connections`를 호출하세요.
|
||||
|
||||
1. **반드시 `trace_connections(start_tag=..., direction="upstream"|"downstream")` 1회 이상 호출.**
|
||||
2. 반환된 `path`의 **모든 노드를 누락 없이** 제시. 특히 `from_tag`/`to_tag`에 **쉼표로 여러 개**가
|
||||
있으면(예: `from_tag="P-6102, P-6201"`) 그건 **병렬 펌프·병렬 라인**이므로 **전부 나열**하세요.
|
||||
하나만 적고 끝내면 오답입니다.
|
||||
3. 각 노드의 `live_state`(R-RUN / L-STOP 등 실시간 상태)를 함께 표기하세요. 병렬 펌프가 여러 대면
|
||||
**실제 가동 중(R-RUN/L-RUN)인 것이 현재 공급원**이고, 정지(L-STOP)인 것은 예비/대기입니다.
|
||||
예: "F-6101A/B 상류 = P-6102(R-RUN, 현재 공급) + P-6201(L-STOP, 예비)".
|
||||
4. `trace_connections` 결과에 없는 장비를 임의로 추가하거나, 있는 분기를 빠뜨리지 마세요. 답변의
|
||||
경로 구성은 매번 이 도구 출력과 1:1로 일치해야 합니다.
|
||||
|
||||
### category 기준 분리
|
||||
|
||||
pid_equipment 테이블의 `category` 열로 물리적 경로와 제어 신호를 구분합니다.
|
||||
|
||||
1. **category != '제어'** → 물리적 유체 경로
|
||||
- 펌프, 열교환기, 탱크, 밸브, 계기, 필터 등
|
||||
- "A → B → C" 흐름 설명에 사용
|
||||
|
||||
2. **category = '제어'** → DCS 제어 신호
|
||||
- FICQ, LICA, TICA, PICA 등 DCS 제어기
|
||||
- from_tag: 물리적 계기 (측정값)
|
||||
- to_tag: 제어기 또는 제어 대상 밸브
|
||||
- "FT-6118 → FICQ-6118 → FCV-6118" 제어 루프 설명에 사용
|
||||
|
||||
### 경로 설명 시 조회 순서
|
||||
|
||||
1. **1단계**: `category != '제어'` 행으로 물리적 흐름 설명
|
||||
2. **2단계**: `category = '제어'` 행 중 `from_tag`가 1단계 결과에 포함되는 것 찾기
|
||||
3. **3단계**: "FT-6118(유량측정) → FICQ-6118(제어) → FCV-6118(밸브)" 형태로 제어 로직 연결
|
||||
|
||||
### 예시: 제품 추출 경로 설명
|
||||
|
||||
```sql
|
||||
-- 1단계: 물리적 경로
|
||||
SELECT tag_no, from_tag, from_at, to_tag, to_at, role, category
|
||||
FROM pid_equipment
|
||||
WHERE tag_no IN ('E-6117','P-6118','FT-6118','FCV-6118','XV-6123','T-6123',...)
|
||||
ORDER BY tag_no;
|
||||
|
||||
-- 2단계: 제어 태그 (from_tag가 1단계 결과에 포함되는 것)
|
||||
SELECT tag_no, from_tag, from_at, to_tag, to_at, role, category
|
||||
FROM pid_equipment
|
||||
WHERE category = '제어'
|
||||
AND from_tag IN ('FT-6118','P-6118',...);
|
||||
```
|
||||
|
||||
### 주의사항
|
||||
|
||||
- 같은 `tag_no`라도 여러 행이 있을 수 있음 (각 행은 서로 다른 유체 경로)
|
||||
- 예: E-6115A 조회 시 2행 반환 → 행1=스팀 경로, 행2=리보일러 경로
|
||||
- 설명할 때 "이 장비는 2개 경로가 있음"이라고 명시
|
||||
- OR 병합된 값("A, B" 같은)은 없음 — 각 행은 단일 경로
|
||||
- `from_at`, `to_at`은 연결 지점 상세 (예: "C-6111 중상부 제품 노즐")
|
||||
|
||||
Reference in New Issue
Block a user