Newer
Older
shipFront / src / views / device / device.vue
[wangxitong] on 30 May 2022 14 KB v1.1.2
<template>
  <div class="overview">
    <div class="top">
      <div  class="block" v-for="(value,key,index) in data" :key="index" @click="btnClick(value)">
        <image-block :data="value"/>
      </div>
    </div>
    <div ref="body" class="body">
      <left ref="left" class="left"/>
      <div ref="right" class="middle">
        <div class="title">{{active.name}}</div>
        <div class="angle">横滚角: {{roll}} <br/>俯仰角: {{pitch}} <br/>航向角: {{heading}} </div>
        <div class="fresh">
          实时姿态:
          <el-switch
            v-model="value"/>
        </div>

        <div ref="device"/>
      </div>
      <right ref="devright" class="right"/>
    </div>
  </div>
</template>

<script>

import { getRobotList,getRobotDetail,getRobotTask } from '@/api/robot'
import ImageBlock from "./components/imageBlock";
import Left from './left'
import { Notification } from 'element-ui'
import * as THREE from 'three'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
import { STLLoader } from 'three/examples/jsm/loaders/STLLoader'
import OrbitControls from 'three-orbitcontrols'
import dat from 'dat.gui'
import Right from "./right";
var refresh = false
export default {
  name: 'Device',
  components: {Right, Left,ImageBlock, THREE, dat,OrbitControls,GLTFLoader,STLLoader},
  data() {
    return {
      active: {name:''},
      clock: 10, // 定时时间
      roll: '45.21°',
      pitch: '51.41°',
      heading: '93.00°',
      arr:[],
      value: false,
      socket: null,
      controls: null,
      scene: null,
      gltf: null,
      modelName:'',
      camera: null,
      renderer: null,
      timer: null, // 定时器
      plane:null,
      paramsThree: {
        modelName: '', // .glb模型的文件名字
        name: 'devGlb', // 模型加入场景的名字,此处统一是devGlb
      },
      data:[
        // {name:'飞翼滑翔机一号',gltf:''},
        // {name:'飞翼滑翔机二号',gltf:''},
        // {name:'飞翼滑翔机三号',gltf:''},
        // {name:'飞翼滑翔机四号',gltf:''},
        // {name:'飞翼滑翔机五号',gltf:''},
        // {name:'飞翼滑翔机六号',gltf:''},
        // {name:'飞翼滑翔机七号',gltf:''},
        // {name:'飞翼滑翔机八号',gltf:''}
      ],
      jumpTo:[]
    }
  },
  created() {
    this.$nextTick(() => {
      this.init()
      this.initWebSocket()
    })
  },
  beforeDestroy(){
    this.stopTimer()
    this.destoryWebSocket()
  },
  watch:{
    'camera.rotation.x':{
      handler: function() {
        this.pitch = (this.camera.rotation.x/Math.PI*180+this.arr[1]).toFixed(2) + '°'
        this.roll = (this.camera.rotation.y/Math.PI*180+this.arr[0]).toFixed(2) + '°'
        this.heading = (this.camera.rotation.z/Math.PI*180+this.arr[2]).toFixed(2) + '°'
      }
    },
    'camera.rotation.y':{
      handler: function() {
        this.pitch = (this.camera.rotation.x/Math.PI*180+this.arr[1]).toFixed(2) + '°'
        this.roll = (this.camera.rotation.y/Math.PI*180+this.arr[0]).toFixed(2) + '°'
        this.heading = (this.camera.rotation.z/Math.PI*180+this.arr[2]).toFixed(2) + '°'
      }
    },
    'camera.rotation.z':{
      handler: function() {
        this.pitch = (this.camera.rotation.x/Math.PI*180+this.arr[1]).toFixed(2) + '°'
        this.roll = (this.camera.rotation.y/Math.PI*180+this.arr[0]).toFixed(2) + '°'
        this.heading = (this.camera.rotation.z/Math.PI*180+this.arr[2]).toFixed(2) + '°'
      }
    }
  },
  methods: {
    initWebSocket() {
      let that = this
      if (typeof (WebSocket) === 'undefined') {
        Notification({
          title: '提示',
          message: '当前浏览器无法接收实时报警信息,请使用谷歌浏览器或360浏览器极速模式!',
          type: 'warning',
          duration: 0
        })
      } else {
        const socketUrl = this.baseConfig.webSocketUrl + this.$store.getters.id
        console.log(socketUrl,'*********************')
        that.socket = new WebSocket(socketUrl)
        // 监听socket打开
        that.socket.onopen = function() {
          console.log('浏览器WebSocket已打开')
        }
        // 监听socket消息接收
        that.socket.onmessage = function(msg) {
          // 转换为json对象
          const msgdata = JSON.parse(msg.data)
          console.log(msgdata)
          const data = msgdata.messageObject
          if (msgdata.type === 'alarm') {
            let message
            switch (data.alarmType) {
              case '1':
                message = data.robotId + '号' + data.alarmTypeName + ':' + data.alarmValue + '%'
                break
              case '2':
                message = data.robotId + '号' + data.alarmTypeName + ':' + data.alarmValue + '海里'
                break
              case '3':
                message = data.alarmTypeName + ':' + data.alarmValue + 'm/s'
                break
              case '4':
                message = data.robotId + '号' + data.alarmTypeName + ':' + data.alarmValue + '海里'
                break
              case '5':
                message = data.robotId + '号' + data.alarmTypeName + ':' + data.alarmValue + '海里'
                break
              case '6':
                message = data.alarmTypeName + ':' + data.alarmValue + 'kb/s'
                break
            }
            Notification({
              title: '新报警来了',
              // 这里也可以把返回信息加入到message中显示
              message: message,
              type: 'warning',
            })
          }
          else if(msgdata.type === 'robotInfo'){
            let data = msgdata.messageObject
            for(let i=0;i<that.data.length;i++){
              let item = that.data[i]
              if(item.id === data.robotId && that.active.id !== data.robotId){
                 if(!that.jumpTo[i]){
                   that.btnClick(item)
                   that.jumpTo[i] = true
                 }
                break
              }
            }
          }
        }
        // 监听socket错误
        that.socket.onerror = function() {
          Notification({
            title: '服务器错误',
            message: '无法接收实时报警信息,请检查服务器后重新刷新页面',
            type: 'error',
            duration: 0
          })
        }
        // 监听socket关闭
        that.socket.onclose = function() {
          console.log('WebSocket已关闭')
        }
      }
    },
    destoryWebSocket(){
      this.socket.close()
    },
    openTimer(){
      this.timer = setInterval(() => {
        if(this.value){
          this.btnClick(this.active)
        }
      }, this.clock * 1000)
    }, // 停止定时器
    stopTimer(){
      if (this.timer){
        clearInterval(this.timer)
        this.timer = null
      }
    },
    btnClick(val){
      this.active = val
      let that = this
      getRobotTask(val.id).then(res => {
        if (res.code === 200) {
          this.$refs.left.initTask(res.data)
        }
      })
      getRobotDetail(val.id).then(res => {
        if (res.code === 200) {
          this.$refs.left.init(res.data)
          this.$refs.devright.init(res.data)
          this.roll = res.data.rollAngle + '°'
          this.pitch = res.data.pitchAngle + '°'
          this.heading = res.data.headingAngle + '°'
          this.arr = [res.data.rollAngle,res.data.pitchAngle,res.data.headingAngle]
          if(that.modelName!==val.gltf){
            this.initModel(val.gltf,res.data.rollAngle,res.data.pitchAngle,res.data.headingAngle)
          }else{
            console.log('changePos')
            this.changePos(val.gltf,res.data.rollAngle,res.data.pitchAngle,res.data.headingAngle)
          }

        }
      })
    },
    init () {
      this.initList()
      this.initMesh()
    },
    changePos(name,roll,pitch,heading){
      let that = this
      this.gltf = null
      if(this.scene&&this.paramsThree.modelName!==''){
        // debugger
        let objM = this.scene.getObjectByName(this.paramsThree.modelName)
        objM.position.set( 0,0,0);
        objM.rotation.set( pitch*Math.PI/180, roll*Math.PI/180, heading*Math.PI/180 );
        refresh = true
      }
      // if(name==='') return
      // const loader = new STLLoader();
      // loader.load(`${process.env.BASE_URL}model/${name}.stl`, function ( geometry ) {
      //
      //   const material = new THREE.MeshPhongMaterial( { color: 0xffffff, specular: 0x111111, shininess: 200 } );
      //   const mesh = new THREE.Mesh( geometry, material );
      //   mesh.name = name
      //   mesh.position.set( 0,0,0);
      //   mesh.rotation.set( pitch*Math.PI/180, roll*Math.PI/180, heading*Math.PI/180 );
      //   mesh.scale.set( 0.03, 0.03, 0.03 );
      //
      //   mesh.castShadow = true;
      //   mesh.receiveShadow = true;
      //
      //   that.scene.add( mesh );
      //   that.paramsThree.modelName = name
      //   that.gltf = mesh
      // } );
    },
    initModel(name,roll,pitch,heading){
      let that = this
      this.gltf = null
      if(this.scene&&this.paramsThree.modelName!==''){
        let objM = this.scene.getObjectByName(this.paramsThree.modelName)
        if (objM) this.scene.remove(objM)
        refresh = true
      }
      if(name==='') return
      const loader = new STLLoader();
      loader.load(`${process.env.BASE_URL}model/${name}.stl`, function ( geometry ) {

        const material = new THREE.MeshPhongMaterial( { color: 0xffffff, specular: 0x111111, shininess: 200 } );
        const mesh = new THREE.Mesh( geometry, material );
        mesh.name = name
        mesh.position.set( 0,0,0);
        mesh.rotation.set( pitch*Math.PI/180, roll*Math.PI/180, heading*Math.PI/180 );
        mesh.scale.set( 0.03, 0.03, 0.03 );

        mesh.castShadow = true;
        mesh.receiveShadow = true;

        that.scene.add( mesh );
        that.paramsThree.modelName = name
        that.gltf = mesh
        that.modelName = name
      } );
    },
    addShadowedLight( x, y, z, color, intensity ) {

      const directionalLight = new THREE.DirectionalLight( color, intensity );
      directionalLight.position.set( x, y, z );
      this.scene.add( directionalLight );

      directionalLight.castShadow = true;

      const d = 1;
      directionalLight.shadow.camera.left = - d;
      directionalLight.shadow.camera.right = d;
      directionalLight.shadow.camera.top = d;
      directionalLight.shadow.camera.bottom = - d;

      directionalLight.shadow.camera.near = 1;
      directionalLight.shadow.camera.far = 4;

      directionalLight.shadow.bias = - 0.002;

    },
    initList(){
      getRobotList().then(res => {
        if (res.code === 200) {
          this.data = res.data.map(item => {
            return {name:'飞翼滑翔机'+item.robotId+'号',id:item.robotId,gltf:item.modelType}
          })
          this.active = this.data[0]
          this.jumpTo = this.data.map(item => {return false})
          this.btnClick(this.active)
          this.openTimer()
        }
      })
    },
    initMesh () {
      this.scene = new THREE.Scene() // 场景
      this.camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000) // 相机.视场,长宽比,近面,远面
      this.camera.position.set( 0, 0, 30 );
      this.camera.lookAt(this.scene.position)

      this.renderer = new THREE.WebGLRenderer({ antialias: true })// 渲染器
      this.renderer.setClearColor(0x179AC6, 1); //设置背景颜色
      this.renderer.setSize(this.$refs.right.clientWidth,this.$refs.right.clientHeight)
      this.renderer.shadowMapEnabled = true // 开启阴影

      this.scene.add( new THREE.HemisphereLight( 0x443333, 0x111122 ) );

      this.addShadowedLight( 1, 1, 1, 0xffffff, 1.35 );
      this.addShadowedLight( 0.5, 1, - 1, 0xffffff, 1 );

      this.$refs.device.append(this.renderer.domElement)
      this.renderer.render(this.scene, this.camera)
      this.controls = new OrbitControls(this.camera, this.renderer.domElement);  //摄像机和容器div   注意事div
      this.controls.mouseButtons = {
        //左键平移
        LEFT: THREE.MOUSE.PAN,
        //滚轮滑动
        MIDDLE: THREE.MOUSE.DOLLY,
        //右键旋转
        RIGHT: THREE.MOUSE.ROTATE
      }
      // 使动画循环使用时阻尼或自转 意思是否有惯性
      this.controls.enableDamping = false;
      //是否可以缩放
      this.controls.enableZoom = true;
      //是否自动旋转
      this.controls.autoRotate = true;
      // //设置相机距离原点的最远距离
      // this.controls.minDistance = 200;
      //设置相机距离原点的最远距离
      this.controls.maxDistance = 600;
      //是否开启右键拖拽
      this.controls.enablePan = true;
      this.renderScene()
    },
    renderScene () {
      let {scene, camera, renderer} = this
      requestAnimationFrame(this.renderScene)
      if(refresh){
        refresh = false
        camera.position.set( 0, 0, 30 )
        camera.lookAt(scene.position)
      }
      renderer.render(scene, camera)
    }
  }
}
</script>

