Files
HC900-Crawler/docs/진단-스팀Advisory-라이브차트.md
windpacer 7409fabc58 컬럼명칭 통일 C-xxxxx + SIGPIPE 대응 + SteamAdvisor/FF 개선
=== 컬럼명칭 통일 (c{prefix} → C-{prefix}11) ===
Python 분석스크립트: data pkl 경로  →
gen_temp_profiles: tempref 파일명  →
SteamAdvisorController: TagsFor() 숫자서픽스 → 풀컬럼키(C-6111), ToSuffix() 변환
steam.js: ST_TEMP_COLS ['61',...] → ['C-6111',...], selectbox defaultColumn
appsettings.json: Columns 키 c61/c62/... → C-6111/C-6211/..., DefaultColumn c6111→C-6111
run_column.py: 추출/분석시 col_key = f"C-{{prefix}}11"
C-{x}11_{model,tempref}.json: 신규 명칭 기준 기준프로파일/모델 7컬럼분

=== SteamAdvisor 수정 ===
SteamModel: [JsonPropertyName] 매핑(snake_case → PascalCase 역직렬화)
예외처리: LinearCoeffs.Count < 3 방어코드
steam.js: catch(_) {} → 에러메시지 표시, missing_tags 응답처리

=== Feedforward Controller 개선 ===
ff.js: 상승/하강 양방향 램프 confirm, 방향뱃지(↑↓), Normal 모드 표시
FeedforwardController: 업램프 단독제한 제거(양방향), tcReturnTcTarget/Band 노출

=== DB ===
Hc900DbContext: realtime_table_tagname_key 레거시 UNIQUE 제약/인덱스 DROP 로직
Hc900Controllers: ToDictionaryAsync → GroupBy 변환 (중복 tagname 대응)

=== SIGPIPE 대응 ===
gateway.cpp: signal(SIGPIPE, SIG_IGN) 메인스레드 설치
modbus_tcp.cpp: send() flags 0 → MSG_NOSIGNAL (EPIPE 복구)
sigpipe_ignore.c: LD_PRELOAD 우회 공유라이브러리
Hc900GatewayProcessService: LD_PRELOAD 환경변수 설정
2026-06-07 00:29:47 +09:00

9.9 KiB

진단 — Steam Advisory 라이브 차트 데이터 불로딩 (2026-06-06)

증상

Steam Advisory 탭 → "라이브" 패널에서 "조회" 버튼 클릭 후 차트와 입력값이 표시되지 않음.

진단 결과

🔴 HIGH — ToDictionaryAsync 중복 tagname ArgumentException

문제: SteamAdvisorController.LiveTempProfile API에서 _ctx.RealtimePoints.Where(...).ToDictionaryAsync(r => r.TagName, ...) 사용. realtime_table의 UNIQUE 제약이 (controller_id, tagname)이므로 같은 tagname이 여러 controller_id로 존재할 수 있음. ToDictionaryAsync는 중복 키에서 ArgumentException을 던짐.

근거:

  • SteamAdvisorController.cs:133-135ToDictionaryAsync(r => r.TagName, r => r.LiveValue)
  • SteamAdvisorController.cs:194-196 — 동일 패턴
  • Hc900Controllers.cs:279-281 — 동일 패턴
  • Hc900DbContext.cs:488-498 — UNIQUE 제약이 (controller_id, tagname)

영향: API 호출 시 ArgumentException 발생 → 500 Internal Server Error → JS catch (_) {}로 삼켜짐 → 사용자에게 아무 표시도 없음.

수정: GroupBy(r => r.TagName).ToDictionaryAsync(g => g.Key, g => g.OrderByDescending(r => r.Timestamp).FirstOrDefault()?.LiveValue)로 변경. 최신 타임스탬프의 값을 사용.

🟠 MED — JS catch (_) {}로 예외 삼킴

문제: stLiveTickcatch (_) {}가 API 에러를 완전히 무시. missing_tags 응답도 stUpdateLive에 전달되어 d.recOpundefined인 상태로 UI 갱신 시도.

근거: steam.js:163-169catch (_) {}

영향: API 에러가 발생해도 사용자에게 표시되지 않음. 디버깅 불가.

수정: catch (e)로 변경 후 st-live-msg에 에러 메시지 표시. missing_tags 응답은 별도 처리.

🟡 LOW — register-map에 없는 태그 존재

문제: appsettings.json의 SteamAdvisor 태그 중 9차, 9-2차, 10-1차, 10-2차 관련 태그가 register-map.json에 없음.

