add gpuusage的逻辑

Change-Id: Ie1634a7d58ab94b05a3ebd7de87d362ca9be2286
diff --git a/Merge/front/src/api/posts_trm.js b/Merge/front/src/api/posts_trm.js
index d6303a7..8f7e877 100644
--- a/Merge/front/src/api/posts_trm.js
+++ b/Merge/front/src/api/posts_trm.js
@@ -209,4 +209,30 @@
   }
   console.log('Normalized sys cost list:', list)
   return list
+}
+
+/**
+ * 获取GPU使用情况数据
+ * GET /gpu-usage
+ * @param {number} limit 获取记录条数,默认100条
+ * @returns Promise<[ {id, gpu_id, gpu_usage, gpu_memory_usage, timestamp}, … ]>
+ */
+export async function fetchGpuUsage(limit = 100) {
+  const token = localStorage.getItem('token')
+  const res = await fetch(`http://10.126.59.25:8082/gpu-usage?limit=${limit}`, {
+    method: 'GET',
+    headers: { 
+      'Content-Type': 'application/json',
+      'Authorization': `Bearer ${token}`
+    }
+  })
+  if (!res.ok) throw new Error(`fetchGpuUsage: ${res.status}`)
+  const json = await res.json()
+  console.log('fetchGpuUsage response:', json)
+  
+  if (json.success === false) {
+    throw new Error(json.message || 'Failed to fetch GPU usage')
+  }
+  
+  return json.data?.records || []
 }
\ No newline at end of file
diff --git a/Merge/front/src/components/PerformanceLogs.js b/Merge/front/src/components/PerformanceLogs.js
index f87ef7a..67518de 100644
--- a/Merge/front/src/components/PerformanceLogs.js
+++ b/Merge/front/src/components/PerformanceLogs.js
@@ -3,16 +3,19 @@
   ResponsiveContainer, LineChart, Line,
   XAxis, YAxis, Tooltip, CartesianGrid, Legend
 } from 'recharts';
