有源标靶上位机
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

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