api_graph.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. from django.contrib import auth
  2. from rest_framework.views import APIView
  3. from rest_framework.response import Response
  4. from rest_framework import status
  5. from rest_framework.authtoken.models import Token
  6. from rest_framework.authentication import BasicAuthentication, TokenAuthentication
  7. from .serializers import UserRegisterSerializer
  8. from django.middleware.csrf import get_token
  9. from django.contrib.auth import login
  10. from api.utils import *
  11. from api.models import Result, Graph, GraphToken, Plan
  12. from random import randint
  13. import logging
  14. import json, csv
  15. logger = logging.getLogger("graph")
  16. class ViewGraphByToken(APIView):
  17. # 通过验证码看图——供VR使用
  18. authentication_classes = []
  19. permission_classes = []
  20. def get(self, request):
  21. print(request.GET)
  22. requestToken = request.GET.get('token')
  23. print(requestToken)
  24. try:
  25. token = GraphToken.objects.get(token=str(requestToken))
  26. except GraphToken.DoesNotExist:
  27. return failed(message="Token不存在")
  28. if token.checkExpire():
  29. return failed(message="Token已过期")
  30. graph = token.graph
  31. return success(data={
  32. 'nodes': graph.nodes,
  33. 'edges': graph.edges,
  34. })
  35. class GenerateGraph(APIView):
  36. # 生成图数据
  37. def get(self, request):
  38. user = request.user
  39. method = request.GET.get('method') # 以何种视图查看
  40. planId = request.GET.get('plan')
  41. try:
  42. plan = Plan.objects.get(id=planId)
  43. except Plan.DoesNotExist:
  44. print("获取结果的Plan失败")
  45. return failed(message="无法找到该结果对应规划")
  46. try:
  47. result = plan.own_result
  48. except Result.DoesNotExist:
  49. logger.error(f"获取任务{plan.mission.id}规划{plan.id}的结果失败,结果不存在")
  50. return failed(message="结果不存在")
  51. # 图表显示不生成图,仅做统计计算
  52. if method == 'chart':
  53. nodeJson = result.nodeFile.toJson()
  54. edgeJson = result.edgeFile.toJson()
  55. def isOriginEdge(edge):
  56. for meta in edge['meta']:
  57. if meta.get('optimize') == 'new' or meta.get('predict') == 'new':
  58. return False
  59. return True
  60. # 需要完善图表视图统计数据,根据具体图表类型决定
  61. # 通用图数据统计,包括SDI节点数量、边数量,各类型节点度数分布
  62. # 平均链长度,聚类系数即D节点间连接紧密程度
  63. # 分别统计SDI节点的数量和度数:sNodes,dNodes,iNodes
  64. sDegree = dDegree = iDegree = [0 for i in range(11)] # 度的取值范围0~10,共11个元素
  65. sNodes = [node for node in nodeJson if node['type'] == 'S']
  66. for s in sNodes:
  67. degree = len([edge for edge in edgeJson if edge['from'] == s['id'] or edge['to'] == s['id']])
  68. if degree <= 10:
  69. sDegree[degree] += 1
  70. else:
  71. sDegree[10] += 1
  72. dNodes = [node for node in nodeJson if node['type'] == 'D']
  73. for d in dNodes:
  74. degree = len([edge for edge in edgeJson if edge['from'] == d['id'] or edge['to'] == d['id']])
  75. if degree <= 10:
  76. dDegree[degree] += 1
  77. else:
  78. dDegree[10] += 1
  79. iNodes = [node for node in nodeJson if node['type'] == 'I']
  80. for i in iNodes:
  81. degree = len([edge for edge in edgeJson if edge['from'] == i['id'] or edge['to'] == i['id']])
  82. if degree <= 10:
  83. iDegree[degree] += 1
  84. else:
  85. iDegree[10] += 1
  86. # 查找所有OODA链路:chains
  87. chains = []
  88. for s in sNodes:
  89. stack = [(s, [s])] # (当前节点, 路径, 已访问节点)
  90. while stack:
  91. current, path = stack.pop()
  92. # 必须限制链路长度,否则无法结束,除限制长度外还可尝试将不同顺序节点的链路视为同一条,从而减少链路数量
  93. if len(path) > 5:
  94. continue
  95. # 终止条件:到达I节点
  96. if current['type'] == 'I':
  97. chains.append(path)
  98. continue
  99. # 遍历邻居,注意通用计算只统计原始图
  100. neighbors = [edge['to'] for edge in edgeJson if (isOriginEdge(edge) and edge['from'] == current['id'])] + [edge['from'] for edge in edgeJson if (isOriginEdge(edge) and edge['to'] == current['id'])]
  101. for neighborId in neighbors:
  102. neighbor = [node for node in nodeJson if node.get('id') == neighborId]
  103. if len(neighbor) != 1:
  104. return failed(message="查找OODA链路时出错:不存在的邻居节点")
  105. neighbor = neighbor[0]
  106. if neighbor in path: continue # 避免环路
  107. # 不能连线的几种情况
  108. if current['type'] == 'S' and neighbor['type'] == 'I':
  109. continue
  110. if current['type'] == 'D' and neighbor['type'] == 'S':
  111. continue
  112. stack.append((neighbor, path + [neighbor]))
  113. chainsNum = len(chains)
  114. chainsNumByLength = [0 for i in range(5)]
  115. for chain in chains:
  116. length = len(chain)
  117. if length >= 5:
  118. length = 5
  119. chainsNumByLength[length-1] += 1
  120. averageChainsLength = round(sum([len(chain) for chain in chains]) / chainsNum, 2)
  121. data = {
  122. 'sNodesNum': len(sNodes),
  123. 'dNodesNum': len(dNodes),
  124. 'iNodesNum': len(iNodes),
  125. 'sDegree': sDegree,
  126. 'dDegree': dDegree,
  127. 'iDegree': iDegree,
  128. 'chains': {
  129. 'num': chainsNum,
  130. 'averageLength': averageChainsLength,
  131. 'numByLength': chainsNumByLength,
  132. },
  133. }
  134. # 根据计算类型的不同分别计算特殊数据
  135. if result.plan.algorithm.type in ['optimize', 'predict'] :
  136. # 拓扑优化算法应该统计原有图的数据与优化后图的数据,优化应该仅优化边
  137. newChains = []
  138. for s in sNodes:
  139. stack = [(s, [s])] # (当前节点, 路径, 已访问节点)
  140. while stack:
  141. current, path = stack.pop()
  142. # 终止条件:到达I节点
  143. if current['type'] == 'I':
  144. newChains.append(path)
  145. continue
  146. # 遍历邻居,注意优化计算只统计新图
  147. neighbors = [edge['to'] for edge in edgeJson if (not isOriginEdge(edge) and edge['from'] == current['id'])] + [edge['from'] for edge in edgeJson if (not isOriginEdge(edge) and edge['to'] == current['id'])]
  148. for neighborId in neighbors:
  149. neighbor = [node for node in nodeJson if node.get('id') == neighborId]
  150. if len(neighbor) != 1:
  151. return failed(message="查找OODA链路时出错:不存在的邻居节点")
  152. neighbor = neighbor[0]
  153. if neighbor in path: continue # 避免环路
  154. # 不能连线的几种情况
  155. if current['type'] == 'S' and neighbor['type'] == 'I':
  156. continue
  157. if current['type'] == 'D' and neighbor['type'] == 'S':
  158. continue
  159. stack.append((neighbor, path + [neighbor]))
  160. newChainsNum = len(newChains)
  161. newAverageChainsLength = 0
  162. if newChainsNum != 0:
  163. newAverageChainsLength = round(sum([len(chain) for chain in newChains]) / newChainsNum, 2)
  164. data['newChains'] = {
  165. 'num': newChainsNum,
  166. 'averageLength': newAverageChainsLength,
  167. }
  168. return success(data=data)
  169. # 如果已经生成过图数据,则直接生成新的验证码
  170. if result.own_graphs.exists():
  171. graph = result.own_graphs.first()
  172. if method == 'web' or method == '3D' or method == '3d':
  173. return success(data={
  174. 'nodes': graph.nodes,
  175. 'edges': graph.edges,
  176. })
  177. elif method == 'VR':
  178. token = graph.generateToken()
  179. return success(data={
  180. 'token': token,
  181. })
  182. # 不存在则需要重新生成图
  183. else:
  184. nodeJson = result.nodeFile.toJson()
  185. edgeJson = result.edgeFile.toJson()
  186. ######测试用,添加几个标签
  187. # for node in nodeJson:
  188. # node['meta'].append({'optimize': 'new'})
  189. # node['meta'].append({'group': randint(1,5)})
  190. # node['meta'].append({'predict': 'new'})
  191. # for edge in edgeJson:
  192. # edge['meta'].append({'optimize': 'new'})
  193. # edge['meta'].append({'predict': 'new'})
  194. ########################
  195. # 两种3D视图的数据结构应该是一样的
  196. if result.plan.algorithm.type == 'optimize':
  197. # 拓扑优化算法生成的结果,只有网络结构数据
  198. # 检查合法性
  199. for node in nodeJson:
  200. if not 'id' in node or not 'type' in node:
  201. return failed(message="拓扑优化结果的节点文件存在问题")
  202. for edge in edgeJson:
  203. if not 'from' in edge or not 'to' in edge:
  204. return failed(message="边文件存在问题")
  205. # 对于优化算法,边的属性中应该有新旧的区分
  206. missingLabel = True
  207. for meta in edge['meta']:
  208. if 'optimize' in meta and meta['optimize'] in ['new', 'old']:
  209. missingLabel = False
  210. if missingLabel:
  211. return failed(message="无拓扑优化标签")
  212. elif result.plan.algorithm.type == 'group':
  213. # 功能体探测算法生成的结果
  214. # 检查合法性
  215. for node in nodeJson:
  216. if not 'id' in node or not 'type' in node:
  217. return failed(message="节点文件存在问题")
  218. # 对于功能体探测算法,节点的属性中应有功能体的编号
  219. missingLabel = True
  220. for meta in node['meta']:
  221. if 'group' in meta and type(meta['group']) == int:
  222. missingLabel = False
  223. if missingLabel:
  224. # 存在节点没有功能体标签,则该节点应被视为孤立节点
  225. # 孤立节点添加一个-1的group表示孤立
  226. node['meta'].append({'group': -1})
  227. # return failed(message="无功能体标签")
  228. for edge in edgeJson:
  229. if not 'from' in edge or not 'to' in edge:
  230. return failed(message="边文件存在问题")
  231. elif result.plan.algorithm.type == 'predict':
  232. # 链路演化预测算法生成的结果
  233. # 检查合法性
  234. for node in nodeJson:
  235. if not 'id' in node or not 'type' in node:
  236. return failed(message="节点文件存在问题")
  237. # 对于演化预测算法,节点的属性中应有新旧区分
  238. # 演化预测和拓扑优化节点本身都没有meta属性,仅对边有区分
  239. # missingLabel = True
  240. # for meta in node['meta']:
  241. # if 'predict' in meta and meta['predict'] in ['new', 'old']:
  242. # missingLabel = False
  243. # if missingLabel:
  244. # return failed(message="无演化预测标签")
  245. for edge in edgeJson:
  246. if not 'from' in edge or not 'to' in edge:
  247. return failed(message="边文件存在问题")
  248. # 对于演化预测算法,边的属性中应有新旧区分
  249. missingLabel = True
  250. for meta in edge['meta']:
  251. if 'predict' in meta and meta['predict'] in ['new', 'old']:
  252. missingLabel = False
  253. if missingLabel:
  254. return failed(message="无演化预测标签")
  255. graph = Graph.objects.createFromResult(result)
  256. graph.save()
  257. if method == 'web':
  258. return success(data={
  259. 'nodes': graph.nodes,
  260. 'edges': graph.edges,
  261. })
  262. if method == 'VR':
  263. token = graph.generateToken()
  264. return success(data={
  265. 'token': token,
  266. })