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>