瀏覽代碼

refactor(sensor): sensor data display

Casper Dai 2 年之前
父節點
當前提交
dc1894de53

+ 9 - 10
src/api/external.js

@@ -229,16 +229,6 @@ export function deleteGatewaySensor ({ id, name }) {
   }, name)
 }
 
-export function getSensorRecords (params, options) {
-  return request({
-    // url: '/device/thirdPartyGateway/sensorRecord',
-    url: '/device/sensorType',
-    method: 'GET',
-    params,
-    ...options
-  })
-}
-
 export function getDevicesByThirdPartyDevice (thirdPartyDeviceId) {
   return request({
     url: `/device/bind/getDevice/${thirdPartyDeviceId}`,
@@ -478,6 +468,15 @@ export function deleteSensor ({ id }) {
   }, '该传感器')
 }
 
+export function getSensorRecords (params, options) {
+  return request({
+    url: '/device/sensorList',
+    method: 'GET',
+    params,
+    ...options
+  })
+}
+
 // PLC
 export function getPLCs (query) {
   const { pageNum: pageIndex, pageSize, ...params } = query

+ 109 - 42
src/views/device/detail/components/external/Sensors/Sensor.vue

@@ -3,37 +3,43 @@
     <div class="l-flex--row l-flex__none">
       <i
         class="l-flex__none c-sibling-item o-icon"
-        :class="type"
+        :class="sensorType"
       />
       <span class="l-flex__fill c-sibling-item near u-color--info u-ellipsis">{{ title }}</span>
       <i
         v-if="enough"
-        class="l-flex__none u-font-size--md u-color--blue el-icon-s-operation u-pointer"
+        class="l-flex__none c-sibling-item u-font-size--md u-color--blue el-icon-s-operation has-active"
         @click="onShowAll"
       />
     </div>
     <div class="l-flex__fill l-flex--row center u-font-size--md u-color--black u-bold">
       <div
         class="u-text--center"
-        :style="{ color: tipColor }"
+        :style="{ color }"
       >
-        {{ tip }}
-        <div
-          v-if="sensor"
-          class="o-sensor__current"
-        >
-          {{ sensor }}
-        </div>
+        <template v-if="sensor">
+          {{ tip }}
+          <div class="o-sensor__current">
+            {{ sensor }}
+          </div>
+        </template>
+        <i
+          v-else-if="loading"
+          class="el-icon-loading"
+        />
+        <template v-else>
+          未知
+        </template>
       </div>
     </div>
     <div
       v-for="item in more"
-      :key="item.key"
+      :key="item.port"
       class="l-flex__none l-flex--row o-sensor__more"
     >
       <div class="l-flex__none">{{ item.time }}</div>
-      <div class="l-flex__none o-sensor__name">{{ item.name }}</div>
-      <div class="l-flex__fill u-text--right">{{ item.value }}</div>
+      <div class="l-flex__none o-sensor__name">传感器{{ item.port }}</div>
+      <div class="l-flex__fill u-text--right">{{ item.info }}</div>
     </div>
     <table-dialog
       ref="tableDialog"
@@ -45,17 +51,18 @@
 
 <script>
 import {
-  Type,
-  addListener,
-  removeListener
-} from '../../../monitor'
+  ThirdPartyDevice,
+  ThirdPartyDeviceInfo
+} from '@/constant'
+import { parseTime } from '@/utils'
+import { getSensorRecords } from '@/api/external'
 
 export default {
   name: 'Sensor',
   props: {
     type: {
-      type: String,
-      default: ''
+      type: Number,
+      required: true
     },
     title: {
       type: String,
@@ -64,17 +71,22 @@ export default {
     color: {
       type: String,
       default: ''
+    },
+    deviceId: {
+      type: String,
+      required: true
     }
   },
   data () {
     return {
+      loading: false,
       list: [],
       schema: {
         nonPagination: true,
         list: this.getSensors,
         cols: [
-          { prop: 'name', label: '传感器名称' },
-          { prop: 'value', label: this.title },
+          { prop: 'port', label: '端口' },
+          { prop: 'info', label: '数据' },
           { prop: 'time', label: '采集时间' }
         ]
       }
@@ -82,22 +94,23 @@ export default {
   },
   computed: {
     listTitle () {
-      return `${this.title}传感器详情`
+      return ThirdPartyDeviceInfo[this.type]
     },
     sorted () {
-      return this.list.slice().sort((a, b) => b.sensorValue - a.sensorValue)
+      return this.list.slice().sort((a, b) => Number(a.value) < Number(b.value) ? 1 : -1)
     },
-    tipColor () {
-      return this.list.length ? this.color : ''
+    sensorType () {
+      return `sensor_${this.type}`
     },
     tip () {
-      return this.list.length ? this.sorted[0].value : '未知'
+      return this.sorted.length ? this.sorted[0].info : null
     },
     sensor () {
-      return this.list.length
-        ? this.list.length === 1
+      const length = this.sorted.length
+      return length
+        ? length === 1
           ? this.sorted[0].time
-          : `${this.sorted[0].time} 传感器${this.sorted[0].key}`
+          : `${this.sorted[0].time} 传感器${this.sorted[0].port}`
         : null
     },
     more () {
@@ -108,15 +121,69 @@ export default {
     }
   },
   created () {
-    addListener(this.type, this.onUpdate, { type: Type.CACHE, value: [] })
+    this.$running = true
+    this.$timer = -1
+    this.startRun()
   },
   beforeDestroy () {
-    removeListener(this.type, this.onUpdate)
+    this.$running = false
+    clearTimeout(this.$timer)
   },
   methods: {
-    onUpdate (list) {
-      this.list = list
-      this.$refs.tableDialog?.getTable()?.pageTo()
+    startRun () {
+      if (!this.$running) {
+        return
+      }
+      const now = Date.now()
+      this.loading = true
+      getSensorRecords({
+        deviceId: this.deviceId,
+        sensorType: this.type,
+        startTime: parseTime(now - 30000, '{y}-{m}-{d} {h}:{i}:{s}'),
+        endTime: parseTime(now, '{y}-{m}-{d} {h}:{i}:{s}')
+      }, { custom: true }).then(({ data }) => {
+        this.list = this.transfromData(data)
+        this.$refs.tableDialog?.getTable()?.pageTo()
+      }).finally(() => {
+        this.loading = false
+        if (this.$running) {
+          this.$timer = setTimeout(this.startRun, 10000)
+        }
+      })
+    },
+    transfromData (data) {
+      const map = {}
+      const arr = []
+      data.forEach(sensor => {
+        if (!map[sensor.port]) {
+          map[sensor.port] = 1
+          arr.push(this.transformSensorData(sensor))
+        }
+      })
+      return arr
+    },
+    transformSensorData (data) {
+      const { port, type, value, time } = data
+      return {
+        port,
+        value,
+        time,
+        info: this.transformValue(type, value)
+      }
+    },
+    transformValue (type, value) {
+      switch (type) {
+        case ThirdPartyDevice.SMOKE_SENSOR:
+          return `${value}ppm`
+        case ThirdPartyDevice.TEMPERATURE_SENSOR:
+          return `${value}℃`
+        case ThirdPartyDevice.LIGHT_SENSOR:
+          return `${value}Lux`
+        case ThirdPartyDevice.FLOODING_SENSOR:
+          return value ? '是' : '否'
+        default:
+          return value
+      }
     },
     getSensors () {
       return Promise.resolve({ data: this.list })
@@ -130,21 +197,21 @@ export default {
 
 <style lang="scss" scoped>
 .o-icon {
-  &.temperature {
-    background-image: url("~@/assets/icon_temperature.png");
-  }
-
-  &.smoke {
+  &.sensor_9 {
     background-image: url("~@/assets/icon_smoke.png");
   }
 
-  &.flooding {
-    background-image: url("~@/assets/icon_flooding.png");
+  &.sensor_10 {
+    background-image: url("~@/assets/icon_temperature.png");
   }
 
-  &.light {
+  &.sensor_11 {
     background-image: url("~@/assets/icon_light.png");
   }
+
+  &.sensor_12 {
+    background-image: url("~@/assets/icon_flooding.png");
+  }
 }
 
 .o-sensor {

+ 21 - 26
src/views/device/detail/components/external/Sensors/index.vue

@@ -1,33 +1,19 @@
 <template>
   <div class="l-grid--info medium">
     <sensor
-      type="temperature"
-      title="温度"
-      color="#ff0000"
-    />
-    <sensor
-      type="smoke"
-      title="烟雾"
-      color="#8400ff"
-    />
-    <sensor
-      type="flooding"
-      title="水浸"
-    />
-    <sensor
-      type="light"
-      title="光照"
-      color="#ffa200"
+      v-for="sensor in sensors"
+      :key="sensor.type"
+      :type="sensor.type"
+      :title="sensor.title"
+      :color="sensor.color"
+      :device-id="deviceId"
     />
   </div>
 </template>
 
 <script>
-import {
-  startSensor,
-  stopSensor
-} from '../../../monitor'
-import Sensor from './Sensor'
+import { ThirdPartyDevice } from '@/constant'
+import Sensor from './Sensor.vue'
 
 export default {
   name: 'Sensors',
@@ -40,11 +26,20 @@ export default {
       required: true
     }
   },
-  created () {
-    startSensor()
+  data () {
+    return {
+      sensors: [
+        { type: ThirdPartyDevice.SMOKE_SENSOR, title: '烟雾', color: '#8400ff' },
+        { type: ThirdPartyDevice.TEMPERATURE_SENSOR, title: '温度', color: '#ff0000' },
+        { type: ThirdPartyDevice.LIGHT_SENSOR, title: '光照', color: '#ffa200' },
+        { type: ThirdPartyDevice.FLOODING_SENSOR, title: '水浸', color: '#409eff' }
+      ]
+    }
   },
-  beforeDestroy () {
-    stopSensor()
+  computed: {
+    deviceId () {
+      return this.device.id
+    }
   }
 }
 </script>

+ 82 - 28
src/views/device/detail/dashboard/Sensor.vue

@@ -10,7 +10,7 @@
     >
       <div
         class="l-flex__none c-device-dashboard-sensor__tip u-bold u-text--center"
-        :style="{ color: tipColor }"
+        :style="{ color }"
       >
         {{ tip }}
       </div>
@@ -37,13 +37,13 @@
               :key="index"
               class="l-flex--row c-device-dashboard-sensor__item"
             >
-              <div class="l-flex__fill u-text--center u-ellipsis"> {{ item.time }}</div>
-              <div class="l-flex__fill u-text--center u-ellipsis"> {{ item.name }}</div>
+              <div class="l-flex__fill u-text--center u-ellipsis">{{ item.time }}</div>
+              <div class="l-flex__fill u-text--center u-ellipsis">传感器{{ item.port }}</div>
               <div
-                :style="{ color: tipColor }"
+                :style="{ color }"
                 class="l-flex__fill u-text--center"
               >
-                {{ item.value || '-' }}
+                {{ item.value }}
               </div>
             </div>
           </vue-seamless-scroll>
@@ -54,11 +54,9 @@
 </template>
 
 <script>
-import {
-  Type,
-  addListener,
-  removeListener
-} from '../monitor'
+import { ThirdPartyDevice } from '@/constant'
+import { parseTime } from '@/utils'
+import { getSensorRecords } from '@/api/external'
 import VueSeamlessScroll from 'vue-seamless-scroll'
 import Box from './Box'
 
@@ -69,8 +67,8 @@ export default {
   },
   props: {
     type: {
-      type: String,
-      default: ''
+      type: Number,
+      required: true
     },
     title: {
       type: String,
@@ -79,6 +77,10 @@ export default {
     color: {
       type: String,
       default: ''
+    },
+    deviceId: {
+      type: String,
+      required: true
     }
   },
   data () {
@@ -92,38 +94,90 @@ export default {
     }
   },
   computed: {
+    sorted () {
+      return this.list.slice().sort((a, b) => Number(a.value) < Number(b.value) ? 1 : -1)
+    },
+    tip () {
+      return this.sorted.length ? this.sorted[0].info : '未知'
+    },
     sensorTime () {
-      return this.list.length
+      return this.sorted.length
         ? this.sorted[0].time
         : null
     },
     sensorName () {
-      return this.list.length
-        ? `传感器${this.sorted[0].key}`
+      return this.sorted.length
+        ? `传感器${this.sorted[0].port}`
         : null
     },
-    tipColor () {
-      return this.list.length ? this.color : ''
-    },
-    tip () {
-      return this.list.length ? this.sorted[0].value : '未知'
-    },
-    sorted () {
-      return this.list.slice().sort((a, b) => b.sensorValue - a.sensorValue)
-    },
     empty () {
       return this.list.length === 0
     }
   },
   created () {
-    addListener(this.type, this.onUpdate, { type: Type.CACHE, value: [] })
+    this.$running = true
+    this.$timer = -1
+    this.startRun()
   },
   beforeDestroy () {
-    removeListener(this.type, this.onUpdate)
+    this.$running = false
+    clearTimeout(this.$timer)
   },
   methods: {
-    onUpdate (list) {
-      this.list = list.map(item => { return { ...item, time: item.time.substring(0, 16) } })
+    startRun () {
+      if (!this.$running) {
+        return
+      }
+      const now = Date.now()
+      this.loading = true
+      getSensorRecords({
+        deviceId: this.deviceId,
+        sensorType: this.type,
+        startTime: parseTime(now - 30000, '{y}-{m}-{d} {h}:{i}:{s}'),
+        endTime: parseTime(now, '{y}-{m}-{d} {h}:{i}:{s}')
+      }, { custom: true }).then(({ data }) => {
+        this.list = this.transfromData(data)
+        this.$refs.tableDialog?.getTable()?.pageTo()
+      }).finally(() => {
+        this.loading = false
+        if (this.$running) {
+          this.$timer = setTimeout(this.startRun, 10000)
+        }
+      })
+    },
+    transfromData (data) {
+      const map = {}
+      const arr = []
+      data.forEach(sensor => {
+        if (!map[sensor.port]) {
+          map[sensor.port] = 1
+          arr.push(this.transformSensorData(sensor))
+        }
+      })
+      return arr
+    },
+    transformSensorData (data) {
+      const { port, type, value, time } = data
+      return {
+        port,
+        value,
+        time,
+        info: this.transformValue(type, value)
+      }
+    },
+    transformValue (type, value) {
+      switch (type) {
+        case ThirdPartyDevice.SMOKE_SENSOR:
+          return `${value}ppm`
+        case ThirdPartyDevice.TEMPERATURE_SENSOR:
+          return `${value}℃`
+        case ThirdPartyDevice.LIGHT_SENSOR:
+          return `${value}Lux`
+        case ThirdPartyDevice.FLOODING_SENSOR:
+          return value ? '是' : '否'
+        default:
+          return value
+      }
     },
     getSensors () {
       return Promise.resolve({ data: this.list })

+ 20 - 34
src/views/device/detail/dashboard/index.vue

@@ -24,36 +24,15 @@
               <LinkState :device="device" />
             </div>
             <div class="c-sibling-item far c-sensors">
-              <div>
-                <Sensor
-                  type="temperature"
-                  title="温度"
-                  color="#ff0000"
-                  :device="device"
-                />
-              </div>
-              <div>
-                <Sensor
-                  type="smoke"
-                  title="烟雾"
-                  color="#8400ff"
-                  :device="device"
-                />
-              </div>
-              <div>
-                <Sensor
-                  type="flooding"
-                  title="水浸"
-                  color="#00D494"
-                  :device="device"
-                />
-              </div>
-              <div>
-                <Sensor
-                  type="light"
-                  title="光照"
-                  color="#ffa200"
-                  :device="device"
+              <div
+                v-for="sensor in sensors"
+                :key="sensor.type"
+              >
+                <sensor
+                  :type="sensor.type"
+                  :title="sensor.title"
+                  :color="sensor.color"
+                  :device-id="device.id"
                 />
               </div>
             </div>
@@ -101,13 +80,15 @@
 </template>
 
 <script>
+import {
+  ThirdPartyDevice,
+  Camera
+} from '@/constant'
 import { getDevice } from '@/api/device'
 import { getCamerasByDevice } from '@/api/external'
-import { Camera } from '@/constant'
 import {
   start,
-  stop,
-  startSensor
+  stop
 } from '../monitor'
 import Header from './Header'
 import DeviceInfo from './DeviceInfo'
@@ -143,6 +124,12 @@ export default {
       loading: true,
       device: null,
       style: null,
+      sensors: [
+        { type: ThirdPartyDevice.SMOKE_SENSOR, title: '烟雾', color: '#8400ff' },
+        { type: ThirdPartyDevice.TEMPERATURE_SENSOR, title: '温度', color: '#ff0000' },
+        { type: ThirdPartyDevice.LIGHT_SENSOR, title: '光照', color: '#ffa200' },
+        { type: ThirdPartyDevice.FLOODING_SENSOR, title: '水浸', color: '#409eff' }
+      ],
       camera: null
     }
   },
@@ -170,7 +157,6 @@ export default {
           if (data) {
             if (!this.device) {
               start(data)
-              startSensor()
               this.getCamera()
               this.checkScale()
               window.addEventListener('resize', this.checkScale)

+ 1 - 65
src/views/device/detail/monitor.js

@@ -1,13 +1,9 @@
-import { getSensorRecords } from '@/api/external'
 import {
   publish,
   subscribe,
   unsubscribe
 } from '@/utils/mqtt'
-import {
-  parseTime,
-  parseByte
-} from '@/utils'
+import { parseByte } from '@/utils'
 
 let productId = null
 let deviceId = null
@@ -271,66 +267,6 @@ function downloadParser (inst, message) {
   return inst.value
 }
 
-function transformValue (type, value, unit) {
-  switch (type) {
-    case 'temperature':
-      return `${((value * 10) | 0) / 10}℃`
-    case 'flooding':
-      return value ? '是' : '否'
-    default:
-      return `${value}${unit}`
-  }
-}
-
-function transform (type, arr) {
-  return arr.map(({ sensorAddr, sensorValue, sensorValueUnit, logDate }) => {
-    return {
-      key: sensorAddr,
-      name: `传感器${sensorAddr}`,
-      sensorValue,
-      value: transformValue(type, sensorValue, sensorValueUnit),
-      time: parseTime(logDate * 1000, '{y}.{m}.{d} {h}:{i}:{s}')
-    }
-  })
-}
-
-export function startSensor () {
-  let sensor = types.get('sensor')
-  if (sensor) {
-    if (sensor.running) {
-      return
-    }
-    sensor.running = true
-  } else {
-    sensor = createType('sensor', { type: Type.LOOP, parser: sensorParser })
-  }
-  updateSensors(sensor)
-}
-
-export function stopSensor () {
-  const sensor = types.get('sensor')
-  if (sensor) {
-    sensor.running = false
-  }
-}
-
-function updateSensors (inst) {
-  inst.running && getSensorRecords({ deviceId }, { custom: true }).then(({ data }) => {
-    inst.running && onUpdate(inst, data)
-    return 5000
-  }, () => 2000).then(delay => {
-    if (inst.running) {
-      setTimeout(updateSensors, delay, inst)
-    }
-  })
-}
-
-function sensorParser (inst, data) {
-  Object.keys(data).forEach(type => {
-    onUpdate(types.get(type) || createType(type, { type: Type.CACHE, value: [] }), transform(type, data[type]))
-  })
-}
-
 function screenParser (inst, message) {
   if (message === RESET_MESSAGE) {
     return null