You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

profiling-dashboard.vue 53 kB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546
  1. <!--
  2. Copyright 2020 Huawei Technologies Co., Ltd.All Rights Reserved.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. -->
  13. <template>
  14. <div class="pro-router-wrap">
  15. <div class="pro-router-left">
  16. <!-- Step trace area -->
  17. <div class="step-trace">
  18. <div class="title-wrap">
  19. <div class="title">{{ $t('profiling.stepTrace') }}</div>
  20. <div class="view-detail">
  21. <button @click="viewDetail('step-trace')"
  22. :disabled="svg.noData && svg.data.length === 0"
  23. :class="{disabled:svg.noData && svg.data.length === 0}">{{ $t('profiling.viewDetail') }}
  24. <i class="el-icon-d-arrow-right"></i></button>
  25. </div>
  26. <!-- Step trace description -->
  27. <div class="tip-icon">
  28. <el-tooltip placement="bottom"
  29. effect="light">
  30. <div slot="content"
  31. class="tooltip-container">
  32. <div class="pro-dash-tooltip">
  33. <div class="font-size-style">{{$t("profiling.features")}}</div>
  34. <div>{{$t('profiling.iterationInfo')}}</div>
  35. <div>
  36. <span class="font-style">{{$t('profiling.queueInfo')}}&nbsp;</span>
  37. <span>{{$t('profiling.iterationGapInfo')}}</span>
  38. </div>
  39. <div>
  40. <span class="font-style">{{$t('profiling.fpbpTitle')}}&nbsp;</span>
  41. <span>{{$t('profiling.fpbpInfo')}}</span>
  42. </div>
  43. <div>
  44. <span class="font-style">{{$t('profiling.iterativeTailingTitle')}}&nbsp;</span>
  45. <span>{{$t('profiling.iterativeTailingInfo')}}</span>
  46. </div>
  47. <br />
  48. <div class="font-size-style">{{$t('profiling.statistics')}}</div>
  49. <div>{{$t('profiling.totalTime')}}
  50. <span>{{totalTime}}{{$t('profiling.millisecond')}}</span>
  51. </div>
  52. <div>{{$t('profiling.totalSteps')}}<span>{{totalSteps}}</span></div>
  53. <div>{{$t('profiling.iterationGapTimeRatio')}}<span>{{iterationIntervalPercent}}</span></div>
  54. <div v-if="fpBpPercent">{{$t('profiling.fpbpTimeRatio')}}<span>{{fpBpPercent}}</span></div>
  55. <div v-else>{{$t('profiling.fpTimeRatio')}}<span>{{fpPercent}}</span></div>
  56. <div v-if="tailPercent">
  57. {{$t('profiling.iterativeTailingTimeRatio')}}
  58. <span>{{tailPercent}}</span>
  59. </div>
  60. </div>
  61. </div>
  62. <i class="el-icon-info"></i>
  63. </el-tooltip>
  64. </div>
  65. </div>
  66. <!-- Step trace SVG container -->
  67. <div class="trace-container">
  68. <div id="trace"
  69. class="training-trace"
  70. :style="{height: svg.totalHeight + 'px'}">
  71. <svg version="1.1"
  72. xmlns="http://www.w3.org/2000/svg"
  73. height="100%"
  74. width="100%">
  75. <defs>
  76. <marker id="marker_end"
  77. refX="5"
  78. refY="4"
  79. markerWidth="10"
  80. markerHeight="8"
  81. orient="auto">
  82. <path d="M1,1 L1,7 L9,4 z"
  83. fill="#6c7280"
  84. stroke="#6c7280"></path>
  85. </marker>
  86. <marker id="marker_start"
  87. refX="5"
  88. refY="4"
  89. markerWidth="10"
  90. markerHeight="8"
  91. orient="auto">
  92. <path d="M9,1 L9,7 L1,4 z"
  93. fill="#6c7280"
  94. stroke="#6c7280"></path>
  95. </marker>
  96. </defs>
  97. </svg>
  98. </div>
  99. <div class="image-noData"
  100. v-if="svg.noData">
  101. <div>
  102. <img :src="require('@/assets/images/nodata.png')"
  103. alt="" />
  104. </div>
  105. <p v-show="!svg.initOver">{{$t("public.dataLoading")}}</p>
  106. <p v-show="svg.initOver">{{$t("public.noData")}}</p>
  107. </div>
  108. </div>
  109. </div>
  110. <!-- Process summary display area -->
  111. <div class="minddata">
  112. <div class="title-wrap">
  113. <div class="title">{{ $t('profiling.mindData') }}</div>
  114. <div class="view-detail">
  115. <button @click="viewDetail('data-process')"
  116. :disabled="processSummary.noData"
  117. :class="{disabled:processSummary.noData}">
  118. {{ $t('profiling.viewDetail') }}
  119. <i class="el-icon-d-arrow-right"></i></button>
  120. </div>
  121. <div class="tip-icon">
  122. <el-tooltip placement="bottom"
  123. effect="light">
  124. <div slot="content"
  125. class="tooltip-container">
  126. <div class="pro-dash-tooltip">
  127. <div class="font-size-style">{{$t("profiling.features")}}</div>
  128. <div>{{$t('profiling.dataProcess')}}</div>
  129. <div>{{$t('profiling.dataProcessInfo')}}</div>
  130. <div>{{$t('profiling.analysisOne')}}</div>
  131. <div>{{$t('profiling.analysisTwo')}}</div>
  132. <div v-show="deviceInfoShow || queueInfoShow">{{$t('profiling.higherAnalysis')}}</div>
  133. <br />
  134. <div v-show="deviceInfoShow || queueInfoShow"
  135. class="font-size-style">{{$t('profiling.statistics')}}</div>
  136. <div v-show="queueInfoShow">{{$t('profiling.chipInfo')}}
  137. <span>{{processSummary.get_next.empty}} / {{processSummary.get_next.total}}</span>
  138. </div>
  139. <div v-show="deviceInfoShow">
  140. <div>{{$t('profiling.hostIsEmpty')}}
  141. <span>{{processSummary.device.empty}} / {{processSummary.device.total}}</span>
  142. </div>
  143. <div>{{$t('profiling.hostIsFull')}}
  144. <span>{{processSummary.device.full}} / {{processSummary.device.total}}</span>
  145. </div>
  146. </div>
  147. </div>
  148. </div>
  149. <i class="el-icon-info"></i>
  150. </el-tooltip>
  151. </div>
  152. </div>
  153. <div class="pipeline-container"
  154. v-show="!processSummary.noData">
  155. <div class="cell-container data-process">
  156. <div class="title">
  157. {{$t('profiling.pipeline')}}
  158. </div>
  159. </div>
  160. <div class="queue-container">
  161. <div class="img">
  162. <div class="edge">
  163. <img src="@/assets/images/data-flow.png"
  164. alt="" />
  165. </div>
  166. <div class="icon">
  167. <img src="@/assets/images/queue.svg"
  168. alt=""
  169. clickKey="connector_queue" />
  170. </div>
  171. <div class="edge">
  172. <img src="@/assets/images/data-flow.png"
  173. alt="" />
  174. </div>
  175. </div>
  176. <div class="title">{{$t('profiling.connectorQuene')}}</div>
  177. <div class="description">
  178. <div class="line"></div>
  179. <div class="item"
  180. v-if="processSummary.device.empty || processSummary.device.empty === 0">
  181. {{$t('profiling.queueTip2')}}
  182. <span class="num">
  183. {{processSummary.device.empty}} / {{processSummary.device.total}}
  184. </span>
  185. </div>
  186. <div class="item"
  187. v-if="processSummary.device.full || processSummary.device.full === 0">
  188. {{$t('profiling.queueTip1')}}
  189. <span class="num">
  190. {{processSummary.device.full}} / {{processSummary.device.total}}
  191. </span>
  192. </div>
  193. </div>
  194. </div>
  195. <div class="cell-container device_queue_op"
  196. clickKey="device_queue_op">
  197. <div class="title">
  198. {{$t('profiling.deviceQueueOp')}}
  199. </div>
  200. </div>
  201. <div class="queue-container"
  202. v-if="processSummary.count === processSummary.maxCount">
  203. <div class="img">
  204. <div class="edge">
  205. <img src="@/assets/images/data-flow.png"
  206. alt="" />
  207. </div>
  208. <div class="icon">
  209. <img src="@/assets/images/queue.svg"
  210. clickKey="data_queue"
  211. alt="" />
  212. </div>
  213. <div class="edge">
  214. <img src="@/assets/images/data-flow.png"
  215. alt="" />
  216. </div>
  217. </div>
  218. <div class="title">{{$t('profiling.dataQueue')}}</div>
  219. <div class="description">
  220. <div class="line"></div>
  221. <div class="item"
  222. v-if="processSummary.get_next.empty || processSummary.get_next.empty === 0">
  223. {{$t('profiling.queueTip2')}}
  224. <span class="num">
  225. {{processSummary.get_next.empty}} / {{processSummary.get_next.total}}
  226. </span>
  227. </div>
  228. <div class="item"
  229. v-if="processSummary.get_next.full || processSummary.get_next.full === 0">
  230. {{$t('profiling.queueTip1')}}
  231. <span class="num">
  232. {{processSummary.get_next.full}} / {{processSummary.get_next.total}}
  233. </span>
  234. </div>
  235. </div>
  236. </div>
  237. <div class="cell-container get-next"
  238. clickKey="get_next"
  239. v-if="processSummary.count === processSummary.maxCount">
  240. <div class="title">
  241. {{$t('profiling.getData')}}
  242. </div>
  243. </div>
  244. </div>
  245. <div class="image-noData"
  246. v-if="processSummary.noData">
  247. <div>
  248. <img :src="require('@/assets/images/nodata.png')"
  249. alt="" />
  250. </div>
  251. <p v-show="!processSummary.initOver">{{$t("public.dataLoading")}}</p>
  252. <p v-show="processSummary.initOver">{{$t("public.noData")}}</p>
  253. </div>
  254. </div>
  255. </div>
  256. <!-- Operator information display area -->
  257. <div class="pro-router-right">
  258. <div class="op-time-consume">
  259. <div class="title-wrap">
  260. <div class="title">{{ $t('profiling.rankOfOperator') }}</div>
  261. <div class="view-detail">
  262. <button @click="viewDetail('operator')"
  263. :disabled="pieChart.noData && pieChart.data.length === 0"
  264. :class="{disabled:pieChart.noData && pieChart.data.length === 0}">{{ $t('profiling.viewDetail') }}
  265. <i class="el-icon-d-arrow-right"></i></button>
  266. </div>
  267. </div>
  268. <div class="image-noData"
  269. v-if="pieChart.noData">
  270. <div>
  271. <img :src="require('@/assets/images/nodata.png')"
  272. alt="" />
  273. </div>
  274. <p v-show="!pieChart.initOver">{{$t("public.dataLoading")}}</p>
  275. <p v-show="pieChart.initOver">{{$t("public.noData")}}</p>
  276. </div>
  277. <div class="op-time-content">
  278. <div id="pieChart"
  279. class="pie-chart"
  280. v-if="pieChart.data.length"></div>
  281. <!-- Operator time consumption top5 -->
  282. <div class="time-list"
  283. v-if="pieChart.data.length">
  284. <ul>
  285. <li v-for="(item, index) in pieChart.topN"
  286. :key="index"
  287. class="item">
  288. <span class="index"
  289. :style="{'background-color': pieChart.colorList[index]}">{{index + 1}}</span>
  290. <span class="name">{{item.name}}</span>
  291. <span class="num">{{item.frequency + $t('profiling.times')}}</span>
  292. <span class="time">
  293. <span class="bar"
  294. :style="{width: item.time / pieChart.topN[0].time * 100 + '%'}"></span>
  295. <span class="value">{{item.time + $t('profiling.unit')}}</span>
  296. </span>
  297. </li>
  298. </ul>
  299. </div>
  300. </div>
  301. </div>
  302. <!-- Time line display area -->
  303. <div class="time-line">
  304. <div class="title-wrap">
  305. <div class="title">{{ $t('profiling.timeLine') }}</div>
  306. <div class="view-detail">
  307. <button @click="downloadTimelineFile()"
  308. v-show="!timeLine.waiting"
  309. :disabled="timeLine.disable"
  310. :class="{disabled:timeLine.disable}">{{ $t('profiling.downloadTimeline') }}
  311. </button>
  312. <div class="el-icon-loading loading-icon"
  313. v-show="timeLine.waiting"></div>
  314. </div>
  315. <div class="tip-icon">
  316. <el-tooltip placement="bottom"
  317. effect="light">
  318. <div slot="content"
  319. class="tooltip-container">
  320. <div class="pro-dash-tooltip">
  321. <div class="font-size-style">{{$t("profiling.features")}}</div>
  322. <div class="font-style">{{$t("profiling.timelineTips.title1")}}</div>
  323. <div>{{$t("profiling.timelineTips.content11")}}</div>
  324. <div>{{$t("profiling.timelineTips.content12")}}</div>
  325. <div>{{$t("profiling.timelineTips.content13")}}</div>
  326. <br>
  327. <div class="font-style">{{$t("profiling.timelineTips.title2")}}</div>
  328. <div>
  329. {{$t("profiling.timelineTips.content21.part1")}}
  330. <b>{{$t("profiling.timelineTips.content21.part2")}}</b>
  331. {{$t("profiling.timelineTips.content21.part3")}}
  332. </div>
  333. <div>{{$t("profiling.timelineTips.content22")}}</div>
  334. <div>
  335. {{$t("profiling.timelineTips.content23.part1")}}
  336. <b>{{$t("profiling.timelineTips.content23.part2")}}</b>
  337. {{$t("profiling.timelineTips.content23.part3")}}
  338. <b>{{$t("profiling.timelineTips.content23.part4")}}</b>
  339. {{$t("profiling.timelineTips.content23.part5")}}
  340. <b>{{$t("profiling.timelineTips.content23.part6")}}</b>
  341. {{$t("profiling.timelineTips.content23.part7")}}
  342. </div>
  343. <br>
  344. <div class="font-style">{{$t("profiling.timelineTips.title3")}}</div>
  345. <div>{{$t("profiling.timelineTips.content31")}}</div>
  346. <div>{{$t("profiling.timelineTips.content32")}}</div>
  347. </div>
  348. </div>
  349. <i class="el-icon-info"></i>
  350. </el-tooltip>
  351. </div>
  352. </div>
  353. <!-- Time line detail -->
  354. <div class="timeline-info"
  355. v-if="!timelineInfo.noData">
  356. <div class="info-line">
  357. <span>{{$t('profiling.opTotalTime')}}</span><span>{{timelineInfo.totalTime}}ms</span>
  358. </div>
  359. <div class="info-line">
  360. <span>{{$t('profiling.streamNum')}}</span><span>{{timelineInfo.streamNum}}</span>
  361. </div>
  362. <div class="info-line">
  363. <span>{{$t('profiling.opNum')}}</span><span>{{timelineInfo.opNum}}</span>
  364. </div>
  365. <div class="info-line">
  366. <span>{{$t('profiling.opTimes')}}</span><span>{{timelineInfo.opTimes + $t('profiling.times')}}</span>
  367. </div>
  368. </div>
  369. <div class="image-noData"
  370. v-if="timelineInfo.noData">
  371. <div>
  372. <img :src="require('@/assets/images/nodata.png')"
  373. alt="" />
  374. </div>
  375. <p v-show="!timelineInfo.initOver">{{$t("public.dataLoading")}}</p>
  376. <p v-show="timelineInfo.initOver">{{$t("public.noData")}}</p>
  377. </div>
  378. </div>
  379. </div>
  380. </div>
  381. </template>
  382. <script>
  383. import echarts from 'echarts';
  384. import RequestService from '../../services/request-service';
  385. import CommonProperty from '../../common/common-property';
  386. export default {
  387. data() {
  388. return {
  389. fpBpPercent: '--', // Ratio of time consumed by forward and backward propagation
  390. fpPercent: '--', // Ratio of time consumed by forward propagation
  391. iterationIntervalPercent: '--', // Ratio of time consumed by step interval
  392. totalSteps: '--',
  393. totalTime: '--',
  394. tailPercent: '--', // Ratio of time consumed by step tail
  395. queueInfoShow: false, // Whether to show queue information
  396. deviceInfoShow: false, // Whether to show device information
  397. svg: {
  398. // Step trace svg information
  399. data: [], // Data of svg
  400. svgPadding: 20, // Padding of svg
  401. totalWidth: 0, // Total width of svg
  402. totalTime: 0,
  403. cellHeight: 40,
  404. cellPadding: 0,
  405. rowPadding: 20,
  406. rowMargin: 10,
  407. totalHeight: 0,
  408. markerPadding: 4,
  409. minRate: 0.1, // Minimum time share threshold of non wrapping display
  410. minTime: 0, // Minimum time for non wrapping display
  411. minWidth: 1, // Minimum width of graphics in SVG
  412. fontSize: 12,
  413. textMargin: 21, // The minimum margin of the text from the border
  414. namespaceURI: 'http://www.w3.org/2000/svg', // XML namespace
  415. resizeTimer: null, // Response delay of resize event
  416. colors: {
  417. // Colors of different types of data presentation
  418. iteration_interval: ['#A6DD82', '#edf8e6'],
  419. fp_and_bp: ['#6CBFFF', '#e2f2ff'],
  420. tail: ['#fa8e5b', '#fff4de'],
  421. stream_parallel: ['#01a5a7', '#cceded'],
  422. },
  423. noData: true,
  424. initOver: false,
  425. },
  426. trainingJobId: this.$route.query.id, // Training job id
  427. summaryPath: this.$route.query.dir, // Summary path data
  428. relativePath: this.$route.query.path, // Relative path of summary log
  429. currentCard: '', // current device card
  430. pieChart: {
  431. // Pie graph information of operators
  432. chartDom: null,
  433. data: [],
  434. noData: true,
  435. topN: [],
  436. colorList: ['#6C92FA', '#6CBFFF', '#4EDED2', '#7ADFA0', '#A6DD82'],
  437. initOver: false, // Is initialization complete
  438. },
  439. timeLine: {
  440. // Time line data
  441. data: null,
  442. waiting: true, // Is it waiting for interface return
  443. disable: true,
  444. },
  445. timelineInfo: {
  446. // Time line information
  447. totalTime: 0,
  448. streamNum: 0,
  449. opNum: 0, // Number of operators
  450. opTimes: 0, // Operator time consuming
  451. noData: true,
  452. initOver: false, // Is initialization complete
  453. },
  454. processSummary: {
  455. // Data of process summary
  456. noData: true,
  457. count: 6,
  458. maxCount: 6,
  459. device: {
  460. empty: 0, // Number of empty devices
  461. full: 0, // Number of full devices
  462. total: 0, // Total number of devices
  463. },
  464. get_next: {
  465. empty: 0,
  466. full: 0,
  467. total: 0,
  468. },
  469. initOver: false, // Is initialization complete
  470. },
  471. };
  472. },
  473. mounted() {
  474. // Collapse the left column to respond to events
  475. setTimeout(() => {
  476. this.$bus.$on('collapse', this.resizeTrace);
  477. }, 500);
  478. },
  479. watch: {
  480. // Monitor current card information
  481. '$parent.curDashboardInfo': {
  482. handler(newValue, oldValue) {
  483. if (newValue.initOver) {
  484. this.pieChart.noData = true;
  485. this.svg.noData = true;
  486. this.svg.initOver = true;
  487. this.pieChart.initOver = true;
  488. this.timelineInfo.initOver = true;
  489. this.processSummary.initOver = true;
  490. this.timeLine.waiting = false;
  491. }
  492. if (
  493. newValue.query.dir &&
  494. newValue.query.id &&
  495. newValue.query.path &&
  496. newValue.curCardNum
  497. ) {
  498. this.summaryPath = newValue.query.dir;
  499. this.trainingJobId = newValue.query.id;
  500. this.relativePath = newValue.query.path;
  501. this.currentCard = newValue.curCardNum;
  502. if (this.trainingJobId) {
  503. document.title = `${decodeURIComponent(
  504. this.trainingJobId,
  505. )}-${this.$t('profiling.profilingDashboard')}-MindInsight`;
  506. } else {
  507. document.title = `${this.$t(
  508. 'profiling.profilingDashboard',
  509. )}-MindInsight`;
  510. }
  511. this.svg.initOver = false;
  512. this.pieChart.initOver = false;
  513. this.timelineInfo.initOver = false;
  514. this.processSummary.initOver = false;
  515. this.init();
  516. }
  517. },
  518. deep: true,
  519. immediate: true,
  520. },
  521. },
  522. methods: {
  523. /**
  524. * Initialization function
  525. */
  526. init() {
  527. this.queryTimeline();
  528. this.queryTrainingTrace();
  529. this.getProccessSummary();
  530. this.initPieChart();
  531. window.addEventListener('resize', this.resizeTrace, false);
  532. },
  533. /**
  534. * Get the data of proccess summary
  535. */
  536. getProccessSummary() {
  537. const params = {
  538. train_id: this.trainingJobId,
  539. profile: this.summaryPath,
  540. device_id: this.currentCard,
  541. };
  542. RequestService.queryProcessSummary(params).then((resp) => {
  543. this.processSummary.initOver = true;
  544. if (resp && resp.data) {
  545. const data = JSON.parse(JSON.stringify(resp.data));
  546. this.processSummary.count = Object.keys(data).length;
  547. this.dealProcess(data);
  548. } else {
  549. this.dealProcess(null);
  550. this.processSummary.initOver = true;
  551. }
  552. });
  553. },
  554. /**
  555. * router link
  556. * @param { String } path router path
  557. */
  558. viewDetail(path) {
  559. this.$router.push({
  560. path,
  561. query: {
  562. id: this.trainingJobId,
  563. dir: this.summaryPath,
  564. path: this.relativePath,
  565. },
  566. });
  567. },
  568. /**
  569. * chart setOption
  570. */
  571. setPieOption() {
  572. const option = {};
  573. option.tooltip = {
  574. trigger: 'item',
  575. formatter: (params) => {
  576. return `${params.data.name}<br>${params.marker}${params.percent}%`;
  577. },
  578. confine: true,
  579. extraCssText: 'white-space:normal; word-break:break-word;',
  580. };
  581. option.series = [
  582. {
  583. type: 'pie',
  584. center: ['50%', '50%'],
  585. data: this.pieChart.data,
  586. radius: '80%',
  587. label: {
  588. normal: {
  589. show: false,
  590. positionL: 'inner',
  591. },
  592. },
  593. itemStyle: {
  594. normal: {
  595. color: function(params) {
  596. return CommonProperty.pieColorArr[params.dataIndex];
  597. },
  598. },
  599. },
  600. },
  601. ];
  602. this.$nextTick(() => {
  603. const dom = document.getElementById('pieChart');
  604. if (dom) {
  605. this.pieChart.chartDom = echarts.init(dom, null);
  606. } else {
  607. if (this.pieChart.chartDom) {
  608. this.pieChart.chartDom.clear();
  609. }
  610. return;
  611. }
  612. this.pieChart.chartDom.setOption(option, true);
  613. this.pieChart.chartDom.resize();
  614. }, 10);
  615. },
  616. /**
  617. * init chart
  618. */
  619. initPieChart() {
  620. const params = {};
  621. params.params = {
  622. profile: this.summaryPath,
  623. train_id: this.trainingJobId,
  624. };
  625. params.body = {
  626. op_type: 'aicore_type',
  627. device_id: this.currentCard,
  628. filter_condition: {},
  629. sort_condition: {
  630. name: 'execution_time',
  631. type: 'descending',
  632. },
  633. };
  634. RequestService.getProfilerOpData(params)
  635. .then((res) => {
  636. this.pieChart.initOver = true;
  637. if (res && res.data) {
  638. if (res.data.object) {
  639. this.pieChart.data = [];
  640. res.data.object.forEach((item) => {
  641. if (this.pieChart.data && this.pieChart.data.length < 5) {
  642. this.pieChart.data.push({
  643. name: item[0],
  644. value: item[1],
  645. frequency: item[2],
  646. percent: item[3],
  647. });
  648. } else {
  649. if (!this.pieChart.data[5]) {
  650. this.pieChart.data[5] = {
  651. name: 'Other',
  652. value: 0,
  653. percent: 0,
  654. };
  655. }
  656. this.pieChart.data[5].value += item[1];
  657. this.pieChart.data[5].percent += item[3];
  658. }
  659. });
  660. this.setPieOption();
  661. this.pieChart.noData = !!!this.pieChart.data.length;
  662. this.pieChart.topN = this.pieChart.data
  663. .slice(0, Math.min(this.pieChart.data.length, 5))
  664. .map((i) => {
  665. return {
  666. name: i.name,
  667. time: i.value,
  668. frequency: i.frequency,
  669. };
  670. });
  671. }
  672. }
  673. })
  674. .catch(() => {
  675. this.pieChart.data = [];
  676. this.pieChart.noData = true;
  677. this.pieChart.initOver = true;
  678. });
  679. },
  680. /**
  681. * Get the data of training trace
  682. */
  683. queryTrainingTrace() {
  684. const params = {
  685. dir: this.relativePath,
  686. type: 0,
  687. device_id: this.currentCard,
  688. };
  689. RequestService.queryTrainingTrace(params).then(
  690. (res) => {
  691. this.svg.initOver = true;
  692. if (
  693. res &&
  694. res.data &&
  695. res.data.training_trace_graph &&
  696. res.data.training_trace_graph.length
  697. ) {
  698. this.svg.noData = false;
  699. this.removeTrace();
  700. this.$nextTick(() => {
  701. this.packageTraceData(
  702. JSON.parse(JSON.stringify(res.data.training_trace_graph)),
  703. );
  704. });
  705. // Set the display information in tip
  706. if (res.data.summary) {
  707. this.fpBpPercent = res.data.summary.fp_and_bp_percent;
  708. this.fpPercent = res.data.summary.fp_percent;
  709. this.iterationIntervalPercent = res.data.summary.iteration_interval_percent;
  710. this.totalSteps = res.data.summary.total_steps;
  711. this.totalTime = res.data.summary.total_time;
  712. this.tailPercent = res.data.summary.tail_percent;
  713. } else {
  714. this.fpBpPercent = '--';
  715. this.iterationIntervalPercent = '--';
  716. this.totalSteps = '--';
  717. this.totalTime = '--';
  718. this.tailPercent = '--';
  719. }
  720. } else {
  721. this.svg.totalHeight = 0;
  722. this.svg.noData = true;
  723. this.svg.data = [];
  724. this.removeTrace();
  725. }
  726. },
  727. (error) => {
  728. this.svg.totalHeight = 0;
  729. this.svg.noData = true;
  730. this.svg.data = [];
  731. this.svg.initOver = true;
  732. this.removeTrace();
  733. this.fpBpPercent = '--';
  734. this.iterationIntervalPercent = '--';
  735. this.totalSteps = '--';
  736. this.totalTime = '--';
  737. this.tailPercent = '--';
  738. },
  739. );
  740. },
  741. /**
  742. * Encapsulating the data of training trace
  743. * @param {Object} traceGraph Data of training trace
  744. */
  745. packageTraceData(traceGraph) {
  746. this.svg.totalTime = 0;
  747. this.svg.minTime = 0;
  748. this.svg.totalHeight = 0;
  749. const data = [];
  750. if (traceGraph && traceGraph[0] && traceGraph[0][0]) {
  751. this.svg.totalTime = traceGraph[0][0].duration;
  752. this.svg.minTime = this.svg.minRate * this.svg.totalTime;
  753. // If there is data less than the minimum time in each row,
  754. // the data in each row is divided into several rows
  755. traceGraph.forEach((row, index) => {
  756. const rowObj = {
  757. rowCount: 0,
  758. data: [],
  759. height: 0,
  760. startY: this.svg.totalHeight,
  761. };
  762. let obj = [];
  763. for (let i = 0; i < row.length; i++) {
  764. if (row[i].duration < this.svg.minTime) {
  765. if (obj.length) {
  766. rowObj.data.push(obj);
  767. obj = [];
  768. rowObj.rowCount++;
  769. }
  770. rowObj.data.push([row[i]]);
  771. rowObj.rowCount++;
  772. } else {
  773. obj.push(row[i]);
  774. }
  775. if (i === row.length - 1 && obj.length) {
  776. rowObj.data.push(obj);
  777. obj = [];
  778. rowObj.rowCount++;
  779. }
  780. }
  781. rowObj.height =
  782. rowObj.rowCount * this.svg.cellHeight +
  783. (rowObj.rowCount - 1) * this.svg.cellPadding +
  784. (index ? this.svg.rowPadding * 2 : 0);
  785. this.svg.totalHeight += rowObj.height + this.svg.rowMargin;
  786. data.push(rowObj);
  787. });
  788. this.svg.totalHeight += this.svg.rowPadding;
  789. this.svg.data = JSON.parse(JSON.stringify(data));
  790. this.$nextTick(() => {
  791. this.dealTraceData();
  792. });
  793. }
  794. },
  795. /**
  796. * Processing the data of training trace, Control data generation svg
  797. */
  798. dealTraceData() {
  799. const traceDom = document.querySelector('#trace');
  800. if (traceDom) {
  801. this.svg.totalWidth = traceDom.offsetWidth - this.svg.svgPadding * 2;
  802. if (this.svg.data[0] && this.svg.data[0].data.length) {
  803. const svg = traceDom.querySelector('svg');
  804. if (this.svg.totalTime) {
  805. this.svg.data.forEach((item, index) => {
  806. let itemDom = {};
  807. if (index) {
  808. itemDom = this.createMultipleRowContainer(item);
  809. } else {
  810. itemDom = this.createRowContainer(item.data, item.startY);
  811. }
  812. svg.appendChild(itemDom);
  813. });
  814. }
  815. } else {
  816. this.removeTrace();
  817. }
  818. }
  819. },
  820. /**
  821. * Generate a container with multiple rows
  822. * @param {Object} item Multi row data
  823. * @return {Object} Generated DOM object
  824. */
  825. createMultipleRowContainer(item) {
  826. const rectContainer = document.createElementNS(
  827. this.svg.namespaceURI,
  828. 'g',
  829. );
  830. rectContainer.setAttribute('class', 'container');
  831. const rect = document.createElementNS(this.svg.namespaceURI, 'rect');
  832. rect.setAttribute('x', this.svg.svgPadding);
  833. rect.setAttribute('y', item.startY + this.svg.rowPadding);
  834. rect.setAttribute('height', item.height);
  835. rect.setAttribute('width', this.svg.totalWidth);
  836. rect.setAttribute('style', 'fill:#edf0f5;stroke:#E2E2E2;stroke-width:1');
  837. rectContainer.appendChild(rect);
  838. const temp = this.createRowContainer(
  839. item.data,
  840. item.startY + this.svg.rowPadding,
  841. );
  842. rectContainer.appendChild(temp);
  843. return rectContainer;
  844. },
  845. /**
  846. * DOM for generating a single SVG image
  847. * @param {Object} data Data of single SVG image
  848. * @param {Number} startY Start y position of box
  849. * @return {Object}
  850. */
  851. createRowContainer(data, startY) {
  852. const g = document.createElementNS(this.svg.namespaceURI, 'g');
  853. data.forEach((row, index) => {
  854. const y =
  855. startY +
  856. this.svg.rowPadding +
  857. index * (this.svg.cellPadding + this.svg.cellHeight);
  858. row.forEach((i) => {
  859. if (i.duration) {
  860. let temp;
  861. if (i.name) {
  862. temp = this.createRect(i, y);
  863. g.insertBefore(temp, g.querySelector('g'));
  864. } else {
  865. temp = this.createArrow(i, y);
  866. g.appendChild(temp);
  867. }
  868. }
  869. });
  870. });
  871. return g;
  872. },
  873. /**
  874. * Create a box DOM from the data
  875. * @param {Object} data Data of single SVG image
  876. * @param {Number} startY Start y position of box
  877. * @return {Object}
  878. */
  879. createRect(data, startY) {
  880. const color =
  881. data.name && this.svg.colors[data.name]
  882. ? this.svg.colors[data.name]
  883. : this.svg.colors.stream_parallel;
  884. // Start x position of box
  885. const x1 =
  886. (data.start / this.svg.totalTime) * this.svg.totalWidth +
  887. this.svg.svgPadding;
  888. // The width of the box
  889. const width = Math.max(
  890. this.svg.minWidth,
  891. (data.duration / this.svg.totalTime) * this.svg.totalWidth,
  892. );
  893. // Contents of the box
  894. let name = '';
  895. switch (data.name) {
  896. case 'iteration_interval':
  897. name = this.$t('profiling.lterationGap');
  898. break;
  899. case 'fp_and_bp':
  900. name = this.$t('profiling.deviceQueueOpTip');
  901. break;
  902. case 'fp':
  903. name = this.$t('profiling.deviceQueueOpFpTip');
  904. break;
  905. case 'tail':
  906. name = this.$t('profiling.lterationTail');
  907. break;
  908. default:
  909. name = data.name;
  910. break;
  911. }
  912. const textContent = `${name}: ${this.toFixedFun(data.duration, 4)}ms`;
  913. const textWidth = this.getTextWidth(textContent);
  914. const normalSize = data.duration >= this.svg.minTime;
  915. const g = document.createElementNS(this.svg.namespaceURI, 'g');
  916. g.setAttribute('class', 'rect');
  917. const rect = document.createElementNS(this.svg.namespaceURI, 'rect');
  918. rect.setAttribute('x', x1);
  919. rect.setAttribute('y', startY);
  920. rect.setAttribute('height', this.svg.cellHeight);
  921. rect.setAttribute('width', width);
  922. rect.setAttribute('style', `fill:${color[1]};stroke:${color[0]};`);
  923. const foreignObject = document.createElementNS(
  924. this.svg.namespaceURI,
  925. 'foreignObject',
  926. );
  927. foreignObject.textContent = textContent;
  928. foreignObject.setAttribute(
  929. 'x',
  930. normalSize
  931. ? x1
  932. : Math.min(
  933. this.svg.svgPadding * 2 +
  934. this.svg.totalWidth -
  935. textWidth -
  936. this.svg.textMargin,
  937. Math.max(this.svg.textMargin, x1 + width / 2 - textWidth / 2),
  938. ),
  939. );
  940. foreignObject.setAttribute('y', startY);
  941. foreignObject.setAttribute('height', this.svg.cellHeight);
  942. foreignObject.setAttribute('width', width);
  943. foreignObject.setAttribute('style', `color:${color[0]}`);
  944. foreignObject.setAttribute(
  945. 'class',
  946. `content${normalSize ? '' : ' content-mini'}`,
  947. );
  948. const title = document.createElementNS(this.svg.namespaceURI, 'title');
  949. title.textContent = textContent;
  950. g.appendChild(rect);
  951. g.appendChild(foreignObject);
  952. g.appendChild(title);
  953. return g;
  954. },
  955. /**
  956. * Create a arrow DOM from the data
  957. * @param {Object} data Data of single SVG image
  958. * @param {Number} startY Start y position of arrow
  959. * @return {Object}
  960. */
  961. createArrow(data, startY) {
  962. const width = (data.duration / this.svg.totalTime) * this.svg.totalWidth;
  963. const x1 =
  964. (data.start / this.svg.totalTime) * this.svg.totalWidth +
  965. this.svg.svgPadding;
  966. const centerY = startY + this.svg.cellHeight / 2;
  967. const g = document.createElementNS(this.svg.namespaceURI, 'g');
  968. g.setAttribute('class', 'arrow');
  969. const line = document.createElementNS(this.svg.namespaceURI, 'line');
  970. line.setAttribute('y1', centerY);
  971. line.setAttribute('y2', centerY);
  972. line.setAttribute('style', 'stroke:#6c7280;stroke-width:1');
  973. if (width > this.svg.markerPadding) {
  974. line.setAttribute('x1', x1 + this.svg.markerPadding);
  975. line.setAttribute('x2', x1 + width - this.svg.markerPadding);
  976. line.setAttribute('marker-end', 'url(#marker_end)');
  977. line.setAttribute('marker-start', 'url(#marker_start)');
  978. } else {
  979. line.setAttribute('x1', x1);
  980. line.setAttribute('x2', x1 + width);
  981. }
  982. const text = document.createElementNS(this.svg.namespaceURI, 'text');
  983. text.textContent = `${
  984. data.duration === this.svg.totalTime
  985. ? this.$t('profiling.approximateTime')
  986. : ''
  987. }${this.toFixedFun(data.duration, 4)}ms`;
  988. const textWidth = text.textContent
  989. ? this.getTextWidth(text.textContent)
  990. : 0;
  991. // The position of the text cannot go beyond the border of the SVG
  992. text.setAttribute(
  993. 'x',
  994. Math.min(
  995. this.svg.svgPadding * 2 +
  996. this.svg.totalWidth -
  997. textWidth -
  998. this.svg.textMargin,
  999. Math.max(this.svg.textMargin, width / 2 + x1 - textWidth / 2),
  1000. ),
  1001. );
  1002. text.setAttribute('y', centerY - this.svg.fontSize / 2);
  1003. text.setAttribute('font-size', this.svg.fontSize);
  1004. text.setAttribute('fill', 'black');
  1005. const startLine = document.createElementNS(this.svg.namespaceURI, 'line');
  1006. startLine.setAttribute('x1', x1);
  1007. startLine.setAttribute('y1', startY);
  1008. startLine.setAttribute('x2', x1);
  1009. startLine.setAttribute('y2', startY + this.svg.cellHeight);
  1010. startLine.setAttribute('style', 'stroke:#6c7280;stroke-width:1');
  1011. g.appendChild(startLine);
  1012. const endLine = document.createElementNS(this.svg.namespaceURI, 'line');
  1013. endLine.setAttribute('x1', x1 + width);
  1014. endLine.setAttribute('y1', startY);
  1015. endLine.setAttribute('x2', x1 + width);
  1016. endLine.setAttribute('y2', startY + this.svg.cellHeight);
  1017. endLine.setAttribute('style', 'stroke:#6c7280;stroke-width:1');
  1018. g.appendChild(endLine);
  1019. g.appendChild(line);
  1020. g.appendChild(text);
  1021. return g;
  1022. },
  1023. /**
  1024. * Gets the width of a string
  1025. * @param {String} text
  1026. * @return {Number}
  1027. */
  1028. getTextWidth(text) {
  1029. const body = document.querySelector('body');
  1030. const temp = document.createElement('span');
  1031. temp.style['font-size'] = '12px';
  1032. temp.textContent = text;
  1033. body.appendChild(temp);
  1034. const textWidth = temp.offsetWidth;
  1035. body.removeChild(temp);
  1036. return textWidth;
  1037. },
  1038. /**
  1039. * Remove SVG DOM from page
  1040. */
  1041. removeTrace() {
  1042. const svgDom = document.querySelector('#trace svg');
  1043. if (svgDom) {
  1044. const gDoms = svgDom.children;
  1045. if (gDoms) {
  1046. for (let i = 0; i < gDoms.length; i++) {
  1047. if (gDoms[i].nodeName === 'g') {
  1048. svgDom.removeChild(gDoms[i--]);
  1049. }
  1050. }
  1051. }
  1052. }
  1053. },
  1054. /**
  1055. * Respond to the reset event and update the page display
  1056. */
  1057. resizeTrace() {
  1058. if (this.svg.resizeTimer) {
  1059. clearTimeout(this.svg.resizeTimer);
  1060. }
  1061. this.svg.resizeTimer = setTimeout(() => {
  1062. this.removeTrace();
  1063. this.dealTraceData();
  1064. this.svg.resizeTimer = null;
  1065. }, 500);
  1066. },
  1067. /**
  1068. * Query the data of time line
  1069. */
  1070. queryTimeline() {
  1071. this.timeLine.waiting = true;
  1072. this.timeLine.disable = true;
  1073. const params = {
  1074. dir: this.relativePath,
  1075. device_id: this.currentCard,
  1076. };
  1077. RequestService.queryTimlineInfo(params)
  1078. .then((res) => {
  1079. this.timelineInfo.initOver = true;
  1080. if (res && res.data) {
  1081. this.timelineInfo.noData = false;
  1082. this.timelineInfo.totalTime =
  1083. this.toFixedFun(res.data.total_time, 4) ||
  1084. (res.data.total_time === 0 ? 0 : '--');
  1085. this.timelineInfo.streamNum =
  1086. res.data.num_of_streams ||
  1087. (res.data.num_of_streams === 0 ? 0 : '--');
  1088. this.timelineInfo.opNum =
  1089. res.data.num_of_ops || (res.data.num_of_ops === 0 ? 0 : '--');
  1090. this.timelineInfo.opTimes =
  1091. res.data.op_exe_times || (res.data.op_exe_times === 0 ? 0 : '--');
  1092. } else {
  1093. this.timelineInfo.noData = true;
  1094. }
  1095. })
  1096. .catch(() => {
  1097. this.timelineInfo.noData = true;
  1098. this.timelineInfo.initOver = true;
  1099. });
  1100. RequestService.queryTimeline(params)
  1101. .then((res) => {
  1102. this.timeLine.waiting = false;
  1103. if (res && res.data && res.data.length) {
  1104. this.timeLine.data = JSON.stringify(res.data);
  1105. this.timeLine.disable = false;
  1106. }
  1107. })
  1108. .catch(() => {
  1109. this.timeLine.waiting = false;
  1110. });
  1111. },
  1112. /**
  1113. * Download timeline data file
  1114. */
  1115. downloadTimelineFile() {
  1116. const downloadLink = document.createElement('a');
  1117. downloadLink.download = this.getDocName();
  1118. downloadLink.style.display = 'none';
  1119. const blob = new Blob([this.timeLine.data]);
  1120. downloadLink.href = URL.createObjectURL(blob);
  1121. document.body.appendChild(downloadLink);
  1122. downloadLink.click();
  1123. document.body.removeChild(downloadLink);
  1124. },
  1125. /**
  1126. * Set the data of process
  1127. * @param {Object} data The data of process
  1128. */
  1129. dealProcess(data) {
  1130. this.processSummary.device = {
  1131. empty: 0,
  1132. full: 0,
  1133. total: 0,
  1134. };
  1135. this.processSummary.get_next = {
  1136. empty: 0,
  1137. full: 0,
  1138. total: 0,
  1139. };
  1140. this.processSummary.noData = true;
  1141. if (data && Object.keys(data).length) {
  1142. if (data.device_queue_info && data.device_queue_info.summary) {
  1143. this.deviceInfoShow = true;
  1144. this.processSummary.device = {
  1145. empty: data.device_queue_info.summary.empty_batch_count,
  1146. full: data.device_queue_info.summary.full_batch_count,
  1147. total: data.device_queue_info.summary.total_batch,
  1148. };
  1149. }
  1150. if (data.get_next_queue_info && data.get_next_queue_info.summary) {
  1151. this.queueInfoShow = true;
  1152. this.processSummary.get_next = {
  1153. empty: data.get_next_queue_info.summary.empty_batch_count,
  1154. full: data.get_next_queue_info.summary.full_batch_count,
  1155. total: data.get_next_queue_info.summary.total_batch,
  1156. };
  1157. }
  1158. this.processSummary.noData = false;
  1159. }
  1160. },
  1161. /**
  1162. * Generate a download file name
  1163. * @return {String}
  1164. */
  1165. getDocName() {
  1166. const dealNumber = (value) => {
  1167. const prefix = value < 10 ? '0' : '';
  1168. return prefix + value;
  1169. };
  1170. const date = new Date();
  1171. const year = date.getFullYear();
  1172. const mouth = dealNumber(date.getMonth() + 1);
  1173. const day = dealNumber(date.getDate());
  1174. const hour = dealNumber(date.getHours());
  1175. const minute = dealNumber(date.getMinutes());
  1176. const second = dealNumber(date.getSeconds());
  1177. const millisecond = date.getMilliseconds();
  1178. const timestamp = `${year}${mouth}${day}${hour}${minute}${second}${millisecond}`;
  1179. return `timeline_${this.trainingJobId}_${this.currentCard}_${timestamp}.json`;
  1180. },
  1181. /**
  1182. * Keep the number with n decimal places.
  1183. * @param {Number} num
  1184. * @param {Number} pow Number of decimal places
  1185. * @return {Number}
  1186. */
  1187. toFixedFun(num, pow) {
  1188. if (isNaN(num) || isNaN(pow) || !num || !pow) {
  1189. return num;
  1190. }
  1191. return Math.round(num * Math.pow(10, pow)) / Math.pow(10, pow);
  1192. },
  1193. },
  1194. destroyed() {
  1195. window.removeEventListener('resize', this.resizeTrace, false);
  1196. this.$bus.$off('collapse');
  1197. },
  1198. };
  1199. </script>
  1200. <style>
  1201. .el-tooltip-popper {
  1202. max-width: 500px;
  1203. }
  1204. .tooltip-container .pro-dash-tooltip {
  1205. line-height: 20px;
  1206. padding: 10px;
  1207. }
  1208. .tooltip-container .pro-dash-tooltip .font-style {
  1209. font-weight: bold;
  1210. }
  1211. .tooltip-container .pro-dash-tooltip .font-size-style {
  1212. font-weight: bold;
  1213. font-size: 16px;
  1214. }
  1215. .pro-router-wrap {
  1216. height: 100%;
  1217. }
  1218. .pro-router-wrap > div {
  1219. float: left;
  1220. height: 100%;
  1221. }
  1222. .pro-router-wrap > div > div {
  1223. border: 1px solid #eee;
  1224. border-radius: 4px;
  1225. }
  1226. .pro-router-wrap > div .title-wrap {
  1227. padding: 15px;
  1228. }
  1229. .pro-router-wrap > div .title-wrap .title {
  1230. float: left;
  1231. font-weight: bold;
  1232. font-size: 16px;
  1233. }
  1234. .pro-router-wrap > div .title-wrap .tip-icon {
  1235. float: right;
  1236. margin-right: 10px;
  1237. font-size: 20px;
  1238. color: #6c7280;
  1239. }
  1240. .pro-router-wrap > div .title-wrap .tip-icon .el-icon-warning {
  1241. cursor: pointer;
  1242. }
  1243. .pro-router-wrap > div .title-wrap .tip-icon .el-icon-warning:hover::before {
  1244. color: #00a5a7;
  1245. }
  1246. .pro-router-wrap > div .title-wrap .view-detail {
  1247. float: right;
  1248. cursor: pointer;
  1249. font-size: 12px;
  1250. height: 24px;
  1251. line-height: 24px;
  1252. }
  1253. .pro-router-wrap > div .title-wrap .view-detail a {
  1254. color: #00a5a7 !important;
  1255. padding-right: 6px;
  1256. }
  1257. .pro-router-wrap > div .title-wrap .view-detail button {
  1258. color: #00a5a7;
  1259. border: none;
  1260. background-color: #fff;
  1261. cursor: pointer;
  1262. }
  1263. .pro-router-wrap > div .title-wrap .view-detail button.disabled {
  1264. cursor: not-allowed;
  1265. color: #c0c4cc;
  1266. }
  1267. .pro-router-wrap > div .title-wrap::after {
  1268. content: '';
  1269. clear: both;
  1270. display: block;
  1271. }
  1272. .pro-router-wrap > div .loading-icon {
  1273. margin-left: 5px;
  1274. }
  1275. .pro-router-wrap > div .coming-soon-content {
  1276. height: calc(100% - 50px);
  1277. position: relative;
  1278. }
  1279. .pro-router-wrap > div .coming-soon-content .coming-soon-container {
  1280. text-align: center;
  1281. position: absolute;
  1282. top: 50%;
  1283. left: 50%;
  1284. border-radius: 5px;
  1285. -webkit-transform: translate(-50%, -50%);
  1286. -moz-transform: translate(-50%, -50%);
  1287. transform: translate(-50%, -50%);
  1288. }
  1289. .pro-router-wrap > div .coming-soon-content .coming-soon-text {
  1290. font-size: 16px;
  1291. }
  1292. .pro-router-wrap .pro-router-left {
  1293. width: calc(100% - 400px);
  1294. padding-right: 15px;
  1295. }
  1296. .pro-router-wrap .pro-router-left .step-trace {
  1297. height: 45%;
  1298. margin-bottom: 15px;
  1299. }
  1300. .pro-router-wrap .pro-router-left .step-trace .trace-container {
  1301. width: 100%;
  1302. height: calc(100% - 54px);
  1303. overflow: auto;
  1304. }
  1305. .pro-router-wrap .pro-router-left .step-trace .trace-container .training-trace {
  1306. position: relative;
  1307. height: 0;
  1308. }
  1309. .pro-router-wrap .pro-router-left .step-trace .trace-container .training-trace .content {
  1310. overflow: hidden;
  1311. text-align: center;
  1312. text-overflow: ellipsis;
  1313. white-space: nowrap;
  1314. font-size: 12px;
  1315. line-height: 40px;
  1316. }
  1317. .pro-router-wrap .pro-router-left .step-trace .trace-container .training-trace .content-mini {
  1318. overflow: visible;
  1319. }
  1320. .pro-router-wrap .pro-router-left .minddata {
  1321. height: calc(55% - 15px);
  1322. }
  1323. .pro-router-wrap .pro-router-left .minddata .pipeline-container {
  1324. width: 100%;
  1325. padding: 20px 20px;
  1326. height: calc(100% - 52px);
  1327. display: flex;
  1328. font-size: 0;
  1329. align-items: baseline;
  1330. }
  1331. .pro-router-wrap .pro-router-left .minddata .pipeline-container .cell-container {
  1332. width: 20%;
  1333. min-width: 110px;
  1334. padding: 20px 0;
  1335. border: 2px solid transparent;
  1336. }
  1337. .pro-router-wrap .pro-router-left .minddata .pipeline-container .cell-container .title {
  1338. font-size: 14px;
  1339. line-height: 20px;
  1340. padding: 0 0 0 10px;
  1341. font-weight: bold;
  1342. }
  1343. .pro-router-wrap .pro-router-left .minddata .pipeline-container .data-process {
  1344. background-color: #e3f8eb;
  1345. }
  1346. .pro-router-wrap .pro-router-left .minddata .pipeline-container .data-process .title {
  1347. border-left: 2px solid #00a5a7;
  1348. }
  1349. .pro-router-wrap .pro-router-left .minddata .pipeline-container .device_queue_op {
  1350. background-color: #e1f2ff;
  1351. }
  1352. .pro-router-wrap .pro-router-left .minddata .pipeline-container .device_queue_op .title {
  1353. border-left: 2px solid #6cbfff;
  1354. }
  1355. .pro-router-wrap .pro-router-left .minddata .pipeline-container .get-next {
  1356. background-color: #fef4dd;
  1357. }
  1358. .pro-router-wrap .pro-router-left .minddata .pipeline-container .get-next .title {
  1359. border-left: 2px solid #fdca5a;
  1360. }
  1361. .pro-router-wrap .pro-router-left .minddata .pipeline-container .queue-container {
  1362. width: 20%;
  1363. position: relative;
  1364. }
  1365. .pro-router-wrap .pro-router-left .minddata .pipeline-container .queue-container .img {
  1366. width: 100%;
  1367. height: 24px;
  1368. margin-top: 30px;
  1369. }
  1370. .pro-router-wrap .pro-router-left .minddata .pipeline-container .queue-container .img .edge {
  1371. width: calc(50% - 40px);
  1372. display: inline-block;
  1373. vertical-align: middle;
  1374. }
  1375. .pro-router-wrap .pro-router-left .minddata .pipeline-container .queue-container .img .edge img {
  1376. width: 100%;
  1377. }
  1378. .pro-router-wrap .pro-router-left .minddata .pipeline-container .queue-container .img .icon {
  1379. padding: 0 20px;
  1380. display: inline-block;
  1381. vertical-align: middle;
  1382. }
  1383. .pro-router-wrap .pro-router-left .minddata .pipeline-container .queue-container .img .icon img {
  1384. padding: 3px;
  1385. border: 2px solid transparent;
  1386. }
  1387. .pro-router-wrap .pro-router-left .minddata .pipeline-container .queue-container .title {
  1388. text-align: center;
  1389. font-size: 14px;
  1390. margin-top: 10px;
  1391. font-weight: bold;
  1392. }
  1393. .pro-router-wrap .pro-router-left .minddata .pipeline-container .queue-container .description {
  1394. position: absolute;
  1395. font-size: 12px;
  1396. line-height: 12px;
  1397. white-space: nowrap;
  1398. overflow: visible;
  1399. width: 100%;
  1400. text-align: center;
  1401. }
  1402. .pro-router-wrap .pro-router-left .minddata .pipeline-container .queue-container .description .line {
  1403. width: 1px;
  1404. height: 40px;
  1405. margin: 20px 0;
  1406. border-left: 1px solid #979797;
  1407. display: inline-block;
  1408. }
  1409. .pro-router-wrap .pro-router-left .minddata .pipeline-container .queue-container .description .item {
  1410. font-size: 12px;
  1411. line-height: 16px;
  1412. white-space: normal;
  1413. overflow: visible;
  1414. }
  1415. .pro-router-wrap .pro-router-left .minddata .pipeline-container .queue-container .description .item .num {
  1416. white-space: nowrap;
  1417. color: #07a695;
  1418. }
  1419. .pro-router-wrap .pro-router-right {
  1420. width: 400px;
  1421. }
  1422. .pro-router-wrap .pro-router-right .op-time-consume {
  1423. height: calc(60% - 15px);
  1424. margin-bottom: 15px;
  1425. }
  1426. .pro-router-wrap .pro-router-right .op-time-consume .time-list {
  1427. height: calc(40% - 52px);
  1428. }
  1429. .pro-router-wrap .pro-router-right .op-time-consume .time-list .item {
  1430. height: 25px;
  1431. line-height: 25px;
  1432. padding: 0 20px;
  1433. }
  1434. .pro-router-wrap .pro-router-right .op-time-consume .time-list .item > span {
  1435. display: inline-block;
  1436. height: 100%;
  1437. vertical-align: middle;
  1438. }
  1439. .pro-router-wrap .pro-router-right .op-time-consume .time-list .item .index {
  1440. color: white;
  1441. background-color: #6c92fa;
  1442. width: 20px;
  1443. height: 20px;
  1444. border-radius: 20px;
  1445. text-align: center;
  1446. vertical-align: middle;
  1447. line-height: 20px;
  1448. }
  1449. .pro-router-wrap .pro-router-right .op-time-consume .time-list .item .name {
  1450. margin-left: 10px;
  1451. width: calc(50% - 30px);
  1452. text-overflow: ellipsis;
  1453. overflow: hidden;
  1454. }
  1455. .pro-router-wrap .pro-router-right .op-time-consume .time-list .item .num {
  1456. width: 20%;
  1457. }
  1458. .pro-router-wrap .pro-router-right .op-time-consume .time-list .item .time {
  1459. width: 30%;
  1460. position: relative;
  1461. }
  1462. .pro-router-wrap .pro-router-right .op-time-consume .time-list .item .time span {
  1463. display: inline-block;
  1464. position: absolute;
  1465. left: 0;
  1466. height: 20px;
  1467. }
  1468. .pro-router-wrap .pro-router-right .op-time-consume .time-list .item .time .bar {
  1469. background-color: #cceded;
  1470. top: 2px;
  1471. }
  1472. .pro-router-wrap .pro-router-right .op-time-consume .time-list .item .time .value {
  1473. line-height: 25px;
  1474. height: 25px;
  1475. }
  1476. .pro-router-wrap .pro-router-right .time-line {
  1477. height: 40%;
  1478. overflow: hidden;
  1479. }
  1480. .pro-router-wrap .pro-router-right .time-line .timeline-info {
  1481. width: 100%;
  1482. height: calc(100% - 54px);
  1483. padding-left: 36px;
  1484. }
  1485. .pro-router-wrap .pro-router-right .time-line .info-line {
  1486. line-height: 30px;
  1487. }
  1488. .pro-router-wrap .op-time-content {
  1489. height: calc(100% - 54px);
  1490. overflow: auto;
  1491. }
  1492. .pro-router-wrap .pie-chart {
  1493. width: 100%;
  1494. height: 260px;
  1495. overflow: hidden;
  1496. }
  1497. .pro-router-wrap .image-noData {
  1498. width: 100%;
  1499. height: calc(100% - 52px);
  1500. display: flex;
  1501. justify-content: center;
  1502. align-items: center;
  1503. flex-direction: column;
  1504. }
  1505. .pro-router-wrap .image-noData p {
  1506. font-size: 16px;
  1507. padding-top: 10px;
  1508. }
  1509. </style>