diff --git a/src/pages/measure/components/MeasureAction.tsx b/src/pages/measure/components/MeasureAction.tsx index 02f7638..efeea06 100644 --- a/src/pages/measure/components/MeasureAction.tsx +++ b/src/pages/measure/components/MeasureAction.tsx @@ -5,6 +5,7 @@ import { startMeasurement } from "../../../services/measure/analysis" import { createWebSocket, sharedWsUrl } from "../../../services/socket"; import GridLayer from "./graph/GridLayer"; import StandardLayer from "./graph/StandardLayer"; +import RealtimeLayer from "./graph/RealtimeLayer"; export default function MeasureAction() { const navigate = useNavigate(); @@ -40,12 +41,44 @@ export default function MeasureAction() { } return (
-
- +
+
- +
-
+
+ +
+

测量步骤

diff --git a/src/pages/measure/components/graph/RealtimeLayer.tsx b/src/pages/measure/components/graph/RealtimeLayer.tsx index 4d4c794..7a34e57 100644 --- a/src/pages/measure/components/graph/RealtimeLayer.tsx +++ b/src/pages/measure/components/graph/RealtimeLayer.tsx @@ -1,31 +1,105 @@ -import { useCallback, useEffect, useRef } from "react"; +import { useCallback, useEffect, useRef, useState } from "react"; +import points from "../../../../utils/measure.json"; +import { createWebSocket, sharedWsUrl } from "../../../../services/socket"; +const wsClient = createWebSocket(sharedWsUrl); +console.log(sharedWsUrl); + +const pointArr: { x: number; y: number }[] = []; export default function RealtimeLayer(props: { - width: number; - height: number; - leftPadding: number; - rightPadding: number; - topPadding: number; - bottomPadding: number; + width: number; + height: number; + leftPadding: number; + rightPadding: number; + topPadding: number; + bottomPadding: number; + columns: number; + rows: number; }) { - const canvasRef = useRef(null); - const draw = useCallback((ctx: CanvasRenderingContext2D) => { - // 使用context对象进行绘图 - ctx.fillStyle = "skyblue"; // 设置填充颜色 - ctx.fillRect(50, 50, 150, 100); // 绘制一个矩形 - }, []); - useEffect(() => { - const canvas = canvasRef.current; - if (!canvas) return; - - const context = canvas.getContext("2d"); - if (!context) return; - draw(context); - }, [draw]); - - return ( -
- -
- ); + const xStartPx = props.leftPadding; + const xEndPx = props.width - props.rightPadding; + const xStepPx = (props.width - props.leftPadding - props.rightPadding) / props.columns; + const yStartPx = props.topPadding; + // const yEndPx = props.height - props.bottomPadding; + const yStepPx = (props.height - props.topPadding - props.bottomPadding) / props.rows; + + const unitPx = xStepPx / 10; + const pointsPx = points.map(p => ({ x: p.x * unitPx, y: p.y * unitPx + 4 })); // 特意偏移 + + const canvasRef = useRef(null); + + const [rtPoints, setRtPoints] = useState<{ x: number; y: number }[]>([]); + + // const draw = useCallback( + // (ctx: CanvasRenderingContext2D) => { + // for (let idx = 0; idx < pointsPx.length; idx++) { + // if (idx === 0) { + // ctx.moveTo(pointsPx[idx].x, pointsPx[idx].y); + // } else { + // ctx.lineTo(pointsPx[idx].x, pointsPx[idx].y); + // } + // } + // ctx.stroke(); + // }, + // [pointsPx, xEndPx, xStartPx, yStartPx, yStepPx] + // ); + + useEffect(() => { + const subscription = wsClient.dataOb.subscribe(data => { + if (data.path === "/measurement-task/get-task-state") { + const canvas = canvasRef.current; + if (!canvas) return; + + const ctx = canvas.getContext("2d"); + if (!ctx) return; + + if (data.data.event === "START_RECORD_SIG") { + // setRtPoints([]); + pointArr.length = 0; + console.log("-----------------------------------") + ctx.resetTransform(); + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.strokeStyle = "blue"; + const xOffset = (xEndPx - xStartPx) / 2; + const yOffset = yStepPx * 2; + ctx.translate(xStartPx + xOffset, yStartPx + yOffset); + } else if (data.data.event === "END_RECORD_SIG") { + // + } + } else if (data.path === "/measurement-task/profile-record-ctrl-sig") { + // console.log(data.data); + // setRtPoints(rtPoints.concat([data.data])); + pointArr.push(data.data); + console.log(pointArr.length); + + const canvas = canvasRef.current; + if (!canvas) return; + + const ctx = canvas.getContext("2d"); + if (!ctx) return; + + // ctx.clearRect(0, 0, canvas.width * 2, canvas.height * 2); + const pointsPx = pointArr.map(p => ({ x: p.x * unitPx, y: p.y * unitPx })); + + for (let idx = 0; idx < pointsPx.length; idx++) { + if (idx === 0) { + ctx.moveTo(pointsPx[idx].x, pointsPx[idx].y); + } else { + ctx.lineTo(pointsPx[idx].x, pointsPx[idx].y); + } + } + // if (pointsPx.length > 1) { + ctx.stroke(); + // } + } + }); + wsClient.connect(); + return () => subscription.unsubscribe(); + }, [rtPoints, unitPx, xEndPx, xStartPx, yStartPx, yStepPx]); + + return ( +
+ +
+ ); } diff --git a/src/pages/measure/components/graph/StandardLayer.tsx b/src/pages/measure/components/graph/StandardLayer.tsx index d6bed8f..ed243f0 100644 --- a/src/pages/measure/components/graph/StandardLayer.tsx +++ b/src/pages/measure/components/graph/StandardLayer.tsx @@ -1,8 +1,6 @@ import { useCallback, useEffect, useRef } from "react"; import { calculateCircleAbove, calculateCircleBelow } from "../../../../utils"; -const unitPx = 8; - const pointsR: [number, number][] = [ [0, 0], [9.949007022412, 0.1650166186941], @@ -21,8 +19,6 @@ const pointsL: [number, number][] = [ [-9.949007022412, 0.1650166186941], [0, 0], ]; -const pointsRight = pointsR.map(p => [p[0] * unitPx, p[1] * unitPx]); -const pointsLeft = pointsL.map(p => [p[0] * unitPx, p[1] * unitPx]); export default function StandardLayer(props: { width: number; @@ -41,6 +37,10 @@ export default function StandardLayer(props: { const yEndPx = props.height - props.bottomPadding; const yStepPx = (props.height - props.topPadding - props.bottomPadding) / props.rows; + const unitPx = xStepPx / 10; + const pointsRight = pointsR.map(p => [p[0] * unitPx, p[1] * unitPx]); + const pointsLeft = pointsL.map(p => [p[0] * unitPx, p[1] * unitPx]); + const canvasRef = useRef(null); const draw = useCallback( @@ -190,7 +190,7 @@ export default function StandardLayer(props: { ctx.stroke(); // ctx.fillRect(100, 100, 150, 100); // 绘制一个矩形 }, - [xEndPx, xStartPx, xStepPx, yEndPx, yStartPx, yStepPx] + [pointsLeft, pointsRight, unitPx, xEndPx, xStartPx, xStepPx, yEndPx, yStartPx, yStepPx] ); useEffect(() => { const canvas = canvasRef.current; diff --git a/src/services/socket.ts b/src/services/socket.ts index 82c1b3c..83f18d1 100644 --- a/src/services/socket.ts +++ b/src/services/socket.ts @@ -1,6 +1,7 @@ import { Subject } from "rxjs"; +import { Datagram } from "./wsTypes"; -export type SocketState = 'open' | 'close' | 'error' +export type SocketState = "open" | "close" | "error"; class WebSocketClient { private ws: WebSocket | null = null; @@ -9,9 +10,14 @@ class WebSocketClient { private maxReconnectAttempts: number = 5; private reconnectInterval: number = 3000; - readonly dataOb = new Subject() - readonly stateOb = new Subject() - + private dataSub = new Subject(); + get dataOb() { + return this.dataSub.asObservable(); + } + private stateSub = new Subject(); + get stateOb() { + return this.stateSub.asObservable(); + } constructor(url: string) { this.url = url; } @@ -41,28 +47,32 @@ class WebSocketClient { this.ws.onopen = () => { console.log("WebSocket 连接已建立"); this.reconnectAttempts = -1; // 重置重连次数 - this.stateOb.next('open') + this.stateSub.next("open"); }; // 接收消息的处理 this.ws.onmessage = (event: MessageEvent) => { try { - const data = JSON.parse(event.data); - // console.log('🚀 ~ WebSocketClient ~ bindEvents ~ data:', data) - this.dataOb.next(data) + const data = JSON.parse(event.data) as Datagram; + // console.log("🚀 ~ WebSocketClient ~ bindEvents ~ data:", data); + // if (data.type === "cmd") { + // this.dataSub.next({ type: data.type, data: { ...data.data, success: data.data.status === "D0000" } }); + // } else { + this.dataSub.next(data); + // } } catch (error) { console.error("消息解析错误:", error); } }; this.ws.onclose = () => { - this.stateOb.next('close') + this.stateSub.next("close"); console.log("WebSocket 连接已关闭"); this.reconnect(); }; this.ws.onerror = error => { - this.stateOb.next('error') + this.stateSub.next("error"); console.error("WebSocket 错误:", error); }; } @@ -107,5 +117,4 @@ export const createWebSocket = (url: string): WebSocketClient => { } }; - -export const sharedWsUrl = `ws://${process.env.REACT_APP_WS_URL}`; \ No newline at end of file +export const sharedWsUrl = `ws://${process.env.REACT_APP_WS_URL}`; diff --git a/src/services/wsTypes.ts b/src/services/wsTypes.ts new file mode 100644 index 0000000..7113fc9 --- /dev/null +++ b/src/services/wsTypes.ts @@ -0,0 +1,20 @@ + // 开始、停止绘制 +export type TaskState = { + messageType: "EVENT"; + data: { + event: "START_RECORD_SIG" | "END_RECORD_SIG"; + }; + path: "/measurement-task/get-task-state"; +}; + +// 连接上报坐标点 +export type TrackRecordSig = { + messageType: "EVENT"; + data: { + x: number; + y: number; + }; + path: "/measurement-task/profile-record-ctrl-sig"; +}; + +export type Datagram = TrackRecordSig | TaskState;