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, })