diff --git a/package-lock.json b/package-lock.json index a00e1d6..4379b92 100644 --- a/package-lock.json +++ b/package-lock.json @@ -47,7 +47,6 @@ "jest": "^27.4.3", "jest-resolve": "^27.4.2", "jest-watch-typeahead": "^1.0.0", - "konva": "^8.3.5", "mini-css-extract-plugin": "^2.4.5", "postcss-flexbugs-fixes": "^5.0.2", "postcss-loader": "^6.2.1", @@ -12227,7 +12226,8 @@ "url": "https://github.com/sponsors/lavrton" } ], - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/language-subtag-registry": { "version": "0.3.23", diff --git a/scripts/start.js b/scripts/start.js index 4104798..deac8a2 100644 --- a/scripts/start.js +++ b/scripts/start.js @@ -44,7 +44,7 @@ if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) { } // Tools like Cloud9 rely on this. -const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3000; +const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3010; const HOST = process.env.HOST || '0.0.0.0'; if (process.env.HOST) { diff --git a/src/assets/icon_bluetooth.svg b/src/assets/icon_bluetooth.svg new file mode 100644 index 0000000..a8b6c42 --- /dev/null +++ b/src/assets/icon_bluetooth.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 0c13eaf..5bff310 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -1,4 +1,5 @@ import icon_usb from "../assets/icon_usb.svg"; +import icon_bluetooth from "../assets/icon_bluetooth.svg"; import icon_battery from "../assets/icon_battery.svg"; import icon_avatar from "../assets/icon_avatar.svg"; import icon_logout from "../assets/icon_logout.svg"; @@ -18,11 +19,24 @@ export default function Header() { const navigate = useNavigate(); const dispatch = useAppDispatch(); const deviceInfo = useAppSelector(store => store.context.device); - const deviceState = useAppSelector(store => store.deviceState); + // const deviceState = useAppSelector(store => store.context.device); const userInfo = useAppSelector(store => store.context.user.loginUser); //获取当前websocet的状态 + const showBlueImg = () => { - if (deviceState.isConnect) { + if (deviceInfo.isConnected && deviceInfo.connectedType === "BLE_CHANNEL") { + return ( + +
+ +
+
+ ); + } + return null; + }; + const showUsbImg = () => { + if (deviceInfo.isConnected && deviceInfo.connectedType === "UART_CHANNEL") { return (
@@ -84,6 +98,22 @@ export default function Header() { }, ]; + const getBtBlueContent = () => { + return ( +
+
+
+ +
蓝牙已连接
+
+
+
sn码:{deviceInfo.sn}
+
+
+
+ ); + }; + //设备已连接 const getBtContent = () => { return ( @@ -94,8 +124,7 @@ export default function Header() {
设备已连接
-
sn码:{deviceState.sn}
-
设备描述:{deviceState.descriptivePortName}
+
sn码:{deviceInfo.sn}
@@ -124,6 +153,7 @@ export default function Header() { )} {showBlueImg()} + {showUsbImg()}
diff --git a/src/pages/measure/components/MeasureAction.tsx b/src/pages/measure/components/MeasureAction.tsx index 7056831..0266250 100644 --- a/src/pages/measure/components/MeasureAction.tsx +++ b/src/pages/measure/components/MeasureAction.tsx @@ -119,7 +119,10 @@ export default function MeasureAction() { // 开始/重新测量按钮点击事件 const onStart = useCallback(() => { - //电量低于20%时不可进行测量 + if (!deviceInfo.isConnected) { + message.error("请先连接设备"); + return; + } if(deviceInfo.power < 20){ message.error('电量低于20%,请充电后再测量!') return @@ -151,7 +154,7 @@ export default function MeasureAction() { setStartBtnText("重新测量"); } }); - }, [initialStatusList, startBtnText]); + }, [initialStatusList, startBtnText, deviceInfo]); //停止测量 const onStop = () => { diff --git a/src/pages/measure/components/konva/MeasurementCanvas.tsx b/src/pages/measure/components/konva/MeasurementCanvas.tsx index 227e654..ae7e5d4 100644 --- a/src/pages/measure/components/konva/MeasurementCanvas.tsx +++ b/src/pages/measure/components/konva/MeasurementCanvas.tsx @@ -150,7 +150,7 @@ const MeasurementCanvas = forwardRef(initialMeasurementDataRight); const [measurementCalibrationData, setMeasurementCalibrationDataState] = useState(initMeasurementCalibrationData); const [measurementData, setMeasurementDataState] = useState([]); - const refreshInterval = 50; + const refreshInterval = 10; const refreshTimer = useRef(null); useEffect(() => { if (!refreshTimer.current) { diff --git a/src/pages/system/Setting.tsx b/src/pages/system/Setting.tsx index 1bb7e24..5eb41e1 100644 --- a/src/pages/system/Setting.tsx +++ b/src/pages/system/Setting.tsx @@ -1,28 +1,51 @@ -import { useState, useEffect, ReactNode } from 'react'; -import type { CascaderProps } from 'antd'; -import { Button, Cascader, Input, message } from 'antd'; +import { useState, useEffect } from 'react'; +import { Button, Cascader, Input, message, List, Typography } from 'antd'; import { getOrgListService } from '../../services/ktj/org'; -import { options, OrgItem } from '../../services/ktjTypes'; -import { child, GwdItem, orgCascaderType, systemItem } from './types'; +import { OrgItem } from '../../services/ktjTypes'; +import {bleItem, child, GwdItem, orgCascaderType, systemItem} from './types'; import { sysSet } from '../../services/user/system'; import { useAppDispatch, useAppSelector } from "../../utils/hooks"; import { updateSystemAccountState, updateSystemOrgState } from '../../store/system/systemSlice'; +import {createWebSocket, sharedWsUrl} from "../../services/socket"; +import {start, stop, disconnect, connect} from "../../services/ble/ble"; export default function Setting(){ useEffect(()=>{ queryRailData() + onBleStart() querySettingData() + return ()=>{ + onBleStop() + } },[]) const dispatch = useAppDispatch(); + const deviceInfo = useAppSelector(store => store.context.device); const systemState = useAppSelector((store) => store.systemState); const [systemList, setSystemList] = useState([]) const [accountInfo, setAccountInfo] = useState({}) const [orgInfo, setOrgInfo] = useState({}) const [orgValues, setOrgValues] = useState([]) + const [bleList, setBleList] = useState([]) + + // 创建 websocket 客户端 + const wsClient = createWebSocket(sharedWsUrl); function querySettingData(){ let systemInfo = systemState.systemInfo setSystemList(systemInfo) } + + useEffect(() => { + const subscription = wsClient.dataOb.subscribe(data => { + // @ts-ignore + if (data.messageType === "STATE" && data.path === "/api/ble/ble-list") { + // @ts-ignore + setBleList(data.data) + } + }); + wsClient.connect(); + return () => subscription.unsubscribe(); + }); + useEffect(()=>{ if(systemState.orgInfo){ const cloneOrgItem = systemState.orgInfo @@ -144,14 +167,94 @@ export default function Setting(){ }); } - return
-
-
铁路局:
-
- {accountInfo.name}: - {onAccountChange(e.target.value)}} className='w-[300px]'> - -
-
+ function onBleStart() { + start().then(() => { + message.success('蓝牙开始扫描') + }) + } + + function onBleStop() { + stop().then(() => { + }) + } + + function bleConnect(address: string) { + connect(address).then((res) => { + if (res.status !== 0) { + message.error(res.data.info); + return + } + message.success('蓝牙连接成功') + }) + } + + function bleDisconnect() { + disconnect().then(() => { + message.success('蓝牙断开连接') + }) + } + + const connectionStatus = () => { + if (!deviceInfo.isConnected) { + return ( +
+
附近设备
+ {bleList.map(item => { + return
+
+

+

{item.name}

+

{item.address}

+

+ { + item.connect ?

已连接

: + } + +
+
+ })} +
+ ); + } else { + if(deviceInfo.connectedType === 'UART_CHANNEL') { + return ( +
+ 已通过usb连接 +
sn码:{deviceInfo.sn}
+
+ ) + + }else if(deviceInfo.connectedType === 'BLE_CHANNEL') { + return ( +
+ 已通过蓝牙连接 + +
sn码:{deviceInfo.sn}
+
+ ) + } + } + }; + + + // @ts-ignore + return <>
+
+

系统配置

+
+
+ 铁路局: +
+
+ {accountInfo.name}: + {onAccountChange(e.target.value)}} className='w-[300px]'> + +
+
+

