File size: 8,277 Bytes
7f72952
db44040
7f72952
b68e62b
7f72952
 
 
 
db44040
9059a27
7f72952
9059a27
db44040
9059a27
db44040
 
 
 
 
 
 
 
9059a27
7f72952
db44040
7f72952
 
 
db44040
7f72952
db44040
 
 
 
7f72952
db44040
7f72952
 
db44040
 
7f72952
db44040
 
 
 
 
7f72952
62b92d8
db44040
 
 
71fd9dd
 
 
 
 
 
 
 
62b92d8
71fd9dd
 
 
 
db44040
7f72952
db44040
9059a27
7f72952
9059a27
db44040
9059a27
db44040
7f72952
db44040
9059a27
 
62b92d8
9059a27
 
62b92d8
9059a27
 
 
 
62b92d8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9059a27
 
62b92d8
 
9059a27
 
 
 
 
 
62b92d8
 
9059a27
 
 
 
db44040
 
 
 
 
9059a27
db44040
 
 
 
 
 
b68e62b
62b92d8
db44040
 
62b92d8
 
 
 
 
db44040
 
7f72952
db44040
9059a27
db44040
 
 
 
62b92d8
 
 
 
 
db44040
 
9059a27
 
62b92d8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
import os
import sqlite3
import datetime
import streamlit as st
import pandas as pd
from huggingface_hub import hf_hub_download
from xgboost import XGBRegressor

# --- KONFIGURASI HALAMAN ---
st.set_page_config(page_title="Sistem Monitoring BTA v2.1", page_icon="🏗️", layout="wide")

# --- KONEKSI DATABASE ---
def init_db():
    conn = sqlite3.connect('furnace_data.db', check_same_thread=False)
    c = conn.cursor()
    c.execute('''CREATE TABLE IF NOT EXISTS cycles 
                 (id INTEGER PRIMARY KEY, start_date TEXT, initial_thickness REAL, active INTEGER)''')
    c.execute('''CREATE TABLE IF NOT EXISTS daily_logs 
                 (id INTEGER PRIMARY KEY, cycle_id INTEGER, log_date TEXT, raw_temp REAL)''')
    conn.commit()
    return conn

# --- LOAD MODEL ---
HF_TOKEN = os.environ.get("HF_TOKEN")
REPO_MODEL = "Rendhaputra/BTA_predictive"
FILE_MODEL = "xgboost_bta.json"

@st.cache_resource
def load_model():
    try:
        path = hf_hub_download(repo_id=REPO_MODEL, filename=FILE_MODEL, token=HF_TOKEN)
        m = XGBRegressor()
        m.load_model(path)
        return m
    except Exception as e:
        st.error(f"Gagal muat model: {e}")
        return None

model = load_model()
db = init_db()

# --- FUNGSI HELPER ---
def get_active_cycle():
    c = db.cursor()
    c.execute("SELECT id, start_date FROM cycles WHERE active = 1 ORDER BY id DESC LIMIT 1")
    return c.fetchone()

# (Fungsi ini tetap disimpan sebagai cadangan/helper, meski logika utamanya sekarang menggunakan Pandas)
def get_rolling_30(cycle_id, current_temp):
    c = db.cursor()
    c.execute("SELECT raw_temp FROM daily_logs WHERE cycle_id = ? ORDER BY log_date DESC LIMIT 29", (cycle_id,))
    
    past_temps = []
    for row in c.fetchall():
        try:
            if row[0] is not None:
                past_temps.append(float(row[0]))
        except ValueError:
            pass
            
    all_temps = past_temps + [float(current_temp)]
    if len(all_temps) == 0:
        return 0.0
        
    return sum(all_temps) / len(all_temps)

# --- ANTARMUKA (UI) ---
st.title("🏗️ Smart Monitoring BTA Furnace (v2.1 - Data Migration Enabled)")

