You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

597 lines
19 KiB

import StepItem from '../components/StepItem';
import { useNavigate } from 'react-router-dom';
import CustomNavBar from '../components/CustomNavBar';
import MeasurementCanvas, {
BenchmarkShape,
MeasurementCanvasRef,
Point,
} from '../components/konva/MeasurementCanvas';
import { useEffect, useRef, useState } from 'react';
import RailTypeBtn from '../components/RailTypeBtn';
import { Cascader, Dialog, Mask, Picker, SpinLoading, Toast } from 'antd-mobile';
import { useAppDispatch, useAppSelector } from '../utils/hooks';
import { updateMeasureData, updateTaskState } from '../store/features/measureSlice';
import Bridge from '../utils/bridge';
import { selectLabeledKtjOrgs, updateRailPoints } from '../store/features/baseData';
import { updateOrg } from '../store/features/contextSlice';
import { selectOrgTextArr } from '../store';
import icon_left from '../assets/icon_left.svg';
import icon_right from '../assets/icon_right.svg';
import icon_up from '../assets/icon_up.svg';
import icon_down from '../assets/icon_down.svg';
import icon_leftR from '../assets/icon_leftR.svg';
import icon_rightR from '../assets/icon_rightR.svg';
export default function Measure() {
const navigate = useNavigate();
const dispatch = useAppDispatch();
const labeledKtjOrgs = useAppSelector(selectLabeledKtjOrgs);
const orgTextArr = useAppSelector(selectOrgTextArr);
const measureState = useAppSelector((state) => state.measure);
const contextState = useAppSelector((state) => state.context);
const baseState = useAppSelector((state) => state.baseData);
const [railPickerVisible, setRailPickerVisible] = useState(false);
const [railId, setRailId] = useState<(number | string | null)[]>([]);
const canvasRef = useRef<MeasurementCanvasRef>(null);
const [railSize, setRailSize] = useState<number | string | null>();
const iconWidth = 35;
const [showStandard, setShowStandard] = useState(false);
// 默认选中第一个轨型
useEffect(() => {
if (baseState.railTypes.length > 0) {
let railData = baseState.railTypes[0];
setRailId([railData.id]);
setRailSize(railData.code);
}
}, [baseState.railTypes]);
function drawRailBaseLine(points: string) {
const benchmarkShapes = JSON.parse(points) as BenchmarkShape[];
if (canvasRef.current) {
canvasRef.current.setBenchmarkData(benchmarkShapes);
}
}
// 检查轨型有没有坐标,如果有,绘制轨型基准线,如果没,拉取再绘制其线
useEffect(() => {
if (railId.length > 0) {
const r = baseState.railTypes.find((rail) => rail.id === railId[0]);
if (!r) return;
if (!!r.points) {
drawRailBaseLine(r.points);
return;
}
Bridge.getTrackPoint({ code: r.code }).then((res) => {
if (res.success) {
dispatch(updateRailPoints(res.data));
drawRailBaseLine(res.data.points!);
} else {
Toast.show(res.message);
}
});
}
}, [baseState.railTypes, dispatch, railId]);
// 绘制测量坐标线
useEffect(() => {
if (canvasRef.current) {
canvasRef.current.setMeasurementDataLeft(measureState.leftPoints);
}
}, [measureState.leftPoints]);
useEffect(() => {
if (canvasRef.current) {
canvasRef.current.setMeasurementDataRight(measureState.rightPoints);
}
}, [measureState.rightPoints]);
// 左右两测量完成,转换后的测量线(两线合一线)
useEffect(() => {
if (canvasRef.current && measureState.measureFinishData.length) {
canvasRef.current?.setMeasurementCalibrationData(measureState.measureFinishData);
// setshowCalibration(true);
setShowMeasureFinish(true)
setCaloading(false)
}
}, [measureState.measureFinishData])
useEffect(() => {
if(measureState.measureStatus === 'FINISH_RECORD'){
setCaloading(true)
}
else {
setCaloading(false)
}
}, [measureState.measureStatus])
const onSaveClick = () => {
dispatch(updateMeasureData(newMeasureData));
navigate('/measure/save');
};
const [caloading, setCaloading] = useState(false);
const [showCalibration, setshowCalibration] = useState(false);
const [showMeasureFinish, setShowMeasureFinish] = useState(false);
const onCalibrationBtnClick = () => {
setCaloading(true);
Bridge.alignPoints({ railSize: railSize || 'GX-60' })
.then((res) => {
if (res.success) {
setshowCalibration(true);
canvasRef.current?.setMeasurementCalibrationData(res.data);
} else {
}
setCaloading(false);
})
.catch((e) => {
setCaloading(false);
Toast.show({
content: <span></span>,
position: 'top',
});
});
};
const [initStart, setInitStart] = useState(false);
const onStartClick = () => {
setshowCalibration(false);
setShowMeasureFinish(false)
dispatch(updateMeasureData([]));
if (!contextState.device.connected) {
Dialog.alert({
content: '蓝牙未连接,请先连接蓝牙',
onConfirm: () => {
navigate('/home/bluetooth');
},
});
return;
}
if (baseState.ktjOrgs.length === 0) {
Dialog.alert({
content: '请在基础数据同步完成后重试',
onConfirm: () => {
navigate('/home/mine');
},
});
return;
}
if (!contextState.currOrgCode) {
Dialog.alert({
content: '请选择铁路局/工务段/线路',
onConfirm: () => {
onOrgBarClick();
},
});
return;
}
// if (contextState.device.power < 20) {
// Toast.show("电量低于20%,请充电后测量");
// return;
// }
Bridge.startMeasure().then((res) => {
if (res.success) {
dispatch(updateTaskState('START_RECORD_SIG'));
} else {
Toast.show(res.message);
}
setInitStart(true);
setShowStandard(false)
});
};
const onOrgBarClick = async () => {
if (baseState.ktjOrgs.length === 0) {
Dialog.alert({
content: '请在基础数据同步完成后重试',
onConfirm: () => {
navigate('/home/mine');
},
});
return;
}
const value = await Cascader.prompt({
options: labeledKtjOrgs,
placeholder: '请选择',
});
// Toast.show(value ? `你选择了 ${value.join(' - ')}` : '你没有进行选择');
if (value) {
dispatch(updateOrg(value as string[]));
}
};
const [state, setState] = useState({
left_ready: 'none',
right_ready: 'none',
left_begin: 'none',
right_begin: 'none',
left_end: 'none',
right_end: 'none',
});
const [status, setStatus] = useState(0)
useEffect(() => {
console.log(measureState.taskState)
switch (measureState.taskState) {
case 'WAITING_FOR_RECORD_THE_1ST_SIDE':
setStatus(1);
setState({
left_ready: 'ongoing',
right_ready: 'none',
left_begin: 'none',
right_begin: 'none',
left_end: 'none',
right_end: 'none',
});
break;
case 'WAITING_FOR_RECORD_THE_2ND_SIDE':
setStatus(2)
break;
case 'START_RECORD_SIG':
setState({
left_ready: 'ongoing',
right_ready: 'none',
left_begin: 'none',
right_begin: 'none',
left_end: 'none',
right_end: 'none',
});
break;
case 'START_RECORD_LEFT':
case 'START_RECORD_RIGHT':
if (status === 1) {
setState({
left_ready: 'done',
right_ready: 'none',
left_begin: 'ongoing',
right_begin: 'none',
left_end: 'none',
right_end: 'none',
});
} else if (status === 2) {
setState({
left_ready: 'done',
right_ready: 'done',
left_begin: 'done',
right_begin: 'ongoing',
left_end: 'done',
right_end: 'none',
});
}
break;
case 'FINISH_RECORD_LEFT':
case 'FINISH_RECORD_RIGHT':
if (status === 1) {
setState({
left_ready: 'done',
right_ready: 'none',
left_begin: 'done',
right_begin: 'none',
left_end: 'done',
right_end: 'none',
});
} else if (status === 2) {
setState({
left_ready: 'done',
right_ready: 'done',
left_begin: 'done',
right_begin: 'done',
left_end: 'done',
right_end: 'done',
});
}
break;
case 'FINISH_RECORD':
setState({
left_ready: 'done',
right_ready: 'done',
left_begin: 'done',
right_begin: 'done',
left_end: 'done',
right_end: 'done',
});
// 测量完成,显示基线
setShowStandard(true)
break;
default:
setState({
left_ready: 'none',
right_ready: 'none',
left_begin: 'none',
right_begin: 'none',
left_end: 'none',
right_end: 'none',
});
}
}, [measureState.taskState, status]);
function railName() {
return baseState.railTypes.find((r) => r.id === railId[0])?.name || '';
}
function onRailSizeChange(ids: (number | string | null)[]) {
if (ids && ids.length) {
setRailId(ids);
let id = ids[0];
const codes = baseState.railTypes.map((item) => {
if (item.id === id) {
return item.code;
}
return item.code;
});
if (codes && codes.length) {
setRailSize(codes[0]);
}
}
}
//上下移动
const timerRef = useRef<NodeJS.Timeout | null>(null);
const handlePressStart = (type: string) => {
timerRef.current = setInterval(() => {
console.log('你进行了长按操作!');
onHandleMove(type);
}, 500);
};
const handlePressEnd = () => {
if (timerRef.current) {
clearInterval(timerRef.current);
timerRef.current = null;
}
};
const onMoveLine = (type: string) => {
console.log('这是点击');
onHandleMove(type);
};
const onHandleMove = (type: string) => {
let list = canvasRef.current?.getMeasurementCalibrationData();
if (list && list.length) {
list.forEach((item) => {
if (type === 'up') {
//向上移动,原数据减y X轴不动
item.y = item.y - distance / 1000;
}
if (type === 'down') {
//向上移动,原数据加y X轴不动
item.y = item.y + distance / 1000;
}
if (type === 'left') {
//向左移动,原数据减x Y轴不动
item.x = item.x - distance / 1000;
}
if (type === 'right') {
//向右移动,原数据加x Y轴不动
item.x = item.x + distance / 1000;
}
});
canvasRef.current?.setMeasurementCalibrationData(list);
setNewMeasureData(list);
}
};
const handleRotationPressStart = (type: string) => {
timerRef.current = setInterval(() => {
onRotationLine(type);
}, 500);
};
//旋转
let [measurementRotation] = useState<number>(0);
let [newMeasureData, setNewMeasureData] = useState<Point[]>();
let [angle] = useState<number>(5); //角度单位 分
let [distance] = useState<number>(100);
const onRotationLine = (type: string) => {
let mrValue = 0;
if (type === 'left') {
//逆时针
mrValue = measurementRotation - ((angle / 60) * Math.PI) / 180;
}
if (type === 'right') {
//顺时针
mrValue = measurementRotation + ((angle / 60) * Math.PI) / 180;
}
let list = canvasRef.current?.getMeasurementCalibrationData();
if (list && list.length) {
list.forEach((item, index) => {
let cloneItem = rotatePoint(item, mrValue);
item.x = cloneItem.x;
item.y = cloneItem.y;
});
canvasRef.current?.setMeasurementCalibrationData(list);
setNewMeasureData(list);
}
};
const rotatePoint = (pt: { x: number; y: number }, angle: number) => {
const item = {
x: pt.x * Math.cos(angle) - pt.y * Math.sin(angle),
y: pt.x * Math.sin(angle) + pt.y * Math.cos(angle),
};
return item;
};
const handleContextMenu = (e: any) => {
e.preventDefault();
};
return (
<>
<div className="relative pt-[--navBarHeight]">
<div className="absolute top-0 w-full z-10">
<CustomNavBar title={'测量'}></CustomNavBar>
</div>
<main className="home-page-content overflow-x-hidden overflow-y-auto">
<div className="relative h-0 p-0 pb-[70%]">
{/**正在校准时的loading */}
{caloading && (
<Mask opacity="thin" className="h-[100vh] flex justify-center items-center">
<div style={{ margin: '45%' }}>
<SpinLoading color="#5c92b4" />
<div className="w-[100px] mt-[20px] text-[#5c92b4]">...</div>
</div>
</Mask>
)}
{/**测量区 */}
<div className="absolute left-0 right-0 top-0 bottom-0 bg-title">
<MeasurementCanvas
width={window.innerWidth}
height={window.innerWidth * 0.7}
logicalExtent={{ minX: -45, maxX: 45, minY: -18, maxY: 52 }}
gridStep={3}
origin={{ x: 0, y: 20 }}
pixelPerMm={window.innerWidth / 90}
maxZoom={8}
showGrid={true}
showBenchmark={showStandard}
showAnalysis={false}
showScale={false}
scaleInterval={1}
showCoordinates={false}
showCalibration={showCalibration || showMeasureFinish}
ref={canvasRef}
/>
{/**选择轨型区 */}
{railId.length > 0 && (
<div className="absolute left-1 bottom-1">
<RailTypeBtn text={railName()} onClick={() => setRailPickerVisible(true)} />
</div>
)}
</div>
</div>
{/**局段线区 */}
<section
className="h-10 bg-[#e3e8f5] flex justify-between items-center px-4"
onClick={onOrgBarClick}
>
<p className="text-text" style={{ color: contextState.currOrgCode ? '#333' : 'red' }}>
{contextState.currOrgCode ? orgTextArr.join('/') : '点击此处选择铁路局和工务段'}
</p>
<span className="text-primary underline"></span>
</section>
{/**手动校准区 */}
{showCalibration && (
<section className="h-10 bg-[#e3e8f5] flex justify-between items-center px-4">
<img
width={iconWidth}
src={icon_left}
onClick={() => onMoveLine('left')}
onTouchStart={() => handlePressStart('left')}
onTouchEnd={handlePressEnd}
onContextMenu={handleContextMenu}
className="text-[20px] ml-[5px]"
alt="左移"
/>
<img
width={iconWidth}
src={icon_right}
onClick={() => onMoveLine('right')}
onTouchStart={() => handlePressStart('right')}
onTouchEnd={handlePressEnd}
onContextMenu={handleContextMenu}
className="text-[20px] ml-[5px]"
alt="右移"
/>
<img
width={iconWidth}
src={icon_up}
onClick={() => onMoveLine('up')}
onTouchStart={() => handlePressStart('up')}
onTouchEnd={handlePressEnd}
onContextMenu={handleContextMenu}
className="text-[20px] ml-[5px]"
alt="上移"
/>
<img
width={iconWidth}
src={icon_down}
onClick={() => onMoveLine('down')}
onTouchStart={() => handlePressStart('down')}
onTouchEnd={handlePressEnd}
onContextMenu={handleContextMenu}
className="text-[20px] ml-[5px]"
alt="下移"
/>
<img
width={iconWidth}
src={icon_leftR}
onClick={() => onRotationLine('left')}
onTouchStart={() => handleRotationPressStart('left')}
onTouchEnd={handlePressEnd}
onContextMenu={handleContextMenu}
className="text-[20px] ml-[5px]"
alt="逆时针旋转"
/>
<img
width={iconWidth}
src={icon_rightR}
onClick={() => onRotationLine('right')}
onTouchStart={() => handleRotationPressStart('right')}
onTouchEnd={handlePressEnd}
onContextMenu={handleContextMenu}
className="text-[20px] ml-[5px]"
alt="顺时针旋转"
/>
</section>
)}
{/**按钮操作区 */}
<section className="flex items-center gap-4 px-4 my-4">
<button className="btn-contained rounded-md text-sm h-10 flex-1" onClick={onStartClick}>
{!initStart ||
(measureState.leftPoints.length > 0 && measureState.rightPoints.length > 0)
? '开始测量'
: '重新测量'}
</button>
<button
className="btn-contained rounded-md text-sm h-10 flex-1"
disabled={measureState.taskState !== 'FINISH_RECORD'}
onClick={onSaveClick}
>
</button>
<button
className="btn-contained rounded-md text-sm h-10 flex-1"
disabled={measureState.taskState !== 'FINISH_RECORD'}
onClick={onCalibrationBtnClick}
>
</button>
</section>
{/**测量状态区 */}
<section className="grid grid-cols-2 gap-[10px]">
<StepItem state={state.left_ready} text={'等待测量'} />
<StepItem state={state.right_ready} text={'等待测量另一侧'} />
<StepItem state={state.left_begin} text={'正在进行测量'} />
<StepItem state={state.right_begin} text={'正在进行测量'} />
<StepItem state={state.left_end} text={'一侧测量完成'} />
<StepItem state={state.right_end} text={'测量已完成'} />
</section>
</main>
</div>
<Picker
columns={[baseState.railTypes.map((t) => ({ label: t.name, value: t.id }))]}
visible={railPickerVisible}
onClose={() => {
setRailPickerVisible(false);
}}
value={railId}
onConfirm={(v) => {
onRailSizeChange(v);
}}
/>
</>
);
}