sensorgraph-june23

PHOTO EMBED

Mon Jun 23 2025 11:50:08 GMT+0000 (Coordinated Universal Time)

Saved by @AKAMAY001

import React, { useEffect, useState } from 'react';
import axios from 'axios';
import { Line } from 'react-chartjs-2';
import 'chart.js/auto';

const SensorGraph = () => {
  const [chartData, setChartData] = useState(null);
  const [allData, setAllData] = useState([]);
  const [selectedSensor, setSelectedSensor] = useState('All');
  const [showTimestamps, setShowTimestamps] = useState(true);
  const [showForecast, setShowForecast] = useState(false);
  const [forecastData, setForecastData] = useState([]);

  const [avgRainfall, setAvgRainfall] = useState(0);
  const [currentWaterLevel, setCurrentWaterLevel] = useState(0);
  const [last24hFlow, setLast24hFlow] = useState(0);

  const variable = 'rainfall_mm'; // forecasting variable

  const updateChart = (data, sensorFilter, forecastOverlay = []) => {
    const filteredData = sensorFilter === 'All'
      ? data
      : data.filter(entry => entry.sensor_id === sensorFilter);

    const timestamps = filteredData.map(entry => entry.timestamp);
    const rainfall = filteredData.map(entry => entry.rainfall_mm);
    const waterLevel = filteredData.map(entry => entry.water_level_cm);
    const flowRate = filteredData.map(entry => entry.flow_rate_lps);

    // Stats
    const avgRain = (rainfall.reduce((a, b) => a + b, 0) / rainfall.length || 0).toFixed(2);
    const currWater = waterLevel[waterLevel.length - 1] || 0;

    const now = new Date();
    const past24h = new Date(now.getTime() - 24 * 60 * 60 * 1000);
    const flowLast24h = filteredData.filter(entry => new Date(entry.timestamp) > past24h)
      .map(e => e.flow_rate_lps);
    const sumFlow = (flowLast24h.reduce((a, b) => a + b, 0) / flowLast24h.length || 0).toFixed(2);

    setAvgRainfall(avgRain);
    setCurrentWaterLevel(currWater.toFixed(2));
    setLast24hFlow(sumFlow);

    const datasets = [
      {
        label: 'Rainfall',
        data: rainfall,
        borderColor: 'blue',
        backgroundColor: 'rgba(0, 0, 255, 0.1)',
        tension: 0.4,
        pointRadius: 3,
        fill: true,
        unit: ' mm'
      },
      {
        label: 'Water Level',
        data: waterLevel,
        borderColor: 'green',
        backgroundColor: 'rgba(0, 128, 0, 0.1)',
        tension: 0.4,
        pointRadius: 3,
        fill: true,
        unit: ' cm'
      },
      {
        label: 'Flow Rate',
        data: flowRate,
        borderColor: 'orange',
        backgroundColor: 'rgba(255, 165, 0, 0.1)',
        tension: 0.4,
        pointRadius: 3,
        fill: true,
        unit: ' lps'
      }
    ];

    if (forecastOverlay.length > 0) {
      datasets.push({
        label: 'Forecasted Rainfall',
        data: forecastOverlay.map(d => d.value),
        borderColor: 'purple',
        borderDash: [6, 4],
        backgroundColor: 'rgba(128, 0, 128, 0.1)',
        tension: 0.4,
        pointRadius: 2,
        fill: false,
        unit: ' mm'
      });

      const combinedLabels = [...timestamps, ...forecastOverlay.map(d => d.timestamp)];
      setChartData({ labels: combinedLabels, datasets });
    } else {
      setChartData({ labels: timestamps, datasets });
    }
  };

  const fetchForecast = async () => {
    try {
      const res = await axios.get(`http://localhost:5000/api/forecast?sensor_id=${selectedSensor}&variable=${variable}`);
      setForecastData(res.data.forecast);
      updateChart(allData, selectedSensor, res.data.forecast);
    } catch (err) {
      console.error("Forecast error:", err);
    }
  };

  useEffect(() => {
    axios.get('http://localhost:5000/get-sensor-data')
      .then((res) => setAllData(res.data))
      .catch(err => console.error("Error fetching sensor data:", err));
  }, []);

  useEffect(() => {
    if (allData.length > 0) {
      if (showForecast) {
        fetchForecast();
      } else {
        updateChart(allData, selectedSensor);
      }
    }
  }, [allData, selectedSensor, showForecast]);

  const chartOptions = {
    responsive: true,
    plugins: {
      tooltip: {
        callbacks: {
          label: function (context) {
            const label = context.dataset.label || '';
            const value = context.raw;
            const unit = context.dataset.unit || '';
            return `${label}: ${value}${unit}`;
          }
        }
      },
      legend: {
        position: 'top',
        labels: { font: { size: 14 } }
      },
      title: {
        display: true,
        text: 'SuDS Performance Over Time',
        font: { size: 20 }
      }
    },
    scales: {
      x: {
        ticks: {
          display: showTimestamps,
          maxRotation: 45,
          minRotation: 20,
          font: { size: 12 }
        }
      },
      y: {
        beginAtZero: true,
        ticks: { font: { size: 12 } }
      }
    }
  };

  return (
    <div style={{
      backgroundColor: '#f4f7fa',
      minHeight: '100vh',
      padding: '40px',
      fontFamily: 'Segoe UI, sans-serif'
    }}>
      <div style={{
        backgroundColor: '#ffffff',
        padding: '30px',
        borderRadius: '12px',
        boxShadow: '0 2px 12px rgba(0, 0, 0, 0.1)',
        maxWidth: '1000px',
        margin: '0 auto'
      }}>
        <h1 style={{
          fontSize: '24px',
          marginBottom: '20px',
          textAlign: 'center',
          color: '#333'
        }}>
          SuDS Sensor Data Dashboard
        </h1>

        {/* Controls */}
        <div style={{ textAlign: 'center', marginBottom: '10px' }}>
          <label style={{ fontWeight: 'bold', marginRight: 10 }}>Sensor ID:</label>
          <select
            value={selectedSensor}
            onChange={(e) => {
              setSelectedSensor(e.target.value);
              setForecastData([]);
            }}
            style={{
              padding: '6px 12px',
              fontSize: '14px',
              borderRadius: '6px',
              border: '1px solid #ccc',
              marginRight: '10px'
            }}
          >
            <option value="All">All Sensors</option>
            {[...new Set(allData.map(d => d.sensor_id))].map((id, idx) => (
              <option key={idx} value={id}>{id}</option>
            ))}
          </select>

          <button onClick={() => setShowTimestamps(prev => !prev)}
            style={{
              padding: '6px 12px',
              fontSize: '14px',
              borderRadius: '6px',
              backgroundColor: '#007bff',
              color: '#fff',
              marginRight: '10px',
              border: 'none'
            }}>
            {showTimestamps ? 'Hide Timestamps' : 'Show Timestamps'}
          </button>

          <button onClick={() => setShowForecast(prev => !prev)}
            style={{
              padding: '6px 12px',
              fontSize: '14px',
              borderRadius: '6px',
              backgroundColor: showForecast ? '#6c757d' : '#28a745',
              color: '#fff',
              border: 'none'
            }}>
            {showForecast ? 'Hide Forecast' : 'Show Forecast'}
          </button>
        </div>

        {/* Summary Stats */}
        <div style={{ display: 'flex', justifyContent: 'space-around', marginBottom: '20px' }}>
          <div style={{ background: '#e3f2fd', padding: '10px 20px', borderRadius: '8px' }}>
            <strong>Avg Rainfall:</strong> {avgRainfall} mm
          </div>
          <div style={{ background: '#e8f5e9', padding: '10px 20px', borderRadius: '8px' }}>
            <strong>Current Water Level:</strong> {currentWaterLevel} cm
          </div>
          <div style={{ background: '#fff8e1', padding: '10px 20px', borderRadius: '8px' }}>
            <strong>Last 24h Flow:</strong> {last24hFlow} lps
          </div>
        </div>

        {/* Chart */}
        {chartData ? (
          <Line data={chartData} options={chartOptions} />
        ) : (
          <p style={{ textAlign: 'center', color: '#888' }}>Loading chart...</p>
        )}
      </div>
    </div>
  );
};

export default SensorGraph;
content_copyCOPY