import React, { useEffect, useState, useRef } from 'react'; import { getDevices, getHistory, SensorData, Device } from '../services/api'; import { formatSensorValue, getSensorPrecision } from '../utils/formatters'; import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts'; import LoadingSpinner from '../components/LoadingSpinner'; import StatusIndicator from '../components/StatusIndicator'; import { ConnectionStatus } from '../hooks/useRealTimeData'; const History: React.FC = () => { const [devices, setDevices] = useState([]); const [selectedDevice, setSelectedDevice] = useState(null); const [historyData, setHistoryData] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [lastUpdate, setLastUpdate] = useState(''); const [connectionStatus, setConnectionStatus] = useState('disconnected'); const pollingRef = useRef(null); // 디바이스 목록 불러오기 useEffect(() => { (async () => { try { setError(null); setConnectionStatus('connecting'); const devs = await getDevices(); setDevices(devs); if (devs.length > 0) { setSelectedDevice(devs[0]); } setConnectionStatus('connected'); } catch (err) { setError('디바이스 목록을 불러오는 중 오류가 발생했습니다.'); setConnectionStatus('error'); } })(); }, []); // 디바이스 변경 시 히스토리 불러오기 useEffect(() => { if (selectedDevice) { fetchHistory(selectedDevice.device_id); startPolling(selectedDevice.device_id); } return () => { stopPolling(); }; // eslint-disable-next-line }, [selectedDevice]); // 폴링 시작 const startPolling = (deviceId: string) => { stopPolling(); console.log('🔄 히스토리 폴링 시작 - 디바이스:', deviceId); // 즉시 첫 번째 데이터 가져오기 fetchHistory(deviceId); // 5초마다 폴링 pollingRef.current = setInterval(() => { console.log('⏰ 히스토리 폴링 실행 - 디바이스:', deviceId); fetchHistory(deviceId); }, 5000); }; // 폴링 중지 const stopPolling = () => { if (pollingRef.current) { clearInterval(pollingRef.current); pollingRef.current = null; console.log('🛑 히스토리 폴링 중지'); } }; // 히스토리 데이터 불러오기 const fetchHistory = async (deviceId: string) => { setLoading(true); try { setError(null); setConnectionStatus('connecting'); console.log(`📡 히스토리 데이터 요청 - 디바이스: ${deviceId}`); const res = await getHistory(deviceId, 100, 0); // 데이터 유효성 검사 및 정리 const validData = res.data .filter((item: any) => item && typeof item === 'object' && item.device_id) .map((item: any) => { const safeNumber = (value: any): number => { if (typeof value === 'number' && !isNaN(value) && isFinite(value)) { return value; } return 0; }; return { ...item, temperature: safeNumber(item.temperature), humidity: safeNumber(item.humidity), pm10: safeNumber(item.pm10), pm25: safeNumber(item.pm25), pressure: safeNumber(item.pressure), illumination: safeNumber(item.illumination), tvoc: safeNumber(item.tvoc), co2: safeNumber(item.co2), o2: safeNumber(item.o2), co: safeNumber(item.co) }; }); setHistoryData(validData); setLastUpdate(new Date().toISOString()); setConnectionStatus('connected'); console.log(`✅ 히스토리 데이터 수신 완료 - ${validData.length}개`); } catch (e) { setHistoryData([]); setError('히스토리 데이터를 불러오는 중 오류가 발생했습니다.'); setConnectionStatus('error'); console.error('❌ 히스토리 데이터 요청 실패:', e); } finally { setLoading(false); } }; // 수동 새로고침 const handleRefresh = () => { if (selectedDevice) { console.log('🔄 수동 새로고침 시작'); fetchHistory(selectedDevice.device_id); } }; return (
{/* 상태 표시 */}

센서 히스토리

{/* 에러 메시지 */} {error && (

{error}

)}
{loading ? (
) : (
{/* 온도 */}

온도 시계열

{historyData.length > 0 ? ( v.slice(5, 16)} /> formatSensorValue(v, getSensorPrecision('temperature'))} /> ) : (

데이터가 없습니다

)}
{/* 습도 */}

습도 시계열

{historyData.length > 0 ? ( v.slice(5, 16)} /> formatSensorValue(v, getSensorPrecision('humidity'))} /> ) : (

데이터가 없습니다

)}
{/* PM10/PM2.5 */}

미세먼지(PM10/PM2.5)

{historyData.length > 0 ? ( v.slice(5, 16)} /> ) : (

데이터가 없습니다

)}
{/* CO2/TVOC */}

CO2/TVOC

{historyData.length > 0 ? ( v.slice(5, 16)} /> ) : (

데이터가 없습니다

)}
{/* CO/O2 */}

CO/O2

{historyData.length > 0 ? ( v.slice(5, 16)} /> ) : (

데이터가 없습니다

)}
{/* 기압/조도 */}

기압/조도

{historyData.length > 0 ? ( v.slice(5, 16)} /> ) : (

데이터가 없습니다

)}
)}
); }; export default History;