From 92bd36c7051c053ed463c6b0908c62214088c416 Mon Sep 17 00:00:00 2001 From: zhanghao <774378400@qq.com> Date: Fri, 14 Aug 2026 17:31:21 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E6=8C=89=E6=9C=BA=E5=99=A8?= =?UTF-8?q?=E4=BA=BA=E5=8A=A8=E6=80=81=E8=A7=A3=E6=9E=90gRPC=E7=AB=AF?= =?UTF-8?q?=E7=82=B9=E4=B8=8E=E8=AE=BE=E5=A4=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/audioWebSocket.js | 21 ++++- server/cameraVideoStream.js | 9 +- server/index.js | 115 +++++++++++++++---------- server/robotRegistry.js | 9 ++ src/api/agv.js | 4 +- src/stores/robot.js | 26 +----- src/utils/request.js | 5 +- src/views/Address.vue | 83 +++--------------- src/views/arm/MechanicalArm.vue | 25 +----- src/views/camera/VoiceConversation.vue | 20 ++--- src/views/camera/index.vue | 16 +--- src/views/chassis/Chassis.vue | 5 +- src/views/chassis/MapCanvas.vue | 4 +- 13 files changed, 130 insertions(+), 212 deletions(-) diff --git a/server/audioWebSocket.js b/server/audioWebSocket.js index cf4beba..9c85a96 100644 --- a/server/audioWebSocket.js +++ b/server/audioWebSocket.js @@ -23,7 +23,7 @@ function waitForGrpcReady(client, timeoutMs = 5000) { }) } -export function attachAudioWebSocket(httpServer) { +export function attachAudioWebSocket(httpServer, { resolveRobotAddress, resolveRobotDeviceId } = {}) { const wss = new WebSocketServer({ server: httpServer, path: '/audio' }) wss.on('connection', (socket) => { @@ -101,14 +101,27 @@ export function attachAudioWebSocket(httpServer) { } if (message.type !== 'init' || initialized) return - const { microphoneId, robotAddress } = message - speakerId = message.speakerId?.trim() - if (![microphoneId, speakerId, robotAddress].every((value) => typeof value === 'string' && value.trim())) { + const { robotId } = message + if (typeof robotId !== 'string' || !robotId.trim()) { sendJson(socket, { type: 'error', message: '机器人地址、麦克风 ID 和扬声器 ID 不能为空' }) socket.close(1008, 'invalid init') return } + const robotAddress = resolveRobotAddress?.(robotId.trim()) + if (!robotAddress) { + sendJson(socket, { type: 'error', message: `机器人 ${robotId} 不在线或尚未注册` }) + socket.close(1008, 'robot unavailable') + return + } + const microphoneId = resolveRobotDeviceId?.(robotId.trim(), 'microphone') + speakerId = resolveRobotDeviceId?.(robotId.trim(), 'speaker') + if (!microphoneId || !speakerId) { + sendJson(socket, { type: 'error', message: `机器人 ${robotId} 缺少麦克风或扬声器设备` }) + socket.close(1008, 'audio device unavailable') + return + } + initialized = true microphoneClient = createMicrophoneGrpcClient(robotAddress.trim()) speakerClient = createSpeakerGrpcClient(robotAddress.trim()) diff --git a/server/cameraVideoStream.js b/server/cameraVideoStream.js index 27c279d..b92772d 100644 --- a/server/cameraVideoStream.js +++ b/server/cameraVideoStream.js @@ -84,14 +84,9 @@ function message(error) { } export function streamCameraVideo(req, res) { - const ip = typeof req.query.ip === 'string' ? req.query.ip.trim() : '' - const deviceId = typeof req.query.deviceId === 'string' ? req.query.deviceId.trim() : '' - if (!ip || !deviceId) { - res.status(400).json({ code: 400, message: 'ip 和 deviceId 不能为空' }) - return - } + const deviceId = req.deviceId - const client = createCameraGrpcClient(ip) + const client = createCameraGrpcClient(req.robotAddress) const grpcStream = client.getRgbImageStream() let transcoder let closed = false diff --git a/server/index.js b/server/index.js index b4014e2..c217a6c 100644 --- a/server/index.js +++ b/server/index.js @@ -17,7 +17,7 @@ import { import { attachAudioWebSocket } from './audioWebSocket.js'; import { streamCameraVideo } from './cameraVideoStream.js'; import { startQuicServer } from './quicServer.js'; -import { consumeQuicEvent, listRobots } from './robotRegistry.js'; +import { consumeQuicEvent, getRobotAddress, getRobotDeviceId, listRobots } from './robotRegistry.js'; const app = express() app.use(express.static(path.join(__dirname, '../dist'))); @@ -28,19 +28,49 @@ app.get('/api/quic/robots', (req, res) => { res.json({ code: 200, data: listRobots() }) }) +app.use('/api', (req, res, next) => { + const robotId = String(req.body?.robotId || req.query?.robotId || '').trim() + if (!robotId) { + res.status(400).json({ code: 400, message: 'robotId 不能为空' }) + return + } + const address = getRobotAddress(robotId) + if (!address) { + res.status(404).json({ code: 404, message: `机器人 ${robotId} 不在线或尚未注册` }) + return + } + req.robotId = robotId + req.robotAddress = address + next() +}) + +app.use('/api', (req, res, next) => { + let kind = '' + if (req.path.startsWith('/agv/')) kind = 'agv' + else if (req.path.startsWith('/arm/')) kind = 'arm' + else if (req.path.startsWith('/audio/microphone/')) kind = 'microphone' + else if (req.path.startsWith('/audio/speaker/')) kind = 'speaker' + else if (req.path.startsWith('/camera/')) kind = 'camera' + if (!kind) return next() + + const deviceId = getRobotDeviceId(req.robotId, kind) + if (!deviceId) { + res.status(404).json({ code: 404, message: `机器人 ${req.robotId} 没有可用的 ${kind} 设备` }) + return + } + req.deviceId = deviceId + next() +}) + // 将相机逐帧 gRPC 数据转码为浏览器可直接播放的 fragmented MP4。 -// 示例:/api/camera/video?ip=192.168.0.28%3A50052&deviceId=camera1 +// 示例:/api/camera/video?robotId=robot-1 app.get('/api/camera/video', streamCameraVideo) // 获取机器人麦克风音量(0-100)。 app.post('/api/audio/microphone/volume/get', (req, res) => { - const { ip, deviceId } = req.body - if (!ip || !deviceId) { - res.status(400).json({ code: 400, message: '机器人地址和麦克风设备 ID 不能为空' }) - return - } + const deviceId = req.deviceId - const grpcClient = createMicrophoneGrpcClient(ip) + const grpcClient = createMicrophoneGrpcClient(req.robotAddress) grpcClient.getVolume( { header: { device_id: deviceId } }, { deadline: Date.now() + 5000 }, @@ -65,18 +95,14 @@ app.post('/api/audio/microphone/volume/get', (req, res) => { // 设置机器人麦克风音量(0-100)。 app.post('/api/audio/microphone/volume/set', (req, res) => { - const { ip, deviceId } = req.body + const deviceId = req.deviceId const volume = Number(req.body.volume) - if (!ip || !deviceId) { - res.status(400).json({ code: 400, message: '机器人地址和麦克风设备 ID 不能为空' }) - return - } if (!Number.isInteger(volume) || volume < 0 || volume > 100) { res.status(400).json({ code: 400, message: '音量必须是 0 到 100 的整数' }) return } - const grpcClient = createMicrophoneGrpcClient(ip) + const grpcClient = createMicrophoneGrpcClient(req.robotAddress) grpcClient.setVolume({ header: { device_id: deviceId }, volume @@ -100,13 +126,9 @@ app.post('/api/audio/microphone/volume/set', (req, res) => { // 获取机器人扬声器音量(0-100)。 app.post('/api/audio/speaker/volume/get', (req, res) => { - const { ip, deviceId } = req.body - if (!ip || !deviceId) { - res.status(400).json({ code: 400, message: '机器人地址和扬声器设备 ID 不能为空' }) - return - } + const deviceId = req.deviceId - const grpcClient = createSpeakerGrpcClient(ip) + const grpcClient = createSpeakerGrpcClient(req.robotAddress) grpcClient.getVolume( { header: { device_id: deviceId } }, { deadline: Date.now() + 5000 }, @@ -131,18 +153,14 @@ app.post('/api/audio/speaker/volume/get', (req, res) => { // 设置机器人扬声器音量(0-100)。 app.post('/api/audio/speaker/volume/set', (req, res) => { - const { ip, deviceId } = req.body + const deviceId = req.deviceId const volume = Number(req.body.volume) - if (!ip || !deviceId) { - res.status(400).json({ code: 400, message: '机器人地址和扬声器设备 ID 不能为空' }) - return - } if (!Number.isInteger(volume) || volume < 0 || volume > 100) { res.status(400).json({ code: 400, message: '音量必须是 0 到 100 的整数' }) return } - const grpcClient = createSpeakerGrpcClient(ip) + const grpcClient = createSpeakerGrpcClient(req.robotAddress) grpcClient.setVolume({ header: { device_id: deviceId }, volume @@ -168,11 +186,11 @@ app.post('/api/audio/speaker/volume/set', (req, res) => { app.post('/api/agv/getRuntimeState', async (req, res) => { const request = { header: { - device_id: req.body.deviceId || 'agv_src1100' + device_id: req.deviceId } }; - const grpcClient = createAGVGrpcClient(req.body.ip) + const grpcClient = createAGVGrpcClient(req.robotAddress) const resp = await new Promise((resolve, reject) => { grpcClient.getRuntimeState(request, (err, res) => { @@ -199,10 +217,10 @@ app.post('/api/agv/getRuntimeState', async (req, res) => { // 获取机器人当前地图 app.post('/api/agv/getCurrentMap', async (req, res) => { - const grpcClient = createAGVGrpcClient(req.body.ip) + const grpcClient = createAGVGrpcClient(req.robotAddress) const request = { header: { - device_id: req.body.deviceId || 'agv_src1100' + device_id: req.deviceId }, map_name: req.body.mapName }; @@ -232,10 +250,10 @@ app.post('/api/agv/getCurrentMap', async (req, res) => { // 机器人运动控制 app.post('/api/agv/moveRobot', async (req, res) => { - const grpcClient = createAGVGrpcClient(req.body.ip) + const grpcClient = createAGVGrpcClient(req.robotAddress) const request = { header: { - device_id: req.body.deviceId || 'agv_src1100' + device_id: req.deviceId }, velocity: { vx: req.body.vx, @@ -272,10 +290,10 @@ app.post('/api/agv/moveRobot', async (req, res) => { // 机器人停止运动 app.post('/api/agv/stopRobot', async (req, res) => { - const grpcClient = createAGVGrpcClient(req.body.ip) + const grpcClient = createAGVGrpcClient(req.robotAddress) const request = { header: { - device_id: req.body.deviceId || 'agv_src1100' + device_id: req.deviceId } }; @@ -312,11 +330,11 @@ app.post('/api/agv/stopRobot', async (req, res) => { app.post('/api/arm/getPose', async (req, res) => { const request = { header: { - device_id: req.body.deviceId || 'huayan_arm' + device_id: req.deviceId } }; - const grpcClient = createARMGrpcClient(req.body.ip) + const grpcClient = createARMGrpcClient(req.robotAddress) const resp = await new Promise((resolve, reject) => { grpcClient.getPose(request, (err, res) => { @@ -345,11 +363,11 @@ app.post('/api/arm/getPose', async (req, res) => { app.post('/api/arm/getJointState', async (req, res) => { const request = { header: { - device_id: req.body.deviceId || 'huayan_arm' + device_id: req.deviceId } }; - const grpcClient = createARMGrpcClient(req.body.ip) + const grpcClient = createARMGrpcClient(req.robotAddress) const resp = await new Promise((resolve, reject) => { grpcClient.getJointState(request, (err, res) => { @@ -378,7 +396,7 @@ app.post('/api/arm/getJointState', async (req, res) => { app.post('/api/arm/speedJ', async (req, res) => { const request = { header: { - device_id: req.body.deviceId || 'huayan_arm' + device_id: req.deviceId }, velocity: { velocity: req.body.velocities @@ -386,7 +404,7 @@ app.post('/api/arm/speedJ', async (req, res) => { acceleration: req.body.acceleration, duration: req.body.duration }; - const grpcClient = createARMGrpcClient(req.body.ip) + const grpcClient = createARMGrpcClient(req.robotAddress) const resp = await new Promise((resolve, reject) => { grpcClient.speedJ(request, (err, res) => { @@ -414,13 +432,13 @@ app.post('/api/arm/speedJ', async (req, res) => { app.post('/api/arm/speedL', async (req, res) => { const request = { header: { - device_id: req.body.deviceId || 'huayan_arm' + device_id: req.deviceId }, velocity: req.body.velocity, acceleration: req.body.acceleration, duration: req.body.duration }; - const grpcClient = createARMGrpcClient(req.body.ip) + const grpcClient = createARMGrpcClient(req.robotAddress) const resp = await new Promise((resolve, reject) => { grpcClient.speedL(request, (err, res) => { @@ -449,10 +467,10 @@ app.post('/api/arm/speedL', async (req, res) => { app.post('/api/arm/stopMotion', async (req, res) => { const request = { // header: { - device_id: req.body.deviceId || 'huayan_arm', + device_id: req.deviceId, // }, }; - const grpcClient = createARMGrpcClient(req.body.ip) + const grpcClient = createARMGrpcClient(req.robotAddress) const resp = await new Promise((resolve, reject) => { grpcClient.stopMotion(request, (err, res) => { @@ -479,9 +497,9 @@ app.post('/api/arm/stopMotion', async (req, res) => { app.post('/api/arm/torqueOn', async (req, res) => { const request = { - device_id: req.body.deviceId || 'huayan_arm', + device_id: req.deviceId, }; - const grpcClient = createARMGrpcClient(req.body.ip) + const grpcClient = createARMGrpcClient(req.robotAddress) const resp = await new Promise((resolve, reject) => { grpcClient.torqueOn(request, (err, res) => { @@ -551,7 +569,10 @@ if (httpsEnabled) { httpServer = http.createServer(app); } -attachAudioWebSocket(httpServer) +attachAudioWebSocket(httpServer, { + resolveRobotAddress: getRobotAddress, + resolveRobotDeviceId: getRobotDeviceId +}) startQuicServer({ onEvent: consumeQuicEvent }) httpServer.listen(PORT, '0.0.0.0', () => { diff --git a/server/robotRegistry.js b/server/robotRegistry.js index b6a5462..09c9bdd 100644 --- a/server/robotRegistry.js +++ b/server/robotRegistry.js @@ -82,3 +82,12 @@ export function listRobots() { return [...robots.values()].sort((a, b) => a.robotId.localeCompare(b.robotId)) } +export function getRobotAddress(robotId) { + if (!robotId) return '' + return robots.get(robotId)?.ip || '' +} + +export function getRobotDeviceId(robotId, kind) { + const devices = robots.get(robotId)?.devices?.[kind] || [] + return (devices.find(device => device.enabled) || devices[0])?.deviceId || '' +} diff --git a/src/api/agv.js b/src/api/agv.js index 29de072..62038e5 100644 --- a/src/api/agv.js +++ b/src/api/agv.js @@ -3,7 +3,7 @@ import request from '@/utils/request' /** * 机器人前往目标站点 * @param {Object} params - * @param {string} [params.deviceId] - 设备ID,默认 agv_src1100 + * robotId 由统一请求层从 Pinia 自动附加,设备 ID 由后端注册表解析。 */ export function getRuntimeState(params) { return request('/api/agv/getRuntimeState', params, 'POST') @@ -23,4 +23,4 @@ export function moveRobot(params) { export function stopRobot(params) { return request('/api/agv/stopRobot', params, 'POST') -} \ No newline at end of file +} diff --git a/src/stores/robot.js b/src/stores/robot.js index 106eac3..de73d26 100644 --- a/src/stores/robot.js +++ b/src/stores/robot.js @@ -3,13 +3,8 @@ import { defineStore } from 'pinia' // 唯一id:robot export const useRobotStore = defineStore('robot', { state: () => ({ - ip: '192.168.0.28:50052', + robotId: '', robotType: 'inspection', - agvDeviceId:'src1100', - almDeviceId: 'huayan_arm', - spkDeviceId: 'spk1', - micDeviceId: 'mic1', - cameraDeviceId: 'real_cam1', position: { x: 0, y: 0, theta: 0 }, battery: { percentage: 0.99, @@ -30,27 +25,12 @@ export const useRobotStore = defineStore('robot', { } }, actions: { - setIp(str) { - this.ip = str + setRobotId(str) { + this.robotId = str }, setRobotType(str) { this.robotType = str }, - setAgvDeviceId(str) { - this.agvDeviceId = str - }, - setAlmDeviceId(str) { - this.almDeviceId = str - }, - setSpkDeviceId(str) { - this.spkDeviceId = str - }, - setMicDeviceId(str) { - this.micDeviceId = str - }, - setCameraDeviceId(str) { - this.cameraDeviceId = str - }, // 更新机器人位置 setPosition(data = {}) { this.position = data diff --git a/src/utils/request.js b/src/utils/request.js index 10f0333..fc16fbf 100644 --- a/src/utils/request.js +++ b/src/utils/request.js @@ -1,4 +1,5 @@ import { ElMessage } from 'element-plus' +import { useRobotStore } from '@/stores/robot' const request = async (url, params = {}, method = 'GET', headers = { 'Content-Type': 'application/json' }) => { const controller = new AbortController() @@ -9,13 +10,15 @@ const request = async (url, params = {}, method = 'GET', headers = { 'Content-Ty }, 5000) try { + const robotId = useRobotStore().robotId + const requestParams = robotId && !params.robotId ? { ...params, robotId } : params const options = { method, headers, signal } if (!['GET', 'HEAD'].includes(method.toUpperCase())) { - options.body = JSON.stringify(params) + options.body = JSON.stringify(requestParams) } const res = await fetch(url, options) diff --git a/src/views/Address.vue b/src/views/Address.vue index cdbc4b0..45757d0 100644 --- a/src/views/Address.vue +++ b/src/views/Address.vue @@ -16,7 +16,7 @@ @@ -28,30 +28,12 @@ - - - - 进入上位机控制页面 @@ -74,7 +56,7 @@ const robotStore = useRobotStore() const loading = ref(false) const robotListLoading = ref(false) const robots = ref([]) -const selectedRobotId = ref('') +const selectedRobotId = ref(robotStore.robotId || '') const selectedRobot = computed(() => robots.value.find(robot => robot.robotId === selectedRobotId.value)) const selectedDevices = computed(() => Object.entries(selectedRobot.value?.devices || {}).flatMap( ([kind, devices]) => devices.map(device => ({ kind, ...device })) @@ -82,51 +64,19 @@ const selectedDevices = computed(() => Object.entries(selectedRobot.value?.devic let refreshTimer const ruleFormRef = ref() const ruleForm = ref({ - ip: '', robotType: 'inspection', - // agvDeviceId:'src1100', - // almDeviceId: 'huayan_arm', - // spkDeviceId: 'spk1', - // micDeviceId: 'mic1', - // cameraDeviceId: 'real_cam1' }) const rules = ref({ - ip: [ - { required: true, message: '请输入机器人IP和端口', trigger: 'blur' } - ], robotType: [ { required: true, message: '请选择机器人类型', trigger: 'blur' } ], - // agvDeviceId: [ - // { required: true, message: '请输入底盘设备ID', trigger: 'blur' } - // ], - // almDeviceId: [ - // { required: true, message: '请输入机械臂设备ID', trigger: 'blur' } - // ], - // spkDeviceId: [ - // { required: true, message: '请输入扬声器设备ID', trigger: 'blur' } - // ], - // micDeviceId: [ - // { required: true, message: '请输入麦克风设备ID', trigger: 'blur' } - // ], - // cameraDeviceId: [ - // { required: true, message: '请输入相机设备ID', trigger: 'blur' } - // ], }) -const firstDeviceId = (robot, kind, fallback) => robot?.devices?.[kind]?.[0]?.deviceId || fallback - const selectRobot = () => { const robot = selectedRobot.value if (!robot) return - ruleForm.value.ip = robot.ip - robotStore.setIp(robot.ip) - robotStore.setAgvDeviceId(firstDeviceId(robot, 'agv', 'src1100')) - robotStore.setAlmDeviceId(firstDeviceId(robot, 'arm', 'huayan_arm')) - robotStore.setSpkDeviceId(firstDeviceId(robot, 'speaker', 'spk1')) - robotStore.setMicDeviceId(firstDeviceId(robot, 'microphone', 'mic1')) - robotStore.setCameraDeviceId(firstDeviceId(robot, 'camera', 'real_cam1')) + robotStore.setRobotId(robot.robotId) } const loadRobots = async () => { @@ -147,32 +97,19 @@ onMounted(() => { onUnmounted(() => clearInterval(refreshTimer)) const testConnect = async () => { + if (!selectedRobot.value) { + ElMessage.warning('请先选择已连接机器人') + return + } const valid = await ruleFormRef.value.validate(); if (valid) { loading.value = true - robotStore.setIp(ruleForm.value.ip) + robotStore.setRobotId(selectedRobot.value.robotId) robotStore.setRobotType(ruleForm.value.robotType) - if (selectedRobot.value) { - selectRobot() - } else if (ruleForm.value.robotType === 'inspection') { - robotStore.setAgvDeviceId('src1100') - robotStore.setAlmDeviceId('huayan_arm') - robotStore.setSpkDeviceId('spk1') - robotStore.setMicDeviceId('mic1') - robotStore.setCameraDeviceId('real_cam1') - } else if (ruleForm.value.robotType === 'explanation') { - robotStore.setAgvDeviceId('src1100') - robotStore.setAlmDeviceId('huayan_arm') - robotStore.setSpkDeviceId('spk1') - robotStore.setMicDeviceId('mic1') - robotStore.setCameraDeviceId('real_cam1') - } + selectRobot() try { - const result = await getRuntimeState({ - ip: robotStore.ip, - deviceId: robotStore.agvDeviceId - }) + const result = await getRuntimeState() loading.value = false if (result.code === 200) { robotStore.setPosition(result.data.pose) diff --git a/src/views/arm/MechanicalArm.vue b/src/views/arm/MechanicalArm.vue index 83fecb8..c48d3d4 100644 --- a/src/views/arm/MechanicalArm.vue +++ b/src/views/arm/MechanicalArm.vue @@ -303,7 +303,6 @@ import { Remove, CirclePlus } from '@element-plus/icons-vue' import { useRobotStore } from '@/stores/robot' const robotStore = useRobotStore() -const deviceId = robotStore.almDeviceId const urdfViewerRef = ref() @@ -343,17 +342,11 @@ const tcp = ref({ }) const torqueOn = async () => { - await torqueOnApi({ - ip: robotStore.ip, - deviceId: deviceId - }) + await torqueOnApi() } const getPose = async () => { - const res = await getPoseApi({ - ip: robotStore.ip, - deviceId: deviceId - }) + const res = await getPoseApi() if (res.code === 200) { const { x, y, z, rx, ry, rz } = res.data tcp.value = { x, y, z, rx, ry, rz } @@ -375,10 +368,7 @@ const jointNamsMap = { */ const getJointState = async (robot) => { try { - const res = await getJointStateApi({ - deviceId: deviceId, - ip: robotStore.ip, - }) + const res = await getJointStateApi() if (res.code === 200) { setTimeout(() => { const names = res.data.name @@ -404,8 +394,6 @@ const updateJoint = async (jointName, _dir) => { const index = jointControls.value.findIndex(joint => joint.name === jointName) velocities[index] = jointSpeed.value * (_dir === '+' ? 1 : -1) const res = await speedJApi({ - deviceId: deviceId, - ip: robotStore.ip, acceleration: jointSpeed.value * (_dir === '+' ? 1 : -1) + 0.1, duration: 60, velocities: velocities @@ -475,8 +463,6 @@ const move = async (axis, direction, type) => { } speedLApi({ - deviceId: deviceId, - ip: robotStore.ip, duration: 60, acceleration: type === 'posture' ? postureSpeed.value + 0.1 : endSpeed.value + 0.1, velocity: { @@ -489,10 +475,7 @@ const move = async (axis, direction, type) => { * 停止机械臂移动 */ const stop = async () => { - await stopMotionApi({ - deviceId: deviceId, - ip: robotStore.ip - }) + await stopMotionApi() } const activePointerIds = new Set() diff --git a/src/views/camera/VoiceConversation.vue b/src/views/camera/VoiceConversation.vue index cdad6d3..693a383 100644 --- a/src/views/camera/VoiceConversation.vue +++ b/src/views/camera/VoiceConversation.vue @@ -151,14 +151,11 @@ const statusMeta = computed(() => { }) function microphoneVolumeRequestParams() { - return { - ip: robotStore.ip, - deviceId: robotStore.micDeviceId - } + return {} } async function loadMicrophoneVolume({ silent = false } = {}) { - if (!robotStore.ip || !robotStore.micDeviceId) return + if (!robotStore.robotId) return isMicrophoneVolumeLoading.value = true try { const response = await getMicrophoneVolume(microphoneVolumeRequestParams()) @@ -194,14 +191,11 @@ async function updateMicrophoneVolume(volume) { } function speakerVolumeRequestParams() { - return { - ip: robotStore.ip, - deviceId: robotStore.spkDeviceId - } + return {} } async function loadSpeakerVolume({ silent = false } = {}) { - if (!robotStore.ip || !robotStore.spkDeviceId) return + if (!robotStore.robotId) return isSpeakerVolumeLoading.value = true try { const response = await getSpeakerVolume(speakerVolumeRequestParams()) @@ -391,9 +385,7 @@ function connectAudioServer(sessionToken) { const timeout = window.setTimeout(() => reject(new Error('连接音频服务超时')), JOIN_TIMEOUT) socket.onopen = () => sendControlMessage({ type: 'init', - microphoneId: robotStore.micDeviceId, - speakerId: robotStore.spkDeviceId, - robotAddress: robotStore.ip + robotId: robotStore.robotId }) socket.onmessage = (event) => { if (typeof event.data !== 'string') { @@ -455,7 +447,7 @@ async function startCall() { errorMessage.value = '' const sessionToken = ++callSessionId try { - if (!robotStore.ip || !robotStore.micDeviceId || !robotStore.spkDeviceId) { + if (!robotStore.robotId) { throw new Error('请先配置机器人地址、麦克风 ID 和扬声器 ID') } // 必须在按钮点击的用户手势中创建,以符合浏览器自动播放策略。 diff --git a/src/views/camera/index.vue b/src/views/camera/index.vue index 8975598..7d4a90d 100644 --- a/src/views/camera/index.vue +++ b/src/views/camera/index.vue @@ -14,20 +14,10 @@ import VoiceConversation from './VoiceConversation.vue'; import { computed } from 'vue'; import { useRobotStore } from '@/stores/robot'; -const VIDEO_SERVER_HOST = import.meta.env.VITE_VIDEO_SERVER_HOST || 'http://localhost:8080/' -const TerminalId = import.meta.env.VITE_TERMINAL_ID || 'c2f8a06826baf03843925c1a2a13bcfd' - const robotStore = useRobotStore() -const videoUrl = `${VIDEO_SERVER_HOST}/api/edge/camera/rgb-stream/public?terminalId=${TerminalId}&deviceId=${robotStore.cameraDeviceId}` - -// const videoUrl = computed(() => { -// if (!robotStore.ip || !robotStore.cameraDeviceId) return '' -// const params = new URLSearchParams({ -// ip: robotStore.ip, -// deviceId: robotStore.cameraDeviceId -// }) -// return `/api/camera/video?${params}` -// }) +const videoUrl = computed(() => robotStore.robotId + ? `/api/camera/video?robotId=${encodeURIComponent(robotStore.robotId)}` + : '')