근거: appsettings.json:76-80 — 9차/10차 태그 정의. register-map.json에 해당 태그 없음.

영향: 해당 컬럼 선택 시 missing_tags 응답. 6차(C3)는 정상 동작.

수정: register-map에 태그 추가 또는 appsettings에서 제거.

교차 검증

질문 확인 방법 결과
Q1. 이미 수정된 문제인가? 파일 현재 상태 확인 수정되지 않음
Q2. 다른 레이어에서 처리되고 있는가? 호출 계층 확인 JS catch (_) {}로 삼킴
Q3. 의도적 설계인가? 문서·주석 확인 의도적 아님
Q4. 실제 장애 시나리오가 있는가? 재현 경로 구체화 같은 tagname이 여러 controller_id로 존재하면 500 Error

수정 내역

파일 변경 내용
SteamAdvisorController.cs:133-136 ToDictionaryAsyncGroupBy + FirstOrDefault
SteamAdvisorController.cs:194-197 동일 패턴 수정
Hc900Controllers.cs:279-282 동일 패턴 수정
steam.js:163-176 catch (_) {} → 에러 표시 + missing_tags 처리

재평가 (2026-06-06, 코드·DB 데이터 검증 후)

진단을 데이터로 검증한 결과, #1의 "현재 장애 원인" 진술은 부정확하나, 위험의 본질은 설계상 유효함. 정정·심화:

#1 재평가 — "현재 ArgumentException 발생"은 거짓, 그러나 위험은 실재

  • 현 데이터: realtime_tabletagname 단독 UNIQUE 인덱스(realtime_table_tagname_key)가 존재 → tagname 전역 유일 → 현재 중복 0행 → ToDictionaryAsync 지금은 안 터짐. 즉 현재 불로딩의 원인이 아님.
  • 그러나(핵심): HC900은 컨트롤러 간 peer 통신으로 동일 태그를 미러링함. 설계상 같은 tagname이 여러 controller_id로 존재 가능하며, 올바른 제약은 UNIQUE(controller_id, tagname)(메모리·idx_realtime_table_ctrl_tag_unique와 일치).
  • 진짜 버그: 현재 공존하는 tagname 단독 UNIQUE 제약이 설계 위반. C4 online 시 peer 미러 태그의 두 번째 INSERT/upsert를 막아 적재 자체가 깨짐.
  • 결론: ToDictionaryAsync → GroupBy 수정은 단독 UNIQUE 제거를 전제로 방어적으로 정당. 단 "현재 500 에러 발생"은 아님(잠재 위험).

추가로 필요한 근본 수정 (진단보다 한 겹 깊음)

  1. realtime_table_tagname_key(tagname 단독 UNIQUE) 제거 — 멀티컨트롤러 peer 적재 정합. (controller_id, tagname)만 유지.
  2. Live/TempProfile 조회를 controller_id로 한정 — 컬럼→컨트롤러 매핑(6차=C3 등)으로 필터. tagname만으로 조회하면 peer 미러된 동명 태그의 엉뚱한 컨트롤러 값이 섞일 수 있음(GroupBy로 예외는 막아도 어느 컨트롤러 값인지 모호).