<style rel="stylesheet/scss" lang="scss" scoped>
  .overview{
    display: flex;
    flex-direction: column;
    padding-right: 30px;
    width: 100%;
    height: 100%;
    .title{
      color: #ffffff;
      font-size: 20px;
      font-weight: bold;
      position: absolute;
      top: 30px;
      left: 360px;
    }
    .angle{
      color: #2f3244;
      font-size: 17px;
      position: absolute;
      top: 80px;
      left: 360px;
      line-height: 25px;
      font-weight: bold;
    }
    .fresh{
      color: white;
      font-size: 17px;
      position: absolute;
      top: 40px;
      right: 300px;
      line-height: 25px;
      font-weight: bold;
    }
    .top{
      margin: 10px 30px;
      display: flex;
      flex-direction: row;
      .block{
        width: 12.5%;
        height: 30px;
        margin-right: 10px;
        cursor: pointer;
      }
    }
    .body{
      position:relative;
      width: 100%;
      flex: 1;
      display: flex;
      flex-direction: row;
      .left{
        margin-left: 20px;
        min-width: 300px;
        width: 300px;
        display: flex;
        flex-direction: column;
        padding-bottom: 20px;
      }
      .right{
        margin-left: 20px;
        margin-right: 10px;
        min-width: 200px;
        width: 200px;
        display: flex;
        flex-direction: column;
        padding-bottom: 20px;
      }
      .middle{
        flex: 1;
        /*background-color: #E6A23C;*/
        margin-left: 20px;
        margin-top: 5px;
        margin-bottom: 25px;
      }
    }
    .map{
      width: 100%;
      flex: 1;
      background-color: white;
    }
  }
</style>