设备配置

+ {connectionStatus()} +
+
+ } \ No newline at end of file diff --git a/src/pages/system/types.ts b/src/pages/system/types.ts index bf9aa64..ce9c7e8 100644 --- a/src/pages/system/types.ts +++ b/src/pages/system/types.ts @@ -22,3 +22,9 @@ export type systemItem = { value?: string; } +export type bleItem = { + "name": string; + "address": string; + "connect": boolean +} + diff --git a/src/services/ble/ble.ts b/src/services/ble/ble.ts new file mode 100644 index 0000000..8811c8f --- /dev/null +++ b/src/services/ble/ble.ts @@ -0,0 +1,29 @@ +import httpRequest, {type BaseResponse} from "../httpRequest"; +export function start() { + return httpRequest({ + url: "/api/ble/list/start", + method: "POST", + }); +} + +export function stop() { + return httpRequest({ + url: "/api/ble/list/stop", + method: "POST", + }); +} + +export function connect(address: string) { + return httpRequest({ + url: `/api/ble/connect/${address}`, + method: "POST" + }); +} +export function disconnect() { + return httpRequest({ + url: `/api/ble/disconnect`, + method: "POST" + }); +} + + diff --git a/src/services/wsTypes.ts b/src/services/wsTypes.ts index d4875e2..6e94d92 100644 --- a/src/services/wsTypes.ts +++ b/src/services/wsTypes.ts @@ -129,10 +129,14 @@ export type DeviceStatus = { messageType: "STATE"; data: { isConnected: boolean; //是否链接 + connectedType: "UART_CHANNEL" | "BLE_CHANNEL"; + sn: string; power: number; //电量 inclinatorX: number; //x轴倾斜 inclinatorY: number; //y轴倾斜 temperature: number; //温度 + state: number; + flag: number }; path: "/api/profiler-state/get-state"; }; @@ -148,4 +152,14 @@ export type ProgressStatus = { path: "/get-task-progress"; }; +export type bleStatus = { + messageType: "STATE"; + data: { + name: string; + address: string; + connect: boolean; + }; + path: "/api/ble/ble-list"; +}; + export type Datagram = TrackRecordSig | TaskState | ContextMessage | MeasureState | ChannelMessage | DeviceStatus | ProgressStatus; diff --git a/src/store/ble/bleState.ts b/src/store/ble/bleState.ts new file mode 100644 index 0000000..ca327d6 --- /dev/null +++ b/src/store/ble/bleState.ts @@ -0,0 +1,31 @@ +// counterSlice.ts 文件 + +import { createSlice } from "@reduxjs/toolkit"; +import { ChannelMessage } from "../../services/wsTypes"; + +const initialState: ChannelMessage["data"] = { + isConnect: false, //是否连接 + connectPort: "COM4", //串口名 + sn: "", //连接的设备ID + descriptivePortName: "COM4 serial ch340", //用于详细系 +}; + +// 创建一个 Slice +export const bleStateSlice = createSlice({ + name: "bleState", + initialState, + // 定义 reducers 并生成关联的操作 + reducers: { + // 定义一个加的方法 + updateBleState: (state, { payload }) => { + state.isConnect = payload.isConnect; + state.connectPort = payload.connectPort; + state.sn = payload.sn; + state.descriptivePortName = payload.descriptivePortName; + }, + }, +}); +export const { updateBleState } = bleStateSlice.actions; + +// 默认导出 +export default bleStateSlice.reducer; diff --git a/src/store/features/contextSlice.ts b/src/store/features/contextSlice.ts index 2778c46..0d32142 100644 --- a/src/store/features/contextSlice.ts +++ b/src/store/features/contextSlice.ts @@ -12,11 +12,15 @@ const initialState: ContextSlice = { user: { loginFlag: false, loginUser: { nickname: "", userRole: "User", account: "" } }, newMeasureAfterSave: false, device: { - isConnected: true, //是否链接 + isConnected: false, //是否链接 + connectedType: "BLE_CHANNEL", power: 60, //电量 inclinatorX: 0.276, //x轴倾斜 inclinatorY: 3.019, //y轴倾斜 temperature: 32.026, //温度 + sn: '00000', + state: 1, + flag: 0 }, }; diff --git a/src/store/index.ts b/src/store/index.ts index c39f325..8f7878b 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -4,6 +4,7 @@ import { configureStore } from "@reduxjs/toolkit"; import counterSlice from "./features/counterSlice"; import contextSlice from "./features/contextSlice"; import deviceStateSlice from "./device/deviceState"; +import bleState from "./ble/bleState"; import orgStateSlice from "./ktj/orgState"; import measureStateSlice from "./measure/measureState"; import systemStateSlice from "./system/systemSlice"; @@ -15,6 +16,7 @@ const store = configureStore({ counter: counterSlice, context: contextSlice, deviceState: deviceStateSlice, + bleState: bleState, orgState: orgStateSlice, measureState:measureStateSlice, systemState:systemStateSlice