封装控制设备 初始哈 xbox显示 优化
This commit is contained in:
4
env.js
4
env.js
@@ -15,8 +15,8 @@ const envConfig = {
|
|||||||
},
|
},
|
||||||
[ENV.PROD]: {
|
[ENV.PROD]: {
|
||||||
// baseURL: 'http://192.168.2.56:8081',
|
// baseURL: 'http://192.168.2.56:8081',
|
||||||
baseURL: 'http://1.95.137.212:59015',//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: 'https://serviceri.satabot.com',
|
||||||
WS_URL: 'wss://ri.satabot.com/ws/'
|
WS_URL: 'wss://ri.satabot.com/ws/'
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ const RobotControlCenter = ({ setView }) => {
|
|||||||
const [selectedRoute, setSelectedRoute] = useState(null); // ✅ 新增
|
const [selectedRoute, setSelectedRoute] = useState(null); // ✅ 新增
|
||||||
const [activeTab, setActiveTab] = useState<'remote' | 'gps' | 'vizanti'>('remote');
|
const [activeTab, setActiveTab] = useState<'remote' | 'gps' | 'vizanti'>('remote');
|
||||||
const [waypoints, setWaypoints] = useState(INITIAL_WAYPOINTS);
|
const [waypoints, setWaypoints] = useState(INITIAL_WAYPOINTS);
|
||||||
|
const [isConnecting, setIsConnecting] = useState(false); // 连接中状态
|
||||||
const [newWaypoint, setNewWaypoint] = useState({ id: 0, lon: '', lat: '' });
|
const [newWaypoint, setNewWaypoint] = useState({ id: 0, lon: '', lat: '' });
|
||||||
const [taskIssued, setTaskIssued] = useState(false);
|
const [taskIssued, setTaskIssued] = useState(false);
|
||||||
const [taskRunning, setTaskRunning] = useState(false);
|
const [taskRunning, setTaskRunning] = useState(false);
|
||||||
@@ -456,6 +457,8 @@ const RobotControlCenter = ({ setView }) => {
|
|||||||
|
|
||||||
}
|
}
|
||||||
const getControl = (serialnum) => {
|
const getControl = (serialnum) => {
|
||||||
|
setIsConnecting(true);
|
||||||
|
|
||||||
const data = { platform: "web", deviceId: serialnum };
|
const data = { platform: "web", deviceId: serialnum };
|
||||||
getJurisdiction(data).then(res => {
|
getJurisdiction(data).then(res => {
|
||||||
if (res?.data?.code === 200 && res?.data?.data?.remoteControl == true) {
|
if (res?.data?.code === 200 && res?.data?.data?.remoteControl == true) {
|
||||||
@@ -470,7 +473,9 @@ const RobotControlCenter = ({ setView }) => {
|
|||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
controlRef.current = false;
|
controlRef.current = false;
|
||||||
setIsControlled(false);
|
setIsControlled(false);
|
||||||
})
|
}).finally(() => {
|
||||||
|
setIsConnecting(false);
|
||||||
|
});
|
||||||
|
|
||||||
};
|
};
|
||||||
const handleSelectRobot = async (robot) => {
|
const handleSelectRobot = async (robot) => {
|
||||||
@@ -1541,6 +1546,7 @@ const RobotControlCenter = ({ setView }) => {
|
|||||||
onGamepadUpdate={handleGamepad}
|
onGamepadUpdate={handleGamepad}
|
||||||
handleControlModal={handleControlModal}
|
handleControlModal={handleControlModal}
|
||||||
isControlled={isControlled}
|
isControlled={isControlled}
|
||||||
|
isConnecting={isConnecting}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
import React, { useEffect, useState, useRef } from 'react';
|
import React, { useEffect, useState, useRef } from 'react';
|
||||||
|
|
||||||
const XboxController = ({ onGamepadUpdate, handleControlModal, isControlled }) => {
|
const XboxController = ({ onGamepadUpdate, handleControlModal, isControlled, isConnecting }) => {
|
||||||
const [gamepadState, setGamepadState] = useState<{
|
const [gamepadState, setGamepadState] = useState<{
|
||||||
buttons: boolean[];
|
buttons: boolean[];
|
||||||
axes: number[];
|
axes: number[];
|
||||||
@@ -123,15 +123,24 @@ const XboxController = ({ onGamepadUpdate, handleControlModal, isControlled }) =
|
|||||||
{/* Gamepad Status Badge */}
|
{/* Gamepad Status Badge */}
|
||||||
<div
|
<div
|
||||||
className={`absolute top-0 right-0 px-2 py-0.5 rounded text-[8px] font-bold tracking-widest border
|
className={`absolute top-0 right-0 px-2 py-0.5 rounded text-[8px] font-bold tracking-widest border
|
||||||
${connected
|
${connected
|
||||||
? (isControlled
|
? isConnecting
|
||||||
? 'bg-cyan-500/20 text-cyan-400 border-cyan-500/30' // 控制中:青色样式
|
? 'bg-yellow-500/20 text-yellow-400 border-yellow-500/30'
|
||||||
: 'bg-red-500/20 text-red-400 border-red-500/30') // 无控制权限:红色样式
|
: isControlled
|
||||||
: 'bg-slate-800 text-slate-500 border-white/5'}`} // 无控制器连接:灰色样式
|
? '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>
|
</div>
|
||||||
|
|
||||||
<svg viewBox="0 0 400 300" className="w-full h-full drop-shadow-[0_0_20px_rgba(34,211,238,0.15)]">
|
<svg viewBox="0 0 400 300" className="w-full h-full drop-shadow-[0_0_20px_rgba(34,211,238,0.15)]">
|
||||||
|
|||||||
185
src/components/devices/deviceController.js
Normal file
185
src/components/devices/deviceController.js
Normal 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 },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user