Ver Fonte

feat: support real-time status detection of device

Casper Dai há 3 anos atrás
pai
commit
4d00efd66c

+ 6 - 0
.env.production

@@ -20,3 +20,9 @@ VUE_APP_KEYCLOAK_OPTIONS_URL = 'http://liangke00.home.langchao.com:18080/auth'
 VUE_APP_KEYCLOAK_OPTIONS_REALM = 'smsb'
 VUE_APP_KEYCLOAK_OPTIONS_CLIENTID = 'frontend-dev-api'
 VUE_APP_KEYCLOAK_OPTIONS_ONLOAD = 'login-required'
+
+# aliyun
+# VUE_APP_KEYCLOAK_OPTIONS_URL = '/auth'
+# VUE_APP_KEYCLOAK_OPTIONS_REALM = 'smsb-test'
+# VUE_APP_KEYCLOAK_OPTIONS_CLIENTID = 'frontend-api'
+# VUE_APP_KEYCLOAK_OPTIONS_ONLOAD = 'login-required'

+ 49 - 32
src/utils/mqtt.js

@@ -37,6 +37,31 @@ const options = {
   }
 }
 
+const whiteList = [
+  {
+    type: 'topic',
+    val: /^\$SYS/
+  }, {
+    type: 'topic',
+    eq: true,
+    val: willTopic
+  }, {
+    type: 'topic',
+    val: /^\d+\/\d+\/status/
+  }, {
+    val: /messageId/
+  }
+]
+
+function isWhite (item, topic, message) {
+  switch (item.type) {
+    case 'topic':
+      return item.eq ? topic === item.val : item.val.test(topic)
+    default:
+      return item.val.test(message)
+  }
+}
+
 let mqttClient
 function start () {
   console.log('Connecting mqtt client...')
@@ -73,44 +98,36 @@ function start () {
     mqttClient = null
   })
 
