=== 컬럼명칭 통일 (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 환경변수 설정
81 lines
2.8 KiB
Python
81 lines
2.8 KiB
Python
"""
|
|
모델 JSON export → C# SteamAdvisor에서 로드.
|
|
|
|
선형근사(1안): GBM 대신 LinearRegression 계수 export.
|
|
steam = w0 + w1*feed + w2*product + w3*T_C
|
|
valve_inv(flow) = poly3 → OP
|
|
|
|
사용법:
|
|
python3 c6111_export_model.py --data c6111_data.pkl --prefix c6111
|
|
"""
|
|
import argparse
|
|
import json
|
|
import numpy as np
|
|
import pandas as pd
|
|
from sklearn.linear_model import LinearRegression
|
|
|
|
BASE = "/home/windpacer/projects/hc900_ax/scripts/analysis/"
|
|
FEATURES = ["feed", "product", "T_C"]
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("--data", default=BASE + "C-6111_data.pkl")
|
|
parser.add_argument("--prefix", default="C-6111")
|
|
parser.add_argument("--output", help="JSON 출력 경로 (기본: scripts/analysis/{prefix}_model.json)")
|
|
args = parser.parse_args()
|
|
df = pd.read_pickle(args.data)
|
|
|
|
prod = df[df["mode"] == "PROD"].copy()
|
|
prod = prod[(prod["feed"] > 50) & (prod["steam_flow"] > 10) & (prod["steam_op"] > 1)]
|
|
prod = prod.dropna(subset=FEATURES + ["steam_op", "steam_flow"])
|
|
ops = (prod.set_index("dtat").resample("6h").median(numeric_only=True)
|
|
.dropna(subset=["steam_flow", "feed"]))
|
|
ops = ops[ops["feed"] > 50]
|
|
|
|
# 선형 모델
|
|
lr = LinearRegression()
|
|
lr.fit(ops[FEATURES].values, ops["steam_flow"].values)
|
|
r2 = lr.score(ops[FEATURES].values, ops["steam_flow"].values)
|
|
print(f"선형 steam_flow R² = {r2:.4f} (GBM 대비 비교용)")
|
|
|
|
# 밸브 역특성: steam_flow → steam_op (3차)
|
|
vp = np.polyfit(prod["steam_flow"], prod["steam_op"], 3)
|
|
|
|
# Envelope (1%, 99%)
|
|
lo = ops[FEATURES].quantile(0.01)
|
|
hi = ops[FEATURES].quantile(0.99)
|
|
|
|
# GBM feature importance (참고용)
|
|
try:
|
|
from sklearn.ensemble import GradientBoostingRegressor
|
|
gbm = GradientBoostingRegressor(n_estimators=200, max_depth=2,
|
|
learning_rate=0.05, random_state=0)
|
|
gbm.fit(ops[FEATURES].values, ops["steam_flow"].values)
|
|
gbm_r2 = gbm.score(ops[FEATURES].values, ops["steam_flow"].values)
|
|
except Exception:
|
|
gbm_r2 = None
|
|
|
|
model = {
|
|
"column": args.prefix,
|
|
"features": FEATURES,
|
|
"linear_coeffs": lr.coef_.tolist(),
|
|
"intercept": lr.intercept_,
|
|
"linear_r2": round(r2, 4),
|
|
"gbm_r2": round(gbm_r2, 4) if gbm_r2 else None,
|
|
"valve_poly": vp.tolist(),
|
|
"envelope_lo": {c: round(float(lo[c]), 1) for c in FEATURES},
|
|
"envelope_hi": {c: round(float(hi[c]), 1) for c in FEATURES},
|
|
"n_operating_points": len(ops),
|
|
"n_prod_rows": len(prod),
|
|
}
|
|
out = args.output or (BASE + f"{args.prefix}_model.json")
|
|
with open(out, "w") as f:
|
|
json.dump(model, f, indent=2)
|
|
print(f"\n모델 export: {out}")
|
|
print(json.dumps(model, indent=2))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|