Commit Graph

82 Commits

Author SHA1 Message Date
windpacer
b08fc32b76 fix(report): 운전모드 펌프 가동 판정 — '{N | label | }' 포맷 매칭
DetectMode가 펌프 livevalue를 IN ('1','5','L-RUN','R-RUN')로 정확일치 비교했으나,
realtime_table은 디지털 상태점을 '{5 | R-RUN | }' 포맷으로 저장(FormatValue) →
절대 매칭 안 됨 → pumpRunning 항상 false → 펌프 설정된 컬럼은 가동 중에도 무조건
'미가동(idle)'으로 오판정(카드 state·알람 진실원 오류).

LIKE '%RUN%'로 RUN 라벨 부분일치(TRIP/FAULT/STOP은 RUN 미포함→정지). 구 bare 값도 허용.
라이브 검증: C-6111 펌프 가동수 0→2 → 정상(normal) 판정 복구.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 07:06:26 +09:00
207104047d Merge pull request 'chore(sql): report_keep_qv_tags.sql 폐기표시 — 빌더가 근본 처리' (#9) from chore/deprecate-keep-qv-sql into main 2026-06-19 05:47:40 +09:00
windpacer
b828647cca chore(sql): report_keep_qv_tags.sql 폐기표시 — 빌더가 근본 처리
응급 수동 보정(FIQ-*.QV를 addr=0/realtime=false로 keep 분류)은 이제 불필요.
빌더가 AnalogPoint 보조슬롯에서 FIQ-*.QV를 실제 주소·realtime=TRUE·archive=TRUE로
생성하므로 map 재빌드/--db-conn 적용 시 자동 분류·폴링된다. 이력 보존용으로만 유지.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 05:47:38 +09:00
7c49e91c27 Merge pull request 'fix(crawler): realtime_table id bigint(시퀀스 오버플로) + HCX 폴링 비활성' (#8) from fix/realtime-seq-bigint-and-hcx into main 2026-06-19 05:44:38 +09:00
windpacer
b3985d77fb fix(crawler): realtime_table id bigint(시퀀스 오버플로) + HCX 폴링 비활성
운영 사고 재발 방지:
- realtime_table.id가 SERIAL(int4)이라 매 poll의 nextval 소비로 2^31 도달 →
  전체 realtime 업서트가 'reached maximum value of sequence'로 정지(현장에서 14h
  무갱신 발생, bigint ALTER로 라이브 복구 완료). 신규 배포: CREATE를 BIGSERIAL로,
  기존 배포: 기동 시 멱등 마이그레이션(integer일 때만 bigint+시퀀스 ALTER).
- gateway-config: HCX(IP·맵 없는 플레이스홀더)가 enabled=true라 50055 연결거부
  로그 양산 → enabled=false.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 05:44:11 +09:00
017caaff3a Merge pull request 'fix(builder): AnalogPoint 보조 A1~A4 일반 어드레싱 + 폴링 대상화' (#7) from fix/builder-aux-generalize into main 2026-06-19 05:20:43 +09:00
windpacer
c7a115c928 fix(builder): AnalogPoint 보조 A1~A4 일반 어드레싱 + 폴링 대상화
이전(PR#5)은 비루프 AnalogPoint에만, 또 realtime=False로 보조를 넣어 적산 QV가
크롤러 폴링집합(is_active AND realtime_enabled)에서 빠져 라이브로 흐르지 않았다.

- is_analog 판정을 분기 앞으로 올려 모든 AnalogPoint(루프 포함)에 보조 처리 적용.
  루프 엔트리와 겹치면 seen_tags가 중복 흡수.
- 슬롯명 있는 A1~A4를 일반적으로 모두 어드레싱(공백 슬롯만 제외).
- realtime 기본값(True) 사용 → 보조도 폴링 대상. QV=연속 누적값만 archive 유지.
- FlexibleParameter(사용자정의)는 기존대로 realtime=False(폴링 제외) 유지.

맵 재생성(c1~c4): 순수 추가. C3 +6(FICQ-8118/LICA-5113 GAIN/RATE/RESET 등 루프
보조 누락분), 리포트 스팀적산 FIQ-*.QV 전부 포함, 제거 0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 05:20:12 +09:00
f969cbb4a6 Merge pull request 'chore(map): register-map c1~c4 재생성 — 보조 QV(FIQ 적산) 포함' (#6) from chore/regen-register-maps into main 2026-06-18 21:54:12 +09:00
windpacer
9d8e0583e7 chore(map): register-map c1~c4 재생성 — 보조 QV(FIQ 적산) 포함
PR #5(빌더 보조 QV 처리)로 재생성. AnalogPoint 보조슬롯의 적산 QV가 실제 주소로
포함됨 → 게이트웨이 재시작 시 라이브 적산 폴링, archive=true.
순수 추가(제거 0): .QV 다수 + 일부 RST/한계값(기존 빌더 드리프트 반영).
리포트 의존 스팀적산 FIQ-6115/6215/8115(C3)·9115/9215/10115/10215(C4) 모두 포함.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 21:53:40 +09:00
ea6c4625e8 Merge pull request 'fix(builder): AnalogPoint 보조 QV(AuxiliaryNameA*) 레지스터 생성 — FIQ 적산 누락 근본해결' (#5) from fix/builder-analogpoint-aux-qv into main 2026-06-18 21:52:56 +09:00
windpacer
47149253aa fix(builder): AnalogPoint 보조 QV(AuxiliaryNameA*) 레지스터 생성
FIQ 류 적산기는 QV(적산)가 FlexibleParameter가 아니라 AnalogPoint 보조슬롯
(AuxiliaryNameA{n}=QV ↔ SourceAddressA{n})에 노출된다. 빌더는 FlexibleParameter
에서만 .QV를 만들어 FICQ-*.QV는 잡았지만 FIQ-*.QV(스팀 적산 등)를 전부 놓쳤다.
→ 리포트(energy_intensity_qv 등)가 의존하는 태그가 register-map/map_master에서
누락되어 archive 기준 '삭제대상'으로 오분류, 라이브 폴링도 안 됐음.

AnalogPoint else 분기에 보조 A1~A4 처리 추가: 이름 있는 보조슬롯을 실제
SourceAddress로 해석해 {item}.{AuxName} 엔트리 생성, QV=연속 누적값→archive=TRUE.
realtime=False(기존 .QV/FlexibleParameter와 동일 정책).

검증(dry build): C3 FIQ-6115/6215/8115.QV, C4 FIQ-9115/9215/10115/10215.QV 등이
실제 주소(8390 등)·archive=true로 생성. 커밋맵 대비 순수 추가(제거 0).
→ map 재빌드 시 keep 분류 자동 유지 + 라이브 적산 폴링 가능.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 20:55:44 +09:00
1d54b80160 Merge pull request 'chore(report): 디지털잔재정리 문서 폐기 + 리포트 .QV 7개 keep 재분류' (#4) from chore/report-keep-qv-and-supersede-cleanup into main 2026-06-18 20:43:23 +09:00
windpacer
bb99729e9e chore(report): 디지털잔재정리 문서 폐기 + 리포트 .QV 7개 keep 재분류
- docs/작업지시-history-디지털잔재정리.md: SUPERSEDED 배너(실행 금지).
  2026-06-18 실측 — 삭제대상(비-archive ~2천만)은 디지털 잔재 0, 전부 아날로그
  루프/적산. 특히 .QV는 리포트(history_table)가 의존 → 삭제 시 과거 리포트 파손.
- scripts/sql/report_keep_qv_tags.sql: SteamFlow 적산 FIQ-*.QV 7개를
  archive_enabled=TRUE로 재분류(register-map 누락 보정). realtime/is_active=FALSE
  로 폴링 부작용 0. 멱등(map 재빌드 후 재적용). DB에는 이미 적용.

검증: 리포트 태그 삭제대상 0행, 전체 삭제대상 19.98M→17.62M(2.36M 보호).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 20:42:37 +09:00
56628fca7a Merge pull request 'fix(di): DbContextOptions 수명 Singleton — AddDbContextFactory 스코프 충돌 해소' (#3) from fix/dbcontextfactory-options-lifetime into main 2026-06-18 10:07:31 +09:00
windpacer
12d90c1d79 fix(di): DbContextOptions 수명 Singleton — AddDbContextFactory 병용 스코프 충돌 해소
AddDbContextFactory(싱글톤)가 Scoped DbContextOptions를 소비해 Development의
ValidateOnBuild에서 기동 크래시. optionsLifetime=Singleton으로 옵션을 공유
(불변 설정이라 동작 무변경; DbContext 자체는 Scoped 유지). EF Core 권장 패턴.
Development 재기동으로 검증.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 10:06:29 +09:00
5ce09480b6 Merge pull request 'P2 셀프서비스 리포트 — 기간 일반화·템플릿 관리·자동생성 스케줄·EU레인지 클린범위' (#2) from feat/p0-selfservice-report into main 2026-06-18 08:51:07 +09:00
windpacer
f8e6d5bf24 feat(report): 생성본 과거 다운로드 — report_run.out_blob 이력 조회/다운로드
- IReportTemplateStore.ListRunsAsync/GetRunBlobAsync (+ ReportRunInfo DTO).
- GET /api/report/template/{id}/runs (최신순), GET /api/report/run/{runId}/download.
- UI: 선택 템플릿 '생성 이력' expander — 기간/소스/상태/시각 + 과거본 다운로드 링크.
  스케줄 자동생성본도 여기서 회수(기존엔 생성·기록만 되고 조회 불가).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 21:50:47 +09:00
windpacer
4ac35c3ce0 feat(report): P2-D 클린범위 자동도출 — tag_metadata EU레인지(eulo/euhi)
- EuRangeProvider: tag_metadata의 eulo/euhi를 base_tag별 1회 캐시(thread-safe),
  미존재/로드실패 시 역할 기본값 폴백. ComputeAsync에서 선로딩(동기 조회 대비).
- ReportColumnMap.Tag(): 클린범위(스파이크/드롭아웃 BETWEEN 경계) = EU레인지 우선.
  energy_efficiency/yield/control_residual의 하드코딩 TEMP/STEAM/FLOW 제거.
- 검증: hc900.tag_metadata에 947태그 eulo/euhi 적재 확인(리포트 태그 전부 포함).
  운전모드 제외는 별개(cleaning 마스크); EU레인지는 계기 물리유효범위만 담당.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 21:48:45 +09:00
windpacer
400ba2c639 feat(report): P2-C 템플릿별 자동생성 스케줄러 — 멱등 catchup BackgroundService
- report_template에 schedule_enabled/period/hour/source 컬럼(ALTER IF NOT EXISTS,
  p0_report.sql + 서비스 EnsureSchema 멱등).
- Hc900ReportScheduleService: 매 N초 활성 템플릿마다 완료된 기간(anchor)을
  lookback 내 산출 → report_run 성공기록 없으면 FillAsync 생성·기록.
  중복 0(HasSuccessfulRun dedup)·다운타임 자동복구. 윈도는 셀 토큰 period=가 결정.
- IReportTemplateStore.UpdateSchedule/HasSuccessfulRun, ListAsync에 스케줄 컬럼.
- PUT /api/report/template/{id}/schedule (period/hour 검증).
- UI: 목록에 스케줄 상태 표시 + 선택 템플릿 스케줄 편집기.
- appsettings Report:Schedule (간격·catchup) 추가.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 21:30:17 +09:00
windpacer
c825e45b38 feat(report): P2-B 템플릿 관리 UI — 저장 템플릿 목록/선택/삭제 + 등록·생성 분리
- IReportTemplateStore.ListAsync(생성이력 집계 LEFT JOIN)/DeleteAsync(report_run CASCADE).
- GET /api/report/templates, DELETE /api/report/template/{id}.
- 엑셀폼 패널: 매번 재업로드 → 등록(1회) → 저장 목록에서 라디오 선택 → 생성.
  생성횟수·최근시각 표시, 삭제 버튼, 생성 후 목록 자동 갱신.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 21:10:36 +09:00
windpacer
5f2780186e feat(report): P2-A 토큰 파서 일반화 — period=DAILY|MONTHLY|YEARLY 윈도 분기
- MetricRequest/Result에 Period 추가, ReportMetricService.PeriodWindowUtc로
  선택일이 속한 일/월/연 [from,to)를 KST→UTC 변환(DAILY는 기존 동작 동일).
- 오타 period는 daily 묵인 대신 명시적 error(결정론 게이트).
- ReportFillService 토큰 period= 파싱 + 셀주석/cells_json 박제.
- summary 엔드포인트 period 파라미터, 웹 바로보기 기간 드롭다운 + 치트시트.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 21:07:03 +09:00
windpacer
52defc6a62 docs(report): P1 전환 후 죽은코드 점검 결과 정리 — history_1min 보존·p1_historian.sql 유지 결정
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 11:12:34 +09:00
1031ec7599 Merge pull request 'P0 셀프서비스 결정론 리포트 — 적산·물질수지 폐합·cleaning 마스크 (+ P1 온라인 스펙)' (#1) from feat/p0-selfservice-report into main 2026-06-17 07:49:22 +09:00
windpacer
cdf2719a5e feat(report): 운전모드에 펌프 가동(RUN/STOP) 반영 + /live 최신 window만
- 펌프 게이트: 펌프 정지(state STOP/TRIP/FAULT) → 잔류 유량과 무관하게 idle.
  펌프 가동(L-RUN=1/R-RUN=5) → 흐름 기반(normal/cleaning/drawdown). DetectModeAsync 통합.
  인디케이터 펌프 = Report:Pumps:{col} 또는 제품펌프 P-{제품번호} 유도. realtime_table 현재 상태로 판정.
- /live: DISTINCT ON (column_id,kpi) 최신 window_start만 반환 — 과거 날짜 잔여 행이
  stale state로 표시되던 버그 수정.

검증: C-6111 펌프RUN→normal, C-6211 펌프STOP→idle(잔류유량 12.8 있어도). 펌프 상태값
5=R-RUN/1=L-RUN/0=L-STOP (tag_metadata state 라벨 확정). /live 컬럼당 state 단일.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 21:41:29 +09:00
windpacer
333d5f2a7b fix(report): 카드 state = 운전모드 단일 진실원 + 이송 메시지 컬럼별 정정
- ★카드 state를 production_total(누적적산)이 아니라 운전모드(순간 feed/product/vacuum)에서
  도출. 적산기가 평평해도(데모) 유량 흐르면 normal로 표시 — "살아있는데 미가동" 모순 해결.
  DetectModeAsync(idle/cleaning/drawdown/normal/no_data) = 카드 state·알람 공통 진실원.
  idle = feed≈0 AND product≈0(진짜 미가동)일 때만. 버퍼에 없으면 no_data(idle 아님).
- 알람도 같은 모드 사용(state-알람 진실원 일치). 이송 메시지는 Report:Transfer config로
  컬럼별 정정 — C-9111↔C-10111만 "이송" 언급, 그 외는 "인벤토리 인출"만. 배너 룰접두사 중복 제거.

검증: C-6111 state=normal(이전 idle 모순 해결), C-9111 no_data(버퍼밖 정직표시),
C-8111 drawdown 메시지 이송 미언급.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 11:17:13 +09:00
windpacer
005e0dc3dd feat(report): 알람 디바운스 + 카드 민감단 트렌드 스파크라인
- 디바운스: 알람 조건이 AlertDebounceSec(기본 60s) 연속 유지돼야 발화(인메모리 후보 추적).
  flapping(임계 빈번교차) 억제. 조건 해제 시 후보 리셋.
- 스파크라인: 카드마다 민감단 온도(TC) 최근 60분 미니 SVG 트렌드.
  GET /api/report/sparks (history_1s 다운샘플, 순수 SQL — Timescale 함수 미사용으로
  search_path=hc900 무관). ReportColumnMap.TcTag.

검증: /sparks 200(C-6111 31점 79.5~81.7℃), 디바운스가 데모 sim flapping 정확히 억제
(70초 후 알람 0 — 지속조건 아님). 실데이터의 지속 drawdown은 60s 후 발화.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 20:52:29 +09:00
windpacer
1b6a717e76 feat(report): 라이브 모니터 웹뷰 (상태 카드 그리드) + 알람·동특성 패널
운전원/관리자용 실시간 대시보드 — 한눈에 상태 파악.

- 리포트 탭 최상단 ① 라이브 모니터: 컬럼 카드 그리드(상태 색상 normal/idle/cleaning/
  drawdown/error), 폐합%가 hero(98~102 초록/이탈 빨강), IN/OUT·생산·수율, 알람 chip.
- 상단 알람 배너(severity 색상), 자동갱신 15s 토글, 카드 클릭 → 상세 패널
  (물질수지 IN/OUT 분해 + 제어품질 잔차 + 동특성 OP travel/헌팅주기, history_1s).
- 데이터: /api/report/live + /alerts + /summary(history_1s) + dynamics.
- LiveKpiService: 카드용 폐합 성분(feed_qv/out_total)도 live_kpi 저장(closure ok 시).

검증: 새 JS 서빙, /live 28행(state=idle 정상), /alerts C-8111 drawdown 활성.
데모 sim은 totalizer 평평이라 폐합/생산 "—"(카드·색상·알람·동특성 동작).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 15:30:47 +09:00
windpacer
9d4269a02c feat(report): dynamics 메트릭 (고해상 전용) — 밸브 stiction/hunting 진단
history_1s/fast_record 전용 루프 동특성 — 60초로는 불가능한 진단.

- 대상: 하부온도 루프 TICA-*A의 PV vs OP(스팀밸브).
- 산출: OP travel/h(밸브 활동=stiction/마모/hunting proxy), PV sd(고해상 변동),
  헌팅 주기(s, PV mean-crossing 기반), crossings.
- 고해상 게이트: 60초 history → no_data + 사유. 표본<30s → no_data.
- FOPDT 모델 식별은 step-test 필요라 미표방(정직 범위 = 진단지표).

검증: C-6111 하부루프 history_1s 30분 → PV sd 2.77, OP travel 35/h, 헌팅주기 ~97s.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 08:20:57 +09:00
windpacer
c3a5258bf2 feat(report): P1d 실시간 알람 엔진 (kpi_alert) — P1 온라인 히스토리안 완료
운전모드/폐합 기반 실시간 알람 — P1 마지막 조각.

- kpi_alert 테이블(edge-trigger: 진입 active, 해제 resolved, DB상태라 크래시 무관).
- Hc900LiveKpiService 알람 평가: 최신 history_1s 샘플로 운전모드(cleaning/drawdown/normal) 판정
  + 폐합 이탈(normal & |closure-100|>tol). 룰: cleaning/drawdown(info), closure_deviation(warning).
- GET /api/report/alerts. Report:LiveKpi:ClosureTolerancePct(기본 2%) config.

검증: 라이브 C-8111 drawdown 알람 발화(feed≈0 포착), opened_at 고정·updated_at 갱신(edge-trigger),
/alerts 정상. (데모 sim은 임계 빈번교차로 flapping 가능 — M분 디바운스는 운영 튜닝.)

P1 완료: P1a 1초버퍼 · P1b 연속집계 · P1c 온라인누적기 · P1d 알람.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 08:15:29 +09:00
windpacer
b820e6c33a feat(report): P1c 온라인 KPI 누적기 (live_kpi) + 메트릭 소스 일반화
온라인 히스토리안 3계층 — 실시간 KPI를 history_1s에서 산출·캐싱.

- 메트릭 소스 일반화: history_table | history_1s | history_1min_src | fast_record.
  history_1min_src = 연속집계 드롭인 호환뷰(bucket→recorded_at).
- Hc900LiveKpiService: 매 15s 오늘(KST) 컬럼별 KPI(production/yield/energy/closure)를
  history_1s에서 재계산해 live_kpi upsert. 재계산=러닝상태의 stateless 동등판(causal 동치,
  크래시 복구 불필요). 컬럼 state(normal/idle/error) 산출.
- live_kpi 테이블 + GET /api/report/live 조회 엔드포인트.
- Report:LiveKpi config(Enabled/IntervalSeconds/Source).

검증: 라이브 live_kpi 28행(7컬럼×4KPI) 15s 갱신, /live 정상. 데모 sim은 totalizer가
평평해 값 0/idle(플러밍 정상, 실데이터면 실값).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 07:52:31 +09:00
windpacer
3506a67c28 feat(report): P1b 연속집계 history_1min — 1초 버퍼 60초 롤업(장기 무손실)
온라인 히스토리안 2계층: history_1s(최근 14일, 고해상) + history_1min(장기, 60초).

- history_1min 연속집계(timescaledb.continuous): time_bucket('1 min') + last(value)/last(controller_id).
  refresh 정책(start 3h, end 10min, 5분마다) → 집계 lag ≪ 보존윈도(14일)이라 1초 raw evict 전 materialize.
- Hc900FastHistoryService.EnsureSchemaAsync에 cagg 생성+정책 멱등 추가. SQL 파일 동기화.

검증: 2계층 값 일치(1s last == 1min cagg), 정책 활성, 무손실 불변식 충족.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 07:13:12 +09:00
windpacer
7f67f0e54d feat(report): P1a 1초 링버퍼 히스토리안 (history_1s)
온라인 히스토리안 1계층 — 디스크 상한 고정 1초 버퍼.

- history_1s Timescale 하이퍼테이블(1h 청크) + 압축(6h) + 보존 14일(청크DROP, 디스크 상한 고정).
- Hc900FastHistoryService: 매 1초 realtime_table 큐레이션 태그 → history_1s append.
  기동 시 스키마/정책 멱등 생성. Hc900HistoryService(60s) 패턴.
- ReportColumnMap.HistorianTags(): 메트릭/마스크가 읽는 태그를 기존 config에서 유도(~105개,
  중복정의 없음) — 유량 .PV/.QV, 진공, 민감단 TC, 하부루프 .PV/.SP/.OP.
- Report:Historian config(Enabled/IntervalSeconds/RetentionDays).

검증: 라이브 초당 ~38행 append, 샘플 간격 ~1.004s, 보존/압축 정책 활성.
주의(데모): realtime 폴링이 areas 61/62/81만 커버 → 1s 버퍼도 해당 컬럼만(배포 시 전 컨트롤러).
1s 버퍼 주 가치는 .PV/.OP 동특성; .QV 적산은 60s로도 정확.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 04:56:15 +09:00
windpacer
e2b1b8f6e0 docs(report): P1 온라인 히스토리안 스펙 — 1초 링버퍼·연속집계·실시간 KPI/알람
P0 결정론 메트릭(마스크·적산·리셋)의 온라인 확장 스펙. 핵심: 로직이 causal이라
온라인 결과(T) ≡ 배치(시작~T), 로직 변경 없이 스트리밍 가능.

- A. history_1s 1초 링버퍼: Timescale 하이퍼테이블 + 보존정책(14일, 청크DROP) + 압축.
  디스크 = 태그수×윈도×행크기로 상한 고정. 큐레이션 태그(~200) 1초 append.
- B. history_1min 연속집계: evict 전 롤업(집계lag ≪ 보존윈도) → 무손실 장기보존. 2계층.
- C. Hc900LiveKpiService 온라인 누적기: 러닝 상태로 P0와 동일 마스크/양증분/리셋,
  live_kpi 적재, 인과성 동치검증, 버퍼 리플레이 복구.
- D. kpi_alert 실시간 알람: cleaning/drawdown 진입·폐합 베이스라인 이탈(계량드리프트/이송)·생산정지.

큐레이션 태그셋·P0 연계(source 추가)·단계(P1a~d)·수용기준 포함. MVP 문서에 링크.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 18:40:33 +09:00
windpacer
c64bf08aa5 feat(report): cleaning/drawdown 마스크 + 폐합 7컬럼 — 적산 메트릭 비정상운전 제외
적산(.QV) 메트릭이 비정상 운전 분을 포함해 풀기간/월간 집계가 틀어지던 문제 해결.

- 3신호 마스크: (진공 PICA-*.PV>300) OR (제품 FICQ-*18.PV<10) OR (원료 FICQ-*01.PV<10).
  = cleaning(진공깨짐/제품~0) + drawdown(feed~0, 인벤토리 인출/컬럼간 이송) 제외.
- QvDeltaAsync 재작성: 비정상분 제외 후 정상구간 양(+)증분만 합산(cap 5e4).
  리셋 3종 자동처리(999999 wrap·cleaning 리셋·운전조건변경 리셋). excluded_min 메타.
- appsettings Report:Cleaning(컬럼별 진공태그+임계) + Closure 7컬럼 전체 등록.

검증(풀기간 폐합): 마스크 후 6/7 컬럼 99~100% (C-6211 216.8→100.0, C-9111 133.2→99.9,
C-9211 97.8→99.9, C-10211 121.9→99.1). C-10111 91.4%는 C-9111↔C-10111 연결라인
시운전 중 홀드업 축적(정상 비정상상태, KPI가 표시). 라이브: C-9111 일일(04-10) 99.46%,
C-6111 일일(05-15) 99.04% 확인.

현장사실: C-9111↔C-10111 잇는 라인 추가·시운전 중 — C-9111 drawdown 시 인출물이
C-10111 feed로 유입(FICQ-10101 0→~1090, 진공 750→45). 개별 수지 불성립 주원인.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 18:04:13 +09:00
windpacer
30a3286d35 feat(report): 적산(.QV) 정확 메트릭 + 물질수지 폐합 + 웹 대시보드
PV-중앙값 근사 대신 유량 적산(.QV)으로 정확한 적분 메트릭 추가, 웹에서 바로 보기.

- QV 메트릭 4종: production_total·yield_qv·energy_intensity_qv·mass_balance_closure.
  적산 Δ 헬퍼(QvDeltaAsync)는 값 급감(reset/wrap)으로 구간분할 후 구간별 (마지막-처음)
  합산 → gap·양자화 강건, DCS 일일적산과 동일 의미(노이즈 과대계상 없음).
- 매핑은 appsettings SteamAdvisor:Columns 재사용(.QV 치환, 7컬럼). 폐합 스트림은
  Report:Closure:C-6111(Feed + Outputs[제품·경비물·중비물]).
- 웹 대시보드: GET /api/report/columns, /api/report/summary →
  리포트 탭에서 컬럼·날짜 선택 시 물질수지 신뢰블록(IN/OUT/폐합%, 98~101% 색상)
  + 메트릭 표를 즉시 렌더. 엑셀 export와 병존.
- 물리적 타당성 게이트: 수율>1.5·에너지원단위>5·≤0 → no_data+사유(garbage 차단).

검증(2026-05-15 C-6111): 생산 8455kg·수율 0.8739·에너지 0.7791·폐합 99.04%
(feed 9675/out 9582), 17주 폐합 99.1~99.8%. C-9111 garbage 수율 → no_data 처리 확인.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 07:54:34 +09:00
windpacer
37a464ab96 feat(report): P0 셀프서비스 결정론 리포트 — 메트릭 3종·엑셀 토큰 채움·fastRecord 소스
운전원이 올린 엑셀 템플릿의 {{ metric=...; column=... }} 토큰을, 선택한 날짜의
결정론 계산값으로 채워 다운로드하는 셀프서비스 리포트 레이어(P0).

- 메트릭 3종: energy_efficiency·yield·control_residual (분 버킷 피벗, 클린필터,
  KST→UTC 경계). history_table(60s)·fast_record(s/min) 동일 long 포맷이라 source만 교체.
- 컬럼→태그 매핑은 기존 appsettings SteamAdvisor:Columns 재사용(7컬럼 무료).
- EPPlus 토큰 치환 + 셀 주석에 해상도 메타(source/sampling/n/keep) 부착.
- report_template/report_run 테이블 + cells_json 감사 박제.
- 리포트 탭(웹 UI) + 샘플 템플릿.

검증: 빌드 0에러. E2E 라운드트립 실동작(2026-05-15 C-6111 효율 0.778·수율 0.872·
잔차 mean 0.746 — 오프라인 검증값과 일치). 결정론 검증 게이트 준수(표본0=N/A, 0 날조 금지).

⚠️ EPPlus는 NonCommercial 컨텍스트 — 상용 출시 전 라이선스 정리 필요.

설계: docs/작업플랜-셀프서비스-분석리포트-MVP-P0-상세설계.md

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 00:14:35 +09:00
windpacer
aa2174118a docs: 작업지시서(PascalCase 통일)·AI운전원 제어 논의메모·HC900 IO 데이터시트 추가
- 작업지시서-API-필드-PascalCase-통일.md: PascalCase 통일 작업 지시서.
- 논의-AI운전원-제어-아이디어.md: AI 운전원 제어 아이디어 논의 메모.
- ControlEdge HC900 IO Modules Specifications.pdf: RTD/AI 모듈 스펙(레인지·정확도)
  데이터시트 — PT100 양자화 분석 근거 자료.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 12:33:32 +09:00
windpacer
7124b222c3 chore: gateway-config에 HCX 컨트롤러 항목 추가 + plant_context sub_area 도구호출 설명 정리
- gateway-config.json: HCX 컨트롤러 슬롯(grpcPort 50055) 추가.
- plant_context.md: active_alarms/find_tags/query_events 등 sub_area 인자 전달
  방식 설명을 prose로 정리.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 12:31:24 +09:00
windpacer
f97be981a4 style(api): JSON 응답 필드 PascalCase 통일 (컨트롤러·DTO·JS)
C# 익명객체 응답 속성을 camelCase→PascalCase로 통일(key→Key, success→Success,
error→Error 등)하고, 프런트 JS의 응답 접근도 맞춰 변경(ramp.jobs→ramp.Jobs,
data.columns→data.Columns 등). Kb/Pid/Steam/Feedforward/Ollama/PointBuilder 등
전 컨트롤러와 대응 JS, ExperionDtos·TrendDtos, McpService 반영.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 12:31:14 +09:00
windpacer
cfef06c21e fix(nl2sql): query_with_nl 프롬프트 단일화 + 극값 CTE(전체반환·앵커) + 결정론 태그 검증 게이트
- server.py: inline _DB_SCHEMA 제거, worker/sql_prompt.SQL_SYSTEM_PROMPT로 단일화.
  query_pv_history/query_with_nl description 보강(집계·극값은 query_with_nl 유도).
- 결정론 태그 검증 게이트(_verify_sql_tags)+피드백 재시도(최대3회): 환각 태그는
  실행 거부·교정, 끝내 미존재면 빈 결과 대신 명시적 에러(날조 차단). 대소문자 교정.
- sql_prompt.py: 극값→관련태그 CTE를 'value=MAX(...) 모든 시각 반환 + max_occurrences
  + 앵커(극값기준 태그) 포함'으로 교체. 태그 대문자 규칙.
- 피벗 시 max_occurrences 등 스칼라 컬럼 보존.
- curate/golden: 극값 CTE(전체반환·앵커≠요청) 학습예제·eval 추가.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 12:22:31 +09:00
windpacer
fc197465d4 docs: PT100 RTD 온도값 양자화 원인분석 — RTD 레인지(high/low) 설정 문제
TI-6111C/TI-9211C가 소수점 5자리까지 반복되는 현상의 원인 규명.
field_hist 원본 대조로 데이터·이관·OPC·하드웨어 기각, P2(low+F0) 대조군
실측으로 RTD 레인지 설정(high 1000°C스팬 vs low 500°C스팬)이 분해능·정확도
2배 차이의 단독 원인임을 확정. 해법=레인지를 low로 변경.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 12:21:31 +09:00
windpacer
dbad4a54cf fix: T2S vLLM URL, first()/last() compatibility, PascalCase unification, event date parsing
- Fix vLLM base URL from port 8001 to 8000 (server.py)
- Replace TimeScaleDB first()/last() with array_agg(ORDER BY) for standard PostgreSQL compatibility (TextToSqlService.cs)
- Unify all backend response fields to PascalCase (TextToSqlController.cs)
- Update frontend to match PascalCase response fields (t2s.js)
- Change T2S date inputs to calendar popup (same as hist.html) (t2s.html)
- Fix evt.js date parsing: empty string causes Invalid Date error (evt.js)
2026-06-11 06:11:28 +09:00
windpacer
f1c11931fe feat: learned-control 브랜치 main 머지
Point Builder, SteamAdvisor, Feedforward 제어, MCP RAG/NL2SQL,
multi-controller 지원 등 28커밋 분량 기능 통합.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-11 04:41:57 +09:00
windpacer
cb749568d0 chore: 완료된 작업지시서·계획 문서 삭제
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-11 04:41:40 +09:00
windpacer
0057bd3a44 fix(ui): history query - match PascalCase JSON property names
API 응답이 PascalCase(RecordedAt, Values)인데 프론트엔드가
camelCase(recordedAt, values)로 접근해 데이터 미표시 현상 수정.
- hist.js: recordedAt→RecordedAt, values→Values
- trend.js: consistency fix
- index.html: cache busting 버전 갱신 (20260604→20260611)
2026-06-11 04:39:19 +09:00
windpacer
8c7b25e667 feat: Point Builder 실시간 추가/제거(선택분만) + 미리보기 닫기
- 전체 태그 목록 체크박스 + 버튼을 realtime_table 기준으로 전환
  · + 실시간 추가: 선택 태그만 is_active+realtime_enabled 켜고 realtime_table 직접 등록(즉시 라이브)
  · - 실시간 제거: realtime_enabled 끄고 realtime_table 행 직접 삭제
  · full-Sync 미사용(전체 추가 사고 방지) → 선택 id만 insert/delete
  · 신규 API: POST /api/pointbuilder/realtime-add, realtime-remove
  · 레거시 pbBulkActivate(is_active만 토글) 제거
- 미리보기 ✕ 닫기 버튼 추가(설정 화면 복귀)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 21:27:47 +09:00
windpacer
56df27c85f refactor: Point Builder 행별 is_active 램프 제거 + 등록/상태 컬럼 확장
- 🟢/🔴 is_active 토글 램프 삭제 (등록/상태 색상과 중복, is_active 전역 TRUE라 무의미)
- 미사용 pbToggleOne 함수 제거
- 등록/상태 75→120px(긴 라벨 대응), 관리 110→80px(삭제만 남음)
- 개별 활성/비활성은 상단 일괄 버튼으로 일원화

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 19:40:38 +09:00
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
windpacer
0a7ba93cc8 docs: 진공압력 끓는점 계산법(Antoine 식·상수 A/B/C) 문서 추가
- Antoine 식 log10P=A-B/(C+T)와 A/B/C 의미·단위 주의점 정리
- MSDS만으로 가능한 Clausius–Clapeyron 2-점 근사 방법
- PMA/PGMEA/EL 물성 발췌 + Antoine 상수 자리표시자(외부 DB 필요)
- 온도프로파일 기준밴드와의 교차검증 활용 연결

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 17:01:13 +09:00
windpacer
18963455f2 feat: Steam 온도프로파일 제품명 사용자정의 + 자동매칭 폐지
- 제품 라벨(P0/P1)에 MSDS 화학물질명(PMA/PGMEA/EL) 매핑 레이어 추가
  (product_labels.json, 플랜트=컬럼별). 통계 기준밴드는 데이터 산출 유지
- 운전원용 ⚙제품명 설정 모달: GET/POST /api/steam/productlabels/{col}
- 온도기반 자동매칭 폐지 → 운전원이 고른 제품만 기준밴드로 비교, 미선택 시작
- 드롭다운: 탭 진입/컬럼변경 시 즉시 채움, 선택은 플랜트별 localStorage 유지
- 중복 '제품(수동)' 배지 제거(헤더 줄바꿈 해소)
- 설계·검수 문서 추가

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 16:56:50 +09:00