RequestCountersChart.tsx 7,15 ko
Newer Older
/**
 * Graphique montrant l'évolution des compteurs de requêtes
 * - En file d'attente
 * - Traitées (requêtes qui sont passées par le serveur)
 * - Sorties (requêtes qui ont quitté le serveur)
 */
import { useMemo } from 'react';
import { Box, Typography, Paper } from '@mui/material';
import { TrendingUp as TrendingUpIcon, MenuBook as MenuBookIcon } from '@mui/icons-material';
import { Line } from 'react-chartjs-2';
import { useSimulationStore } from '../../store/simulationStore';
import { formatTime } from '../../utils/timeFormat';

export default function RequestCountersChart() {
  const { simulationResults, timeUnit } = useSimulationStore();

  const chartData = useMemo(() => {
    if (!simulationResults?.time_series) return null;

    const { timestamps, cumulative_arrivals_per_queue, cumulative_departures_per_queue, customers_per_queue } = simulationResults.time_series;

    if (!timestamps || timestamps.length === 0) return null;
    if (!cumulative_arrivals_per_queue || !cumulative_departures_per_queue) return null;

    const datasets = [];

    // Couleurs pour les différents types de compteurs
    const colors = {
      waiting: { border: 'rgb(156, 39, 176)', bg: 'rgba(156, 39, 176, 0.5)' }, // Violet/Magenta
      treated: { border: 'rgb(76, 175, 80)', bg: 'rgba(76, 175, 80, 0.5)' },   // Vert
      arrivals: { border: 'rgb(33, 150, 243)', bg: 'rgba(33, 150, 243, 0.5)' }, // Bleu
    };

    // Pour chaque queue (coordinateur + serveurs)
    const queueIds = Object.keys(cumulative_arrivals_per_queue);

    queueIds.forEach((queueId) => {
      const arrivalsData = cumulative_arrivals_per_queue[queueId];
      const departuresData = cumulative_departures_per_queue[queueId];
      const customersData = customers_per_queue[queueId];

      if (!arrivalsData || !departuresData) return;

      // Créer les datasets pour cette queue
      const label = queueId === 'coordinator' ? 'Coordinateur' : queueId.replace('_', ' ');

      // En file d'attente (nombre de clients dans la queue)
      datasets.push({
        label: `${label} - En file`,
        data: customersData,
        borderColor: colors.waiting.border,
        backgroundColor: colors.waiting.bg,
        borderWidth: 0,
        fill: false,
        tension: 0,
        pointRadius: 2,
        pointHoverRadius: 5,
        showLine: false, // Nuage de points uniquement
        yAxisID: 'y',
      });

      // Requêtes traitées (sorties)
      datasets.push({
        label: `${label} - Traitées`,
        data: departuresData,
        borderColor: colors.treated.border,
        backgroundColor: colors.treated.bg,
        borderWidth: 0,
        fill: false,
        tension: 0,
        pointRadius: 2,
        pointHoverRadius: 5,
        showLine: false, // Nuage de points uniquement
        yAxisID: 'y',
      });

      // Requêtes arrivées (entrées)
      datasets.push({
        label: `${label} - Sorties`,
        data: arrivalsData,
        borderColor: colors.arrivals.border,
        backgroundColor: colors.arrivals.bg,
        borderWidth: 0,
        fill: false,
        tension: 0,
        pointRadius: 2,
        pointHoverRadius: 5,
        showLine: false, // Nuage de points uniquement
        yAxisID: 'y',
      });
    });

    return {
      labels: timestamps.map(t => formatTime(t, timeUnit)),
      datasets,
    };
  }, [simulationResults, timeUnit]);

  if (!simulationResults) {
    return null;
  }

  // Si les données des compteurs cumulés ne sont pas disponibles, afficher un message
  if (!chartData) {
    return (
      <Paper sx={{ p: 3 }}>
        <Typography variant="h6" gutterBottom sx={{ fontWeight: 600, mb: 2, display: 'flex', alignItems: 'center', gap: 1 }}>
          <TrendingUpIcon /> Évolution des Compteurs de Requêtes
        </Typography>
        <Box sx={{ textAlign: 'center', py: 8, bgcolor: 'warning.50', borderRadius: 1 }}>
          <Typography variant="body1" color="warning.main" fontWeight={600} gutterBottom>
            Données de compteurs non disponibles
          </Typography>
          <Typography variant="body2" color="text.secondary">
            Veuillez lancer une nouvelle simulation pour voir les compteurs cumulés.
          </Typography>
          <Typography variant="caption" color="text.secondary" sx={{ mt: 1, display: 'block' }}>
            (Les simulations précédentes ne contenaient pas ces données)
          </Typography>
        </Box>
      </Paper>
    );
  }

  const options = {
    responsive: true,
    maintainAspectRatio: false,
    interaction: {
      mode: 'index' as const,
      intersect: false,
    },
    plugins: {
      legend: {
        position: 'top' as const,
        labels: {
          usePointStyle: true,
          padding: 15,
          boxWidth: 6,
        },
      },
      tooltip: {
        callbacks: {
          title: (items: any[]) => {
            if (items.length > 0) {
              return `Temps: ${items[0].label}`;
            }
            return '';
          },
          label: (context: any) => {
            return `${context.dataset.label}: ${context.parsed.y} requêtes`;
          },
        },
      },
    },
    scales: {
      x: {
        title: {
          display: true,
          text: `Temps (${timeUnit})`,
          font: {
            size: 13,
            weight: 'bold' as const,
          },
        },
        ticks: {
          maxTicksLimit: 10,
        },
      },
      y: {
        title: {
          display: true,
          text: 'Nombre de requêtes',
          font: {
            size: 13,
            weight: 'bold' as const,
          },
        },
        beginAtZero: true,
        ticks: {
          precision: 0,
        },
      },
    },
  };

  return (
    <Paper sx={{ p: 3 }}>
      <Typography variant="h6" gutterBottom sx={{ fontWeight: 600, mb: 2, display: 'flex', alignItems: 'center', gap: 1 }}>
        <TrendingUpIcon /> Évolution des Compteurs de Requêtes
      </Typography>

      {/* Graphique */}
      <Box sx={{ height: 450 }}>
        <Line data={chartData} options={options} />
      </Box>

      {/* Explications */}
      <Box sx={{ mt: 3, p: 2, bgcolor: 'info.50', borderRadius: 1 }}>
        <Typography variant="caption" fontWeight={600} gutterBottom display="block" sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>
          <MenuBookIcon fontSize="small" /> Légende:
        </Typography>
        <Typography variant="caption" component="div" sx={{ mb: 0.5 }}>
<strong style={{ color: 'rgb(156, 39, 176)' }}>En file d'attente</strong>: Requêtes en attente de traitement
        </Typography>
        <Typography variant="caption" component="div" sx={{ mb: 0.5 }}>
<strong style={{ color: 'rgb(76, 175, 80)' }}>Traitées</strong>: Requêtes ayant terminé le traitement
        </Typography>
        <Typography variant="caption" component="div" sx={{ mb: 0.5 }}>
<strong style={{ color: 'rgb(33, 150, 243)' }}>Sorties</strong>: Requêtes arrivées dans la file
        </Typography>
        <Typography variant="caption" component="div" sx={{ mt: 1, fontStyle: 'italic' }}>
          Note: Ces courbes correspondent aux résultats 7 et 8 du sujet (compteurs de requêtes).
        </Typography>
      </Box>
    </Paper>
  );
}