# SIDEBAR
with st.sidebar:
    st.header("⚙️ Pengaturan & Import")
    active_cycle = get_active_cycle()
    
    if active_cycle:
        st.success(f"Siklus Aktif: {active_cycle[1]}")
        
        # --- FITUR IMPORT CSV (Versi Diperbarui) ---
        st.markdown("---")
        st.subheader("📤 Bulk Import Data Historis")
        st.caption("Gunakan CSV dengan kolom: 'Tanggal' dan 'Bodi Tengah (°C)'")
        uploaded_file = st.file_uploader("Pilih file CSV", type="csv")
        
        if uploaded_file is not None:
            try:
                # 1. Melewati baris judul (skiprows=1) & Penanganan Encoding
                try:
                    data_import = pd.read_csv(uploaded_file, encoding='utf-8-sig', skiprows=1)
                except UnicodeDecodeError:
                    uploaded_file.seek(0)
                    data_import = pd.read_csv(uploaded_file, encoding='latin1', skiprows=1)

                # 2. Cek apakah pemisahnya ternyata titik koma (;)
                if len(data_import.columns) == 1:
                    uploaded_file.seek(0)
                    data_import = pd.read_csv(uploaded_file, encoding='utf-8-sig', sep=';', skiprows=1)
                    
                # 3. Bersihkan nama kolom dari spasi
                data_import.columns = data_import.columns.str.strip()

                # 4. Samakan nama kolom di CSV ("Bodi Tengah (°C)") ke "Suhu"
                if 'Bodi Tengah (°C)' in data_import.columns:
                    data_import.rename(columns={'Bodi Tengah (°C)': 'Suhu'}, inplace=True)

                st.info("Preview Data yang akan diimpor:")
                st.dataframe(data_import[['Tanggal', 'Suhu']].head(3)) 

                if st.button("Konfirmasi Import Data"):
                    count = 0
                    # Abaikan data kosong pada kolom vital
                    for index, row in data_import.dropna(subset=['Tanggal', 'Suhu']).iterrows():
                        db.execute("INSERT INTO daily_logs (cycle_id, log_date, raw_temp) VALUES (?, ?, ?)", 
                                   (active_cycle[0], str(row['Tanggal']), float(row['Suhu'])))
                        count += 1
                    db.commit()
                    st.success(f"✅ Berhasil mengimpor {count} data historis!")
                    st.rerun()
            except KeyError as e:
                st.error(f"Kolom tidak ditemukan: {e}. Pastikan file memiliki kolom 'Tanggal' dan 'Bodi Tengah (°C)'.")
            except Exception as e:
                st.error(f"Gagal memproses CSV: {e}")
        
        st.markdown("---")
        if st.button("Reset / Pasang BTA Baru"):
            db.execute("UPDATE cycles SET active = 0 WHERE active = 1")
            db.commit()
            st.rerun()
    else:
        st.warning("Buat siklus dulu sebelum import!")
        new_date = st.date_input("Tanggal Pemasangan BTA Baru", datetime.date.today())
        if st.button("Mulai Siklus Baru"):
            db.execute("INSERT INTO cycles (start_date, initial_thickness, active) VALUES (?, ?, ?)", 
                       (new_date.isoformat(), 230.0, 1))
            db.commit()
            st.rerun()

# --- MAIN AREA (Versi Diperbarui: Otomatis Update) ---
if active_cycle:
    cycle_id, start_date_str = active_cycle
    # Pastikan start_date dikonversi dengan aman menggunakan Pandas
    try:
        start_date = pd.to_datetime(start_date_str).date()
    except Exception:
        start_date = datetime.date.fromisoformat(start_date_str)
    
    col1, col2 = st.columns([1, 2])
    
    with col1:
        st.subheader("📥 Input Harian Tunggal")
        tgl_skrg = st.date_input("Tanggal Hari Ini", datetime.date.today())
        temp_raw = st.number_input("Suhu Bodi Tengah (°C)", 200.0, 500.0, 350.0)
        
        if st.button("Simpan & Prediksi", type="primary"):
            # Hanya menyimpan data ke DB, perhitungannya ditangani di Col2
            db.execute("INSERT INTO daily_logs (cycle_id, log_date, raw_temp) VALUES (?, ?, ?)", 
                       (cycle_id, tgl_skrg.isoformat(), temp_raw))
            db.commit()
            st.rerun()
    
    with col2:
        st.subheader("📊 Visualisasi & Analisis")
        
        # Tarik semua data dari DB menggunakan Pandas
        df_hist = pd.read_sql_query(f"SELECT log_date, raw_temp FROM daily_logs WHERE cycle_id={cycle_id}", db)
        
        if not df_hist.empty and model is not None:
            # Rapikan dan urutkan tanggal (antisipasi beda format CSV "MM/DD/YYYY" vs Streamlit "YYYY-MM-DD")
            df_hist['log_date'] = pd.to_datetime(df_hist['log_date'], format="mixed", dayfirst=False)
            df_hist = df_hist.sort_values('log_date').reset_index(drop=True)
            
            # Ambil data hari terakhir yang ada di database
            latest_row = df_hist.iloc[-1]
            latest_date = latest_row['log_date'].date()
            
            # Perhitungan Hari Operasi
            hari_ops = (latest_date - start_date).days
            
            # Hitung Rolling 30 dari Pandas
            tail_30 = pd.to_numeric(df_hist['raw_temp'].tail(30), errors='coerce').dropna()
            roll30 = tail_30.mean() if not tail_30.empty else 0.0
            
            # Lakukan Prediksi Model
            pred_mm = float(model.predict([[hari_ops, roll30]])[0])
            sisa_hari = max(0, int((pred_mm - 100) / 0.191))
            
            # Tampilkan ke Layar secara Otomatis
            m1, m2, m3 = st.columns(3)
            m1.metric("Ketebalan BTA", f"{pred_mm:.1f} mm")
            m2.metric("Umur Operasi", f"{hari_ops} Hari")
            m3.metric("Est. Sisa Umur", f"{sisa_hari} Hari")
            
            # Kembalikan tipe data ke string untuk chart agar rapi
            df_hist['log_date'] = df_hist['log_date'].dt.strftime('%Y-%m-%d')
            st.line_chart(df_hist.set_index('log_date')['raw_temp'])
            st.write(f"Total data dalam database: **{len(df_hist)} baris**")
            
        else:
            st.info("Belum ada data untuk dianalisis. Silakan input harian atau import CSV.")