실제 현재 불로딩 원인

  • 6차(C-6111) 필수 태그(FICQ-6101.PV·FICQ-6118.PV·TI-6111C)는 realtime에 모두 존재(C3) → 6차 선택 시 정상 동작해야 함.
  • 표면 트리거 = 기본 선택 컬럼이 configured 알파벳순 첫(C-10111=10차) → 조회 시 C4 태그 부재 → missing_tagscatch(_){}(#2)로 삼켜져 무표시.
  • 해법: DefaultColumn=C-6111로 변경 + UI stLoadColumns에서 기본 선택을 C-6111으로 강제(작업플랜 4-3) + missing_tags 사용자 표시(#2 수정으로 반영됨). #3(9·10차 태그 부재)은 사실.

종합

항목 진단 주장 재평가
#1 ToDictionaryAsync HIGH·현재 500 현재는 미발생(tagname 단독 UNIQUE)이나 peer 설계상 잠재 실재 → GroupBy 정당 + 단독 UNIQUE 제거·controller_id 한정 조회가 근본
#2 catch 삼킴 MED 타당(표면 원인을 가림)
#3 9·10차 태그 부재 LOW 타당 + 기본 컬럼이 10차라 첫 화면 불로딩 트리거

추가 진단 — 컬럼명칭 혼재 문제 (2026-06-06)

🟠 MED — c6111/c61 중복 + 3가지 네이밍 체계 혼재

문제: appsettings.json에서 c6111c61이 같은 태그 매핑을 가리킴. ListModels API는 둘 다 반환 → UI에서 중복 선택 가능. 또한 컬럼키(c6111), 파일prefix(c6111/c61), TempProfile route param(61)이 각각 다른 규칙 사용.

근거: appsettings.json:73-74c6111c61 중복. SteamAdvisorController.cs:184c{col}_tempref.json (col = numeric suffix). steam.js:74ST_TEMP_COLS가 numeric suffix 사용.

영향: UI 컬럼 선택에서 중복, TempProfile API 호출 시 컬럼키 ↔ numeric suffix 변환 누락 가능성.

해결: 작업플랜-스팀컬럼명칭통일.md 참조. 컬럼키·파일prefix 모두 C-6111 형식으로 통일.

🟡 LOW — TagsFor numeric suffix 불일치

문제: TagsFor"61", "62" 등 2자리 numeric suffix를 기대하지만, 컬럼명칭 통일 후 "6111", "6211" 등 4자리로 변경됨. 템플릿 $"TICA-{p}11A.PV"$"TICA-{p}A.PV"로 변경 필요.

근거: SteamAdvisorController.cs:249-268TagsFor 메서드.

영향: 컬럼명칭 통일 후 수정하지 않으면 태그명 생성 실패 → missing_tags 응답.

해결: 작업플랜-스팀컬럼명칭통일.md 작업 2-5 참조.


통일 작업플랜 보완 진단 (2026-06-06)

작업플랜-스팀컬럼명칭통일.md에 명칭 통일 방향은 타당하나, 원래 증상(라이브 불로딩) 해결과 Python 영향 범위가 과소평가됨.

🔴 누락 A — 통일해도 라이브 불로딩 재현 (UI 기본선택 강제 누락)

  • 통일 후에도 OrderBy(x) 알파벳순 첫은 C-10111(1 < 6) → st-col 첫 옵션 자동선택 = 데이터 없는 10차 → missing_tags불로딩 그대로 재현.
  • DefaultColumn=C-6111(작업1)은 Live API의 col 누락 시 fallback일 뿐, UI select 기본선택과 별개(steam.js stLoadColumns가 첫 옵션 선택).
  • 해결: stLoadColumns에서 기본 선택을 C-6111으로 강제 (작업플랜 4-3).

🟠 누락 B — Python c{prefix} 접두 패턴 (작업5 과소평가)

  • run_column.py:51f"c{prefix}_data.pkl", gen_temp_profiles.py:38f"c{prefix}_data.pkl", gen_temp_profiles.py:71f"c{prefix}", gen_temp_profiles.py:75f"c{prefix}_tempref.json".
  • prefix=C-6111이면 "cC-6111_data.pkl"깨짐.
  • 해결: 스크립트 내부 c{prefix}{prefix} 패턴 변경 + gen_temp_profiles.py:71"column": f"c{prefix}""column": prefix.

추가 진단 — 컬럼명칭 혼재 문제 (2026-06-06)

🟠 MED — c6111/c61 중복 + 3가지 네이밍 체계 혼재

문제: appsettings.json에서 c6111c61이 같은 태그 매핑을 가리킴. ListModels API는 둘 다 반환 → UI에서 중복 선택 가능. 또한 컬럼키(c6111), 파일prefix(c6111/c61), TempProfile route param(61)이 각각 다른 규칙 사용.

근거: appsettings.json:73-74c6111c61 중복. SteamAdvisorController.cs:184c{col}_tempref.json (col = numeric suffix). steam.js:74ST_TEMP_COLS가 numeric suffix 사용.

영향: UI 컬럼 선택에서 중복, TempProfile API 호출 시 컬럼키 ↔ numeric suffix 변환 누락 가능성.

해결: 작업플랜-스팀컬럼명칭통일.md 참조. 컬럼키·파일prefix 모두 C-6111 형식으로 통일.

🟡 LOW — TagsFor numeric suffix 불일치

문제: TagsFor"61", "62" 등 2자리 numeric suffix를 기대하지만, 컬럼명칭 통일 후 "6111", "6211" 등 4자리로 변경됨. 템플릿 $"TICA-{p}11A.PV"$"TICA-{p}A.PV"로 변경 필요.

근거: SteamAdvisorController.cs:249-268TagsFor 메서드.

영향: 컬럼명칭 통일 후 수정하지 않으면 태그명 생성 실패 → missing_tags 응답.

해결: 작업플랜-스팀컬럼명칭통일.md 작업 2-5 참조.