import React, { useState, useEffect } from 'react';
import { serialService } from './services/serialService';
import { JBDProtocol, CMD_BASIC_INFO, CMD_CELL_INFO, CMD_HW_VERSION } from './services/jbdProtocol';
import { BMSBasicInfo, BMSCellInfo, ConnectionState } from './types';
import Dashboard from './components/Dashboard';
import Settings from './components/Settings';
import Terminal from './components/Terminal';
import { LayoutDashboard, Settings as SettingsIcon, Usb, AlertCircle, RefreshCw, PanelRightClose, PanelRightOpen } from 'lucide-react';
const AdSenseBanner: React.FC = () => {
useEffect(() => {
try {
// @ts-ignore
(window.adsbygoogle = window.adsbygoogle || []).push({});
} catch (e) {
console.error("AdSense error", e);
}
}, []);
return (
{/* AppLeftSizeBox */}
);
};
const App: React.FC = () => {
const [connectionState, setConnectionState] = useState(ConnectionState.DISCONNECTED);
const [basicInfo, setBasicInfo] = useState(null);
const [cellInfo, setCellInfo] = useState(null);
const [hwVersion, setHwVersion] = useState(null);
const [activeTab, setActiveTab] = useState<'dashboard' | 'settings'>('dashboard');
const [errorMsg, setErrorMsg] = useState(null);
const [isTerminalOpen, setIsTerminalOpen] = useState(false);
useEffect(() => {
let isMounted = true;
let timeoutId: number;
const runPoll = async () => {
// ONLY POLL IF ON DASHBOARD AND CONNECTED
if (connectionState !== ConnectionState.CONNECTED || activeTab !== 'dashboard') return;
try {
// 1. Get Basic Info
const basicData = await serialService.sendCommand(CMD_BASIC_INFO);
const parsedBasic = JBDProtocol.parseBasicInfo(basicData);
if (isMounted) setBasicInfo(parsedBasic);
// Delay 500ms
await new Promise(r => setTimeout(r, 500));
if (!isMounted) return;
// 2. Get Cell Info
const cellData = await serialService.sendCommand(CMD_CELL_INFO);
const parsedCells = JBDProtocol.parseCellInfo(cellData);
if (isMounted) setCellInfo(parsedCells);
// Delay 500ms
await new Promise(r => setTimeout(r, 500));
if (!isMounted) return;
// 3. Get Hardware Version (CMD 0x05)
const hwData = await serialService.sendCommand(CMD_HW_VERSION);
// CMD 0x05 usually returns raw ASCII string in payload
const versionStr = new TextDecoder().decode(hwData);
if (isMounted) setHwVersion(versionStr);
if (isMounted) setErrorMsg(null);
} catch (e: any) {
console.error("Polling error:", e);
// Log to terminal for debugging
serialService.log('error', `Poll Fail: ${e.message}`);
setIsTerminalOpen(true);
} finally {
// Schedule next poll cycle in 500ms
if (isMounted && connectionState === ConnectionState.CONNECTED && activeTab === 'dashboard') {
timeoutId = window.setTimeout(runPoll, 500);
}
}
};
if (connectionState === ConnectionState.CONNECTED && activeTab === 'dashboard') {
runPoll();
}
return () => {
isMounted = false;
window.clearTimeout(timeoutId);
};
}, [connectionState, activeTab]);
const handleConnect = async () => {
try {
setConnectionState(ConnectionState.CONNECTING);
await serialService.connect();
setConnectionState(ConnectionState.CONNECTED);
setErrorMsg(null);
} catch (e: any) {
console.error(e);
setConnectionState(ConnectionState.ERROR);
setErrorMsg(e.message || "시리얼 포트 연결 실패");
setIsTerminalOpen(true);
}
};
const handleDisconnect = async () => {
try {
await serialService.disconnect();
setConnectionState(ConnectionState.DISCONNECTED);
setBasicInfo(null);
setCellInfo(null);
setHwVersion(null);
} catch (e) {
console.error(e);
}
};
const toggleMosfet = async (type: 'charge' | 'discharge', currentState: boolean) => {
if (!basicInfo) return;
try {
const newCharge = type === 'charge' ? !currentState : basicInfo.mosfetStatus.charge;
const newDischarge = type === 'discharge' ? !currentState : basicInfo.mosfetStatus.discharge;
await serialService.toggleMosfet(newCharge, newDischarge);
} catch (e: any) {
alert("MOSFET 제어 실패: " + e.message);
}
};
const renderContent = () => {
switch (activeTab) {
case 'dashboard':
return ;
case 'settings':
return ;
default:
return null;
}
};
return (
{/* Sidebar / Topbar */}
{/* AdSense Area - Expanded */}
{/* Main Content Area */}
{/* Header */}
{/* Content Body with Right Sidebar */}
{/* Main View */}
{errorMsg && (
)}
{renderContent()}
{/* Empty State Overlay */}
{connectionState !== ConnectionState.CONNECTED && (
장치가 연결되지 않았습니다
JBD BMS를 UART-to-USB 어댑터로 연결하여 실시간 상태를 확인하고 설정을 변경하세요.
)}
{/* Right Terminal Panel */}
);
};
export default App;