MetricsComparisonChart.tsx 3,91 ko
Newer Older
/**
 * Metrics comparison chart - shows L and W for each queue.
 */
import {
  Chart as ChartJS,
  CategoryScale,
  LinearScale,
  BarElement,
  Title,
  Tooltip,
  Legend,
} from 'chart.js';
import { Bar } from 'react-chartjs-2';
import type { SimulationResults, NetworkAnalytics } from '../../types/simulation';

ChartJS.register(CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend);

interface MetricsComparisonChartProps {
  simulationResults?: SimulationResults | null;
  analyticalResults?: NetworkAnalytics | null;
  metric: 'L' | 'W';
}

export default function MetricsComparisonChart({
  simulationResults,
  analyticalResults,
  metric,
}: MetricsComparisonChartProps) {
  if (!simulationResults && !analyticalResults) {
    return null;
  }

  const labels: string[] = ['Coordinateur'];
  const simulationData: number[] = [];
  const analyticalData: number[] = [];

  // Coordinator data
  if (simulationResults && metric === 'W') {
    simulationData.push(simulationResults.coordinator_stats.average_system_time);
  }
  if (analyticalResults) {
    if (metric === 'L') {
      analyticalData.push(analyticalResults.coordinator.average_customers ?? 0);
    } else {
      analyticalData.push(analyticalResults.coordinator.average_time ?? 0);
    }
  }

  // Server data
  if (simulationResults) {
    Object.entries(simulationResults.server_stats).forEach(([serverId, stats]) => {
      const serverNum = serverId.replace('server_', '');
      labels.push(`Serveur ${serverNum}`);
      if (metric === 'W') {
        simulationData.push(stats.average_system_time);
      }
    });
  }

  if (analyticalResults) {
    Object.entries(analyticalResults.servers).forEach(([_, analytics]) => {
      if (simulationData.length <= analyticalData.length) {
        const serverNum = labels.length;
        labels.push(`Serveur ${serverNum}`);
      }
      if (metric === 'L') {
        analyticalData.push(analytics.average_customers ?? 0);
      } else {
        analyticalData.push(analytics.average_time ?? 0);
      }
    });
  }

  const data = {
    labels,
    datasets: [
      ...(simulationResults && metric === 'W'
        ? [
            {
              label: 'Simulation',
              data: simulationData,
              backgroundColor: 'rgba(16, 185, 129, 0.7)',
              borderColor: 'rgba(16, 185, 129, 1)',
              borderWidth: 1,
            },
          ]
        : []),
      ...(analyticalResults
        ? [
            {
              label: 'Analytique (Jackson)',
              data: analyticalData,
              backgroundColor: 'rgba(245, 158, 11, 0.7)',
              borderColor: 'rgba(245, 158, 11, 1)',
              borderWidth: 1,
            },
          ]
        : []),
    ],
  };

  const metricLabel = metric === 'L' ? 'Nombre moyen de clients (L)' : 'Temps moyen dans le système (W)';
  const yAxisLabel = metric === 'L' ? 'Clients' : 'Unités de temps';

  const options = {
    responsive: true,
    maintainAspectRatio: false,
    plugins: {
      legend: {
        position: 'top' as const,
      },
      title: {
        display: true,
        text: metricLabel,
        font: {
          size: 14,
          weight: 'bold' as const,
        },
      },
      tooltip: {
        callbacks: {
          label: function (context: any) {
            let label = context.dataset.label || '';
            if (label) {
              label += ': ';
            }
            label += context.parsed.y.toFixed(4);
            return label;
          },
        },
      },
    },
    scales: {
      y: {
        beginAtZero: true,
        title: {
          display: true,
          text: yAxisLabel,
        },
      },
      x: {
        title: {
          display: true,
          text: 'Files d\'attente',
        },
      },
    },
  };

  return (
    <div className="bg-white border border-gray-200 rounded-lg p-4">
      <div className="h-80">
        <Bar data={data} options={options} />
      </div>
    </div>
  );
}