github.com/e154/smart-home@v0.17.2-0.20240311175135-e530a6e5cd45/static_source/admin/src/views/Automation/components/TaskTelemetry.vue (about) 1 <script setup lang="ts"> 2 import {nextTick, onMounted, onUnmounted, PropType, ref, watch} from 'vue' 3 import {ElEmpty} from 'element-plus' 4 import {ApiTask, ApiTelemetryItem} from "@/api/stub"; 5 import api from "@/api/api"; 6 import {UUID} from "uuid-generator-ts"; 7 import stream from "@/api/stream"; 8 import {EventTaskCompleted} from "@/api/types"; 9 import {debounce} from "lodash-es"; 10 import {EChartsOption} from "echarts"; 11 import {Echart} from '@/components/Echart' 12 import * as echarts from 'echarts/core' 13 14 const telemetry = ref<ApiTelemetryItem[]>([]) 15 const props = defineProps({ 16 task: { 17 type: Object as PropType<Nullable<ApiTask>>, 18 default: () => null 19 } 20 }) 21 22 watch( 23 () => props.task, 24 (val) => { 25 if (!val) return 26 telemetry.value = val?.telemetry || undefined; 27 }, 28 { 29 immediate: true 30 } 31 ) 32 33 const currentID = ref('') 34 onMounted(() => { 35 const uuid = new UUID() 36 currentID.value = uuid.getDashFreeUUID() 37 stream.subscribe('event_task_completed', currentID.value, onEventTaskActivated); 38 }) 39 40 onUnmounted(() => { 41 stream.unsubscribe('event_task_completed', currentID.value); 42 }) 43 44 const fetch = debounce(async () => { 45 const res = await api.v1.automationServiceGetTask(props.task?.id) 46 .catch(() => { 47 }) 48 .finally(() => { 49 }) 50 if (res) { 51 const task = res.data as ApiTask; 52 telemetry.value = task?.telemetry; 53 getOptions() 54 } 55 }, 100) 56 57 const onEventTaskActivated = (event: EventTaskCompleted) => { 58 if (event.id != props.task?.id) { 59 return; 60 } 61 fetch() 62 } 63 64 const chartOptions = ref<EChartsOption>({}) 65 66 const reloadKey = ref(0) 67 const reload = () => { 68 reloadKey.value += 1 69 } 70 71 const renderItem = (params, api) => { 72 let categoryIndex = api.value(0); 73 let start = api.coord([api.value(1), categoryIndex]); 74 let end = api.coord([api.value(2), categoryIndex]); 75 let height = api.size([0, 1])[1] * 0.6; 76 let rectShape = echarts.graphic.clipRectByRect( 77 { 78 x: start[0], 79 y: start[1] - height / 2, 80 width: end[0] - start[0], 81 height: height 82 }, 83 { 84 x: params.coordSys.x, 85 y: params.coordSys.y, 86 width: params.coordSys.width, 87 height: params.coordSys.height 88 } 89 ); 90 return ( 91 rectShape && { 92 type: 'rect', 93 transition: ['shape'], 94 shape: rectShape, 95 style: api.style() 96 } 97 ); 98 } 99 100 var colors = ['#7b9ce1', '#bd6d6c', '#75d874', '#e0bc78', '#dc77dc', '#72b362', '#7b9ce1', '#bd6d6c', '#75d874', '#e0bc78', '#dc77dc', '#72b362']; 101 102 const getID = (attrs: Record<string, string>) => { 103 if (!attrs) { 104 return '' 105 } 106 if (attrs['id']) { 107 return `(id: ${attrs['id']})` || '' 108 } else { 109 return '' 110 } 111 } 112 const getOptions = () => { 113 114 let startTime = 0; 115 let categories = []; 116 let data = []; 117 118 if (!telemetry.value) { 119 return 120 } 121 122 for (const item of telemetry.value) { 123 const label = `level${item.level}` 124 if (categories.indexOf(label) === -1) { 125 categories.push(label) 126 } 127 const start = item?.start; 128 const end = item?.end; 129 if (startTime === 0) { 130 startTime = start || 0; 131 } 132 const timeEstimate = item?.timeEstimate; 133 data.push({ 134 name: item.name + getID(item.attributes), 135 value: [item.level - 1, start, end, timeEstimate], 136 itemStyle: { 137 normal: { 138 color: colors[item.num], 139 } 140 } 141 }) 142 } 143 144 let options: EChartsOption = { 145 tooltip: { 146 formatter: function (params) { 147 return params.marker + params.name + ': ' + params.value[3] + ' ms'; 148 } 149 }, 150 grid: { 151 height: 300 152 }, 153 xAxis: { 154 min: startTime, 155 scale: true, 156 axisLabel: { 157 formatter: function (val) { 158 return Math.max(0, val - startTime) + ' ms'; 159 } 160 } 161 }, 162 yAxis: { 163 data: categories 164 }, 165 series: [ 166 { 167 type: 'custom', 168 renderItem: renderItem, 169 itemStyle: { 170 opacity: 0.8 171 }, 172 encode: { 173 x: [1, 2], 174 y: 0 175 }, 176 data: data 177 } 178 ] 179 } 180 181 nextTick(() => { 182 chartOptions.value = options; 183 }) 184 185 } 186 187 getOptions() 188 189 </script> 190 191 <template> 192 <div class="h-[100%] w-[100%]" style="height: 400px" v-if="telemetry && telemetry.length"> 193 <Echart :options="chartOptions" :key="reloadKey"/> 194 </div> 195 <ElEmpty v-if="!telemetry || !telemetry.length" :rows="5"/> 196 </template>