From de18827af6d0e85b564f9e39ea32f0c66e180554 Mon Sep 17 00:00:00 2001 From: JayJiaJun Date: Wed, 26 Mar 2025 17:10:39 +0800 Subject: [PATCH] 3.26 --- public/web/content_pattern1.html | 15 +- public/web/screen_main.html | 19 +- public/web/screen_par.html | 9 +- public/web/screen_program.html | 12 +- public/web/screen_virtual.html | 6 +- src/assets/js/GamepadController.js | 82 +++++-- src/assets/js/audio-transmitter.js | 333 ++++++++++++++++------------- src/views/CarControl.vue | 230 ++++++++++++++++---- 8 files changed, 472 insertions(+), 234 deletions(-) diff --git a/public/web/content_pattern1.html b/public/web/content_pattern1.html index 31b020b..149cce5 100644 --- a/public/web/content_pattern1.html +++ b/public/web/content_pattern1.html @@ -1500,13 +1500,14 @@ screen_start = new Object() screen_start.get_screen = 1; var str = JSON.stringify(screen_start); + let send_string = "{\"JSON_id\":" + json_id + ",\"board_id\":102"+",\"led_protocol\":" + str + "}"; console.log(str); result_prompt_display(1, "获取屏幕参数中..."); if (MQTT_MODE == 0) { var xhr = new XMLHttpRequest(); xhr.open('post', baseHost + '/communication', true); xhr.setRequestHeader('content-type', 'application/json'); - xhr.send(str); + xhr.send(send_string); xhr.onreadystatechange = function () { if (xhr.readyState == 4) { if (xhr.status == 200) { @@ -1527,13 +1528,14 @@ screen_start = new Object() screen_start.get_sensor = 1; var str = JSON.stringify(screen_start); + let send_string = "{\"JSON_id\":" + json_id + ",\"board_id\":102"+",\"led_protocol\":" + str + "}"; console.log(str); result_prompt_display(1, "获取传感器中..."); if (MQTT_MODE == 0) { var xhr = new XMLHttpRequest(); xhr.open('post', baseHost + '/communication', true); xhr.setRequestHeader('content-type', 'application/json'); - xhr.send(str); + xhr.send(send_string); xhr.onreadystatechange = function () { if (xhr.readyState == 4) { if (xhr.status == 200) { @@ -1554,13 +1556,14 @@ screen_start = new Object() screen_start.get_program = parseInt(pro_num); var str = JSON.stringify(screen_start); + let send_string = "{\"JSON_id\":" + json_id + ",\"board_id\":102"+",\"led_protocol\":" + str + "}"; console.log(str); result_prompt_display(1, "获取屏幕参数中..."); if (MQTT_MODE == 0) { var xhr = new XMLHttpRequest(); xhr.open('post', baseHost + '/communication', true); xhr.setRequestHeader('content-type', 'application/json'); - xhr.send(str); + xhr.send(send_string); xhr.onreadystatechange = function () { if (xhr.readyState == 4) { if (xhr.status == 200) { @@ -1589,11 +1592,12 @@ manage.pro_manage.type = type; manage.pro_manage.data = data; let string = JSON.stringify(manage); + let send_string = "{\"JSON_id\":" + json_id + ",\"board_id\":102"+",\"led_protocol\":" + str + "}"; if (MQTT_MODE == 0) { var xhr = new XMLHttpRequest(); xhr.open('post', baseHost + '/communication', true); xhr.setRequestHeader('content-type', 'application/json'); - xhr.send(string); + xhr.send(send_string); xhr.onreadystatechange = function () { if (xhr.readyState == 4) { if (xhr.status == 200) { @@ -1705,13 +1709,14 @@ } } let string = JSON.stringify(json_cfg); + let send_string = "{\"JSON_id\":" + json_id + ",\"board_id\":102"+",\"led_protocol\":" + string + "}"; console.log(string); result_prompt_display(1, "设置中..."); if (MQTT_MODE == 0) { var xhr = new XMLHttpRequest(); xhr.open('post', baseHost + '/communication', true); xhr.setRequestHeader('content-type', 'application/json'); - xhr.send(string); + xhr.send(send_string); xhr.onreadystatechange = function () { if (xhr.readyState == 4) { if (xhr.status == 200) { diff --git a/public/web/screen_main.html b/public/web/screen_main.html index 3c12411..e0e5719 100644 --- a/public/web/screen_main.html +++ b/public/web/screen_main.html @@ -212,6 +212,8 @@ screen_start.get_cmd = new Object() screen_start.get_cmd.type = _type var str = JSON.stringify(screen_start); + let send_string = "{\"JSON_id\":" + json_id + ",\"board_id\":102"+",\"led_protocol\":" + str + "}"; + // 设置超时逻辑 var timeoutId = setTimeout(function () { result_prompt_display(2, "数据获取失败"); @@ -222,7 +224,7 @@ var xhr = new XMLHttpRequest(); xhr.open('POST', baseHost + '/communication', true); xhr.setRequestHeader('content-type', 'application/json'); - xhr.send(str); + xhr.send(send_string); xhr.onreadystatechange = function () { if (xhr.readyState == 4) { if (xhr.status == 200) { @@ -292,13 +294,14 @@ screen_start.cmd.data2 = parseInt($('#power_on2').find('option:selected').val()); screen_start.cmd.data3 = 0; var str = JSON.stringify(screen_start); + let send_string = "{\"JSON_id\":" + json_id + ",\"board_id\":102"+",\"led_protocol\":" + str + "}"; console.log(str); result_prompt_display(1, "发送中..."); if (MQTT_MODE == 0) { var xhr = new XMLHttpRequest(); xhr.open('POST', baseHost + '/communication', true); xhr.setRequestHeader('content-type', 'application/json'); - xhr.send(str); + xhr.send(send_string); xhr.onreadystatechange = function () { if (xhr.readyState == 4) { if (xhr.status == 200) { @@ -332,13 +335,14 @@ screen_start.cmd.data2 = 0; screen_start.cmd.data3 = 0; var str = JSON.stringify(screen_start); + let send_string = "{\"JSON_id\":" + json_id + ",\"board_id\":102"+",\"led_protocol\":" + str + "}"; console.log(str); result_prompt_display(1, "发送中..."); if (MQTT_MODE == 0) { var xhr = new XMLHttpRequest(); xhr.open('POST', baseHost + '/communication', true); xhr.setRequestHeader('content-type', 'application/json'); - xhr.send(str); + xhr.send(send_string); xhr.onreadystatechange = function () { if (xhr.readyState == 4) { if (xhr.status == 200) { @@ -395,13 +399,14 @@ screen_start.cmd.data2 = 0; screen_start.cmd.data3 = 0; var str = JSON.stringify(screen_start); + let send_string = "{\"JSON_id\":" + json_id + ",\"board_id\":102"+",\"led_protocol\":" + str + "}"; console.log(str); result_prompt_display(1, "发送中..."); if (MQTT_MODE == 0) { var xhr = new XMLHttpRequest(); xhr.open('POST', baseHost + '/communication', true); xhr.setRequestHeader('content-type', 'application/json'); - xhr.send(str); + xhr.send(send_string); xhr.onreadystatechange = function () { if (xhr.readyState == 4) { if (xhr.status == 200) { @@ -429,13 +434,14 @@ screen_close.cmd.data2 = 0; screen_close.cmd.data3 = 0; var str = JSON.stringify(screen_close); + let send_string = "{\"JSON_id\":" + json_id + ",\"board_id\":102"+",\"led_protocol\":" + str + "}"; console.log(str); result_prompt_display(1, "发送中..."); if (MQTT_MODE == 0) { var xhr = new XMLHttpRequest(); xhr.open('POST', baseHost + '/communication', true); xhr.setRequestHeader('content-type', 'application/json'); - xhr.send(str); + xhr.send(send_string); xhr.onreadystatechange = function () { if (xhr.readyState == 4) { if (xhr.status == 200) { @@ -467,13 +473,14 @@ screen_clear.cmd.data2 = 0; screen_clear.cmd.data3 = 0; var str = JSON.stringify(screen_clear); + let send_string = "{\"JSON_id\":" + json_id + ",\"board_id\":102"+",\"led_protocol\":" + str + "}"; console.log(str); result_prompt_display(1, "发送中..."); if (MQTT_MODE == 0) { var xhr = new XMLHttpRequest(); xhr.open('POST', baseHost + '/communication', true); xhr.setRequestHeader('content-type', 'application/json'); - xhr.send(str); + xhr.send(send_string); xhr.onreadystatechange = function () { if (xhr.readyState == 4) { if (xhr.status == 200) { diff --git a/public/web/screen_par.html b/public/web/screen_par.html index d44b65b..52df65c 100644 --- a/public/web/screen_par.html +++ b/public/web/screen_par.html @@ -150,13 +150,14 @@ screen_start = new Object() screen_start.get_screen = 1; var str = JSON.stringify(screen_start); + let send_string = "{\"JSON_id\":" + json_id + ",\"board_id\":102"+",\"led_protocol\":" + str + "}"; console.log(str); result_prompt_display(1, "获取中..."); if (MQTT_MODE == 0) { var xhr = new XMLHttpRequest(); xhr.open('POST', baseHost + '/communication', true); xhr.setRequestHeader('content-type', 'application/json'); - xhr.send(str); + xhr.send(send_string); xhr.onreadystatechange = function () { if (xhr.readyState == 4) { if (xhr.status == 200) { @@ -224,11 +225,12 @@ manage.pro_manage.type = 2; manage.pro_manage.data = -1; var str = JSON.stringify(manage); + let send_string = "{\"JSON_id\":" + json_id + ",\"board_id\":102"+",\"led_protocol\":" + str + "}"; if (MQTT_MODE == 0) { var xhr = new XMLHttpRequest(); xhr.open('post', baseHost + '/communication', true); xhr.setRequestHeader('content-type', 'application/json'); - xhr.send(str); + xhr.send(send_string); xhr.onreadystatechange = function () { if (xhr.readyState == 4) { if (xhr.status == 200) { @@ -271,13 +273,14 @@ screen.screen_p.data = parseInt($('#data_polarity').val()) screen.screen_p.screen_angle = screen_angle; let str = JSON.stringify(screen); + let send_string = "{\"JSON_id\":" + json_id + ",\"board_id\":102"+",\"led_protocol\":" + str + "}"; console.log(str); result_prompt_display(1, "设置中..."); if (MQTT_MODE == 0) { var xhr = new XMLHttpRequest(); xhr.open('post', baseHost + '/communication', true); xhr.setRequestHeader('content-type', 'application/json'); - xhr.send(str); + xhr.send(send_string); xhr.onreadystatechange = function () { if (xhr.readyState == 4) { if (xhr.status == 200) { diff --git a/public/web/screen_program.html b/public/web/screen_program.html index 814b84b..ad55da9 100644 --- a/public/web/screen_program.html +++ b/public/web/screen_program.html @@ -257,11 +257,12 @@ manage.pro_manage.remarks = new Array(); manage.pro_manage.remarks[0] = cont; let string = JSON.stringify(manage); + let send_string = "{\"JSON_id\":" + json_id + ",\"board_id\":102"+",\"led_protocol\":" + string + "}"; if (MQTT_MODE == 0) { var xhr = new XMLHttpRequest(); xhr.open('post', baseHost + '/communication', true); xhr.setRequestHeader('content-type', 'application/json'); - xhr.send(string); + xhr.send(send_string); xhr.onreadystatechange = function () { if (xhr.readyState == 4 || xhr.status == 200) { program_list_reply_parse(xhr.responseText); @@ -295,11 +296,12 @@ manage = new Object(); manage.get_pro_manage = 1; let string = JSON.stringify(manage); + let send_string = "{\"JSON_id\":" + json_id + ",\"board_id\":102"+",\"led_protocol\":" + string + "}"; if (MQTT_MODE == 0) { var xhr = new XMLHttpRequest(); xhr.open('post', baseHost + '/communication', true); xhr.setRequestHeader('content-type', 'application/json'); - xhr.send(string); + xhr.send(send_string); xhr.onreadystatechange = function () { if (xhr.readyState == 4 && xhr.status == 200) { program_list_reply_parse(xhr.responseText); @@ -329,11 +331,12 @@ manage.pro_manage.data = data; result_prompt_display(1, "删除中..."); let string = JSON.stringify(manage); + let send_string = "{\"JSON_id\":" + json_id + ",\"board_id\":102"+",\"led_protocol\":" + string + "}"; if (MQTT_MODE == 0) { var xhr = new XMLHttpRequest() xhr.open('post', baseHost + '/communication', true) xhr.setRequestHeader('content-type', 'application/json') - xhr.send(string); + xhr.send(send_string); xhr.onreadystatechange = function () { if ((xhr.readyState == 4) && (xhr.status == 200)) { program_list_reply_parse(xhr.responseText); @@ -375,12 +378,13 @@ manage.pro_manage.type = type; manage.pro_manage.data = data; let string = JSON.stringify(manage); + let send_string = "{\"JSON_id\":" + json_id + ",\"board_id\":102"+",\"led_protocol\":" + string + "}"; result_prompt_display(1, "发送中..."); if (MQTT_MODE == 0) { var xhr = new XMLHttpRequest() xhr.open('post', baseHost + '/communication', true) xhr.setRequestHeader('content-type', 'application/json') - xhr.send(string) + xhr.send(send_string) xhr.onreadystatechange = function () { if (xhr.readyState == 4) { if (xhr.status == 200) { diff --git a/public/web/screen_virtual.html b/public/web/screen_virtual.html index 570a7f3..e8dcd7d 100644 --- a/public/web/screen_virtual.html +++ b/public/web/screen_virtual.html @@ -96,11 +96,12 @@ manage = new Object(); manage.get_pro_manage = 1; let string = JSON.stringify(manage); + let send_string = "{\"JSON_id\":" + json_id + ",\"board_id\":102"+",\"led_protocol\":" + string + "}"; if (MQTT_MODE == 0) { var xhr = new XMLHttpRequest(); xhr.open('post', baseHost + '/communication', true); xhr.setRequestHeader('content-type', 'application/json'); - xhr.send(string); + xhr.send(send_string); xhr.onreadystatechange = function () { if (xhr.readyState == 4 && xhr.status == 200) { screen_virtual_reply_parse(xhr.responseText); @@ -126,12 +127,13 @@ manage.pro_manage.type = 6; manage.pro_manage.data = parseInt(data); let string = JSON.stringify(manage); + let send_string = "{\"JSON_id\":" + json_id + ",\"board_id\":102"+",\"led_protocol\":" + string + "}"; result_prompt_display(1, "打开中"); if (MQTT_MODE == 0) { var xhr = new XMLHttpRequest() xhr.open('post', baseHost + '/communication', true) xhr.setRequestHeader('content-type', 'application/json') - xhr.send(string) + xhr.send(send_string) xhr.onreadystatechange = function () { if (xhr.readyState == 4) { if (xhr.status == 200) { diff --git a/src/assets/js/GamepadController.js b/src/assets/js/GamepadController.js index ffcdaa8..83af212 100644 --- a/src/assets/js/GamepadController.js +++ b/src/assets/js/GamepadController.js @@ -12,17 +12,26 @@ class GamepadController { this.gamepadIndex = null; this.gamepad = null; this.interval = null; - this.angle = 0; // 这里将存储 0-360 的角度值 + this.angle = 0; this.speed = 0; this.directionAxis9 = 0; this.rightJoystickPressed = false; this.isActive = false; - this.updateLoop = setInterval(() => this.update(), this.options.updateInterval); + // 立即开始更新循环,不等待连接事件 + this.startUpdateLoop(); } update() { - const gamepad = navigator.getGamepads()[0]; + // 获取所有手柄 + const gamepads = navigator.getGamepads(); + if (!gamepads) { + this.isActive = false; + return; + } + + // 找到第一个连接的手柄 + const gamepad = Array.from(gamepads).find(pad => pad && pad.connected); if (!gamepad) { this.isActive = false; return; @@ -45,25 +54,26 @@ class GamepadController { this.updateDirection(gamepad.axes); } - // 获取右摇杆的值(用于云台控制) - const rightY = gamepad.axes[3]; - if (Math.abs(rightY) > this.options.deadZone) { - this.directionAxis9 = rightY > 0 ? 1 : 2; - } else { - this.directionAxis9 = 0; - } + // 更新 platform 值 + this.updateDirectionAxis9(gamepad.axes); - // 更新右摇杆按钮状态 - this.rightJoystickPressed = gamepad.buttons[10].pressed; + // 更新右摇杆按钮状态 - 尝试多个可能的按钮索引 + this.rightJoystickPressed = + (gamepad.buttons[10] && gamepad.buttons[10].pressed) || + (gamepad.buttons[9] && gamepad.buttons[9].pressed) || + (gamepad.buttons[11] && gamepad.buttons[11].pressed); if (this.options.debug) { console.log('GamepadController 状态:', { + id: gamepad.id, + index: gamepad.index, x: x.toFixed(2), y: y.toFixed(2), angle: this.angle, speed: this.speed.toFixed(2), directionAxis9: this.directionAxis9, - rightJoystickPressed: this.rightJoystickPressed + rightJoystickPressed: this.rightJoystickPressed, + buttons: Array.from(gamepad.buttons).map((btn, idx) => btn.pressed ? idx : null).filter(idx => idx !== null) }); } } @@ -90,22 +100,52 @@ class GamepadController { // 计算速度(0-100) const distance = Math.sqrt(axis0 * axis0 + axis1 * axis1); this.speed = Math.min(Math.round(distance * 100), 100); + } + + updateDirectionAxis9(axes) { + // 添加调试输出,查看所有轴的值 + if (this.options.debug) { + // console.log('所有轴的值:', axes.map((value, index) => `轴${index}: ${value}`)); + } + + // 检查第9轴是否存在 + if (axes[9] === undefined) { + // 可能是第3轴或第4轴 + const axis3 = axes[3]; + const axis4 = axes[4]; + + if (axis3 !== undefined && Math.abs(axis3) > this.options.deadZone) { + this.directionAxis9 = axis3 > 0 ? 2 : 1; + } else if (axis4 !== undefined && Math.abs(axis4) > this.options.deadZone) { + this.directionAxis9 = axis4 > 0 ? 2 : 1; + } else { + this.directionAxis9 = 0; + } + } else { + const axis9 = axes[9]; + const roundedAxis9 = Math.round(axis9 * 100) / 100; + if (roundedAxis9 <= -0.9) { + this.directionAxis9 = 1; // 向上 + } else if (roundedAxis9 >= 0.0 && roundedAxis9 <= 0.2) { + this.directionAxis9 = 2; // 向下 + } else { + this.directionAxis9 = 0; // 中间位置 + } + } if (this.options.debug) { - console.log('方向计算:', { - x: axis0, - y: axis1, - angle: this.angle, - speed: this.speed - }); + // console.log('当前 directionAxis9 值:', this.directionAxis9); } } + startUpdateLoop() { + this.updateLoop = setInterval(() => this.update(), this.options.updateInterval); + } + destroy() { if (this.updateLoop) { clearInterval(this.updateLoop); } } -} +}export default GamepadController; -export default GamepadController; \ No newline at end of file diff --git a/src/assets/js/audio-transmitter.js b/src/assets/js/audio-transmitter.js index c4939f6..dbf3d2a 100644 --- a/src/assets/js/audio-transmitter.js +++ b/src/assets/js/audio-transmitter.js @@ -1,163 +1,204 @@ // audio-transmitter.js - + export default { - data() { - return { - ws: null, // WebSocket 连接 - mediaRecorder: null, // MediaRecorder 实例 - audioChunks: [], // 音频数据 - status: '点击按钮开始传输音频', - isRecording: false, // 用于判断是否在录音 - audioConfig: { - sampleRate: 44100, // 固定为 44100Hz - bitsPerSample: 16, // 固定为 16 位深度 - channels: 2 // 固定为双声道 - }, - stream: null, - audioContext: null, - worklet: null, - lastSendTime: 0, - sendInterval: 200, // 数据传输间隔(毫秒) - audioWs: null, // 音频 WebSocket 连接 - }; - }, - mounted() { - // 初始化 WebSocket 连接 - this.ws = new WebSocket('ws://192.168.4.103/ws'); // 替换为 ESP32 IP 地址 - - this.ws.onopen = () => { - console.log('WebSocket 连接已打开'); - }; - this.ws.onmessage = (event) => { - console.log(' 接收到来自 ESP32 的消息:', event.data); - }; - this.ws.onclose = () => { - console.log('WebSocket 连接已关闭'); - }; - }, - methods: { - toggleRecording() { - if (this.isRecording) { - this.stopRecording(); - } else { - this.startRecording(); - } + data() { + return { + ws: null, // WebSocket 连接 + mediaRecorder: null, // MediaRecorder 实例 + audioChunks: [], // 音频数据 + status: '点击按钮开始传输音频', + isRecording: false, // 用于判断是否在录音 + audioConfig: { + sampleRate: 44100, // 固定为 44100Hz + bitsPerSample: 16, // 固定为 16 位深度 + channels: 2 // 固定为双声道 }, - - async startRecording() { - console.log('音频传输器: 开始录音'); - this.status = '开始录音并传输音频...'; - this.isRecording = true; - - const constraints = { - audio: { - sampleRate: this.audioConfig.sampleRate, - channelCount: this.audioConfig.channels, - sampleSize: this.audioConfig.bitsPerSample + stream: null, + audioContext: null, + worklet: null, + lastSendTime: 0, + sendInterval: 200, // 数据传输间隔(毫秒) + audioWs: null, // 音频 WebSocket 连接 + }; + }, + beforeDestroy() { + // 组件销毁前清理资源 + this.cleanup(); + + // 关闭主 WebSocket 连接 + if (this.ws) { + console.log('关闭主 WebSocket 连接'); + this.ws.close(); + this.ws = null; + } + }, + methods: { + toggleRecording() { + if (this.isRecording) { + this.stopRecording(); + } else { + this.startRecording(); + } + }, + + async startRecording() { + if (this.isRecording) return; + + console.log('音频传输器: 开始录音'); + this.status = '开始录音并传输音频...'; + this.isRecording = true; + + const constraints = { + audio: { + sampleRate: this.audioConfig.sampleRate, + channelCount: this.audioConfig.channels, + sampleSize: this.audioConfig.bitsPerSample + } + }; + + console.log('使用的音频配置:', constraints); + + try { + // 直接获取麦克风权限,不再重新建立 WebSocket 连接 + const stream = await navigator.mediaDevices.getUserMedia(constraints); + console.log('成功获取麦克风权限'); + + const audioContext = new AudioContext({ + sampleRate: this.audioConfig.sampleRate + }); + + await audioContext.audioWorklet.addModule('audio-processor.js'); + + const source = audioContext.createMediaStreamSource(stream); + const worklet = new AudioWorkletNode(audioContext, 'audio-processor'); + + worklet.port.onmessage = (event) => { + if (this.audioWs && this.audioWs.readyState === WebSocket.OPEN) { + const currentTime = Date.now(); + if (!this.lastSendTime || currentTime - this.lastSendTime > this.sendInterval) { + const pcmData = new Int16Array(event.data); + console.log('发送的音频数据:', Array.from(pcmData.slice(0, 20))); + this.audioWs.send(event.data); + this.lastSendTime = currentTime; + } } }; - - console.log('使用的音频配置:', constraints); - - try { - // 初始化音频 WebSocket 连接 - this.audioWs = new WebSocket('ws://192.168.1.60:81'); - - // 等待 WebSocket 连接成功 - await new Promise((resolve, reject) => { - this.audioWs.onopen = () => { - console.log('音频 WebSocket 已连接'); - resolve(); - }; - this.audioWs.onerror = (error) => { - console.error('音频 WebSocket 错误:', error); - reject(error); - }; - }); - - // WebSocket 连接成功后,开始获取麦克风权限 - const stream = await navigator.mediaDevices.getUserMedia(constraints); - console.log('成功获取麦克风权限'); - - // 创建 AudioContext - const audioContext = new AudioContext({ - sampleRate: this.audioConfig.sampleRate - }); - - // 加载 AudioWorklet - await audioContext.audioWorklet.addModule('audio-processor.js'); - - const source = audioContext.createMediaStreamSource(stream); - const worklet = new AudioWorkletNode(audioContext, 'audio-processor'); - - // 监听来自 worklet 的消息 - worklet.port.onmessage = (event) => { - if (this.audioWs && this.audioWs.readyState === WebSocket.OPEN) { - const currentTime = Date.now(); - if (!this.lastSendTime || currentTime - this.lastSendTime > this.sendInterval) { - const pcmData = new Int16Array(event.data); - console.log('发送的音频数据:', Array.from(pcmData.slice(0, 20))); - this.audioWs.send(event.data); - this.lastSendTime = currentTime; - } - } - }; - - source.connect(worklet); - worklet.connect(audioContext.destination); - - // 保存引用以便后续停止 - this.stream = stream; - this.audioContext = audioContext; - this.worklet = worklet; - } catch (error) { - console.error('音频初始化失败:', error); - // 清理音频相关资源 - if (this.stream) { - this.stream.getTracks().forEach(track => track.stop()); - } - if (this.worklet) { - this.worklet.disconnect(); - } - if (this.audioContext) { - this.audioContext.close(); - } - if (this.audioWs) { - this.audioWs.close(); - this.audioWs = null; - } - this.isRecording = false; - - // 不抛出错误,让程序继续执行 - return; - } - }, - - stopRecording() { - console.log('音频传输器: 停止录音'); - this.status = '停止录音'; - this.isRecording = false; - - // 关闭音频资源 + + source.connect(worklet); + worklet.connect(audioContext.destination); + + this.stream = stream; + this.audioContext = audioContext; + this.worklet = worklet; + } catch (error) { + console.error('音频初始化失败:', error); + this.cleanup(); + return; + } + }, + + // stopRecording() { + // console.log('音频传输器: 停止录音'); + // this.status = '停止录音'; + // this.isRecording = false; + + // // 关闭音频资源 + // if (this.stream) { + // console.log('关闭音频流'); + // this.stream.getTracks().forEach(track => track.stop()); + // } + // if (this.worklet) { + // console.log('断开 AudioWorklet'); + // this.worklet.disconnect(); + // } + // if (this.audioContext) { + // console.log('关闭音频上下文'); + // this.audioContext.close(); + // } + + // // 关闭音频 WebSocket 连接 + // if (this.audioWs) { + // console.log('关闭音频 WebSocket 连接'); + // this.audioWs.close(); + // this.audioWs = null; + // } + // } + // 修改停止录音方法 + stopRecording() { + console.log('音频传输器: 停止录音'); + this.status = '停止录音'; + this.isRecording = false; + + // 异步关闭防止状态冲突 + const cleanup = async () => { + // 优先关闭音频流 if (this.stream) { console.log('关闭音频流'); this.stream.getTracks().forEach(track => track.stop()); + this.stream = null; } + + // 断开音频处理节点 if (this.worklet) { console.log('断开 AudioWorklet'); this.worklet.disconnect(); + this.worklet = null; } - if (this.audioContext) { + + // 安全关闭音频上下文 + if (this.audioContext && this.audioContext.state !== 'closed') { console.log('关闭音频上下文'); - this.audioContext.close(); - } - - // 关闭音频 WebSocket 连接 - if (this.audioWs) { - console.log('关闭音频 WebSocket 连接'); - this.audioWs.close(); - this.audioWs = null; + await this.audioContext.close(); + this.audioContext = null; } + + // 移除关闭 WebSocket 的代码 + // if (this.audioWs) { + // console.log('关闭音频 WebSocket 连接'); + // this.audioWs.close(); + // this.audioWs = null; + // } + }; + + cleanup().catch(error => { + console.error('清理过程中发生错误:', error); + }); + }, + async cleanup() { + console.log('开始清理音频资源...'); + + // 停止录音 + if (this.isRecording) { + this.isRecording = false; } + + // 优先关闭音频流 + if (this.stream) { + console.log('关闭音频流'); + this.stream.getTracks().forEach(track => track.stop()); + this.stream = null; + } + + // 断开音频处理节点 + if (this.worklet) { + console.log('断开 AudioWorklet'); + this.worklet.disconnect(); + this.worklet = null; + } + + // 安全关闭音频上下文 + if (this.audioContext && this.audioContext.state !== 'closed') { + console.log('关闭音频上下文'); + await this.audioContext.close(); + this.audioContext = null; + } + + // 关闭音频 WebSocket 连接 + // if (this.audioWs) { + // console.log('关闭音频 WebSocket 连接'); + // this.audioWs.close(); + // this.audioWs = null; + // } } - }; \ No newline at end of file + } +}; \ No newline at end of file diff --git a/src/views/CarControl.vue b/src/views/CarControl.vue index 05e2c46..c0f9bc9 100644 --- a/src/views/CarControl.vue +++ b/src/views/CarControl.vue @@ -2,6 +2,9 @@
+
+ ← +
@@ -13,6 +16,7 @@
link +
@@ -42,7 +46,7 @@
-
云台状态
+
{{ getptz }}
boom @@ -68,10 +72,11 @@
+ @mouseup="handleControlRelease" @mouseleave="handleControlRelease" @touchstart.prevent="handleControlPress" + @touchend.prevent="handleControlRelease" @touchcancel.prevent="handleControlRelease" @contextmenu.prevent + @copy.prevent @selectstart.prevent style="user-select: none;"> control + style="width: 80px; height: 80px; pointer-events: none;" draggable="false">
@@ -117,12 +122,15 @@ export default { reconnectTimer: null, // 添加重连定时器引用 isComponentUnmounted: false, // 添加组件卸载标志 isInitialized: false, // 添加初始化标志 + contenttext: "", + _mousePressed: false, // 添加鼠标/触摸按下标志 + contentptz: "", } }, created() { - // 初始化 GamepadController,禁用调试模式 + // 初始化 GamepadController,启用调试模式 this.gamepadController = new GamepadController({ - debug: false, // 将 debug 设置为 false + debug: false, // 启用调试 deadZone: 0.1, updateInterval: 50 }); @@ -131,24 +139,39 @@ export default { window.addEventListener("gamepadconnected", this.handleGamepadConnected); window.addEventListener("gamepaddisconnected", this.handleGamepadDisconnected); - this.isComponentUnmounted = false; // 添加组件卸载标志 + this.isComponentUnmounted = false; this.initWebSocket(); // 启动连接状态检查 this.connectionCheckInterval = setInterval(() => { this.checkConnectionStatus(); - }, 3000); // 每3秒检查一次 + }, 3000); // 启动定时发送数据 this.sendInterval = setInterval(() => { this.sendControlData(); - }, 400); // 每100ms发送一次 + }, 400); // 初始化音频处理器 this.audioHandler = { ...audioTransmitter.data(), ...audioTransmitter.methods }; + + // 初始化音频 WebSocket 连接 + this.audioHandler.audioWs = new WebSocket('ws://192.168.4.103/ws'); + + this.audioHandler.audioWs.onopen = () => { + console.log('音频 WebSocket 已连接'); + }; + + this.audioHandler.audioWs.onerror = (error) => { + console.error('音频 WebSocket 错误:', error); + }; + + this.audioHandler.audioWs.onclose = () => { + console.log('音频 WebSocket 已关闭'); + }; }, beforeDestroy() { this.isComponentUnmounted = true; // 标记组件已卸载 @@ -159,19 +182,7 @@ export default { this.reconnectTimer = null; } - // 组件销毁前清理资源 - if (this.gamepadController) { - this.gamepadController.destroy(); - } - // 移除全局事件监听 - window.removeEventListener("gamepadconnected", this.handleGamepadConnected); - window.removeEventListener("gamepaddisconnected", this.handleGamepadDisconnected); - - if (this.ws) { - this.ws.close(); - this.ws = null; - } - + // 清除定时器 if (this.connectionCheckInterval) { clearInterval(this.connectionCheckInterval); } @@ -180,10 +191,34 @@ export default { clearInterval(this.sendInterval); } + // 移除全局事件监听 + window.removeEventListener("gamepadconnected", this.handleGamepadConnected); + window.removeEventListener("gamepaddisconnected", this.handleGamepadDisconnected); + + // 销毁手柄控制器 + if (this.gamepadController) { + this.gamepadController.destroy(); + } + // 确保停止音频传输 if (this.audioHandler && this.audioHandler.isRecording) { this.audioHandler.stopRecording(); } + + // 关闭音频 WebSocket 连接 + if (this.audioHandler && this.audioHandler.audioWs) { + console.log('关闭音频 WebSocket 连接'); + this.audioHandler.audioWs.close(); + this.audioHandler.audioWs = null; + } + + // 关闭控制 WebSocket 连接 + if (this.ws) { + console.log('关闭控制 WebSocket 连接'); + this.ws.close(); + this.ws = null; + this.wsConnected = false; + } }, methods: { goto(path) { @@ -207,17 +242,19 @@ export default { } }, handleControlPress() { + console.log('按钮被按下,设置 isControlPressed = true'); + this._mousePressed = true; this.isControlPressed = true; // 开始音频传输 - console.log('开始音频传输...'); if (this.audioHandler) { this.audioHandler.startRecording(); } }, handleControlRelease() { + console.log('按钮被释放,设置 isControlPressed = false'); + this._mousePressed = false; this.isControlPressed = false; // 停止音频传输 - console.log('停止音频传输...'); if (this.audioHandler) { this.audioHandler.stopRecording(); } @@ -289,7 +326,7 @@ export default { // 根据角度计算方向值(0-4) let direction = 0; // 默认停止 - if (speed > 10) { // 添加速度阈值,避免小幅度移动 + if (speed > 10) { if (angle >= 315 || angle < 45) { direction = 4; // 右转 } else if (angle >= 45 && angle < 135) { @@ -301,27 +338,29 @@ export default { } } - // 更新控制按钮状态 - const wasPressed = this.isControlPressed; - this.isControlPressed = this.gamepadController.rightJoystickPressed; + // 检测手柄右摇杆按钮状态,但不覆盖鼠标/触摸按下的状态 + const gamepadButtonPressed = this.gamepadController.rightJoystickPressed; - // 检测按钮状态变化并处理音频传输 - if (!wasPressed && this.isControlPressed) { - // 按钮刚被按下 - console.log('手柄按钮按下,开始音频传输...'); + // 如果手柄按钮被按下且当前状态为未按下,则开始录音 + if (gamepadButtonPressed && !this.isControlPressed) { + console.log('手柄按钮被按下,开始录音'); + this.isControlPressed = true; if (this.audioHandler) { this.audioHandler.startRecording(); } - } else if (wasPressed && !this.isControlPressed) { - // 按钮刚被释放 - console.log('手柄按钮释放,停止音频传输...'); + } + // 如果手柄按钮被释放且当前状态为按下,则停止录音 + else if (!gamepadButtonPressed && this.isControlPressed && !this._mousePressed) { + console.log('手柄按钮被释放,停止录音'); + this.isControlPressed = false; if (this.audioHandler) { this.audioHandler.stopRecording(); } } - // 更新手柄相关状态 - this.lastDirection = direction; + // 更新状态 + this.contenttext = direction; + this.lastDirection = angle; if (directionAxis9 !== undefined) this.platform_fun = directionAxis9; if (speed !== undefined) this.lastSpeed = speed; } @@ -357,15 +396,16 @@ export default { top: data.attribute.obstacle_sta === 1, bottom: data.attribute.obstacle_sta === 2 }; - + // 只在初始化时更新开关状态 - if (!this.isInitialized) { - this.screenStatus = data.attribute.screen_en === 1; - this.warningLightStatus = data.attribute.warn_light_en === 1; - this.followStatus = data.attribute.follow_en === 1; - this.obstacleAvoidEnabled = data.attribute.obstacle_avoid_en === 1; - this.isInitialized = true; - } + // if (!this.isInitialized) { + this.screenStatus = data.attribute.screen_en === 1; + this.warningLightStatus = data.attribute.warn_light_en === 1; + this.followStatus = data.attribute.follow_en === 1; + this.obstacleAvoidEnabled = data.attribute.obstacle_avoid_en === 1; + this.contentptz = data.attribute.platform; + // this.isInitialized = true; + // } } // 更新方向状态 @@ -431,6 +471,64 @@ export default { } }, + handleBack() { + // 先标记组件已卸载,防止重连 + this.isComponentUnmounted = true; + + // 立即清除所有定时器 + if (this.connectionCheckInterval) { + clearInterval(this.connectionCheckInterval); + this.connectionCheckInterval = null; + } + if (this.sendInterval) { + clearInterval(this.sendInterval); + this.sendInterval = null; + } + if (this.reconnectTimer) { + clearTimeout(this.reconnectTimer); + this.reconnectTimer = null; + } + + // 停止音频传输 + if (this.audioHandler && this.audioHandler.isRecording) { + this.audioHandler.stopRecording(); + } + + // 移除全局事件监听 + window.removeEventListener("gamepadconnected", this.handleGamepadConnected); + window.removeEventListener("gamepaddisconnected", this.handleGamepadDisconnected); + + // 销毁手柄控制器 + if (this.gamepadController) { + this.gamepadController.destroy(); + this.gamepadController = null; + } + + // 断开所有 WebSocket 连接 + const closePromises = []; + + if (this.ws) { + console.log('断开控制 WebSocket 连接'); + this.ws.onclose = null; // 移除重连监听 + this.ws.close(); + this.ws = null; + this.wsConnected = false; + } + + if (this.audioHandler && this.audioHandler.audioWs) { + console.log('断开音频 WebSocket 连接'); + this.audioHandler.audioWs.onclose = null; // 移除重连监听 + this.audioHandler.audioWs.close(); + this.audioHandler.audioWs = null; + } + + // 确保所有清理工作完成后再跳转 + setTimeout(() => { + // 返回主页 + this.$router.push('/'); + }, 100); + }, + }, computed: { // 注释掉电池图片计算属性 @@ -450,7 +548,7 @@ export default { }, getDirectionText() { // 根据 direction 值显示对应状态 - switch (this.lastDirection) { + switch (this.contenttext) { case 0: return '遥控车已停止'; case 1: @@ -464,6 +562,18 @@ export default { default: return '遥控车当前状态'; } + }, + getptz() { + switch (this.contentptz) { + case 0: + return '云台静止'; + case 1: + return '云台上升'; + case 2: + return '云台下降'; + default: + return '云台静止'; + } } }, setup() { @@ -526,7 +636,8 @@ export default { display: flex; align-items: center; justify-content: center; - min-width: 180px; /* 确保文本变化时盒子大小稳定 */ + min-width: 180px; + /* 确保文本变化时盒子大小稳定 */ } .main-content { @@ -675,6 +786,12 @@ export default { align-items: center; transition: border-color 0.3s ease; cursor: pointer; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; } .circle-border.pressed { @@ -701,4 +818,23 @@ export default { :deep(.van-nav-bar) { z-index: 999; } + +.back-button { + cursor: pointer; + width: 40px; + height: 40px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + transition: background-color 0.3s; + font-size: 24px; + /* 调整箭头大小 */ + color: #00ffff; + /* 使用青色,与其他元素保持一致 */ +} + +.back-button:hover { + background-color: rgba(255, 255, 255, 0.1); +}