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 50 kB

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