测权限控制 释放权限 优化ws连接
This commit is contained in:
10
env.js
10
env.js
@@ -9,17 +9,17 @@ const currentEnv = import.meta.env?.MODE || process.env.NODE_ENV;
|
||||
// 环境配置映射
|
||||
const envConfig = {
|
||||
[ENV.DEV]: {
|
||||
//baseURL: 'http://192.168.2.59:8081',//http://192.168.2.56:8081
|
||||
baseURL: 'http://1.95.137.212:8081',//http://192.168.2.56:8081
|
||||
////baseURL: 'http://1.95.137.212:59015',//http://192.168.2.56:8081
|
||||
//WS_URL: 'ws://192.168.2.59:9002/ws/',
|
||||
//mqtt_url: "ws://1.95.137.212:8083/mqtt",
|
||||
//geoserver_url: "http://192.168.2.59:7000/geoserver"
|
||||
|
||||
baseURL: 'http://1.95.137.212:8081',//http://192.168.2.56:8081
|
||||
//baseURL: 'http://192.168.2.34:8081',//http://192.168.2.56:8081
|
||||
//baseURL: 'http://1.95.137.212:8081',//http://192.168.2.56:8081
|
||||
//baseURL: 'http://192.168.2.62:8081',//http://192.168.2.56:8081
|
||||
//baseURL: 'https://serviceri.satabot.com',
|
||||
//WS_URL: 'ws://1.95.137.212:9002/ws/',
|
||||
WS_URL: 'ws://192.168.2.34:9002/ws/',
|
||||
WS_URL: 'ws://1.95.137.212:9002/ws/',
|
||||
//WS_URL: 'ws://192.168.2.62:9002/ws/',
|
||||
mqtt_url: "ws://1.95.137.212:8083/mqtt",
|
||||
geoserver_url: "http://1.95.137.212:7900/geoserver"
|
||||
|
||||
|
||||
@@ -39,6 +39,8 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine,
|
||||
const planLineRef = useRef(null); // points 规划航线
|
||||
const navLineRef = useRef(null); // 完成点实时轨迹
|
||||
const markedPointsRef = useRef([]); // 标记点实体
|
||||
const { t } = useTranslation();
|
||||
|
||||
|
||||
const realtimeEntityRef = useRef(null);
|
||||
const realtimeLineRef = useRef(null);
|
||||
@@ -343,8 +345,7 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine,
|
||||
}
|
||||
}, [markedPoints]);
|
||||
|
||||
function showRobotTooltip(tooltip, robot, position) {
|
||||
const { t } = useTranslation();
|
||||
function showRobotTooltip(tooltip, robot, position, t) {
|
||||
const statusMap = {
|
||||
1: { text: t('common.working'), color: "#10b981" },
|
||||
2: { text: t('devices.idle'), color: "#f59e0b" },
|
||||
@@ -1400,7 +1401,7 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine,
|
||||
const picked = viewer.scene.pick(movement.endPosition);
|
||||
if (window.Cesium.defined(picked) && picked.id?.properties?.robot) {
|
||||
const robot = picked.id.properties.robot.getValue();
|
||||
showRobotTooltip(tooltipRef.current, robot, movement.endPosition);
|
||||
showRobotTooltip(tooltipRef.current, robot, movement.endPosition, t);
|
||||
} else {
|
||||
tooltipRef.current.style.display = "none";
|
||||
}
|
||||
|
||||
@@ -459,8 +459,17 @@ const RobotControlCenter = ({ setView }) => {
|
||||
}
|
||||
const getControl = (serialnum) => {
|
||||
setIsConnecting(true);
|
||||
let userParse = null;
|
||||
try {
|
||||
const dataStr = localStorage.getItem("userInfo");
|
||||
userParse = dataStr ? JSON.parse(dataStr) : null;
|
||||
} catch (e) {
|
||||
console.error("用户信息解析失败", e);
|
||||
}
|
||||
|
||||
const data = { platform: "web", deviceId: serialnum };
|
||||
const token = userParse.token || Cookies.get("Admin-Token");
|
||||
|
||||
const data = { platform: "web", deviceId: serialnum, token: token };
|
||||
getJurisdiction(data).then(res => {
|
||||
if (res?.data?.code === 200 && res?.data?.data?.remoteControl == true) {
|
||||
console.log("获取远程遥控权限成功");
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, {
|
||||
import React, {
|
||||
useState,
|
||||
useEffect,
|
||||
useMemo,
|
||||
@@ -78,6 +78,7 @@ export default function DeviceStatusPage() {
|
||||
const [arrivedPoint, setArrivedPoint] = useState(null);//完成点
|
||||
const [isLoopMode, setIsLoopMode] = useState(true);
|
||||
const controllerRef = useRef<DeviceController | null>(null);
|
||||
const isFirstConnect = useRef(true);
|
||||
const [messageApi, contextHolder] = message.useMessage();
|
||||
|
||||
|
||||
@@ -388,7 +389,6 @@ export default function DeviceStatusPage() {
|
||||
|
||||
|
||||
const runningStatus = useMemo(() => {
|
||||
console.log(locationInfo, "locationInfo")
|
||||
// 判断对象是否为空
|
||||
if ((!wsDeviceInfo || Object.keys(wsDeviceInfo).length === 0) && (!locationInfo || Object.keys(locationInfo).length === 0) && !isUAV) {
|
||||
return {};
|
||||
@@ -408,7 +408,6 @@ export default function DeviceStatusPage() {
|
||||
...locationInfo
|
||||
};
|
||||
}, [device, wsDeviceInfo, isUAV, locationInfo]);
|
||||
console.log(runningStatus, "runningStatus")
|
||||
|
||||
const { video: robotVideo, error, ready } = useWebRTCPlayer(
|
||||
(!isUAV && isVideoPlaying) ? (device?.serialNumber || '') : '',
|
||||
@@ -485,14 +484,19 @@ export default function DeviceStatusPage() {
|
||||
|
||||
useEffect(() => {
|
||||
if (!device || !controllerRef.current || !device?.serialNumber || isUAV) {
|
||||
//controllerRef.current?.destroy();
|
||||
//setWsDeviceInfo({});
|
||||
return;
|
||||
}
|
||||
setWsDeviceInfo({});
|
||||
setLocationInfo({});
|
||||
|
||||
controllerRef.current.connectDevice(device);
|
||||
if (isFirstConnect.current) {
|
||||
|
||||
isFirstConnect.current = false;
|
||||
controllerRef.current.connectDevice(device);
|
||||
} else {
|
||||
|
||||
controllerRef.current.switchDevice(device);
|
||||
}
|
||||
|
||||
}, [device?.serialNumber]);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect, useRef, useMemo } from 'react';
|
||||
import React, { useState, useEffect, useRef, useMemo } from 'react';
|
||||
import {
|
||||
Row, Col, Card, Button, Space, Table, Tag, Typography, Progress, Badge,
|
||||
Tabs, Input, Switch, Divider, Tooltip, Timeline, Select, message, DatePicker,
|
||||
@@ -141,6 +141,7 @@ export default function RobotTaskPage() {
|
||||
});
|
||||
const [messageApi, contextHolder] = message.useMessage();
|
||||
const controllerRef = useRef<DeviceController | null>(null);
|
||||
const isFirstConnect = useRef(true);
|
||||
const handleFinish = (data) => {
|
||||
setExecute(false);
|
||||
setShowVideo(false); // 任务完成关闭视频
|
||||
@@ -240,7 +241,7 @@ export default function RobotTaskPage() {
|
||||
setExecute(true);
|
||||
setExecutingRobot(targetDevice);
|
||||
setClearNavLine(v => v + 1); // 清空地图轨迹
|
||||
controllerRef.current?.connectDevice(targetDevice);
|
||||
if (isFirstConnect.current) { isFirstConnect.current = false; controllerRef.current?.connectDevice(targetDevice); } else { controllerRef.current?.switchDevice(targetDevice); }
|
||||
setShowVideo(true);
|
||||
};
|
||||
|
||||
@@ -300,7 +301,7 @@ export default function RobotTaskPage() {
|
||||
setExecute(true);
|
||||
setExecutingRobot(targetDevice);
|
||||
setClearNavLine(v => v + 1); // 清空地图轨迹
|
||||
controllerRef.current?.connectDevice(targetDevice);
|
||||
if (isFirstConnect.current) { isFirstConnect.current = false; controllerRef.current?.connectDevice(targetDevice); } else { controllerRef.current?.switchDevice(targetDevice); }
|
||||
setShowVideo(true);
|
||||
|
||||
}, [devices, taskPoolList, executingRobot, runningPoolTask]);
|
||||
@@ -450,7 +451,7 @@ export default function RobotTaskPage() {
|
||||
setExecute(true);
|
||||
setExecutingRobot(targetDevice);
|
||||
setClearNavLine(v => v + 1); // 清空地图轨迹
|
||||
controllerRef.current?.connectDevice(targetDevice);
|
||||
if (isFirstConnect.current) { isFirstConnect.current = false; controllerRef.current?.connectDevice(targetDevice); } else { controllerRef.current?.switchDevice(targetDevice); }
|
||||
setShowVideo(true);
|
||||
}
|
||||
}
|
||||
@@ -556,7 +557,7 @@ export default function RobotTaskPage() {
|
||||
fetchTaskList();
|
||||
fetchTaskPoolList();
|
||||
fetchTaskHistoryList();
|
||||
controllerRef.current?.connectDevice(selectedRobotForExecute);
|
||||
if (isFirstConnect.current) { isFirstConnect.current = false; controllerRef.current?.connectDevice(selectedRobotForExecute); } else { controllerRef.current?.switchDevice(selectedRobotForExecute); }
|
||||
setShowVideo(true); // 连接成功后自动打开视频
|
||||
|
||||
} else {
|
||||
|
||||
@@ -116,7 +116,7 @@ const XboxController = ({ onGamepadUpdate, handleControlModal, isControlled, isC
|
||||
return (
|
||||
<div className="relative h-[252px] aspect-[4/3] flex items-center justify-center "
|
||||
onClick={() => {
|
||||
if (connected && !isControlled && hasControlRight) {
|
||||
if (!isControlled) {
|
||||
console.log("点击了无控制权限,发起权限请求");
|
||||
// 示例:调用你代码中已有的权限请求弹窗逻辑
|
||||
handleControlModal({ control: true, type: "switchPermission" });
|
||||
@@ -125,7 +125,7 @@ const XboxController = ({ onGamepadUpdate, handleControlModal, isControlled, isC
|
||||
{/* Gamepad Status Badge */}
|
||||
<div
|
||||
className={`absolute top-0 right-0 px-2 py-0.5 rounded text-[8px] font-bold tracking-widest border
|
||||
${connected || !hasControlRight
|
||||
${connected
|
||||
? isConnecting
|
||||
? 'bg-yellow-500/20 text-yellow-400 border-yellow-500/30'
|
||||
: isControlled
|
||||
@@ -137,8 +137,6 @@ const XboxController = ({ onGamepadUpdate, handleControlModal, isControlled, isC
|
||||
>
|
||||
{!connected ? (
|
||||
t('devices.controllerNotConnected')
|
||||
) : !hasControlRight ? (
|
||||
t('devices.otherWebConnected')
|
||||
) : isConnecting ? (
|
||||
t('devices.control2')
|
||||
) : isControlled ? (
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import throttle from 'lodash/throttle';
|
||||
import { wsManager } from '../WebSocketManager.ts';
|
||||
import {
|
||||
getControlRight,
|
||||
getJurisdiction,
|
||||
releaseControl
|
||||
} from '../../api/device.ts';
|
||||
@@ -63,11 +62,15 @@ export default class DeviceController {
|
||||
document.addEventListener('visibilitychange', this.handleVisibilityChange);
|
||||
document.addEventListener('beforeunload', this.handleBeforeUnload);
|
||||
}
|
||||
levae() {
|
||||
wsManager.close();
|
||||
|
||||
}
|
||||
|
||||
handleVisibilityChange() {
|
||||
if (document.visibilityState === 'hidden') {
|
||||
console.log('?? 页<>面进入后台,释放控制权限');
|
||||
|
||||
console.log('Page hidden, release control');
|
||||
//this.release();
|
||||
wsManager.close();
|
||||
this.connectedDeviceSn = null;
|
||||
|
||||
@@ -76,7 +79,7 @@ export default class DeviceController {
|
||||
}
|
||||
if (document.visibilityState === 'visible' && this.currentDevice) {
|
||||
if (!this.currentDevice?.serialNumber) return;
|
||||
console.log('? 页面切回前台,自动重连 WebSocket');
|
||||
console.log('Page visible, reconnect WebSocket');
|
||||
setTimeout(() => {
|
||||
this.connectDevice(this.currentDevice);
|
||||
}, 500);
|
||||
@@ -84,7 +87,7 @@ export default class DeviceController {
|
||||
}
|
||||
|
||||
handleBeforeUnload() {
|
||||
console.log('?? 浏览器即将关闭/刷新,释放控制权限');
|
||||
console.log('Browser closing/refreshing, release control');
|
||||
this.release();
|
||||
}
|
||||
|
||||
@@ -95,35 +98,30 @@ export default class DeviceController {
|
||||
this.wsStatus !== 'connected' &&
|
||||
this.currentDevice?.serialNumber
|
||||
) {
|
||||
console.log('?? 后台保活:重新连接 WebSocket');
|
||||
console.log('Keep alive: reconnect WebSocket');
|
||||
this.connectDevice(this.currentDevice);
|
||||
}
|
||||
}, 8000);
|
||||
}
|
||||
|
||||
|
||||
async release() {
|
||||
console.log('?? 释放控制权限');
|
||||
//if (this.isControlled && this.connectedDeviceSn) {
|
||||
if (!this.connectedDeviceSn) return;
|
||||
const data = {
|
||||
platform: 'web',
|
||||
type: 1,
|
||||
deviceId: this.connectedDeviceSn,
|
||||
token: this.userInfo.token,
|
||||
};
|
||||
const url = `${envConfig.baseURL}/forward/device/releaseControl`;
|
||||
console.log('?? 释放控制权限1111');
|
||||
|
||||
try {
|
||||
await releaseControl(data);
|
||||
console.log('?? 释放控制权限1111222');
|
||||
|
||||
const blob = new Blob([JSON.stringify(data)], { type: 'application/json' });
|
||||
navigator.sendBeacon(url, blob);
|
||||
console.log('✅ 已发送 releaseControl 请求');
|
||||
console.log('releaseControl sent');
|
||||
} catch (e) {
|
||||
console.error('Failed to send releaseControl beacon:', e);
|
||||
console.error('Failed to send releaseControl:', e);
|
||||
}
|
||||
//}
|
||||
}
|
||||
|
||||
destroy() {
|
||||
@@ -138,6 +136,7 @@ export default class DeviceController {
|
||||
document.removeEventListener('beforeunload', this.handleBeforeUnload);
|
||||
|
||||
this.release();
|
||||
this.levae();
|
||||
|
||||
this.unSubMessage?.();
|
||||
this.unSubStatus?.();
|
||||
@@ -172,6 +171,7 @@ export default class DeviceController {
|
||||
wsManager.close();
|
||||
|
||||
try {
|
||||
console.log('Connecting to', this.wsUrl);
|
||||
wsManager.connect(sn, this.wsUrl);
|
||||
this.connectedDeviceSn = sn;
|
||||
|
||||
@@ -182,11 +182,42 @@ export default class DeviceController {
|
||||
}
|
||||
}
|
||||
|
||||
async switchDevice(device) {
|
||||
if (!device) return;
|
||||
if (!device.serialNumber) return;
|
||||
|
||||
const newSn = device.serialNumber;
|
||||
|
||||
if (this.connectedDeviceSn === newSn && this.wsStatus === 'connected') {
|
||||
console.log('switchDevice: same device, skip');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('Connecting to switchDevice: switching to', newSn);
|
||||
|
||||
this.currentDevice = device;
|
||||
this.connectedDeviceSn = newSn;
|
||||
this.wsDeviceInfo = {};
|
||||
this.locationInfo = {};
|
||||
this.isControlled = false;
|
||||
this.onControlStatusChange?.(false);
|
||||
|
||||
wsManager.send({
|
||||
type: 'AUTH',
|
||||
token: this.userInfo.token,
|
||||
userName: this.userInfo.username,
|
||||
deviceId: newSn,
|
||||
});
|
||||
|
||||
await this.getControl(newSn);
|
||||
}
|
||||
|
||||
async getControl(serialnum) {
|
||||
try {
|
||||
const res = await getJurisdiction({
|
||||
platform: 'web',
|
||||
deviceId: serialnum,
|
||||
token: this.userInfo.token,
|
||||
});
|
||||
|
||||
this.isControlled = res?.code === 200 && res?.data?.remoteControl === true;
|
||||
@@ -200,10 +231,7 @@ export default class DeviceController {
|
||||
|
||||
requestControl() {
|
||||
if (this.isRequestControl) return;
|
||||
if (!this.hasControlRight) {
|
||||
this.messageApi.warning(i18n.t('devices.noneControl'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
this.isRequestControl = true;
|
||||
this.onRequestControlStatusChange?.(true);
|
||||
@@ -246,7 +274,7 @@ export default class DeviceController {
|
||||
}
|
||||
|
||||
handleWsMessage(msg) {
|
||||
console.log('?? 收到消息:', msg);
|
||||
console.log('WS message:', msg);
|
||||
switch (msg.type) {
|
||||
case 'deviceInfo': {
|
||||
const data = {};
|
||||
@@ -280,7 +308,7 @@ export default class DeviceController {
|
||||
this.isRequestControl = false;
|
||||
this.onRequestControlStatusChange?.(false);
|
||||
|
||||
if (msg.success === false || msg?.respond?.switchResult === false) {
|
||||
if (msg?.success === false || msg?.respond?.switchResult === false) {
|
||||
this.isControlled = false;
|
||||
this.onControlStatusChange?.(false);
|
||||
this.messageApi.error({ key: 'control', content: msg.message || i18n.t('devices.controlFailed') });
|
||||
@@ -294,15 +322,14 @@ export default class DeviceController {
|
||||
case "device_task_change": {
|
||||
if (msg.deviceId != this.connectedDeviceSn) return;
|
||||
this.onIsFinished?.(true);
|
||||
console.log('?? 任务结束收到,', msg);
|
||||
|
||||
console.log('task change:', msg);
|
||||
break;
|
||||
}
|
||||
case "device_task_arrive_point": {
|
||||
if (msg.deviceId != this.connectedDeviceSn) return;
|
||||
|
||||
this.handleArrivedPoint?.(msg.entity);
|
||||
console.log('?? 收到抵达,', msg);
|
||||
|
||||
console.log('arrive point:', msg);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'switchPermission': {
|
||||
|
||||
@@ -659,6 +659,8 @@ const en = {
|
||||
optimize: 'Optimize',
|
||||
optionalEmpty: 'Optional Empty',
|
||||
otherWebControlling: 'Other Web Controlling',
|
||||
platformRequestControl: "{{platform}} platformRequestControl",
|
||||
|
||||
outDock: 'Out Dock',
|
||||
outdoor: 'Outdoor',
|
||||
panorama: 'Panorama',
|
||||
@@ -1285,9 +1287,10 @@ const en = {
|
||||
|
||||
failedFixed22: '权限校验 Failed Fixed2',
|
||||
|
||||
noneControl: 'None Control 权限',
|
||||
noneControl: 'None Control jurisdiction',
|
||||
|
||||
fixed2ControlFixed2: 'Fixed2 Control 请求 超时 Fixed2',
|
||||
controlFailed:"请求被拒绝",
|
||||
},
|
||||
video: {
|
||||
onlineVideo: 'Online Video',
|
||||
|
||||
@@ -1302,6 +1302,9 @@ const zh = {
|
||||
cancel: "取消",
|
||||
UnitsRobot: "台",
|
||||
UnitsDrone: "架",
|
||||
platformRequestControl: "{{platform}} 正在请求控制权限",
|
||||
controlFailed: "controlFailed",
|
||||
|
||||
},
|
||||
video: {
|
||||
onlineVideo: '在线视频',
|
||||
|
||||
Reference in New Issue
Block a user