Browse Source

优化测量校准

feat_upload_server_url_0416
LiLongLong 4 months ago
parent
commit
6f1a3064a6
  1. 2
      .env
  2. BIN
      build.zip
  3. 2
      package.json
  4. 31
      src/App.tsx
  5. 84
      src/components/Header.tsx
  6. 53
      src/components/syncData.tsx
  7. 18
      src/pages/measure/components/Detail.tsx
  8. 69
      src/pages/measure/components/MeasureAction.tsx
  9. 69
      src/pages/measure/components/MeasureDetail.tsx
  10. 102
      src/pages/measure/components/konva/MeasurementCanvas.tsx
  11. 15
      src/services/measure/analysis.ts
  12. 1
      src/services/measure/type.ts
  13. 13
      src/services/wsTypes.ts
  14. 4
      src/store/index.ts
  15. 8
      src/store/ktj/orgState.ts
  16. 22
      src/store/measure/measureState.ts

2
.env

@ -1 +1 @@
REACT_APP_WS_URL=127.0.0.1:8080/ws
REACT_APP_WS_URL=192.168.1.146:8080/ws

BIN
build.zip

2
package.json

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

31
src/App.tsx

@ -2,16 +2,17 @@ import React, { useEffect, useState } from "react";
import "./App.scss"; import "./App.scss";
import { Outlet, useNavigate } from "react-router"; import { Outlet, useNavigate } from "react-router";
import { Layout, ConfigProvider } from "antd";
import { Layout, ConfigProvider, message} from "antd";
import { default as AppHeader } from "./components/Header"; import { default as AppHeader } from "./components/Header";
import { default as AppFooter } from "./components/Footer"; import { default as AppFooter } from "./components/Footer";
import SideMenu from "./components/SideMenu"; import SideMenu from "./components/SideMenu";
import { createWebSocket, sharedWsUrl } from "./services/socket"; import { createWebSocket, sharedWsUrl } from "./services/socket";
import { useAppDispatch } from "./utils/hooks"; import { useAppDispatch } from "./utils/hooks";
import { updateDeviceState } from "./store/device/deviceState"; import { updateDeviceState } from "./store/device/deviceState";
import { setSyncData } from "./store/ktj/orgState";
import zhCN from 'antd/lib/locale/zh_CN'; // 引入中文语言包 import zhCN from 'antd/lib/locale/zh_CN'; // 引入中文语言包
import { updateDevice } from "./store/features/contextSlice"; import { updateDevice } from "./store/features/contextSlice";
import { getStatus } from "./services/ktj/org";
import { getStatus, update } from "./services/ktj/org";
import SyncData from "./components/syncData"; import SyncData from "./components/syncData";
const { Header, Footer, Sider, Content } = Layout; const { Header, Footer, Sider, Content } = Layout;
@ -36,6 +37,25 @@ function App() {
} else if (data.messageType === "STATE" && data.path === "/api/profiler-state/get-state") { } else if (data.messageType === "STATE" && data.path === "/api/profiler-state/get-state") {
// console.log(data.data); // console.log(data.data);
dispatch(updateDevice(data.data)); dispatch(updateDevice(data.data));
}else if(data.path === "/get-task-progress"){
let syncData = data.data;
if(syncData){
let {
progress,
success
} = syncData;
if(syncData.hasOwnProperty('success') && !success){
message.error(syncData.message)
}
//取整
progress = Math.floor(progress)
dispatch(setSyncData(progress))
if(progress!== 100){
setIsSync(true)
}else{
setIsSync(false)
}
}
} }
}); });
wsClient.connect(); wsClient.connect();
@ -59,8 +79,9 @@ function App() {
const [isSync, setIsSync] = useState(false) const [isSync, setIsSync] = useState(false)
const queryStatus = () => { const queryStatus = () => {
getStatus().then(res => { getStatus().then(res => {
console.log('res-----是否需要同步数据------', res.data)
setIsSync(res.data)
if(res.data){
update();
}
}) })
} }
@ -102,7 +123,7 @@ function App() {
</Layout> </Layout>
</Layout> </Layout>
</ConfigProvider> </ConfigProvider>
{isSync && <SyncData isShowModal={isSync} closeModal={closeModal}></SyncData>}
{isSync && <SyncData isShowModal={isSync} closeModal={closeModal} setIsSync={setIsSync}></SyncData>}
</div> </div>
); );
} }

84
src/components/Header.tsx

