=== 컬럼명칭 통일 (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 환경변수 설정
13 KiB
13 KiB
작업플랜 — SteamAdvisor 컬럼명칭 통일 (2026-06-06)
목표
appsettings.json에서c6111/c61중복 제거- 컬럼키·파일prefix 모두
C-6111,C-6211,C-8111,C-9111,C-9211,C-10111,C-10211으로 통일 - 컬럼키 = 파일prefix → 매핑 헬퍼 불필요
통일 매핑
| 컬럼키 (UI/API) | 파일 prefix | 설명 |
|---|---|---|
C-6111 |
C-6111 |
6-1차 |
C-6211 |
C-6211 |
6-2차 |
C-8111 |
C-8111 |
8차 |
C-9111 |
C-9111 |
9-1차 |
C-9211 |
C-9211 |
9-2차 |
C-10111 |
C-10111 |
10-1차 |
C-10211 |
C-10211 |
10-2차 |
TagsFor numeric suffix: 컬럼키에서 "C-" prefix 제거 → "6111", "6211", "8111" 등
작업 0 — 파일 리네임 [선행 필수]
scripts/analysis/ 내 데이터 파일 일괄 리네임:
cd scripts/analysis
# Model files
mv c6111_model.json C-6111_model.json
mv c61_model.json C-6111_model.json.bak # 중복, 제거
mv c62_model.json C-6211_model.json
mv c81_model.json C-8111_model.json
mv c91_model.json C-9111_model.json
mv c92_model.json C-9211_model.json
mv c101_model.json C-10111_model.json
mv c102_model.json C-10211_model.json
# Tempref files
mv c61_tempref.json C-6111_tempref.json
mv c62_tempref.json C-6211_tempref.json
mv c81_tempref.json C-8111_tempref.json
mv c91_tempref.json C-9111_tempref.json
mv c92_tempref.json C-9211_tempref.json
mv c101_tempref.json C-10111_tempref.json
mv c102_tempref.json C-10211_tempref.json
# Plotdata files
mv c6111_plotdata.json C-6111_plotdata.json
mv c61_plotdata.json C-6111_plotdata.json.bak # 중복, 제거
mv c62_plotdata.json C-6211_plotdata.json
mv c81_plotdata.json C-8111_plotdata.json
mv c91_plotdata.json C-9111_plotdata.json
mv c92_plotdata.json C-9211_plotdata.json
mv c101_plotdata.json C-10111_plotdata.json
mv c102_plotdata.json C-10211_plotdata.json
동시 수정: 각 JSON 파일 내부의 "column" / "prefix" 필드 값도 C-6111 등으로 변경.
# JSON 내부 필드 수정 (python one-liner)
for f in C-*_model.json; do
python3 -c "
import json, sys
d = json.load(open('$f'))
d['column'] = '$f'.replace('_model.json','')
json.dump(d, open('$f','w'), indent=2)
"
done
for f in C-*_plotdata.json; do
python3 -c "
import json, sys
d = json.load(open('$f'))
d['prefix'] = '$f'.replace('_plotdata.json','')
json.dump(d, open('$f','w'), indent=2)
"
done
for f in C-*_tempref.json; do
python3 -c "
import json, sys
d = json.load(open('$f'))
d['column'] = '$f'.replace('_tempref.json','')
json.dump(d, open('$f','w'), indent=2)
"
done
작업 1 — appsettings.json 수정
"SteamAdvisor": {
"ModelPath": "/home/windpacer/projects/hc900_ax/scripts/analysis/C-6111_model.json",
"PlotDataDir": "/home/windpacer/projects/hc900_ax/scripts/analysis",
"ModelDir": "/home/windpacer/projects/hc900_ax/scripts/analysis",
"DefaultColumn": "C-6111",
"Columns": {
"C-6111": { "Feed": "FICQ-6101.PV", "Product": "FICQ-6118.PV", "TC": "TI-6111C", "SteamOp": "TICA-6111A.OP", "SteamFlow": "FIQ-6115" },
"C-6211": { "Feed": "FICQ-6201.PV", "Product": "FICQ-6218.PV", "TC": "TI-6211C", "SteamOp": "TICA-6211A.OP", "SteamFlow": "FIQ-6215" },
"C-8111": { "Feed": "FICQ-8101.PV", "Product": "FICQ-8118.PV", "TC": "TI-8111C", "SteamOp": "TICA-8111A.OP", "SteamFlow": "FIQ-8115" },
"C-9111": { "Feed": "FICQ-9101.PV", "Product": "FICQ-9118.PV", "TC": "TI-9111C", "SteamOp": "TICA-9111A.OP", "SteamFlow": "FIQ-9115" },
"C-9211": { "Feed": "FICQ-9201.PV", "Product": "FICQ-9218.PV", "TC": "TI-9211C", "SteamOp": "TICA-9211A.OP", "SteamFlow": "FIQ-9215" },
"C-10111": { "Feed": "FICQ-10101.PV", "Product": "FICQ-10118.PV", "TC": "TI-10111C", "SteamOp": "TICA-10111A.OP", "SteamFlow": "FIQ-10115" },
"C-10211": { "Feed": "FICQ-10201.PV", "Product": "FICQ-10218.PV", "TC": "TI-10211C", "SteamOp": "TICA-10211A.OP", "SteamFlow": "FIQ-10215" }
}
}
작업 2 — SteamAdvisorController.cs 수정
2-1. 헬퍼 메서드 추가
// 컬럼키 "C-6111" → 파일prefix "C-6111" (동일)
// 컬럼키 "C-6111" → TagsFor numeric suffix "6111"
private static string ToSuffix(string col) => col.StartsWith("C-") ? col[2..] : col;
2-2. ListModels — 파일명에서 컬럼키 추출 (변경 불필요)
*_model.json 파일명의 _model.json 제거 → C-6111 반환. 컬럼키 = 파일prefix이므로 추가 변환 불필요.
// 변경 전
.Select(n => n!.Replace("_model", "")) // "c6111"
// 변경 후 — 그대로 "C-6111" 반환
.Select(n => n!.Replace("_model", ""))
2-3. Backtest — 컬럼키 그대로 파일prefix 사용
// 변경 전
var path = Path.Combine(plotDir, $"{col}_plotdata.json");
// 변경 후 — 컬럼키 = 파일prefix
var path = Path.Combine(plotDir, $"{col}_plotdata.json"); // 동일!
2-4. TempProfile — 컬럼키 = 파일prefix, TagsFor는 numeric suffix
// 변경 전
var path = Path.Combine(dir, $"c{col}_tempref.json"); // col = "61"
var tagMap = TagsFor(col);
// 변경 후
var path = Path.Combine(dir, $"{col}_tempref.json"); // col = "C-6111"
var tagMap = TagsFor(ToSuffix(col)); // "6111"
2-5. TagsFor — numeric suffix 기반 태그명 생성
// 변경 전 — col = "61", "62", "81" 등
private static Dictionary<string, string> TagsFor(string p)
{
var m = new Dictionary<string, string>
{
["reb_temp"] = $"TICA-{p}11A.PV",
["T_B"] = $"TI-{p}11B.PV",
["T_C"] = $"TI-{p}11C.PV",
["T_D"] = $"TI-{p}11D.PV",
["vacuum"] = $"PICA-{p}11.PV",
};
switch (p) {
case "51": m["T_C"] = "TI-5111B.PV"; break;
case "81": m["reb_temp"] = "TICA-8111.PV"; m["vacuum"] = "PICA-8111A.PV"; break;
// ...
}
return m;
}
// 변경 후 — p = "6111", "6211", "8111" 등
private static Dictionary<string, string> TagsFor(string p)
{
var m = new Dictionary<string, string>
{
["reb_temp"] = $"TICA-{p}A.PV",
["T_B"] = $"TI-{p}B.PV",
["T_C"] = $"TI-{p}C.PV",
["T_D"] = $"TI-{p}D.PV",
["vacuum"] = $"PICA-{p}.PV",
};
switch (p) {
case "8111": m["reb_temp"] = "TICA-8111.PV"; m["vacuum"] = "PICA-8111A.PV"; break;
case "9111": m["vacuum"] = "PICA-9111A.PV"; break;
case "9211": m["vacuum"] = "PICA-9211A.PV"; break;
case "10111": m["vacuum"] = "PICA-10111A.PV"; break;
case "10211": m["vacuum"] = "PICA-10211A.PV"; break;
}
return m;
}
2-6. Live — 컬럼키 fallback 변경
// 변경 전
col ??= _config.GetValue<string>("SteamAdvisor:DefaultColumn") ?? "c6111";
// 변경 후
col ??= _config.GetValue<string>("SteamAdvisor:DefaultColumn") ?? "C-6111";
작업 3 — SteamAdvisor.cs 수정
// 변경 전
_modelPath = config.GetValue<string>("SteamAdvisor:ModelPath")
?? "/home/windpacer/projects/hc900_ax/scripts/analysis/c6111_model.json";
// 변경 후
_modelPath = config.GetValue<string>("SteamAdvisor:ModelPath")
?? "/home/windpacer/projects/hc900_ax/scripts/analysis/C-6111_model.json";
작업 4 — UI (steam.js) 수정
4-1. ST_TEMP_COLS — 컬럼키 사용
// 변경 전
const ST_TEMP_COLS = [['61','6-1차'],['62','6-2차'],['81','8차'],['91','9-1차'],['92','9-2차'],['101','10-1차'],['102','10-2차']];
// 변경 후
const ST_TEMP_COLS = [
['C-6111','6-1차'],['C-6211','6-2차'],['C-8111','8차'],
['C-9111','9-1차'],['C-9211','9-2차'],['C-10111','10-1차'],['C-10211','10-2차']
];
4-2. API 호출 — 컬럼키 그대로 전달
stLiveTick, stTempTick 모두 컬럼키를 API에 전달. 백엔드가 컬럼키를 직접 사용하므로 추가 변환 불필요.
4-3. stLoadColumns — 기본 선택 컬럼 강제 [★ 누락 A 해결]
알파벳순 정렬 시 C-10111이 C-6111보다 앞서 첫 옵션 자동 선택됨 → 데이터 없는 10차 → missing_tags → 불로딩 재현.
// 변경 전
[sel1, sel2].forEach(sel => {
sel.innerHTML = cols.map(c => `<option value="${c}">${c}</option>`).join('');
});
// 변경 후 — DefaultColumn(C-6111)을 기본 선택으로 강제
const defaultCol = 'C-6111'; // 또는 d.defaultColumn에서 동적 획득
[sel1, sel2].forEach(sel => {
sel.innerHTML = cols.map(c => `<option value="${c}" ${c===defaultCol?'selected':''}>${c}</option>`).join('');
});
4-3. stLoadColumns — 기본 선택 컬럼 강제 [★ 누락 A 해결]
알파벳순 정렬 시 C-10111이 C-6111보다 앞서 첫 옵션 자동 선택됨 → 데이터 없는 10차 → missing_tags → 불로딩 재현.
// 변경 전
[sel1, sel2].forEach(sel => {
sel.innerHTML = cols.map(c => `<option value="${c}">${c}</option>`).join('');
});
// 변경 후 — DefaultColumn(C-6111)을 기본 선택으로 강제
const defaultCol = 'C-6111'; // 또는 d.defaultColumn에서 동적 획득
[sel1, sel2].forEach(sel => {
sel.innerHTML = cols.map(c => `<option value="${c}" ${c===defaultCol?'selected':''}>${c}</option>`).join('');
});
작업 5 — Python 스크립트 출력 파일명 수정
Python 스크립트명 변경 불가 (- 포함 모듈 import 불가)이나, 출력 파일명은 C-6111 형식으로 변경.
수정 대상 (각 스크립트의 --prefix 기본값 + 출력 파일명):
| 스크립트 | 변경 항목 |
|---|---|
c6111_extract.py |
--data 기본값: c6111_data.pkl → C-6111_data.pkl |
c6111_prodmap.py |
--prefix 기본값: c6111 → C-6111 |
c6111_shadow.py |
--prefix 기본값: c6111 → C-6111 |
c6111_rolling.py |
--prefix 기본값: c6111 → C-6111 |
c6111_startup.py |
--prefix 기본값: c6111 → C-6111 |
c6111_shutdown.py |
--prefix 기본값: c6111 → C-6111 |
c6111_operator_assist.py |
--prefix 기본값: c6111 → C-6111 |
c6111_export_model.py |
--prefix 기본값: c6111 → C-6111 |
gen_temp_profiles.py |
--prefix 기본값: c61 → C-6111 |
export_plotdata.py |
--prefix 기본값: c6111 → C-6111 |
run_column.py |
스크립트 목록 + 데이터 파일명 |
주의: c6111_data.pkl → C-6111_data.pkl로 리네임 필요.
권장 순서
작업0 (파일 리네임) → 작업1 (appsettings) → 작업2 (Controller) → 작업3 (SteamAdvisor) → 작업4 (UI) → 작업5 (Python)
리스크
| 항목 | 수준 | 대응 |
|---|---|---|
| 파일 리네임 누락 | 중 | ls scripts/analysis/로 확인 |
TagsFor switch case 누락 |
중 | 기존 6개 case → 5개 case로 변경 (51 제거) |
Python 스크립트 --prefix 기본값 누락 |
낮 | 스크립트 11개 확인 |
검증
ls scripts/analysis/— 모든 데이터 파일이C-6111_등으로 시작dotnet build성공/api/steam/models→["C-6111", "C-6211", ...]반환 (중복 없음)/api/steam/live?col=C-6111→ 정상 응답/api/steam/tempprofile/C-6111→ 정상 응답/api/steam/backtest/C-6111→ 정상 응답- UI 컬럼 선택 → 차트 데이터 표시
재진단 보완 (2026-06-06) — 통일 작업플랜의 누락 2건
명칭 통일 방향은 타당하나, 이 작업만으로 원래 증상(라이브 불로딩)이 해결되지 않으며 Python 영향 범위가 과소평가됨.
🔴 누락 A — 통일해도 라이브 불로딩이 재현 (기본선택 강제 누락)
- 이 문서 본진단의 증상 = "라이브 차트 불로딩", 실제 원인 = UI 기본 선택 컬럼이 알파벳순 첫 = 10차(재평가 §실제 원인).
- 통일 후에도
OrderBy(x)알파벳순 첫은C-10111(1 < 6) →st-col첫 옵션 자동선택 = 데이터 없는 10차 →missing_tags→ 불로딩 그대로 재현. DefaultColumn=C-6111(작업1)은 Live API의col누락 시 fallback일 뿐, UIselect기본선택과 별개(steam.js stLoadColumns가 첫 옵션 선택).- 추가 작업 필요(작업4에 포함):
stLoadColumns에서 기본 선택을DefaultColumn(C-6111)으로 강제 +missing_tags사용자 표시. ★명칭 통일이 불로딩을 고친다는 착시 주의 — 별개 수정.
🟠 누락 B — Python c{prefix} 접두 패턴 (작업5 과소평가)
- 분석 스크립트·
gen_temp_profiles.py가 파일명을f"c{prefix}_data.pkl"처럼 코드에 접두c를 박아 생성. prefix=C-6111이면"cC-6111_data.pkl"로 깨짐. - 작업5는 "
--prefix기본값 + 출력 파일명"만 명시 — 실제론 각 스크립트 내부c{prefix}→{prefix}문자열 패턴을 모두 바꿔야 함. gen_temp_profiles.build()의 6-1 fallback(if prefix=="61")·c6111_data.pkl경로 등도 함께 수정 대상.- 작업5 보강: "출력 파일명 변경" = 스크립트 내부
c{prefix}접두 제거 + prefix 비교 리터럴("61","81"등) 갱신.
🟡 경미 (인지됨)
- 모듈명(
c6111_extract.py,-import 불가로 변경 불가) vs 출력 prefix(C-6111) 영구 불일치 — 동작엔 무해하나 혼란 잔존. 헤더 주석으로 명시 권장.
종합
| 항목 | 통일 작업플랜 | 보완 |
|---|---|---|
| 명칭 혼재(c6111/c61 중복·3규약) | ✅ 해결 | 타당 |
| 라이브 불로딩(원래 증상) | ❌ 미해결 | 작업4에 기본선택=C-6111 강제 + missing_tags 표시 추가 필수 |
| Python 파일명 | ⚠️ 과소평가 | 작업5를 내부 c{prefix}→{prefix} 패턴 변경으로 확장 |