You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
548 lines
15 KiB
548 lines
15 KiB
<template>
|
|
<div class="debug_container">
|
|
<div class="left_oper_container">
|
|
<div class="bread_wrap">
|
|
<t-breadcrumb>
|
|
<t-breadcrumbItem>首页</t-breadcrumbItem>
|
|
<t-breadcrumbItem>相机调试</t-breadcrumbItem>
|
|
</t-breadcrumb>
|
|
</div>
|
|
<div class="operation_proposed">
|
|
<div class="top_btns">
|
|
<div class="two_btns">
|
|
<div
|
|
:class="isCameraOpen ? 'default_btn' : 'active_btn'"
|
|
@click="handleStartCpature"
|
|
>
|
|
打开相机
|
|
</div>
|
|
<div
|
|
:class="isCameraOpen ? 'active_btn' : 'default_btn'"
|
|
@click="handleStopCpature"
|
|
>
|
|
关闭相机
|
|
</div>
|
|
</div>
|
|
<div class="two_btns">
|
|
<div
|
|
:class="flashStatus ? 'default_btn' : 'active_btn'"
|
|
@click="handleOpenFlashLight"
|
|
>
|
|
打开闪光灯
|
|
</div>
|
|
<div
|
|
:class="flashStatus ? 'active_btn' : 'default_btn'"
|
|
@click="handleCloseFlashLight"
|
|
>
|
|
关闭闪光灯
|
|
</div>
|
|
</div>
|
|
<div class="active_btn" @click="handleGetMechanicalArmState">
|
|
获取机械臂坐标
|
|
</div>
|
|
</div>
|
|
<div class="camera_param">
|
|
<div class="slider_wrap mb50">
|
|
<p class="title">模拟通道亮度</p>
|
|
<t-slider
|
|
v-model="simulation_brightness"
|
|
:min="0"
|
|
:max="65535"
|
|
@change="handleSimulationBrightness"
|
|
/>
|
|
<p class="number">{{ simulation_brightness }}</p>
|
|
</div>
|
|
<div class="slider_wrap">
|
|
<p class="title">曝光时间</p>
|
|
<t-slider
|
|
v-model="exposure"
|
|
:min="0"
|
|
:max="100000"
|
|
@change="handleExposureTime"
|
|
/>
|
|
<p class="number">{{ exposure }}</p>
|
|
</div>
|
|
</div>
|
|
<div class="camera_image">
|
|
<div class="active_btn" @click="handleTakePhoto">拍照</div>
|
|
<div class="active_btn" @click="handleGetCharacterRecognitionResult">
|
|
识别
|
|
</div>
|
|
</div>
|
|
<div class="identify_results">
|
|
<div class="result_wrap">
|
|
<p class="title">结果</p>
|
|
<div class="result">{{ recognitionResult }}</div>
|
|
</div>
|
|
<div class="save_form">
|
|
<t-select
|
|
v-model="station_core_id"
|
|
class="demo-select-base"
|
|
clearable
|
|
filterable
|
|
placeholder="请选择堆芯"
|
|
>
|
|
<t-option
|
|
v-for="item in coreList"
|
|
:value="item.id"
|
|
:label="item.name"
|
|
:key="item.id"
|
|
>
|
|
{{ item.name }}
|
|
</t-option>
|
|
</t-select>
|
|
<t-button class="save_btn" @click="onSubmit">保存</t-button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="right_camera_container">
|
|
<p class="time_photo">实时照片</p>
|
|
<div class="default_photo" v-if="!isCameraOpen">
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
fill="none"
|
|
version="1.1"
|
|
width="85.90400695800781"
|
|
height="76.7254638671875"
|
|
viewBox="0 0 85.90400695800781 76.7254638671875"
|
|
>
|
|
<g>
|
|
<path
|
|
d="M6.91322,0L78.9908,0C82.8088,0,85.904,3.2252,85.904,7.20367L85.904,69.6923C85.8151,73.6041,82.7458,76.7266,78.9908,76.7255L6.91322,76.7255C3.15816,76.7266,0.0888908,73.6041,0,69.6923L0,7.20367C0,3.22519,3.09515,0,6.91322,0ZM63.5689,38.8742C64.784,37.9363,66.4446,37.9363,67.6596,38.8742L78.9908,43.7761L78.9908,7.20367L6.91322,7.20367L6.91322,38.107L22.3351,21.7815C23.6974,20.2285,26.0451,20.2285,27.4074,21.7815L48.6381,49.3601L63.5689,38.8742ZM51.0912,24.6798C51.0912,19.1005,55.4317,14.5776,60.7861,14.5776C66.1404,14.5777,70.4809,19.1005,70.481,24.6798C70.481,30.2591,66.1404,34.7821,60.7861,34.7821C55.4317,34.7821,51.0912,30.2591,51.0912,24.6798ZM58.0045,24.4667C58.0045,26.0675,59.2498,27.3652,60.7861,27.3652L60.7861,27.5357C62.3886,27.5387,63.6621,26.1337,63.5678,24.4667C63.5678,22.8658,62.3224,21.5681,60.7862,21.5681C59.2499,21.5681,58.0045,22.8658,58.0045,24.4667Z"
|
|
fill-rule="evenodd"
|
|
fill="#5E5C5C"
|
|
fill-opacity="1"
|
|
/>
|
|
</g>
|
|
</svg>
|
|
</div>
|
|
<img :src="photoUrl" class="photo" v-else />
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, onMounted, onUnmounted } from 'vue'
|
|
import {
|
|
startCapture,
|
|
stopCapture,
|
|
getCameraState,
|
|
takeAndSavePhoto,
|
|
startCharacterRecognition,
|
|
getCharacterRecognitionResult,
|
|
stopCharacterRecognition,
|
|
getFlashBrightnessAnalog,
|
|
getMechanicalArmState,
|
|
openFlashLight,
|
|
closeFlashLight,
|
|
setFlashBrightnessAnalog,
|
|
setExposureTimeRaw,
|
|
getCameraParametersInteger,
|
|
} from '@/command'
|
|
import { coreListApi } from '@/api/info'
|
|
import { MessagePlugin } from 'tdesign-vue-next'
|
|
import { addCameraConfig } from '@/api/camera'
|
|
|
|
const photoUrl = ref('')
|
|
const recognitionResult = ref('')
|
|
const station_core_id = ref('')
|
|
|
|
const coreList = ref([])
|
|
|
|
onMounted(async () => {
|
|
const res = await coreListApi()
|
|
if (res?.code == 200) {
|
|
coreList.value = res?.data?.list
|
|
}
|
|
})
|
|
|
|
const onSubmit = async () => {
|
|
if (!station_core_id.value) {
|
|
MessagePlugin('error', { content: '请选择堆芯' })
|
|
return
|
|
}
|
|
// 保存当前相机参数到相应的堆芯
|
|
const data = {
|
|
station_core_id: station_core_id.value,
|
|
simulation_brightness: simulation_brightness.value,
|
|
exposure: exposure.value,
|
|
}
|
|
const res = await addCameraConfig(data)
|
|
console.log(res)
|
|
if (res?.code == 200) {
|
|
MessagePlugin('success', { content: '保存成功' })
|
|
}
|
|
}
|
|
|
|
setInterval(
|
|
() => {
|
|
photoUrl.value = `${
|
|
import.meta.env.VITE_HOST_URL
|
|
}app/core/realtime_photo/now.png?${Math.random()}`
|
|
},
|
|
500,
|
|
500,
|
|
)
|
|
|
|
const simulation_brightness = ref(0)
|
|
const exposure = ref(0)
|
|
const flashStatus = ref(false)
|
|
|
|
// 相机参数
|
|
const isCameraOpen = ref(false)
|
|
|
|
const handleStartCpature = () => {
|
|
if (!isCameraOpen.value) {
|
|
websocketsend(startCapture)
|
|
}
|
|
}
|
|
const handleStopCpature = () => {
|
|
if (isCameraOpen.value) {
|
|
websocketsend(stopCapture)
|
|
}
|
|
}
|
|
const handleGetCameraState = () => {
|
|
websocketsend(getCameraState)
|
|
}
|
|
const handleTakePhoto = () => {
|
|
if (isCameraOpen.value) {
|
|
// 如果相机打开则发送拍照,如果相机关闭 则提醒用户
|
|
websocketsend(takeAndSavePhoto)
|
|
} else {
|
|
MessagePlugin('error', { content: '请先开启相机' })
|
|
}
|
|
}
|
|
|
|
const handleStartCharacterRecognition = () => {
|
|
websocketsend(startCharacterRecognition)
|
|
}
|
|
const handleStopCharacterRecognition = () => {
|
|
websocketsend(stopCharacterRecognition)
|
|
}
|
|
const handleGetCharacterRecognitionResult = () => {
|
|
if (isCameraOpen.value) {
|
|
// 如果相机打开则发送拍照,如果相机关闭 则提醒用户
|
|
websocketsend(getCharacterRecognitionResult)
|
|
} else {
|
|
MessagePlugin('error', { content: '请先开启相机' })
|
|
}
|
|
}
|
|
const handleGetMechanicalArmState = () => {
|
|
websocketsend(getMechanicalArmState)
|
|
}
|
|
|
|
const handleOpenFlashLight = () => {
|
|
if (!flashStatus.value) {
|
|
websocketsend(openFlashLight)
|
|
}
|
|
}
|
|
const handleCloseFlashLight = flag => {
|
|
if (flashStatus.value) {
|
|
websocketsend(closeFlashLight)
|
|
}
|
|
}
|
|
|
|
const isOpenCamera = () => {
|
|
if (!isCameraOpen.value) {
|
|
MessagePlugin('error', { content: '请先打开相机' })
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
// 是否真正建立连接
|
|
const lockReconnect = ref(false)
|
|
// 断开 重连倒计时
|
|
const timeoutnum = ref(null)
|
|
|
|
const websock = ref(null)
|
|
|
|
const reconnect = () => {
|
|
//重新连接
|
|
if (lockReconnect.value) {
|
|
return
|
|
}
|
|
lockReconnect.value = true
|
|
//没连接上会一直重连,设置延迟避免请求过多
|
|
timeoutnum.value && clearTimeout(timeoutnum.value)
|
|
timeoutnum.value = setTimeout(function () {
|
|
//新连接
|
|
initWebSocket()
|
|
lockReconnect.value = false
|
|
}, 5000)
|
|
}
|
|
|
|
// 接收到消息后改变状态
|
|
const websocketonmessage = e => {
|
|
// 相机启动停止后接收到回执 而后发送获取相机状态指令 实时改变页面状态
|
|
const data = JSON.parse(e.data)
|
|
const { messageId, success } = data
|
|
if (success) {
|
|
switch (messageId) {
|
|
case 'startCapture':
|
|
MessagePlugin('success', { content: '开启相机成功' })
|
|
handleGetCameraState()
|
|
break
|
|
case 'stopCapture':
|
|
MessagePlugin('success', { content: '关闭相机成功' })
|
|
handleGetCameraState()
|
|
break
|
|
case 'getCameraState':
|
|
const { cameraState } = data
|
|
const { isOpen } = cameraState
|
|
isCameraOpen.value = isOpen
|
|
break
|
|
case 'getMechanicalArmState':
|
|
const { state } = data
|
|
const { x, y } = state || {}
|
|
recognitionResult.value = `机械臂坐标x:${x}, y: ${y}`
|
|
break
|
|
case 'takeAndSavePhoto':
|
|
const { photoInfo } = data
|
|
const { path } = photoInfo
|
|
recognitionResult.value = `图片保存路径为${path}`
|
|
break
|
|
case 'getCameraParametersInteger':
|
|
const { value } = data
|
|
exposure.value = value
|
|
break
|
|
case 'openFlashLight':
|
|
flashStatus.value = true
|
|
break
|
|
case 'CloseFlashLight':
|
|
flashStatus.value = false
|
|
break
|
|
case 'getFlashBrightnessAnalog':
|
|
const { brightness } = data
|
|
simulation_brightness.value = brightness
|
|
break
|
|
case 'getCharacterRecognitionResult':
|
|
const { result } = data
|
|
const { result: str, srcImagePath } = result || {}
|
|
recognitionResult.value = `识别结果为${
|
|
str ? str : '空结果'
|
|
}, 图片保存路径为${srcImagePath}`
|
|
break
|
|
default:
|
|
break
|
|
}
|
|
} else {
|
|
// MessagePlugin('error', { content: 'ws发送指令执行错误' })
|
|
}
|
|
}
|
|
|
|
const websocketonopen = () => {
|
|
console.log('客户端链接成功!!!')
|
|
handleGetCameraState()
|
|
setTimeout(() => {
|
|
websocketsend(getCameraParametersInteger)
|
|
// 无法获取当前闪光灯是开是关。建议上层代码,初始化时候关闭闪光灯,然后在本地保存一个当前状态
|
|
handleCloseFlashLight(false)
|
|
websocketsend(getFlashBrightnessAnalog)
|
|
}, 1000)
|
|
}
|
|
|
|
const websocketonerror = () => {
|
|
reconnect()
|
|
}
|
|
|
|
// 发送消息
|
|
const websocketsend = data => {
|
|
websock.value.send(data)
|
|
}
|
|
|
|
const websocketclose = () => {
|
|
reconnect()
|
|
}
|
|
|
|
const initWebSocket = () => {
|
|
//初始化weosocket
|
|
const wsuri = import.meta.env.VITE_WEBSOCKET_CAMERA_URL
|
|
websock.value = new WebSocket(wsuri)
|
|
// 客户端接收服务端数据时触发
|
|
websock.value.onmessage = websocketonmessage
|
|
// 连接建立时触发
|
|
websock.value.onopen = websocketonopen
|
|
// 通信发生错误时触发
|
|
websock.value.onerror = websocketonerror
|
|
// 连接关闭时触发
|
|
websock.value.onclose = websocketclose
|
|
}
|
|
|
|
initWebSocket()
|
|
|
|
// 业务处理
|
|
const handleExposureTime = value => {
|
|
websocketsend(setExposureTimeRaw(value))
|
|
}
|
|
|
|
const handleSimulationBrightness = value => {
|
|
websocketsend(setFlashBrightnessAnalog(value))
|
|
}
|
|
|
|
onUnmounted(() => {
|
|
websock.value.close()
|
|
})
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.debug_container {
|
|
width: 100%;
|
|
height: 100%;
|
|
background: #fff;
|
|
border-radius: 6px;
|
|
padding-bottom: 30px;
|
|
padding-right: 30px;
|
|
box-sizing: border-box;
|
|
display: flex;
|
|
align-items: center;
|
|
.left_oper_container {
|
|
flex: 1;
|
|
height: 100%;
|
|
padding-left: 28px;
|
|
box-sizing: border-box;
|
|
.bread_wrap {
|
|
padding: 20px 0;
|
|
box-sizing: border-box;
|
|
border-bottom: 1px solid #d8d8d8;
|
|
}
|
|
.operation_proposed {
|
|
padding: 36px 36px 0 0;
|
|
box-sizing: border-box;
|
|
.top_btns {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
.two_btns {
|
|
display: flex;
|
|
align-items: center;
|
|
.default_btn {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border-radius: 6px;
|
|
margin-right: 10px;
|
|
padding: 7px 16px;
|
|
font-size: 16px;
|
|
font-weight: normal;
|
|
letter-spacing: 0.07em;
|
|
color: #ffffff;
|
|
background: #ebebeb;
|
|
}
|
|
}
|
|
.active_btn {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 7px 16px;
|
|
font-size: 16px;
|
|
font-weight: normal;
|
|
letter-spacing: 0.07em;
|
|
color: #ffffff;
|
|
background: #0052d9;
|
|
border-radius: 6px;
|
|
margin-right: 10px;
|
|
cursor: pointer;
|
|
}
|
|
}
|
|
.camera_param {
|
|
padding: 53px 0;
|
|
.slider_wrap {
|
|
display: flex;
|
|
align-items: center;
|
|
.title {
|
|
font-size: 20px;
|
|
font-weight: 500;
|
|
letter-spacing: 0.07em;
|
|
color: #191919;
|
|
margin-right: 32px;
|
|
white-space: nowrap;
|
|
}
|
|
.number {
|
|
margin-left: 20px;
|
|
width: 30px;
|
|
}
|
|
}
|
|
.mb50 {
|
|
margin-bottom: 50px;
|
|
}
|
|
}
|
|
.camera_image {
|
|
padding: 48px 0;
|
|
border-top: solid 1px #d8d8d8;
|
|
border-bottom: solid 1px #d8d8d8;
|
|
display: flex;
|
|
align-items: center;
|
|
.active_btn {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 7px 23px;
|
|
font-size: 16px;
|
|
font-weight: normal;
|
|
letter-spacing: 0.07em;
|
|
color: #ffffff;
|
|
background: #0052d9;
|
|
border-radius: 6px;
|
|
margin-right: 30px;
|
|
cursor: pointer;
|
|
}
|
|
}
|
|
.identify_results {
|
|
padding-top: 44px;
|
|
display: flex;
|
|
.result_wrap {
|
|
flex: 1;
|
|
.title {
|
|
font-size: 20px;
|
|
font-weight: 500;
|
|
letter-spacing: 0.07em;
|
|
color: #191919;
|
|
}
|
|
.result {
|
|
padding: 10px 0;
|
|
}
|
|
}
|
|
.save_form {
|
|
flex: 1;
|
|
.save_btn {
|
|
margin-top: 14px;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.right_camera_container {
|
|
flex: 1;
|
|
height: 100%;
|
|
padding-left: 30px;
|
|
box-sizing: border-box;
|
|
display: flex;
|
|
flex-direction: column;
|
|
.time_photo {
|
|
padding: 20px 0;
|
|
font-size: 20px;
|
|
box-sizing: border-box;
|
|
font-weight: normal;
|
|
letter-spacing: 0.07em;
|
|
color: #191919;
|
|
}
|
|
.default_photo {
|
|
flex: 1;
|
|
width: 100%;
|
|
border-radius: 6px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background: #ebebeb;
|
|
}
|
|
.photo {
|
|
flex: 1;
|
|
width: 100%;
|
|
border-radius: 6px;
|
|
}
|
|
}
|
|
}
|
|
</style>
|