Browse Source

监听坐标上报

feature/rail
zhangjiming 5 months ago
parent
commit
b11e50fd72
  1. 41
      src/pages/measure/components/MeasureAction.tsx
  2. 128
      src/pages/measure/components/graph/RealtimeLayer.tsx
  3. 10
      src/pages/measure/components/graph/StandardLayer.tsx
  4. 33
      src/services/socket.ts
  5. 20
      src/services/wsTypes.ts

41
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 (
<div className="flex h-full ">
<div className="flex-none border relative">
<GridLayer width={840} height={600} leftPadding={30} rightPadding={10} topPadding={10} bottomPadding={30} columns={10} rows={7} colCellNum={1} rowCellNum={2} />
<div className="flex-none relative">
<GridLayer
width={840}
height={600}
leftPadding={30}
rightPadding={10}
topPadding={10}
bottomPadding={30}
columns={10}
rows={7}
colCellNum={1}
rowCellNum={2}
/>
<div className="absolute top-0">
<StandardLayer width={840} height={600} leftPadding={30} rightPadding={10} topPadding={10} bottomPadding={30} columns={10} rows={7} />
<StandardLayer
width={840}
height={600}
leftPadding={30}
rightPadding={10}
topPadding={10}
bottomPadding={30}
columns={10}
rows={7}
/>
</div>
</div>
<div className="absolute top-0">
<RealtimeLayer
width={840}
height={600}
leftPadding={30}
rightPadding={10}
topPadding={10}
bottomPadding={30}
columns={10}
rows={7}
/>
</div>
</div>
<div className="w-[300px] flex-none py-6">
<h1 className="font-medium text-xl text-center"></h1>
<section className="flex flex-col items-center gap-4 mt-6 border-t border-[#D8D8D8] py-4">

128
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<HTMLCanvasElement | null>(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 (
<div>
<canvas ref={canvasRef} width={props.width} height={props.height}></canvas>
</div>
);
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<HTMLCanvasElement | null>(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 (
<div>
<canvas ref={canvasRef} width={props.width} height={props.height}></canvas>
</div>
);
}

10
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<HTMLCanvasElement | null>(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;

33
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<SocketState>()
private dataSub = new Subject<Datagram>();
get dataOb() {
return this.dataSub.asObservable();
}
private stateSub = new Subject<SocketState>();
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}`;
export const sharedWsUrl = `ws://${process.env.REACT_APP_WS_URL}`;

20
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;
Loading…
Cancel
Save