Browse Source

智能故障诊断

Gaokun Wang 1 week ago
parent
commit
41eb0f7658

+ 5 - 0
src/api/als/algorithm.js

@@ -35,6 +35,11 @@ export const executeFault = async (data) => {
   return await post('/als/algorithm/execute/fault', data)
 }
 
+// 智能故障辅助决策
+export const executeFaultIntell = async (data) => {
+  return await post('/als/algorithm/execute/faultIntell', data)
+}
+
 // 执行退化评估算法
 export const executeEvaluation = async (data) => {
   return await post('/als/algorithm/execute/evaluation', data)

+ 4 - 3
src/api/als/intelligentQA.js

@@ -1,9 +1,10 @@
 import { get, post, deletes } from '@/http/index'
 
 // 问答接口
-// export const handlerAsk = async (data) => {
-//   return await post('/kgqa/ask/', data)
-// }
+// 问答接口
+export const handlerQAndA = async (data) => {
+  return await post('/kgqa/ask/', data)
+}
 // 问答接口
 export const handlerAsk = async (data) => {
   return await post('/als/algorithm/execute/qa', data)

+ 9 - 0
src/router/modules/faultDiagnosis.js

@@ -33,6 +33,15 @@ export default [
           parent: 'FaultManage',
           title: '智能故障诊断'
         }
+      },
+      {
+        name: 'FaultDiagnosisIntell',
+        path: '/faultDiagnosis-intell',
+        component: () => import('@/views/als/faultDiagnosisIntell/index.vue'),
+        meta: {
+          parent: 'FaultManage',
+          title: '智能辅助维修决策'
+        }
       }
     ]
   }

+ 547 - 0
src/views/als/faultDiagnosisIntell/index.vue