@ -14,14 +14,12 @@ import { useAppDispatch, useAppSelector } from "../utils/hooks";
import { updateUser } from "../store/features/contextSlice"; import { updateUser } from "../store/features/contextSlice";
import "./bluetooth.scss"; import "./bluetooth.scss";
import { update } from "../services/ktj/org"; import { update } from "../services/ktj/org";
import SyncData from "./syncData";
export default function Header() { export default function Header() {
const navigate = useNavigate(); const navigate = useNavigate();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const deviceInfo = useAppSelector(store => store.context.device); const deviceInfo = useAppSelector(store => store.context.device);
const deviceState = useAppSelector(store => store.deviceState); const deviceState = useAppSelector(store => store.deviceState);
const userInfo = useAppSelector(store => store.context.user.loginUser); const userInfo = useAppSelector(store => store.context.user.loginUser);
let [isConnect, setIsConnect] = useState(deviceState.isConnect);
//获取当前websocet的状态 //获取当前websocet的状态
const showBlueImg = () => { const showBlueImg = () => {
if (deviceState.isConnect) { if (deviceState.isConnect) {
@ -34,30 +32,8 @@ export default function Header() {
); );
} }
return null; return null;
// if(!isConnect){
// return <Popover content={getBtList()} title="可连接设备">
// <div ></div>
// <img src={bluetooth_nc} alt="" className="ext-base ml-2 h-6" />
// </Popover>
// }else {
// return <Popover content={getBtContent()} title="">
// <img src={icon_usb} onClick={onDisconnectBt} alt="" className="ext-base ml-2 h-6" />
// </Popover>
// }
}; };
let list = [
{
name: "Kdkow_1",
id: "1",
},
{
name: "llwoa_2",
id: "2",
},
];
let [bluetoothList, setbluetoothList] = useState(list);
//获取mock数据
useEffect(() => { useEffect(() => {
if (userInfo.nickname || userInfo.account) { if (userInfo.nickname || userInfo.account) {
setNickname(userInfo.nickname || userInfo.account); setNickname(userInfo.nickname || userInfo.account);
@ -71,36 +47,10 @@ export default function Header() {
}, [userInfo.nickname, userInfo.account]); }, [userInfo.nickname, userInfo.account]);
//同步科天健基础数据 //同步科天健基础数据
const [isShowModal, setIsShowModal] = useState(false)
const onSyncKTJData = () => { const onSyncKTJData = () => {
setIsShowModal(true)
}
const closeModal = () => {
setIsShowModal(false)
update()
} }
// const [percent, setPercent] = useState(0)
// useEffect(() => {
// const intervalId = setInterval(() => {
// if (percent < 100) {
// const randomIncrement = Math.floor(Math.random() * 10) + 1;
// const newProgress = Math.min(percent + randomIncrement, 100);
// setPercent(newProgress);
// } else {
// clearInterval(intervalId);
// }
// }, 1000);
// if(percent >= 90){
// clearInterval(intervalId);
// }
// return () => {
// clearInterval(intervalId);
// };
// }, [isShowModal,percent]);
const [messageApi, contextHolder] = message.useMessage(); const [messageApi, contextHolder] = message.useMessage();
const [nickname, setNickname] = useState<string>(); const [nickname, setNickname] = useState<string>();
const items: MenuProps["items"] = [ const items: MenuProps["items"] = [
@ -133,24 +83,6 @@ export default function Header() {
}, },
]; ];
const getBtList = () => {
let Dom = null;
if (!isConnect) {
Dom = (
<div>
{bluetoothList.map(item => {
return (
<div className="mt-[1rem]" onClick={connectBt}>
<Button type="link">{item.name}</Button>
</div>
);
})}
</div>
);
}
return Dom;
};
//设备已连接 //设备已连接
const getBtContent = () => { const getBtContent = () => {
return ( return (
@ -169,16 +101,6 @@ export default function Header() {
); );
}; };
//断开蓝牙连接
const onDisconnectBt = () => {
setIsConnect(false);
};
const connectBt = () => {
setIsConnect(true);
setTimeout(() => {}, 1000);
};
return ( return (
<> <>
{contextHolder} {contextHolder}
@ -200,9 +122,6 @@ export default function Header() {
</section> </section>
</> </>
)} )}
{/* <section className="bg-white rounded-md h-9 w-10 flex justify-center items-center mr-3">
<img src={icon_usb} className="w-6" alt="icon" />
</section> */}
{showBlueImg()} {showBlueImg()}
<div className="mr-8 flex items-center min-w-[5rem]"> <div className="mr-8 flex items-center min-w-[5rem]">
<Dropdown menu={{ items }} trigger={["click"]}> <Dropdown menu={{ items }} trigger={["click"]}>
@ -213,7 +132,6 @@ export default function Header() {
</Dropdown> </Dropdown>
</div> </div>
</div> </div>
{isShowModal && <SyncData isShowModal={isShowModal} closeModal={closeModal}></SyncData>}
</> </>
); );
} }

53
src/components/syncData.tsx

