Spaces:
Running
Running
import React, { useState, useEffect } from 'react'; | |
import './EnergySimulation.css'; | |
// Mock data for demonstration purposes | |
const mockEnergyData = { | |
solarGeneration: { | |
current: 85, | |
capacity: 150, | |
dailyForecast: [ | |
{ hour: '06:00', value: 0 }, | |
{ hour: '07:00', value: 15 }, | |
{ hour: '08:00', value: 45 }, | |
{ hour: '09:00', value: 75 }, | |
{ hour: '10:00', value: 95 }, | |
{ hour: '11:00', value: 120 }, | |
{ hour: '12:00', value: 140 }, | |
{ hour: '13:00', value: 150 }, | |
{ hour: '14:00', value: 145 }, | |
{ hour: '15:00', value: 130 }, | |
{ hour: '16:00', value: 100 }, | |
{ hour: '17:00', value: 70 }, | |
{ hour: '18:00', value: 40 }, | |
{ hour: '19:00', value: 10 }, | |
{ hour: '20:00', value: 0 }, | |
] | |
}, | |
batteryStorage: { | |
current: 72, | |
capacity: 500, | |
chargingRate: 45, | |
dischargingRate: 60, | |
history: [ | |
{ time: '00:00', level: 65 }, | |
{ time: '01:00', level: 68 }, | |
{ time: '02:00', level: 70 }, | |
{ time: '03:00', level: 72 }, | |
{ time: '04:00', level: 74 }, | |
{ time: '05:00', level: 76 }, | |
{ time: '06:00', level: 78 }, | |
{ time: '07:00', level: 80 }, | |
{ time: '08:00', level: 82 }, | |
{ time: '09:00', level: 80 }, | |
{ time: '10:00', level: 78 }, | |
{ time: '11:00', level: 75 }, | |
{ time: '12:00', level: 72 }, | |
] | |
}, | |
gridConsumption: { | |
current: 120, | |
peak: 250, | |
average: 180, | |
history: [ | |
{ time: '00:00', value: 100 }, | |
{ time: '01:00', value: 90 }, | |
{ time: '02:00', value: 85 }, | |
{ time: '03:00', value: 80 }, | |
{ time: '04:00', value: 90 }, | |
{ time: '05:00', value: 110 }, | |
{ time: '06:00', value: 130 }, | |
{ time: '07:00', value: 150 }, | |
{ time: '08:00', value: 180 }, | |
{ time: '09:00', value: 210 }, | |
{ time: '10:00', value: 230 }, | |
{ time: '11:00', value: 250 }, | |
{ time: '12:00', value: 240 }, | |
] | |
}, | |
chargingDemand: { | |
current: 210, | |
activeChargers: 6, | |
totalChargers: 12, | |
byType: [ | |
{ type: 'Level 2', count: 2, power: 20 }, | |
{ type: 'DC Fast', count: 3, power: 150 }, | |
{ type: 'Ultra Fast', count: 1, power: 350 } | |
], | |
forecast: [ | |
{ time: '13:00', value: 220 }, | |
{ time: '14:00', value: 240 }, | |
{ time: '15:00', value: 260 }, | |
{ time: '16:00', value: 280 }, | |
{ time: '17:00', value: 300 }, | |
{ time: '18:00', value: 280 }, | |
{ time: '19:00', value: 250 }, | |
{ time: '20:00', value: 220 }, | |
{ time: '21:00', value: 180 }, | |
{ time: '22:00', value: 150 }, | |
{ time: '23:00', value: 120 }, | |
] | |
} | |
}; | |
function EnergySimulation() { | |
const [activeTab, setActiveTab] = useState('overview'); | |
const [loading, setLoading] = useState(true); | |
const [simulationSpeed, setSimulationSpeed] = useState(1); | |
const [simulationRunning, setSimulationRunning] = useState(false); | |
const [simulationTime, setSimulationTime] = useState('12:00'); | |
const [energyData, setEnergyData] = useState(mockEnergyData); | |
const [optimizationMode, setOptimizationMode] = useState('balanced'); | |
useEffect(() => { | |
// Simulate loading | |
const timer = setTimeout(() => { | |
setLoading(false); | |
}, 1500); | |
return () => clearTimeout(timer); | |
}, []); | |
useEffect(() => { | |
let interval; | |
if (simulationRunning) { | |
interval = setInterval(() => { | |
// Update simulation time | |
setSimulationTime(prevTime => { | |
const [hours, minutes] = prevTime.split(':').map(Number); | |
let newMinutes = minutes + 15; | |
let newHours = hours; | |
if (newMinutes >= 60) { | |
newMinutes = 0; | |
newHours = (newHours + 1) % 24; | |
} | |
return `${newHours.toString().padStart(2, '0')}:${newMinutes.toString().padStart(2, '0')}`; | |
}); | |
// Update energy data based on simulation | |
setEnergyData(prevData => { | |
// This would be a more complex algorithm in a real implementation | |
// For demo purposes, we're just making small random adjustments | |
const solarVariation = Math.random() * 10 - 5; | |
const batteryVariation = Math.random() * 3 - 1; | |
const gridVariation = Math.random() * 15 - 7; | |
const demandVariation = Math.random() * 20 - 10; | |
return { | |
solarGeneration: { | |
...prevData.solarGeneration, | |
current: Math.max(0, Math.min(prevData.solarGeneration.capacity, prevData.solarGeneration.current + solarVariation)) | |
}, | |
batteryStorage: { | |
...prevData.batteryStorage, | |
current: Math.max(0, Math.min(100, prevData.batteryStorage.current + batteryVariation)) | |
}, | |
gridConsumption: { | |
...prevData.gridConsumption, | |
current: Math.max(0, prevData.gridConsumption.current + gridVariation) | |
}, | |
chargingDemand: { | |
...prevData.chargingDemand, | |
current: Math.max(0, prevData.chargingDemand.current + demandVariation) | |
} | |
}; | |
}); | |
}, 1000 / simulationSpeed); | |
} | |
return () => clearInterval(interval); | |
}, [simulationRunning, simulationSpeed]); | |
const toggleSimulation = () => { | |
setSimulationRunning(!simulationRunning); | |
}; | |
const resetSimulation = () => { | |
setSimulationRunning(false); | |
setSimulationTime('12:00'); | |
setEnergyData(mockEnergyData); | |
}; | |
const handleOptimizationChange = (mode) => { | |
setOptimizationMode(mode); | |
// In a real implementation, this would adjust the simulation parameters | |
// For demo purposes, we'll just show different messages | |
let message; | |
switch(mode) { | |
case 'cost': | |
message = "Optimizing for lowest operational cost"; | |
break; | |
case 'renewable': | |
message = "Maximizing renewable energy usage"; | |
break; | |
case 'demand': | |
message = "Prioritizing charging demand fulfillment"; | |
break; | |
default: | |
message = "Balanced optimization mode"; | |
} | |
// This would show a notification in a real implementation | |
console.log(message); | |
}; | |
// Helper function to render a gauge chart | |
const renderGauge = (value, max, label, color) => { | |
const percentage = (value / max) * 100; | |
const angle = (percentage / 100) * 180; | |
return ( | |
<div className="gauge-container"> | |
<div className="gauge"> | |
<div className="gauge-body"> | |
<div | |
className="gauge-fill" | |
style={{ | |
transform: `rotate(${angle}deg)`, | |
background: `linear-gradient(90deg, ${color}, ${color}CC)` | |
}} | |
></div> | |
<div className="gauge-cover"> | |
<div className="gauge-value">{value}</div> | |
<div className="gauge-label">{label}</div> | |
</div> | |
</div> | |
</div> | |
<div className="gauge-max">{max} {label}</div> | |
</div> | |
); | |
}; | |
// Helper function to render a simple bar chart | |
const renderBarChart = (data, valueKey, labelKey, color) => { | |
const maxValue = Math.max(...data.map(item => item[valueKey])); | |
return ( | |
<div className="bar-chart"> | |
{data.map((item, index) => ( | |
<div key={index} className="bar-container"> | |
<div className="bar-label">{item[labelKey]}</div> | |
<div className="bar-wrapper"> | |
<div | |
className="bar" | |
style={{ | |
width: `${(item[valueKey] / maxValue) * 100}%`, | |
background: `linear-gradient(90deg, ${color}, ${color}CC)` | |
}} | |
></div> | |
<span className="bar-value">{item[valueKey]}</span> | |
</div> | |
</div> | |
))} | |
</div> | |
); | |
}; | |
// Helper function to render the energy flow diagram | |
const renderEnergyFlow = () => { | |
const { solarGeneration, batteryStorage, gridConsumption, chargingDemand } = energyData; | |
// Calculate flow widths based on energy values | |
const solarWidth = Math.min(100, (solarGeneration.current / 200) * 100); | |
const gridWidth = Math.min(100, (gridConsumption.current / 300) * 100); | |
const batteryWidth = Math.min(100, (batteryStorage.chargingRate / 100) * 100); | |
const demandWidth = Math.min(100, (chargingDemand.current / 400) * 100); | |
return ( | |
<div className="energy-flow-diagram"> | |
<div className="energy-sources"> | |
<div className="energy-source solar"> | |
<div className="source-icon">☀️</div> | |
<div className="source-label">Solar Array</div> | |
<div className="source-value">{solarGeneration.current} kW</div> | |
</div> | |
<div className="energy-source grid"> | |
<div className="source-icon">⚡</div> | |
<div className="source-label">Grid Power</div> | |
<div className="source-value">{gridConsumption.current} kW</div> | |
</div> | |
</div> | |
<div className="energy-flows"> | |
<div className="flow solar-flow" style={{ width: `${solarWidth}%` }}></div> | |
<div className="flow grid-flow" style={{ width: `${gridWidth}%` }}></div> | |
</div> | |
<div className="energy-storage"> | |
<div className="storage-icon">🔋</div> | |
<div className="storage-label">Battery Storage</div> | |
<div className="storage-value">{batteryStorage.current}%</div> | |
<div className="storage-capacity">{batteryStorage.capacity} kWh</div> | |
</div> | |
<div className="energy-flows"> | |
<div className="flow battery-flow" style={{ width: `${batteryWidth}%` }}></div> | |
</div> | |
<div className="energy-demand"> | |
<div className="demand-icon">🚗</div> | |
<div className="demand-label">Charging Demand</div> | |
<div className="demand-value">{chargingDemand.current} kW</div> | |
<div className="demand-details">{chargingDemand.activeChargers}/{chargingDemand.totalChargers} Chargers Active</div> | |
</div> | |
</div> | |
); | |
}; | |
if (loading) { | |
return ( | |
<div className="energy-simulation loading"> | |
<div className="loading-logo"> | |
<img src="/images/unity_fleet_logo.png" alt="Unity Fleet" /> | |
<div className="loading-pulse"></div> | |
</div> | |
<p>Initializing Energy Simulation...</p> | |
</div> | |
); | |
} | |
return ( | |
<div className="energy-simulation"> | |
<header className="simulation-header"> | |
<div className="simulation-title"> | |
<h1>Energy Simulation</h1> | |
<span className="simulation-subtitle">The Link Charging Network</span> | |
</div> | |
<div className="simulation-controls"> | |
<div className="time-display"> | |
<span className="time-label">Simulation Time:</span> | |
<span className="time-value">{simulationTime}</span> | |
</div> | |
<div className="control-buttons"> | |
<button | |
className={`control-button ${simulationRunning ? 'active' : ''}`} | |
onClick={toggleSimulation} | |
> | |
{simulationRunning ? 'Pause' : 'Start'} Simulation | |
</button> | |
<button | |
className="control-button" | |
onClick={resetSimulation} | |
> | |
Reset | |
</button> | |
</div> | |
<div className="speed-control"> | |
<span className="speed-label">Speed:</span> | |
<div className="speed-buttons"> | |
<button | |
className={`speed-button ${simulationSpeed === 0.5 ? 'active' : ''}`} | |
onClick={() => setSimulationSpeed(0.5)} | |
> | |
0.5x | |
</button> | |
<button | |
className={`speed-button ${simulationSpeed === 1 ? 'active' : ''}`} | |
onClick={() => setSimulationSpeed(1)} | |
> | |
1x | |
</button> | |
<button | |
className={`speed-button ${simulationSpeed === 2 ? 'active' : ''}`} | |
onClick={() => setSimulationSpeed(2)} | |
> | |
2x | |
</button> | |
<button | |
className={`speed-button ${simulationSpeed === 5 ? 'active' : ''}`} | |
onClick={() => setSimulationSpeed(5)} | |
> | |
5x | |
</button> | |
</div> | |
</div> | |
</div> | |
</header> | |
<div className="simulation-navigation"> | |
<button | |
className={`nav-button ${activeTab === 'overview' ? 'active' : ''}`} | |
onClick={() => setActiveTab('overview')} | |
> | |
Overview | |
</button> | |
<button | |
className={`nav-button ${activeTab === 'solar' ? 'active' : ''}`} | |
onClick={() => setActiveTab('solar')} | |
> | |
Solar Generation | |
</button> | |
<button | |
className={`nav-button ${activeTab === 'battery' ? 'active' : ''}`} | |
onClick={() => setActiveTab('battery')} | |
> | |
Battery Storage | |
</button> | |
<button | |
className={`nav-button ${activeTab === 'grid' ? 'active' : ''}`} | |
onClick={() => setActiveTab('grid')} | |
> | |
Grid Integration | |
</button> | |
<button | |
className={`nav-button ${activeTab === 'demand' ? 'active' : ''}`} | |
onClick={() => setActiveTab('demand')} | |
> | |
Charging Demand | |
</button> | |
<button | |
className={`nav-button ${activeTab === 'optimization' ? 'active' : ''}`} | |
onClick={() => setActiveTab('optimization')} | |
> | |
Optimization | |
</button> | |
</div> | |
<main className="simulation-content"> | |
{activeTab === 'overview' && ( | |
<div className="overview-tab"> | |
<div className="energy-summary"> | |
<div className="summary-card"> | |
<h3>Current Energy Balance</h3> | |
<div className="balance-metrics"> | |
<div className="metric"> | |
<span className="metric-label">Total Input:</span> | |
<span className="metric-value">{energyData.solarGeneration.current + energyData.gridConsumption.current} kW</span> | |
</div> | |
<div className="metric"> | |
<span className="metric-label">Total Demand:</span> | |
<span className="metric-value">{energyData.chargingDemand.current} kW</span> | |
</div> | |
<div className="metric"> | |
<span className="metric-label">Battery Change:</span> | |
<span className={`metric-value ${energyData.batteryStorage.chargingRate > energyData.batteryStorage.dischargingRate ? 'positive' : 'negative'}`}> | |
{energyData.batteryStorage.chargingRate > energyData.batteryStorage.dischargingRate ? '+' : '-'} | |
{Math.abs(energyData.batteryStorage.chargingRate - energyData.batteryStorage.dischargingRate)} kW | |
</span> | |
</div> | |
<div className="metric"> | |
<span className="metric-label">Renewable Ratio:</span> | |
<span className="metric-value"> | |
{Math.round((energyData.solarGeneration.current / (energyData.solarGeneration.current + energyData.gridConsumption.current)) * 100)}% | |
</span> | |
</div> | |
</div> | |
</div> | |
</div> | |
{renderEnergyFlow()} | |
<div className="optimization-summary"> | |
<h3>Optimization Mode: {optimizationMode.charAt(0).toUpperCase() + optimizationMode.slice(1)}</h3> | |
<div className="optimization-metrics"> | |
<div className="metric"> | |
<span className="metric-label">Cost Efficiency:</span> | |
<div className="metric-bar-container"> | |
<div | |
className="metric-bar" | |
style={{ | |
width: `${optimizationMode === 'cost' ? 90 : optimizationMode === 'balanced' ? 70 : 50}%`, | |
background: 'linear-gradient(90deg, #00E0FF, #35F2DB)' | |
}} | |
></div> | |
</div> | |
</div> | |
<div className="metric"> | |
<span className="metric-label">Renewable Usage:</span> | |
<div className="metric-bar-container"> | |
<div | |
className="metric-bar" | |
style={{ | |
width: `${optimizationMode === 'renewable' ? 90 : optimizationMode === 'balanced' ? 70 : 50}%`, | |
background: 'linear-gradient(90deg, #00E0FF, #35F2DB)' | |
}} | |
></div> | |
</div> | |
</div> | |
<div className="metric"> | |
<span className="metric-label">Charging Capacity:</span> | |
<div className="metric-bar-container"> | |
<div | |
className="metric-bar" | |
style={{ | |
width: `${optimizationMode === 'demand' ? 90 : optimizationMode === 'balanced' ? 70 : 50}%`, | |
background: 'linear-gradient(90deg, #00E0FF, #35F2DB)' | |
}} | |
></div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
)} | |
{activeTab === 'solar' && ( | |
<div className="solar-tab"> | |
<div className="solar-metrics"> | |
<div className="metric-card"> | |
{renderGauge( | |
energyData.solarGeneration.current, | |
energyData.solarGeneration.capacity, | |
'kW', | |
'#FFD700' | |
)} | |
<h3>Current Solar Generation</h3> | |
</div> | |
<div className="metric-card"> | |
<div className="daily-production"> | |
<h3>Daily Production Forecast</h3> | |
{renderBarChart( | |
energyData.solarGeneration.dailyForecast, | |
'value', | |
'hour', | |
'#FFD700' | |
)} | |
</div> | |
</div> | |
</div> | |
<div className="solar-details"> | |
<div className="detail-card"> | |
<h3>Solar Array Details</h3> | |
<div className="detail-grid"> | |
<div className="detail-item"> | |
<span className="detail-label">Array Capacity:</span> | |
<span className="detail-value">{energyData.solarGeneration.capacity} kW</span> | |
</div> | |
<div className="detail-item"> | |
<span className="detail-label">Panel Type:</span> | |
<span className="detail-value">Monocrystalline</span> | |
</div> | |
<div className="detail-item"> | |
<span className="detail-label">Panel Count:</span> | |
<span className="detail-value">375</span> | |
</div> | |
<div className="detail-item"> | |
<span className="detail-label">Array Area:</span> | |
<span className="detail-value">750 m²</span> | |
</div> | |
<div className="detail-item"> | |
<span className="detail-label">Efficiency:</span> | |
<span className="detail-value">22.5%</span> | |
</div> | |
<div className="detail-item"> | |
<span className="detail-label">Installation Date:</span> | |
<span className="detail-value">March 2025</span> | |
</div> | |
</div> | |
</div> | |
<div className="detail-card"> | |
<h3>Weather Impact</h3> | |
<div className="weather-forecast"> | |
<div className="weather-item"> | |
<div className="weather-icon">☀️</div> | |
<div className="weather-day">Today</div> | |
<div className="weather-impact positive">100%</div> | |
</div> | |
<div className="weather-item"> | |
<div className="weather-icon">🌤️</div> | |
<div className="weather-day">Tomorrow</div> | |
<div className="weather-impact neutral">85%</div> | |
</div> | |
<div className="weather-item"> | |
<div className="weather-icon">☁️</div> | |
<div className="weather-day">Wednesday</div> | |
<div className="weather-impact negative">60%</div> | |
</div> | |
<div className="weather-item"> | |
<div className="weather-icon">🌧️</div> | |
<div className="weather-day">Thursday</div> | |
<div className="weather-impact negative">40%</div> | |
</div> | |
<div className="weather-item"> | |
<div className="weather-icon">🌤️</div> | |
<div className="weather-day">Friday</div> | |
<div className="weather-impact neutral">75%</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
)} | |
{activeTab === 'battery' && ( | |
<div className="battery-tab"> | |
<div className="battery-metrics"> | |
<div className="metric-card"> | |
{renderGauge( | |
energyData.batteryStorage.current, | |
100, | |
'%', | |
'#00E0FF' | |
)} | |
<h3>Current Battery Level</h3> | |
</div> | |
<div className="metric-card"> | |
<div className="battery-status"> | |
<h3>Battery Status</h3> | |
<div className="status-grid"> | |
<div className="status-item"> | |
<span className="status-label">Capacity:</span> | |
<span className="status-value">{energyData.batteryStorage.capacity} kWh</span> | |
</div> | |
<div className="status-item"> | |
<span className="status-label">Charging Rate:</span> | |
<span className="status-value">{energyData.batteryStorage.chargingRate} kW</span> | |
</div> | |
<div className="status-item"> | |
<span className="status-label">Discharging Rate:</span> | |
<span className="status-value">{energyData.batteryStorage.dischargingRate} kW</span> | |
</div> | |
<div className="status-item"> | |
<span className="status-label">Net Flow:</span> | |
<span className={`status-value ${energyData.batteryStorage.chargingRate > energyData.batteryStorage.dischargingRate ? 'positive' : 'negative'}`}> | |
{energyData.batteryStorage.chargingRate > energyData.batteryStorage.dischargingRate ? '+' : '-'} | |
{Math.abs(energyData.batteryStorage.chargingRate - energyData.batteryStorage.dischargingRate)} kW | |
</span> | |
</div> | |
<div className="status-item"> | |
<span className="status-label">Estimated Runtime:</span> | |
<span className="status-value"> | |
{Math.round((energyData.batteryStorage.current / 100) * energyData.batteryStorage.capacity / energyData.batteryStorage.dischargingRate)} hours | |
</span> | |
</div> | |
<div className="status-item"> | |
<span className="status-label">Health:</span> | |
<span className="status-value">98%</span> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div className="battery-history"> | |
<h3>Battery Level History</h3> | |
{renderBarChart( | |
energyData.batteryStorage.history, | |
'level', | |
'time', | |
'#00E0FF' | |
)} | |
</div> | |
<div className="battery-controls"> | |
<h3>Battery Management Controls</h3> | |
<div className="control-grid"> | |
<div className="control-item"> | |
<span className="control-label">Charge Limit:</span> | |
<div className="control-slider-container"> | |
<input type="range" min="50" max="100" defaultValue="90" className="control-slider" /> | |
<span className="slider-value">90%</span> | |
</div> | |
</div> | |
<div className="control-item"> | |
<span className="control-label">Discharge Limit:</span> | |
<div className="control-slider-container"> | |
<input type="range" min="10" max="50" defaultValue="20" className="control-slider" /> | |
<span className="slider-value">20%</span> | |
</div> | |
</div> | |
<div className="control-item"> | |
<span className="control-label">Priority Mode:</span> | |
<div className="control-buttons"> | |
<button className="mode-button active">Auto</button> | |
<button className="mode-button">Charge</button> | |
<button className="mode-button">Discharge</button> | |
<button className="mode-button">Hold</button> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
)} | |
{activeTab === 'grid' && ( | |
<div className="grid-tab"> | |
<div className="grid-metrics"> | |
<div className="metric-card"> | |
{renderGauge( | |
energyData.gridConsumption.current, | |
energyData.gridConsumption.peak, | |
'kW', | |
'#FF5555' | |
)} | |
<h3>Current Grid Consumption</h3> | |
</div> | |
<div className="metric-card"> | |
<div className="grid-status"> | |
<h3>Grid Connection Status</h3> | |
<div className="status-grid"> | |
<div className="status-item"> | |
<span className="status-label">Connection:</span> | |
<span className="status-value status-active">Active</span> | |
</div> | |
<div className="status-item"> | |
<span className="status-label">Peak Capacity:</span> | |
<span className="status-value">{energyData.gridConsumption.peak} kW</span> | |
</div> | |
<div className="status-item"> | |
<span className="status-label">Average Load:</span> | |
<span className="status-value">{energyData.gridConsumption.average} kW</span> | |
</div> | |
<div className="status-item"> | |
<span className="status-label">Current Rate:</span> | |
<span className="status-value">$0.12/kWh</span> | |
</div> | |
<div className="status-item"> | |
<span className="status-label">Peak Hours:</span> | |
<span className="status-value">2:00 PM - 7:00 PM</span> | |
</div> | |
<div className="status-item"> | |
<span className="status-label">Grid Stability:</span> | |
<span className="status-value status-stable">Stable</span> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div className="grid-history"> | |
<h3>Grid Consumption History</h3> | |
{renderBarChart( | |
energyData.gridConsumption.history, | |
'value', | |
'time', | |
'#FF5555' | |
)} | |
</div> | |
<div className="grid-controls"> | |
<h3>Grid Integration Controls</h3> | |
<div className="control-grid"> | |
<div className="control-item"> | |
<span className="control-label">Peak Shaving:</span> | |
<div className="toggle-switch"> | |
<input type="checkbox" id="peak-shaving" className="toggle-input" defaultChecked /> | |
<label htmlFor="peak-shaving" className="toggle-label"></label> | |
</div> | |
</div> | |
<div className="control-item"> | |
<span className="control-label">Load Balancing:</span> | |
<div className="toggle-switch"> | |
<input type="checkbox" id="load-balancing" className="toggle-input" defaultChecked /> | |
<label htmlFor="load-balancing" className="toggle-label"></label> | |
</div> | |
</div> | |
<div className="control-item"> | |
<span className="control-label">Grid Export:</span> | |
<div className="toggle-switch"> | |
<input type="checkbox" id="grid-export" className="toggle-input" /> | |
<label htmlFor="grid-export" className="toggle-label"></label> | |
</div> | |
</div> | |
<div className="control-item"> | |
<span className="control-label">Max Grid Draw:</span> | |
<div className="control-slider-container"> | |
<input type="range" min="100" max="300" defaultValue="250" className="control-slider" /> | |
<span className="slider-value">250 kW</span> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
)} | |
{activeTab === 'demand' && ( | |
<div className="demand-tab"> | |
<div className="demand-metrics"> | |
<div className="metric-card"> | |
{renderGauge( | |
energyData.chargingDemand.current, | |
energyData.chargingDemand.byType.reduce((sum, item) => sum + item.count * item.power, 0), | |
'kW', | |
'#35F2DB' | |
)} | |
<h3>Current Charging Demand</h3> | |
</div> | |
<div className="metric-card"> | |
<div className="charger-status"> | |
<h3>Charger Status</h3> | |
<div className="status-grid"> | |
<div className="status-item"> | |
<span className="status-label">Active Chargers:</span> | |
<span className="status-value">{energyData.chargingDemand.activeChargers}/{energyData.chargingDemand.totalChargers}</span> | |
</div> | |
<div className="status-item"> | |
<span className="status-label">Utilization:</span> | |
<span className="status-value">{Math.round((energyData.chargingDemand.activeChargers / energyData.chargingDemand.totalChargers) * 100)}%</span> | |
</div> | |
<div className="status-item"> | |
<span className="status-label">Average Session:</span> | |
<span className="status-value">42 minutes</span> | |
</div> | |
<div className="status-item"> | |
<span className="status-label">Queue Length:</span> | |
<span className="status-value">3 vehicles</span> | |
</div> | |
<div className="status-item"> | |
<span className="status-label">Wait Time:</span> | |
<span className="status-value">15 minutes</span> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div className="charger-types"> | |
<h3>Charger Types and Usage</h3> | |
<div className="charger-type-grid"> | |
{energyData.chargingDemand.byType.map((type, index) => ( | |
<div key={index} className="charger-type-card"> | |
<div className="type-header"> | |
<h4>{type.type}</h4> | |
<span className="type-power">{type.power} kW</span> | |
</div> | |
<div className="type-count"> | |
<span className="count-value">{type.count}</span> | |
<span className="count-label">Chargers</span> | |
</div> | |
<div className="type-usage"> | |
<div className="usage-bar-container"> | |
<div | |
className="usage-bar" | |
style={{ | |
width: `${(type.count / energyData.chargingDemand.totalChargers) * 100}%`, | |
background: `linear-gradient(90deg, #35F2DB, #35F2DBCC)` | |
}} | |
></div> | |
</div> | |
<span className="usage-label"> | |
{Math.round((type.count / energyData.chargingDemand.totalChargers) * 100)}% of Total | |
</span> | |
</div> | |
<div className="type-power-share"> | |
<div className="power-bar-container"> | |
<div | |
className="power-bar" | |
style={{ | |
width: `${(type.count * type.power / energyData.chargingDemand.byType.reduce((sum, item) => sum + item.count * item.power, 0)) * 100}%`, | |
background: `linear-gradient(90deg, #00E0FF, #00E0FFCC)` | |
}} | |
></div> | |
</div> | |
<span className="power-label"> | |
{Math.round((type.count * type.power / energyData.chargingDemand.byType.reduce((sum, item) => sum + item.count * item.power, 0)) * 100)}% of Power | |
</span> | |
</div> | |
</div> | |
))} | |
</div> | |
</div> | |
<div className="demand-forecast"> | |
<h3>Demand Forecast</h3> | |
{renderBarChart( | |
energyData.chargingDemand.forecast, | |
'value', | |
'time', | |
'#35F2DB' | |
)} | |
</div> | |
</div> | |
)} | |
{activeTab === 'optimization' && ( | |
<div className="optimization-tab"> | |
<div className="optimization-modes"> | |
<h3>Optimization Mode Selection</h3> | |
<div className="mode-cards"> | |
<div | |
className={`mode-card ${optimizationMode === 'balanced' ? 'active' : ''}`} | |
onClick={() => handleOptimizationChange('balanced')} | |
> | |
<div className="mode-icon">⚖️</div> | |
<h4>Balanced</h4> | |
<p>Optimize for balanced performance across all metrics</p> | |
<ul className="mode-benefits"> | |
<li>Moderate cost efficiency</li> | |
<li>Good renewable utilization</li> | |
<li>Reliable charging availability</li> | |
</ul> | |
</div> | |
<div | |
className={`mode-card ${optimizationMode === 'cost' ? 'active' : ''}`} | |
onClick={() => handleOptimizationChange('cost')} | |
> | |
<div className="mode-icon">💰</div> | |
<h4>Cost Optimization</h4> | |
<p>Minimize operational costs and maximize revenue</p> | |
<ul className="mode-benefits"> | |
<li>Reduced grid consumption during peak hours</li> | |
<li>Strategic battery usage</li> | |
<li>Dynamic pricing implementation</li> | |
</ul> | |
</div> | |
<div | |
className={`mode-card ${optimizationMode === 'renewable' ? 'active' : ''}`} | |
onClick={() => handleOptimizationChange('renewable')} | |
> | |
<div className="mode-icon">🌱</div> | |
<h4>Renewable Maximization</h4> | |
<p>Prioritize usage of renewable energy sources</p> | |
<ul className="mode-benefits"> | |
<li>Highest solar utilization</li> | |
<li>Reduced carbon footprint</li> | |
<li>Battery storage optimization</li> | |
</ul> | |
</div> | |
<div | |
className={`mode-card ${optimizationMode === 'demand' ? 'active' : ''}`} | |
onClick={() => handleOptimizationChange('demand')} | |
> | |
<div className="mode-icon">⚡</div> | |
<h4>Demand Response</h4> | |
<p>Focus on meeting charging demand and reducing wait times</p> | |
<ul className="mode-benefits"> | |
<li>Maximum charger availability</li> | |
<li>Reduced wait times</li> | |
<li>Optimized charging speeds</li> | |
</ul> | |
</div> | |
</div> | |
</div> | |
<div className="optimization-results"> | |
<h3>Projected Optimization Results</h3> | |
<div className="results-grid"> | |
<div className="result-card"> | |
<h4>Daily Operational Cost</h4> | |
<div className="result-comparison"> | |
<div className="comparison-item"> | |
<span className="comparison-label">Current:</span> | |
<span className="comparison-value">$480</span> | |
</div> | |
<div className="comparison-item"> | |
<span className="comparison-label">Optimized:</span> | |
<span className="comparison-value optimized"> | |
${optimizationMode === 'cost' ? 380 : optimizationMode === 'balanced' ? 420 : optimizationMode === 'renewable' ? 450 : 460} | |
</span> | |
</div> | |
<div className="comparison-item"> | |
<span className="comparison-label">Savings:</span> | |
<span className="comparison-value savings"> | |
${optimizationMode === 'cost' ? 100 : optimizationMode === 'balanced' ? 60 : optimizationMode === 'renewable' ? 30 : 20} | |
({optimizationMode === 'cost' ? 21 : optimizationMode === 'balanced' ? 13 : optimizationMode === 'renewable' ? 6 : 4}%) | |
</span> | |
</div> | |
</div> | |
</div> | |
<div className="result-card"> | |
<h4>Renewable Energy Usage</h4> | |
<div className="result-comparison"> | |
<div className="comparison-item"> | |
<span className="comparison-label">Current:</span> | |
<span className="comparison-value">42%</span> | |
</div> | |
<div className="comparison-item"> | |
<span className="comparison-label">Optimized:</span> | |
<span className="comparison-value optimized"> | |
{optimizationMode === 'renewable' ? 68 : optimizationMode === 'balanced' ? 55 : optimizationMode === 'cost' ? 48 : 45}% | |
</span> | |
</div> | |
<div className="comparison-item"> | |
<span className="comparison-label">Improvement:</span> | |
<span className="comparison-value savings"> | |
+{optimizationMode === 'renewable' ? 26 : optimizationMode === 'balanced' ? 13 : optimizationMode === 'cost' ? 6 : 3}% | |
</span> | |
</div> | |
</div> | |
</div> | |
<div className="result-card"> | |
<h4>Average Wait Time</h4> | |
<div className="result-comparison"> | |
<div className="comparison-item"> | |
<span className="comparison-label">Current:</span> | |
<span className="comparison-value">15 min</span> | |
</div> | |
<div className="comparison-item"> | |
<span className="comparison-label">Optimized:</span> | |
<span className="comparison-value optimized"> | |
{optimizationMode === 'demand' ? 5 : optimizationMode === 'balanced' ? 8 : optimizationMode === 'cost' ? 12 : 10} min | |
</span> | |
</div> | |
<div className="comparison-item"> | |
<span className="comparison-label">Reduction:</span> | |
<span className="comparison-value savings"> | |
-{optimizationMode === 'demand' ? 10 : optimizationMode === 'balanced' ? 7 : optimizationMode === 'cost' ? 3 : 5} min | |
({optimizationMode === 'demand' ? 67 : optimizationMode === 'balanced' ? 47 : optimizationMode === 'cost' ? 20 : 33}%) | |
</span> | |
</div> | |
</div> | |
</div> | |
<div className="result-card"> | |
<h4>Carbon Footprint</h4> | |
<div className="result-comparison"> | |
<div className="comparison-item"> | |
<span className="comparison-label">Current:</span> | |
<span className="comparison-value">320 kg CO₂/day</span> | |
</div> | |
<div className="comparison-item"> | |
<span className="comparison-label">Optimized:</span> | |
<span className="comparison-value optimized"> | |
{optimizationMode === 'renewable' ? 180 : optimizationMode === 'balanced' ? 240 : optimizationMode === 'cost' ? 280 : 300} kg CO₂/day | |
</span> | |
</div> | |
<div className="comparison-item"> | |
<span className="comparison-label">Reduction:</span> | |
<span className="comparison-value savings"> | |
-{optimizationMode === 'renewable' ? 140 : optimizationMode === 'balanced' ? 80 : optimizationMode === 'cost' ? 40 : 20} kg | |
({optimizationMode === 'renewable' ? 44 : optimizationMode === 'balanced' ? 25 : optimizationMode === 'cost' ? 13 : 6}%) | |
</span> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div className="optimization-actions"> | |
<button className="action-button apply">Apply Optimization Settings</button> | |
<button className="action-button schedule">Schedule Optimization</button> | |
<button className="action-button report">Generate Efficiency Report</button> | |
</div> | |
</div> | |
)} | |
</main> | |
<footer className="simulation-footer"> | |
<div className="footer-info"> | |
<span>Unity Fleet Energy Simulation</span> | |
<span>Powered by Atlas Intelligence</span> | |
</div> | |
<div className="footer-actions"> | |
<button className="footer-button">Export Data</button> | |
<button className="footer-button">Save Scenario</button> | |
</div> | |
</footer> | |
</div> | |
); | |
} | |
export default EnergySimulation; | |