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.
206 lines
7.0 KiB
206 lines
7.0 KiB
import { useState, useEffect } from 'react'
|
|
import styles from './SiderHeader.module.css'
|
|
import { Flex, Button, Input, message, AutoComplete } from 'antd'
|
|
import { IPC_EVENT } from '../../common/ipcEvents.js'
|
|
import useDeviceStore from '../../stores/deviceStore'
|
|
import {
|
|
VideoCameraFilled,
|
|
GoldFilled,
|
|
ApiFilled,
|
|
SecurityScanFilled,
|
|
SlidersFilled
|
|
} from '@ant-design/icons'
|
|
import SystemSettings from '../SystemSettings/SystemSettings'
|
|
import MeasurementPointSetting from '../MeasurementPointSetting/MeasurementPointSetting'
|
|
import PropTypes from 'prop-types'
|
|
|
|
function SiderHeader({ showSystemSettings = true }) {
|
|
const [searching, setSearching] = useState(false)
|
|
const [deviceList, setDeviceList] = useState([])
|
|
const [selectedDevice, setSelectedDevice] = useState(undefined)
|
|
const [devicePort, setDevicePort] = useState('2230')
|
|
const [connected, setConnected] = useState(null) // 保存已连接设备IP
|
|
|
|
// 使用设备store
|
|
const setConnectedDevice = useDeviceStore((state) => state.setConnectedDevice)
|
|
const clearConnectedDevice = useDeviceStore((state) => state.clearConnectedDevice)
|
|
const setDeviceList_store = useDeviceStore((state) => state.setDeviceList)
|
|
|
|
// 监听设备搜索结果
|
|
useEffect(() => {
|
|
const handler = (event, results) => {
|
|
setSearching(false)
|
|
if (Array.isArray(results) && results.length > 0) {
|
|
const ips = results.map((item) => item.from)
|
|
const devices = ips.map((ip) => ({ value: ip, label: ip }))
|
|
setDeviceList(devices)
|
|
// 同时更新到store中
|
|
setDeviceList_store(ips.map((ip) => ({ ip, name: ip })))
|
|
message.success(`发现${ips.length}台设备`)
|
|
} else {
|
|
setDeviceList([])
|
|
setDeviceList_store([])
|
|
message.warning('未发现设备')
|
|
}
|
|
}
|
|
window.electron.ipcRenderer.on(IPC_EVENT.DEVICE_SEARCH_REPLY, handler)
|
|
return () => {
|
|
window.electron.ipcRenderer.removeListener(IPC_EVENT.DEVICE_SEARCH_REPLY, handler)
|
|
}
|
|
}, [setDeviceList_store])
|
|
|
|
// 监听设备连接/断开结果
|
|
useEffect(() => {
|
|
const connectHandler = (event, result) => {
|
|
if (result.success) {
|
|
setConnected(result.ip)
|
|
// 更新到store中
|
|
setConnectedDevice({
|
|
ip: result.ip,
|
|
port: devicePort,
|
|
connectedAt: new Date().toISOString()
|
|
})
|
|
message.success('设备连接成功')
|
|
} else {
|
|
setConnected(null)
|
|
clearConnectedDevice()
|
|
message.error('设备连接失败: ' + (result.error || '未知错误'))
|
|
}
|
|
}
|
|
const disconnectHandler = (event, result) => {
|
|
setConnected(null)
|
|
// 清除store中的连接信息
|
|
clearConnectedDevice()
|
|
if (result.success) {
|
|
message.success('设备已断开')
|
|
} else {
|
|
message.error('断开失败: ' + (result.error || '未知错误'))
|
|
}
|
|
}
|
|
window.electron.ipcRenderer.on(IPC_EVENT.DEVICE_CONNECT_REPLY, connectHandler)
|
|
window.electron.ipcRenderer.on(IPC_EVENT.DEVICE_DISCONNECT_REPLY, disconnectHandler)
|
|
return () => {
|
|
window.electron.ipcRenderer.removeListener(IPC_EVENT.DEVICE_CONNECT_REPLY, connectHandler)
|
|
window.electron.ipcRenderer.removeListener(
|
|
IPC_EVENT.DEVICE_DISCONNECT_REPLY,
|
|
disconnectHandler
|
|
)
|
|
}
|
|
}, [devicePort, setConnectedDevice, clearConnectedDevice])
|
|
|
|
// 搜索设备
|
|
const handleSearchDevice = () => {
|
|
setSearching(true)
|
|
setDeviceList([])
|
|
setSelectedDevice(undefined)
|
|
window.electron.ipcRenderer.send(IPC_EVENT.DEVICE_SEARCH)
|
|
}
|
|
|
|
// 连接/断开设备
|
|
const handleConnectOrDisconnect = () => {
|
|
console.log('点击连接,当前selectedDevice:', selectedDevice, '端口:', devicePort)
|
|
|
|
if (connected) {
|
|
// 已连接,断开
|
|
window.electron.ipcRenderer.send(IPC_EVENT.DEVICE_DISCONNECT, { ip: connected })
|
|
} else {
|
|
// 未连接,连接
|
|
if (!selectedDevice || !devicePort) {
|
|
message.error('请输入或选择设备IP地址并填写端口')
|
|
return
|
|
}
|
|
|
|
// IP格式验证
|
|
const ipPattern =
|
|
/^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
|
|
if (!ipPattern.test(selectedDevice)) {
|
|
message.error('请输入有效的IP地址格式 (例如: 192.168.1.100)')
|
|
return
|
|
}
|
|
|
|
console.log('发送连接请求:', { ip: selectedDevice, port: devicePort })
|
|
window.electron.ipcRenderer.send(IPC_EVENT.DEVICE_CONNECT, {
|
|
ip: selectedDevice,
|
|
port: devicePort
|
|
})
|
|
}
|
|
}
|
|
|
|
// 除断开连接按钮外,其他UI是否禁用
|
|
const uiDisabled = !!connected
|
|
|
|
return (
|
|
<Flex vertical>
|
|
<Flex className={styles.header}>
|
|
<div className={styles.titlePanel}>
|
|
挠度仪控制面板
|
|
<SlidersFilled />
|
|
</div>
|
|
<Flex vertical gap={8} className={styles.contentPanel}>
|
|
<Flex align="center">
|
|
<VideoCameraFilled className={styles.icon} />
|
|
<span className={styles.label}>设备列表:</span>
|
|
<AutoComplete
|
|
className={styles.deviceSelect}
|
|
placeholder="搜索设备或输入IP地址"
|
|
value={selectedDevice}
|
|
options={deviceList}
|
|
onChange={(value) => {
|
|
setSelectedDevice(value)
|
|
}}
|
|
onSelect={(value) => {
|
|
setSelectedDevice(value)
|
|
}}
|
|
style={{ minWidth: 120, flex: 1 }}
|
|
disabled={searching || uiDisabled}
|
|
allowClear
|
|
filterOption={(inputValue, option) =>
|
|
option?.value?.toLowerCase().indexOf(inputValue.toLowerCase()) !== -1
|
|
}
|
|
/>
|
|
</Flex>
|
|
|
|
<Flex align="center">
|
|
<GoldFilled className={styles.icon} />
|
|
<span className={styles.label}>设备端口:</span>
|
|
<Input
|
|
className={styles.deviceInput}
|
|
value={devicePort}
|
|
onChange={(e) => setDevicePort(e.target.value)}
|
|
disabled={uiDisabled}
|
|
/>
|
|
</Flex>
|
|
|
|
<Flex gap={8}>
|
|
<Button
|
|
className={styles.actionButton}
|
|
type="primary"
|
|
icon={<SecurityScanFilled />}
|
|
loading={searching}
|
|
onClick={handleSearchDevice}
|
|
disabled={searching || uiDisabled}
|
|
>
|
|
{searching ? '正在搜索...' : '搜索设备'}
|
|
</Button>
|
|
<Button
|
|
className={styles.actionButton}
|
|
icon={<ApiFilled />}
|
|
danger={!!connected}
|
|
type={connected ? 'primary' : 'default'}
|
|
onClick={handleConnectOrDisconnect}
|
|
>
|
|
{connected ? '断开连接' : '连接设备'}
|
|
</Button>
|
|
</Flex>
|
|
</Flex>
|
|
</Flex>
|
|
{showSystemSettings ? <SystemSettings /> : <MeasurementPointSetting />}
|
|
</Flex>
|
|
)
|
|
}
|
|
|
|
SiderHeader.propTypes = {
|
|
showSystemSettings: PropTypes.bool
|
|
}
|
|
|
|
export default SiderHeader
|
|
|