import React, { useState, useEffect, useRef } from "react"; import { Button, Checkbox, CheckboxProps, message, Switch } from "antd"; import { useNavigate } from "react-router"; import { fetchAnalysisReport, getBaseRecordPointSetByCode, saveMeasurement, startMeasurement, } from "../../../services/measure/analysis"; import { createWebSocket, sharedWsUrl } from "../../../services/socket"; import { switchMeasureAfterSave } from "../../../store/features/contextSlice"; import { AnalysisReport, AnalyzeAngle } from "../../../services/measure/type"; import { MeasureState, TaskState, taskStatusDescMap, TrackRecordSig } 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"; import MeasurementCanvas, { AnalysisData, BenchmarkShape, MeasurementCanvasRef } from "./konva/MeasurementCanvas"; import "./MeasureAction.scss"; // 创建 websocket 客户端 const wsClient = createWebSocket(sharedWsUrl); export default function MeasureAction() { const dispatch = useAppDispatch(); const navigate = useNavigate(); /** ----------------------- 引用 ----------------------- **/ const canvasRef = useRef(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([]); // const [taskStatus, setTaskStatus] = useState("IDLE"); const [startBtnText, setStartBtnText] = useState("开始测量"); const [measurementFinished, setMeasurementFinished] = useState(false); const [analysisClicked, setAnalysisClicked] = useState(false); const [saveClicked, setSaveClicked] = useState(false); const [analysisReport, setAnalysisReport] = useState(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" }, ]; const [statusList, setStatusList] = useState(initialStatusList); /** ----------------------- 事件处理函数 ----------------------- */ // 切换保存后自动开始新测量 const onAfterSaveChange: CheckboxProps["onChange"] = e => { dispatch(switchMeasureAfterSave(e.target.checked)); }; // 分析按钮点击事件 const onAnalysisBtnClick = () => { setAnalysisClicked(true); fetchAnalysisReport("6001").then(res => { if (res.success) { const report: AnalysisReport = res.data; console.log(report); // 更新 canvas 的分析数据 if (report && report.angleAnalysisList) { // 先过滤掉 distance 为 null 的数据 const validItems = report.angleAnalysisList.filter(item => item.distance !== null); const analysisData: AnalysisData[] = validItems.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: { 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), describe: item.describe, })); canvasRef.current?.setAnalysisData(analysisData); } setAnalysisReport(report); setShowAnalysisTable(true); } else { message.error("分析报告请求失败: " + res.data.info); } }); }; // 开始/重新测量按钮点击事件 const onStart = () => { if (startBtnText === "新测量") { navigate("../newMeasure"); return; } // 重置测量相关状态 setShowAnalysisTable(false); setMeasurementFinished(false); setAnalysisClicked(false); setSaveClicked(false); 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) { message.error(res.data.info); } else { message.success("保存成功"); if (afterSave) { navigate("../config"); } else { setStartBtnText("新测量"); } } }); }; // 辅助函数:渲染状态项的图标 const renderStatusIcon = (item: (typeof initialStatusList)[0]) => { if (item.color === "g") { return green; } else if (item.color === "b") { return blue; } else { return (
); } }; /** ----------------------- WebSocket 消息处理 ----------------------- **/ useEffect(() => { // 处理任务状态消息 const handleStateMessage = (state: MeasureState["data"]) => { }; // 处理事件消息 const handleEventMessage = (type: TaskState["data"]) => { setStatusList(prev => { const updated = [...prev]; switch (type) { case "START_RECORD_LEFT": updated[0].color = "g"; updated[1].color = "b"; const audio1 = new Audio("/audio/begin_left.mp3"); // 播放音频 audio1 .play() .then(() => { console.log("音频开始播放"); }) .catch(err => { console.error("播放音频时出错:", err); }); break; case "FINISH_RECORD_LEFT": updated[1].color = "g"; updated[2].color = "g"; updated[3].color = "b"; isLeftFinished.current = true; const audio2 = new Audio("/audio/end_left.mp3"); // 播放音频 audio2 .play() .then(() => { console.log("音频开始播放"); }) .catch(err => { console.error("播放音频时出错:", err); }); break; case "START_RECORD_RIGHT": updated[3].color = "g"; updated[4].color = "b"; const audio3 = new Audio("/audio/begin_right.mp3"); // 播放音频 audio3 .play() .then(() => { console.log("音频开始播放"); }) .catch(err => { console.error("播放音频时出错:", err); }); break; case "FINISH_RECORD_RIGHT": updated[4].color = "g"; updated[5].color = "g"; setMeasurementFinished(true); const audio4 = new Audio("/audio/end_right.mp3"); // 播放音频 audio4 .play() .then(() => { console.log("音频开始播放"); }) .catch(err => { console.error("播放音频时出错:", err); }); break; default: break; } return updated; }); }; // 处理点数据消息 const handlePointReport = (pointData: TrackRecordSig["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 subscription = wsClient.dataOb.subscribe((data) => { if ( data.path === "/measurement-task/get-task-state") { handleStateMessage(data.data); } else if (data.path === "/measurement-task/event") { handleEventMessage(data.data); } else if (data.path === "/measurement-task/point-report") { handlePointReport(data.data); } }); wsClient.connect(); return () => subscription.unsubscribe(); }, []); /** ----------------------- 页面加载获取基础图形数据 ----------------------- **/ useEffect(() => { getBaseRecordPointSetByCode("6001").then(res => { if (res.success) { const benchmarkShapes = JSON.parse(res.data.points) as BenchmarkShape[]; if (canvasRef.current) { console.log("解析后的基础图形数据:", benchmarkShapes); canvasRef.current.setBenchmarkData(benchmarkShapes); } } }); }, []); /** ----------------------- 渲染 ----------------------- **/ return (
{/* 左侧区域:包含开关区域和测量画布 */}
{/* 参考线开关 */}
setShowGrid(checked)} /> 参考线
{/* 标准线开关 */}
{ setShowStandard(checked); if (!checked) { setAngleMarkBackup(showMark); setShowMark(false); } else { setShowMark(angleMarkBackup); } }} /> 标准线
{/* 角度线开关,仅在点击分析按钮后显示 */} {analysisClicked && (
{ setShowMark(checked); setAngleMarkBackup(checked); }} /> 角度线
)}
{/* 右侧区域:根据 showAnalysisTable 状态显示测量步骤或分析表格 */}
{showAnalysisTable && analysisReport ? ( <>
分析
{analysisReport.angleAnalysisList.map((item, index) => ( ))}
W1垂直磨耗 {analysisReport.w1}
轨头宽度 {analysisReport.railHeadWidth}
{item.describe} {item.distance}
) : (

测量步骤

{statusList.map((item, index) => (
{renderStatusIcon(item)}
{item.name}
))}
保存后自动开始新测量
)}
); }