From e7234ce63efcb75fb43eee415e4ec9e600b6a8a2 Mon Sep 17 00:00:00 2001 From: mmc <1556375442@qq.com> Date: Wed, 20 May 2026 09:25:58 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B0=81=E8=A3=85=E6=8E=A7=E5=88=B6=E8=AE=BE?= =?UTF-8?q?=E5=A4=87=20=20=E5=88=9D=E5=A7=8B=E5=93=88=20=20=20xbox?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=20=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- env.js | 4 +- src/components/devices/DeviceControl.tsx | 8 +- src/components/devices/XboxController.tsx | 27 ++- src/components/devices/deviceController.js | 185 +++++++++++++++++++++ 4 files changed, 212 insertions(+), 12 deletions(-) create mode 100644 src/components/devices/deviceController.js diff --git a/env.js b/env.js index 8259afa..5f29637 100644 --- a/env.js +++ b/env.js @@ -15,8 +15,8 @@ const envConfig = { }, [ENV.PROD]: { // baseURL: 'http://192.168.2.56:8081', - baseURL: 'http://1.95.137.212:59015',//http://192.168.2.56:8081 - //baseURL: 'https://serviceri.satabot.com', + //baseURL: 'http://1.95.137.212:59015',//http://192.168.2.56:8081 + baseURL: 'https://serviceri.satabot.com', WS_URL: 'wss://ri.satabot.com/ws/' }, }; diff --git a/src/components/devices/DeviceControl.tsx b/src/components/devices/DeviceControl.tsx index 9a6e2ab..9ce1b57 100644 --- a/src/components/devices/DeviceControl.tsx +++ b/src/components/devices/DeviceControl.tsx @@ -86,6 +86,7 @@ const RobotControlCenter = ({ setView }) => { const [selectedRoute, setSelectedRoute] = useState(null); // ✅ 新增 const [activeTab, setActiveTab] = useState<'remote' | 'gps' | 'vizanti'>('remote'); const [waypoints, setWaypoints] = useState(INITIAL_WAYPOINTS); + const [isConnecting, setIsConnecting] = useState(false); // 连接中状态 const [newWaypoint, setNewWaypoint] = useState({ id: 0, lon: '', lat: '' }); const [taskIssued, setTaskIssued] = useState(false); const [taskRunning, setTaskRunning] = useState(false); @@ -456,6 +457,8 @@ const RobotControlCenter = ({ setView }) => { } const getControl = (serialnum) => { + setIsConnecting(true); + const data = { platform: "web", deviceId: serialnum }; getJurisdiction(data).then(res => { if (res?.data?.code === 200 && res?.data?.data?.remoteControl == true) { @@ -470,7 +473,9 @@ const RobotControlCenter = ({ setView }) => { }).catch(err => { controlRef.current = false; setIsControlled(false); - }) + }).finally(() => { + setIsConnecting(false); + }); }; const handleSelectRobot = async (robot) => { @@ -1541,6 +1546,7 @@ const RobotControlCenter = ({ setView }) => { onGamepadUpdate={handleGamepad} handleControlModal={handleControlModal} isControlled={isControlled} + isConnecting={isConnecting} /> diff --git a/src/components/devices/XboxController.tsx b/src/components/devices/XboxController.tsx index 1c35f03..8c744b6 100644 --- a/src/components/devices/XboxController.tsx +++ b/src/components/devices/XboxController.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useState, useRef } from 'react'; -const XboxController = ({ onGamepadUpdate, handleControlModal, isControlled }) => { +const XboxController = ({ onGamepadUpdate, handleControlModal, isControlled, isConnecting }) => { const [gamepadState, setGamepadState] = useState<{ buttons: boolean[]; axes: number[]; @@ -123,15 +123,24 @@ const XboxController = ({ onGamepadUpdate, handleControlModal, isControlled }) = {/* Gamepad Status Badge */}
- {connected ? isControlled ? '控制中' : '重新选中设备获取控制权限' : '无控制器连接'} + {connected + ? isConnecting + ? '连接中...' + : isControlled + ? '控制中' + : '无控制权限' + : '未连接设备' + }
diff --git a/src/components/devices/deviceController.js b/src/components/devices/deviceController.js new file mode 100644 index 0000000..a27cfde --- /dev/null +++ b/src/components/devices/deviceController.js @@ -0,0 +1,185 @@ +// src/components/device/deviceController.js +import { message } from 'antd'; +import { wsManager } from '../WebSocketManager.ts'; +import { getControlRight, getJurisdiction } from '../../api/device.ts'; +import envConfig from '../../../env'; + +export default class DeviceController { + constructor(userInfo, messageApi) { + this.userInfo = userInfo; + this.messageApi = messageApi; + this.wsUrl = envConfig.WS_URL || 'wss://ri.satabot.com/ws/'; + + // 状态 + this.connectedDeviceSn = null; + this.requestDeviceId = ''; + this.isControlled = false; + this.isRequestControl = false; + this.hasControlRight = false; + this.wsDeviceInfo = {}; + this.wsStatus = 'disconnected'; + + // 回调 + this.onDeviceInfoUpdate = null; + this.onControlStatusChange = null; + this.onWsStatusChange = null; + this.onPermissionRequest = null; + + // 绑定 this + this.handleWsMessage = this.handleWsMessage.bind(this); + this.getControl = this.getControl.bind(this); + this.connectDevice = this.connectDevice.bind(this); + this.sendPermissionRequest = this.sendPermissionRequest.bind(this); + } + + // 初始化监听 + init() { + wsManager.onMessage(this.handleWsMessage); + wsManager.onStatusChange((status) => { + this.wsStatus = status; + if (this.onWsStatusChange) this.onWsStatusChange(status); + }); + } + + // 销毁 + destroy() { + wsManager.close(); + } + + // 获取设备控制权限 + async getControl(serialnum) { + try { + const data = { platform: 'web', deviceId: serialnum }; + const res = await getJurisdiction(data); + const ok = res?.code === 200 && res?.data?.remoteControl === true; + this.isControlled = ok; + if (this.onControlStatusChange) this.onControlStatusChange(ok); + } catch (err) { + this.isControlled = false; + if (this.onControlStatusChange) this.onControlStatusChange(false); + } + } + + // 连接设备 WebSocket + async connectDevice(device) { + if (!device) return; + const sn = device.serialNumber || device.device_sn; + + wsManager.close(); + this.connectedDeviceSn = null; + this.wsDeviceInfo = {}; + + try { + const res = await getControlRight({ + userName: this.userInfo.username, + sourceType: 1, + token: this.userInfo.token, + }); + + if (res?.code === 200) { + this.hasControlRight = true; + wsManager.connect(sn, this.wsUrl); + this.connectedDeviceSn = sn; + await this.getControl(sn); + } else { + this.hasControlRight = false; + this.messageApi.warning('其他端正在控制'); + } + } catch (err) { + this.messageApi.error('权限校验失败'); + } + } + + // 发送权限请求 + sendPermissionRequest(data, type) { + const wsData = { + type, + switchResult: data, + token: this.userInfo.token, + deviceId: + type === 'switchPermission' + ? this.connectedDeviceSn + : this.requestDeviceId, + userName: this.userInfo.username, + }; + wsManager.send(wsData); + } + + // 处理外部请求控制 + requestControl() { + if (this.isRequestControl || !this.hasControlRight) { + if (!this.hasControlRight) this.messageApi.warning('当前网页端无控制权限'); + return; + } + this.isRequestControl = true; + this.messageApi.loading({ + key: 'control', + content: '权限请求中...', + duration: 0, + }); + this.sendPermissionRequest(true, 'switchPermission'); + } + + // 处理 WebSocket 消息 + handleWsMessage(msg) { + switch (msg.type) { + case 'deviceInfo': + const data = {}; + msg.data.forEach((item) => (data[item.name] = item.value)); + this.wsDeviceInfo = data; + if (this.onDeviceInfoUpdate) this.onDeviceInfoUpdate(data); + break; + + case 'AUTH': + if (msg.isAuth) this.getControl(this.connectedDeviceSn); + break; + + case 'switchResult': + this.messageApi.success({ + content: '权限获取成功', + key: 'control', + duration: 2, + }); + this.isRequestControl = false; + this.isControlled = true; + if (this.onControlStatusChange) this.onControlStatusChange(true); + break; + + case 'switchPermission': + const platform = (msg.platform || '').toLowerCase().trim(); + if (!msg.platform || platform === 'web') return; + this.requestDeviceId = msg.deviceId; + if (this.onPermissionRequest) { + this.onPermissionRequest({ + title: '权限请求', + content: `${msg.platform} 请求控制 ${msg.deviceId}`, + }); + } + break; + + case 'detectionReport': + if (msg.data?.deviceId !== this.connectedDeviceSn) return; + const { isSecure, obstacle } = msg.data; + if (isSecure) return; + const type = obstacle?.type; + const distance = (obstacle?.distance || 0).toFixed(2); + const types = { 0: '未知', 1: '人', 2: '车辆', 3: '围栏', 4: '坑洞', 5: '草丛' }; + this.messageApi.warning({ + key: 'obstacle-warning', + content: `检测到 ${types[type] ?? '未知物'},距离 ${distance}m`, + duration: 3, + }); + break; + } + } + + // 发送游戏手柄指令 + sendGamepad(state) { + if (!this.isControlled || !this.connectedDeviceSn) return; + wsManager.send({ + type: 'gamepad', + deviceId: this.connectedDeviceSn, + data: { axes: state.axes, buttons: state.buttons }, + }); + } +}