测试优化实时监控中心,接收全部的机器信息 ,优化显示图标的问题,优化且任务的时候显示 规划路径
This commit is contained in:
@@ -5,7 +5,7 @@ import { Badge, message } from "antd";
|
||||
AgoraRTC.setLogLevel(2); // 2=只输出warning/error,屏蔽debug日志
|
||||
|
||||
const APP_ID = "7887b4db6033410d9ac3264b40ca9c1a";
|
||||
const TOKEN = "007eJxTYDhxaLPL8617mtfHFi2tfJhQNSXh5vNJnqvu8b/3PenKKdumwGBuYWGeZJKSZGZgbGxiaJBimZhsbGRmkmRikJxomWyYuHHFiqyGQEaGB6+fMDEyQCCIz81gYqhbklGUmphrYMTAAAAyfSTX";
|
||||
const TOKEN = "007eJxTYPh6QSj59sUzD3mPNm64dK9n/n1PP64UzrhzdwUeqMUKv1BVYDC3sDBPMklJMjMwNjYxNEixTEw2NjIzSTIxSE60TDZMlOdYldUQyMiQXKbEwAiFID43g4mhbklGUWpiroExAwMAjGAhHQ==";
|
||||
|
||||
export default function AgrtcRoomRobot({ CHANNEL }: { CHANNEL: string }) {
|
||||
const [uid] = useState(() => Number(Math.floor(Math.random() * 100000)));
|
||||
@@ -22,7 +22,7 @@ export default function AgrtcRoomRobot({ CHANNEL }: { CHANNEL: string }) {
|
||||
|
||||
useEffect(() => {
|
||||
const client = AgoraRTC.createClient({
|
||||
mode: "rtc",
|
||||
mode: "live",
|
||||
codec: "h264",
|
||||
});
|
||||
clientRef.current = client;
|
||||
@@ -145,7 +145,7 @@ export default function AgrtcRoomRobot({ CHANNEL }: { CHANNEL: string }) {
|
||||
|
||||
const onUserUnpublished = (user: any, mediaType: "video" | "audio") => {
|
||||
if (isUnmounted) return;
|
||||
console.log("user-unpublished", user.uid, mediaType);
|
||||
console.log("user-unpublished", user.uid, mediaType, subscribedUidRef.current);
|
||||
if (mediaType === "video" && subscribedUidRef.current === user.uid) {
|
||||
if (unpublishTimer) clearTimeout(unpublishTimer);
|
||||
// 延迟2s清空,短闪断自动恢复不黑屏
|
||||
|
||||
@@ -795,7 +795,6 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine,
|
||||
};
|
||||
|
||||
function showCoordinateTooltip(tooltip, position, t, lon, lat, robot) {
|
||||
console.log('showCoordinateTooltip', position, lon, lat, robot);
|
||||
tooltip.style.display = "block";
|
||||
tooltip.style.left = position.x + 12 + "px";
|
||||
tooltip.style.top = position.y + 12 + "px";
|
||||
@@ -1309,7 +1308,7 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine,
|
||||
if (focusLocation.type === "plane" || focusLocation.type === "drone") {
|
||||
image = planeImg;
|
||||
} else if (focusLocation.type === "robot") {
|
||||
image = droneInspectImg;
|
||||
image = carImg;
|
||||
} else if (focusLocation.type === "mower") {
|
||||
image = carImg;
|
||||
}
|
||||
@@ -1739,7 +1738,6 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine,
|
||||
// ==============================
|
||||
|
||||
// 光圈颜色:工作中蓝 / 空闲绿 / 离线红
|
||||
const haloColor = getDeviceHaloColor(device);
|
||||
|
||||
let entity = entityMapRef.current.get(id);
|
||||
|
||||
@@ -1781,6 +1779,7 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine,
|
||||
// 设备底部状态光晕:比机器稍大一点
|
||||
// ==============================
|
||||
const haloId = `${id}_halo`;
|
||||
|
||||
const haloColor = getDeviceHaloColor(device);
|
||||
|
||||
// 尺寸可根据设备类型微调:车辆类稍大,IOT/相机类稍小
|
||||
|
||||
@@ -1236,7 +1236,7 @@ export default function DeviceStatusPage() {
|
||||
) : mowerView === 'front' ? (
|
||||
<div style={{ position: 'relative', width: '100%', height: '100%' }}>
|
||||
{/*<AgrtcRoomRobot CHANNEL={device?.serialNumber || ''} />*/}
|
||||
<AgrtcRoomRobot CHANNEL={"41-thream02"} />
|
||||
<AgrtcRoomRobot CHANNEL={"41-thream03"} />
|
||||
|
||||
</div>
|
||||
) : (
|
||||
|
||||
@@ -241,6 +241,7 @@ export default function RobotTaskPage() {
|
||||
const handlePoolTaskClick = (record) => {
|
||||
setRunningPoolTask(record);
|
||||
loadTaskPath(record.id);
|
||||
fetchRouteDetail(record.id);
|
||||
|
||||
// 查找对应的设备
|
||||
const targetDevice = devices?.find(d => d.serialNumber === record.deviceId);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import throttle from 'lodash/throttle';
|
||||
import dayjs from 'dayjs';
|
||||
import { wsManager } from '../WebSocketManager.ts';
|
||||
import {
|
||||
getJurisdiction,
|
||||
@@ -9,9 +10,10 @@ import envConfig from '../../../env';
|
||||
import i18n from '../../i18n';
|
||||
|
||||
export default class DeviceController {
|
||||
constructor(userInfo, messageApi) {
|
||||
constructor(userInfo, messageApi, siteId) {
|
||||
this.userInfo = userInfo;
|
||||
this.messageApi = messageApi;
|
||||
this.siteId = siteId;
|
||||
|
||||
this.wsUrl =
|
||||
envConfig.WS_URL || 'wss://ri.satabot.com/ws/';
|
||||
@@ -44,6 +46,12 @@ export default class DeviceController {
|
||||
this.onIsFinished = null;
|
||||
this.handleArrivedPoint = null;
|
||||
|
||||
// 全量设备实时状态(mapPush)连接
|
||||
this.mapPushWs = null;
|
||||
this.mapPushManualClose = false;
|
||||
this.mapPushReconnectTimer = null;
|
||||
this.onMapPush = null;
|
||||
|
||||
this.handleWsMessage = this.handleWsMessage.bind(this);
|
||||
this.handleWsStatus = this.handleWsStatus.bind(this);
|
||||
this.handleVisibilityChange = this.handleVisibilityChange.bind(this);
|
||||
@@ -78,6 +86,98 @@ export default class DeviceController {
|
||||
this.onControlStatusChange?.(false);
|
||||
}
|
||||
|
||||
// ============ 全量设备实时状态(mapPush)连接 ============
|
||||
// 进入实时监控页时连接一次,接收后端广播的所有设备状态;退出页面时调用 closeMapPush 断开。
|
||||
initMapPush(onMapPush) {
|
||||
console.log('DeviceController initMapPush: 连接全量设备 WebSocket');
|
||||
this.onMapPush = onMapPush || null;
|
||||
this.mapPushManualClose = false;
|
||||
this.connectMapPush();
|
||||
}
|
||||
|
||||
connectMapPush() {
|
||||
if (this.mapPushWs && this.mapPushWs.readyState === WebSocket.OPEN) return;
|
||||
try {
|
||||
this.mapPushManualClose = false;
|
||||
const ws = new WebSocket(this.wsUrl);
|
||||
this.mapPushWs = ws;
|
||||
|
||||
ws.onopen = () => {
|
||||
ws.send(JSON.stringify({
|
||||
type: 'mapPush',
|
||||
token: this.userInfo?.token,
|
||||
siteId: this.siteId,
|
||||
userName: this.userInfo?.username,
|
||||
deviceId: '',
|
||||
}));
|
||||
};
|
||||
|
||||
ws.onmessage = (event) => {
|
||||
let msg = null;
|
||||
try { msg = JSON.parse(event.data); } catch (e) { return; }
|
||||
|
||||
console.log('[DeviceController] 收到全量设备状态 初始状态', msg);
|
||||
if (msg?.type !== 'mapPush') return;
|
||||
const list = Array.isArray(msg?.data) && msg.data.length
|
||||
? msg.data
|
||||
: [];
|
||||
if (!list.length) return;
|
||||
|
||||
const next = {};
|
||||
list.forEach((it) => {
|
||||
const key = String(it?.id ?? '');
|
||||
if (!key) return;
|
||||
next[key] = {
|
||||
id: key,
|
||||
lng: String(it?.lng ?? it?.lon ?? it?.longitude),
|
||||
lat: String(it?.lat ?? it?.latitude),
|
||||
yaw: it?.y,
|
||||
fix: it?.f !== undefined ? String(it.f) : undefined,
|
||||
fixStatus: it?.f !== undefined ? String(it.f) : undefined,
|
||||
altitude: it?.a !== undefined ? String(it.a) : undefined,
|
||||
status: it?.s ?? it?.status,
|
||||
taskId: it?.t ?? it?.taskId ?? null,
|
||||
battery: it?.b !== undefined ? Number(it.b) : (it?.batteryLevel !== undefined ? Number(it.batteryLevel) : undefined),
|
||||
hasAlarm: !!it?.ha || !!it?.hasAlarm,
|
||||
updateTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
|
||||
};
|
||||
});
|
||||
console.log('[DeviceController] 收到全量设备状态', next);
|
||||
this.onMapPush?.(next);
|
||||
};
|
||||
|
||||
ws.onerror = () => {
|
||||
console.error('[DeviceController] mapPush WebSocket 错误');
|
||||
};
|
||||
|
||||
ws.onclose = () => {
|
||||
if (this.mapPushWs === ws) this.mapPushWs = null;
|
||||
if (!this.mapPushManualClose) {
|
||||
this.mapPushReconnectTimer = setTimeout(() => this.connectMapPush(), 5000);
|
||||
}
|
||||
};
|
||||
} catch (err) {
|
||||
console.error('[DeviceController] 连接全量设备 WebSocket 失败', err);
|
||||
this.mapPushReconnectTimer = setTimeout(() => this.connectMapPush(), 5000);
|
||||
}
|
||||
}
|
||||
|
||||
closeMapPush() {
|
||||
this.mapPushManualClose = true;
|
||||
if (this.mapPushReconnectTimer) {
|
||||
clearTimeout(this.mapPushReconnectTimer);
|
||||
this.mapPushReconnectTimer = null;
|
||||
}
|
||||
if (this.mapPushWs) {
|
||||
this.mapPushWs.onopen = null;
|
||||
this.mapPushWs.onmessage = null;
|
||||
this.mapPushWs.onerror = null;
|
||||
this.mapPushWs.onclose = null;
|
||||
try { this.mapPushWs.close(); } catch (e) { /* ignore */ }
|
||||
this.mapPushWs = null;
|
||||
}
|
||||
}
|
||||
|
||||
handleVisibilityChange() {
|
||||
if (document.visibilityState === 'hidden') {
|
||||
console.log('Page hidden, release control');
|
||||
|
||||
@@ -1193,7 +1193,7 @@ const en = {
|
||||
|
||||
s24: 'Recharging',
|
||||
|
||||
fixed2StopFixed2VideoFixed2: 'Fixed2 Stop Fixed2 Video Fixed2',
|
||||
fixed2StopFixed2VideoFixed2: ' Stop Video ',
|
||||
|
||||
s29: 'Emergency Stop',
|
||||
fixed2AiActions: 'AI Analysis and Actions Suggestions',
|
||||
|
||||
@@ -21,7 +21,6 @@ import {
|
||||
NodeIndexOutlined, SaveOutlined, MaximizeOutlined, ReloadOutlined
|
||||
} from '@ant-design/icons';
|
||||
import { Line, Bar, Pie } from '@ant-design/charts';
|
||||
import bgMain from '../assets/images/bgmain.jpg';
|
||||
import { UniversalBarChart, UniversalLineChart, UniversalPieChart } from '../components/recharts';
|
||||
import CesiumMap from '../components/Map';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -106,7 +105,6 @@ const RealTimeMonitorPage = () => {
|
||||
const [filterStatus, setFilterStatus] = useState('all');
|
||||
const [filterType, setFilterType] = useState('all');
|
||||
const [searchName, setSearchName] = useState('');
|
||||
const [mapViewMode, setMapViewMode] = useState('all');
|
||||
|
||||
const cardStyle = { borderRadius: 12, border: '1px solid #f0f0f0', boxShadow: '0 2px 8px rgba(0,0,0,0.04)' };
|
||||
|
||||
@@ -122,28 +120,16 @@ const RealTimeMonitorPage = () => {
|
||||
const [listPage, setListPage] = useState(1);
|
||||
const [listPageSize, setListPageSize] = useState(8);
|
||||
|
||||
// ========= WebSocket + 地图交互相关 =========
|
||||
const controllerRef = useRef(null);
|
||||
const isFirstConnect = useRef(true);
|
||||
// ========= WebSocket(全量设备 mapPush)+ 地图交互相关 =========
|
||||
const [focusLocation, setFocusLocation] = useState(null);
|
||||
const [execute, setExecute] = useState(false);
|
||||
const [excuteStatus, setExcuteStatus] = useState('stop');
|
||||
const [points, setPoints] = useState([]);
|
||||
const [polyPoints, setPolyPoints] = useState([]);
|
||||
const [routeMode, setRouteMode] = useState('');
|
||||
const [clearNavLine, setClearNavLine] = useState(0);
|
||||
const [clearPlanLine, setClearPlanLine] = useState(0);
|
||||
const [arrivedPoint, setArrivedPoint] = useState(null);
|
||||
const [robotRealtimePosition, setRobotRealtimePosition] = useState(null);
|
||||
const runningRealtimePosition = useMemo(() => {
|
||||
return {
|
||||
lon: robotRealtimePosition?.longitude || robotRealtimePosition?.lon || 0,
|
||||
lat: robotRealtimePosition?.latitude || robotRealtimePosition?.lat || 0,
|
||||
heading: robotRealtimePosition?.yaw || robotRealtimePosition?.heading || 0,
|
||||
};
|
||||
}, [robotRealtimePosition]);
|
||||
const [realtimeMap, setRealtimeMap] = useState<any>({});
|
||||
const [runningPoolTask, setRunningPoolTask] = useState(null);
|
||||
const [proPoints, setProPoints] = useState({});//规划点
|
||||
|
||||
// ========= UI 状态 =========
|
||||
const [selectedDevice, setSelectedDevice] = useState(null);
|
||||
@@ -168,62 +154,22 @@ const RealTimeMonitorPage = () => {
|
||||
|
||||
|
||||
// ============================================================
|
||||
// DeviceController 初始化(仅一次)
|
||||
// 全量设备状态 WebSocket 连接(mapPush:进入页面自动连接,退出页面自动断开)
|
||||
// 连接逻辑统一封装在 DeviceController 中
|
||||
// ============================================================
|
||||
const controllerRef = useRef<any>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!userInfo) return;
|
||||
const controller = new DeviceController(userInfo, messageApi);
|
||||
|
||||
controller.onLocationInfoUpdate = (pos) => {
|
||||
setRobotRealtimePosition(pos);
|
||||
};
|
||||
controller.onIsFinished = handleFinish;
|
||||
|
||||
|
||||
controller.onRoutePost = (msg) => {
|
||||
const { taskId, data } = msg;
|
||||
//const matchedTask = taskPoolList.find(task => String(task.id) === String(taskId));
|
||||
//const sameAsSelected = runningPoolTask && String(runningPoolTask.id) === String(taskId);
|
||||
//if ((!runningPoolTask && matchedTask) || sameAsSelected) {
|
||||
// if (!runningPoolTask && matchedTask) setRunningPoolTask(matchedTask);
|
||||
console.log('[RTMonitor] route_post:', data)
|
||||
setProPoints(msg);
|
||||
//}
|
||||
};
|
||||
|
||||
controller.init();
|
||||
controllerRef.current = controller;
|
||||
|
||||
return () => {
|
||||
if (controllerRef.current) {
|
||||
controllerRef.current.destroy?.();
|
||||
}
|
||||
controllerRef.current = null;
|
||||
};
|
||||
// eslint-disable-next-line
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
//console.log('runningPoolTask?.id, proPoints?.taskId', proPoints)
|
||||
if (!runningPoolTask?.id || !proPoints?.taskId) return;
|
||||
if (runningPoolTask?.id == proPoints?.taskId) {
|
||||
console.log('1111111111', proPoints?.data);
|
||||
setPoints(proPoints?.data);
|
||||
if (!userInfo || !stationId) return;
|
||||
if (!controllerRef.current) {
|
||||
controllerRef.current = new DeviceController(userInfo, messageApi, stationId);
|
||||
}
|
||||
|
||||
|
||||
}, [runningPoolTask, proPoints]);
|
||||
const handleFinish = (data) => {
|
||||
setExecute(false);
|
||||
|
||||
utils.message.success(t('devices.fixed2TaskSuspended2Fixed2CompletedFixed2'));
|
||||
setPoints([]);
|
||||
setPolyPoints([]);
|
||||
setRunningPoolTask(null);
|
||||
setExcuteStatus('stop');
|
||||
};
|
||||
// ============================================================
|
||||
// WebSocket 连接
|
||||
|
||||
const controller = controllerRef.current;
|
||||
controller.initMapPush((rtMap: any) => setRealtimeMap(rtMap || {}));
|
||||
return () => {
|
||||
controller.closeMapPush();
|
||||
};
|
||||
}, [userInfo, stationId]);
|
||||
|
||||
// ============================================================
|
||||
// 拉取基础数据
|
||||
@@ -301,7 +247,6 @@ const RealTimeMonitorPage = () => {
|
||||
}
|
||||
};
|
||||
|
||||
// 加载路线详情(预设路径点击执行时)
|
||||
// 加载路线详情(预设路径点击执行时)
|
||||
const fetchRouteDetailAndShow = async (routeId, routeData) => {
|
||||
try {
|
||||
@@ -403,21 +348,79 @@ const RealTimeMonitorPage = () => {
|
||||
return [...robots, ...planeDeviceRows, ...mocks];
|
||||
}, [devices, planeDeviceRows, currentStation]);
|
||||
|
||||
const mapDevices = useMemo(() => {
|
||||
const all = [...devices, ...planeDeviceRows];
|
||||
if (mapViewMode === 'selected' && (selectedDevice?.serialNumber || selectedDevice?.device_sn)) {
|
||||
const sn = selectedDevice?.serialNumber || selectedDevice?.device_sn;
|
||||
return all
|
||||
.filter(d => (d.serialNumber == sn) || (d.device_sn == sn))
|
||||
|
||||
const pickRealtimeRecord = (rtMap: any, d: any) => {
|
||||
const candidates = [d?.serialNumber, d?.device_sn, d?.sn, d?.id, d?.deviceId];
|
||||
for (const key of candidates) {
|
||||
if (key == null || String(key) === '') continue;
|
||||
const hit = rtMap[String(key)];
|
||||
if (hit) return hit;
|
||||
}
|
||||
return all
|
||||
.filter(d => (d?.lastRunningStatus?.latitude && d?.lastRunningStatus?.longitude) || (d?.locationMessage?.latitude && d?.locationMessage?.longitude) || (d?.longitude && d?.latitude));
|
||||
return null;
|
||||
};
|
||||
|
||||
}, [devices, planeDeviceRows, mapViewMode, selectedDevice]);
|
||||
const realtimeMapList = useMemo(() => Object.values(realtimeMap || {}), [realtimeMap]);
|
||||
|
||||
const deviceListWithRT = useMemo(() => {
|
||||
return (devices || []).map((d: any) => {
|
||||
const rt = pickRealtimeRecord(realtimeMap, d);
|
||||
if (!rt) return d;
|
||||
const lon = rt.lng;
|
||||
const lat = rt.lat;
|
||||
//if (!isFinite(lon) || !isFinite(lat) || (lon === 0 && lat === 0)) return d;
|
||||
return {
|
||||
...d,
|
||||
longitude: lon,
|
||||
latitude: lat,
|
||||
heading: rt.yaw,
|
||||
batteryLevel: rt.battery,
|
||||
feStatus: rt.status,
|
||||
taskId: rt.taskId,
|
||||
updateTime: rt.updateTime || d.updateTime,
|
||||
raw: { ...(d.raw || d), heading: rt.yaw, batteryLevel: rt.b, longitude: lon, latitude: lat },
|
||||
};
|
||||
});
|
||||
}, [devices, realtimeMap]);
|
||||
|
||||
const mapDevices = useMemo(() => {
|
||||
const base = [...deviceListWithRT];
|
||||
const knownKeys = new Set();
|
||||
base.forEach((d: any) => {
|
||||
const k = d?.serialNumber ?? d?.device_sn ?? d?.id;
|
||||
if (k != null && k !== '') knownKeys.add(String(k));
|
||||
});
|
||||
const extras = realtimeMapList
|
||||
.filter((rt: any) => isFinite(rt.lng) && isFinite(rt.lat) && !(rt.lng === 0 && rt.lat === 0) && !knownKeys.has(String(rt.id)))
|
||||
.map((rt: any) => ({
|
||||
id: rt.id,
|
||||
name: rt.name || rt.alias || `设备-${String(rt.id).slice(-4)}`,
|
||||
serialNumber: rt.id,
|
||||
deviceType: 'ROBOT',
|
||||
type: 'ROBOT',
|
||||
category: 'robot',
|
||||
typeText: '机器人',
|
||||
status: rt.status,
|
||||
onlineStatus: rt.status,
|
||||
longitude: rt.lng,
|
||||
latitude: rt.lat,
|
||||
heading: rt.yaw,
|
||||
batteryLevel: rt.battery,
|
||||
updateTime: rt.updateTime,
|
||||
feStatus: rt.feStatus,
|
||||
taskId: rt.t,
|
||||
isRealtimeOnly: true,
|
||||
raw: { ...rt },
|
||||
}));
|
||||
const all = [...base, ...extras];
|
||||
|
||||
return all.filter((d: any) =>
|
||||
(d?.lastRunningStatus?.latitude && d?.lastRunningStatus?.longitude) ||
|
||||
(d?.locationMessage?.latitude && d?.locationMessage?.longitude) ||
|
||||
(d?.longitude && d?.latitude)
|
||||
);
|
||||
}, [deviceListWithRT, planeDeviceRows, realtimeMapList, selectedDevice]);
|
||||
|
||||
// ============================================================
|
||||
// 点击设备:WebSocket 连接 + 地图定位
|
||||
// 点击设备:仅打开侧边栏 + 地图定位(不再连接单台设备 WebSocket)
|
||||
// ============================================================
|
||||
const handleDeviceClick = (robot) => {
|
||||
console.log('[RTMonitor] 点击设备:', robot);
|
||||
@@ -445,13 +448,11 @@ const RealTimeMonitorPage = () => {
|
||||
setMqttDrone(null);
|
||||
return;
|
||||
}
|
||||
setExecute(true);
|
||||
setDeviceDrawerOpen(true);
|
||||
|
||||
|
||||
// ---- 无人机:先断开 WebSocket,再订阅该无人机 MQTT 实时数据 ----
|
||||
if (isDroneDevice) {
|
||||
controllerRef.current?.disconnect?.();
|
||||
setMqttDrone(dev);
|
||||
if (activeDroneTask?.device_sn !== dev.device_sn) {
|
||||
setDroneReturningHome(false);
|
||||
@@ -465,22 +466,10 @@ const RealTimeMonitorPage = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
// ---- 机器人:先取消 MQTT 订阅,再连接 WebSocket ----
|
||||
// ---- 机器人:仅打开侧边栏,不做实时 WebSocket 连接 ----
|
||||
setMqttDrone(null);
|
||||
if (dev.serialNumber && controllerRef.current) {
|
||||
const isSameDevice = controllerRef.current.connectedDeviceSn === dev.serialNumber;
|
||||
if (isSameDevice) {
|
||||
console.log('[RTMonitor] 已连接相同设备,跳过');
|
||||
} else if (isFirstConnect.current) {
|
||||
isFirstConnect.current = false;
|
||||
controllerRef.current.connectDevice(dev);
|
||||
} else {
|
||||
controllerRef.current.switchDevice(dev);
|
||||
}
|
||||
setClearNavLine(c => c + 1);
|
||||
}
|
||||
|
||||
// ---- 刷新该设备任务数据,并在地图显示该设备正在执行任务的路线和区域 ----
|
||||
// ---- 刷新该设备任务数据(仅更新侧边栏当前任务,路线点击时才绘制规划路径) ----
|
||||
fetchTaskPoolList(dev.serialNumber).then(poolRows => {
|
||||
const rows = poolRows || [];
|
||||
const runningTask = rows.find(t =>
|
||||
@@ -490,9 +479,9 @@ const RealTimeMonitorPage = () => {
|
||||
console.log('[RTMonitor] 设备正在执行的任务:', runningTask);
|
||||
if (runningTask) {
|
||||
setRunningPoolTask(runningTask);
|
||||
setExcuteStatus(runningTask.taskStaus === 'EXECUTING' ? 'running' : 'pause');
|
||||
loadTaskPath(runningTask.id);
|
||||
fetchDetailTaskPath(runningTask.id);
|
||||
setExcuteStatus(runningTask.taskStaus === 'EXECUTING' ? 'running' : 'pause');
|
||||
} else {
|
||||
setRunningPoolTask(null);
|
||||
setPoints([]);
|
||||
@@ -820,7 +809,6 @@ const RealTimeMonitorPage = () => {
|
||||
const newTask = [...rest].sort((a, b) => new Date(b.createTime) - new Date(a.createTime))[0];
|
||||
if (newTask) {
|
||||
setRunningPoolTask(newTask);
|
||||
await loadTaskPath(newTask.id);
|
||||
setExcuteStatus('running');
|
||||
}
|
||||
} else {
|
||||
@@ -907,7 +895,7 @@ const RealTimeMonitorPage = () => {
|
||||
<Text type="secondary" style={{ fontSize: '12px' }}>{t('page.deviceFixed2StatusAndGeneration')} · {currentStation?.siteName || t('common.loading')}</Text>
|
||||
</div>
|
||||
<Space>
|
||||
<Segmented
|
||||
{/*<Segmented
|
||||
value={mapViewMode}
|
||||
onChange={setMapViewMode}
|
||||
size="small"
|
||||
@@ -915,7 +903,7 @@ const RealTimeMonitorPage = () => {
|
||||
{ label: t('page.showAllDevices'), value: 'all' },
|
||||
{ label: t('page.showOnlySelected'), value: 'selected' }
|
||||
]}
|
||||
/>
|
||||
/>*/}
|
||||
<Button type="text" icon={<SyncOutlined spin={loading} />} onClick={handleRefresh} style={{ color: '#1677ff' }}>
|
||||
{t('common.refresh')}
|
||||
</Button>
|
||||
@@ -971,11 +959,7 @@ const RealTimeMonitorPage = () => {
|
||||
onCameraClick={handleDeviceClick}
|
||||
points={points}
|
||||
focusLocation={focusLocation}
|
||||
realtimePosition={runningRealtimePosition}
|
||||
polyPoints={polyPoints}
|
||||
realtimeType="robot"
|
||||
drawRealTime={execute}
|
||||
finishPoint={arrivedPoint}
|
||||
ifClearNavLine={clearNavLine}
|
||||
ifClearPlanLine={clearPlanLine}
|
||||
routeMode={routeMode}
|
||||
@@ -988,6 +972,21 @@ const RealTimeMonitorPage = () => {
|
||||
{t('page.foundDevices', { count: mapDevices.length })}
|
||||
</Space>
|
||||
</div>
|
||||
{/*图例:工作中蓝 / 空闲绿 / 离线红
|
||||
<div style={{ background: 'rgba(0,0,0,0.55)', color: 'white', padding: '6px 12px', borderRadius: 8, fontSize: 12, backdropFilter: 'blur(4px)', display: 'flex', flexDirection: 'column', gap: 4 }}>
|
||||
<Space size={10}>
|
||||
<span style={{ display: 'inline-block', width: 10, height: 10, borderRadius: '50%', background: '#1677ff' }} />
|
||||
{t('devices.s59')}
|
||||
</Space>
|
||||
<Space size={10}>
|
||||
<span style={{ display: 'inline-block', width: 10, height: 10, borderRadius: '50%', background: '#52c41a' }} />
|
||||
{t('devices.idle')}
|
||||
</Space>
|
||||
<Space size={10}>
|
||||
<span style={{ display: 'inline-block', width: 10, height: 10, borderRadius: '50%', background: '#ff4d4f' }} />
|
||||
{t('devices.offline')}
|
||||
</Space>
|
||||
</div>*/}
|
||||
{runningPoolTask && (
|
||||
<div style={{ background: excuteStatus === 'running' ? 'rgba(82,196,26,0.95)' : 'rgba(250,173,20,0.95)', color: 'white', padding: '8px 12px', borderRadius: 8, fontSize: 12, backdropFilter: 'blur(4px)', maxWidth: 280 }}>
|
||||
<div style={{ fontWeight: 600, marginBottom: 4, display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||
@@ -999,15 +998,7 @@ const RealTimeMonitorPage = () => {
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{robotRealtimePosition && (
|
||||
<div style={{ background: 'rgba(22,119,255,0.9)', color: 'white', padding: '8px 12px', borderRadius: 8, fontSize: 11, backdropFilter: 'blur(4px)' }}>
|
||||
<div>📡 {t('devices.realtimePosition')}</div>
|
||||
<div>Lng: {Number(robotRealtimePosition.longitude || robotRealtimePosition.lng || 0).toFixed(5)}</div>
|
||||
<div>Lat: {Number(robotRealtimePosition.latitude || robotRealtimePosition.lat || 0).toFixed(5)}</div>
|
||||
<div>yaw: {Number(robotRealtimePosition.heading || 0).toFixed(5)}</div>
|
||||
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
@@ -1298,7 +1289,7 @@ const RealTimeMonitorPage = () => {
|
||||
</span>
|
||||
}
|
||||
extra={
|
||||
<Space size={4}>
|
||||
<Space size={4} onClick={(e) => e.stopPropagation()}>
|
||||
<Tooltip title={t('devices.pause')}>
|
||||
<Button
|
||||
size="small"
|
||||
@@ -1332,7 +1323,14 @@ const RealTimeMonitorPage = () => {
|
||||
style={{
|
||||
borderRadius: 10,
|
||||
border: `1px solid ${runningPoolTask ? (excuteStatus === 'running' ? 'rgba(82,196,26,0.3)' : 'rgba(250,173,20,0.3)') : '#f0f0f0'}`,
|
||||
background: runningPoolTask ? (excuteStatus === 'running' ? 'rgba(82,196,26,0.03)' : 'rgba(250,173,20,0.03)') : '#fff'
|
||||
background: runningPoolTask ? (excuteStatus === 'running' ? 'rgba(82,196,26,0.03)' : 'rgba(250,173,20,0.03)') : '#fff',
|
||||
cursor: 'pointer'
|
||||
}}
|
||||
onClick={() => {
|
||||
const task = runningPoolTask || (deviceTasks && deviceTasks.current);
|
||||
if (task?.id) {
|
||||
loadTaskPath(task.id); fetchDetailTaskPath(task.id);
|
||||
};
|
||||
}}
|
||||
>
|
||||
{(runningPoolTask || deviceTasks.current) ? (
|
||||
@@ -1415,7 +1413,6 @@ const RealTimeMonitorPage = () => {
|
||||
<div
|
||||
key={route.id || route.workName}
|
||||
onClick={() => {
|
||||
setExecute(true);
|
||||
fetchRouteDetailAndShow(route.id, route);
|
||||
}}
|
||||
style={{
|
||||
|
||||
Reference in New Issue
Block a user