使用web worker优化计算
This commit is contained in:
		
							parent
							
								
									dd56a5b202
								
							
						
					
					
						commit
						346ed68356
					
				
							
								
								
									
										147
									
								
								src/views/GroupEvolution/components/graphWorker.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										147
									
								
								src/views/GroupEvolution/components/graphWorker.js
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,147 @@
 | 
			
		|||
// 图表计算Web Worker
 | 
			
		||||
 | 
			
		||||
// 处理力导向布局计算
 | 
			
		||||
self.onmessage = function(e) {
 | 
			
		||||
  const { type, data } = e.data;
 | 
			
		||||
    
 | 
			
		||||
  let result;
 | 
			
		||||
    
 | 
			
		||||
  switch(type) {
 | 
			
		||||
    case 'CLUSTER_ANALYZE':
 | 
			
		||||
      result = processClusterAnalyze(data);
 | 
			
		||||
      break;
 | 
			
		||||
    case 'HIGHLIGHT_NODES':
 | 
			
		||||
      result = processHighlightNodes(data);
 | 
			
		||||
      break;
 | 
			
		||||
    case 'HIGHLIGHT_LINKS':
 | 
			
		||||
      result = processHighlightLinks(data);
 | 
			
		||||
      break;
 | 
			
		||||
    case 'ANOMALOUS_GROUP_PROCESS':
 | 
			
		||||
      result = processAnomalousGroup(data);
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
      self.postMessage({
 | 
			
		||||
        success: false,
 | 
			
		||||
        type,
 | 
			
		||||
        error: `Unknown task type: ${type}`
 | 
			
		||||
      });
 | 
			
		||||
      return;
 | 
			
		||||
  }
 | 
			
		||||
    
 | 
			
		||||
  self.postMessage({
 | 
			
		||||
    success: true,
 | 
			
		||||
    type,
 | 
			
		||||
    result
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// 处理聚类分析
 | 
			
		||||
function processClusterAnalyze(data) {
 | 
			
		||||
  const { nodes, storeId } = data;
 | 
			
		||||
  const clusterNodesMap = new Map();
 | 
			
		||||
  
 | 
			
		||||
  // 创建cluster到nodes的映射
 | 
			
		||||
  nodes.forEach(node => {
 | 
			
		||||
    const cluster = parseInt(node.type);
 | 
			
		||||
    if (!clusterNodesMap.has(cluster)) {
 | 
			
		||||
      clusterNodesMap.set(cluster, []);
 | 
			
		||||
    }
 | 
			
		||||
    clusterNodesMap.get(cluster).push(node);
 | 
			
		||||
  });
 | 
			
		||||
  
 | 
			
		||||
  let colorMap = {
 | 
			
		||||
    0: "75,241,184", // 绿色
 | 
			
		||||
    1: "250,222,37", // 黄色
 | 
			
		||||
    6: "69,192,242" // 蓝色
 | 
			
		||||
  };
 | 
			
		||||
  
 | 
			
		||||
  const processedNodes = [];
 | 
			
		||||
  const groups = [];
 | 
			
		||||
  
 | 
			
		||||
  // 处理节点颜色和分组
 | 
			
		||||
  clusterNodesMap.forEach((nodes, cluster) => {
 | 
			
		||||
    const color = colorMap[cluster];
 | 
			
		||||
    
 | 
			
		||||
    nodes.forEach(node => {
 | 
			
		||||
      const processedNode = { ...node };
 | 
			
		||||
      processedNode.fillColor = color;
 | 
			
		||||
      processedNode.color = color;
 | 
			
		||||
      processedNodes.push(processedNode);
 | 
			
		||||
    });
 | 
			
		||||
    
 | 
			
		||||
    // 非异常群体模块需要添加分组
 | 
			
		||||
    if (storeId !== "anomalousGroup") {
 | 
			
		||||
      groups.push({
 | 
			
		||||
        nodes: nodes.map(n => n.id),
 | 
			
		||||
        options: {
 | 
			
		||||
          shape: "polygon",
 | 
			
		||||
          color: color,
 | 
			
		||||
          alpha: 0.2
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
  
 | 
			
		||||
  return { nodes: processedNodes, groups };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 处理节点高亮
 | 
			
		||||
function processHighlightNodes(data) {
 | 
			
		||||
  const { nodes, newNodes } = data;
 | 
			
		||||
  const newNodeIds = new Set(newNodes.map(node => node.name));
 | 
			
		||||
  
 | 
			
		||||
  return nodes.map(node => ({
 | 
			
		||||
    ...node,
 | 
			
		||||
    selected: newNodeIds.has(node.id)
 | 
			
		||||
  }));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 处理连线高亮
 | 
			
		||||
function processHighlightLinks(data) {
 | 
			
		||||
  const { links, newLinks } = data;
 | 
			
		||||
  const newLinkSet = new Set(newLinks.map(link => `${link.source}-${link.target}`));
 | 
			
		||||
  
 | 
			
		||||
  return links.map(link => ({
 | 
			
		||||
    ...link,
 | 
			
		||||
    selected: newLinkSet.has(`${link.source.id}-${link.target.id}`)
 | 
			
		||||
  }));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 处理异常群体分析
 | 
			
		||||
function processAnomalousGroup(data) {
 | 
			
		||||
  const { nodes, currentUtc, abnormalData } = data;
 | 
			
		||||
  const abnormalGroupConfigs = [
 | 
			
		||||
    { timeThreshold: "2024-06-19T08:57:55Z", groupKey: "groupA", color: "85, 125, 15" }, // 绿色
 | 
			
		||||
    { timeThreshold: "2024-06-19T10:58:03Z", groupKey: "groupB", color: "125, 114, 15" }, // 黄色
 | 
			
		||||
    { timeThreshold: "2024-06-19T12:58:04Z", groupKey: "groupC", color: "15, 106, 125" } // 蓝色
 | 
			
		||||
  ];
 | 
			
		||||
  
 | 
			
		||||
  // 计算当前应激活的异常组配置
 | 
			
		||||
  const activeConfigs = abnormalGroupConfigs.filter(config => currentUtc >= config.timeThreshold);
 | 
			
		||||
  
 | 
			
		||||
  // 优化异常节点检测 - 预处理异常ID集合
 | 
			
		||||
  const abnormalMap = new Map();
 | 
			
		||||
  activeConfigs.forEach(config => {
 | 
			
		||||
    const abnormalIds = abnormalData[config.groupKey];
 | 
			
		||||
    if (abnormalIds) {
 | 
			
		||||
      abnormalMap.set(config.groupKey, new Set(abnormalIds));
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
  
 | 
			
		||||
  // 单次遍历处理所有异常节点
 | 
			
		||||
  return nodes.map(node => {
 | 
			
		||||
    const processedNode = { ...node };
 | 
			
		||||
    
 | 
			
		||||
    // 检查节点是否属于任何激活的异常组
 | 
			
		||||
    for (const config of activeConfigs) {
 | 
			
		||||
      const abnormalIdsSet = abnormalMap.get(config.groupKey);
 | 
			
		||||
      // 如果节点在异常组中,则设置对应的颜色
 | 
			
		||||
      if (abnormalIdsSet && abnormalIdsSet.has(node.id)) {
 | 
			
		||||
        processedNode.fillColor = config.color;
 | 
			
		||||
        break; // 一旦找到匹配的异常组,就不再检查其他组
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    return processedNode;
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -41,6 +41,74 @@ import group2Leg from "@/assets/images/groupEvolution/legends-group2.png"
 | 
			
		|||
import group3Leg from "@/assets/images/groupEvolution/legends-group3.png"
 | 
			
		||||
import abnormalLeg from "@/assets/images/groupEvolution/legends-abnormal-group.png"
 | 
			
		||||
 | 
			
		||||
// 初始化Web Worker
 | 
			
		||||
let graphWorker = null
 | 
			
		||||
let workerCallbacks = new Map()
 | 
			
		||||
let workerTaskId = 0
 | 
			
		||||
 | 
			
		||||
const initWorker = () => {
 | 
			
		||||
  if (!graphWorker) {
 | 
			
		||||
    // 创建Web Worker实例
 | 
			
		||||
    graphWorker = new Worker(new URL("./graphWorker.js", import.meta.url))
 | 
			
		||||
 | 
			
		||||
    // 设置Worker消息处理
 | 
			
		||||
    graphWorker.onmessage = (e) => {
 | 
			
		||||
      const { success, type, result, error, taskId } = e.data
 | 
			
		||||
 | 
			
		||||
      // 查找对应的回调函数
 | 
			
		||||
      const callback = workerCallbacks.get(taskId)
 | 
			
		||||
      if (callback) {
 | 
			
		||||
        if (success) {
 | 
			
		||||
          callback(null, result)
 | 
			
		||||
        } else {
 | 
			
		||||
          callback(new Error(`Worker error: ${error}`))
 | 
			
		||||
        }
 | 
			
		||||
        workerCallbacks.delete(taskId)
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 设置Worker错误处理
 | 
			
		||||
    graphWorker.onerror = (error) => {
 | 
			
		||||
      console.error("Graph Worker error:", error)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 向Worker发送任务
 | 
			
		||||
const postWorkerTask = (type, data) => {
 | 
			
		||||
  return new Promise((resolve, reject) => {
 | 
			
		||||
    if (!graphWorker) {
 | 
			
		||||
      initWorker()
 | 
			
		||||
 | 
			
		||||
      // 如果Worker初始化失败,使用同步处理
 | 
			
		||||
      if (!graphWorker) {
 | 
			
		||||
        reject(new Error("Web Worker not available"))
 | 
			
		||||
        return
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const taskId = ++workerTaskId
 | 
			
		||||
    workerCallbacks.set(taskId, (error, result) => {
 | 
			
		||||
      if (error) {
 | 
			
		||||
        reject(error)
 | 
			
		||||
      } else {
 | 
			
		||||
        resolve(result)
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
    graphWorker.postMessage({ type, data, taskId })
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 终止Worker
 | 
			
		||||
const terminateWorker = () => {
 | 
			
		||||
  if (graphWorker) {
 | 
			
		||||
    graphWorker.terminate()
 | 
			
		||||
    graphWorker = null
 | 
			
		||||
    workerCallbacks.clear()
 | 
			
		||||
    workerTaskId = 0
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
  store: {
 | 
			
		||||
    required: true
 | 
			
		||||
| 
						 | 
				
			
			@ -194,18 +262,42 @@ const clearEvents = () => {
 | 
			
		|||
      if (typeof graphVis.unregistEventListener === "function") {
 | 
			
		||||
        graphVis.unregistEventListener(type, event, handler)
 | 
			
		||||
      } else {
 | 
			
		||||
        // 如果没有专门的移除方法,尝试其他方式(根据GraphVis实际API调整)
 | 
			
		||||
        console.warn("GraphVis instance does not have unregistEventListener method")
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
    eventListeners = []
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//公用连线或节点高亮函数
 | 
			
		||||
const highLightAboutNodesOrLinks = (type) => {
 | 
			
		||||
// 公用连线或节点高亮函数(使用Web Worker)
 | 
			
		||||
const highLightAboutNodesOrLinks = async (type) => {
 | 
			
		||||
  graphVis.cancelAllSelected()
 | 
			
		||||
  const { newNodes, newLinks } = graph.value
 | 
			
		||||
 | 
			
		||||
  try {
 | 
			
		||||
    let processedItems
 | 
			
		||||
 | 
			
		||||
    if (type == "nodes") {
 | 
			
		||||
      // 使用Web Worker处理节点高亮
 | 
			
		||||
      processedItems = await postWorkerTask("HIGHLIGHT_NODES", {
 | 
			
		||||
        nodes: graphVis.nodes,
 | 
			
		||||
        newNodes
 | 
			
		||||
      })
 | 
			
		||||
      graphVis.nodes = processedItems
 | 
			
		||||
    } else if (type == "links") {
 | 
			
		||||
      // 使用Web Worker处理连线高亮
 | 
			
		||||
      processedItems = await postWorkerTask("HIGHLIGHT_LINKS", {
 | 
			
		||||
        links: graphVis.links,
 | 
			
		||||
        newLinks
 | 
			
		||||
      })
 | 
			
		||||
      graphVis.links = processedItems
 | 
			
		||||
    } else {
 | 
			
		||||
      return
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 触发重绘
 | 
			
		||||
    graphVis.render()
 | 
			
		||||
  } catch (error) {
 | 
			
		||||
    // 降级处理:使用同步方法
 | 
			
		||||
    if (type == "nodes") {
 | 
			
		||||
      const newNodeIds = new Set(newNodes.map((node) => node.name))
 | 
			
		||||
      graphVis.nodes.forEach((node) => {
 | 
			
		||||
| 
						 | 
				
			
			@ -220,8 +312,7 @@ const highLightAboutNodesOrLinks = (type) => {
 | 
			
		|||
          link.selected = true
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
  } else {
 | 
			
		||||
    return
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
const runDiffForceLayout = (layoutConfig, layoutType, isAsync) => {
 | 
			
		||||
| 
						 | 
				
			
			@ -239,7 +330,21 @@ const runDiffForceLayout = (layoutConfig, layoutType, isAsync) => {
 | 
			
		|||
      highLightAboutNodesOrLinks("nodes")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const handleAnomalousGroup = () => {
 | 
			
		||||
    const handleAnomalousGroup = async () => {
 | 
			
		||||
      try {
 | 
			
		||||
        // 使用Web Worker处理异常群体分析
 | 
			
		||||
        const processedNodes = await postWorkerTask("ANOMALOUS_GROUP_PROCESS", {
 | 
			
		||||
          nodes: graphVis.nodes,
 | 
			
		||||
          currentUtc: props.store.currentUtc,
 | 
			
		||||
          abnormalData: props.store.graphAbnormalData
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        graphVis.nodes = processedNodes
 | 
			
		||||
        graphVis.render()
 | 
			
		||||
      } catch (error) {
 | 
			
		||||
        console.error("Error processing anomalous group:", error)
 | 
			
		||||
 | 
			
		||||
        // 降级处理:使用同步方法
 | 
			
		||||
        // 当前时间从 store 取
 | 
			
		||||
        const now = props.store.currentUtc
 | 
			
		||||
        const abnormalData = props.store.graphAbnormalData
 | 
			
		||||
| 
						 | 
				
			
			@ -274,6 +379,7 @@ const runDiffForceLayout = (layoutConfig, layoutType, isAsync) => {
 | 
			
		|||
          return node
 | 
			
		||||
        })
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    new Map([
 | 
			
		||||
      ["groupDiscovery", () => handleGroupDiscoveryDiff()],
 | 
			
		||||
| 
						 | 
				
			
			@ -286,8 +392,30 @@ const runDiffForceLayout = (layoutConfig, layoutType, isAsync) => {
 | 
			
		|||
  }
 | 
			
		||||
  graphVis.excuteLocalLayout(layoutType, layoutConfig, isAsync, handleLayoutSuccess())
 | 
			
		||||
}
 | 
			
		||||
// 根据节点的cluster属性进行分组
 | 
			
		||||
const clusterAnalyze = () => {
 | 
			
		||||
// 根据节点的cluster属性进行分组(使用Web Worker)
 | 
			
		||||
const clusterAnalyze = async () => {
 | 
			
		||||
  try {
 | 
			
		||||
    // 使用Web Worker处理聚类分析
 | 
			
		||||
    const { nodes: processedNodes, groups } = await postWorkerTask("CLUSTER_ANALYZE", {
 | 
			
		||||
      nodes: graphVis.nodes,
 | 
			
		||||
      storeId
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    graphVis.removeAllGroup() // 清除原有分组
 | 
			
		||||
    graphVis.nodes = processedNodes
 | 
			
		||||
    // 添加分组
 | 
			
		||||
    if (storeId !== "anomalousGroup" && groups && groups.length) {
 | 
			
		||||
      groups.forEach((group) => {
 | 
			
		||||
        const groupNodes = processedNodes.filter((node) => group.nodes.includes(node.id))
 | 
			
		||||
        if (groupNodes.length) {
 | 
			
		||||
          graphVis.addNodesInGroup(groupNodes, group.options)
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    graphVis.render()
 | 
			
		||||
  } catch (error) {
 | 
			
		||||
    // 降级处理:使用同步方法
 | 
			
		||||
    graphVis.removeAllGroup() // 清除原有分组
 | 
			
		||||
    // 创建cluster到nodes的映射
 | 
			
		||||
    const clusterNodesMap = new Map()
 | 
			
		||||
| 
						 | 
				
			
			@ -317,7 +445,7 @@ const clusterAnalyze = () => {
 | 
			
		|||
        })
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
  // graphVis.autoGroupLayout(graphVis.nodes)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 仅对“异常群体模块”生效:时间变化时强制重绘一次
 | 
			
		||||
| 
						 | 
				
			
			@ -370,7 +498,7 @@ onMounted(() => {
 | 
			
		|||
})
 | 
			
		||||
 | 
			
		||||
// 添加更新图表的函数(优化版本:使用requestAnimationFrame和节流)
 | 
			
		||||
const updateChart = (newGraphData) => {
 | 
			
		||||
const updateChart = async (newGraphData) => {
 | 
			
		||||
  if (!graphVis) {
 | 
			
		||||
    initChart()
 | 
			
		||||
    return
 | 
			
		||||
| 
						 | 
				
			
			@ -379,8 +507,12 @@ const updateChart = (newGraphData) => {
 | 
			
		|||
  try {
 | 
			
		||||
    graphVis.clearAll()
 | 
			
		||||
    graphVis.addGraph({ ...toRaw(newGraphData) })
 | 
			
		||||
 | 
			
		||||
    // 初始化Web Worker
 | 
			
		||||
    initWorker()
 | 
			
		||||
 | 
			
		||||
    // 重新运行力导向布局
 | 
			
		||||
    clusterAnalyze()
 | 
			
		||||
    await clusterAnalyze()
 | 
			
		||||
    runDiffForceLayout({ strength: -300, ajustCluster: true }, "simulation", true)
 | 
			
		||||
  } catch (error) {
 | 
			
		||||
    console.error("Error updating chart:", error)
 | 
			
		||||
| 
						 | 
				
			
			@ -445,6 +577,9 @@ onUnmounted(() => {
 | 
			
		|||
    forceSimulator = null
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // 终止Web Worker
 | 
			
		||||
  terminateWorker()
 | 
			
		||||
 | 
			
		||||
  // 清除引用,帮助垃圾回收
 | 
			
		||||
  currentSelectNode.value = null
 | 
			
		||||
  isPlay.value = false
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -101,10 +101,9 @@ const startAutomaticPlay = () => {
 | 
			
		|||
 | 
			
		||||
  isAutoPlaying.value = true
 | 
			
		||||
 | 
			
		||||
  // 每300ms移动2%
 | 
			
		||||
  animationTimer = setInterval(() => {
 | 
			
		||||
    // 计算新位置
 | 
			
		||||
    const newPosition = clickedPosition.value + 2
 | 
			
		||||
    const newPosition = clickedPosition.value + 0.1
 | 
			
		||||
 | 
			
		||||
    // 检查是否到达终点
 | 
			
		||||
    if (newPosition >= 100) {
 | 
			
		||||
| 
						 | 
				
			
			@ -118,7 +117,7 @@ const startAutomaticPlay = () => {
 | 
			
		|||
 | 
			
		||||
    // 根据当前位置更新activeTimePoint
 | 
			
		||||
    updateActiveTimePointByPosition(newPosition)
 | 
			
		||||
  }, 1000)
 | 
			
		||||
  }, 100)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 根据位置更新activeTimePoint
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user