手机登录/注册
X
登录
1. 建立在 TCP 协议之上,服务器端的实现比较容易。
2. 与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
3. 数据格式比较轻量,性能开销小,通信高效。
4. 可以发送文本,也可以发送二进制数据。
5. 没有同源限制,客户端可以与任意服务器通信。
6. 协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。
import { ref, onMounted, onUnmounted } from 'vue';
/**
* @description websocket hook
* @param url websocket地址:ws://xxxxxxx/
* @param onMessage 收到消息的回调
* @param heartbeatInterval 心跳间隔时间
*/
export default function useWebsocket(url:string, onMessage:(res:any)=>void,heartbeatInterval = 3000,) {
// 用来存放websocket实例
const socketTask = ref(null as any);
// 连接是否处于断开状态的标识
const isDisconnect = ref(true);
// 心跳定时器
let heartbeatTimer = null as any;
// 连接
const connect = () => {
if (isDisconnect.value) {
isDisconnect.value = false;
console.log('WebSocket连接中...',url)
console.log('socketTask',socketTask)
socketTask.value = new WebSocket(url);
socketTask.value.onopen = () => {
console.log('WebSocket连接已打开');
startHeartbeat();
}
socketTask.value.onclose = () => {
console.log('WebSocket连接已关闭');
if (isDisconnect.value) {
setTimeout(() => {
console.log('WebSocket尝试重新连接');
connect();
}, 1000);
}
} ;
socketTask.value.onerror =(error:any) => {
console.error('WebSocket连接发生错误:', error);
isDisconnect.value = true;
}
socketTask.value.onmessage = (res:{data:string}) => {
let data = JSON.parse(res.data);
console.log('收到服务器消息:', data)
if (onMessage) {
onMessage(data);
}
};
}
};
// 断开连接
const disconnect = () => {
console.log('主动断开')
if (socketTask.value) {
isDisconnect.value = false;
socketTask.value.close();
stopHeartbeat();
socketTask.value = null;
}
};
// 发送心跳
const startHeartbeat = () => {
heartbeatTimer = setInterval(() => {
if (socketTask.value) {
console.log('发送心跳')
socketTask.value.send({});
}
}, heartbeatInterval);
};
// 停止心跳
const stopHeartbeat = () => {
clearInterval(heartbeatTimer);
};
// 发送消息
const sendMessage = (message:any) => {
console.log('发送消息:socketTask.value', socketTask.value)
if (socketTask.value) {
socketTask.value.send({
data: JSON.stringify(message)
});
}
};
// 组件挂载时连接
onMounted(() => {
connect();
});
// 组件卸载时断开连接
onUnmounted(() => {
disconnect();
});
return {
socketTask,
connect,
disconnect,
sendMessage,
};
}
// Vue项目中,EventSource触发的事件中this指向变了
// 使用const that = this,然后在EventSource触发的事件中使用that
if (typeof (EventSource) !== 'undefined') {
const evtSource = new EventSource('/log/print', { withCredentials: true }) // 后端接口,要配置允许跨域属性
// 与事件源的连接刚打开时触发
evtSource.onopen = function(e){
console.log(e);
}
// 当从事件源接收到数据时触发
evtSource.onmessage = function(e){
console.log(e);
}
// 与事件源的连接无法打开时触发
evtSource.onerror = function(e){
console.log(e);
evtSource.close(); // 关闭连接
}
// 也可以侦听命名事件,即自定义的事件
evtSource.addEventListener('notice', function(e) {
console.log(e.data)
})
} else {
console.log('当前浏览器不支持使用EventSource接收服务器推送事件!');
}
// utils/sse.js
//如果加自定义参数可以使用三方插件event-source-polyfill(添加请求头token)
import { EventSourcePolyfill } from 'event-source-polyfill'
function createSSE(deviceName, handle) {
const clientId = JSON.parse(localStorage.getItem('params')).id
const token = JSON.parse(localStorage.getItem('params')).token
const sse = new EventSourcePolyfill(`/monitor/sse/createConnect?sign=${deviceName}&clientId=${clientId}`, {
headers: { token: token }
})
sse.addEventListener('message', env => {
const obj = JSON.parse(env.data)
handle(obj)
})
return sse
}
export default createSSE
// vue
<template>
<div>
<div class="dialog">
<div class="dialogTltle">
< img :src="
require('@/assets/images/digitalTwin/' +
(title == '采煤机'
? 'coal_cutter'
: title == '破碎机'
? 'crushing_machine'
: title == '运输机-机头'
? 'transport_machine'
: title == '运输机-机尾'
? 'transport_machine'
: title == '乳化液泵'
? 'emulsion_pump'
: title == '清水泵'
? 'pump'
: title == '液压支架'
? 'hydraulic_support'
: title == '转载机'
? 'transport_machine'
: title == '皮带机'
? 'transport_machine'
: 'transport_machine') +
'.png')
"
alt="" />
<div>{{ title }}</div>
</div>
< img @click="handleEyeClick"
v-show="state.eyeFlag"
src="@/assets/images/digitalTwin/close_eye.png"
alt="" />
< img @click="handleEyeClick"
v-show="!state.eyeFlag"
src="@/assets/images/digitalTwin/eye.png"
alt="" />
<div class="dialogContent">
<div v-for="item in Object.keys(newLabelObj)"
:key="item">
<div v-if="
item.indexOf('VoltageSide') === -1 &&
item != 'crusher' &&
item != 'pumpRunningQ' &&
item != 'pumpRunningR' &&
item != 'bracketInfo'
">
<div class="title">
{{
state.moduleType.find(val => val.typeDes === item)
? state.moduleType.find(val => val.typeDes === item).title
: '未知'
}}
</div>
<el-row :gutter="20">
<el-col :span="12"
v-for="item1 in Object.keys(newLabelObj[item])"
:key="item1">
<div class="dialogName">{{ item1 ? newLabelObj[item][item1] : '' }}</div>
<div class="dialogNameR"
:class="
item1.indexOf('ArmMiningHeight') != -1 ||
item1 === 'communicationState' ||
item1 === 'runningDirection' ||
item1 === 'warningCode' ||
item1 === 'faultCode' ||
item1 === 'deviceState' ||
item1 === 'speedDisplay'
? 'dialogNameBackgroundArm'
: item1 === 'totalFailure'
? 'dialogVoltageQ'
: 'dialogNameBackground'
">
<span>
<span>
{{
state.moduleType.find(val => val.typeDes === item).data[item1]
? state.moduleType.find(val => val.typeDes === item).data[item1]
: '--'
}}
</span>
</span>
</div>
</el-col>
</el-row>
</div>
<div
v-if="item === 'crusher' || item === 'pumpRunningQ' || item === 'pumpRunningR' || item === 'bracketInfo'">
<el-row :gutter="20"
v-for="item1 in Object.keys(newLabelObj[item])"
:key="item1">
<el-col :span="14">
<div class="dialogName">{{ item1 ? newLabelObj[item][item1] : '' }}</div>
<div class="dialogNameR"
:class="
item1.indexOf('ArmMiningHeight') != -1 ||
item1 === 'communicationState' ||
item1 === 'runningDirection' ||
item1 === 'runState' ||
item1 === 'runningState' ||
item1 === 'bracketNumber' ||
item1 === 'miningHigh' ||
item1 === 'frontPillarPressure' ||
item1 === 'advanceDistance' ||
item1 === 'action' ||
item1 === 'frontBeamInclination'
? 'dialogNameBackgroundArm'
: 'dialogNameBackground'
">
<span>
<span>
{{
state.moduleType.find(val => val.typeDes === item).data[item1]
? state.moduleType.find(val => val.typeDes === item).data[item1]
: '--'
}}
</span>
</span>
</div>
</el-col>
</el-row>
</div>
</div>
<div>
<template v-if="Object.keys(newLabelObj).find(val => val.indexOf('VoltageSide') != -1)">
<div class="title">移变参数</div>
<el-row :gutter="20">
<el-col :span="12">
<div class="dialogVoltageNameH">高压侧</div>
</el-col>
<el-col :span="12">
<div class="dialogVoltageNameH">低压侧</div>
</el-col>
<el-col :span="12">
<div v-for="item in Object.keys(newLabelObj)"
:key="item">
<div v-if="item.indexOf('ShiftHighVoltageSide') != -1">
<div v-for="item1 in Object.keys(newLabelObj[item])"
:key="item1">
<div :class="item1 === 'communicationStatus' ? 'dialogVoltageState' : 'dialogVoltage'">
{{ item1 ? newLabelObj[item][item1] : '--' }}
</div>
<div class="dialogVoltageT"
:class="
item1 == 'faultCode'
? 'dialogVoltageQ'
: item1 == 'communicationStatus'
? 'dialogVoltageStateValue'
: 'dialogVoltage'
">
<span>
<span>
{{
state.moduleType.find(val => val.typeDes === item).data[item1]
? state.moduleType.find(val => val.typeDes === item).data[item1]
: '--'
}}
</span>
</span>
</div>
</div>
</div>
</div>
</el-col>
<el-col :span="12">
<div v-for="item in Object.keys(newLabelObj)"
:key="item">
<div v-if="item.indexOf('ShiftLowVoltageSide') != -1">
<div v-for="item1 in Object.keys(newLabelObj[item])"
:key="item1">
<div :class="item1 === 'communicationStatus' ? 'dialogVoltageState' : 'dialogVoltage'">
{{ item1 ? newLabelObj[item][item1] : '--' }}
</div>
<div class="dialogVoltageT"
:class="
item1 == 'faultCode'
? 'dialogVoltageQ'
: item1 == 'communicationStatus'
? 'dialogVoltageStateValue'
: 'dialogVoltage'
">
<span>
<span>
{{
state.moduleType.find(val => val.typeDes === item).data[item1]
? state.moduleType.find(val => val.typeDes === item).data[item1]
: '--'
}}
</span>
</span>
</div>
</div>
</div>
</div>
</el-col>
</el-row>
</template>
</div>
</div>
</div>
</div>
</template>
<script >
import { ref, reactive, onMounted, onUnmounted, watch } from 'vue'
import { labelObj, symbolObj } from '@/assets/contant/digitalTwinLabel'
import createSSE from '@/utils/sse'
let sse = null
const moduleType = [
// 采煤机
{
typeDes: 'shearer',
showType: 'flexList',
title: '实时数据',
voltage: false,
data: {}
},
{
typeDes: 'shearerShiftHighVoltageSide',
showType: 'flexList',
title: '移变参数高',
voltage: true,
data: {}
}
]
var transObj = {}
export default {
components: {},
props: {
armData: {
type: Object,
default: () => { }
},
title: {
type: String,
default: ''
},
bracketInfoIndex: {
type: String,
default: '2'
}
},
setup(props, { emit }) {
const newLabelObj = ref({ ...labelObj })
const state = reactive({
title: '数字孪生',
titlekey: '',
eyeFlag: false,
moduleType
})
watch(
() => props.title,
newProps => {
state.titlekey =
newProps === '皮带机'
? 'belt'
: newProps === '清水泵'
? 'pumpRunningQ'
: newProps === '乳化液泵'
? 'pumpRunningR'
: newProps === '破碎机'
? 'crusher'
: newProps === '转载机'
? 'transfer'
: newProps === '运输机-机头'
? 'transport'
: newProps === '运输机-机尾'
? 'transporter'
: newProps === '液压支架'
? 'bracketInfo'
: newProps === '采煤机'
? 'shearer'
: ''
transObj = {}
Object.keys(labelObj).forEach(item => {
// 除了采煤机的实时数据需要两个地方获取
if (state.titlekey === 'transport') {
item.indexOf(state.titlekey) === 0 && item !== 'transporterTailInfo' && (transObj[item] = labelObj[item])
} else if (state.titlekey === 'transporter') {
item.indexOf(state.titlekey) === 0 && item !== 'transporterHeaderInfo' && (transObj[item] = labelObj[item])
} else if (state.titlekey === 'pumpRunningQ') {
item.indexOf('pumpRunningQ') === 0 && (transObj[item] = labelObj[item])
} else if (state.titlekey === 'pumpRunningR') {
item.indexOf('pumpRunningR') === 0 && (transObj[item] = labelObj[item])
} else {
item.indexOf(state.titlekey) === 0 && (transObj[item] = labelObj[item])
}
})
console.log(transObj)
newLabelObj.value = []
newLabelObj.value = transObj
}
)
sse = createSSE('digitalTwin', data => {
state.moduleType.forEach(item => {
const type = item.typeDes
if (data.type === type) {
if (type === 'bracketInfo') {
item.data = Object.assign(item.data, { ...data.data[props.bracketInfoIndex] })
} else {
item.data = Object.assign(item.data, { ...data.data })
}
}
if (type === 'shearer') {
if (data.type === 'shearerLeftArm' || data.type === 'shearerRightArm') {
item.data = Object.assign(item.data, { ...data.data })
}
}
if (data.type === 'pumpRunning') {
if (type === 'pumpRunningR') {
item.data = Object.assign(item.data, { ...data.data[4] })
}
if (type === 'pumpRunningQ') {
item.data = Object.assign(item.data, { ...data.data[0] })
}
}
})
emit('realDta', data)
})
const handleEyeClick = () => {
emit('closeEye')
}
const initData = JSON.parse(JSON.stringify(labelObj))
for (const p in initData) {
for (const k in initData[p]) {
initData[p][k] = '--'
}
}
state.moduleType.forEach(item => {
item.data = initData[item.typeDes]
})
state.moduleType = JSON.parse(JSON.stringify(state.moduleType))
onMounted(() => { })
onUnmounted(() => {
sse && sse.close()
})
return {
state,
symbolObj,
labelObj,
newLabelObj,
handleEyeClick
}
}
}
</script>