@ -1,60 +1,31 @@
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { Progress } from "antd";
import { message, Progress } from "antd";
import { useAppDispatch, useAppSelector } from "../utils/hooks";
import "./sync.scss"; import "./sync.scss";
import { update } from "../services/ktj/org";
export default function SyncData(props:{isShowModal:boolean, closeModal:Function}) {
//同步科天健基础数据
const onSyncKTJData = () => {
update().then(res => {
setPercent(100)
props.closeModal()
setTimeout(() => {
setPercent(0)
}, 2000);
})
}
useEffect(()=>{
onSyncKTJData()
}, [props.isShowModal])
export default function SyncData(props:{isShowModal:boolean, closeModal:Function, setIsSync:Function}) {
const [percent, setPercent] = useState(0) const [percent, setPercent] = useState(0)
const syncDataProcess = useAppSelector(store => store.orgState.syncDataProcess);
useEffect(()=>{ useEffect(()=>{
const intervalId = setInterval(() => {
if (percent < 100) {
const randomIncrement = Math.floor(Math.random() * 3) + 1;
const newProgress = Math.min(percent + randomIncrement, 100);
setPercent(newProgress);
} else {
clearInterval(intervalId);
}
}, 1000);
if(percent >= 90){
clearInterval(intervalId);
setPercent(syncDataProcess)
if(syncDataProcess === 100){
props.closeModal();
props.setIsSync(false)
} }
return () => {
clearInterval(intervalId);
};
}, [props.isShowModal,percent]);
},[syncDataProcess])
const styleObj:any = { const styleObj:any = {
position: "absolute", position: "absolute",
marginTop: "-200px", marginTop: "-200px",
color: "#0051b9",
color: "#1677ff",
fontSize: "20px" fontSize: "20px"
} }
return ( return (
<>
<div
className="fixed inset-0 bg-red opacity-50 z-50 cursor-pointer flex justify-center items-center" style={{background:"#202020"}}
>
<div className="fixed inset-0 bg-red opacity-50 z-50 cursor-pointer flex justify-center items-center" style={{background:"#202020"}} >
<div style={styleObj}></div> <div style={styleObj}></div>
<div className="flex w-[60vw] justify-center items-center margin-auto"> <div className="flex w-[60vw] justify-center items-center margin-auto">
<Progress type="circle" format={(percent) => <span style={{ color: '#0051b9' }}>{percent}%</span>} percent={percent}/>
<Progress type="circle" format={(percent) => <span style={{ color: '#1677ff' }}>{percent}%</span>} percent={percent}/>
</div> </div>
</div> </div>
</>
); );
} }

18
src/pages/measure/components/Detail.tsx

