Initial commit
This commit is contained in:
155
src/components/DMT/VR/VRButton.js
Normal file
155
src/components/DMT/VR/VRButton.js
Normal file
@@ -0,0 +1,155 @@
|
||||
class VRButton {
|
||||
static createButton(renderer, options) {
|
||||
if (options) {
|
||||
console.error('THREE.VRButton: The "options" parameter has been removed. Please set the reference space type via renderer.xr.setReferenceSpaceType() instead.')
|
||||
}
|
||||
|
||||
const button = document.createElement('button')
|
||||
|
||||
function showEnterVR(/* device*/) {
|
||||
let currentSession = null
|
||||
|
||||
async function onSessionStarted(session) {
|
||||
session.addEventListener('end', onSessionEnded)
|
||||
|
||||
await renderer.xr.setSession(session)
|
||||
button.textContent = 'EXIT VR'
|
||||
|
||||
currentSession = session
|
||||
}
|
||||
|
||||
function onSessionEnded(/* event*/) {
|
||||
currentSession.removeEventListener('end', onSessionEnded)
|
||||
|
||||
button.textContent = 'ENTER VR'
|
||||
|
||||
currentSession = null
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
button.style.display = ''
|
||||
|
||||
button.style.cursor = 'pointer'
|
||||
button.style.left = 'calc(50% - 50px)'
|
||||
button.style.width = '100px'
|
||||
|
||||
button.textContent = 'ENTER VR'
|
||||
|
||||
button.onmouseenter = function() {
|
||||
button.style.opacity = '1.0'
|
||||
}
|
||||
|
||||
button.onmouseleave = function() {
|
||||
button.style.opacity = '0.5'
|
||||
}
|
||||
|
||||
button.onclick = function() {
|
||||
if (currentSession === null) {
|
||||
// WebXR's requestReferenceSpace only works if the corresponding feature
|
||||
// was requested at session creation time. For simplicity, just ask for
|
||||
// the interesting ones as optional features, but be aware that the
|
||||
// requestReferenceSpace call will fail if it turns out to be unavailable.
|
||||
// ('local' is always available for immersive sessions and doesn't need to
|
||||
// be requested separately.)
|
||||
|
||||
const sessionInit = { optionalFeatures: ['local-floor', 'bounded-floor', 'hand-tracking', 'layers'] }
|
||||
navigator.xr.requestSession('immersive-vr', sessionInit).then(onSessionStarted)
|
||||
} else {
|
||||
currentSession.end()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function disableButton() {
|
||||
button.style.display = ''
|
||||
|
||||
button.style.cursor = 'auto'
|
||||
button.style.left = 'calc(50% - 75px)'
|
||||
button.style.width = '150px'
|
||||
|
||||
button.onmouseenter = null
|
||||
button.onmouseleave = null
|
||||
|
||||
button.onclick = null
|
||||
}
|
||||
|
||||
function showWebXRNotFound() {
|
||||
disableButton()
|
||||
|
||||
button.textContent = 'VR NOT SUPPORTED'
|
||||
}
|
||||
|
||||
function showVRNotAllowed(exception) {
|
||||
disableButton()
|
||||
|
||||
console.warn('Exception when trying to call xr.isSessionSupported', exception)
|
||||
|
||||
button.textContent = 'VR NOT ALLOWED'
|
||||
}
|
||||
|
||||
function stylizeElement(element) {
|
||||
element.style.position = 'absolute'
|
||||
element.style.bottom = '20px'
|
||||
element.style.padding = '12px 6px'
|
||||
element.style.border = '1px solid #fff'
|
||||
element.style.borderRadius = '4px'
|
||||
element.style.background = 'rgba(0,0,0,0.1)'
|
||||
element.style.color = '#fff'
|
||||
element.style.font = 'normal 13px sans-serif'
|
||||
element.style.textAlign = 'center'
|
||||
element.style.opacity = '0.5'
|
||||
element.style.outline = 'none'
|
||||
element.style.zIndex = '999'
|
||||
}
|
||||
|
||||
if ('xr' in navigator) {
|
||||
button.id = 'VRButton'
|
||||
button.style.display = 'none'
|
||||
|
||||
stylizeElement(button)
|
||||
|
||||
navigator.xr.isSessionSupported('immersive-vr').then(function(supported) {
|
||||
supported ? showEnterVR() : showWebXRNotFound()
|
||||
|
||||
if (supported && VRButton.xrSessionIsGranted) {
|
||||
button.click()
|
||||
}
|
||||
}).catch(showVRNotAllowed)
|
||||
|
||||
return button
|
||||
} else {
|
||||
const message = document.createElement('a')
|
||||
|
||||
if (window.isSecureContext === false) {
|
||||
message.href = document.location.href.replace(/^http:/, 'https:')
|
||||
message.innerHTML = 'WEBXR NEEDS HTTPS' // TODO Improve message
|
||||
} else {
|
||||
message.href = 'https://immersiveweb.dev/'
|
||||
message.innerHTML = 'WEBXR NOT AVAILABLE'
|
||||
}
|
||||
|
||||
message.style.left = 'calc(50% - 90px)'
|
||||
message.style.width = '180px'
|
||||
message.style.textDecoration = 'none'
|
||||
|
||||
stylizeElement(message)
|
||||
|
||||
return message
|
||||
}
|
||||
}
|
||||
|
||||
static xrSessionIsGranted = false;
|
||||
|
||||
static registerSessionGrantedListener() {
|
||||
if ('xr' in navigator) {
|
||||
navigator.xr.addEventListener('sessiongranted', () => {
|
||||
VRButton.xrSessionIsGranted = true
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VRButton.registerSessionGrantedListener()
|
||||
|
||||
export { VRButton }
|
||||
327
src/components/DMT/config.vue
Normal file
327
src/components/DMT/config.vue
Normal file
@@ -0,0 +1,327 @@
|
||||
<template>
|
||||
<div style="pointer-events: none">
|
||||
<el-dialog title="修改配置文件" :visible.sync="dialogFormVisible" width="1000px" class="myDia" :modal="false" :close-on-press-escape="false" v-el-drag-dialog>
|
||||
<el-collapse v-model="activeNames">
|
||||
<el-collapse-item title="通讯配置" name="1">
|
||||
<el-row class="row-Show">
|
||||
<span class="title-show">标题</span>
|
||||
<el-input size="mini" style="width: 300px" v-model="configInfo['title']"></el-input>
|
||||
<span class="title-show">工位和协议</span>
|
||||
<el-input size="mini" style="width: 120px" v-model="configInfo['webSocketOpName']"></el-input>
|
||||
<el-input size="mini" style="width: 180px" v-model="configInfo['webSocketBSport']"></el-input>
|
||||
</el-row>
|
||||
<el-row class="row-Show">
|
||||
<span class="title-show">通讯类型</span>
|
||||
<el-radio-group size="mini" v-model="configInfo['connectType']">
|
||||
<el-radio :label="1">WebSocket</el-radio>
|
||||
<el-radio :label="2">MQTT</el-radio>
|
||||
<el-radio :label="3">其他</el-radio>
|
||||
</el-radio-group>
|
||||
</el-row>
|
||||
<el-row class="row-Show">
|
||||
<span class="title-show">Socket_IP&Port</span>
|
||||
<el-input size="mini" style="width: 300px" v-model="configInfo['webSocketIp']"></el-input>
|
||||
<span class="title-show">MQTT_IP&Port</span>
|
||||
<el-input size="mini" style="width: 200px" v-model="configInfo['MQTTHost']"></el-input>
|
||||
<el-input size="mini" style="width: 100px" v-model="configInfo['MQTTPort']"></el-input>
|
||||
</el-row>
|
||||
<el-row class="row-Show">
|
||||
<span class="title-show">数据服务器</span>
|
||||
<el-input size="mini" style="width: 300px" v-model="configInfo['dataBaseURL']"></el-input>
|
||||
<span class="title-show">node服务</span>
|
||||
<el-input size="mini" style="width: 300px" v-model="configInfo['NodeRootIp']"></el-input>
|
||||
</el-row>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item name="2">
|
||||
<template slot="title">
|
||||
机器人/设备配置
|
||||
<el-button size="mini" type="warning" style="margin-left: 10px" @click.stop.prevent="dialogFormVisible = false">重置配置文件</el-button>
|
||||
<el-button size="mini" type="primary" style="margin-left: 10px" @click.stop.prevent="exportConfig">导出配置文件</el-button>
|
||||
{{ currentUUid }}
|
||||
</template>
|
||||
<el-col :span="6">
|
||||
<el-table
|
||||
:data="tableDataRobot"
|
||||
:highlight-current-row="false"
|
||||
element-loading-text="数据加载中"
|
||||
element-loading-spinner="el-icon-loading"
|
||||
element-loading-background="rgba(0, 0, 0, 0.2)"
|
||||
style="width: 100%"
|
||||
max-height="300"
|
||||
border
|
||||
size="mini"
|
||||
class="my-table-show"
|
||||
@cell-dblclick="showEdit"
|
||||
@row-click="rowClick">
|
||||
<el-table-column type="index" label=" " align="center" width="40"/>
|
||||
<!-- <el-table-column align="center" label="name">-->
|
||||
<!-- <template slot-scope="scope">-->
|
||||
<!-- {{ scope.row.name }}-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<el-table-column align="center" label="机器人模型id">
|
||||
<template slot-scope="scope">
|
||||
<el-input v-if="scope.row.edit" size="mini" v-model="scope.row.uuid"></el-input>
|
||||
<span v-else>{{ scope.row.uuid }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" width="50px" label="操作">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" style="color: #00893d" @click="applyRobotConfig(scope.row)">应用</el-button>
|
||||
<br>
|
||||
<el-button size="mini" type="text" style="color: red">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-col>
|
||||
<el-col :span="18">
|
||||
<el-table
|
||||
:data="RobotChildrenData"
|
||||
:highlight-current-row="false"
|
||||
element-loading-text="数据加载中"
|
||||
element-loading-spinner="el-icon-loading"
|
||||
element-loading-background="rgba(0, 0, 0, 0.2)"
|
||||
style="width: 100%"
|
||||
max-height="300"
|
||||
border
|
||||
size="mini">
|
||||
<el-table-column type="index" label=" " align="center" width="50"/>
|
||||
<!-- <el-table-column align="center" label="关节名字">-->
|
||||
<!-- <template slot-scope="scope">-->
|
||||
<!-- {{ scope.row.name }}-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<el-table-column align="center" label="关节ID" width="150">
|
||||
<template slot-scope="scope">
|
||||
<el-input size="mini" v-model="scope.row.ModelId "></el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="type" width="110">
|
||||
<template slot-scope="scope">
|
||||
<el-select v-model="scope.row.type" size="mini">
|
||||
<el-option
|
||||
v-for="item in robotJointType"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"/>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="axis" width="80">
|
||||
<template slot-scope="scope">
|
||||
<el-select v-model="scope.row.axis" size="mini">
|
||||
<el-option
|
||||
v-for="item in robotJointAxis"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"/>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="flip" width="80">
|
||||
<template slot-scope="scope">
|
||||
<el-select v-model="scope.row.flip" size="mini">
|
||||
<el-option
|
||||
v-for="item in robotJointFlip"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"/>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="delta" width="80">
|
||||
<template slot-scope="scope">
|
||||
<el-input size="mini" v-model="scope.row.delta "></el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="multiple" width="120">
|
||||
<template slot-scope="scope">
|
||||
<el-input size="mini" v-model="scope.row.multiple "></el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" width="50px" label="操作">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" style="color: #00893d" @click="applyRobotConfig(scope.row, scope.$index)">应用</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-col>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Mousetrap from './js/mousetrap.js'
|
||||
import axios from 'axios'
|
||||
export default {
|
||||
name: 'ConfigVue',
|
||||
data() {
|
||||
return {
|
||||
currentUUid: '',
|
||||
activeNames: '2',
|
||||
configInfo: {},
|
||||
tableDataRobot: [],
|
||||
tableDataRobotChildren: {},
|
||||
RobotChildrenData: [],
|
||||
dialogFormVisible: false,
|
||||
robotJointType: [
|
||||
{
|
||||
label: 'position',
|
||||
value: 'position'
|
||||
},
|
||||
{
|
||||
label: 'rotation',
|
||||
value: 'rotation'
|
||||
}
|
||||
],
|
||||
robotJointFlip: [
|
||||
{
|
||||
label: 1,
|
||||
value: 1
|
||||
},
|
||||
{
|
||||
label: -1,
|
||||
value: -1
|
||||
}
|
||||
],
|
||||
robotJointAxis: [
|
||||
{
|
||||
label: 'x',
|
||||
value: 'x'
|
||||
},
|
||||
{
|
||||
label: 'y',
|
||||
value: 'y'
|
||||
},
|
||||
{
|
||||
label: 'z',
|
||||
value: 'z'
|
||||
}
|
||||
],
|
||||
form: {
|
||||
name: '',
|
||||
region: '',
|
||||
date1: '',
|
||||
date2: '',
|
||||
delivery: false,
|
||||
type: [],
|
||||
resource: '',
|
||||
desc: ''
|
||||
},
|
||||
formLabelWidth: '120px'
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getConfig().then(res => {
|
||||
this.configInfo = res
|
||||
console.log(res)
|
||||
this.solveRobotInfoFirst()
|
||||
})
|
||||
const scope = this
|
||||
Mousetrap.bind('ctrl+c', function() {
|
||||
scope.solveConfig()
|
||||
return false
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
solveConfig() {
|
||||
console.log('ctrl + c')
|
||||
this.dialogFormVisible = !this.dialogFormVisible
|
||||
},
|
||||
getConfig() {
|
||||
return new Promise((resolve, reject) => {
|
||||
axios.get('./DMT_WEB/config/config.json').then(res => {
|
||||
const configData = res.data
|
||||
resolve(configData)
|
||||
}).catch(err => {
|
||||
reject(err)
|
||||
})
|
||||
})
|
||||
},
|
||||
solveRobotInfoFirst() {
|
||||
this.tableDataRobot = []
|
||||
this.tableDataRobotChildren = {}
|
||||
for (const argumentsKey in this.configInfo.robotList) {
|
||||
this.tableDataRobot.push({
|
||||
name: '',
|
||||
uuid: argumentsKey,
|
||||
edit: false
|
||||
})
|
||||
this.tableDataRobotChildren[argumentsKey] = []
|
||||
for (let i = 0; i < this.configInfo.robotList[argumentsKey].length; i++) {
|
||||
this.tableDataRobotChildren[argumentsKey].push(this.configInfo.robotList[argumentsKey][i])
|
||||
}
|
||||
}
|
||||
},
|
||||
showEdit(row) {
|
||||
row.edit = !row.edit
|
||||
},
|
||||
rowClick(row) {
|
||||
this.currentUUid = row.uuid
|
||||
this.RobotChildrenData = this.tableDataRobotChildren[row.uuid]
|
||||
},
|
||||
exportConfig() {
|
||||
this.configInfo['robotList'] = this.tableDataRobotChildren
|
||||
const saveJsonStr = JSON.stringify(this.configInfo)
|
||||
const blob = new Blob([saveJsonStr], {
|
||||
type: 'application/json'
|
||||
})
|
||||
const objectUrl = URL.createObjectURL(blob)
|
||||
const aa = document.createElement('a')
|
||||
aa.href = objectUrl
|
||||
aa.download = 'config.json'
|
||||
aa.click()
|
||||
URL.revokeObjectURL(objectUrl)
|
||||
},
|
||||
applyRobotConfig(row, index) {
|
||||
const rootRobotJoint = window.editor.robotList[this.currentUUid][index]
|
||||
if (row.ModelId === rootRobotJoint.ModelId) {
|
||||
window.editor.robotList[this.currentUUid][index].axis = row.axis
|
||||
window.editor.robotList[this.currentUUid][index].delta = row.delta
|
||||
window.editor.robotList[this.currentUUid][index].flip = row.flip
|
||||
window.editor.robotList[this.currentUUid][index].type = row.type
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.row-Show {
|
||||
margin: 10px 0
|
||||
}
|
||||
.title-show {
|
||||
font-weight: bolder;
|
||||
font-size: 16px;
|
||||
margin: 0 10px;
|
||||
width: 130px;
|
||||
display: inline-block;
|
||||
text-align: right;
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
.myDia .el-dialog {
|
||||
pointer-events: auto;
|
||||
}
|
||||
.myDia .el-dialog__body {
|
||||
padding: 5px 15px;
|
||||
}
|
||||
.myDia .el-dialog--center .el-dialog__body {
|
||||
padding: 15px;
|
||||
}
|
||||
.myDia .el-dialog__title {
|
||||
font-size: 22px;
|
||||
color: #000000;
|
||||
}
|
||||
.myDia .el-dialog__header {
|
||||
padding: 15px;
|
||||
}
|
||||
.myDia .el-dialog__wrapper{
|
||||
pointer-events:none;
|
||||
}
|
||||
.myDia .el-dialog{
|
||||
pointer-events:auto;
|
||||
}
|
||||
</style>
|
||||
46
src/components/DMT/el-dragDialog/drag.js
Normal file
46
src/components/DMT/el-dragDialog/drag.js
Normal file
@@ -0,0 +1,46 @@
|
||||
export default{
|
||||
bind(el, binding) {
|
||||
const dialogHeaderEl = el.querySelector('.el-dialog__header')
|
||||
const dragDom = el.querySelector('.el-dialog')
|
||||
dialogHeaderEl.style = 'cursor:move;'
|
||||
|
||||
// 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
|
||||
const sty = dragDom.currentStyle || window.getComputedStyle(dragDom, null)
|
||||
|
||||
dialogHeaderEl.onmousedown = (e) => {
|
||||
// 鼠标按下,计算当前元素距离可视区的距离
|
||||
const disX = e.clientX - dialogHeaderEl.offsetLeft
|
||||
const disY = e.clientY - dialogHeaderEl.offsetTop
|
||||
|
||||
// 获取到的值带px 正则匹配替换
|
||||
let styL, styT
|
||||
|
||||
// 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px
|
||||
if (sty.left.includes('%')) {
|
||||
styL = +document.body.clientWidth * (+sty.left.replace(/\%/g, '') / 100)
|
||||
styT = +document.body.clientHeight * (+sty.top.replace(/\%/g, '') / 100)
|
||||
} else {
|
||||
styL = +sty.left.replace(/\px/g, '')
|
||||
styT = +sty.top.replace(/\px/g, '')
|
||||
}
|
||||
|
||||
document.onmousemove = function(e) {
|
||||
// 通过事件委托,计算移动的距离
|
||||
const l = e.clientX - disX
|
||||
const t = e.clientY - disY
|
||||
|
||||
// 移动当前元素
|
||||
dragDom.style.left = `${l + styL}px`
|
||||
dragDom.style.top = `${t + styT}px`
|
||||
|
||||
// 将此时的位置传出去
|
||||
// binding.value({x:e.pageX,y:e.pageY})
|
||||
}
|
||||
|
||||
document.onmouseup = function(e) {
|
||||
document.onmousemove = null
|
||||
document.onmouseup = null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
13
src/components/DMT/el-dragDialog/index.js
Normal file
13
src/components/DMT/el-dragDialog/index.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import drag from './drag'
|
||||
|
||||
const install = function(Vue) {
|
||||
Vue.directive('el-drag-dialog', drag)
|
||||
}
|
||||
|
||||
if (window.Vue) {
|
||||
window['el-drag-dialog'] = drag
|
||||
Vue.use(install); // eslint-disable-line
|
||||
}
|
||||
|
||||
drag.install = install
|
||||
export default drag
|
||||
976
src/components/DMT/index.vue
Normal file
976
src/components/DMT/index.vue
Normal file
@@ -0,0 +1,976 @@
|
||||
|
||||
<template>
|
||||
<div style="height: 100%">
|
||||
<div
|
||||
id="viewPort"
|
||||
:style="{ height: containerHeight, width: containerWidth }"
|
||||
style="
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: transparent !important;
|
||||
"
|
||||
></div>
|
||||
<div
|
||||
ref="modelProcess"
|
||||
style="position: absolute; top: 10px; left: 2px; color: white"
|
||||
>
|
||||
正在加载模型0%
|
||||
</div>
|
||||
<div style="display: none">
|
||||
<audio controls loop id="alarm">
|
||||
<source src="../../../public/alarm.mp3" type="audio/mpeg" />
|
||||
</audio>
|
||||
<audio controls id="clash">
|
||||
<source src="../../../public/alarm.mp3" type="audio/mpeg" />
|
||||
</audio>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SpriteText from "./js/three-spritetext.module.js";
|
||||
|
||||
import * as THREE from "three";
|
||||
import axios from "axios";
|
||||
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
|
||||
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader.js";
|
||||
import * as TWEEN from "three/examples/jsm/libs/tween.module.js";
|
||||
|
||||
import { DmtControl } from "./js/DmtControl.js";
|
||||
import { connect } from "./js/websocketConnect.js";
|
||||
import { connectMqtt } from "./js/MqttConnect.js";
|
||||
import { PlaySoundCommand } from "./js/PlaySoundCommand.js";
|
||||
import { EXRLoader } from "three/examples/jsm/loaders/EXRLoader";
|
||||
import {
|
||||
acceleratedRaycast,
|
||||
computeBoundsTree,
|
||||
disposeBoundsTree,
|
||||
} from "./libs/bvh";
|
||||
THREE.Mesh.prototype.raycast = acceleratedRaycast;
|
||||
THREE.BufferGeometry.prototype.computeBoundsTree = computeBoundsTree;
|
||||
THREE.BufferGeometry.prototype.disposeBoundsTree = disposeBoundsTree;
|
||||
let container, scene, camera, axesHelper, renderer;
|
||||
let modelCount = 0;
|
||||
const editor = {
|
||||
cameraStateZoom: 25,
|
||||
addEventListener: THREE.EventDispatcher.prototype.addEventListener,
|
||||
removeEventListener: THREE.EventDispatcher.prototype.removeEventListener,
|
||||
dispatchEvent: THREE.EventDispatcher.prototype.dispatchEvent,
|
||||
hasEventListener: THREE.EventDispatcher.prototype.hasEventListener,
|
||||
ip: "",
|
||||
scene: null,
|
||||
objectIdUUidMap: new Map(),
|
||||
opname: "",
|
||||
connectType: 2,
|
||||
BSport: "",
|
||||
IsAllowConnect: true,
|
||||
service: axios.create({
|
||||
baseURL: "./submit/MESCommonBase.ashx",
|
||||
timeout: 1500000, // 请求超时时间
|
||||
}),
|
||||
selected: null,
|
||||
socket: null,
|
||||
mqttClient: {
|
||||
connected: false,
|
||||
},
|
||||
ProjectCode: null,
|
||||
sceneConfig: null,
|
||||
resourcesTree: null,
|
||||
attachments: null,
|
||||
resources: null,
|
||||
cameraState: null,
|
||||
cameraStateAll: [],
|
||||
isOk: false,
|
||||
objectIdSet: new Map(),
|
||||
animOperations: null,
|
||||
CraftList: {},
|
||||
moveTrigger: null,
|
||||
camera: null,
|
||||
cameraPosition: 10,
|
||||
currentButton: 1,
|
||||
cameraControls: null,
|
||||
TagActList: {}, // 存放tag对应的类型
|
||||
worker: {}, // 播放工艺的线程
|
||||
RotationWorker: {}, // 自传工艺的线程
|
||||
ModelRotateWorker: {}, // 自传工艺的的状态数据
|
||||
rotateSpeed: (5 * Math.PI) / 180,
|
||||
getObjectById: null, // 方法 根据ModelId获取模型
|
||||
solveAttachModel: null, // 方法 处理attach问题
|
||||
removeObject: null, // 方法 移除模型
|
||||
setModelWireFrame: null, // 方法 设置线框
|
||||
setTransparent: null, // 方法 设置透明
|
||||
showMessage: null, // 方法 显示消息
|
||||
drawPartTextInfo: null, // 方法 模型上显示文字
|
||||
ModelLabelList: new Map(), // 模型上显示列表
|
||||
playSoundCommand: new PlaySoundCommand(), // 方法 警告
|
||||
attachList: new Map(), // 记录attach列表
|
||||
objMaterial: new Map(),
|
||||
WareHouseList: [],
|
||||
WareHouse: new Map(), // 立库库存表
|
||||
WareHouseBasicModel: new Map(), // 立库库存基础 零件模型需要命名为warehousepart1-100 立库定位模型需要命名为warehouse1000-1009
|
||||
ProtocolIdentifiers: {
|
||||
Robot_7: "Robot_7",
|
||||
Device_3: "Device_3",
|
||||
Agv_3: "Agv_3",
|
||||
Device_1: "Device_1",
|
||||
FirstPosition: "FirstPosition",
|
||||
WareHousePart: "WareHousePart",
|
||||
Robot_6: "Robot_6",
|
||||
RobotTcp: "RobotTcp",
|
||||
DrawPath: "DrawPath",
|
||||
ReLoadData: "ReLoadData",
|
||||
PlayAct: "PlayAct",
|
||||
CraftCurrOperation: "CraftCurrOperation",
|
||||
Position: "Position",
|
||||
Select: "Select",
|
||||
DragMove: "DragMove",
|
||||
ModelTransparent: "ModelTransparent",
|
||||
Visible: "Visible",
|
||||
Frames: "Frames",
|
||||
ModelRotation: "ModelRotation",
|
||||
GoHome: "GoHome",
|
||||
GetJointAxisInfo: "GetJointAxisInfo",
|
||||
GetDeviceBaseTool: "GetDeviceBaseTool",
|
||||
GetJointValue: "GetJointValue",
|
||||
C_B_ImportModel: "C_B_ImportModel",
|
||||
SendURDF: "SendURDF",
|
||||
SendDeviceJson: "SendDeviceJson",
|
||||
GetDeviceJson: "GetDeviceJson",
|
||||
SendMotionInfo: "SendMotionInfo",
|
||||
C_B_ImportLayout: "C_B_ImportLayout",
|
||||
GetPoseLocation: "GetPoseLocation",
|
||||
C_B_SetProjectCode: "C_B_SetProjectCode",
|
||||
C_B_TagIDTrigger: "C_B_TagIDTrigger",
|
||||
C_B_ReLoad_TagIDHandler: "C_B_ReLoad_TagIDHandler",
|
||||
C_B_SetCurrSignalList: "C_B_SetCurrSignalList",
|
||||
SendClearModelInfo: "SendClearModelInfo",
|
||||
C_B_SaveProjectSimulationData: "C_B_SaveProjectSimulationData",
|
||||
C_B_GetProjectSimulationData: "C_B_GetProjectSimulationData",
|
||||
C_B_KinematicsEditing: "C_B_KinematicsEditing",
|
||||
C_B_SaveModel: "C_B_SaveModel",
|
||||
C_B_ObjectAlarm: "C_B_ObjectAlarm",
|
||||
C_B_JointMoveTo: "C_B_JointMoveTo",
|
||||
JointMoveTo: "JointMoveTo",
|
||||
C_B_ImportDevice: "C_B_ImportDevice",
|
||||
C_B_OutputDevice: "C_B_OutputDevice",
|
||||
C_B_ANDON: "C_B_ANDON",
|
||||
C_B_ModeRun: "C_B_ModeRun", // 1标准模式 2虚拟调试模式 3数字孪生模式
|
||||
},
|
||||
craftPlaySolveType: {
|
||||
visible: "visible",
|
||||
rotation: "rotation",
|
||||
craft: "craft",
|
||||
signalwrite: "signalwrite",
|
||||
wait: "wait",
|
||||
FrameData: "FrameData",
|
||||
attach: "attach",
|
||||
warning: "warning",
|
||||
},
|
||||
FrameOperation: {
|
||||
playCraft: true,
|
||||
visibleObject: true,
|
||||
rotationObject: true,
|
||||
writeSignal: true,
|
||||
waitSignal: true,
|
||||
attach: true,
|
||||
warning: true,
|
||||
},
|
||||
signalSolveType: {
|
||||
visible: "visible",
|
||||
rotation: "rotation",
|
||||
craft: "craft",
|
||||
targetLocation: "targetlocation",
|
||||
attach: "attach",
|
||||
warning: "warning",
|
||||
},
|
||||
};
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
let ModelListTransParentList = [];
|
||||
const ModelList = [];
|
||||
const ModelListId = [];
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const timer = null;
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const lastIntersectModel = null;
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const onUpPosition = new THREE.Vector2();
|
||||
|
||||
var raycaster = new THREE.Raycaster();
|
||||
var mouse = new THREE.Vector2();
|
||||
|
||||
export default {
|
||||
components: {},
|
||||
name: "DMT",
|
||||
props: {
|
||||
containerHeight: {
|
||||
type: String,
|
||||
default: "100vh",
|
||||
},
|
||||
containerWidth: {
|
||||
type: String,
|
||||
default: "100vw",
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
checked: false,
|
||||
cameraData: [],
|
||||
object: null,
|
||||
s: null,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
const scope = this;
|
||||
editor.objectIdSet = new Map();
|
||||
editor.objMaterial = new Map();
|
||||
editor.attachList = new Map();
|
||||
this.getConfig().then((res) => {
|
||||
if (Object.hasOwnProperty.call(res, "robotList")) {
|
||||
editor.robotList = res.robotList;
|
||||
}
|
||||
if (Object.hasOwnProperty.call(res, "WareHouseList")) {
|
||||
editor.WareHouseList = res.WareHouseList;
|
||||
}
|
||||
if (Object.hasOwnProperty.call(res, "transParentList")) {
|
||||
ModelListTransParentList = res.transParentList;
|
||||
}
|
||||
if (Object.hasOwnProperty.call(res, "connectType")) {
|
||||
editor.connectType = res.connectType;
|
||||
}
|
||||
editor.mqttHost = res.MQTTHost;
|
||||
editor.mqttPort = res.MQTTPort;
|
||||
editor.ip = res.webSocketIp;
|
||||
editor.opname = res.webSocketOpName;
|
||||
editor.BSport = res.webSocketBSport;
|
||||
editor.rootIp = res.NodeRootIp;
|
||||
this.getProjectConfig();
|
||||
});
|
||||
editor.showMessage = function (message, type, time = 2000) {
|
||||
scope.$message({
|
||||
message: message,
|
||||
type: type,
|
||||
duration: time,
|
||||
center: true,
|
||||
});
|
||||
};
|
||||
editor.addEventListener("dmtModelDBClick", function (event) {
|
||||
console.log("dmtModelDBClick", event);
|
||||
});
|
||||
},
|
||||
created() {},
|
||||
methods: {
|
||||
getConfig() {
|
||||
return new Promise((resolve, reject) => {
|
||||
axios
|
||||
.get("./DMT_WEB/config/config.json")
|
||||
.then((res) => {
|
||||
const configData = res.data;
|
||||
resolve(configData);
|
||||
})
|
||||
.catch((err) => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
},
|
||||
/**
|
||||
* 获取项目配置文件,用于初始隐藏模型
|
||||
*/
|
||||
getProjectConfig() {
|
||||
const P1 = new Promise(function (resolve, reject) {
|
||||
axios({
|
||||
method: "get",
|
||||
url: "./DMT_WEB/models/DTDATA.json",
|
||||
}).then((res) => {
|
||||
editor.animOperations = {};
|
||||
editor.moveTrigger = {};
|
||||
res.data.forEach((n) => {
|
||||
Object.keys(n).forEach(function (key) {
|
||||
/**
|
||||
* 获取播放数据并解析数据
|
||||
*/
|
||||
if (key === "OperationTree") {
|
||||
n[key].forEach((CraftData) => {
|
||||
editor.CraftList[CraftData.CraftCode] = {};
|
||||
editor.CraftList[CraftData.CraftCode]["CraftName"] =
|
||||
CraftData.CraftName;
|
||||
editor.CraftList[CraftData.CraftCode]["Varieties"] =
|
||||
CraftData.Varieties;
|
||||
editor.CraftList[CraftData.CraftCode]["CurrPlay"] =
|
||||
CraftData.CurrPlay;
|
||||
if (CraftData.CraftPlayData.length < 2) {
|
||||
editor.animOperations[CraftData.CraftCode] = "";
|
||||
} else {
|
||||
editor.animOperations[CraftData.CraftCode] = JSON.parse(
|
||||
CraftData.CraftPlayData
|
||||
);
|
||||
}
|
||||
editor.moveTrigger[CraftData.CraftCode] = {
|
||||
moveStatus: 0,
|
||||
myVarSetInterval: null,
|
||||
doAnimation: false,
|
||||
currentFrame: 0,
|
||||
pausedFrame: 0,
|
||||
animOperations: editor.animOperations[CraftData.CraftCode],
|
||||
simPaceTime: 20,
|
||||
moveStatusPosition: 0,
|
||||
};
|
||||
});
|
||||
}
|
||||
/**
|
||||
* HandlerType: 1:显隐,2:自转,3行为
|
||||
* HandlerCode: 模型或者工艺代码
|
||||
* HandlerValue: 显隐:0/1 ,自传:正负转速&axis,行为暂停播放:0/1
|
||||
* TagValue:信号值
|
||||
*/
|
||||
if (key === "TagTriggerHandler") {
|
||||
n[key].forEach((TagActListValue) => {
|
||||
if (editor.TagActList[TagActListValue.TagID]) {
|
||||
editor.TagActList[TagActListValue.TagID].push({
|
||||
HandlerType: TagActListValue.HandlerType,
|
||||
HandlerCode: TagActListValue.HandlerCode,
|
||||
HandlerValue: TagActListValue.HandlerValue,
|
||||
TagValue: TagActListValue.TagValue,
|
||||
JointType: TagActListValue.JointType,
|
||||
Axis: TagActListValue.Axis,
|
||||
Flip: TagActListValue.Flip,
|
||||
Value: TagActListValue.Value,
|
||||
ValueFactor: TagActListValue.ValueFactor,
|
||||
});
|
||||
} else {
|
||||
editor.TagActList[TagActListValue.TagID] = [];
|
||||
editor.TagActList[TagActListValue.TagID].push({
|
||||
HandlerType: TagActListValue.HandlerType,
|
||||
HandlerCode: TagActListValue.HandlerCode,
|
||||
HandlerValue: TagActListValue.HandlerValue,
|
||||
TagValue: TagActListValue.TagValue,
|
||||
JointType: TagActListValue.JointType,
|
||||
Axis: TagActListValue.Axis,
|
||||
Flip: TagActListValue.Flip,
|
||||
Value: TagActListValue.Value,
|
||||
ValueFactor: TagActListValue.ValueFactor,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
resolve(1);
|
||||
});
|
||||
});
|
||||
const P2 = new Promise(function (resolve, reject) {
|
||||
axios({
|
||||
method: "get",
|
||||
url: "./DMT_WEB/models/RobotPlan_Process.json",
|
||||
})
|
||||
.then((res) => {
|
||||
editor.sceneConfig = res.data.sceneConfig; // 场景配置
|
||||
editor.attachments = res.data.attachments; // 附加物体
|
||||
editor.resources = res.data.resources; // 场景模型资源
|
||||
editor.resourcesTree = res.data.resourcesTree; // 模型树
|
||||
editor.cameraState = res.data.cameraState; // 场景相机位置
|
||||
editor.ProjectCode = res.data.ProjectCode; // 项目代码
|
||||
editor.resourcesTree.forEach((res) => {
|
||||
if (res.treeType === "OriginModel") {
|
||||
ModelList.push(res.name);
|
||||
}
|
||||
});
|
||||
editor.resources.forEach((res) => {
|
||||
editor.objectIdUUidMap.set(res.id, res);
|
||||
});
|
||||
editor.attachments.forEach((res) => {
|
||||
editor.attachList.set(res.RootId, res.attachList);
|
||||
});
|
||||
resolve(1);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
});
|
||||
});
|
||||
Promise.all([P1, P2]).then((res) => {
|
||||
this.init();
|
||||
});
|
||||
},
|
||||
/**
|
||||
* 设定模型初始状态
|
||||
*/
|
||||
setModelSates() {
|
||||
// 模型的显示隐藏
|
||||
editor.resourcesTree.forEach((res) => {
|
||||
if (res.visible === "0") {
|
||||
if (editor.getObjectById(res.id)) {
|
||||
editor.getObjectById(res.id).visible = !!parseInt(res.visible);
|
||||
}
|
||||
}
|
||||
});
|
||||
const treeRes = editor.resourcesTree;
|
||||
for (const treeResValue of treeRes) {
|
||||
if (treeResValue.treeType !== "folder") {
|
||||
const currentModel = editor.getObjectById(treeResValue.id);
|
||||
|
||||
if (
|
||||
Object.hasOwnProperty.call(treeResValue, "WireFrame") &&
|
||||
treeResValue.WireFrame
|
||||
) {
|
||||
editor.setModelWireFrame(currentModel, true);
|
||||
}
|
||||
if (
|
||||
Object.hasOwnProperty.call(treeResValue, "transParent") &&
|
||||
treeResValue.transParent
|
||||
) {
|
||||
editor.setTransparent(treeResValue.id, 0.3);
|
||||
}
|
||||
if (currentModel)
|
||||
currentModel.visible = !!parseInt(treeResValue.visible);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 设定场景初始状态
|
||||
* @param sceneConfig {object}
|
||||
* @param sceneConfig.RenderColorModel {object}
|
||||
* @param sceneConfig.RenderColorModel.color {string} '#dcdcdc'
|
||||
* @param sceneConfig.RenderColorModel.image {string} base64
|
||||
* @param sceneConfig.RenderColorModel.model {string} '0' '1' '2'
|
||||
* @param sceneConfig.sliderToneMappingExposure {number} 1
|
||||
*/
|
||||
setSceneStates(sceneConfig) {
|
||||
const type = sceneConfig.RenderColorModel.model;
|
||||
switch (type) {
|
||||
case "0":
|
||||
scene.background = new THREE.Color("#dcdcdc");
|
||||
break;
|
||||
case "1":
|
||||
scene.background = null;
|
||||
break;
|
||||
case "2":
|
||||
if (sceneConfig.RenderColorModel.image === "") {
|
||||
editor.scene.background = null;
|
||||
return;
|
||||
}
|
||||
new THREE.TextureLoader().load(
|
||||
sceneConfig.RenderColorModel.image === "",
|
||||
function (texture) {
|
||||
scene.background = null;
|
||||
texture.dispose();
|
||||
}
|
||||
);
|
||||
break;
|
||||
default:
|
||||
scene.background = null;
|
||||
break;
|
||||
}
|
||||
const sliderToneMappingExposure = sceneConfig.sliderToneMappingExposure;
|
||||
renderer.toneMappingExposure = sliderToneMappingExposure;
|
||||
},
|
||||
/**
|
||||
* 初始化场景信息
|
||||
*/
|
||||
init() {
|
||||
this.setEditorFunction();
|
||||
const scope = this;
|
||||
container = document.getElementById("viewPort");
|
||||
scene = new THREE.Scene();
|
||||
scene.environmentRotation = new THREE.Euler(Math.PI / 2, 0, 0);
|
||||
scene.backgroundRotation = new THREE.Euler(Math.PI / 2, 0, 0);
|
||||
editor.scene = scene;
|
||||
const width = container.clientWidth;
|
||||
const height = container.clientHeight;
|
||||
const aspect = width / height;
|
||||
|
||||
camera = new THREE.PerspectiveCamera(45, aspect, 0.01, 2000);
|
||||
camera.name = "Camera";
|
||||
camera.position.set(0, 50, 100);
|
||||
camera.up.set(0, 0, 1);
|
||||
camera.lookAt(new THREE.Vector3(1, 1, 1));
|
||||
|
||||
scene.add(camera);
|
||||
editor.camera = camera;
|
||||
axesHelper = new THREE.AxesHelper(1);
|
||||
scene.add(axesHelper);
|
||||
|
||||
renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
|
||||
|
||||
renderer.outputColorSpace = THREE.SRGBColorSpace;
|
||||
renderer.setPixelRatio(window.devicePixelRatio);
|
||||
renderer.setSize(container.clientWidth, container.clientHeight);
|
||||
renderer.setClearColor(0x000000, 0); // 第二个参数0表示完全透明
|
||||
// renderer.setClearColor(0xcccccc);
|
||||
renderer.toneMapping = THREE.LinearToneMapping;
|
||||
renderer.toneMappingExposure = Math.pow(2, 0);
|
||||
|
||||
const pmremGenerator = new THREE.PMREMGenerator(renderer);
|
||||
pmremGenerator.compileEquirectangularShader();
|
||||
const loader = new GLTFLoader().setPath("./DMT_WEB/models/");
|
||||
const dracoLoader = new DRACOLoader();
|
||||
dracoLoader.setDecoderPath("./DMT_WEB/draco/");
|
||||
loader.setDRACOLoader(dracoLoader);
|
||||
new EXRLoader().load(
|
||||
"./DMT_WEB/assets/IndoorEnvironment.exr",
|
||||
(texture) => {
|
||||
const envMap = pmremGenerator.fromEquirectangular(texture).texture;
|
||||
scene.environment = envMap;
|
||||
// scene.background = new THREE.Color("#081f48");
|
||||
pmremGenerator.dispose();
|
||||
},
|
||||
undefined,
|
||||
undefined
|
||||
);
|
||||
this.setSceneStates(editor.sceneConfig);
|
||||
|
||||
container.appendChild(renderer.domElement);
|
||||
container.addEventListener("dblclick", this.ondblclick, false);
|
||||
editor.cameraControls = new DmtControl(
|
||||
editor.camera,
|
||||
renderer.domElement
|
||||
);
|
||||
editor.cameraControls.listenToKeyEvents(window); // optional
|
||||
editor.cameraControls.update();
|
||||
editor.cameraControls.addEventListener("change", (e) => {
|
||||
this.sendCameraState();
|
||||
});
|
||||
window.addEventListener("resize", this.onWindowResize, false);
|
||||
editor.cameraControls.resetState(
|
||||
editor.cameraState.target,
|
||||
editor.cameraState.position,
|
||||
editor.cameraState.zoom0
|
||||
);
|
||||
this.animate();
|
||||
setTimeout(() => {
|
||||
for (let i = 0; i < ModelList.length; i++) {
|
||||
loader.load(
|
||||
ModelList[i] + ".glb",
|
||||
function (gltf) {
|
||||
const currentModel = gltf.scene.children[0];
|
||||
currentModel.traverse((child) => {
|
||||
if (child.isMesh && child["material"]) {
|
||||
child.geometry.computeBoundsTree({
|
||||
maxLeafTris: 5,
|
||||
strategy: 0,
|
||||
});
|
||||
child.geometry.boundsTree.splitStrategy = 0;
|
||||
editor.objMaterial.set(child.id, child["material"]);
|
||||
}
|
||||
});
|
||||
scene.add(currentModel);
|
||||
scope.setObjectByConfig(currentModel);
|
||||
ModelListId.push(gltf.scene.id);
|
||||
modelCount += 1;
|
||||
scope.$refs.modelProcess.innerText =
|
||||
"正在加载模型" +
|
||||
((modelCount / ModelList.length) * 100).toFixed(2) +
|
||||
"%";
|
||||
if (modelCount === ModelList.length) {
|
||||
scope.$refs.modelProcess.style.display = "none";
|
||||
scope.lookObject(scene);
|
||||
scope.setModelSates();
|
||||
scope.connectService();
|
||||
}
|
||||
},
|
||||
(xhr) => {
|
||||
// eslint-disable-next-line handle-callback-err
|
||||
},
|
||||
(error) => {
|
||||
modelCount += 1;
|
||||
console.log(`加载模型${ModelList[i].ModelName}出错!`);
|
||||
if (modelCount === ModelList.length) {
|
||||
scope.$refs.modelProcess.style.display = "none";
|
||||
scope.lookObject(scene);
|
||||
scope.setModelSates();
|
||||
scope.connectService();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}, 1000);
|
||||
},
|
||||
setEditorFunction() {
|
||||
const scope = this;
|
||||
/**
|
||||
* 通过ModelId获取模型
|
||||
* @param ModelId
|
||||
* @returns {null}
|
||||
*/
|
||||
editor.getObjectById = function (ModelId) {
|
||||
let currentModel = null;
|
||||
const currentModelData = editor.objectIdSet.get(ModelId);
|
||||
if (currentModelData) currentModel = currentModelData.object;
|
||||
return currentModel;
|
||||
};
|
||||
/**
|
||||
* attach 弃用
|
||||
* @param ModelId
|
||||
* @param attach
|
||||
*/
|
||||
editor.solveAttachModel = function (ModelId, attach = true) {};
|
||||
/**
|
||||
* 移出物体
|
||||
* @param object
|
||||
*/
|
||||
editor.removeObject = function (object) {
|
||||
if (object === null) return;
|
||||
if (object.parent === null) return;
|
||||
object.parent.remove(object);
|
||||
};
|
||||
/**
|
||||
* 设置模型线框
|
||||
* @param currentObject
|
||||
* @param WireframeValue
|
||||
*/
|
||||
editor.setModelWireFrame = function (currentObject, WireframeValue) {
|
||||
currentObject.traverse((obj) => {
|
||||
if (obj instanceof THREE.Mesh) {
|
||||
obj.material = editor.objMaterial.get(obj.id).clone();
|
||||
obj.material["wireframe"] = WireframeValue;
|
||||
obj.material.needsUpdate = true;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 模型透明
|
||||
* @param ModelId
|
||||
* @param Opacity 透明度
|
||||
*/
|
||||
editor.setTransparent = function (ModelId, Opacity = 0.3) {
|
||||
new Promise((resolve, reject) => {
|
||||
const TransModel = editor.getObjectById(ModelId);
|
||||
if (!TransModel) {
|
||||
console.warn("模型不存在!(setTransparent)");
|
||||
reject(false);
|
||||
} else {
|
||||
TransModel.traverse((obj) => {
|
||||
if (obj.material) {
|
||||
const material_yuan = editor.objMaterial.get(obj.id).clone();
|
||||
obj.material = material_yuan;
|
||||
if (Opacity !== 1) {
|
||||
obj.material = new THREE.MeshStandardMaterial({
|
||||
color: material_yuan.color,
|
||||
side: 2,
|
||||
transparent: true,
|
||||
opacity: Opacity,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
resolve(true);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 模型上方显示悬浮文字
|
||||
* @param ModelId 模型ID
|
||||
* @param TagValue 显示的位置 可以换行
|
||||
*/
|
||||
editor.drawPartTextInfo = function (ModelId, TagValue) {
|
||||
new Promise((resolve, reject) => {
|
||||
let LabelInfo = editor.ModelLabelList.get(ModelId);
|
||||
const TransModel = editor.getObjectById(ModelId);
|
||||
if (TransModel) {
|
||||
if (!LabelInfo) {
|
||||
LabelInfo = {
|
||||
position: scope.getDeltaPosition(new THREE.Vector3(), {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0.5,
|
||||
}),
|
||||
label: TagValue,
|
||||
object: null,
|
||||
};
|
||||
editor.ModelLabelList.set(ModelId, LabelInfo);
|
||||
}
|
||||
if (LabelInfo.object) editor.removeObject(LabelInfo.object);
|
||||
if (TransModel.visible) {
|
||||
LabelInfo.label = TagValue.replaceAll("\\n", "\n");
|
||||
const spriteLabel = new SpriteText(
|
||||
LabelInfo.label,
|
||||
0.2,
|
||||
"#000000"
|
||||
);
|
||||
// spriteLabel.borderColor = '#30ff00'
|
||||
// spriteLabel.borderWidth = [0.3, 0.2]
|
||||
spriteLabel.backgroundColor = "rgb(255,183,0)";
|
||||
spriteLabel.position.copy(LabelInfo.position);
|
||||
LabelInfo.object = spriteLabel;
|
||||
TransModel.add(spriteLabel);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* 自适应窗口尺寸
|
||||
*/
|
||||
onWindowResize() {
|
||||
const aspect = container.clientWidth / container.clientHeight;
|
||||
renderer.setSize(container.clientWidth, container.clientHeight);
|
||||
camera.aspect = aspect;
|
||||
camera.updateProjectionMatrix();
|
||||
this.render();
|
||||
this.sendCameraState();
|
||||
},
|
||||
/**
|
||||
* 根据有配置文件设置模型初始化属性
|
||||
* @param object
|
||||
*/
|
||||
setObjectByConfig(object) {
|
||||
const objectId = object.userData["ModelId"];
|
||||
const objectResource = editor.objectIdUUidMap.get(objectId);
|
||||
if (objectResource) {
|
||||
object.position.copy(
|
||||
new THREE.Vector3(
|
||||
parseFloat(objectResource.location.position.x),
|
||||
parseFloat(objectResource.location.position.y),
|
||||
parseFloat(objectResource.location.position.z)
|
||||
)
|
||||
);
|
||||
object.quaternion.copy(
|
||||
new THREE.Quaternion(
|
||||
parseFloat(objectResource.location.quaternion.x),
|
||||
parseFloat(objectResource.location.quaternion.y),
|
||||
parseFloat(objectResource.location.quaternion.z),
|
||||
parseFloat(objectResource.location.quaternion.w)
|
||||
)
|
||||
);
|
||||
object.visible = Boolean(parseInt(objectResource.visible));
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 渲染
|
||||
*/
|
||||
animate() {
|
||||
this.render();
|
||||
requestAnimationFrame(this.animate);
|
||||
editor.attachList.forEach((attackList, ModelId) => {
|
||||
const ModelAttachParent = editor.getObjectById(ModelId);
|
||||
if (ModelAttachParent) {
|
||||
if (
|
||||
!ModelAttachParent.lastMatrixWorld.equals(
|
||||
ModelAttachParent.matrixWorld
|
||||
)
|
||||
) {
|
||||
for (const attackListElement of attackList) {
|
||||
const attachModel = editor.getObjectById(
|
||||
attackListElement.ModelId
|
||||
);
|
||||
if (attachModel) {
|
||||
const newMatrixWord = ModelAttachParent.matrixWorld.clone();
|
||||
const cubeTransWorld = newMatrixWord
|
||||
.clone()
|
||||
.multiply(ModelAttachParent.lastMatrixWorld.clone().invert())
|
||||
.clone();
|
||||
const cube2OldWorld = attachModel.matrixWorld.clone();
|
||||
const cube2NewWorld = cubeTransWorld
|
||||
.clone()
|
||||
.multiply(cube2OldWorld);
|
||||
const cube2ParentWorld = attachModel.parent.matrixWorld.clone();
|
||||
const cube2ParentNew = cube2ParentWorld
|
||||
.invert()
|
||||
.multiply(cube2NewWorld);
|
||||
const position = new THREE.Vector3();
|
||||
const quaternion = new THREE.Quaternion();
|
||||
const scale = new THREE.Vector3();
|
||||
cube2ParentNew.decompose(position, quaternion, scale);
|
||||
attachModel.quaternion.copy(quaternion);
|
||||
attachModel.position.copy(position);
|
||||
attachModel.updateMatrixWorld();
|
||||
}
|
||||
}
|
||||
ModelAttachParent.lastMatrixWorld =
|
||||
ModelAttachParent.matrixWorld.clone();
|
||||
}
|
||||
}
|
||||
});
|
||||
TWEEN.update();
|
||||
},
|
||||
|
||||
/**
|
||||
* 渲染
|
||||
*/
|
||||
render() {
|
||||
renderer.render(scene, camera);
|
||||
// this.updatePosition(editor.selected)
|
||||
},
|
||||
connectService() {
|
||||
switch (editor.connectType) {
|
||||
case 1:
|
||||
connect(editor);
|
||||
break;
|
||||
case 2:
|
||||
connectMqtt(editor);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 解析模型
|
||||
* @param object
|
||||
*/
|
||||
lookObject(object) {
|
||||
object.traverse(function (child) {
|
||||
if (!child) return;
|
||||
// 避免modelId重复
|
||||
if (Object.hasOwnProperty.call(child.userData, "ModelId")) {
|
||||
if (editor.objectIdSet.has(child.userData["ModelId"])) {
|
||||
child.userData["ModelId"] = child.uuid;
|
||||
editor.objectIdSet.set(child.uuid, {
|
||||
ModelId: child.id,
|
||||
Name: child.name,
|
||||
position: JSON.parse(JSON.stringify(child.position)),
|
||||
rotation: JSON.parse(JSON.stringify(child.rotation)),
|
||||
quaternion: JSON.parse(JSON.stringify(child.quaternion)),
|
||||
object: child,
|
||||
matrix: child.matrix.clone(),
|
||||
lastValue: null,
|
||||
});
|
||||
} else {
|
||||
editor.objectIdSet.set(child.userData["ModelId"], {
|
||||
ModelId: child.id,
|
||||
Name: child.name,
|
||||
position: JSON.parse(JSON.stringify(child.position)),
|
||||
rotation: JSON.parse(JSON.stringify(child.rotation)),
|
||||
quaternion: JSON.parse(JSON.stringify(child.quaternion)),
|
||||
object: child,
|
||||
matrix: child.matrix.clone(),
|
||||
lastValue: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
for (const robotListInfo in editor.robotList) {
|
||||
for (const info of editor.robotList[robotListInfo]) {
|
||||
info.object = editor.getObjectById(info.ModelId);
|
||||
info.matrix = info.object ? info.object.matrix.clone() : null;
|
||||
info.lastValue = null;
|
||||
}
|
||||
}
|
||||
object.traverse(function (child) {
|
||||
child.lastMatrixWorld = child.matrixWorld.clone();
|
||||
});
|
||||
window.editor = editor;
|
||||
},
|
||||
/**
|
||||
* 获取鼠标位置
|
||||
* @param dom
|
||||
* @param x
|
||||
* @param y
|
||||
* @returns {number[]}
|
||||
*/
|
||||
getMousePosition(dom, x, y) {
|
||||
const rect = dom.getBoundingClientRect();
|
||||
return [(x - rect.left) / rect.width, (y - rect.top) / rect.height];
|
||||
},
|
||||
/**
|
||||
* 双击
|
||||
* @param event
|
||||
*/
|
||||
ondblclick(event) {
|
||||
try {
|
||||
const array = this.getMousePosition(
|
||||
container,
|
||||
event.clientX,
|
||||
event.clientY
|
||||
);
|
||||
onUpPosition.fromArray(array);
|
||||
let intersects = this.getIntersects(
|
||||
onUpPosition,
|
||||
editor.scene.children
|
||||
);
|
||||
intersects = intersects.filter((res) => {
|
||||
return res.object.visible;
|
||||
});
|
||||
if (intersects.length > 0) {
|
||||
const rootObjId = intersects[0].object.userData["RootId"];
|
||||
editor.selected = editor.getObjectById(rootObjId);
|
||||
editor.dispatchEvent({
|
||||
type: "dmtModelDBClick",
|
||||
ModelId: editor.selected.userData["ModelId"],
|
||||
ModelName: editor.selected.userData["name"],
|
||||
});
|
||||
} else {
|
||||
editor.selected = null;
|
||||
editor.dispatchEvent({
|
||||
type: "dmtModelDBClick",
|
||||
ModelId: "",
|
||||
ModelName: "",
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
editor.selected = null;
|
||||
editor.dispatchEvent({
|
||||
type: "dmtModelDBClick",
|
||||
ModelId: "",
|
||||
ModelName: "",
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 射线检测
|
||||
* @param point
|
||||
* @param objects
|
||||
* @returns {[]}
|
||||
*/
|
||||
getIntersects(point, objects) {
|
||||
mouse.set(point.x * 2 - 1, -(point.y * 2) + 1);
|
||||
raycaster.setFromCamera(mouse, camera);
|
||||
return raycaster.intersectObjects(objects, true);
|
||||
},
|
||||
/**
|
||||
* 发送相机距离旋转中心点的位置
|
||||
* @param state
|
||||
*/
|
||||
sendCameraState(state) {
|
||||
const ModelCenter = editor.cameraControls.target;
|
||||
const cameraPosition = camera.position;
|
||||
const cameraZoom = cameraPosition.distanceTo(ModelCenter);
|
||||
this.$emit("changeCameraState", cameraZoom); // 自定义事件 传递值“子向父组件传值”
|
||||
},
|
||||
/**
|
||||
* 实时发送双击模型的位置
|
||||
*/
|
||||
updatePosition() {
|
||||
if (editor.selected) {
|
||||
const targetPosition = editor.selected
|
||||
.getWorldPosition(new THREE.Vector3())
|
||||
.clone()
|
||||
.project(camera);
|
||||
const halfWidth = window.innerWidth / 2;
|
||||
const halfHeight = window.innerHeight / 2;
|
||||
const position = {
|
||||
left: targetPosition.x * halfWidth + halfWidth - 200,
|
||||
top: -targetPosition.y * halfHeight + halfHeight - 500,
|
||||
};
|
||||
this.$emit("clickModel", editor.selected, position); // 自定义事件 传递值“子向父组件传值”
|
||||
}
|
||||
},
|
||||
getDeltaPosition(positionModel, positionLabel) {
|
||||
return new THREE.Vector3(
|
||||
positionLabel.x - positionModel.x,
|
||||
positionLabel.y - positionModel.y,
|
||||
positionLabel.z - positionModel.z
|
||||
);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
html {
|
||||
height: 99%;
|
||||
background: transparent;
|
||||
}
|
||||
body {
|
||||
height: 100%;
|
||||
background: transparent;
|
||||
}
|
||||
#app {
|
||||
height: 100%;
|
||||
background: transparent;
|
||||
}
|
||||
#viewPort {
|
||||
background: transparent !important;
|
||||
}
|
||||
</style>
|
||||
1435
src/components/DMT/indexAutoPlay.vue
Normal file
1435
src/components/DMT/indexAutoPlay.vue
Normal file
File diff suppressed because it is too large
Load Diff
1251
src/components/DMT/indexVR.vue
Normal file
1251
src/components/DMT/indexVR.vue
Normal file
File diff suppressed because it is too large
Load Diff
139
src/components/DMT/js/DmtControl.js
Normal file
139
src/components/DMT/js/DmtControl.js
Normal file
@@ -0,0 +1,139 @@
|
||||
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
|
||||
import * as TWEEN from 'three/examples/jsm/libs/tween.module.js'
|
||||
|
||||
// import { viewCubeUpdate } from './DmtViewCube'
|
||||
const _changeEvent = { type: 'change' }
|
||||
class DmtControl extends OrbitControls {
|
||||
constructor(object, domElement) {
|
||||
super(object, domElement)
|
||||
this.cubeControlDirection = {
|
||||
'TOP': 'TOP',
|
||||
'BOTTOM': 'BOTTOM',
|
||||
'FRONT': 'FRONT',
|
||||
'BACK': 'BACK',
|
||||
'LEFT': 'LEFT',
|
||||
'RIGHT': 'RIGHT',
|
||||
|
||||
'BACK-LEFT': 'BACK-LEFT',
|
||||
'FRONT-LEFT': 'FRONT-LEFT',
|
||||
'TOP-LEFT': 'TOP-LEFT',
|
||||
'TOP-RIGHT': 'TOP-RIGHT',
|
||||
'RIGHT-BOTTOM': 'RIGHT-BOTTOM',
|
||||
'LEFT-BOTTOM': 'LEFT-BOTTOM',
|
||||
'BACK-BOTTOM': 'BACK-BOTTOM',
|
||||
'TOP-FRONT': 'TOP-FRONT',
|
||||
'FRONT-BOTTOM': 'FRONT-BOTTOM',
|
||||
'TOP-BACK': 'TOP-BACK',
|
||||
'RIGHT-FRONT': 'RIGHT-FRONT',
|
||||
'BACK-RIGHT': 'BACK-RIGHT',
|
||||
|
||||
'BACK-RIGHT-BOTTOM': 'BACK-RIGHT-BOTTOM',
|
||||
'TOP-RIGHT-BACK': 'TOP-RIGHT-BACK',
|
||||
'TOP-LEFT-BACK': 'TOP-LEFT-BACK',
|
||||
'TOP-LEFT-FRONT': 'TOP-LEFT-FRONT',
|
||||
'FRONT-BOTTOM-RIGHT': 'FRONT-BOTTOM-RIGHT',
|
||||
'TOP-RIGHT-FRONT': 'TOP-RIGHT-FRONT',
|
||||
'BACK-BOTTOM-LEFT': 'BACK-BOTTOM-LEFT',
|
||||
'FRONT-BOTTOM-LEFT': 'FRONT-BOTTOM-LEFT'
|
||||
}
|
||||
|
||||
const scope = this
|
||||
const STATE = {
|
||||
NONE: -1,
|
||||
ROTATE: 0,
|
||||
DOLLY: 1,
|
||||
PAN: 2,
|
||||
TOUCH_ROTATE: 3,
|
||||
TOUCH_PAN: 4,
|
||||
TOUCH_DOLLY_PAN: 5,
|
||||
TOUCH_DOLLY_ROTATE: 6
|
||||
}
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
let state = STATE.NONE
|
||||
this.resetState = function(target0, position0, zoom0) {
|
||||
scope.target.copy(target0)
|
||||
scope.object.position.copy(position0)
|
||||
if (scope.object.isOrthographicCamera) {
|
||||
scope.object.zoom = zoom0
|
||||
} else {
|
||||
scope.object.zoom = 1
|
||||
}
|
||||
scope.object.updateProjectionMatrix()
|
||||
scope.dispatchEvent(_changeEvent)
|
||||
scope.update()
|
||||
state = STATE.NONE
|
||||
// viewCubeUpdate()
|
||||
}
|
||||
|
||||
this.resetStateTween = function(target1, current1, target2, current2, duration) {
|
||||
const scope = this
|
||||
const positionVar = {
|
||||
x1: current1.x,
|
||||
y1: current1.y,
|
||||
z1: current1.z,
|
||||
x2: target1.x,
|
||||
y2: target1.y,
|
||||
z2: target1.z
|
||||
}
|
||||
// 关闭控制器
|
||||
scope.enabled = false
|
||||
var tween = new TWEEN.Tween(positionVar)
|
||||
tween.to({
|
||||
x1: current2.x,
|
||||
y1: current2.y,
|
||||
z1: current2.z,
|
||||
x2: target2.x,
|
||||
y2: target2.y,
|
||||
z2: target2.z
|
||||
}, duration)
|
||||
tween.onUpdate(function() {
|
||||
scope.object.position.x = positionVar.x1
|
||||
scope.object.position.y = positionVar.y1
|
||||
scope.object.position.z = positionVar.z1
|
||||
scope.target.x = positionVar.x2
|
||||
scope.target.y = positionVar.y2
|
||||
scope.target.z = positionVar.z2
|
||||
scope.object.updateProjectionMatrix()
|
||||
scope.update()
|
||||
})
|
||||
tween.start()
|
||||
tween.onComplete(function() {
|
||||
// /开启控制器
|
||||
scope.enabled = true
|
||||
})
|
||||
tween.easing(TWEEN.Easing.Cubic.InOut)
|
||||
}
|
||||
|
||||
this.setLookAt = function(position0, target0, zoom0) {
|
||||
scope.target.copy(target0)
|
||||
scope.object.position.copy(position0)
|
||||
scope.object.zoom = zoom0
|
||||
scope.object.updateProjectionMatrix()
|
||||
scope.dispatchEvent(_changeEvent)
|
||||
scope.update()
|
||||
state = STATE.NONE
|
||||
}
|
||||
this.setLookAt1 = function(target0, rotation) {
|
||||
scope.target.copy(this.target)
|
||||
scope.object.rotation.copy(rotation)
|
||||
scope.object.updateProjectionMatrix()
|
||||
scope.dispatchEvent(_changeEvent)
|
||||
scope.update()
|
||||
state = STATE.NONE
|
||||
}
|
||||
this.moveTo = function(x, y, z) {
|
||||
scope.target.set(x, y, z)
|
||||
}
|
||||
this.getState = function() {
|
||||
return {
|
||||
target: scope.target.clone(),
|
||||
position: scope.object.position.clone(),
|
||||
zoom: parseFloat(scope.object.zoom.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
export {
|
||||
DmtControl
|
||||
}
|
||||
|
||||
81
src/components/DMT/js/MqttConnect.js
Normal file
81
src/components/DMT/js/MqttConnect.js
Normal file
@@ -0,0 +1,81 @@
|
||||
import { WebsocketMessageReceive } from './WebSocketMessage.js'
|
||||
import mqtt from 'mqtt'
|
||||
|
||||
function connectMqtt(editor) {
|
||||
const connection = {
|
||||
host: editor.mqttHost,
|
||||
port: editor.mqttPort,
|
||||
endpoint: '/mqtt',
|
||||
clean: true, // 保留会话
|
||||
connectTimeout: 4000, // 超时时间
|
||||
reconnectPeriod: 4000, // 重连时间间隔
|
||||
// 认证信息
|
||||
clientId: 'mqttjs_3be2c321' + new Date().getTime(), // 同一个ID 无法二次连接
|
||||
username: 'DT',
|
||||
password: ''
|
||||
}
|
||||
const subscription = {
|
||||
topic: editor.opname,
|
||||
qos: 0
|
||||
}
|
||||
const websocketMessageReceive = new WebsocketMessageReceive(editor)
|
||||
|
||||
const { host, port, endpoint, ...options } = connection
|
||||
const wsHeaderString = location.protocol === 'https:' ? 'wss' : 'ws'
|
||||
// const connectUrl = `${wsHeaderString}://${location.host}${endpoint}`
|
||||
const connectUrl = `${wsHeaderString}://${host}:${port}${endpoint}`
|
||||
console.log(connectUrl)
|
||||
try {
|
||||
editor.mqttClient = mqtt.connect(connectUrl, options)
|
||||
} catch (error) {
|
||||
console.log('MQTT connect error', error)
|
||||
}
|
||||
editor.mqttClient.on('connect', () => {
|
||||
editor.showMessage('加载完成,连接MQTT服务器成功!', 'success', 2000)
|
||||
console.log('MQTT Connection succeeded!')
|
||||
editor.isOk = true
|
||||
const { topic, qos } = subscription
|
||||
editor.mqttClient.subscribe(topic, { qos }, (error, res) => {
|
||||
if (error) {
|
||||
console.log('Subscribe to topics error', error)
|
||||
return
|
||||
}
|
||||
websocketMessageReceive.getOriginTagValue()
|
||||
setTimeout(() => {
|
||||
websocketMessageReceive.websocketMessage({ data: 'DT|PlayAct|7B155BE8-16E6-4FDE-B935-8648A4885787|1' })
|
||||
}, 1000)
|
||||
})
|
||||
})
|
||||
editor.mqttClient.on('error', error => {
|
||||
console.log('MQTT Connection failed', error)
|
||||
try {
|
||||
editor.showMessage('MQTT服务器连接失败', 'error', 2000)
|
||||
editor.socket.close()
|
||||
if (editor.IsAllowConnect) {
|
||||
// 显示一下
|
||||
editor.showMessage('重新连接MQTT服务器', 'warning', 2000)
|
||||
console.log('重新连接MQTT服务器')
|
||||
// 重新连接,打开计时器
|
||||
setTimeout(function() {
|
||||
connectMqtt(editor)
|
||||
}, 5000)
|
||||
}
|
||||
} catch (exception) {
|
||||
const error = exception.toString()
|
||||
editor.showMessage.error('MQTT服务器出现错误,关闭连接' + error, 'error', 2000)
|
||||
}
|
||||
})
|
||||
// 接收消息
|
||||
editor.mqttClient.on('message', (topic, message) => {
|
||||
try {
|
||||
websocketMessageReceive.websocketMessage({ data: message.toString() })
|
||||
} catch (exception) {
|
||||
console.log(exception.toString())
|
||||
}
|
||||
})
|
||||
editor.mqttClient.on('close', () => {
|
||||
editor.mqttClient.end()
|
||||
})
|
||||
}
|
||||
export { connectMqtt }
|
||||
|
||||
40
src/components/DMT/js/ObjectAlarmCommand.js
Normal file
40
src/components/DMT/js/ObjectAlarmCommand.js
Normal file
@@ -0,0 +1,40 @@
|
||||
import * as THREE from 'three'
|
||||
|
||||
/**
|
||||
* @param editor Editor
|
||||
* @param object THREE.Object3D
|
||||
* @constructor
|
||||
*/
|
||||
class ObjectAlarmCommand {
|
||||
constructor(editor, ModelId) {
|
||||
this.type = 'ObjectAlarmCommand'
|
||||
this.ModelId = ModelId
|
||||
this.editor = editor
|
||||
this.object = editor.getObjectById(ModelId)
|
||||
}
|
||||
|
||||
exec(isAlarm = true) {
|
||||
if (!this.object) return
|
||||
if (isAlarm) {
|
||||
this.object.traverse(object => {
|
||||
if (object instanceof THREE.Mesh && !Object.hasOwnProperty.call(object.userData, 'isAxis')) {
|
||||
object.material.color = new THREE.Color('#ff0000')
|
||||
object.material.side = 2
|
||||
object.material.transparent = true
|
||||
object.material.opacity = 0.8
|
||||
}
|
||||
})
|
||||
// this.editor.playSoundCommand.exec('Alarm', true, this.ModelId)
|
||||
} else {
|
||||
this.object.traverse(object => {
|
||||
if (object instanceof THREE.Mesh) {
|
||||
const m = this.editor.objMaterial.get(object.id)
|
||||
if (m)object.material = m.clone()
|
||||
}
|
||||
})
|
||||
// this.editor.playSoundCommand.exec('Alarm', false, this.ModelId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { ObjectAlarmCommand }
|
||||
58
src/components/DMT/js/PlaySoundCommand.js
Normal file
58
src/components/DMT/js/PlaySoundCommand.js
Normal file
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* @param editor Editor
|
||||
* @param object THREE.Object3D
|
||||
* @constructor
|
||||
*/
|
||||
class PlaySoundCommand {
|
||||
constructor() {
|
||||
this.type = 'PlaySoundCommand'
|
||||
this.PlayType = {
|
||||
Alarm: 'Alarm',
|
||||
Clash: 'Clash'
|
||||
}
|
||||
this.AlarmList = []
|
||||
this.isAlarm = false
|
||||
}
|
||||
|
||||
/**
|
||||
* 系统声音
|
||||
* @param type 报警Alarm, 碰撞Clash 。。。。。。。
|
||||
* @param state 报警/解除报警
|
||||
* @param ModelId 模型ID
|
||||
*/
|
||||
exec(type, state = true, ModelId) {
|
||||
switch (type) {
|
||||
case this.PlayType.Alarm:
|
||||
if (state) {
|
||||
if (!this.isAlarm) this.playAlarmSound(true)
|
||||
if (this.AlarmList.indexOf(ModelId) === -1) this.AlarmList.push(ModelId)
|
||||
this.isAlarm = true
|
||||
} else {
|
||||
this.AlarmList = this.AlarmList.filter(res => { return res !== ModelId })
|
||||
if (this.AlarmList.length === 0) {
|
||||
this.playAlarmSound(false)
|
||||
this.isAlarm = false
|
||||
}
|
||||
}
|
||||
break
|
||||
case this.PlayType.Clash:
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
playAlarmSound(play = true) {
|
||||
// const audio = document.getElementById('alarm')
|
||||
// play ? audio.play() : audio.pause()
|
||||
}
|
||||
playClashSound(play) {
|
||||
const audio = document.getElementById('clash')
|
||||
play ? audio.play() : audio.pause()
|
||||
}
|
||||
}
|
||||
|
||||
export { PlaySoundCommand }
|
||||
631
src/components/DMT/js/WebSocketMessage.js
Normal file
631
src/components/DMT/js/WebSocketMessage.js
Normal file
@@ -0,0 +1,631 @@
|
||||
import * as THREE from 'three'
|
||||
import { ObjectAlarmCommand } from './ObjectAlarmCommand.js'
|
||||
import * as TWEEN from 'three/examples/jsm/libs/tween.module.js'
|
||||
|
||||
const WebsocketMessageReceive = function(editor) {
|
||||
const scope = this
|
||||
this.websocketMessage = (msg) => {
|
||||
new Promise((resolve, reject) => {
|
||||
let returnMes
|
||||
const arrays_signal = msg.data.split('|')
|
||||
const m_SourceType = arrays_signal[0] // 数据类型:DT:数字孪生数据
|
||||
const m_DeviceType = arrays_signal[1] // 设备类型,Robot:机器人类型的设备,Device:普通设备
|
||||
const m_DeviceName = arrays_signal[2] // 设备名称
|
||||
const m_DevicePartName = arrays_signal[3] // 设备运动部件
|
||||
const m_DevicePartDate = arrays_signal[4] // 设备运动部件数据
|
||||
switch (m_SourceType) {
|
||||
case 'DT':
|
||||
switch (m_DeviceType) {
|
||||
case editor.ProtocolIdentifiers.Robot_7: // 接受机器人运动数据进行播放 --------------------------现场用----------------
|
||||
returnMes = scope.deviceMoveEvent.RobotTranslation7(m_DeviceName, m_DevicePartName, m_DevicePartDate)
|
||||
break
|
||||
case editor.ProtocolIdentifiers.Device_3:
|
||||
console.log('设备运行')// --------------------------现场用----------------
|
||||
returnMes = scope.deviceMoveEvent.DeviceThreeAxis(m_DeviceName, m_DevicePartName, m_DevicePartDate)
|
||||
break
|
||||
case editor.ProtocolIdentifiers.Agv_3:// --------------------------现场用----------------
|
||||
console.log('AGV运行')
|
||||
returnMes = scope.deviceMoveEvent.AGVThreeAxis(m_DeviceName, m_DevicePartName, m_DevicePartDate)
|
||||
break
|
||||
case editor.ProtocolIdentifiers.Device_1: // 接受设备运动数据进行播放,已更新
|
||||
break
|
||||
case editor.ProtocolIdentifiers.FirstPosition: // 接受第一个点的数据,瞬间跳到该点,已更新
|
||||
break
|
||||
case editor.ProtocolIdentifiers.WareHousePart: //
|
||||
returnMes = scope.drawWareHousePartOne(m_DeviceName)
|
||||
break
|
||||
case editor.ProtocolIdentifiers.Robot_6: // 接受机器人运动数据进行播放
|
||||
returnMes = scope.deviceMoveEvent.RobotTranslation(m_DeviceName, m_DevicePartName, m_DevicePartDate)
|
||||
break
|
||||
case editor.ProtocolIdentifiers.ReLoadData: // 加载所有要播放的数据,避免资源浪费 ***********************UUID测试好用了******************************
|
||||
break
|
||||
case editor.ProtocolIdentifiers.PlayAct: // 根据ID 播放数据 状态为1 0暂停 ***********************UUID测试好用了******************************
|
||||
scope.playActData(m_DeviceName, parseInt(m_DevicePartName))
|
||||
break
|
||||
// case editor.ProtocolIdentifiers.ModelTransparent: // 根据传过来的模型名字和透明度 改变模型的透明度 ***********************UUID测试好用了******************************
|
||||
// returnMes = scope.robotPlan_ModelOperation.modelTrans(m_DeviceName, m_DevicePartName)
|
||||
// break
|
||||
case editor.ProtocolIdentifiers.Visible: // 接受CS指令显示、隐藏模型 ***********************UUID测试好用了******************************
|
||||
returnMes = scope.visibleObjectToBS(m_DeviceName, m_DevicePartName)
|
||||
break
|
||||
case 'JointMoveTo':
|
||||
if (!JSON.parse(m_DevicePartName)) console.log('JointMoveTo数据不对' + m_DevicePartName)
|
||||
this.JointMoveTo(m_DeviceName, JSON.parse(m_DevicePartName))
|
||||
break
|
||||
case editor.ProtocolIdentifiers.C_B_TagIDTrigger:
|
||||
{
|
||||
const tag = JSON.parse(m_DeviceName)
|
||||
scope.solveTagValue(tag.TagID, tag.TagValue)
|
||||
}
|
||||
break
|
||||
case editor.ProtocolIdentifiers.C_B_ObjectAlarm:
|
||||
{
|
||||
const Info = JSON.parse(m_DeviceName)
|
||||
console.log(Info)
|
||||
for (const infoElement of Info) {
|
||||
new ObjectAlarmCommand(editor, infoElement.ModelId).exec(!!parseInt(infoElement.value))
|
||||
}
|
||||
}
|
||||
break
|
||||
case editor.ProtocolIdentifiers.C_B_JointMoveTo:
|
||||
{
|
||||
const MoveValueList = JSON.parse(m_DeviceName)
|
||||
scope.ModelJointMoveTo(MoveValueList)
|
||||
}
|
||||
break
|
||||
case 'showWeld':
|
||||
this.showWeld(m_DeviceName, m_DevicePartName)
|
||||
break
|
||||
case 'showCheckOk':
|
||||
this.showCheckOk(m_DeviceName, m_DevicePartName)
|
||||
break
|
||||
default:
|
||||
returnMes = '不存在协议:' + msg
|
||||
break
|
||||
}
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
resolve(returnMes)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* HandlerType: 1:显隐,2:自转,3:行为,4:私服位置,5:附加拆离指令
|
||||
* HandlerCode: 模型或者工艺代码
|
||||
* HandlerValue: 显隐:0/1 ,自传:正负转速&axis,行为暂停播放:0/1
|
||||
* TagValueTagValue: 信号值
|
||||
* @param tagId
|
||||
* @param tagValue
|
||||
*/
|
||||
this.solveTagValue = (tagId, tagValue) => {
|
||||
const scope = this
|
||||
const actionInfo = editor.TagActList[tagId]
|
||||
if (!actionInfo) return
|
||||
for (const actionInfoElement of actionInfo) {
|
||||
switch (actionInfoElement.HandlerType) {
|
||||
case editor.signalSolveType.warning: // 报警
|
||||
if (tagValue === actionInfoElement.TagValue) {
|
||||
new ObjectAlarmCommand(editor, actionInfoElement.HandlerCode).exec(!!parseInt(actionInfoElement.HandlerValue))
|
||||
}
|
||||
break
|
||||
case editor.signalSolveType.visible: // 显隐
|
||||
if (tagValue === actionInfoElement.TagValue) {
|
||||
scope.visibleObjectToBS(actionInfoElement.HandlerCode, parseInt(actionInfoElement.HandlerValue))
|
||||
}
|
||||
break
|
||||
case editor.signalSolveType.rotation: // 自转
|
||||
if (tagValue === actionInfoElement.TagValue) {
|
||||
const ValueFactor = actionInfoElement.ValueFactor
|
||||
const Axis = actionInfoElement.Axis
|
||||
// const JointType = actionInfoElement.JointType
|
||||
const Flip = actionInfoElement.Flip
|
||||
const ModelCode = actionInfoElement.HandlerCode
|
||||
scope.modelRotation(ModelCode, Axis, Flip * parseFloat(ValueFactor))
|
||||
}
|
||||
break
|
||||
case editor.signalSolveType.craft: // 行为
|
||||
if (tagValue === actionInfoElement.TagValue) {
|
||||
scope.playActData(actionInfoElement.HandlerCode, parseInt(actionInfoElement.HandlerValue))
|
||||
}
|
||||
break
|
||||
case editor.signalSolveType.targetLocation: // 伺服位置
|
||||
scope.solveServoSignal(actionInfoElement, tagValue)
|
||||
break
|
||||
case editor.signalSolveType.attach: // 附加功能
|
||||
console.log('这个地方的附加功能没用写')
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 根据ACTid播放动作,开启线程
|
||||
* @param actID
|
||||
* @param state
|
||||
*/
|
||||
this.playActData = (actID, state) => {
|
||||
const scope = this
|
||||
if (!editor.isOk) return
|
||||
if (!editor.worker[actID]) editor.worker[actID] = new Worker('./DMT_WEB/webWorker/playFunction.js')
|
||||
if (editor.moveTrigger[actID].moveStatus !== state) {
|
||||
editor.worker[actID].postMessage({
|
||||
ActID: actID,
|
||||
State: state,
|
||||
isOk: editor.isOk,
|
||||
craftPlaySolveType: editor.craftPlaySolveType,
|
||||
moveTrigger: JSON.stringify(editor.moveTrigger[actID])
|
||||
})
|
||||
editor.moveTrigger[actID].moveStatus = state
|
||||
}
|
||||
editor.worker[actID].onmessage = function(event) {
|
||||
editor.moveTrigger[actID].myVarSetInterval = event.data.myVarSetInterval
|
||||
editor.moveTrigger[actID].currentFrame = event.data.pausedFrame
|
||||
const currentFrameData = event.data.currentFrameData
|
||||
const frameType = event.data.frameType
|
||||
editor.solveAttachModel('', true)
|
||||
switch (frameType) {
|
||||
case editor.craftPlaySolveType.attach:
|
||||
if (editor.FrameOperation.attach) {
|
||||
const position = currentFrameData['AttachToModelCode']
|
||||
const List_AttachModel = currentFrameData['List_AttachModel']
|
||||
const IsTwoWay = currentFrameData['IsTwoWay']
|
||||
const IsAttach = currentFrameData['IsAttach']
|
||||
if (IsAttach) {
|
||||
const attachData = []
|
||||
for (const editorElement of List_AttachModel) {
|
||||
attachData.push({
|
||||
ModelId: editorElement.ModelCode,
|
||||
ModelName: editorElement.ModelName,
|
||||
RootId: position,
|
||||
IsTwoWay: IsTwoWay
|
||||
})
|
||||
}
|
||||
editor.attachList.set(position, attachData)
|
||||
} else {
|
||||
for (const detachData of List_AttachModel) {
|
||||
const detachModelCode = detachData.ModelCode
|
||||
editor.scene.attach(editor.getObjectById(detachModelCode))
|
||||
editor.attachList.forEach((value, key) => {
|
||||
const attachList = value.filter(res => { return res.ModelId !== detachModelCode })
|
||||
editor.attachList.set(key, attachList)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
case editor.craftPlaySolveType.visible:
|
||||
if (editor.FrameOperation.visibleObject) {
|
||||
const position = currentFrameData['ModelCode']
|
||||
const Visible = parseInt(currentFrameData['Visible'])
|
||||
scope.visibleObjectToBS(position, Visible)
|
||||
}
|
||||
break
|
||||
case editor.craftPlaySolveType.rotation:
|
||||
if (editor.FrameOperation.rotationObject) {
|
||||
const position = currentFrameData['ModelCode']
|
||||
const Axis = currentFrameData['Axis']
|
||||
const Rate = currentFrameData['Rate']
|
||||
scope.modelRotation(position, Axis, Rate)
|
||||
}
|
||||
break
|
||||
case editor.craftPlaySolveType.craft:
|
||||
if (editor.FrameOperation.playCraft) {
|
||||
const position = currentFrameData['CraftCode']
|
||||
const craftState = parseInt(currentFrameData['CraftValue'])
|
||||
scope.playActData(position, craftState)
|
||||
}
|
||||
break
|
||||
case editor.craftPlaySolveType.FrameData:
|
||||
{
|
||||
const position = currentFrameData['i']
|
||||
const objectToMove = editor.getObjectById(position)
|
||||
if (objectToMove) {
|
||||
if (currentFrameData.tx === 'NaN' || currentFrameData.ty === 'NaN' || currentFrameData.tz === 'NaN' || currentFrameData.qx === 'NaN' || currentFrameData.qy === 'NaN' || currentFrameData.qz === 'NaN' || currentFrameData.qw === 'NaN') {
|
||||
console.log('craftDataError')
|
||||
// editor.robotPlan_MessageList.execute(position, 'craftDataError')
|
||||
} else {
|
||||
const movePosition = new THREE.Vector3(parseFloat(currentFrameData.tx), parseFloat(currentFrameData.ty), parseFloat(currentFrameData.tz))
|
||||
const nq = new THREE.Quaternion(parseFloat(currentFrameData.qx), parseFloat(currentFrameData.qy), parseFloat(currentFrameData.qz), parseFloat(currentFrameData.qw))
|
||||
objectToMove.quaternion.copy(nq)
|
||||
objectToMove.position.copy(movePosition)
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
case editor.craftPlaySolveType.warning: // 报警
|
||||
if (editor.FrameOperation.warning) {
|
||||
new ObjectAlarmCommand(scope.editor, currentFrameData['ModelCode']).exec(!!parseInt(currentFrameData['Visible']))
|
||||
}
|
||||
break
|
||||
default:
|
||||
delete editor.worker[event.data.ActID]
|
||||
editor.moveTrigger[actID].moveStatus = 0
|
||||
editor.moveTrigger[actID].myVarSetInterval = null
|
||||
editor.moveTrigger[actID].currentFrame = 0
|
||||
editor.moveTrigger[actID].pausedFrame = 0
|
||||
setTimeout(() => {
|
||||
scope.websocketMessage({ data: 'DT|PlayAct|7B155BE8-16E6-4FDE-B935-8648A4885787|1' })
|
||||
}, 1000)
|
||||
break
|
||||
}
|
||||
editor.solveAttachModel('', false)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 接受CS模型自转的指令
|
||||
* 接受CS模型自转的指令
|
||||
* @param objectNames
|
||||
* @param values
|
||||
* @param axis
|
||||
*/
|
||||
this.modelRotation = (objectNames, axis, values) => {
|
||||
const scope = this
|
||||
const EP = 0.000001
|
||||
let dir = 0
|
||||
if (!editor.getObjectById(objectNames)) return
|
||||
const currentModel = editor.getObjectById(objectNames)
|
||||
if ((parseFloat(values) >= -EP) && (parseFloat(values) <= EP)) {
|
||||
if (editor.RotationWorker[objectNames]) {
|
||||
editor.RotationWorker[objectNames].terminate()
|
||||
delete editor.RotationWorker[objectNames]
|
||||
delete editor.ModelRotateWorker[objectNames]
|
||||
}
|
||||
return
|
||||
} else {
|
||||
dir = 1
|
||||
if (editor.RotationWorker[objectNames]) {
|
||||
editor.RotationWorker[objectNames].terminate()
|
||||
delete editor.RotationWorker[objectNames]
|
||||
delete editor.ModelRotateWorker[objectNames]
|
||||
}
|
||||
}
|
||||
editor.RotationWorker[objectNames] = new Worker('webWorker/modelRotation.js')
|
||||
|
||||
if (!editor.ModelRotateWorker[objectNames]) {
|
||||
editor.ModelRotateWorker[objectNames] = {
|
||||
state: 0,
|
||||
myVarSetInterval: null,
|
||||
doAnimation: false,
|
||||
ModelCode: objectNames
|
||||
}
|
||||
}
|
||||
|
||||
editor.RotationWorker[objectNames].postMessage({
|
||||
ObjectNames: objectNames,
|
||||
Values: values,
|
||||
Dir: dir,
|
||||
ModelRotateWorker: editor.ModelRotateWorker[objectNames],
|
||||
RotateSpeed: editor.rotateSpeed,
|
||||
RotationValue: currentModel.rotation[axis]
|
||||
})
|
||||
|
||||
editor.RotationWorker[objectNames].onmessage = (event) => {
|
||||
scope.modelSelfRotation(currentModel, event.data.rotation, axis)
|
||||
}
|
||||
}
|
||||
this.modelSelfRotation = (currentModel, rotation, axis) => {
|
||||
currentModel.rotation[axis] = rotation
|
||||
}
|
||||
this.ModelJointMoveTo = (MoveValueList) => {
|
||||
// TOBS?WEB?SCADAMain?DT|C_B_JointMoveTo|[{"ModelCode":"5E6D91B8-B44D-4C44-AAEF-10492E548A1A","JointType":"rotation","Axis":"x","Flip":1,"Value":0,"ValueFactor":0.017453292519943295,"Delta":0}]
|
||||
// TOBS?WEB?SCADAMain?DT|C_B_JointMoveTo|[{"ModelCode":"644506B1-A05A-4E57-BBA2-C63635586C63","JointType":"position","Axis":"y","Flip":-1,"Value":-15.0,"ValueFactor":0.001,"Delta":-655.0}]
|
||||
new Promise(resolve => {
|
||||
for (const MoveValue of MoveValueList) {
|
||||
const ModelCode = MoveValue.ModelCode
|
||||
const ValueFactor = parseFloat(MoveValue.ValueFactor)
|
||||
const type = MoveValue.JointType
|
||||
const Value = parseFloat(MoveValue.Value)
|
||||
const Flip = parseFloat(MoveValue.Flip)
|
||||
const axis = MoveValue.Axis
|
||||
const Delta = parseFloat(MoveValue.Delta)
|
||||
const object = editor.getObjectById(ModelCode)
|
||||
const originInfo = editor.objectIdSet.get(ModelCode)
|
||||
if (!originInfo) return
|
||||
const matrix = originInfo['matrix']
|
||||
const originValue = type === 'rotation' ? originInfo[type]['_' + axis] : originInfo[type][axis]
|
||||
const lastValue = originInfo['lastValue'] === null ? originValue : originInfo['lastValue']
|
||||
const moveValue = Flip * (Value - Delta) * ValueFactor
|
||||
|
||||
if (object) {
|
||||
editor.solveAttachModel('', true)
|
||||
const JointValue = originValue + moveValue
|
||||
new TWEEN.Tween({
|
||||
moveValue: lastValue
|
||||
}).to({ moveValue: JointValue }, 50)
|
||||
.onStart(function() {
|
||||
originInfo['lastValue'] = JointValue
|
||||
}).onUpdate(function() {
|
||||
switch (type) {
|
||||
case 'position':
|
||||
object[type][axis] = this._object.moveValue
|
||||
break
|
||||
case 'rotation':
|
||||
{
|
||||
const moveAxis = {
|
||||
x: new THREE.Vector3(1, 1, 1),
|
||||
y: new THREE.Vector3(1, 1, 1),
|
||||
z: new THREE.Vector3(1, 1, 1)
|
||||
}
|
||||
matrix.extractBasis(moveAxis.x, moveAxis.y, moveAxis.z)
|
||||
const nM = new THREE.Matrix4()
|
||||
nM.makeRotationAxis(moveAxis[axis], this._object.moveValue)
|
||||
nM.multiply(matrix)
|
||||
object.rotation.setFromRotationMatrix(nM)
|
||||
object.updateWorldMatrix()
|
||||
}
|
||||
break
|
||||
}
|
||||
}).start()
|
||||
editor.solveAttachModel('', false)
|
||||
} else {
|
||||
console.log('模型不存在')
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
/**
|
||||
* 接伺服类的数据
|
||||
* @param ModelInfo
|
||||
* @param tagValue
|
||||
*/
|
||||
this.solveServoSignal = (ModelInfo, tagValue) => {
|
||||
try {
|
||||
const type = ModelInfo.JointType
|
||||
const axis = ModelInfo.Axis
|
||||
const flip = ModelInfo.Flip
|
||||
const Value = ModelInfo.Value
|
||||
const ValueFactor = ModelInfo.ValueFactor // 倍率
|
||||
const ModelId = ModelInfo.HandlerCode // 模型代码
|
||||
const object = editor.getObjectById(ModelId)
|
||||
const originInfo = editor.objectIdSet.get(ModelId)
|
||||
if (!originInfo) return
|
||||
const matrix = originInfo['matrix']
|
||||
const originValue = type === 'rotation' ? originInfo[type]['_' + axis] : originInfo[type][axis]
|
||||
const lastValue = originInfo['lastValue'] === null ? originValue : originInfo['lastValue']
|
||||
const moveValue = flip * (parseFloat(tagValue) - Value) * ValueFactor
|
||||
|
||||
if (object) {
|
||||
editor.solveAttachModel('', true)
|
||||
const JointValue = originValue + moveValue
|
||||
new TWEEN.Tween({
|
||||
moveValue: lastValue
|
||||
}).to({ moveValue: JointValue }, 100)
|
||||
.onStart(function() {
|
||||
originInfo['lastValue'] = JointValue
|
||||
})
|
||||
.onUpdate(function() {
|
||||
switch (type) {
|
||||
case 'position':
|
||||
object[type][axis] = this._object.moveValue
|
||||
break
|
||||
case 'rotation':
|
||||
{
|
||||
const moveAxis = {
|
||||
x: new THREE.Vector3(1, 1, 1),
|
||||
y: new THREE.Vector3(1, 1, 1),
|
||||
z: new THREE.Vector3(1, 1, 1)
|
||||
}
|
||||
matrix.extractBasis(moveAxis.x, moveAxis.y, moveAxis.z)
|
||||
const nM = new THREE.Matrix4()
|
||||
nM.makeRotationAxis(moveAxis[axis], this._object.moveValue)
|
||||
// nM.makeRotationAxis(moveAxis[axis], JointValue)
|
||||
nM.multiply(matrix)
|
||||
object.rotation.setFromRotationMatrix(nM)
|
||||
object.updateWorldMatrix()
|
||||
}
|
||||
break
|
||||
}
|
||||
}).start()
|
||||
editor.solveAttachModel('', false)
|
||||
}
|
||||
//
|
||||
// if (object) {
|
||||
// editor.solveAttachModel('', true)
|
||||
// object[type][axis] = originValue + (ValueFactor * flip * parseFloat(tagValue) + Value)
|
||||
// editor.solveAttachModel('', false)
|
||||
// }
|
||||
} catch (e) {
|
||||
console.log('solveServoSignal错误')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 接受机器人类的数据
|
||||
* @param ModelId
|
||||
* @param JointData
|
||||
* @constructor
|
||||
*/
|
||||
this.JointMoveTo = (ModelId, JointData = {}) => {
|
||||
if (editor.robotList[ModelId]) {
|
||||
const ModelList = editor.robotList[ModelId]
|
||||
// if (window.updateRobotData) window.updateRobotData(ModelId, JointData)
|
||||
// window.updateLabelInfoJoint(ModelId, JointData)
|
||||
for (let i = 0; i < ModelList.length; i++) {
|
||||
if (ModelList[i].ModelId === '') return
|
||||
this.jointMoveToPromise(JointData, ModelList[i], i, ModelId)
|
||||
}
|
||||
}
|
||||
}
|
||||
this.jointMoveToPromise = (JointData, ModelListData, i, ModelId) => {
|
||||
new Promise(resolve => {
|
||||
const ModelIdJoint = ModelListData.ModelId
|
||||
const object = ModelListData.object
|
||||
const type = ModelListData.type
|
||||
const axis = ModelListData.axis
|
||||
const matrix = ModelListData.matrix
|
||||
const flip = ModelListData.flip
|
||||
const delta = parseFloat(ModelListData.delta)
|
||||
const multiple = ModelListData.multiple
|
||||
const originInfo = editor.objectIdSet.get(ModelIdJoint)
|
||||
if (!originInfo) return
|
||||
const originValue = type === 'rotation' ? originInfo[type]['_' + axis] : originInfo[type][axis]
|
||||
const lastValue = ModelListData.lastValue === null ? originValue : ModelListData.lastValue
|
||||
const moveValue = flip * (parseFloat(JointData['J' + (i + 1)]) - delta) * multiple
|
||||
if (object) {
|
||||
const JointValue = originValue + moveValue
|
||||
new TWEEN.Tween({
|
||||
moveValue: lastValue
|
||||
}).to({ moveValue: JointValue }, 200)
|
||||
.onStart(function() {
|
||||
editor.robotList[ModelId][i]['lastValue'] = JointValue
|
||||
})
|
||||
.onUpdate(function() {
|
||||
switch (type) {
|
||||
case 'position':
|
||||
object[type][axis] = this._object.moveValue
|
||||
break
|
||||
case 'rotation':
|
||||
{
|
||||
const moveAxis = {
|
||||
x: new THREE.Vector3(1, 1, 1),
|
||||
y: new THREE.Vector3(1, 1, 1),
|
||||
z: new THREE.Vector3(1, 1, 1)
|
||||
}
|
||||
matrix.extractBasis(moveAxis.x, moveAxis.y, moveAxis.z)
|
||||
const nM = new THREE.Matrix4()
|
||||
nM.makeRotationAxis(moveAxis[axis], this._object.moveValue)
|
||||
// nM.makeRotationAxis(moveAxis[axis], JointValue)
|
||||
nM.multiply(matrix)
|
||||
object.rotation.setFromRotationMatrix(nM)
|
||||
object.updateWorldMatrix()
|
||||
}
|
||||
break
|
||||
}
|
||||
}).start()
|
||||
}
|
||||
})
|
||||
}
|
||||
this.getAxisValue = (axis, value) => {
|
||||
return JSON.parse('{"' + axis + '":' + value + '}')
|
||||
}
|
||||
|
||||
this.getAxisValue = (axis, value) => {
|
||||
return JSON.parse('{"' + axis + '":' + value + '}')
|
||||
}
|
||||
|
||||
this.showWeld = (ModelId, Welding) => {
|
||||
// 徐工专用
|
||||
if (ModelId === '46DDBCD8-D775-49A4-BB14-30015FF87595') editor.WeldLightVisible1 = !!parseInt(Welding)
|
||||
if (ModelId === '1E96DED2-6EEF-469F-BFE6-8F52FB62C2B0') editor.WeldLightVisible2 = !!parseInt(Welding)
|
||||
}
|
||||
this.showCheckOk = (check) => {
|
||||
// 徐工专用
|
||||
if (parseInt(check)) {
|
||||
editor.showTagList[3].title = '工件:QY16KC, 检测OK'
|
||||
} else {
|
||||
editor.showTagList[3].title = '工件:QY16KC'
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 显示隐藏物体
|
||||
* @param m_DeviceName
|
||||
* @param isVisible
|
||||
*/
|
||||
this.visibleObjectToBS = (m_DeviceName, isVisible) => {
|
||||
const currentModel = editor.getObjectById(m_DeviceName)
|
||||
if (currentModel) {
|
||||
currentModel.visible = !!parseInt(isVisible)
|
||||
currentModel.traverse(res => {
|
||||
res.visible = !!parseInt(isVisible)
|
||||
})
|
||||
currentModel.userData['visible'] = !!parseInt(isVisible)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化获取TagValue
|
||||
*/
|
||||
this.getOriginTagValue = () => {
|
||||
let msg = ''
|
||||
let socketMsg = ''
|
||||
const keyWord = 'B_C_DT_WebLoaded'
|
||||
msg = 'DT|' + keyWord
|
||||
socketMsg = editor.BSport + msg
|
||||
const topic = editor.BSport.split('?')[2]
|
||||
switch (editor.connectType) {
|
||||
case 1:
|
||||
editor.socket.send(socketMsg)
|
||||
break
|
||||
case 2:
|
||||
editor.mqttClient.publish(topic, msg, 0, error => {
|
||||
if (error) {
|
||||
console.log('Publish error', error)
|
||||
}
|
||||
})
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 更新库存状态
|
||||
* @param dataInfo
|
||||
*/
|
||||
this.drawWareHousePartOne = (dataInfo) => {
|
||||
const scope = this
|
||||
try {
|
||||
const whInfo = JSON.parse(dataInfo)
|
||||
for (const whStoreInfo of whInfo) {
|
||||
const whStoreNum = whStoreInfo.Key // 库位号 唯一值
|
||||
const whStoreState = whStoreInfo.Value // 立库状态
|
||||
const whNum = whStoreNum.toString().substring(0, 4) // 立库号 1000一期立库 1003刀具库 1002三坐标测量库 1001机床线旁库
|
||||
const currentStoreInfo = editor.WareHouse.get(whStoreNum)
|
||||
if (currentStoreInfo) {
|
||||
if (parseInt(whStoreState) === 0) {
|
||||
// 0的时候直接移除库存
|
||||
editor.removeObject(currentStoreInfo.object)
|
||||
editor.WareHouse.delete(whStoreNum)
|
||||
} else {
|
||||
// 不一样的状态 更新
|
||||
if (currentStoreInfo.whStoreState !== whStoreState) {
|
||||
// 移除零件,更新库存信息信息
|
||||
editor.removeObject(currentStoreInfo.object)
|
||||
scope.updateStoreModel(whNum, whStoreNum, whStoreState)
|
||||
currentStoreInfo.whStoreState = whStoreState
|
||||
currentStoreInfo.object = null
|
||||
}
|
||||
}
|
||||
} else {
|
||||
editor.WareHouse.set(whStoreNum, {
|
||||
whNum: whNum,
|
||||
whStoreNum: whStoreNum,
|
||||
whStoreState: whStoreState,
|
||||
object: null
|
||||
})
|
||||
scope.updateStoreModel(whNum, whStoreNum, whStoreState)
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('库存解析错误')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新零件
|
||||
* @param whNum
|
||||
* @param whStoreNum
|
||||
* @param whStoreState
|
||||
*/
|
||||
this.updateStoreModel = (whNum, whStoreNum, whStoreState) => {
|
||||
try {
|
||||
const basicPartModel = editor.WareHouseBasicModel.get('warehousepart' + whStoreState)
|
||||
const basicWareHouseModelData = editor.WareHouseBasicModel.get('warehouse' + whNum)
|
||||
if (basicPartModel && basicWareHouseModelData) {
|
||||
const basicPartModelClone = basicPartModel.clone()
|
||||
const basicPartModelPosition = basicWareHouseModelData[whStoreNum]
|
||||
basicPartModelClone.position.copy(basicPartModelPosition.getWorldPosition(new THREE.Vector3()))
|
||||
editor.scene.add(basicPartModelClone)
|
||||
editor.WareHouse.get(whStoreNum).object = basicPartModelClone
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('库存模型解析错误')
|
||||
}
|
||||
}
|
||||
}
|
||||
export {
|
||||
WebsocketMessageReceive
|
||||
}
|
||||
1049
src/components/DMT/js/mousetrap.js
Normal file
1049
src/components/DMT/js/mousetrap.js
Normal file
File diff suppressed because it is too large
Load Diff
535
src/components/DMT/js/three-spritetext.module.js
Normal file
535
src/components/DMT/js/three-spritetext.module.js
Normal file
@@ -0,0 +1,535 @@
|
||||
import { LinearFilter, Sprite, SpriteMaterial, Texture } from 'three'
|
||||
|
||||
function _classCallCheck(instance, Constructor) {
|
||||
if (!(instance instanceof Constructor)) {
|
||||
throw new TypeError('Cannot call a class as a function')
|
||||
}
|
||||
}
|
||||
|
||||
function _defineProperties(target, props) {
|
||||
for (var i = 0; i < props.length; i++) {
|
||||
var descriptor = props[i]
|
||||
descriptor.enumerable = descriptor.enumerable || false
|
||||
descriptor.configurable = true
|
||||
if ('value' in descriptor) descriptor.writable = true
|
||||
Object.defineProperty(target, descriptor.key, descriptor)
|
||||
}
|
||||
}
|
||||
|
||||
function _createClass(Constructor, protoProps, staticProps) {
|
||||
if (protoProps) _defineProperties(Constructor.prototype, protoProps)
|
||||
if (staticProps) _defineProperties(Constructor, staticProps)
|
||||
Object.defineProperty(Constructor, 'prototype', {
|
||||
writable: false
|
||||
})
|
||||
return Constructor
|
||||
}
|
||||
|
||||
function _inherits(subClass, superClass) {
|
||||
if (typeof superClass !== 'function' && superClass !== null) {
|
||||
throw new TypeError('Super expression must either be null or a function')
|
||||
}
|
||||
|
||||
subClass.prototype = Object.create(superClass && superClass.prototype, {
|
||||
constructor: {
|
||||
value: subClass,
|
||||
writable: true,
|
||||
configurable: true
|
||||
}
|
||||
})
|
||||
Object.defineProperty(subClass, 'prototype', {
|
||||
writable: false
|
||||
})
|
||||
if (superClass) _setPrototypeOf(subClass, superClass)
|
||||
}
|
||||
|
||||
function _getPrototypeOf(o) {
|
||||
_getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
|
||||
return o.__proto__ || Object.getPrototypeOf(o)
|
||||
}
|
||||
return _getPrototypeOf(o)
|
||||
}
|
||||
|
||||
function _setPrototypeOf(o, p) {
|
||||
_setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
|
||||
o.__proto__ = p
|
||||
return o
|
||||
}
|
||||
|
||||
return _setPrototypeOf(o, p)
|
||||
}
|
||||
|
||||
function _isNativeReflectConstruct() {
|
||||
if (typeof Reflect === 'undefined' || !Reflect.construct) return false
|
||||
if (Reflect.construct.sham) return false
|
||||
if (typeof Proxy === 'function') return true
|
||||
|
||||
try {
|
||||
Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function() {}))
|
||||
return true
|
||||
} catch (e) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
function _assertThisInitialized(self) {
|
||||
if (self === void 0) {
|
||||
throw new ReferenceError("this hasn't been initialised - super() hasn't been called")
|
||||
}
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
function _possibleConstructorReturn(self, call) {
|
||||
if (call && (typeof call === 'object' || typeof call === 'function')) {
|
||||
return call
|
||||
} else if (call !== void 0) {
|
||||
throw new TypeError('Derived constructors may only return object or undefined')
|
||||
}
|
||||
|
||||
return _assertThisInitialized(self)
|
||||
}
|
||||
|
||||
function _createSuper(Derived) {
|
||||
var hasNativeReflectConstruct = _isNativeReflectConstruct()
|
||||
|
||||
return function _createSuperInternal() {
|
||||
var Super = _getPrototypeOf(Derived)
|
||||
var result
|
||||
|
||||
if (hasNativeReflectConstruct) {
|
||||
var NewTarget = _getPrototypeOf(this).constructor
|
||||
|
||||
result = Reflect.construct(Super, arguments, NewTarget)
|
||||
} else {
|
||||
result = Super.apply(this, arguments)
|
||||
}
|
||||
|
||||
return _possibleConstructorReturn(this, result)
|
||||
}
|
||||
}
|
||||
|
||||
function _slicedToArray(arr, i) {
|
||||
return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest()
|
||||
}
|
||||
|
||||
function _toConsumableArray(arr) {
|
||||
return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread()
|
||||
}
|
||||
|
||||
function _arrayWithoutHoles(arr) {
|
||||
if (Array.isArray(arr)) return _arrayLikeToArray(arr)
|
||||
}
|
||||
|
||||
function _arrayWithHoles(arr) {
|
||||
if (Array.isArray(arr)) return arr
|
||||
}
|
||||
|
||||
function _iterableToArray(iter) {
|
||||
if (typeof Symbol !== 'undefined' && iter[Symbol.iterator] != null || iter['@@iterator'] != null) return Array.from(iter)
|
||||
}
|
||||
|
||||
function _iterableToArrayLimit(arr, i) {
|
||||
var _i = arr == null ? null : typeof Symbol !== 'undefined' && arr[Symbol.iterator] || arr['@@iterator']
|
||||
|
||||
if (_i == null) return
|
||||
var _arr = []
|
||||
var _n = true
|
||||
var _d = false
|
||||
|
||||
var _s, _e
|
||||
|
||||
try {
|
||||
for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) {
|
||||
_arr.push(_s.value)
|
||||
|
||||
if (i && _arr.length === i) break
|
||||
}
|
||||
} catch (err) {
|
||||
_d = true
|
||||
_e = err
|
||||
} finally {
|
||||
try {
|
||||
if (!_n && _i['return'] != null) _i['return']()
|
||||
} finally {
|
||||
if (_d) throw _e
|
||||
}
|
||||
}
|
||||
|
||||
return _arr
|
||||
}
|
||||
|
||||
function _unsupportedIterableToArray(o, minLen) {
|
||||
if (!o) return
|
||||
if (typeof o === 'string') return _arrayLikeToArray(o, minLen)
|
||||
var n = Object.prototype.toString.call(o).slice(8, -1)
|
||||
if (n === 'Object' && o.constructor) n = o.constructor.name
|
||||
if (n === 'Map' || n === 'Set') return Array.from(o)
|
||||
if (n === 'Arguments' || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen)
|
||||
}
|
||||
|
||||
function _arrayLikeToArray(arr, len) {
|
||||
if (len == null || len > arr.length) len = arr.length
|
||||
|
||||
for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]
|
||||
|
||||
return arr2
|
||||
}
|
||||
|
||||
function _nonIterableSpread() {
|
||||
throw new TypeError('Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.')
|
||||
}
|
||||
|
||||
function _nonIterableRest() {
|
||||
throw new TypeError('Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.')
|
||||
}
|
||||
|
||||
var three = typeof window !== 'undefined' && window.THREE ? window.THREE // Prefer consumption from global THREE, if exists
|
||||
: {
|
||||
LinearFilter: LinearFilter,
|
||||
Sprite: Sprite,
|
||||
SpriteMaterial: SpriteMaterial,
|
||||
Texture: Texture
|
||||
}
|
||||
|
||||
var _default = /* #__PURE__*/(function(_three$Sprite) {
|
||||
_inherits(_default, _three$Sprite)
|
||||
|
||||
var _super = _createSuper(_default)
|
||||
|
||||
function _default() {
|
||||
var _this
|
||||
|
||||
var text = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''
|
||||
var textHeight = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 10
|
||||
var color = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'rgba(255, 255, 255, 1)'
|
||||
|
||||
_classCallCheck(this, _default)
|
||||
|
||||
_this = _super.call(this, new three.SpriteMaterial())
|
||||
_this._text = ''.concat(text)
|
||||
_this._textHeight = textHeight
|
||||
_this._color = color
|
||||
_this._backgroundColor = false // no background color
|
||||
|
||||
_this._padding = 0
|
||||
_this._borderWidth = 0
|
||||
_this._borderRadius = 0
|
||||
_this._borderColor = 'white'
|
||||
_this._strokeWidth = 0
|
||||
_this._strokeColor = 'white'
|
||||
_this._fontFace = 'Arial'
|
||||
_this._fontSize = 90 // defines text resolution
|
||||
|
||||
_this._fontWeight = 'normal'
|
||||
_this._canvas = document.createElement('canvas')
|
||||
_this.material.depthTest = false
|
||||
_this._genCanvas()
|
||||
|
||||
return _this
|
||||
}
|
||||
|
||||
_createClass(_default, [{
|
||||
key: 'text',
|
||||
get: function get() {
|
||||
return this._text
|
||||
},
|
||||
set: function set(text) {
|
||||
this._text = text
|
||||
|
||||
this._genCanvas()
|
||||
}
|
||||
}, {
|
||||
key: 'textHeight',
|
||||
get: function get() {
|
||||
return this._textHeight
|
||||
},
|
||||
set: function set(textHeight) {
|
||||
this._textHeight = textHeight
|
||||
|
||||
this._genCanvas()
|
||||
}
|
||||
}, {
|
||||
key: 'color',
|
||||
get: function get() {
|
||||
return this._color
|
||||
},
|
||||
set: function set(color) {
|
||||
this._color = color
|
||||
|
||||
this._genCanvas()
|
||||
}
|
||||
}, {
|
||||
key: 'backgroundColor',
|
||||
get: function get() {
|
||||
return this._backgroundColor
|
||||
},
|
||||
set: function set(color) {
|
||||
this._backgroundColor = color
|
||||
|
||||
this._genCanvas()
|
||||
}
|
||||
}, {
|
||||
key: 'padding',
|
||||
get: function get() {
|
||||
return this._padding
|
||||
},
|
||||
set: function set(padding) {
|
||||
this._padding = padding
|
||||
|
||||
this._genCanvas()
|
||||
}
|
||||
}, {
|
||||
key: 'borderWidth',
|
||||
get: function get() {
|
||||
return this._borderWidth
|
||||
},
|
||||
set: function set(borderWidth) {
|
||||
this._borderWidth = borderWidth
|
||||
|
||||
this._genCanvas()
|
||||
}
|
||||
}, {
|
||||
key: 'borderRadius',
|
||||
get: function get() {
|
||||
return this._borderRadius
|
||||
},
|
||||
set: function set(borderRadius) {
|
||||
this._borderRadius = borderRadius
|
||||
|
||||
this._genCanvas()
|
||||
}
|
||||
}, {
|
||||
key: 'borderColor',
|
||||
get: function get() {
|
||||
return this._borderColor
|
||||
},
|
||||
set: function set(borderColor) {
|
||||
this._borderColor = borderColor
|
||||
|
||||
this._genCanvas()
|
||||
}
|
||||
}, {
|
||||
key: 'fontFace',
|
||||
get: function get() {
|
||||
return this._fontFace
|
||||
},
|
||||
set: function set(fontFace) {
|
||||
this._fontFace = fontFace
|
||||
|
||||
this._genCanvas()
|
||||
}
|
||||
}, {
|
||||
key: 'fontSize',
|
||||
get: function get() {
|
||||
return this._fontSize
|
||||
},
|
||||
set: function set(fontSize) {
|
||||
this._fontSize = fontSize
|
||||
|
||||
this._genCanvas()
|
||||
}
|
||||
}, {
|
||||
key: 'fontWeight',
|
||||
get: function get() {
|
||||
return this._fontWeight
|
||||
},
|
||||
set: function set(fontWeight) {
|
||||
this._fontWeight = fontWeight
|
||||
|
||||
this._genCanvas()
|
||||
}
|
||||
}, {
|
||||
key: 'strokeWidth',
|
||||
get: function get() {
|
||||
return this._strokeWidth
|
||||
},
|
||||
set: function set(strokeWidth) {
|
||||
this._strokeWidth = strokeWidth
|
||||
|
||||
this._genCanvas()
|
||||
}
|
||||
}, {
|
||||
key: 'strokeColor',
|
||||
get: function get() {
|
||||
return this._strokeColor
|
||||
},
|
||||
set: function set(strokeColor) {
|
||||
this._strokeColor = strokeColor
|
||||
|
||||
this._genCanvas()
|
||||
}
|
||||
}, {
|
||||
key: '_genCanvas',
|
||||
value: function _genCanvas() {
|
||||
var _this2 = this
|
||||
|
||||
var canvas = this._canvas
|
||||
var ctx = canvas.getContext('2d')
|
||||
var border = Array.isArray(this.borderWidth) ? this.borderWidth : [this.borderWidth, this.borderWidth] // x,y border
|
||||
|
||||
var relBorder = border.map(function(b) {
|
||||
return b * _this2.fontSize * 0.1
|
||||
}) // border in canvas units
|
||||
|
||||
var borderRadius = Array.isArray(this.borderRadius) ? this.borderRadius : [this.borderRadius, this.borderRadius, this.borderRadius, this.borderRadius] // tl tr br bl corners
|
||||
|
||||
var relBorderRadius = borderRadius.map(function(b) {
|
||||
return b * _this2.fontSize * 0.1
|
||||
}) // border radius in canvas units
|
||||
|
||||
var padding = Array.isArray(this.padding) ? this.padding : [this.padding, this.padding] // x,y padding
|
||||
|
||||
var relPadding = padding.map(function(p) {
|
||||
return p * _this2.fontSize * 0.1
|
||||
}) // padding in canvas units
|
||||
|
||||
var lines = this.text.split('\n')
|
||||
var font = ''.concat(this.fontWeight, ' ').concat(this.fontSize, 'px ').concat(this.fontFace)
|
||||
ctx.font = font // measure canvas with appropriate font
|
||||
|
||||
var innerWidth = Math.max.apply(Math, _toConsumableArray(lines.map(function(line) {
|
||||
return ctx.measureText(line).width
|
||||
})))
|
||||
var innerHeight = this.fontSize * lines.length
|
||||
canvas.width = innerWidth + relBorder[0] * 2 + relPadding[0] * 2
|
||||
canvas.height = innerHeight + relBorder[1] * 2 + relPadding[1] * 2 // paint border
|
||||
|
||||
if (this.borderWidth) {
|
||||
ctx.strokeStyle = this.borderColor
|
||||
|
||||
if (relBorder[0]) {
|
||||
// left + right borders
|
||||
var hb = relBorder[0] / 2
|
||||
ctx.lineWidth = relBorder[0]
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(hb, relBorderRadius[0])
|
||||
ctx.lineTo(hb, canvas.height - relBorderRadius[3])
|
||||
ctx.moveTo(canvas.width - hb, relBorderRadius[1])
|
||||
ctx.lineTo(canvas.width - hb, canvas.height - relBorderRadius[2])
|
||||
ctx.stroke()
|
||||
}
|
||||
|
||||
if (relBorder[1]) {
|
||||
// top + bottom borders
|
||||
var _hb = relBorder[1] / 2
|
||||
|
||||
ctx.lineWidth = relBorder[1]
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(Math.max(relBorder[0], relBorderRadius[0]), _hb)
|
||||
ctx.lineTo(canvas.width - Math.max(relBorder[0], relBorderRadius[1]), _hb)
|
||||
ctx.moveTo(Math.max(relBorder[0], relBorderRadius[3]), canvas.height - _hb)
|
||||
ctx.lineTo(canvas.width - Math.max(relBorder[0], relBorderRadius[2]), canvas.height - _hb)
|
||||
ctx.stroke()
|
||||
}
|
||||
|
||||
if (this.borderRadius) {
|
||||
// strike rounded corners
|
||||
var cornerWidth = Math.max.apply(Math, _toConsumableArray(relBorder))
|
||||
|
||||
var _hb2 = cornerWidth / 2
|
||||
|
||||
ctx.lineWidth = cornerWidth
|
||||
ctx.beginPath();
|
||||
[!!relBorderRadius[0] && [relBorderRadius[0], _hb2, _hb2, relBorderRadius[0]], !!relBorderRadius[1] && [canvas.width - relBorderRadius[1], canvas.width - _hb2, _hb2, relBorderRadius[1]], !!relBorderRadius[2] && [canvas.width - relBorderRadius[2], canvas.width - _hb2, canvas.height - _hb2, canvas.height - relBorderRadius[2]], !!relBorderRadius[3] && [relBorderRadius[3], _hb2, canvas.height - _hb2, canvas.height - relBorderRadius[3]]].filter(function(d) {
|
||||
return d
|
||||
}).forEach(function(_ref) {
|
||||
var _ref2 = _slicedToArray(_ref, 4)
|
||||
var x0 = _ref2[0]
|
||||
var x1 = _ref2[1]
|
||||
var y0 = _ref2[2]
|
||||
var y1 = _ref2[3]
|
||||
|
||||
ctx.moveTo(x0, y0)
|
||||
ctx.quadraticCurveTo(x1, y0, x1, y1)
|
||||
})
|
||||
ctx.stroke()
|
||||
}
|
||||
} // paint background
|
||||
|
||||
if (this.backgroundColor) {
|
||||
ctx.fillStyle = this.backgroundColor
|
||||
|
||||
if (!this.borderRadius) {
|
||||
ctx.fillRect(relBorder[0], relBorder[1], canvas.width - relBorder[0] * 2, canvas.height - relBorder[1] * 2)
|
||||
} else {
|
||||
// fill with rounded corners
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(relBorder[0], relBorderRadius[0]);
|
||||
[[relBorder[0], relBorderRadius[0], canvas.width - relBorderRadius[1], relBorder[1], relBorder[1], relBorder[1]], // t
|
||||
[canvas.width - relBorder[0], canvas.width - relBorder[0], canvas.width - relBorder[0], relBorder[1], relBorderRadius[1], canvas.height - relBorderRadius[2]], // r
|
||||
[canvas.width - relBorder[0], canvas.width - relBorderRadius[2], relBorderRadius[3], canvas.height - relBorder[1], canvas.height - relBorder[1], canvas.height - relBorder[1]], // b
|
||||
[relBorder[0], relBorder[0], relBorder[0], canvas.height - relBorder[1], canvas.height - relBorderRadius[3], relBorderRadius[0]] // t
|
||||
].forEach(function(_ref3) {
|
||||
var _ref4 = _slicedToArray(_ref3, 6)
|
||||
var x0 = _ref4[0]
|
||||
var x1 = _ref4[1]
|
||||
var x2 = _ref4[2]
|
||||
var y0 = _ref4[3]
|
||||
var y1 = _ref4[4]
|
||||
var y2 = _ref4[5]
|
||||
|
||||
ctx.quadraticCurveTo(x0, y0, x1, y1)
|
||||
ctx.lineTo(x2, y2)
|
||||
})
|
||||
ctx.closePath()
|
||||
ctx.fill()
|
||||
}
|
||||
}
|
||||
|
||||
ctx.translate.apply(ctx, _toConsumableArray(relBorder))
|
||||
ctx.translate.apply(ctx, _toConsumableArray(relPadding)) // paint text
|
||||
|
||||
ctx.font = font // Set font again after canvas is resized, as context properties are reset
|
||||
|
||||
ctx.fillStyle = this.color
|
||||
ctx.textBaseline = 'bottom'
|
||||
var drawTextStroke = this.strokeWidth > 0
|
||||
|
||||
if (drawTextStroke) {
|
||||
ctx.lineWidth = this.strokeWidth * this.fontSize / 10
|
||||
ctx.strokeStyle = this.strokeColor
|
||||
}
|
||||
|
||||
lines.forEach(function(line, index) {
|
||||
var lineX = (innerWidth - ctx.measureText(line).width) / 2
|
||||
var lineY = (index + 1) * _this2.fontSize
|
||||
drawTextStroke && ctx.strokeText(line, lineX, lineY)
|
||||
ctx.fillText(line, lineX, lineY)
|
||||
}) // Inject canvas into sprite
|
||||
|
||||
if (this.material.map) this.material.map.dispose() // gc previous texture
|
||||
|
||||
var texture = this.material.map = new three.Texture(canvas)
|
||||
texture.minFilter = three.LinearFilter
|
||||
texture.needsUpdate = true
|
||||
var yScale = this.textHeight * lines.length + border[1] * 2 + padding[1] * 2
|
||||
this.scale.set(yScale * canvas.width / canvas.height, yScale, 0.001)
|
||||
}
|
||||
}, {
|
||||
key: 'clone',
|
||||
value: function clone() {
|
||||
return new this.constructor(this.text, this.textHeight, this.color).copy(this)
|
||||
}
|
||||
}, {
|
||||
key: 'copy',
|
||||
value: function copy(source) {
|
||||
three.Sprite.prototype.copy.call(this, source)
|
||||
this.color = source.color
|
||||
this.backgroundColor = source.backgroundColor
|
||||
this.padding = source.padding
|
||||
this.borderWidth = source.borderWidth
|
||||
this.borderColor = source.borderColor
|
||||
this.fontFace = source.fontFace
|
||||
this.fontSize = source.fontSize
|
||||
this.fontWeight = source.fontWeight
|
||||
this.strokeWidth = source.strokeWidth
|
||||
this.strokeColor = source.strokeColor
|
||||
return this
|
||||
}
|
||||
}])
|
||||
|
||||
return _default
|
||||
}(three.Sprite))
|
||||
|
||||
export { _default as default }
|
||||
74
src/components/DMT/js/websocketConnect.js
Normal file
74
src/components/DMT/js/websocketConnect.js
Normal file
@@ -0,0 +1,74 @@
|
||||
import { WebsocketMessageReceive } from './WebSocketMessage.js'
|
||||
|
||||
function connect(editor) {
|
||||
const host = 'ws://' + editor.ip + '//WebModule//websocket'
|
||||
let timeout = null
|
||||
const websocketMessageReceive = new WebsocketMessageReceive(editor)
|
||||
// 尝试连接至服务器
|
||||
try {
|
||||
if ('WebSocket' in window) {
|
||||
editor.socket = new WebSocket(host)
|
||||
} else {
|
||||
editor.showMessage('浏览器不支持WebSocket', 'error', 2000)
|
||||
}
|
||||
} catch (exception) {
|
||||
editor.showMessage('与服务器断开连接', 'error', 2000)
|
||||
return
|
||||
}
|
||||
// 连接成功
|
||||
editor.socket.onopen = function() {
|
||||
// 关闭timer
|
||||
try {
|
||||
clearInterval(timeout)
|
||||
} catch (exception) {
|
||||
console.log(exception)
|
||||
}
|
||||
const name = editor.opname
|
||||
editor.socket.send('LIN,' + name)
|
||||
editor.showMessage('加载完成,连接服务器成功!', 'success', 2000)
|
||||
editor.isOk = true
|
||||
websocketMessageReceive.getOriginTagValue()
|
||||
// let a = 0
|
||||
// setInterval(() => {
|
||||
// a += 0.5
|
||||
// const message = `DT|JointMoveTo|0f26fc6e-1caf-4f57-beb3-d8a65872fbdd|{"J1":"${a}","J2":"0","J3":"0","J4":"0","J5":"0","J6":"0","J7":"0","J8":"0","J9":"0","J10":"0","J11":"0","J12":"0"}`
|
||||
// websocketMessageReceive.websocketMessage({ data: message })
|
||||
// }, 10)
|
||||
}
|
||||
// 收到消息
|
||||
editor.socket.onmessage = function(msg) {
|
||||
try {
|
||||
websocketMessageReceive.websocketMessage(msg)
|
||||
} catch (exception) {
|
||||
const error = exception.toString()
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
// 连接断开
|
||||
editor.socket.onclose = function(event) {
|
||||
try {
|
||||
editor.showMessage('onclose' + '服务器连接失败', 'error', 2000)
|
||||
editor.socket.close()
|
||||
if (editor.IsAllowConnect) {
|
||||
// 显示一下
|
||||
editor.showMessage('重新连接服务器', 'warning', 2000)
|
||||
console.log('onclose' + '重新连接服务器')
|
||||
// 重新连接,打开计时器
|
||||
timeout = setTimeout(function() {
|
||||
connect(editor)
|
||||
}, 5000)
|
||||
}
|
||||
} catch (exception) {
|
||||
const error = exception.toString()
|
||||
editor.showMessage.error('服务器出现错误,关闭连接' + error, 'error', 2000)
|
||||
}
|
||||
}
|
||||
// 出现错误
|
||||
editor.socket.onerror = function(event) {
|
||||
console.log('onerror_' + '服务器连接失败')
|
||||
editor.showMessage.error('服务器连接失败', 'error', 2000)
|
||||
}
|
||||
}
|
||||
|
||||
export { connect }
|
||||
|
||||
202
src/components/LightCard.vue
Normal file
202
src/components/LightCard.vue
Normal file
@@ -0,0 +1,202 @@
|
||||
<template>
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h2 class="card-title">{{ area.name }}</h2>
|
||||
<div class="status-indicator" :class="{ 'on': area.status }"></div>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<div class="light-info">
|
||||
<div class="light-icon" :class="{ 'hidden': !area.status }">💡</div>
|
||||
<div class="light-details">
|
||||
<div class="light-power" :class="{ 'on': area.status }">
|
||||
{{ area.status ? '开启' : '关闭' }}
|
||||
</div>
|
||||
<div class="light-location">{{ area.location }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-buttons">
|
||||
<button
|
||||
class="btn btn-on"
|
||||
:disabled="area.status"
|
||||
@click="controlLight('on')"
|
||||
>
|
||||
开启
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-off"
|
||||
:disabled="!area.status"
|
||||
@click="controlLight('off')"
|
||||
>
|
||||
关闭
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { lightControlAPI } from '@/api/lightControl'
|
||||
|
||||
export default {
|
||||
name: 'LightCard',
|
||||
props: {
|
||||
area: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async controlLight(action) {
|
||||
try {
|
||||
// 调用控制灯光API,传入this(Vue实例)
|
||||
await lightControlAPI.controlLight(this, this.area.id, action)
|
||||
|
||||
// 更新本地状态
|
||||
this.area.status = (action === 'on')
|
||||
|
||||
// 添加操作日志
|
||||
await lightControlAPI.addOperationLog(this, this.area.name, action === 'on' ? '开启' : '关闭')
|
||||
|
||||
// 通知父组件更新
|
||||
this.$emit('light-status-changed')
|
||||
|
||||
this.$message.success(`${this.area.name}灯光已${action === 'on' ? '开启' : '关闭'}`)
|
||||
} catch (error) {
|
||||
console.error('控制灯光失败:', error)
|
||||
this.$message.error('操作失败,请重试')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 保持原有的CSS样式 */
|
||||
.card {
|
||||
background: linear-gradient(145deg, #2c3e50, #1a252f);
|
||||
border-radius: 12px;
|
||||
padding: 25px;
|
||||
box-shadow: 0 6px 15px rgba(0, 0, 0, 0.3);
|
||||
transition: transform 0.3s, box-shadow 0.3s;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 15px;
|
||||
border-bottom: 1px solid #34495e;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 1.5rem;
|
||||
color: #3498db;
|
||||
}
|
||||
|
||||
.status-indicator {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
border-radius: 50%;
|
||||
background-color: #e74c3c;
|
||||
box-shadow: 0 0 10px #e74c3c;
|
||||
}
|
||||
|
||||
.status-indicator.on {
|
||||
background-color: #2ecc71;
|
||||
box-shadow: 0 0 10px #2ecc71;
|
||||
}
|
||||
|
||||
.card-content {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.light-info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.light-icon {
|
||||
font-size: 3rem;
|
||||
color: #f1c40f;
|
||||
text-shadow: 0 0 15px rgba(241, 196, 15, 0.5);
|
||||
transition: opacity 0.5s ease;
|
||||
}
|
||||
|
||||
.light-icon.hidden {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.light-details {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.light-power {
|
||||
font-size: 1.8rem;
|
||||
font-weight: bold;
|
||||
color: #e74c3c;
|
||||
}
|
||||
|
||||
.light-power.on {
|
||||
color: #2ecc71;
|
||||
}
|
||||
|
||||
.light-location {
|
||||
color: #95a5a6;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.control-buttons {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
flex: 1;
|
||||
padding: 12px;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.btn-on {
|
||||
background: linear-gradient(135deg, #2ecc71, #27ae60);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-off {
|
||||
background: linear-gradient(135deg, #e74c3c, #c0392b);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
.btn:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.btn:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
}
|
||||
</style>
|
||||
178
src/components/SystemStatus.vue
Normal file
178
src/components/SystemStatus.vue
Normal file
@@ -0,0 +1,178 @@
|
||||
<template>
|
||||
<div class="system-status">
|
||||
<div class="status-header">
|
||||
<h2 class="status-title">系统状态</h2>
|
||||
<div class="status-indicator on"></div>
|
||||
</div>
|
||||
|
||||
<div class="status-overview">
|
||||
<div class="status-item">
|
||||
<div class="status-value">{{ stats.lightsOn }}</div>
|
||||
<div class="status-label">灯光开启</div>
|
||||
</div>
|
||||
<div class="status-item">
|
||||
<div class="status-value">{{ stats.lightsOff }}</div>
|
||||
<div class="status-label">灯光关闭</div>
|
||||
</div>
|
||||
<div class="status-item">
|
||||
<div class="status-value">{{ stats.totalLights }}</div>
|
||||
<div class="status-label">总灯光数</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 style="margin-bottom: 15px; color: #3498db;">操作日志</h3>
|
||||
<div class="log-container">
|
||||
<div
|
||||
v-for="log in operationLogs"
|
||||
:key="log.id"
|
||||
class="log-entry"
|
||||
>
|
||||
<span class="log-time">{{ formatTime(log.operationTime) }}</span>
|
||||
<span class="log-message">{{ log.message }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { lightControlAPI } from '@/api/lightControl'
|
||||
|
||||
export default {
|
||||
name: 'SystemStatus',
|
||||
data() {
|
||||
return {
|
||||
stats: {
|
||||
lightsOn: 0,
|
||||
lightsOff: 0,
|
||||
totalLights: 0
|
||||
},
|
||||
operationLogs: []
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.loadSystemStats()
|
||||
this.loadOperationLogs()
|
||||
},
|
||||
methods: {
|
||||
async loadSystemStats() {
|
||||
try {
|
||||
const response = await lightControlAPI.getSystemStats(this)
|
||||
if (response.data && response.data.length > 0) {
|
||||
this.stats = response.data[0]
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取系统统计失败:', error)
|
||||
// 模拟数据用于演示
|
||||
this.stats = {
|
||||
lightsOn: 2,
|
||||
lightsOff: 1,
|
||||
totalLights: 3
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
async loadOperationLogs() {
|
||||
try {
|
||||
const response = await lightControlAPI.getOperationLogs(this)
|
||||
if (response.data && response.data.length > 0) {
|
||||
this.operationLogs = response.data
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取操作日志失败:', error)
|
||||
// 模拟数据用于演示
|
||||
this.operationLogs = [
|
||||
{
|
||||
id: 1,
|
||||
operationTime: new Date(),
|
||||
message: '系统启动完成,所有灯光状态已加载'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
formatTime(timeString) {
|
||||
if (!timeString) return ''
|
||||
const date = new Date(timeString)
|
||||
return `${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}:${date.getSeconds().toString().padStart(2, '0')}`
|
||||
},
|
||||
|
||||
refreshData() {
|
||||
this.loadSystemStats()
|
||||
this.loadOperationLogs()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.system-status {
|
||||
background: linear-gradient(145deg, #2c3e50, #1a252f);
|
||||
border-radius: 12px;
|
||||
padding: 25px;
|
||||
box-shadow: 0 6px 15px rgba(0, 0, 0, 0.3);
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.status-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.status-title {
|
||||
font-size: 1.8rem;
|
||||
color: #3498db;
|
||||
}
|
||||
|
||||
.status-overview {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.status-item {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.status-value {
|
||||
font-size: 2.5rem;
|
||||
font-weight: bold;
|
||||
color: #2ecc71;
|
||||
}
|
||||
|
||||
.status-label {
|
||||
color: #95a5a6;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.log-container {
|
||||
background-color: #1a252f;
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.log-entry {
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px solid #2c3e50;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.log-time {
|
||||
color: #3498db;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.log-message {
|
||||
color: #e0e0e0;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.status-overview {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user