Browse Source

优化测量配置碳

feature/rail
LiLongLong 5 months ago
parent
commit
e3fbae92b2
  1. 2
      .env
  2. 2
      package.json
  3. 18
      src/components/SideMenu.tsx
  4. 331
      src/pages/measure/components/MeasureAction.tsx
  5. 4
      src/pages/measure/components/MeasureConfig.tsx

2
.env

@ -1 +1 @@
REACT_APP_WS_URL=192.168.1.200:8080/ws
REACT_APP_WS_URL=192.168.1.201:8080/ws

2
package.json

@ -2,7 +2,7 @@
"name": "outline",
"version": "0.1.0",
"private": true,
"proxy": "http://192.168.1.200:8080",
"proxy": "http://192.168.1.201:8080",
"dependencies": {
"@babel/core": "^7.16.0",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.3",

18
src/components/SideMenu.tsx

@ -25,15 +25,15 @@ const items: MenuItem[] = [
}
],
},
{
key: "rail",
label: "轨形管理",
icon: <img src={icon_substrate} alt=""/>,
children: [{
key: '/rail/config',
label: '轨形配置'
}]
}
// {
// key: "rail",
// label: "轨形管理",
// icon: <img src={icon_substrate} alt=""/>,
// children: [{
// key: '/rail/config',
// label: '轨形配置'
// }]
// }
];
export default function SideMenu() {

331
src/pages/measure/components/MeasureAction.tsx

@ -1,6 +1,5 @@
import React from 'react';
import React, { useState, useEffect, useRef } from 'react';
import { Button, Checkbox, CheckboxProps, message, Switch } from "antd";
import { useEffect, useRef, useState } from "react";
import { useNavigate } from "react-router";
import {
fetchAnalysisReport,
@ -10,7 +9,8 @@ import {
} from "../../../services/measure/analysis";
import { createWebSocket, sharedWsUrl } from "../../../services/socket";
import { switchMeasureAfterSave } from "../../../store/features/contextSlice";
import { AnalysisReport } from "../../../services/measure/type";
import { AnalysisReport, AnalyzeAngle } from "../../../services/measure/type";
import { MeasureState, taskStatusDescMap } from "../../../services/wsTypes";
import { useAppDispatch, useAppSelector } from "../../../utils/hooks";
import Gr_round from '../../../assets/green_round.svg';
import Bl_round from '../../../assets/blue_round.svg';
@ -20,103 +20,64 @@ import MeasurementCanvas, {
MeasurementCanvasRef,
} from "./konva/MeasurementCanvas";
// 创建 websocket 客户端
const wsClient = createWebSocket(sharedWsUrl);
export default function MeasureAction() {
const dispatch = useAppDispatch();
const navigate = useNavigate();
// MeasurementCanvas 的 ref
/** ----------------------- 引用 ----------------------- **/
const canvasRef = useRef<MeasurementCanvasRef>(null);
// 用于累计点数据
const leftPoints = useRef<{ x: number; y: number }[]>([]);
const rightPoints = useRef<{ x: number; y: number }[]>([]);
// 标志左侧数据是否结束
const isLeftFinished = useRef(false);
/** ----------------------- 状态 ----------------------- **/
const [showGrid, setShowGrid] = useState(true);
const [showStandard, setShowStandard] = useState(true);
const [showMark, setShowMark] = useState(true);
// 用于保存角度线的备份状态,当标准线关闭时记住原先角度线是否开启
const [angleMarkBackup, setAngleMarkBackup] = useState(true);
const afterSave = useAppSelector((store) => store.context.newMeasureAfterSave);
// 初始按钮文本为“开始测量”
const [angles, setAngles] = useState<AnalyzeAngle[]>([]);
const [taskStatus, setTaskStatus] = useState<MeasureState["data"]["taskStatus"]>("IDLE");
const [startBtnText, setStartBtnText] = useState("开始测量");
// 测量是否完成的状态
const [measurementFinished, setMeasurementFinished] = useState(false);
// 本次测量周期内按钮是否已点击过(只能点击一次)
const [analysisClicked, setAnalysisClicked] = useState(false);
const [saveClicked, setSaveClicked] = useState(false);
// 新增:保存接口返回的分析报告数据和是否显示分析表格(右侧区域切换)
const [analysisReport, setAnalysisReport] = useState<AnalysisReport | null>(null);
const [showAnalysisTable, setShowAnalysisTable] = useState(false);
const [taskStatusName, setTaskStatusName] = useState("");
// 初始状态列表
const initialStatusList = [
{
statusCode: "START_RECORD_LEFT",
name: "请移动到顶部,停顿2秒",
background: "#ececec",
isReady: false,
color: "h",
},
{
statusCode: "START_RECORD_LEFT",
name: "开始测量左侧",
background: "#ececec",
isReady: false,
color: "h",
},
{
statusCode: "START_RECORD_LEFT",
name: "左侧测量完成",
background: "#ececec",
isReady: false,
color: "h",
},
{
statusCode: "START_RECORD_LEFT",
name: "请移动到顶部,停顿2秒",
background: "#ececec",
isReady: false,
color: "h",
},
{
statusCode: "START_RECORD_LEFT",
name: "开始测量右侧",
background: "#ececec",
isReady: false,
color: "h",
},
{
statusCode: "START_RECORD_LEFT",
name: "右侧测量完成",
background: "#ececec",
isReady: false,
color: "h",
},
{ statusCode: "START_RECORD_LEFT", name: "请移动到顶部,停顿2秒", background: "#ececec", isReady: false, color: "h" },
{ statusCode: "START_RECORD_LEFT", name: "开始测量左侧", background: "#ececec", isReady: false, color: "h" },
{ statusCode: "START_RECORD_LEFT", name: "左侧测量完成", background: "#ececec", isReady: false, color: "h" },
{ statusCode: "START_RECORD_LEFT", name: "请移动到顶部,停顿2秒", background: "#ececec", isReady: false, color: "h" },
{ statusCode: "START_RECORD_LEFT", name: "开始测量右侧", background: "#ececec", isReady: false, color: "h" },
{ statusCode: "START_RECORD_LEFT", name: "右侧测量完成", background: "#ececec", isReady: false, color: "h" },
];
const [statusList, setStatusList] = useState(initialStatusList);
/** ----------------------- 事件处理函数 ----------------------- **/
// 切换保存后自动开始新测量
const onAfterSaveChange: CheckboxProps["onChange"] = (e) => {
dispatch(switchMeasureAfterSave(e.target.checked));
};
dispatch(switchMeasureAfterSave(e.target.checked));
};
// 分析按钮点击事件
const onAnalysisBtnClick = () => {
// 分析按钮只允许点击一次
setAnalysisClicked(true);
fetchAnalysisReport("6001").then((res) => {
if (res.success) {
const report: AnalysisReport = res.data;
console.log(res.data);
// 更新 canvas 分析数据(如有需要)
console.log(report);
// 更新 canvas 的分析数据
if (report && report.angleAnalysisList) {
const analysisData: AnalysisData[] = report.angleAnalysisList.map((item) => ({
pointA: { x: parseFloat(item.pointA.x), y: parseFloat(item.pointA.y) },
pointB: { x: parseFloat(item.pointB.x), y: parseFloat(item.pointB.y) },
// 默认将 base 与 measure 分别设置为 pointA 与 pointB
base: { x: parseFloat(item.pointA.x), y: parseFloat(item.pointA.y) },
measure: { x: parseFloat(item.pointB.x), y: parseFloat(item.pointB.y) },
distance: parseFloat(item.distance),
@ -124,7 +85,6 @@ export default function MeasureAction() {
}));
canvasRef.current?.setAnalysisData(analysisData);
}
// 保存返回数据,并显示分析表格(右侧区域切换)
setAnalysisReport(report);
setShowAnalysisTable(true);
} else {
@ -133,13 +93,13 @@ export default function MeasureAction() {
});
};
// 开始/重新测量按钮点击事件
const onStart = () => {
// 如果按钮文本为“新测量”,则直接跳转到新测量页面
if (startBtnText === "新测量") {
navigate("../newMeasure");
return;
}
// 进入测量流程时恢复右侧区域为测量步骤
// 重置测量相关状态
setShowAnalysisTable(false);
setMeasurementFinished(false);
setAnalysisClicked(false);
@ -147,29 +107,28 @@ export default function MeasureAction() {
isLeftFinished.current = false;
leftPoints.current = [];
rightPoints.current = [];
// 清空绘制的图形,并重置缩放/偏移
canvasRef.current?.clearShapes();
canvasRef.current?.resetCanvas();
// 如果按钮原来为“重新测量”,则重置状态列表
if (startBtnText === "重新测量") {
setStatusList(initialStatusList);
}
startMeasurement().then((res) => {
if (res.status !== 0) {
message.error(res.data.info);
setTaskStatusName(taskStatusDescMap["IDLE"]);
} else {
const newStatusList = [...initialStatusList];
newStatusList[0].color = "b";
setStatusList(newStatusList);
message.success("已通知设备开始测量");
// 测量启动成功后,按钮文本变为“重新测量”
setTaskStatusName(taskStatusDescMap["IDLE"]);
setStartBtnText("重新测量");
}
});
};
// 保存按钮点击事件
const onSaveBtnClick = () => {
// 保存按钮只允许点击一次
setSaveClicked(true);
saveMeasurement().then((res) => {
if (res.status !== 0) {
@ -177,58 +136,120 @@ export default function MeasureAction() {
} else {
message.success("保存成功");
if (afterSave) {
// 勾选了保存后自动开始新测量则直接跳转
navigate("../config");
} else {
// 否则修改按钮文本为“新测量”
setStartBtnText("新测量");
}
}
});
};
// 辅助函数:渲染状态项的背景颜色
const renderStatusBackground = (item: typeof initialStatusList[0]) =>
item.statusCode === "START_RECORD_LEFT" ? item.background : "";
// 辅助函数:渲染状态项的图标
const renderStatusIcon = (item: typeof initialStatusList[0]) => {
if (item.color === "g") {
return <img src={Gr_round} alt="green" />;
} else if (item.color === "b") {
return <img src={Bl_round} alt="blue" />;
} else {
return (
<div
style={{
width: "22px",
height: "22px",
background: "#c0c0c0",
borderRadius: "50%",
marginTop: "10px",
}}
/>
);
}
};
/** ----------------------- WebSocket 消息处理 ----------------------- **/
useEffect(() => {
const subscription = wsClient.dataOb.subscribe((data) => {
// 处理状态变化事件
if (data.messageType === "EVENT" && data.path === "/measurement-task/event") {
if (data.data === "START_RECORD_LEFT") {
statusList[0].color = "g";
statusList[1].color = "b";
} else if (data.data === "FINISH_RECORD_LEFT") {
statusList[1].color = "g";
statusList[2].color = "g";
statusList[3].color = "b";
// 左侧测量结束后,切换到右侧数据累计
isLeftFinished.current = true;
} else if (data.data === "START_RECORD_RIGHT") {
statusList[3].color = "g";
statusList[4].color = "b";
} else if (data.data === "FINISH_RECORD_RIGHT") {
statusList[4].color = "g";
statusList[5].color = "g";
// 接收到 FINISH_RECORD_RIGHT 后认为测量完成
setMeasurementFinished(true);
}
setStatusList([...statusList]);
// 处理任务状态消息
const handleStateMessage = (data: any) => {
if (!data.data) return;
if (data.data.taskStatus === "IDLE") {
setTaskStatusName("空闲");
} else if (!data.data.isMeasuringLeftEnd) {
setTaskStatusName("左侧正在测量");
setStatusList((prev) => {
const updated = [...prev];
updated[0].isReady = true;
return updated;
});
} else if (data.data.isMeasuringLeftEnd && !data.data.isMeasuringRightEnd) {
setTaskStatusName("右侧正在测量");
} else {
setTaskStatusName(taskStatusDescMap[data.data.taskStatus as keyof typeof taskStatusDescMap]);
}
setTaskStatus(data.data.taskStatus);
};
if (data.messageType === "STATE" && (data as any).path === "/measurement-task/point-report") {
const pointData = ((data as unknown) as { data: { x: number; y: number } }).data;
console.log("pointData ====" + pointData.x + "," + pointData.y);
if (!isLeftFinished.current) {
leftPoints.current.push(pointData);
canvasRef.current?.setMeasurementDataLeft([...leftPoints.current]);
} else {
rightPoints.current.push(pointData);
canvasRef.current?.setMeasurementDataRight([...rightPoints.current]);
// 处理事件消息
const handleEventMessage = (data: any) => {
setStatusList((prev) => {
const updated = [...prev];
switch (data.data) {
case "START_RECORD_LEFT":
updated[0].color = "g";
updated[1].color = "b";
break;
case "FINISH_RECORD_LEFT":
updated[1].color = "g";
updated[2].color = "g";
updated[3].color = "b";
isLeftFinished.current = true;
break;
case "START_RECORD_RIGHT":
updated[3].color = "g";
updated[4].color = "b";
break;
case "FINISH_RECORD_RIGHT":
updated[4].color = "g";
updated[5].color = "g";
setMeasurementFinished(true);
break;
default:
break;
}
return updated;
});
};
// 处理点数据消息
const handlePointReport = (data: any) => {
const pointData = data.data as { x: number; y: number };
console.log(`pointData === ${pointData.x},${pointData.y}`);
if (!isLeftFinished.current) {
leftPoints.current.push(pointData);
canvasRef.current?.setMeasurementDataLeft([...leftPoints.current]);
} else {
rightPoints.current.push(pointData);
canvasRef.current?.setMeasurementDataRight([...rightPoints.current]);
}
};
const subscription = wsClient.dataOb.subscribe((data: any) => {
if (data.messageType === "STATE" && data.path === "/measurement-task/get-task-state") {
handleStateMessage(data);
} else if (data.messageType === "EVENT" && data.path === "/measurement-task/event") {
handleEventMessage(data);
} else if (data.messageType === "STATE" && data.path === "/measurement-task/point-report") {
handlePointReport(data);
}
});
wsClient.connect();
return () => subscription.unsubscribe();
}, [statusList]);
}, []);
// 页面加载时获取基础图形数据,并传入 MeasurementCanvas
/** ----------------------- 页面加载获取基础图形数据 ----------------------- **/
useEffect(() => {
getBaseRecordPointSetByCode("6001").then((res) => {
if (res.success) {
@ -241,80 +262,47 @@ export default function MeasureAction() {
});
}, []);
type StatusCodeData = {
statusCode: string;
name: string;
background: string;
isReady: boolean;
color: string;
};
const onHandleChangeStatus = (item: StatusCodeData) => {
let backgroundColor = "";
if (item.statusCode === "START_RECORD_LEFT") {
backgroundColor = item.background;
}
return backgroundColor;
};
const onHandleIcon = (item: StatusCodeData, index: number) => {
if (item.color === "g") {
return <img src={Gr_round} alt="" />;
} else if (item.color === "b") {
return <img src={Bl_round} alt="" />;
} else if (item.color === "h") {
return (
<div
style={{
width: "22px",
height: "22px",
background: "#c0c0c0",
borderRadius: "50%",
marginTop: "10px",
}}
></div>
);
}
};
/** ----------------------- 渲染 ----------------------- **/
return (
<div className="flex h-full">
{/* 左侧区域:包含开关区域和测量画布 */}
<div className="flex-none">
<div className="flex gap-4 items-center px-6 pt-5">
{/* 参考线开关 */}
<div className="flex gap-2 items-center">
<Switch defaultChecked onChange={(checked) => setShowGrid(checked)} />
<span>线</span>
</div>
{/* 标准线开关 */}
<div className="flex gap-2 items-center">
<Switch
checked={showStandard}
onChange={(checked) => {
setShowStandard(checked);
if (!checked) {
// 关闭标准线时,备份当前角度线状态,并关闭角度线
setAngleMarkBackup(showMark);
setShowMark(false);
} else {
// 打开标准线时,恢复角度线之前的状态
setShowMark(angleMarkBackup);
}
}}
/>
<span>线</span>
</div>
<div className="flex gap-2 items-center">
<Switch
checked={showMark}
disabled={!showStandard}
onChange={(checked) => {
setShowMark(checked);
// 当标准线处于开启状态时,允许修改角度线状态,并更新备份状态
setAngleMarkBackup(checked);
}}
/>
<span>线</span>
</div>
{/* 角度线开关,仅在点击分析按钮后显示 */}
{analysisClicked && (
<div className="flex gap-2 items-center">
<Switch
checked={showMark}
disabled={!showStandard}
onChange={(checked) => {
setShowMark(checked);
setAngleMarkBackup(checked);
}}
/>
<span>线</span>
</div>
)}
</div>
<div className="relative m-2">
<MeasurementCanvas
@ -335,7 +323,7 @@ export default function MeasureAction() {
/>
</div>
</div>
{/* 右侧区域:根据 showAnalysisTable 状态决定显示测量步骤区域还是分析表格 */}
{/* 右侧区域:根据 showAnalysisTable 状态显示测量步骤或分析表格 */}
<div className="w-[300px] flex-none py-6">
{showAnalysisTable && analysisReport ? (
<div className="analysis-table">
@ -363,12 +351,9 @@ export default function MeasureAction() {
</tr>
))}
<tr style={{ height: "40px", fontSize: "18px", color: "#9E9E9E" }}>
<td
colSpan={2}
style={{ textAlign: "center", padding: "8px", border: "1px solid #ccc" }}
>
<td colSpan={2} style={{ textAlign: "center", padding: "8px", border: "1px solid #ccc" }}>
<Button style={{ width: 200 }} size="large" type="primary" onClick={() => navigate("../config")}>
</Button>
</td>
</tr>
@ -379,20 +364,18 @@ export default function MeasureAction() {
<div>
<h1 className="font-medium text-xl text-center"></h1>
<div className="ml-[45px] w-[13rem] mt-5">
{statusList.map((item, index) => {
return (
<div
key={index}
style={{ background: onHandleChangeStatus(item), borderRadius: "20px" }}
className="mt-[10px] h-[40px]"
>
<div style={{ display: "flex", lineHeight: "40px" }} className="pl-[1rem]">
{onHandleIcon(item, index)}
<div className="pl-[5px]">{item.name}</div>
</div>
{statusList.map((item, index) => (
<div
key={index}
style={{ background: renderStatusBackground(item), borderRadius: "20px" }}
className="mt-[10px] h-[40px]"
>
<div style={{ display: "flex", lineHeight: "40px" }} className="pl-[1rem]">
{renderStatusIcon(item)}
<div className="pl-[5px]">{item.name}</div>
</div>
);
})}
</div>
))}
</div>
<section className="flex flex-col items-center gap-4 mt-6 border-t border-[#D8D8D8] py-4">
<Button style={{ width: 200 }} size="large" type="primary" onClick={onStart}>
@ -425,4 +408,4 @@ export default function MeasureAction() {
</div>
</div>
);
}
}

4
src/pages/measure/components/MeasureConfig.tsx

@ -14,8 +14,8 @@ export default function MeasureConfig() {
console.log("Received values of form: ", values);
//判断是否连接了设备
if(!deviceState.isConnect){
message.error('设备尚未连接或连接失败,请重新连接')
return;
// message.error('设备尚未连接或连接失败,请重新连接')
// return;
}
createMeasure({
operatorName: values["username"],

Loading…
Cancel
Save