Files
HC900-Crawler/docs/작업플랜-온도프로파일-이력조회.md
windpacer d88784635e docs: 작업지시·진단·아키텍처 설계 문서 추가
온도프로파일/PV일관성/PointBuilder/history 작업지시, 신호태그·스팀유량 진단, 베이직아키텍처 재설계, MSDS, LLM채팅 구조 등.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 08:12:01 +09:00

6.2 KiB
Raw Blame History

작업플랜 — 온도 프로파일 이력(History) 조회 (2026-06-07)

상위: docs/작업플랜-컬럼온도프로파일-이격모니터.md(실시간 모니터)의 시간축 확장. 현재 GET /api/steam/tempprofile/{col}realtime_table 최신값만 읽음 → history_table(60초 스냅샷) 로 과거 시점/구간 프로파일을 보게 한다.

목적

steam 패널 "온도 프로파일 이격 모니터"가 지금은 실시간 1점만 본다. 운전자가 과거 임의 시점의 단면 온도 프로파일(reb-A/B/C/D + 진공 + 제품매칭 + z이격)을 조회하고, 나아가 구간을 슬라이더/재생으로 스크럽해 프로파일이 시간에 따라 어떻게 움직였는지(처짐/상승/붕괴) 보게 한다.

현 자산 (재활용, 중복금지)

  • 엔드포인트: SteamAdvisorController.TempProfile(string col) (SteamAdvisorController.cs:180~). 로직 = ① {col}_tempref.json 로드(정적 기준밴드) → ② 현재값 dict cur 산출 (_ctx.RealtimePoints에서 태그별 최신) → ③ 제품매칭(reb_temp 최근접) → ④ z-score 이격 → ⑤ stages/vacuum/spanAD 응답. ②만 시간축으로 바꾸면 ③~⑤ 그대로 재사용.
  • 태그 매핑: TagsFor(ToSuffix(col)){reb_temp, T_B, T_C, T_D, vacuum, ...} (컬럼 무관, 재활용).
  • 기준밴드 tempref: 정적(period 기준) → 실시간/이력 공통, 변경 불필요.
  • 히스토리 인프라:
    • history_table(tagname, value, recorded_at, controller_id), 60초 간격, TimescaleDB hypertable.
    • IExperionDbService.QueryHistoryAsync(tags, from, to, limit) (Hc900DbContext.cs:1599) — 구간 원시.
    • QueryHistoryWithIntervalAsync + time_bucket 피벗(Hc900DbContext.cs:1719~) — 간격 집계.
    • POST /api/history/query, /api/history/query-interval (HistoryController, Hc900Controllers.cs:130).
  • 프론트: steam.js stTempTick(realtime 폴링)/stRenderTemp(d)(ECharts 렌더, steam.js:108~). stRenderTemp는 응답 d 형태에만 의존 → 이력 응답도 같은 형태면 렌더 재사용.

설계 — 값 제공자(value provider)만 시간축화

TempProfile에서 cur(태그→값 dict) 만드는 부분을 헬퍼로 분리하고 두 소스를 둔다:

  • realtime (기존): RealtimePoints 태그별 최신.
  • history@at: history_table에서 각 태그의 recorded_at <= at 최근접 1건.
    SELECT DISTINCT ON (tagname) tagname, value
    FROM history_table
    WHERE controller_id = @cid AND tagname = ANY(@tags) AND recorded_at <= @at
    ORDER BY tagname, recorded_at DESC;
    
  • history frames(구간 재생): time_bucket(@interval, recorded_at) 피벗으로 태그×시점 행렬 → 프레임 배열. (재활용: BuildHistoryIntervalQuerySql 패턴.)

값 파싱은 기존과 동일(double.TryParse). 상태레이블({N|LABEL|}) 태그는 온도엔 없음(숫자) → 안전.

단계

  1. 백엔드 리팩터 (선행)TempProfilecur 생성부를 BuildCurrent(tagMap, IValueSource) 헬퍼로 분리. 기존 realtime 경로는 동작 보존(회귀 0).
  2. 단일 시점 엔드포인트GET /api/steam/tempprofile/{col}?at={ISO8601}.
    • at 없으면 기존 realtime 동작(하위호환).
    • at 있으면 history@at 소스로 cur 산출 → 이후 로직(제품매칭·z·band) 동일.
    • 응답에 mode:"history", at(실제 매칭된 recorded_at), staleSec(at매칭시각) 추가.
    • 컨트롤러 해석: col→controller_id 매핑 필요(기존 매핑 경로 재활용; 없으면 모든 controller_id 허용).
  3. 구간 재생 엔드포인트(옵션, 2차)GET /api/steam/tempprofile/{col}/frames?from&to&interval.
    • time_bucket 피벗 → { at, stages, vacuum, spanAD, matchedProduct }[] 프레임 배열.
    • 제품매칭/z를 프레임마다 계산(reb_temp 변동 반영).
  4. 프론트 — 모드 토글 (steam.js / steam.html):
    • temp 탭에 [실시간 | 이력] 토글 + datetime-local 입력 + "조회".
    • 이력 선택 시 stTempTimer 중지(폴링 멈춤), ?at= 호출 → stRenderTemp 재사용.
    • 배지/상태줄에 "이력 YYYY-MM-DD HH:mm (스냅샷 Ns)" 표기.
  5. 프론트 — 스크럽/재생(옵션, 2차):
    • from~to + interval 입력 → frames 로드 → 슬라이더로 프레임 이동, ▶재생으로 애니메이션.
    • 각 프레임 stRenderTemp(frame) 호출(차트 setOption 재사용). reb-A/C 처짐 추이가 눈에 보임.

검증기준

  • ?at= 미지정 → 기존 실시간 응답과 동일(회귀 없음).
  • ?at=(과거 1시간 전) → 그 시점 reb-A/B/C/D·진공이 history 스냅샷과 일치, 제품매칭·z 정상.
  • history에 해당 시각 데이터 없음(컬럼 오프라인 구간) → at보다 과거 최근접 반환 + staleSec 큼, 또는 빈 결과 명시(에러 0).
  • 임의 컬럼(6-1/6-2/8/9/10차) 동일 동작 — col 키만 다름(하드코딩 금지).
  • 프론트 이력 모드 진입 시 실시간 폴링 정지, 복귀 시 재개.
  • (2차) frames 재생: 슬라이더 이동마다 프로파일 갱신, 재생 시 부드러운 전이.

주의

  • 타임존: recorded_at는 TIMESTAMPTZ(UTC). datetime-local(로컬 KST) → UTC 변환 후 질의. 응답 표기는 로컬로 환산(기존 toLocaleTimeString 일관).
  • controller_id 필터: history_table은 멀티컨트롤러 공유 → 반드시 col→controller_id로 필터(태그명만으로 부족할 수 있음).
  • 60초 해상도: history는 60초 스냅샷 → at 분해능 60초. 더 촘촘한 건 fast_record(고속) 별도(범위 밖).
  • tempref는 정적 기준 — 이력 조회해도 기준밴드는 동일 period 기준(과거 시점의 당시 기준이 아님). 필요 시 후속 과제로 period-aware 기준 분리.
  • 표시 전용 — 이력 조회는 제어와 무관(write 없음). 안전영향 0.
  • 성능: 단일 at는 DISTINCT ON 인덱스(recorded_at)로 가벼움. frames는 interval·범위 클램프로 과대질의 방지.

권장순서

①백엔드 헬퍼 분리 → ②?at= 단일시점 + 프론트 토글(여기까지가 1차 목표) → ③frames + 슬라이더/재생(2차). ①②는 실시간 경로 회귀 없이 독립 적용 가능.