123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306 |
- from django.contrib import auth
- from rest_framework.views import APIView
- from rest_framework.response import Response
- from rest_framework import status
- from rest_framework.authtoken.models import Token
- from rest_framework.authentication import BasicAuthentication, TokenAuthentication
- from .serializers import UserRegisterSerializer
- from django.middleware.csrf import get_token
- from django.contrib.auth import login
- from api.utils import *
- from api.models import Result, Graph, GraphToken, Plan
- from random import randint
- import logging
- import json, csv
- logger = logging.getLogger("graph")
- def build_adjacency_lists(nodeJson, edgeJson):
- """ 构建类型化邻接表 """
- adj = {n['id']: [] for n in nodeJson}
- type_map = {n['id']: n['type'] for n in nodeJson}
-
- for edge in edgeJson:
- src, dst = edge['from'], edge['to']
- src_type = type_map[src]
- dst_type = type_map[dst]
-
- # 根据类型过滤连接
- if src_type == 'S' and dst_type == 'D':
- adj[src].append(dst)
- elif src_type == 'D' and dst_type in ('D', 'I'):
- adj[src].append(dst)
- # if edge['meta'][0].get('optimize') != 'new': # 双向处理
- # if dst_type == 'S' and src_type == 'D':
- # adj[dst].append(src)
- # elif dst_type == 'D' and src_type in ('D', 'I'):
- # adj[dst].append(src)
- return adj, type_map
- def parallel_chain_search(s_id, adj, type_map):
- """ 并行化路径搜索 """
- from collections import deque
- chains = []
- queue = deque([(s_id, 1)]) # (current_id, path_length)
- visited = {s_id: 1} # 节点: 到达时的路径长度
-
- while queue:
- nid, length = queue.popleft()
- if length > 4: # 限制最大深度为4层(总长度5)
- continue
-
- for neighbor in adj.get(nid, []):
- n_type = type_map[neighbor]
- # 剪枝规则
- if n_type == 'I' and length >= 2:
- chains.append(length + 1)
- continue
- if neighbor not in visited or visited[neighbor] > length + 1:
- visited[neighbor] = length + 1
- queue.append((neighbor, length + 1))
-
- return chains
- class ViewGraphByToken(APIView):
- # 通过验证码看图——供VR使用
- authentication_classes = []
- permission_classes = []
- def get(self, request):
- print(request.GET)
- requestToken = request.GET.get('token')
- print(requestToken)
- try:
- token = GraphToken.objects.get(token=str(requestToken))
- except GraphToken.DoesNotExist:
- return failed(message="Token不存在")
- if token.checkExpire():
- return failed(message="Token已过期")
- graph = token.graph
- return success(data={
- 'nodes': graph.nodes,
- 'edges': graph.edges,
- })
- class GenerateGraph(APIView):
- # 生成图数据
- def get(self, request):
- user = request.user
- method = request.GET.get('method') # 以何种视图查看
- planId = request.GET.get('plan')
- try:
- plan = Plan.objects.get(id=planId)
- except Plan.DoesNotExist:
- print("获取结果的Plan失败")
- return failed(message="无法找到该结果对应规划")
- try:
- result = plan.own_result
- except Result.DoesNotExist:
- logger.error(f"获取任务{plan.mission.id}规划{plan.id}的结果失败,结果不存在")
- return failed(message="结果不存在")
- # 图表显示不生成图,仅做统计计算
- if method == 'chart':
- nodeJson = result.nodeFile.toJson()
- edgeJson = result.edgeFile.toJson()
-
- # 1. 构建类型化邻接表(O(E)时间复杂度)
- adj, type_map = build_adjacency_lists(nodeJson, edgeJson)
-
- # 2. 并行计算度数分布(O(N)时间复杂度)
- from joblib import Parallel, delayed
- s_nodes = [n['id'] for n in nodeJson if n['type'] == 'S']
- d_nodes = [n['id'] for n in nodeJson if n['type'] == 'D']
- i_nodes = [n['id'] for n in nodeJson if n['type'] == 'I']
-
- # 度数统计
- degree_bins = lambda deg: min(deg, 10)
- s_degrees = [degree_bins(len(adj[nid])) for nid in s_nodes]
- d_degrees = [degree_bins(len(adj[nid])) for nid in d_nodes]
- i_degrees = [degree_bins(len(adj[nid])) for nid in i_nodes]
-
- # 3. 并行路径搜索(使用多线程加速)
- from concurrent.futures import ThreadPoolExecutor
- all_chains = []
- with ThreadPoolExecutor(max_workers=8) as executor:
- futures = [executor.submit(parallel_chain_search, s_id, adj, type_map)
- for s_id in s_nodes]
- for future in futures:
- all_chains.extend(future.result())
-
- # 统计结果(O(L)时间复杂度)
- chains_num = len(all_chains)
- len_dist = [0]*5
- for l in all_chains:
- len_dist[min(l-1, 4)] += 1 # 1-5层对应索引0-4
-
- avg_len = round(sum(all_chains)/chains_num, 2) if chains_num > 0 else 0.0
-
- data = {
- 'sNodesNum': len(s_nodes),
- 'dNodesNum': len(d_nodes),
- 'iNodesNum': len(i_nodes),
- 'sDegree': [s_degrees.count(i) for i in range(11)],
- 'dDegree': [d_degrees.count(i) for i in range(11)],
- 'iDegree': [i_degrees.count(i) for i in range(11)],
- 'chains': {
- 'num': chains_num,
- 'averageLength': avg_len,
- 'numByLength': len_dist
- }
- }
- # 根据计算类型的不同分别计算特殊数据
- if result.plan.algorithm.type in ['optimize', 'predict'] :
- # 拓扑优化算法应该统计原有图的数据与优化后图的数据,优化应该仅优化边
- from concurrent.futures import ThreadPoolExecutor
- def is_new_edge(edge):
- """ 判断是否为新增边 """
- return any(meta.get('optimize') == 'new' for meta in edge['meta'])
- def build_new_adjacency(nodeJson, edgeJson):
- """ 构建优化后的邻接表 """
- new_adj = {n['id']: [] for n in nodeJson}
- for edge in edgeJson:
- if is_new_edge(edge):
- src, dst = edge['from'], edge['to']
- new_adj[src].append(dst)
- new_adj[dst].append(src) # 无向图处理
- return new_adj
- # 在chart处理分支中添加
- if result.plan.algorithm.type in ['optimize', 'predict']:
- # 构建新邻接表
- new_adj = build_new_adjacency(nodeJson, edgeJson)
-
- # 并行计算新链路
- with ThreadPoolExecutor(max_workers=8) as executor:
- futures = [executor.submit(parallel_chain_search, s_id, new_adj, type_map)
- for s_id in s_nodes]
- new_chains = []
- for future in futures:
- new_chains.extend(future.result())
-
- # 统计新链路
- new_chains_num = len(new_chains)
- new_len_dist = [0]*5
- for l in new_chains:
- new_len_dist[min(l-1,4)] += 1
- new_avg_len = round(sum(new_chains)/new_chains_num,2) if new_chains_num else 0
-
- data['newChains'] = {
- 'num': new_chains_num,
- 'averageLength': new_avg_len,
- 'numByLength': new_len_dist
- }
- return success(data=data)
- # 如果已经生成过图数据,则直接生成新的验证码
- if result.own_graphs.exists():
- graph = result.own_graphs.first()
- if method == 'web' or method == '3D' or method == '3d':
- return success(data={
- 'nodes': graph.nodes,
- 'edges': graph.edges,
- })
- elif method == 'VR':
- token = graph.generateToken()
- return success(data={
- 'token': token,
- })
- # 不存在则需要重新生成图
- else:
- nodeJson = result.nodeFile.toJson()
- edgeJson = result.edgeFile.toJson()
- ######测试用,添加几个标签
- # for node in nodeJson:
- # node['meta'].append({'optimize': 'new'})
- # node['meta'].append({'group': randint(1,5)})
- # node['meta'].append({'predict': 'new'})
- # for edge in edgeJson:
- # edge['meta'].append({'optimize': 'new'})
- # edge['meta'].append({'predict': 'new'})
- ########################
- # 两种3D视图的数据结构应该是一样的
- if result.plan.algorithm.type == 'optimize':
- # 拓扑优化算法生成的结果,只有网络结构数据
- # 检查合法性
- for node in nodeJson:
- if not 'id' in node or not 'type' in node:
- return failed(message="拓扑优化结果的节点文件存在问题")
- for edge in edgeJson:
- if not 'from' in edge or not 'to' in edge:
- return failed(message="边文件存在问题")
- # 对于优化算法,边的属性中应该有新旧的区分
- missingLabel = True
- for meta in edge['meta']:
- if 'optimize' in meta and meta['optimize'] in ['new', 'old']:
- missingLabel = False
- if missingLabel:
- return failed(message="无拓扑优化标签")
- elif result.plan.algorithm.type == 'group':
- # 功能体探测算法生成的结果
- # 检查合法性
- for node in nodeJson:
- if not 'id' in node or not 'type' in node:
- return failed(message="节点文件存在问题")
- # 对于功能体探测算法,节点的属性中应有功能体的编号
- missingLabel = True
- for meta in node['meta']:
- if 'group' in meta and type(meta['group']) == int:
- missingLabel = False
- if missingLabel:
- # 存在节点没有功能体标签,则该节点应被视为孤立节点
- # 孤立节点添加一个-1的group表示孤立
- node['meta'].append({'group': -1})
- # return failed(message="无功能体标签")
- for edge in edgeJson:
- if not 'from' in edge or not 'to' in edge:
- return failed(message="边文件存在问题")
- elif result.plan.algorithm.type == 'predict':
- # 链路演化预测算法生成的结果
- # 检查合法性
- for node in nodeJson:
- if not 'id' in node or not 'type' in node:
- return failed(message="节点文件存在问题")
- # 对于演化预测算法,节点的属性中应有新旧区分
- # 演化预测和拓扑优化节点本身都没有meta属性,仅对边有区分
- # missingLabel = True
- # for meta in node['meta']:
- # if 'predict' in meta and meta['predict'] in ['new', 'old']:
- # missingLabel = False
- # if missingLabel:
- # return failed(message="无演化预测标签")
- for edge in edgeJson:
- if not 'from' in edge or not 'to' in edge:
- return failed(message="边文件存在问题")
- # 对于演化预测算法,边的属性中应有新旧区分
-
- for meta in edge['meta']:
- if 'predict' in meta and meta['predict'] in ['new', 'old']:
- missingLabel = False
-
- # 暂时取消标签检测,用于生成图
- missingLabel = False
- if missingLabel:
- return failed(message="无演化预测标签")
- graph = Graph.objects.createFromResult(result)
- graph.save()
- if method == 'web':
- return success(data={
- 'nodes': graph.nodes,
- 'edges': graph.edges,
- })
- if method == 'VR':
- token = graph.generateToken()
- return success(data={
- 'token': token,
- })
|