- 컨트롤러 선택 드롭다운 추가, 요약/목록/일괄작업을 선택 컨트롤러로 스코프 - 요약 카드 재정의: 카탈로그/등록(realtime)/라이브/비실시간/미등록 - 목록 필터를 is_active → realtime_table 멤버십(rt: 전체/실시간/실시간 제외)으로 교체 → 미등록 태그를 골라 실시간으로 보내는 워크플로 지원 - 목록 정렬을 controller_id → tagname 순으로(다중 컨트롤러 대비) - liveDict 조인을 (controller_id, tagname) 복합키로 정정 - Apply/Append/Sync에 controllerId 스코프 적용(타 컨트롤러 오염/손상 방지) - 전체 태그 목록을 2단 레이아웃 밖 하단 전체폭으로 이동, 컬럼폭 조정 - Steam Advisor TC 태그 .PV 접미사 정정 - 작업지시서 문서 추가 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
12 KiB
작업지시서 — Point Builder를 컨트롤러별 realtime_table 기준으로 전환
작성일: 2026-06-10 대상:
src/Hc900Crawler(Point Builder 탭 + 관련 API) 목적: Point Builder가 선택한 컨트롤러(플랜트)의 realtime_table 실제 등록 상태를 진실로 표시하도록 전환. 현재is_active가 전 컨트롤러 모두 TRUE라 "무엇이 실제로 폴링/저장되는지"를 분간할 수 없는 문제를 해결.
진단 결과 (코드 + 실데이터 교차 검증, 2026-06-10 재진단)
실제 코드(Hc900Controllers.cs, PointBuilderController.cs, Hc900DbContext.cs, Hc900RealtimeService.cs, pb.html, pb.js)와 실 DB 데이터(컨트롤러 간 동일 태그명 0건 확인)까지 대조한 결과:
🔴 HIGH — 1순위
Apply의 전역
is_active=false(Hc900DbContext.cs:341,Hc900ApplySelectedPointsAsync)
UPDATE hc900_map_master SET is_active = false— WHERE 절 없음. C3에서 Apply 한 번이면 C1/C2/C4의 is_active까지 전부 꺼져 카탈로그 상태가 손상됨.- 수정:
WHERE controller_id = @c추가 (선택 컨트롤러만 비활성화).
Hc900SyncRealtimeTableAsync컨트롤러 미필터 → realtime_table 오염 (Hc900DbContext.cs:446-483)
activeTags= is_active 전체(전 컨트롤러),existing= realtime_table(C3만). →toRemove = existing − active = 0(제거 아님),toAdd = active − existing = 4715(C1/C2/C4 행을 추가).- 즉 실패 양상은 "잘못 제거"가 아니라 "잘못 추가(오염)" — realtime_table이 2146→6861로 부풀어 "realtime_table=실제 폴링" 불변식이 깨짐.
- Append/Apply가 항상 Sync를 호출(L347/L372)하므로, is_active 전역 TRUE인 현 상태에선 Point Builder에서 Append/Apply를 누르는 순간 오염됨 → 잠재버그 아닌 활성 위험.
- 수정: controllerId 파라미터 추가 → activeTags·RealtimePoints 조회 모두 controller 필터.
🟡 DOC — 문서 오기
- API 경로 오기 —
§4.1 B3/§F1: 클래스[Route("api/hc900/tags")](Hc900Controllers.cs:226) +[HttpGet("controller-ids")](L345) → 실제 경로는GET /api/hc900/tags/controller-ids.
- (내 초안
/api/hc900/controllers도, 1차 진단의/api/hc900/controller-ids도 틀림 —/tags세그먼트 누락)🟢 LOW — 방어적 보강 (현재 무해)
Hc900AppendPointsAsync컨트롤러 미필터(Hc900DbContext.cs:368-370) 및 liveDict TagName 단일 매칭(PointBuilderController.cs:369-371)
- 코드상 tagName만 매칭은 맞으나, 실 DB에서 컨트롤러 간 동일 태그명이 0건(
GROUP BY tagname HAVING count(DISTINCT controller_id)>1→ 0행)이라 현재로선 오작동 불가. 향후 동명 태그 도입 대비 방어적 보강 항목으로 강등.✅ 현황 분석 정확성
인용된 14개 코드 위치 모두 실제 파일과 일치. 다만 위 1·2는 심각도 HIGH(일상 클릭에 발생), 4는 LOW(데이터상 무해)로 재평가됨.
1. 배경 (왜)
1.1 확인된 현재 데이터 (2026-06-10)
| C1 | C2 | C3 | C4 | 합계 | |
|---|---|---|---|---|---|
| map_master (카탈로그) | 1076 | 1538 | 2146 | 2101 | 6861 |
| is_active=TRUE | 1076 | 1538 | 2146 | 2101 | 6861 (전부) |
| realtime_table 등록 | 0 | 0 | 2146 | 0 | 2146 |
- map_master =
build_register_map_from_sinam.py가 만든 C1~C4 전체 카탈로그. - realtime_table = 현재 가동 중인 C3 게이트웨이 것만 등록됨.
is_active는 전 컨트롤러 일괄 TRUE → 필터로서 의미 없음. 실제 "등록/폴링" 진실은 realtime_table 멤버십.
1.2 현재 동작·문제점 (코드 위치)
| 구분 | 위치 | 문제 |
|---|---|---|
| 요약 카드 | Hc900Controllers.cs GetSummary L356-367 |
컨트롤러 무필터 → total/active를 4개 컨트롤러 합산(6861). "활성(폴링 중)"이 실제 폴링 수와 무관 |
| 요약 카드 UI | pb.html L38-39 (s-active "활성(폴링 중)", s-inactive) |
합산값 표시 → 어느 컨트롤러 것인지·실제 등록인지 불명 |
| 목록 API | PointBuilderController.cs GetPoints L331-398 |
controllerId 파라미터는 있으나 프론트가 미사용. liveDict 조인이 TagName만으로 매칭(L369-371) → 컨트롤러 무시(동명 태그 0건이라 현재 무해, 방어적 정정 대상) |
| 목록 UI 필터 | pb.html L216-217 (pf-search, pf-active) |
컨트롤러 selector 없음 |
| 목록 로드 | pb.js pbReload L143-151 |
params에 controllerId 미포함 |
| 요약 로드 | pb.js pbLoadSummary L168-173 |
무필터 summary 호출 |
| "현재 활성" 컬럼 | pb.html L201 / pb.js L251-261 |
isActive만 표시 → realtime_table 등록·라이브 여부 구분 없음 |
2. 용어 정의 (작업 공통 기준)
| 용어 | 정의 (판정식) | 의미 |
|---|---|---|
| 카탈로그 | hc900_map_master 행 (controller_id별) |
from_sinam로 만든 전체 후보 |
| 등록(assigned) | realtime_table에 (controller_id, tagname) 존재 |
실제로 그 컨트롤러에 할당되어 값이 저장되는 태그 ← Point Builder의 새 진실 기준 |
| 라이브(live) | is_active AND realtime_enabled (= 폴링 서비스 LoadMappingAsync 조건, Hc900RealtimeService.cs L143) |
실시간 갱신됨 |
| 비실시간(config) | 등록됐으나 realtime_enabled=FALSE (FlexibleParameters) |
행은 있으나 값이 stale (예: 알람SP·HZ) |
| 미등록 | 카탈로그엔 있으나 realtime_table에 없음 | C1/C2/C4 전체, 또는 C3 중 미할당분 |
3. 변경 목표 (To-Be)
- Point Builder 상단에 컨트롤러 선택 드롭다운(C1/C2/C3/C4) 추가. 이후 모든 조회·요약·일괄작업이 선택 컨트롤러로 스코프된다.
- 요약 카드를 선택 컨트롤러 기준 + realtime_table 기준으로 재정의:
- 카탈로그(총) / 등록(realtime_table) / 라이브 / 비실시간(stale) / 미등록
- 목록에 "등록"(realtime_table 존재) 상태 컬럼 추가, "라이브/비실시간/미등록"을 시각 구분.
- liveDict 조인을 (controller_id, tagname) 으로 정정.
- 일괄 활성/비활성·Apply·Append가 선택 컨트롤러 범위 안에서만 동작.
⚠ 범위 외(이번에 건드리지 않음): FlexibleParameters의
realtime_enabled정책. HZ/HZSET 등 필요한 것은 나중에 Point Builder에서 개별 추가한다(별도 작업). 본 작업은 가시화·컨트롤러 스코프에 한정.
4. 작업 항목
4.1 백엔드
(B1) 요약 API에 컨트롤러 + realtime_table 기준 추가
Hc900Controllers.cs GetSummary (L356-367)
- 쿼리파라미터
controllerId추가(필수 또는 기본값). 미지정 시 전체 유지(하위호환). - 카운트를 컨트롤러로 필터하고, realtime_table 기준 항목을 추가:
catalog = map_master WHERE controller_id=@c assigned = realtime_table WHERE controller_id=@c -- 등록(진실) live = map_master WHERE controller_id=@c AND is_active AND realtime_enabled config = assigned - live (등록됐으나 비실시간) unassigned= catalog - assigned - 응답 예:
{ controllerId, catalog, assigned, live, config, unassigned, byType:[...] } - byType도 controller로 필터.
(B2) 목록 API liveDict 조인 정정
PointBuilderController.cs GetPoints L369-376
RealtimePoints.Where(r => tagNames.Contains(r.TagName))→(controllerId, tagName)복합 매칭으로 변경. (controllerId 필터가 적용된 목록이므로 같은 controller로 조인)- 응답 item에
assigned(realtime_table 존재 bool) 필드 추가. (현재liveValue/timestamp로 간접 판단 → 명시 필드로)
(B3) 컨트롤러 목록 API 확인
- 컨트롤러 드롭다운 채울 소스 필요. 기존
GET /api/hc900/tags/controller-ids(Hc900Controllers L345-353, map_master DISTINCT controller_id) 재사용. (응답:["C1","C2","C3","C4"])
(B4) 🔴 Apply/Sync/Append 컨트롤러 스코프 — 최우선 (데이터 손상/오염 방지)
Hc900DbContext.cs Hc900ApplySelectedPointsAsync(L331), Hc900AppendPointsAsync(L359), Hc900SyncRealtimeTableAsync(L446)
- (B4-a) HIGH
UPDATE hc900_map_master SET is_active=false(L341)에WHERE controller_id=@c추가. 현재 WHERE가 없어 C3 Apply가 C1/C2/C4의 is_active까지 전부 끔. - (B4-b) HIGH
Hc900SyncRealtimeTableAsync에 controllerId 파라미터 추가 →activeTags(L448)·existingTagNames(L455) 모두WHERE controller_id=@c적용. 현재 미필터라 Apply/Append 시toAdd로 C1/C2/C4 4715행을 realtime_table에 오염 추가(2146→6861). Apply/Append가 controllerId를 Sync로 전달하도록 시그니처 변경. - (B4-c) LOW
Hc900AppendPointsAsync(L368-370) WHERE에 controllerId 추가(방어용 — 현재 동명 태그 0건이라 무해하나 일관성). - ※ 검증: Apply/Append 후 §5 기대치(C3 외 컨트롤러 행 불변, realtime_table C3=2146 유지)와 즉시 일치하는지 확인.
4.2 프론트
(F1) 컨트롤러 드롭다운 추가 — pb.html 상단(요약 카드 위)
<select id="pf-controller">+ **GET /api/hc900/tags/controller-ids**로 옵션 채움.- 변경 시
pbLoadSummary()+pbReload()재호출. 선택값 localStorage 유지(pb-controller).
(F2) 요약 카드 재구성 — pb.html L38-39, pb.js pbLoadSummary L168-173
- 카드: 카탈로그 / 등록(realtime_table) / 라이브 / 비실시간 / 미등록 (선택 컨트롤러 기준).
- "활성(폴링 중)" 라벨 → "등록(realtime_table)" 으로 교체(오해 해소). 라이브/비실시간 별도 카드.
(F3) 목록에 컨트롤러 파라미터 전달 + 등록 컬럼 — pb.js pbReload L143-151, pb.html L201 / pb.js L251-261
params.set('controllerId', 선택값)추가.- "현재 활성" 컬럼 → "등록/상태" 로:
assigned기준등록(라이브)/등록(비실시간·stale)/미등록3분류 + 색상. - timestamp가 오래된(예: > 5분) 등록행은 stale 뱃지 표기(비실시간 식별 보조).
(F4) 일괄작업 컨트롤러 스코프 안내 — pb.js pbBulkActivate/Apply/Append (L74-99)
- 확인 다이얼로그에 "선택 컨트롤러(Cx) 범위" 명시. 호출 시 controllerId 전달.
5. 검증 (Acceptance)
선택 컨트롤러 = C3 기준:
- 요약 카드: 카탈로그 2146 / 등록 2146 / 라이브 2026 / 비실시간 120 / 미등록 0 으로 표시.
- 컨트롤러를 C1로 바꾸면: 카탈로그 1076 / 등록 0 / 미등록 1076.
- 목록에서
LICA-5113.AL1SP1(FlexibleParameter) → 등록(비실시간·stale), 타임스탬프 06-07. - 목록에서
FICQ-6101.PV→ 등록(라이브), 타임스탬프 현재. - liveValue가 다른 컨트롤러 동명 태그와 섞이지 않음(controller 조인 확인).
- Apply를 C3에서 실행해도 C1/C2/C4의 is_active·realtime_table은 변하지 않음.
검증 쿼리(참고):
-- C3 기대치
SELECT
(SELECT count(*) FROM hc900.hc900_map_master WHERE controller_id='C3') catalog,
(SELECT count(*) FROM hc900.realtime_table WHERE controller_id='C3') assigned,
(SELECT count(*) FROM hc900.hc900_map_master WHERE controller_id='C3' AND is_active AND realtime_enabled) live;
6. 영향 범위 / 주의
- 하위호환: summary
controllerId미지정 시 기존 전체합산 유지 → 타 화면 영향 없음. - B4-a/b는 데이터 손상·오염을 일으키는 활성 위험(Append/Apply 클릭 시 발생): C3 Apply가 C1/C2/C4 is_active를 끄고(L341), Sync가 realtime_table에 C1/C2/C4 4715행을 추가. 반드시 controller 스코프 적용 후 다른 작업 진행.
- 본 작업은 realtime_enabled 정책을 바꾸지 않으므로 stale 120개는 그대로 남는다(의도). 가시화로 "stale임"만 드러낸다.
- realtime_table은 C3만 존재 → C1/C2/C4는 "미등록"으로 정상 표기됨(게이트웨이 미가동).
7. 작업 순서 권장
- B4-a/b 먼저(Apply 전역 UPDATE + Sync 오염 차단 — HIGH) → 2. B1/B2/B3/B4-c → 3. F1~F4 → 4. §5 검증.
빌드/테스트:
cd src/Hc900Crawler && dotnet build후 Point Builder 탭에서 컨트롤러 전환·카드·목록 상태 육안 확인.
본 문서는 작업지시(설계·범위)만 정의한다. 구현은 별도 진행하며, FlexibleParameters 실시간 편입은 범위 외(추후 Point Builder 개별 추가).