\"feat_Enhance_BMS_Simulation_SystemLog_UI_and_ACS_Controls\"
This commit is contained in:
@@ -3,86 +3,119 @@ import { BatteryCharging, Zap, Thermometer, Sliders } from 'lucide-react';
|
||||
import { AgvState } from '../types';
|
||||
|
||||
interface BmsPanelProps {
|
||||
state: AgvState;
|
||||
setBatteryLevel: (level: number) => void;
|
||||
state: AgvState;
|
||||
setBatteryLevel: (level: number) => void;
|
||||
setIsCharging: (isCharging: boolean) => void;
|
||||
}
|
||||
|
||||
const BmsPanel: React.FC<BmsPanelProps> = ({ state, setBatteryLevel }) => {
|
||||
const currentCapacity = (state.maxCapacity * (state.batteryLevel / 100)).toFixed(1);
|
||||
const BmsPanel: React.FC<BmsPanelProps> = ({ state, setBatteryLevel, setIsCharging }) => {
|
||||
const currentCapacity = (state.maxCapacity * (state.batteryLevel / 100)).toFixed(1);
|
||||
|
||||
return (
|
||||
<div className="bg-gray-800 p-4 border-t border-gray-700 h-auto select-none">
|
||||
<h3 className="text-sm font-semibold mb-3 text-gray-300 flex items-center justify-between">
|
||||
<span className="flex items-center gap-2"><BatteryCharging size={16} /> BMS Monitor</span>
|
||||
<span className="text-xs font-mono text-gray-500">ID: 01</span>
|
||||
</h3>
|
||||
|
||||
{/* Main Info Grid */}
|
||||
<div className="grid grid-cols-2 gap-2 mb-3">
|
||||
<div className="bg-gray-700/50 p-2 rounded border border-gray-600">
|
||||
<div className="text-[10px] text-gray-400 uppercase tracking-wider">Total Voltage</div>
|
||||
<div className="text-lg font-mono text-white font-bold flex items-baseline gap-1">
|
||||
{(state.cellVoltages.reduce((a,b)=>a+b, 0)).toFixed(1)} <span className="text-xs text-gray-400">V</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-gray-700/50 p-2 rounded border border-gray-600">
|
||||
<div className="text-[10px] text-gray-400 flex items-center gap-1 uppercase tracking-wider">
|
||||
<Thermometer size={10} /> Temp
|
||||
</div>
|
||||
<div className="text-lg font-mono text-white font-bold flex items-baseline gap-1">
|
||||
{state.batteryTemp.toFixed(1)} <span className="text-xs text-gray-400">°C</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-gray-700/50 p-2 rounded border border-gray-600">
|
||||
<div className="text-[10px] text-gray-400 uppercase tracking-wider">Capacity (Ah)</div>
|
||||
<div className="text-sm font-mono text-white">
|
||||
<span className="text-yellow-400 font-bold">{currentCapacity}</span>
|
||||
<span className="text-gray-500"> / </span>
|
||||
{state.maxCapacity}
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-gray-700/50 p-2 rounded border border-gray-600">
|
||||
<div className="text-[10px] text-gray-400 uppercase tracking-wider">Level (%)</div>
|
||||
<div className={`text-sm font-mono font-bold ${state.batteryLevel < 20 ? 'text-red-400' : 'text-green-400'}`}>
|
||||
{state.batteryLevel.toFixed(1)}%
|
||||
return (
|
||||
<div className="bg-gray-800 p-4 border-t border-gray-700 h-auto select-none">
|
||||
<h3 className="text-sm font-semibold mb-3 text-gray-300 flex items-center justify-between">
|
||||
<span className="flex items-center gap-2"><BatteryCharging size={16} /> BMS Monitor</span>
|
||||
<span className="text-xs font-mono text-gray-500">ID: 01</span>
|
||||
</h3>
|
||||
|
||||
{/* Main Info Grid */}
|
||||
<div className="grid grid-cols-2 gap-2 mb-3">
|
||||
<div className="bg-gray-700/50 p-2 rounded border border-gray-600">
|
||||
<div className="text-[10px] text-gray-400 uppercase tracking-wider">Total Voltage</div>
|
||||
<div className="text-lg font-mono text-white font-bold flex items-baseline gap-1">
|
||||
{(state.cellVoltages.reduce((a, b) => a + b, 0)).toFixed(1)} <span className="text-xs text-gray-400">V</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-gray-700/50 p-2 rounded border border-gray-600">
|
||||
<div className="text-[10px] text-gray-400 flex items-center gap-1 uppercase tracking-wider">
|
||||
<Thermometer size={10} /> Temp 1 / 2
|
||||
</div>
|
||||
<div className="flex flex-col gap-0.5">
|
||||
<div className="text-sm font-mono text-white font-bold flex justify-between">
|
||||
<span className="text-[10px] text-gray-500">1:</span> {state.batteryTemps?.[0]?.toFixed(1)}°C
|
||||
</div>
|
||||
<div className="text-sm font-mono text-white font-bold flex justify-between">
|
||||
<span className="text-[10px] text-gray-500">2:</span> {state.batteryTemps?.[1]?.toFixed(1)}°C
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Manual Slider */}
|
||||
<div className="mb-4">
|
||||
<div className="flex justify-between text-xs text-gray-400 mb-1">
|
||||
<span className="flex items-center gap-1"><Sliders size={10}/> Manual Adjust</span>
|
||||
</div>
|
||||
<input
|
||||
type="range"
|
||||
min="0"
|
||||
max="100"
|
||||
step="1"
|
||||
value={state.batteryLevel}
|
||||
onChange={(e) => setBatteryLevel(Number(e.target.value))}
|
||||
className="w-full h-1.5 bg-gray-600 rounded-lg appearance-none cursor-pointer accent-green-500 hover:accent-green-400"
|
||||
/>
|
||||
</div>
|
||||
<div className="bg-gray-700/50 p-2 rounded border border-gray-600">
|
||||
<div className="text-[10px] text-gray-400 uppercase tracking-wider">Capacity (Ah)</div>
|
||||
<div className="text-sm font-mono text-white">
|
||||
<span className="text-yellow-400 font-bold">{currentCapacity}</span>
|
||||
<span className="text-gray-500"> / </span>
|
||||
{state.maxCapacity}
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-gray-700/50 p-2 rounded border border-gray-600">
|
||||
<div className="text-[10px] text-gray-400 uppercase tracking-wider">Level (%)</div>
|
||||
<div className={`text-sm font-mono font-bold ${state.batteryLevel < 20 ? 'text-red-400' : 'text-green-400'}`}>
|
||||
{state.batteryLevel.toFixed(1)}%
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Cells */}
|
||||
<div className="space-y-1">
|
||||
<div className="text-[10px] text-gray-500 uppercase tracking-wider mb-1">Cell Voltages (V)</div>
|
||||
<div className="grid grid-cols-4 gap-1">
|
||||
{state.cellVoltages.map((v, i) => (
|
||||
<div key={i} className={`py-1 px-0.5 text-center rounded text-[10px] font-mono border ${
|
||||
v < 2.9 ? 'bg-red-900/30 border-red-800 text-red-300' :
|
||||
v > 3.5 ? 'bg-blue-900/30 border-blue-800 text-blue-300' :
|
||||
'bg-gray-700/30 border-gray-600 text-gray-300'
|
||||
}`}>
|
||||
{v.toFixed(3)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
<div className="bg-gray-700/50 p-2 rounded border border-gray-600">
|
||||
<div className="text-[10px] text-gray-400 uppercase tracking-wider">Current (A)</div>
|
||||
<div className={`text-sm font-mono font-bold ${state.isCharging ? 'text-yellow-400' : 'text-blue-400'}`}>
|
||||
{(state.isCharging ? -18.7 : 2.1).toFixed(1)} A
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-gray-700/50 p-2 rounded border border-gray-600">
|
||||
<div className="text-[10px] text-gray-400 uppercase tracking-wider">Power (W)</div>
|
||||
<div className={`text-sm font-mono font-bold ${state.isCharging ? 'text-yellow-400' : 'text-blue-400'}`}>
|
||||
{state.isCharging ? -450 : 50} W
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Charge Button */}
|
||||
<div className="mb-3">
|
||||
<button
|
||||
onClick={() => setIsCharging(!state.isCharging)}
|
||||
className={`w-full py-1.5 rounded flex items-center justify-center gap-2 text-xs font-bold transition-all ${state.isCharging
|
||||
? 'bg-yellow-600 text-white shadow-[0_0_10px_rgba(202,138,4,0.5)] border border-yellow-500'
|
||||
: 'bg-gray-700 text-gray-400 hover:bg-gray-600 border border-gray-600'
|
||||
}`}
|
||||
>
|
||||
<Zap size={14} className={state.isCharging ? "fill-white animate-pulse" : ""} />
|
||||
{state.isCharging ? 'CHARGING...' : 'START CHARGING'}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Manual Slider */}
|
||||
<div className="mb-4">
|
||||
<div className="flex justify-between text-xs text-gray-400 mb-1">
|
||||
<span className="flex items-center gap-1"><Sliders size={10} /> Manual Adjust</span>
|
||||
</div>
|
||||
<input
|
||||
type="range"
|
||||
min="0"
|
||||
max="100"
|
||||
step="1"
|
||||
value={state.batteryLevel}
|
||||
onChange={(e) => setBatteryLevel(Number(e.target.value))}
|
||||
className="w-full h-1.5 bg-gray-600 rounded-lg appearance-none cursor-pointer accent-green-500 hover:accent-green-400"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Cells */}
|
||||
<div className="space-y-1">
|
||||
<div className="text-[10px] text-gray-500 uppercase tracking-wider mb-1">Cell Voltages (V)</div>
|
||||
<div className="grid grid-cols-4 gap-1">
|
||||
{state.cellVoltages.map((v, i) => (
|
||||
<div key={i} className={`py-1 px-0.5 text-center rounded text-[10px] font-mono border ${v < 2.9 ? 'bg-red-900/30 border-red-800 text-red-300' :
|
||||
v > 3.5 ? 'bg-blue-900/30 border-blue-800 text-blue-300' :
|
||||
'bg-gray-700/30 border-gray-600 text-gray-300'
|
||||
}`}>
|
||||
{v.toFixed(3)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default BmsPanel;
|
||||
|
||||
Reference in New Issue
Block a user