-  client.on('message', (topic, message, packet) => {
-    console.log('MQTT topic')
+  client.on('message', (topic, message) => {
+    console.log('MQTT topic', topic)
 
-    if (topic === willTopic) {
-      console.log('lwt')
-      message = utf8ArrayBufferToString(message)
-    } else if (/^\$SYS/.test(topic)) {
-      console.log('sys')
-      message = utf8ArrayBufferToString(message)
-    } else {
-      if (typeof message !== 'string') {
-        try {
-          const s = utf8ArrayBufferToString(message)
-          if (s) {
-            if (/messageId/.test(s)) {
-              console.log('json')
-              message = s
+    if (typeof message !== 'string') {
+      try {
+        const s = utf8ArrayBufferToString(message)
+        if (s) {
+          if (whiteList.some(item => isWhite(item, topic, s))) {
+            console.log('white list')
+            message = s
+          } else {
+            const decodeStr = sm4.decrypt(message)
+            if (decodeStr) {
+              console.log('sm4')
+              message = decodeStr
             } else {
-              const decodeStr = sm4.decrypt(message)
-              if (decodeStr) {
-                console.log('sm4')
-                message = decodeStr
-              } else {
-                console.log('unencrypted')
-                message = s
-              }
+              console.log('unencrypted')
+              message = s
             }
-          } else {
-            console.log('null')
-            message = s
           }
-        } catch (e) {
-          console.warn(e)
-          message = utf8ArrayBufferToString(message)
+        } else {
+          console.log('null')
+          message = s
         }
-      } else {
-        console.log('char')
+      } catch (e) {
+        console.warn(e)
+        message = utf8ArrayBufferToString(message)
       }
+    } else {
+      console.log('char')
     }
 
     console.log(message)

+ 13 - 9
src/views/dashboard/components/Card.vue

@@ -42,16 +42,20 @@ export default {
   },
   props: {
     type: {
-      type: String
+      type: String,
+      default: ''
     },
     title: {
-      type: String
+      type: String,
+      default: ''
     },
     tip: {
-      type: String
+      type: String,
+      default: ''
     },
     desc: {
-      type: String
+      type: String,
+      default: ''
     },
     count: {
       type: [String, Number],
@@ -113,23 +117,23 @@ export default {
     background-repeat: no-repeat;
 
     &.info {
-      background-image: url('~@/assets/icon_info.png');
+      background-image: url("~@/assets/icon_info.png");
     }
 
     &.safety {
-      background-image: url('~@/assets/icon_safety.png');
+      background-image: url("~@/assets/icon_safety.png");
     }
 
     &.performance {
-      background-image: url('~@/assets/icon_performance.png');
+      background-image: url("~@/assets/icon_performance.png");
     }
 
     &.log {
-      background-image: url('~@/assets/icon_log.png');
+      background-image: url("~@/assets/icon_log.png");
     }
 
     &.date {
-      background-image: url('~@/assets/icon_date.png');
+      background-image: url("~@/assets/icon_date.png");
     }
   }
 

+ 92 - 2
src/views/dashboard/components/Device.vue

@@ -1,5 +1,9 @@
 <template>
-  <div class="o-device has-padding">
+  <div
+    class="o-device has-padding"
+    :class="{ 'u-pointer': isActivated && isOnline }"
+    @click="askStatus"
+  >
     <div class="l-flex__none l-flex--row o-device__header">
       <i
         class="l-flex__none o-device__status dark"
@@ -43,10 +47,40 @@
       class="l-flex__none o-device__footer"
       :text="address"
     />
+    <el-dialog
+      :title="name"
+      :visible.sync="show"
+      custom-class="c-dialog"
+      :before-close="handleClose"
+      append-to-body
+    >
+      <div class="u-text-center">
+        <i
+          v-if="loading"
+          class="el-icon-loading"
+        />
+        <template v-else>
+          <div class="has-bottom-padding">
+            <button
+              class="o-button"
+              @click="ask"
+            >
+              检测状态
+            </button>
+          </div>
+          <div>{{ message }}</div>
+        </template>
+      </div>
+    </el-dialog>
   </div>
 </template>
 
 <script>
+import {
+  subscribe,
+  unsubscribe,
+  publish
+} from '@/utils/mqtt'
 import AutoText from '@/components/AutoText'
 
 export default {
@@ -56,7 +90,15 @@ export default {
   },
   props: {
     device: {
-      type: Object
+      type: Object,
+      default: null
+    }
+  },
+  data () {
+    return {
+      show: false,
+      loading: false,
+      message: ''
     }
   },
   computed: {
@@ -100,6 +142,54 @@ export default {
       // return `位置:${this.device.address}`
       return `备注:${this.device.remark}`
     }
+  },
+  beforeDestroy () {
+    if (this.show) {
+      this.handleClose()
+    }
+  },
+  methods: {
+    askStatus () {
+      if (!this.isActivated || !this.isOnline) {
+        return
+      }
+      this.show = true
+      subscribe(`${this.device.productId}/${this.device.id}/status/reply`, this.onMessage)
+    },
+    handleClose () {
+      this.loading = false
+      this.message = ''
+      this.show = false
+      unsubscribe(`${this.device.productId}/${this.device.id}/status/reply`, this.onMessage)
+    },
+    onMessage (topic, message) {
+      this.loading = false
+      if (message && new RegExp(`${this.device.productId}/${this.device.id}/status/reply`).test(topic)) {
+        try {
+          message = JSON.parse(message)
+          switch (message.status) {
+            case 1:
+              this.message = '未播放节目,处于默认状态'
+              break
+            case 2:
+              this.message = '正在播放节目'
+              break
+            case 3:
+              this.message = '解析节目异常,请重新发布'
+              break
+            default:
+              this.message = '未知'
+              break
+          }
+        } catch {
+          this.message = '解析异常,请重试'
+        }
+      }
+    },
+    ask () {
+      this.loading = true
+      publish(`${this.device.productId}/${this.device.id}/status/ask`, JSON.stringify({ timestamp: Date.now() }))
+    }
   }
 }
 </script>