Explorar o código

fix for renviews

sutcalag %!s(int64=3) %!d(string=hai) anos
pai
achega
b7c1cef7d1

+ 20 - 0
client/src/hooks/SystemView.tsx

@@ -0,0 +1,20 @@
+
+import { useRef, useEffect } from 'react';
+
+export const useInterval = (callback: Function, delay: number) => {
+  const savedCallback = useRef() as { current: any };
+
+  useEffect(() => {
+    savedCallback.current = callback;
+  });
+
+  useEffect(() => {
+    function tick() {
+      savedCallback.current();
+    }
+    if (delay) {
+      let id = setInterval(tick, delay);
+      return () => clearInterval(id);
+    }
+  }, [delay]);
+}

+ 19 - 18
client/src/pages/system/DataCard.tsx

@@ -2,7 +2,8 @@
 import { FC } from 'react';
 import { useTranslation } from 'react-i18next';
 import { makeStyles } from '@material-ui/core';
-import Progress, { getByteString } from './Progress';
+import Progress from './Progress';
+import { getByteString } from '../../utils/Format';
 import { DataProgressProps, DataSectionProps, DataCardProps } from './Types';
 
 const getStyles = makeStyles(() => ({
@@ -95,14 +96,14 @@ const getStyles = makeStyles(() => ({
 
 const DataSection: FC<DataSectionProps> = (props) => {
   const classes = getStyles();
-  const { title, content } = props;
+  const { titles, contents } = props;
 
   return (
     <div className={classes.sectionRoot}>
       <div className={classes.sectionRow}>
-        {title.map((titleEntry) => <div key={titleEntry} className={classes.sectionHeaderCell}>{titleEntry}</div>)}
+        {titles.map((titleEntry) => <div key={titleEntry} className={classes.sectionHeaderCell}>{titleEntry}</div>)}
       </div>
-      {content.map((contentEntry) => {
+      {contents.map((contentEntry) => {
         return (
           <div key={contentEntry.label} className={classes.sectionRow}>
             <div className={classes.sectionCell}>
@@ -136,8 +137,8 @@ const DataCard: FC<DataCardProps & React.HTMLAttributes<HTMLDivElement>> = (prop
   const { t: commonTrans } = useTranslation();
   const capacityTrans: { [key in string]: string } = commonTrans('capacity');
   const { node, extend } = props;
-  const dataTitle1 = [t('hardwareTitle'), t('valueTitle')];
-  const dataContent1 = [];
+  const hardwareTitle = [t('hardwareTitle'), t('valueTitle')];
+  const hardwareContent = [];
   const infos = node?.infos?.hardware_infos || {};
 
   const {
@@ -150,20 +151,20 @@ const DataCard: FC<DataCardProps & React.HTMLAttributes<HTMLDivElement>> = (prop
   } = infos;
 
   if (extend) {
-    dataContent1.push({ label: t('thCPUCount'), value: cpu });
-    dataContent1.push({
+    hardwareContent.push({ label: t('thCPUCount'), value: cpu });
+    hardwareContent.push({
       label: t('thCPUUsage'), value: <DataProgress percent={cpuUsage / 100} />
     });
-    dataContent1.push({
+    hardwareContent.push({
       label: t('thMemUsage'), value: <DataProgress percent={memoryUsage / memory} desc={getByteString(memoryUsage, memory, capacityTrans)} />
     });
-    dataContent1.push({
+    hardwareContent.push({
       label: t('thDiskUsage'), value: <DataProgress percent={diskUsage / disk} desc={getByteString(diskUsage, disk, capacityTrans)} />
     });
   }
 
-  const dataTitle2 = [t('systemTitle'), t('valueTitle')];
-  const dataContent2 = [];
+  const systemTitle = [t('systemTitle'), t('valueTitle')];
+  const systemContent = [];
   const sysInfos = node?.infos?.system_info || {};
   const {
     system_version: version,
@@ -171,10 +172,10 @@ const DataCard: FC<DataCardProps & React.HTMLAttributes<HTMLDivElement>> = (prop
     created_time: create = '',
     updated_time: update = '',
   } = sysInfos;
-  dataContent2.push({ label: t('thVersion'), value: version });
-  dataContent2.push({ label: t('thDeployMode'), value: mode });
-  dataContent2.push({ label: t('thCreateTime'), value: create });
-  dataContent2.push({ label: t('thUpdateTime'), value: update });
+  systemContent.push({ label: t('thVersion'), value: version });
+  systemContent.push({ label: t('thDeployMode'), value: mode });
+  systemContent.push({ label: t('thCreateTime'), value: create });
+  systemContent.push({ label: t('thUpdateTime'), value: update });
 
   return (
     <div className={classes.root}>
@@ -185,8 +186,8 @@ const DataCard: FC<DataCardProps & React.HTMLAttributes<HTMLDivElement>> = (prop
         </div>
         <div className={classes.ip}>{`${t('thIP')}:${infos?.ip || ''}`}</div>
       </div>
-      {extend && <DataSection title={dataTitle1} content={dataContent1} />}
-      <DataSection title={dataTitle2} content={dataContent2} />
+      {extend && <DataSection titles={hardwareTitle} contents={hardwareContent} />}
+      <DataSection titles={systemTitle} contents={systemContent} />
     </div>
   );
 };

+ 18 - 12
client/src/pages/system/LineChartCard.tsx

@@ -35,10 +35,12 @@ const getStyles = makeStyles(() => ({
 }));
 
 const LineChartCard: FC<LineChartCardProps> = (props) => {
-  const fullHeight = 60;
-  const fullWidth = 300;
-  const round = 5;
-  const step = 25;
+
+  const FULL_HEIGHT = 60;
+  const FULL_WIDTH = 300;
+  const ROUND = 5;
+  const STEP = 25;
+
   const classes = getStyles();
   const { title, value } = props;
   const [displayNodes, setDisplayNodes] = useState<LinceChartNode[]>([]);
@@ -53,11 +55,13 @@ const LineChartCard: FC<LineChartCardProps> = (props) => {
   const nodes = useRef<LinceChartNode[]>([]);
 
   useEffect(() => {
+    // show at most 10 nodes. so remove the earliest node when nodes exceed 10
     if (nodes.current.length > 9) {
       nodes.current.shift();
     }
 
     if (value && max.current) {
+      // calculate the y-axis max scale
       let currentMax = max.current;
       if (value > max.current) {
         const pow = Math.ceil(Math.log10(value));
@@ -65,6 +69,7 @@ const LineChartCard: FC<LineChartCardProps> = (props) => {
         max.current = currentMax;
       }
 
+      // generate a new node and save in ref
       if (nodes.current) {
         const newNodes = nodes.current.slice(0);
         const newNode = {
@@ -75,6 +80,7 @@ const LineChartCard: FC<LineChartCardProps> = (props) => {
         newNodes.push(newNode);
         nodes.current = newNodes;
 
+        // refresh nodes for display when mouse is not hovering on the chart
         if (!isHover.current) {
           setDisplayNodes(newNodes);
           setCurrentNode(newNode);
@@ -86,25 +92,25 @@ const LineChartCard: FC<LineChartCardProps> = (props) => {
   return (
     nodes.current.length ? (
       <BaseCard title={title} content={`${Math.round(currentNode.value)}ms`} desc={new Date(currentNode.timestamp).toLocaleString()}>
-        <svg className={classes.root} onMouseEnter={() => isHover.current = true} onMouseLeave={() => isHover.current = false} width={fullWidth} height={fullHeight} viewBox={`0 5 ${fullWidth} ${fullHeight}`} fill="white" xmlns="http://www.w3.org/2000/svg">
+        <svg className={classes.root} onMouseEnter={() => isHover.current = true} onMouseLeave={() => isHover.current = false} width={FULL_WIDTH} height={FULL_HEIGHT} viewBox={`0 5 ${FULL_WIDTH} ${FULL_HEIGHT}`} fill="white" xmlns="http://www.w3.org/2000/svg">
           {
             displayNodes.map((node, index) => {
-              const x1 = fullWidth - (displayNodes.length - index + 1) * step;
-              const y1 = node.percent * .5 + round * 2;
+              const x1 = FULL_WIDTH - (displayNodes.length - index + 1) * STEP;
+              const y1 = node.percent * .5 + ROUND * 2;
 
               let line = null;
               if (index < displayNodes.length - 1) {
-                const x2 = fullWidth - (displayNodes.length - index) * step;
-                const y2 = displayNodes[index + 1]['percent'] * .5 + round * 2;
+                const x2 = FULL_WIDTH - (displayNodes.length - index) * STEP;
+                const y2 = displayNodes[index + 1]['percent'] * .5 + ROUND * 2;
                 line = <line x1={x1} y1={y1} x2={x2} y2={y2} stroke="#06AFF2" />;
               }
               return (
                 <g key={`${node.value}${index}`}>
                   {line}
                   <g className={classes.ycoord} onMouseOver={() => { setCurrentNode(node) }}>
-                    <circle cx={x1} cy={y1} r={round} fill="white" stroke="#06AFF2" />
-                    <rect opacity="0" x={x1 - round} y={0} width={round * 2} height={fullHeight} fill="#E9E9ED" />
-                    <line opacity="0" x1={x1} y1={0} x2={x1} y2={fullWidth} strokeWidth="2" stroke="#06AFF2" strokeDasharray="2.5" />
+                    <circle cx={x1} cy={y1} r={ROUND} fill="white" stroke="#06AFF2" />
+                    <rect opacity="0" x={x1 - ROUND} y={0} width={ROUND * 2} height={FULL_HEIGHT} fill="#E9E9ED" />
+                    <line opacity="0" x1={x1} y1={0} x2={x1} y2={FULL_WIDTH} strokeWidth="2" stroke="#06AFF2" strokeDasharray="2.5" />
                   </g>
                 </g>
               )

+ 16 - 15
client/src/pages/system/MiniTopology.tsx

@@ -52,29 +52,30 @@ const capitalize = (s: string) => {
 const MiniTopo: FC<MiniTopoProps> = (props) => {
   const classes = getStyles();
   const { selectedCord, selectedChildNode, setCord } = props;
-  const width = 400;                // width for svg
-  const height = 400;               // height for svg
-  const line = 80;                // line lenght from lv2 node
-  const angle = 10;                // angle offset for lv2 node
-  const r1 = 45;                    // root node radius
-  const r2 = 30;                    // lv1 node radius
-  const w3 = 20;                    // width of child rect
 
-  const childNodeCenterX = width / 2 + line * Math.cos(angle * Math.PI / 180);
-  const childNodeCenterY = height / 2 + line * Math.sin(angle * Math.PI / 180);
+  const WIDTH = 400;                // width for svg
+  const HEIGHT = 400;               // height for svg
+  const LINE = 80;                // line lenght from lv2 node
+  const ANGLE = 10;                // angle offset for lv2 node
+  const R1 = 45;                    // root node radius
+  const R2 = 30;                    // lv1 node radius
+  const W3 = 20;                    // width of child rect
+
+  const childNodeCenterX = WIDTH / 2 + LINE * Math.cos(ANGLE * Math.PI / 180);
+  const childNodeCenterY = HEIGHT / 2 + LINE * Math.sin(ANGLE * Math.PI / 180);
 
   return (
-    <svg className={classes.container} width={width} height={height} viewBox={`0 0 ${width} ${height}`} xmlns="http://www.w3.org/2000/svg">
+    <svg className={classes.container} width={WIDTH} height={HEIGHT} viewBox={`0 0 ${WIDTH} ${HEIGHT}`} xmlns="http://www.w3.org/2000/svg">
       <rect width="100%" height="100%" fill="white" />
-      <line x1={`${width / 3}`} y1={`${height / 3}`} x2={childNodeCenterX} y2={childNodeCenterY} stroke="#06AFF2" />
+      <line x1={`${WIDTH / 3}`} y1={`${HEIGHT / 3}`} x2={childNodeCenterX} y2={childNodeCenterY} stroke="#06AFF2" />
       <g className={classes.childNode} onClick={() => { setCord(null) }}>
-        <circle cx={`${width / 3}`} cy={`${height / 3}`} r={r1} fill="white" stroke="#06AFF2" />
-        <text fontFamily="Roboto" alignmentBaseline="middle" textAnchor="middle" fill="#06AFF2" fontWeight="700" fontSize="12" x={`${width / 3}`} y={`${height / 3}`}>{selectedCord ? capitalize(selectedCord.infos?.name) : ''}</text>
+        <circle cx={`${WIDTH / 3}`} cy={`${HEIGHT / 3}`} r={R1} fill="white" stroke="#06AFF2" />
+        <text fontFamily="Roboto" alignmentBaseline="middle" textAnchor="middle" fill="#06AFF2" fontWeight="700" fontSize="12" x={`${WIDTH / 3}`} y={`${HEIGHT / 3}`}>{selectedCord ? capitalize(selectedCord.infos?.name) : ''}</text>
       </g>
       <g>
         <svg width="60" height="60" viewBox="0 0 60 60" x={childNodeCenterX - 30} y={childNodeCenterY - 30}>
-          <circle cx={r2} cy={r2} r={r2} fill="#06AFF2" stroke="white" />
-          <rect className="selected" x={r2 - w3 / 2} y={r2 - w3 / 2} width={w3} height={w3} fill="white" />
+          <circle cx={R2} cy={R2} r={R2} fill="#06AFF2" stroke="white" />
+          <rect className="selected" x={R2 - W3 / 2} y={R2 - W3 / 2} width={W3} height={W3} fill="white" />
         </svg>
         <text fontFamily="Roboto" textAnchor="middle" fill="#82838E" fontSize="12" x={childNodeCenterX} y={childNodeCenterY + 50}>{`${selectedChildNode ? selectedChildNode.infos?.name : ''}`}</text>
       </g>

+ 23 - 19
client/src/pages/system/NodeListView.tsx

@@ -1,4 +1,4 @@
-import { FC, useState } from 'react';
+import { FC, useState, useEffect } from 'react';
 import { useTranslation } from 'react-i18next';
 import { makeStyles, Theme } from '@material-ui/core';
 import KeyboardArrowDown from '@material-ui/icons/KeyboardArrowDown';
@@ -6,7 +6,7 @@ import { DataGrid } from '@mui/x-data-grid';
 import { useNavigationHook } from '../../hooks/Navigation';
 import { ALL_ROUTER_TYPES } from '../../router/Types';
 import MiniTopo from './MiniTopology';
-import { getByteString } from './Progress';
+import { getByteString } from '../../utils/Format';
 import DataCard from './DataCard';
 import { NodeListViewProps, Node } from './Types';
 
@@ -101,24 +101,28 @@ const NodeListView: FC<NodeListViewProps> = (props) => {
       flex: 1,
     },
   ];
-  if (selectedCord) {
-    const connectedIds = selectedCord.connected.map(node => node.connected_identifier);
-    rows = [];
-    childNodes.forEach(node => {
-      if (connectedIds.includes(node.identifier)) {
-        const dataRow = {
-          id: node?.identifier,
-          ip: node?.infos?.hardware_infos.ip,
-          cpuCore: node?.infos?.hardware_infos.cpu_core_count,
-          cpuUsage: node?.infos?.hardware_infos.cpu_core_usage,
-          diskUsage: getByteString(node?.infos?.hardware_infos.disk_usage, node?.infos?.hardware_infos.disk, capacityTrans),
-          memUsage: getByteString(node?.infos?.hardware_infos.memory_usage, node?.infos?.hardware_infos.memory, capacityTrans),
-          name: node?.infos?.name,
+
+  useEffect(() => {
+    if (selectedCord) {
+      const connectedIds = selectedCord.connected.map(node => node.connected_identifier);
+      rows = [];
+      childNodes.forEach(node => {
+        if (connectedIds.includes(node.identifier)) {
+          const dataRow = {
+            id: node?.identifier,
+            ip: node?.infos?.hardware_infos.ip,
+            cpuCore: node?.infos?.hardware_infos.cpu_core_count,
+            cpuUsage: node?.infos?.hardware_infos.cpu_core_usage,
+            diskUsage: getByteString(node?.infos?.hardware_infos.disk_usage, node?.infos?.hardware_infos.disk, capacityTrans),
+            memUsage: getByteString(node?.infos?.hardware_infos.memory_usage, node?.infos?.hardware_infos.memory, capacityTrans),
+            name: node?.infos?.name,
+          }
+          rows.push(dataRow);
         }
-        rows.push(dataRow);
-      }
-    })
-  }
+      })
+    }
+  }, [selectedCord, childNodes]);
+
 
   return (
     <div className={classes.root}>

+ 0 - 30
client/src/pages/system/Progress.tsx

@@ -3,36 +3,6 @@ import { FC } from 'react';
 import { makeStyles } from '@material-ui/core';
 import { ProgressProps } from './Types';
 
-export const getByteString = (value1: number, value2: number, capacityTrans: { [key in string]: string }) => {
-  if (!value1 || !value2) return `0${capacityTrans.b}`;
-  const power = Math.round(Math.log(value1) / Math.log(1024));
-  let unit = '';
-  switch (power) {
-    case 1:
-      unit = capacityTrans.kb;
-      break;
-    case 2:
-      unit = capacityTrans.mb;
-      break;
-    case 3:
-      unit = capacityTrans.gb;
-      break;
-    case 4:
-      unit = capacityTrans.tb;
-      break;
-    case 5:
-      unit = capacityTrans.pb;
-      break;
-    default:
-      unit = capacityTrans.b;
-      break;
-  }
-  const byteValue1 = value1 / (1024 ** power);
-  const byteValue2 = value2 / (1024 ** power);
-
-  return `${(byteValue1).toFixed(2)}/${(byteValue2).toFixed(2)} ${unit}`;
-}
-
 const getStyles = makeStyles(() => ({
   root: {
     height: 'auto',

+ 5 - 19
client/src/pages/system/SystemView.tsx

@@ -5,6 +5,7 @@ import clsx from 'clsx';
 import { useNavigationHook } from '../../hooks/Navigation';
 import { ALL_ROUTER_TYPES } from '../../router/Types';
 import { MilvusHttp } from '../../http/Milvus';
+import { useInterval } from '../../hooks/SystemView';
 import Topo from './Topology';
 import NodeListView from './NodeListView';
 import LineChartCard from './LineChartCard';
@@ -68,8 +69,10 @@ const parseJson = (jsonData: any) => {
 
   jsonData?.response?.nodes_info.forEach((node: any) => {
     const type = node?.infos?.type;
+    // coordinator node
     if (type.includes("Coord")) {
       nodes.push(node);
+      // other nodes
     } else {
       childNodes.push(node);
     }
@@ -83,30 +86,13 @@ const parseJson = (jsonData: any) => {
   return { nodes, childNodes, system };
 }
 
-const useInterval = (callback: Function, delay: number) => {
-  const savedCallback = useRef() as { current: any };
-
-  useEffect(() => {
-    savedCallback.current = callback;
-  });
-
-  useEffect(() => {
-    function tick() {
-      savedCallback.current();
-    }
-    if (delay) {
-      let id = setInterval(tick, delay);
-      return () => clearInterval(id);
-    }
-  }, [delay]);
-}
 
 const SystemView: any = () => {
   useNavigationHook(ALL_ROUTER_TYPES.SYSTEM);
   const { t } = useTranslation('systemView');
 
   const classes = getStyles();
-  const interval = 10000;
+  const INTERVAL = 10000;
 
   const [data, setData] = useState<{ nodes: any, childNodes: any, system: any }>({ nodes: [], childNodes: [], system: {} });
   const [selectedNode, setNode] = useState<any>();
@@ -118,7 +104,7 @@ const SystemView: any = () => {
       const res = await MilvusHttp.getMetrics();
       setData(parseJson(res));
     }
-  }, interval);
+  }, INTERVAL);
 
   useEffect(() => {
     async function fetchData() {

+ 2 - 2
client/src/pages/system/Types.ts

@@ -41,8 +41,8 @@ export interface DataProgressProps {
 }
 
 export interface DataSectionProps {
-  title: string[],
-  content: { label: string, value: string }[],
+  titles: string[],
+  contents: { label: string, value: string }[],
 }
 
 export interface DataCardProps {

+ 31 - 0
client/src/utils/Format.ts

@@ -142,3 +142,34 @@ export const getCreateFieldType = (config: Field): CreateFieldType => {
 
 // Trim the address
 export const formatAddress = (address: string): string => address.trim();
+
+// generate a sting like 20.22/98.33MB with proper unit
+export const getByteString = (value1: number, value2: number, capacityTrans: { [key in string]: string }) => {
+  if (!value1 || !value2) return `0${capacityTrans.b}`;
+  const power = Math.round(Math.log(value1) / Math.log(1024));
+  let unit = '';
+  switch (power) {
+    case 1:
+      unit = capacityTrans.kb;
+      break;
+    case 2:
+      unit = capacityTrans.mb;
+      break;
+    case 3:
+      unit = capacityTrans.gb;
+      break;
+    case 4:
+      unit = capacityTrans.tb;
+      break;
+    case 5:
+      unit = capacityTrans.pb;
+      break;
+    default:
+      unit = capacityTrans.b;
+      break;
+  }
+  const byteValue1 = value1 / (1024 ** power);
+  const byteValue2 = value2 / (1024 ** power);
+
+  return `${(byteValue1).toFixed(2)}/${(byteValue2).toFixed(2)} ${unit}`;
+}