@ -6,22 +6,26 @@ import { useNavigate, useParams } from 'react-router-dom';
import type { AnalysisReport } from "../../../services/measure/type"; import type { AnalysisReport } from "../../../services/measure/type";
import { AnalysisData, BenchmarkShape, MeasurementCanvasRef } from './konva/MeasurementCanvas'; import { AnalysisData, BenchmarkShape, MeasurementCanvasRef } from './konva/MeasurementCanvas';
import MeasurementCanvas from "./konva/MeasurementCanvas"; import MeasurementCanvas from "./konva/MeasurementCanvas";
import {
getReport
} from "../../../services/measure/analysis";
import { getReport } from "../../../services/measure/analysis";
import { getBaseRecordPointSetByCode } from "../../../services/track/trackShape" import { getBaseRecordPointSetByCode } from "../../../services/track/trackShape"
import { GX_CODE } from '../../../constant';
import { useAppSelector } from "../../../utils/hooks";
export default function MeasureDetail() { export default function MeasureDetail() {
const {id} = useParams() const {id} = useParams()
const measureState = useAppSelector((store) => store.measureState);
const [measureId, setMeasureId] = useState<number>(Number(id)) const [measureId, setMeasureId] = useState<number>(Number(id))
useEffect(()=>{ useEffect(()=>{
onShowDetail(measureId) onShowDetail(measureId)
}, []) }, [])
const [gxCode, setGxCode] = useState<string>('')
useEffect(()=>{
setGxCode(measureState.gxCode)
}, [measureState])
const onShowDetail = (measureId:number)=> { const onShowDetail = (measureId:number)=> {
//获取基线 //获取基线
getBaseRecordPointSetByCode(GX_CODE).then(res => {
getBaseRecordPointSetByCode(gxCode).then(res => {
if (res.success && res.data && res.data.points) { if (res.success && res.data && res.data.points) {
const benchmarkShapes = JSON.parse(res.data.points) as BenchmarkShape[]; const benchmarkShapes = JSON.parse(res.data.points) as BenchmarkShape[];
if (canvasRef.current) { if (canvasRef.current) {
@ -42,7 +46,7 @@ export default function MeasureDetail() {
//法线 //法线
const onDetaiResult = (uuid:string) => { const onDetaiResult = (uuid:string) => {
getReport(uuid,GX_CODE).then(res=> {
getReport(uuid,gxCode).then(res=> {
if (res.success) { if (res.success) {
const report: AnalysisReport = res.data; const report: AnalysisReport = res.data;
console.log(report); console.log(report);

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

@ -1,13 +1,15 @@
import React, { useState, useEffect, useRef, useCallback, useMemo } from "react"; import React, { useState, useEffect, useRef, useCallback, useMemo } from "react";
import { Button, Checkbox, CheckboxProps, Drawer, message, Select, Switch } from "antd";
import { Button, Checkbox, CheckboxProps, Drawer, message, Select, Spin, Switch } from "antd";
import { useNavigate } from "react-router"; import { useNavigate } from "react-router";
import { import {
fetchAnalysisReport, fetchAnalysisReport,
getAlignPointsByRailSize,
startMeasurement, startMeasurement,
} from "../../../services/measure/analysis"; } from "../../../services/measure/analysis";
import { getBaseRecordPointSetByCode, gx_list } from "../../../services/track/trackShape" import { getBaseRecordPointSetByCode, gx_list } from "../../../services/track/trackShape"
import { createWebSocket, sharedWsUrl } from "../../../services/socket"; import { createWebSocket, sharedWsUrl } from "../../../services/socket";
import { switchMeasureAfterSave } from "../../../store/features/contextSlice"; import { switchMeasureAfterSave } from "../../../store/features/contextSlice";
import measureState, { updateGxState } from "../../../store/measure/measureState";
import { AnalysisReport, trackItem } from "../../../services/measure/type"; import { AnalysisReport, trackItem } from "../../../services/measure/type";
import { MeasureState, TaskState, TrackRecordSig } from "../../../services/wsTypes"; import { MeasureState, TaskState, TrackRecordSig } from "../../../services/wsTypes";
import { useAppDispatch, useAppSelector } from "../../../utils/hooks"; import { useAppDispatch, useAppSelector } from "../../../utils/hooks";
@ -23,6 +25,9 @@ const wsClient = createWebSocket(sharedWsUrl);
export default function MeasureAction() { export default function MeasureAction() {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const navigate = useNavigate(); const navigate = useNavigate();
const measureState = useAppSelector((store) => store.measureState);
const [gxCode, setGxCode] = useState<string>(GX_CODE)
const STEP_COLOR_GREEN = "green"; const STEP_COLOR_GREEN = "green";
const STEP_COLOR_BLUE = "blue"; const STEP_COLOR_BLUE = "blue";
const STEP_COLOR_GREY = "grey"; const STEP_COLOR_GREY = "grey";
@ -67,13 +72,18 @@ export default function MeasureAction() {
dispatch(switchMeasureAfterSave(e.target.checked)); dispatch(switchMeasureAfterSave(e.target.checked));
}; };
useEffect(()=>{
setGxCode(measureState.gxCode)
}, [measureState])
// 分析按钮点击事件 // 分析按钮点击事件
const onAnalysisBtnClick = () => { const onAnalysisBtnClick = () => {
if (analysisReport) { if (analysisReport) {
setOpenDrawer(true); setOpenDrawer(true);
return; return;
} }
fetchAnalysisReport(GX_CODE).then(res => {
fetchAnalysisReport(gxCode).then(res => {
if (res.success) { if (res.success) {
const report: AnalysisReport = res.data; const report: AnalysisReport = res.data;
console.log(report); console.log(report);
@ -104,7 +114,7 @@ export default function MeasureAction() {
// 重置测量相关状态 // 重置测量相关状态
setMeasurementFinished(false); setMeasurementFinished(false);
setAnalysisReport(null); setAnalysisReport(null);
setshowCalibration(false)//校准线
isLeftFinished.current = false; isLeftFinished.current = false;
leftPoints.current = []; leftPoints.current = [];
rightPoints.current = []; rightPoints.current = [];
@ -162,6 +172,25 @@ export default function MeasureAction() {
// }); // });
}; };
//校准
const [showCalibration, setshowCalibration] = useState(false)
const [caloading, setCaLoading] = useState(false)
const onCalibrationBtnClick = () => {
setCaLoading(true)
//获取校准数据
getAlignPointsByRailSize({railSize:railSize}).then(res => {
if(res.success){
setshowCalibration(true)
canvasRef.current?.setMeasurementCalibrationData(res.data)
}else{
message.error('校准失败!')
}
setCaLoading(false)
}).catch(e=>{
message.error('校准失败!')
})
}
// 辅助函数:渲染状态项的图标 // 辅助函数:渲染状态项的图标
const renderStatusIcon = (item: (typeof initialStatusList)[0]) => { const renderStatusIcon = (item: (typeof initialStatusList)[0]) => {
if (item.color === STEP_COLOR_GREEN) { if (item.color === STEP_COLOR_GREEN) {
@ -299,13 +328,14 @@ export default function MeasureAction() {
return () => subscription.unsubscribe(); return () => subscription.unsubscribe();
}, [onStart]); }, [onStart]);
/** ----------------------- 页面加载获取基础图形数据 ----------------------- **/
/** ----------------------- 页面加载获取基础图形数据 -------基线---------------- **/
useEffect(() => { useEffect(() => {
queryBasePoints(GX_CODE)
queryBasePoints(gxCode)
//获取轨型 //获取轨型
getTrackDataList() getTrackDataList()
}, []); }, []);
//获取测量基线
const queryBasePoints = (gxCode:string) => { const queryBasePoints = (gxCode:string) => {
getBaseRecordPointSetByCode(gxCode).then(res => { getBaseRecordPointSetByCode(gxCode).then(res => {
if (res.success) { if (res.success) {
@ -343,11 +373,18 @@ export default function MeasureAction() {
},[trackList]) },[trackList])
//当前选择的轨型 默认"GX-60"
const [railSize, setRailSize] = useState<string>(GX_CODE)
const onTrackChange = (value: string) => { const onTrackChange = (value: string) => {
setRailSize(value)
queryBasePoints(value) queryBasePoints(value)
//缓存至STORE
dispatch(updateGxState(value))
} }
const onExport = () => {};
const onExport = () => {
};
/** ----------------------- 渲染 ----------------------- **/ /** ----------------------- 渲染 ----------------------- **/
return ( return (
<> <>
@ -410,11 +447,11 @@ export default function MeasureAction() {
)} )}
</section> </section>
</div> </div>
<Spin spinning={caloading} tip="正在校准...">
<div className="relative"> <div className="relative">
<MeasurementCanvas <MeasurementCanvas
width={800} width={800}
height={600}
height={650}
logicalExtent={{ minX: -50, maxX: 50, minY: -20, maxY: 60 }} logicalExtent={{ minX: -50, maxX: 50, minY: -20, maxY: 60 }}
gridStep={1} gridStep={1}
origin={{ x: 0, y: 20 }} origin={{ x: 0, y: 20 }}
@ -426,10 +463,11 @@ export default function MeasureAction() {
showScale={false} showScale={false}
scaleInterval={1} scaleInterval={1}
showCoordinates={showGrid} showCoordinates={showGrid}
showCalibration={showCalibration}
ref={canvasRef} ref={canvasRef}
/> />
</div> </div>
</Spin>
</div> </div>
{/* 右侧区域:根据 showAnalysisTable 状态显示测量步骤或分析表格 */} {/* 右侧区域:根据 showAnalysisTable 状态显示测量步骤或分析表格 */}
<div className="min-w-[300px] flex-auto py-6 flex flex-col items-center"> <div className="min-w-[300px] flex-auto py-6 flex flex-col items-center">
@ -469,6 +507,15 @@ export default function MeasureAction() {
> >
</Button> </Button>
{/* <Button
style={{ width: 200 }}
size="large"
type="primary"
onClick={onCalibrationBtnClick}
disabled={!measurementFinished}
>
</Button> */}
<Checkbox checked={afterSave} onChange={onAfterSaveChange}> <Checkbox checked={afterSave} onChange={onAfterSaveChange}>
</Checkbox> </Checkbox>
@ -508,11 +555,11 @@ export default function MeasureAction() {
</tbody> </tbody>
</table> </table>
</div> </div>
<div className="mt-5 flex justify-center">
{/* <div className="mt-5 flex justify-center">
<Button style={{ minWidth: 200 }} size="large" type="primary" onClick={onExport}> <Button style={{ minWidth: 200 }} size="large" type="primary" onClick={onExport}>
</Button> </Button>
</div>
</div> */}
</> </>
)} )}
</Drawer> </Drawer>

69
src/pages/measure/components/MeasureDetail.tsx

@ -1,6 +1,6 @@
import {getDetailList, delDetail, getDetail, getPointByUuid, getPointsById} from '../../../services/measure/analysis'
import {getDetailList, delDetail, getDetail, getPointByUuid, getPointsById, getAlignPointById} from '../../../services/measure/analysis'
import { useState, useEffect, useRef } from 'react' import { useState, useEffect, useRef } from 'react'
import {message, Button, type TableColumnsType, type TableProps, Modal, Table, Pagination, Input, Select, Switch, Progress } from 'antd';
import {message, Button, type TableColumnsType, type TableProps, Modal, Table, Pagination, Input, Select, Switch, Progress, Flex, Spin } from 'antd';
import type { AnalysisReport, DetailTable, SearchParams } from "../../../services/measure/type"; import type { AnalysisReport, DetailTable, SearchParams } from "../../../services/measure/type";
import { ExclamationCircleFilled, CheckCircleOutlined, WarningOutlined } from '@ant-design/icons'; import { ExclamationCircleFilled, CheckCircleOutlined, WarningOutlined } from '@ant-design/icons';
import { AnalysisData, BenchmarkShape, MeasurementCanvasRef } from './konva/MeasurementCanvas'; import { AnalysisData, BenchmarkShape, MeasurementCanvasRef } from './konva/MeasurementCanvas';
@ -177,26 +177,13 @@ export default function MeasureDetail() {
} }
const onDel = (item:DetailTable) => {
confirm({
title: '提示',
icon: <ExclamationCircleFilled />,
content: '请确认是否删除选中的数据',
okText:'确认',
cancelText:'取消',
onOk() {
doDel({ids:item.id})
},
onCancel() {
console.log('Cancel');
},
});
}
const [currentRecord, setCurrentRecord] = useState<Partial<DetailTable>>({})
const onShowDetail = async (item:DetailTable)=> { const onShowDetail = async (item:DetailTable)=> {
//获取基线 //获取基线
setIsModalOpen(true) setIsModalOpen(true)
let res = await getBaseRecordPointSetByCode(GX_CODE)
setCurrentRecord(item)
setshowCalibration(false)
let res = await getBaseRecordPointSetByCode(item.railSize)
if (res.success && res.data && res.data.points) { if (res.success && res.data && res.data.points) {
const benchmarkShapes = JSON.parse(res.data.points) as BenchmarkShape[]; const benchmarkShapes = JSON.parse(res.data.points) as BenchmarkShape[];
if(!benchmarkShapes || !benchmarkShapes.length){ if(!benchmarkShapes || !benchmarkShapes.length){
@ -215,11 +202,10 @@ export default function MeasureDetail() {
if(resData){ if(resData){
// navigate(`/measure/detail/${item.id}`) // navigate(`/measure/detail/${item.id}`)
//@ts-ignore //@ts-ignore
getRecordByUuid(resData.data.uuid)
// getRecordByUuid(resData.data.uuid)
//@ts-ignore //@ts-ignore
onDetaiResult(resData.data.uuid) onDetaiResult(resData.data.uuid)
} }
//获取测量的坐标点 //获取测量的坐标点
getMeasurePoints(item) getMeasurePoints(item)
} }
@ -267,7 +253,8 @@ export default function MeasureDetail() {
//法线 //法线
const [analysisReport, setAnalysisReport] = useState<AnalysisReport>(); const [analysisReport, setAnalysisReport] = useState<AnalysisReport>();
const onDetaiResult = (uuid:string) => { const onDetaiResult = (uuid:string) => {
getReport(uuid,GX_CODE).then(res=> {
const gxValue= currentRecord.railSize || GX_CODE
getReport(uuid, gxValue).then(res=> {
if (res.success) { if (res.success) {
const report: AnalysisReport = res.data; const report: AnalysisReport = res.data;
console.log(report); console.log(report);
@ -381,6 +368,26 @@ export default function MeasureDetail() {
setIsModalOpen(false) setIsModalOpen(false)
} }
//校准
const [showCalibration, setshowCalibration] = useState(false)
const [caloading, setCaLoading] = useState(false)
const handleCalibration = () => {
setCaLoading(true)
if(currentRecord && currentRecord.id){
getAlignPointById({id:currentRecord.id}).then(res=>{
if(res.success){
setshowCalibration(true)
canvasRef.current?.setMeasurementCalibrationData(res.data)
}else{
message.error('校准失败!')
}
setCaLoading(false)
}).catch(e=>{
message.error('校准失败!')
})
}
}
useEffect(() => { useEffect(() => {
const intervalId = setInterval(() => { const intervalId = setInterval(() => {
@ -406,7 +413,7 @@ export default function MeasureDetail() {
{isModalOpen ? {isModalOpen ?
<div className="pt-[5px]"> <div className="pt-[5px]">
<div className="flex"> <div className="flex">
<div className="ml-[7rem]">
<div className="ml-[2rem]">
<Switch defaultChecked onChange={checked => setShowGrid(checked)} /> <Switch defaultChecked onChange={checked => setShowGrid(checked)} />
<span>线</span> <span>线</span>
</div> </div>
@ -438,11 +445,10 @@ export default function MeasureDetail() {
<span>线</span> <span>线</span>
</div> </div>
</div> </div>
<div className="flex ml-[7rem] mt-[5px]">
<div className="flex ml-[2rem] mt-[5px]">
<MeasurementCanvas <MeasurementCanvas
width={800} width={800}
height={600}
height={700}
logicalExtent={{ minX: -50, maxX: 50, minY: -20, maxY: 60 }} logicalExtent={{ minX: -50, maxX: 50, minY: -20, maxY: 60 }}
gridStep={1} gridStep={1}
origin={{ x: 0, y: 20 }} origin={{ x: 0, y: 20 }}
@ -454,6 +460,7 @@ export default function MeasureDetail() {
showScale={false} showScale={false}
scaleInterval={1} scaleInterval={1}
showCoordinates={true} showCoordinates={true}
showCalibration={showCalibration}
ref={canvasRef} ref={canvasRef}
/> />
@ -494,16 +501,21 @@ export default function MeasureDetail() {
</tbody> </tbody>
</table> </table>
</div> </div>
{/* <div className="flex justify-center mt-[1.5rem]" >
<Button type="primary" onClick={handleCalibration} className='w-[150px]'>
</Button>
</div> */}
<div className="flex justify-center mt-[1.5rem]" > <div className="flex justify-center mt-[1.5rem]" >
<Button type="primary" onClick={handleCancel} className='w-[200px]'>
<Button type="primary" onClick={handleCancel} className='w-[150px]'>
</Button> </Button>
</div> </div>
</div> </div>
} }
</div>
</div> </div>
</div>
: :
<> <>
<div className="p-[15px]"> <div className="p-[15px]">
@ -537,6 +549,7 @@ export default function MeasureDetail() {
</div> </div>
<div> <div>
<Table<DetailTable> <Table<DetailTable>
style={{height:"70vh"}}
locale={{ locale={{
emptyText: '无数据', emptyText: '无数据',
}} }}

102
src/pages/measure/components/konva/MeasurementCanvas.tsx

@ -62,11 +62,13 @@ export interface MeasurementCanvasProps {
initialBenchmarkData?: BenchmarkShape[]; initialBenchmarkData?: BenchmarkShape[];
initialMeasurementDataLeft?: Point[]; initialMeasurementDataLeft?: Point[];
initialMeasurementDataRight?: Point[]; initialMeasurementDataRight?: Point[];
initMeasurementCalibrationData?: Point[];
initialAnalysisData?: AnalysisData[]; initialAnalysisData?: AnalysisData[];
// 控制是否显示标准线(benchmark shapes) // 控制是否显示标准线(benchmark shapes)
showBenchmark?: boolean; showBenchmark?: boolean;
// 控制是否显示分析线 // 控制是否显示分析线
showAnalysis?: boolean; showAnalysis?: boolean;
showCalibration?: boolean;
} }
export interface MeasurementCanvasRef { export interface MeasurementCanvasRef {
@ -75,6 +77,7 @@ export interface MeasurementCanvasRef {
setBenchmarkData: (data: BenchmarkShape[]) => void; setBenchmarkData: (data: BenchmarkShape[]) => void;
setMeasurementDataLeft: (data: Point[]) => void; setMeasurementDataLeft: (data: Point[]) => void;
setMeasurementDataRight: (data: Point[]) => void; setMeasurementDataRight: (data: Point[]) => void;
setMeasurementCalibrationData: (data: Point[]) => void;
setMeasurementData: (data: Point[]) => void; setMeasurementData: (data: Point[]) => void;
setAnalysisData: (data: AnalysisData[]) => void; setAnalysisData: (data: AnalysisData[]) => void;
redraw: () => void; redraw: () => void;
@ -106,9 +109,11 @@ const MeasurementCanvas = forwardRef<MeasurementCanvasRef, MeasurementCanvasProp
initialBenchmarkData = [], initialBenchmarkData = [],
initialMeasurementDataLeft = [], initialMeasurementDataLeft = [],
initialMeasurementDataRight = [], initialMeasurementDataRight = [],
initMeasurementCalibrationData = [],
initialAnalysisData = [], initialAnalysisData = [],
showBenchmark = true, showBenchmark = true,
showAnalysis = true, showAnalysis = true,
showCalibration = false
} = props; } = props;
// Stage 物理中心(像素) // Stage 物理中心(像素)
@ -141,6 +146,7 @@ const MeasurementCanvas = forwardRef<MeasurementCanvasRef, MeasurementCanvasProp
const rightPointsRef = useRef<Point[]>([...initialMeasurementDataRight]); const rightPointsRef = useRef<Point[]>([...initialMeasurementDataRight]);
const [measurementDataLeft, setMeasurementDataLeftState] = useState<Point[]>(initialMeasurementDataLeft); const [measurementDataLeft, setMeasurementDataLeftState] = useState<Point[]>(initialMeasurementDataLeft);
const [measurementDataRight, setMeasurementDataRightState] = useState<Point[]>(initialMeasurementDataRight); const [measurementDataRight, setMeasurementDataRightState] = useState<Point[]>(initialMeasurementDataRight);
const [measurementCalibrationData, setMeasurementCalibrationDataState] = useState<Point[]>(initMeasurementCalibrationData);
const [measurementData, setMeasurementDataState] = useState<Point[]>([]); const [measurementData, setMeasurementDataState] = useState<Point[]>([]);
const refreshInterval = 50; const refreshInterval = 50;
const refreshTimer = useRef<number | null>(null); const refreshTimer = useRef<number | null>(null);
@ -190,6 +196,10 @@ const MeasurementCanvas = forwardRef<MeasurementCanvasRef, MeasurementCanvasProp
redraw: () => { redraw: () => {
setScale((prev) => prev); setScale((prev) => prev);
}, },
setMeasurementCalibrationData: (data: Point[])=>{
setMeasurementCalibrationDataState(data)
}
})); }));
const stageRef = useRef<any>(null); const stageRef = useRef<any>(null);
@ -482,9 +492,11 @@ const MeasurementCanvas = forwardRef<MeasurementCanvasRef, MeasurementCanvasProp
); );
}; };
const renderMeasurementCurveRight = () => {
if (measurementDataRight.length === 0) return null;
const pts = measurementDataRight
//校准线
const [points, setPoints] = useState<{x:number;y:number}[]>([])
const renderMeasurementCalibration = () => {
if (!measurementCalibrationData || measurementCalibrationData.length === 0) return null;
const pts = measurementCalibrationData
.map((pt) => { .map((pt) => {
const p = transform(pt); const p = transform(pt);
return [p.x, p.y]; return [p.x, p.y];
@ -498,6 +510,79 @@ const MeasurementCanvas = forwardRef<MeasurementCanvasRef, MeasurementCanvasProp
tension={1} tension={1}
lineCap="round" lineCap="round"
lineJoin="round" lineJoin="round"
ref={lineRef}
draggable
onDragStart={onHandDragStrat}
onDragEnd={onHandleDragEnd}
onDragMove={onHandleDragMove}
/>
);
}
// 拖动开始时的 X 坐标
const [dragStartX, setDragStartX] = useState(0);
// 拖动开始时的 Y 坐标
const [dragStartY, setDragStartY] = useState(0);
// 拖动偏移量 X
const [dragOffsetX, setDragOffsetX] = useState(0);
// 拖动偏移量 Y
const [dragOffsetY, setDragOffsetY] = useState(0);
// 是否正在拖动
const [isDragging, setIsDragging] = useState(false);
// 折线的引用
const lineRef = useRef<any>(null);
// 拖动开始的处理函数
const onHandDragStrat = (e:any) => {
setIsDragging(true);
if (lineRef.current) {
const pos = lineRef.current.position();
setDragStartX(pos.x);
setDragStartY(pos.y);
const offset = stageRef.current.getPointerPosition();
if (offset) {
setDragOffsetX(offset.x);
setDragOffsetY(offset.y);
}
console.log('start========', pos.x, pos.y, offset.x, offset.y)
}
};
// 拖动过程中的处理函数
const onHandleDragMove = (e:any) => {
if (isDragging && lineRef.current && e.nativeEvent) {
const pos = lineRef.current.position();
const newX = pos.x + (e.nativeEvent.offsetX - dragOffsetX);
const newY = pos.y + (e.nativeEvent.offsetY - dragOffsetY);
lineRef.current.position({ x: newX, y: newY });
console.log('Move========', pos.x, pos.y, newX, newY)
}
};
// 拖动结束的处理函数
const onHandleDragEnd = (e:any) => {
const newPoints = lineRef.current.points();
const newCoords = [];
for (let i = 0; i < newPoints.length; i += 2) {
newCoords.push({ x: newPoints[i], y: newPoints[i + 1] });
}
console.log('原坐标点:', measurementCalibrationData);
console.log('移动后的新坐标点:', newCoords);
};
const renderMeasurementCurveRight = () => {
if (measurementDataRight.length === 0) return null;
const pts = measurementDataRight.map((pt) => {
const p = transform(pt);
return [p.x, p.y];
}).flat();
return (
<Line
points={pts}
stroke="red"
strokeWidth={2}
tension={1}
lineCap="round"
lineJoin="round"
/> />
); );
}; };
@ -534,7 +619,6 @@ const MeasurementCanvas = forwardRef<MeasurementCanvasRef, MeasurementCanvasProp
); );
}); });
}; };
return ( return (
<div <div
style={{ style={{
@ -556,11 +640,17 @@ const MeasurementCanvas = forwardRef<MeasurementCanvasRef, MeasurementCanvasProp
onWheel={handleWheel} onWheel={handleWheel}
style={{ background: "#fff" }} style={{ background: "#fff" }}
> >
<Layer> <Layer>
{showGrid && renderGridAndAxes()} {showGrid && renderGridAndAxes()}
{/**基线层 */}
{showBenchmark && renderBenchmarkShapes()} {showBenchmark && renderBenchmarkShapes()}
{renderMeasurementCurveLeft()}
{renderMeasurementCurveRight()}
{/**左线层 校准时不显示*/}
{!showCalibration && renderMeasurementCurveLeft()}
{/**右线层 */}
{!showCalibration && renderMeasurementCurveRight()}
{/**校准层 */}
{showCalibration && renderMeasurementCalibration()}
{renderMeasurementCurve()} {renderMeasurementCurve()}
{showAnalysis && renderAnalysis()} {showAnalysis && renderAnalysis()}
</Layer> </Layer>

15
src/services/measure/analysis.ts

@ -124,3 +124,18 @@ export function getPointsById(params:{id:number}){
}); });
} }
//测量记录校正测量
export function getAlignPointById(params:{id:number}){
return httpRequest<BaseResponse>({
url: `/api/measurement-data/getAlignPointById/${params.id}`,
method: "POST",
});
}
//开始测量校正测量
export function getAlignPointsByRailSize(params:{railSize:string}){
return httpRequest<BaseResponse>({
url: `/api/align/catch/points/${params.railSize}`,
method: "GET",
});
}

1
src/services/measure/type.ts

@ -16,6 +16,7 @@ export type DetailTable = {
direction:string; direction:string;
dataSource:string; dataSource:string;
extraDesc:string; extraDesc:string;
railSize:string;
} }
export type MeasureRecord = { export type MeasureRecord = {

13
src/services/wsTypes.ts

@ -137,4 +137,15 @@ export type DeviceStatus = {
path: "/api/profiler-state/get-state"; path: "/api/profiler-state/get-state";
}; };
export type Datagram = TrackRecordSig | TaskState | ContextMessage | MeasureState | ChannelMessage | DeviceStatus;
export type ProgressStatus = {
messageType: "SYNC";
data: {
progress: number; //同步进度
message: string; //消息
success: boolean;//同步状态
};
path: "/get-task-progress";
};
export type Datagram = TrackRecordSig | TaskState | ContextMessage | MeasureState | ChannelMessage | DeviceStatus | ProgressStatus;

4
src/store/index.ts

@ -5,6 +5,7 @@ import counterSlice from "./features/counterSlice";
import contextSlice from "./features/contextSlice"; import contextSlice from "./features/contextSlice";
import deviceStateSlice from "./device/deviceState"; import deviceStateSlice from "./device/deviceState";
import orgStateSlice from "./ktj/orgState"; import orgStateSlice from "./ktj/orgState";
import measureStateSlice from "./measure/measureState";
// configureStore创建一个redux数据 // configureStore创建一个redux数据
const store = configureStore({ const store = configureStore({
// 合并多个Slice // 合并多个Slice
@ -12,7 +13,8 @@ const store = configureStore({
counter: counterSlice, counter: counterSlice,
context: contextSlice, context: contextSlice,
deviceState: deviceStateSlice, deviceState: deviceStateSlice,
orgState: orgStateSlice
orgState: orgStateSlice,
measureState:measureStateSlice
}, },
}); });

8
src/store/ktj/orgState.ts

@ -3,14 +3,18 @@ import { createSlice } from '@reduxjs/toolkit';
const orgSlice = createSlice({ const orgSlice = createSlice({
name: 'orgData', name: 'orgData',
initialState: { initialState: {
value: null
value: null,
syncDataProcess:0
}, },
reducers: { reducers: {
setOrgData: (state, action) => { setOrgData: (state, action) => {
state.value = action.payload; state.value = action.payload;
},
setSyncData: (state, action) => {
state.syncDataProcess = action.payload
} }
} }
}); });
export const { setOrgData } = orgSlice.actions;
export const { setOrgData, setSyncData } = orgSlice.actions;
export default orgSlice.reducer; export default orgSlice.reducer;

22
src/store/measure/measureState.ts

@ -0,0 +1,22 @@
import { createSlice } from "@reduxjs/toolkit";
const initialState = {
gxCode:''
}
// 创建一个 Slice
export const measureStateSlice = createSlice({
name: "measureState",
initialState,
// 定义 reducers 并生成关联的操作
reducers: {
// 更新轨型
updateGxState: (state, { payload }) => {
state.gxCode = payload.gxCode || "GX-60";
},
},
});
export const { updateGxState } = measureStateSlice.actions;
// 默认导出
export default measureStateSlice.reducer;
Loading…
Cancel
Save