Spaces:
Running
Running
Download EnergySimulation.jsx from lattmamb/Vision_Meta0: direct link, hf CLI and curl.
- Browser
- Download file 44.7 kB
-
https://huggingface.co/spaces/lattmamb/Vision_Meta0/resolve/main/EnergySimulation.jsx
- Command line
-
hf download hf://spaces/lattmamb/Vision_Meta0/EnergySimulation.jsx
-
curl -L -o EnergySimulation.jsx https://huggingface.co/spaces/lattmamb/Vision_Meta0/resolve/main/EnergySimulation.jsx
44.7 kB
| 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; | |