@@ -0,0 +1,547 @@
+<template>
+  <div class="view-table-content">
+    <div class="view-dataSpecies-left">
+      <MenuTree :currentNodeKey="currentNodeKey" nodeKey="id" :treedata="menuTreeData" @TreeNodeclick="treeNodeClick" v-bind="treeObj"> </MenuTree>
+    </div>
+    <div class="view-dataSpecies-right" v-loading="loading">
+      <div class="view-dataType-title">
+        <div class="view-dataType-title-btn"></div>
+        <div class="view-dataType-title-search">
+          <el-input placeholder="请输入架次号" v-model="keyWordData" class="input1">
+            <el-button slot="append" icon="el-icon-search" @click="searchClick"></el-button>
+          </el-input>
+        </div>
+      </div>
+      <div class="view-dataType-table">
+        <LTable ref="table" @selection-change="selection" :defaultFetch="false" :fetch="fetch" :columns="columns" :dataSource="tableData" :options="options" :pagination="tableRequset"></LTable>
+      </div>
+      <el-dialog title="诊断结果" :visible.sync="resultVisible" width="1200px">
+        <LTable ref="resultTable" :showColumnSetting="false" :defaultFetch="false" :columns="resultColumns" :dataSource="resultTableData" :options="resultOptions" :pagination="resultTableRequset"></LTable>
+      </el-dialog>
+      <el-dialog title="执行进度" :visible.sync="progressVisible" width="800px">
+        <el-progress :text-inside="true" :stroke-width="24" :percentage="percentage" status="success"></el-progress>
+      </el-dialog>
+      <el-dialog title="结果展示" :visible.sync="resultShowVisible" :before-close="resultShowDialogClose" v-loading="tagLoading" width="800px">
+        <el-descriptions border :column="1">
+          <el-descriptions-item label="评分">
+            <div style="display: flex; align-items: center">
+              <el-rate v-model="changeScore" disabled text-color="#ff9900" score-template="{value}"> </el-rate>
+              <span style="margin-left: 3px; color: #f7ba2a; font-size: 1.2rem">{{ diagnosisResult.score }}</span>
+            </div>
+          </el-descriptions-item>
+          <el-descriptions-item label="退化结果">
+            <el-tag class="fault" style="margin-right: 20px" type="danger" v-for="(item, index) in diagnosisResult.degradation" :key="index" @click="handelQA(item)">{{ item }}</el-tag>
+          </el-descriptions-item>
+          <el-descriptions-item label="故障结果">
+            <el-tag class="fault" style="margin-right: 20px" type="danger" v-for="(item, index) in diagnosisResult.fault" :key="index" @click="handelQA(item)">{{ item }}</el-tag>
+          </el-descriptions-item>
+        </el-descriptions>
+        <span slot="footer" class="dialog-footer">
+          <el-button type="primary" @click="resultShowDialogClose">确 定</el-button>
+        </span>
+      </el-dialog>
+      <el-dialog title="问答结果" :visible.sync="QAVisible" width="1000px" :before-close="QADialogClose" append-to-body>
+        <div style="color: white; font-size: 20px; margin-bottom: 20px">
+          {{ QAData.graphAnswer.answer }}
+        </div>
+        <graphECharts :width="950" :height="400" v-if="QAData.graphAnswer.graph" :graphData="QAData.graphAnswer.graph"></graphECharts>
+        <span slot="footer" class="dialog-footer">
+          <el-button type="primary" @click="QAVisible = false">确 定</el-button>
+        </span>
+      </el-dialog>
+    </div>
+  </div>
+</template>
+
+<script>
+import store from '@/store'
+import { getDataImport } from '@/api/als/dataImport'
+import { getFaultDiagnosisResult } from '@/api/als/faultDiagnosisResult'
+import { getAircaftCatalogTree } from '@/api/als/sideTree'
+import { getAircaftCatalogAll } from '@/api/als/aircraft'
+import { deepClone, debounce } from '@/utils/index'
+import { handleTree, flattenTree } from '../utils/common'
+import { getAirConfiguration } from '@/api/als/airConfiguration'
+import { executeFaultIntell } from '@/api/als/algorithm'
+import { getListByIdsApi } from '@/api/als/oss'
+import { handlerQAndA } from '@/api/als/intelligentQA'
+import graphECharts from '@/views/als/components/Charts/graph.vue'
+
+export default {
+  name: 'FaultDiagnosis',
+  components: { graphECharts },
+  data() {
+    // 这里存放数据
+    return {
+      loading: false,
+      resultVisible: false,
+      progressVisible: false,
+      resultShowVisible: false,
+      QAVisible: false,
+      keyWordData: '',
+      aircaftModelIdList: [],
+      currentNodeKey: '',
+      currentNode: {},
+      menuTreeData: [],
+      treeObj: {
+        title: '所属机种',
+        activityheight: '275px',
+        searchIcon: false,
+        configure: {
+          children: 'children',
+          label: 'label'
+        }
+      },
+      typeTree: {
+        children: 'children',
+        label: 'label'
+      },
+      searchValue: '',
+      columns: [
+        {
+          prop: 'aircraftId',
+          label: '编目',
+          render: (h, params) => {
+            const matchedItem = this.aircaftCatalogAll.find((item) => params.row.aircraftId.trim() === item.aircaftCatalogId.trim())
+            if (matchedItem) {
+              return h('span', matchedItem.aircaftCatalogCode)
+            } else {
+              return h('span', {}, '')
+            }
+          }
+        },
+        {
+          prop: 'sortieNo',
+          label: '架次号'
+        },
+        {
+          prop: 'flightDate',
+          label: '飞行时间'
+        },
+        {
+          prop: 'source',
+          label: '数据源',
+          render: (h, params) => {
+            if (params.row.source == 1) {
+              return h('span', '飞参数据')
+            } else {
+              return h('span', 'MDC数据')
+            }
+          }
+        },
+        {
+          prop: 'status',
+          label: '状态',
+          // 1 未处理,2  已处理
+          render: (h, params) => {
+            if (params.row.status == '1') {
+              return h('span', '未处理')
+            } else if (params.row.status == '2') {
+              return h('span', { class: 'success-state' }, '已处理')
+            } else {
+              return h('span', { class: 'warning-state' }, '异常')
+            }
+          }
+        },
+        {
+          button: true,
+          label: '操作',
+          width: '240px',
+          group: [
+            {
+              name: '故障诊断',
+              type: 'text',
+              round: false,
+              plain: false,
+              onClick: (row, index, scope) => {
+                this.faultExecute(row)
+              }
+            },
+            {
+              name: '查看',
+              type: 'text',
+              statusKey: 'status',
+              unDisableKey: '2',
+              round: false,
+              plain: false,
+              onClick: (row, index, scope) => {
+                this.checkRelustList(row)
+              }
+            }
+          ]
+        }
+      ],
+      options: {
+        stripe: false, // 斑马纹
+        mutiSelect: true, // 多选框
+        index: false, // 显示序号, 多选则 mutiSelect
+        loading: false, // 表格动画
+        initTable: false, // 是否一挂载就加载数据
+        border: true,
+        height: 'calc(100vh - 300px)'
+      },
+      tableCheckItems: [],
+      tableData: [],
+      tableRequset: {
+        total: 0,
+        pageIndex: 1,
+        pageSize: 20,
+        searchValue: ''
+      },
+      resultColumns: [
+        {
+          prop: 'modelType',
+          label: '算法名称'
+        },
+        {
+          prop: 'createTime',
+          label: '执行时间',
+          render: (h, params) => {
+            return h('span', params.row.createTime.split(' ')[0])
+          }
+        },
+        {
+          prop: 'status',
+          label: '状态',
+          render: (h, params) => {
+            return h('span', { class: 'success-state' }, '成功')
+          }
+        },
+        {
+          button: true,
+          label: '结果',
+          width: '240px',
+          group: [
+            {
+              name: '查看结果',
+              type: 'text',
+              round: false,
+              plain: false,
+              onClick: (row, index, scope) => {
+                this.checkRelustShow(row)
+              }
+            }
+          ]
+        }
+      ],
+      resultOptions: {
+        stripe: false, // 斑马纹
+        mutiSelect: true, // 多选框
+        index: false, // 显示序号, 多选则 mutiSelect
+        loading: false, // 表格动画
+        initTable: false, // 是否一挂载就加载数据
+        border: true,
+        height: 'calc(100vh - 600px)'
+      },
+      resultTableCheckItems: [],
+      resultTableData: [],
+      resultTableRequset: {
+        total: 0,
+        pageIndex: 1,
+        pageSize: 10,
+        searchValue: ''
+      },
+      debounceFn: debounce(this.fetch, 500),
+      aircaftCatalogAll: [],
+      form: {
+        id: '',
+        sortieNo: '',
+        ossId: '',
+        aircraftId: '',
+        aircraftType: '',
+        flightDate: '',
+        status: '',
+        result: '',
+        partId: null
+      },
+      currentSortieNo: '',
+      partsData: [],
+      allAirConfig: [],
+      percentage: 0,
+      resultShowData: {
+        url: '',
+        result: ''
+      },
+      diagnosisResult: {
+        degradation: [],
+        fault: [],
+        score: null
+      },
+      changeScore: null,
+      QAData: {
+        userId: '',
+        graphAnswer: {}
+      },
+      tagLoading: false
+    }
+  },
+  watch: {
+    keyWord() {
+      this.tableRequset.pageIndex = 1
+      this.debounceFn()
+    }
+  },
+  computed: {},
+  mounted() {
+    this.getAircaftCatalogTreeAPI()
+  },
+  methods: {
+    async getAircaftCatalogTreeAPI(params) {
+      try {
+        const { data } = await getAircaftCatalogTree(params)
+        this.menuTreeData = data
+        const getAircaftCatalogAllParams = {
+          keyWord: '',
+          aircaftModelIdList: []
+        }
+        const { data: data2 } = await getAircaftCatalogAll(getAircaftCatalogAllParams)
+        this.aircaftCatalogAll = data2
+        const { data: airConfigData } = await getAirConfiguration()
+        this.allAirConfig = airConfigData
+        if (data.length) {
+          this.currentNodeKey = data[0].id
+          this.currentNode = data[0]
+          this.aircaftModelIdList = this.getTreeLeafData(data[0]?.children)
+            .map((e) => e.id)
+            .toString()
+          this.getDataImportAPI({ aircraftId: this.aircaftModelIdList })
+        }
+      } catch (error) {}
+    },
+
+    getTreeLeafData(list) {
+      const newArr = []
+      function getLeaf(data, arr) {
+        data.forEach((e) => {
+          if (e.type === 2) {
+            arr.push(e)
+          }
+          if (e.children.length) {
+            getLeaf(e.children, arr)
+          }
+        })
+      }
+      getLeaf(list, newArr)
+      return newArr
+    },
+
+    async getFaultDiagnosisResultAPI(params) {
+      if (this.$refs.resultTable) this.$refs.resultTable.clearSelection()
+      const { pageSize, pageIndex } = this.resultTableRequset
+      const {
+        data: { list, total }
+      } = await getFaultDiagnosisResult({ pageSize, pageNum: pageIndex, ...params })
+      this.resultTableData = list
+      this.resultTableRequset.total = total
+    },
+
+    async getDataImportAPI(params) {
+      if (this.$refs.table) this.$refs.table.clearSelection()
+      const { keyWord } = this
+      const { pageSize, pageIndex } = this.tableRequset
+      const {
+        data: { list, total }
+      } = await getDataImport({ pageSize, pageNum: pageIndex, ...params, source: 1 })
+      this.tableData = list
+      this.tableRequset.total = total
+    },
+
+    fetch() {
+      this.getDataImportAPI({ aircraftId: this.aircaftModelIdList })
+    },
+
+    async searchClick() {
+      this.getDataImportAPI({ aircraftId: this.aircaftModelIdList, sortieNo: this.keyWordData })
+    },
+
+    treeNodeClick(data) {
+      this.$refs.table.clearSelection()
+      this.currentNodeKey = data.id
+      this.currentNode = data
+      this.aircaftModelIdList = this.getTreeLeafData(data.children.length ? data.children : [data])
+        .map((e) => e.id)
+        .toString()
+      if (!this.aircaftModelIdList) {
+        this.aircaftModelIdList = '#'
+      }
+      this.getDataImportAPI({ aircraftId: this.aircaftModelIdList })
+    },
+
+    async getAirConfigurationAPI(params) {
+      try {
+        const { data } = await getAirConfiguration(params)
+        const treeData = handleTree(data, 'id')
+        this.partsData = treeData
+      } catch (error) {}
+    },
+
+    faultExecute(row) {
+      this.form = deepClone(row)
+      const allAir = flattenTree(this.menuTreeData)
+      const item = allAir.find((item) => item.id === this.form.aircraftId)
+      this.form.aircraftType = item.parentId
+      this.currentSortieNo = row.sortieNo
+      this.beginExecute(row)
+    },
+
+    partIdSelect(node) {
+      this.form.partId = node.id
+    },
+
+    normalizer(node) {
+      if (node.children && !node.children.length) {
+        delete node.children
+      }
+      return {
+        id: node.id,
+        label: node.name,
+        children: node.children
+      }
+    },
+    handleClose() {
+      this.currentSortieNo = ''
+      this.form = {
+        id: '',
+        sortieNo: '',
+        ossId: '',
+        aircraftId: '',
+        aircraftType: '',
+        flightDate: '',
+        status: '',
+        result: '',
+        partId: null
+      }
+    },
+
+    resultShowDialogClose() {
+      this.resultShowVisible = false
+      this.diagnosisResult = {
+        degradation: [],
+        fault: [],
+        score: null
+      }
+      this.changeScore = null
+    },
+
+    submit() {
+      this.beginExecute(row)
+    },
+
+    selection(val) {
+      this.tableCheckItems = val
+    },
+
+    async checkRelustList(row) {
+      this.getFaultDiagnosisResultAPI({ diagnosisId: row.id })
+      this.resultVisible = true
+    },
+
+    checkRelustShow(row) {
+      const resData = JSON.parse(row.resultContent)
+      this.changeScore = resData.score / 20
+      this.diagnosisResult = resData
+      this.resultShowVisible = true
+    },
+
+    async getImgUrl(ossId) {
+      const { data } = await getListByIdsApi(ossId)
+      const newUrl = data[0].url
+      return newUrl
+    },
+
+    async beginExecute(row) {
+      this.progressVisible = true
+
+      let myTimer = setInterval(() => {
+        if (this.percentage < 100) {
+          if (this.percentage === 99) {
+            this.percentage = 99
+          } else {
+            this.percentage += 1
+          }
+        }
+      }, 30)
+
+      try {
+        this.form.dataId = row.id
+        const res = await executeFaultIntell(this.form)
+        clearInterval(myTimer)
+        if (res?.code === 200) {
+          this.percentage = 100
+          this.$message({
+            type: 'success',
+            message: '执行成功!'
+          })
+          const resData = JSON.parse(res.data)
+          this.changeScore = resData.score / 20
+          this.diagnosisResult = resData
+
+          // this.resultVisible = true
+          this.resultShowVisible = true
+          this.getDataImportAPI({ aircraftId: this.aircaftModelIdList })
+          this.handleClose()
+        }
+      } catch (error) {
+        clearInterval(myTimer)
+      } finally {
+        this.progressVisible = false
+        this.percentage = 0
+      }
+    },
+    handelQA(question) {
+      this.loading = true
+      this.tagLoading = true
+      const sendInput = {
+        question: question,
+        userId: String(store.state.user.userInfo.user.userId)
+      }
+      handlerQAndA(sendInput)
+        .then((res) => {
+          if (res.code == 200) {
+            this.QAData = this.handleData(res.data)
+            this.QAVisible = true
+          }
+        })
+        .catch((e) => {
+          console.log(e)
+        })
+        .finally(() => {
+          this.loading = false
+          this.tagLoading = false
+        })
+    },
+    handleData(data) {
+      if (data.graphAnswer?.graph) {
+        const graphAnswer = eval('(' + data.graphAnswer.graph + ')')
+        data.graphAnswer.graph = graphAnswer
+        const categories = []
+        data.graphAnswer.graph.data.forEach((node) => {
+          const flag = categories.find((item) => {
+            return item.name === node.category
+          })
+          if (!flag) {
+            categories.push({ name: node.category })
+          }
+        })
+        data.graphAnswer.graph.categories = categories
+      }
+      return data
+    },
+
+    QADialogClose() {
+      this.QAData = {
+        userId: '',
+        graphAnswer: {}
+      }
+      this.QAVisible = false
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+@import '../index.scss';
+.fault {
+  cursor: pointer;
+}
+</style>

+ 2 - 1
src/views/als/utils/enum-data.js

@@ -7,7 +7,8 @@ export const agloModelList = [
   { key: '3', name: '补全' },
   { key: '4', name: '虚警抑制' },
   { key: '5', name: '故障诊断' },
-  { key: '6', name: '退化评估' }
+  { key: '6', name: '退化评估' },
+  { key: '7', name: '判据算法' }
 ]
 
 // 抽取状态