封装控制设备 初始哈 xbox显示 优化

This commit is contained in:
mmc
2026-05-20 09:25:58 +08:00
parent 64682d9ef5
commit e7234ce63e
4 changed files with 212 additions and 12 deletions

4
env.js
View File

@@ -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/'
},
};

View File

@@ -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}
/>
</div>

View File

@@ -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 */}
<div
className={`absolute top-0 right-0 px-2 py-0.5 rounded text-[8px] font-bold tracking-widest border
${connected
? (isControlled
? 'bg-cyan-500/20 text-cyan-400 border-cyan-500/30' // 控制中:青色样式
: 'bg-red-500/20 text-red-400 border-red-500/30') // 无控制权限:红色样式
: 'bg-slate-800 text-slate-500 border-white/5'}`} // 无控制器连接:灰色样式
${connected
? isConnecting
? 'bg-yellow-500/20 text-yellow-400 border-yellow-500/30'
: isControlled
? 'bg-cyan-500/20 text-cyan-400 border-cyan-500/30'
: 'bg-red-500/20 text-red-400 border-red-500/30'
: 'bg-slate-800 text-slate-500 border-white/5'
}
`}
>
{connected ? isControlled ? '控制中' : '重新选中设备获取控制权限' : '无控制器连接'}
{connected
? isConnecting
? '连接中...'
: isControlled
? '控制中'
: '无控制权限'
: '未连接设备'
}
</div>
<svg viewBox="0 0 400 300" className="w-full h-full drop-shadow-[0_0_20px_rgba(34,211,238,0.15)]">

View File

@@ -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 },
});
}
}