-import { fetchSysCost } from '../api/posts_trm';
+import { fetchSysCost, fetchGpuUsage } from '../api/posts_trm';
 
 function PerformanceLogs({ userId }) {
   const [data, setData] = useState([]);
+  const [gpuData, setGpuData] = useState([]);
   const [loading, setLoading] = useState(true);
   const [unauthorized, setUnauthorized] = useState(false);
 
   useEffect(() => {
     setLoading(true);
-    fetchSysCost(userId)
+    
+    // 获取系统性能数据
+    const fetchSystemData = fetchSysCost(userId)
       .then(result => {
         // 检查是否是权限错误
         if (result && result.status === 'error' && result.message === 'Unauthorized') {
@@ -49,10 +52,59 @@
           setUnauthorized(true);
           setData([]);
         }
+      });
+
+    // 获取GPU数据
+    const fetchGpuData = fetchGpuUsage(100)
+      .then(result => {
+        console.log('GPU data:', result);
+        const processedData = processGpuData(result);
+        setGpuData(processedData);
       })
+      .catch(err => {
+        console.error('fetchGpuUsage error:', err);
+      });
+
+    Promise.all([fetchSystemData, fetchGpuData])
       .finally(() => setLoading(false));
   }, [userId]);
 
+  // 处理GPU数据,按时间戳分组
+  const processGpuData = (rawData) => {
+    const timeMap = new Map();
+    
+    rawData.forEach(record => {
+      const timestamp = new Date(record.timestamp).getTime();
+      if (!timeMap.has(timestamp)) {
+        timeMap.set(timestamp, { timestamp });
+      }
+      const timeEntry = timeMap.get(timestamp);
+      timeEntry[`gpu${record.gpu_id}_usage`] = record.gpu_usage;
+      timeEntry[`gpu${record.gpu_id}_memory`] = record.gpu_memory_usage;
+    });
+    
+    return Array.from(timeMap.values()).sort((a, b) => a.timestamp - b.timestamp);
+  };
+
+  // 获取GPU颜色
+  const getGpuColors = () => {
+    return ['#8884d8', '#82ca9d', '#ffc658', '#ff7300', '#8dd1e1', '#d084d0', '#ffb347', '#87ceeb'];
+  };
+
+  // 获取唯一的GPU ID列表
+  const getGpuIds = () => {
+    const ids = new Set();
+    gpuData.forEach(entry => {
+      Object.keys(entry).forEach(key => {
+        if (key.includes('gpu') && key.includes('_usage')) {
+          const gpuId = key.replace('_usage', '').replace('gpu', '');
+          ids.add(gpuId);
+        }
+      });
+    });
+    return Array.from(ids).sort((a, b) => parseInt(a) - parseInt(b));
+  };
+
   if (loading) {
     return (
       <section className="dashboard-performance">
@@ -114,6 +166,70 @@
 
   return (
     <section className="dashboard-performance">
+      {/* GPU使用率图表 */}
+      <div style={{ marginBottom: '30px' }}>
+        <h3>GPU 使用率</h3>
+        <ResponsiveContainer width="100%" height={300}>
+          <LineChart data={gpuData} margin={{ top: 20, right: 30, left: 20, bottom: 5 }}>
+            <CartesianGrid strokeDasharray="3 3" />
+            <XAxis
+              dataKey="timestamp"
+              type="number"
+              domain={['dataMin', 'dataMax']}
+              tickFormatter={ts => new Date(ts).toLocaleTimeString()}
+            />
+            <YAxis domain={[0, 100]} label={{ value: '使用率 (%)', angle: -90, position: 'insideLeft' }} />
+            <Tooltip 
+              formatter={(val, name) => [`${val?.toFixed(2)}%`, name]}
+              labelFormatter={ts => new Date(ts).toLocaleString()}
+            />
+            <Legend />
+            {getGpuIds().map((gpuId, index) => (
+              <Line
+                key={`gpu${gpuId}_usage`}
+                type="monotone"
+                dataKey={`gpu${gpuId}_usage`}
+                stroke={getGpuColors()[index % getGpuColors().length]}
+                name={`GPU ${gpuId} 使用率`}
+                connectNulls={false}
+              />
+            ))}
+          </LineChart>
+        </ResponsiveContainer>
+      </div>
+
+      {/* GPU内存占用图表 */}
+      <div style={{ marginBottom: '30px' }}>
+        <h3>GPU 内存占用</h3>
+        <ResponsiveContainer width="100%" height={300}>
+          <LineChart data={gpuData} margin={{ top: 20, right: 30, left: 20, bottom: 5 }}>
+            <CartesianGrid strokeDasharray="3 3" />
+            <XAxis
+              dataKey="timestamp"
+              type="number"
+              domain={['dataMin', 'dataMax']}
+              tickFormatter={ts => new Date(ts).toLocaleTimeString()}
+            />
+            <YAxis domain={[0, 'auto']} label={{ value: '内存 (MB)', angle: -90, position: 'insideLeft' }} />
+            <Tooltip 
+              formatter={(val, name) => [`${val?.toFixed(2)} MB`, name]}
+              labelFormatter={ts => new Date(ts).toLocaleString()}
+            />
+            <Legend />
+            {getGpuIds().map((gpuId, index) => (
+              <Line
+                key={`gpu${gpuId}_memory`}
+                type="monotone"
+                dataKey={`gpu${gpuId}_memory`}
+                stroke={getGpuColors()[index % getGpuColors().length]}
+                name={`GPU ${gpuId} 内存`}
+                connectNulls={false}
+              />
+            ))}
+          </LineChart>
+        </ResponsiveContainer>
+      </div>
+
       {/* 响应时间图表 */}
       <div style={{ marginBottom: '30px' }}>
         <h3>响应时间</h3>