Files
HC900-Crawler/docs/작업지시서-포인트빌더-컨트롤러별-realtime_table-기준.md
windpacer 61ca8a8c78 feat: Point Builder 컨트롤러 스코핑 + realtime_table 기준 필터/표시
- 컨트롤러 선택 드롭다운 추가, 요약/목록/일괄작업을 선택 컨트롤러로 스코프
- 요약 카드 재정의: 카탈로그/등록(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>
2026-06-10 19:22:50 +09:00

12 KiB
Raw Blame History

작업지시서 — 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순위

  1. Apply의 전역 is_active=false (Hc900DbContext.cs:341, Hc900ApplySelectedPointsAsync)

    • UPDATE hc900_map_master SET is_active = falseWHERE 절 없음. C3에서 Apply 한 번이면 C1/C2/C4의 is_active까지 전부 꺼져 카탈로그 상태가 손상됨.
    • 수정: WHERE controller_id = @c 추가 (선택 컨트롤러만 비활성화).
  2. 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 — 문서 오기

  1. 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 — 방어적 보강 (현재 무해)

  1. 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)

  1. Point Builder 상단에 컨트롤러 선택 드롭다운(C1/C2/C3/C4) 추가. 이후 모든 조회·요약·일괄작업이 선택 컨트롤러로 스코프된다.
  2. 요약 카드를 선택 컨트롤러 기준 + realtime_table 기준으로 재정의:
    • 카탈로그(총) / 등록(realtime_table) / 라이브 / 비실시간(stale) / 미등록
  3. 목록에 "등록"(realtime_table 존재) 상태 컬럼 추가, "라이브/비실시간/미등록"을 시각 구분.
  4. liveDict 조인을 (controller_id, tagname) 으로 정정.
  5. 일괄 활성/비활성·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 Hc900SyncRealtimeTableAsynccontrollerId 파라미터 추가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 기준:

  1. 요약 카드: 카탈로그 2146 / 등록 2146 / 라이브 2026 / 비실시간 120 / 미등록 0 으로 표시.
  2. 컨트롤러를 C1로 바꾸면: 카탈로그 1076 / 등록 0 / 미등록 1076.
  3. 목록에서 LICA-5113.AL1SP1(FlexibleParameter) → 등록(비실시간·stale), 타임스탬프 06-07.
  4. 목록에서 FICQ-6101.PV등록(라이브), 타임스탬프 현재.
  5. liveValue가 다른 컨트롤러 동명 태그와 섞이지 않음(controller 조인 확인).
  6. 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. 작업 순서 권장

  1. 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 개별 추가).