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.
231 lines
6.4 KiB
231 lines
6.4 KiB
import { ipcMain } from 'electron'
|
|
import dgram from 'dgram'
|
|
import net from 'net'
|
|
import { IPC_EVENT } from '../renderer/src/common/ipcEvents.js'
|
|
import fs from 'fs'
|
|
|
|
// 全局保存所有TCP连接和相关信息
|
|
const tcpClients = new Map()
|
|
// 保存待处理的请求,用于关联响应
|
|
const pendingRequests = new Map()
|
|
|
|
export function registerIpRouter() {
|
|
ipcMain.on(IPC_EVENT.DEVICE_SEARCH, searchDevice)
|
|
ipcMain.on(IPC_EVENT.DEVICE_CONNECT, connectDevice)
|
|
ipcMain.on(IPC_EVENT.DEVICE_DISCONNECT, disconnectDevice)
|
|
ipcMain.handle(IPC_EVENT.SENSORS_GET, sensorLoad) // 改为handle,支持异步返回
|
|
}
|
|
|
|
const searchDevice = (event) => {
|
|
const message = Buffer.from(JSON.stringify({ command: 'name', type: 'get' }))
|
|
const PORT = 2230
|
|
const BROADCAST_ADDR = '255.255.255.255'
|
|
const udpClient = dgram.createSocket('udp4')
|
|
|
|
let timer = null
|
|
const resultMap = new Map()
|
|
|
|
udpClient.bind(() => {
|
|
udpClient.setBroadcast(true)
|
|
udpClient.send(message, 0, message.length, PORT, BROADCAST_ADDR, (err) => {
|
|
if (err) {
|
|
console.error('UDP send failed', err)
|
|
udpClient.close()
|
|
} else {
|
|
console.log('UDP send successful')
|
|
}
|
|
})
|
|
})
|
|
|
|
udpClient.on('message', (msg, rinfo) => {
|
|
try {
|
|
resultMap.set(rinfo.address, {
|
|
from: rinfo.address,
|
|
data: msg.toString()
|
|
})
|
|
} catch (e) {
|
|
console.error('parse UDP message failed:', e)
|
|
}
|
|
if (timer) clearTimeout(timer)
|
|
timer = setTimeout(() => {
|
|
udpClient.close()
|
|
console.log('UDP socket closed after timeout')
|
|
event.reply && event.reply(IPC_EVENT.DEVICE_SEARCH_REPLY, Array.from(resultMap.values()))
|
|
}, 1000)
|
|
})
|
|
|
|
udpClient.on('error', (err) => {
|
|
console.error('UDP error:', err)
|
|
udpClient.close()
|
|
event.reply && event.reply(IPC_EVENT.DEVICE_SEARCH_REPLY, Array.from(resultMap.values()))
|
|
})
|
|
}
|
|
|
|
const connectDevice = (event, { ip, port }) => {
|
|
if (!ip || !port) {
|
|
event.reply(IPC_EVENT.DEVICE_CONNECT_REPLY, { success: false, error: '参数缺失' })
|
|
return
|
|
}
|
|
if (tcpClients.has(ip)) {
|
|
event.reply(IPC_EVENT.DEVICE_CONNECT_REPLY, { success: false, error: '已连接' })
|
|
return
|
|
}
|
|
|
|
const client = new net.Socket()
|
|
|
|
// 保存连接信息,包括event sender用于后续通信
|
|
const connectionInfo = {
|
|
client,
|
|
eventSender: event.sender,
|
|
ip,
|
|
port
|
|
}
|
|
|
|
client.connect(Number(port), ip, () => {
|
|
event.reply(IPC_EVENT.DEVICE_CONNECT_REPLY, { success: true, ip })
|
|
tcpClients.set(ip, connectionInfo)
|
|
})
|
|
|
|
let buffer = ''
|
|
|
|
client.on('data', (data) => {
|
|
buffer += data.toString()
|
|
let index
|
|
while ((index = buffer.indexOf('\n')) !== -1) {
|
|
const line = buffer.slice(0, index)
|
|
buffer = buffer.slice(index + 1)
|
|
if (!line.trim()) continue
|
|
|
|
let msg
|
|
try {
|
|
msg = JSON.parse(line)
|
|
console.log('Received command:', `${msg.command}:${msg.type}`)
|
|
} catch (e) {
|
|
console.error('TCP data parse error:', e)
|
|
fs.appendFileSync('error_log.txt', line + '\n')
|
|
continue
|
|
}
|
|
|
|
if (!msg || !msg.command) {
|
|
console.log('invalid msg format:', msg)
|
|
continue
|
|
}
|
|
|
|
// 处理TCP响应
|
|
handleTcpResponse(ip, msg)
|
|
}
|
|
})
|
|
|
|
client.on('error', (err) => {
|
|
event.reply(IPC_EVENT.DEVICE_CONNECT_REPLY, { success: false, error: err.message })
|
|
client.destroy()
|
|
tcpClients.delete(ip)
|
|
// 清理该IP的所有待处理请求
|
|
clearPendingRequestsByIp(ip)
|
|
})
|
|
|
|
client.on('close', () => {
|
|
tcpClients.delete(ip)
|
|
clearPendingRequestsByIp(ip)
|
|
})
|
|
}
|
|
|
|
const disconnectDevice = (event, { ip }) => {
|
|
const connectionInfo = tcpClients.get(ip)
|
|
if (connectionInfo) {
|
|
connectionInfo.client.destroy()
|
|
tcpClients.delete(ip)
|
|
clearPendingRequestsByIp(ip)
|
|
event.reply(IPC_EVENT.DEVICE_DISCONNECT_REPLY, { success: true })
|
|
} else {
|
|
event.reply(IPC_EVENT.DEVICE_DISCONNECT_REPLY, { success: false, error: '未连接' })
|
|
}
|
|
}
|
|
|
|
// 处理TCP响应
|
|
const handleTcpResponse = (ip, msg) => {
|
|
const connectionInfo = tcpClients.get(ip)
|
|
if (!connectionInfo) return
|
|
|
|
switch (`${msg.command}:${msg.type}`) {
|
|
case IPC_EVENT.RESULT_REPLY:
|
|
connectionInfo.eventSender.send(IPC_EVENT.RESULT_REPLY, { ip, ...msg })
|
|
break
|
|
case IPC_EVENT.IMAGE_REPLY:
|
|
connectionInfo.eventSender.send(IPC_EVENT.IMAGE_REPLY, { ip, ...msg })
|
|
break
|
|
case IPC_EVENT.SENSORS_GET:
|
|
{
|
|
const sensorRequest = pendingRequests.get(`${ip}${IPC_EVENT.SENSORS_GET}`)
|
|
if (sensorRequest) {
|
|
// 响应传感器加载请求
|
|
sensorRequest.resolve({ success: true, data: msg })
|
|
pendingRequests.delete(`${ip}${IPC_EVENT.SENSORS_GET}`)
|
|
}
|
|
}
|
|
break
|
|
case IPC_EVENT.HEARTBEAT_REPLY:
|
|
// 心跳处理
|
|
break
|
|
|
|
default:
|
|
console.warn('unknown command:', `${msg.command}:${msg.type}`)
|
|
}
|
|
}
|
|
|
|
// 传感器加载 - 改为异步处理
|
|
const sensorLoad = async (event, { ip }) => {
|
|
return new Promise((resolve, reject) => {
|
|
const connectionInfo = tcpClients.get(ip)
|
|
if (!connectionInfo) {
|
|
resolve({ success: false, error: '设备未连接' })
|
|
return
|
|
}
|
|
|
|
// 生成请求ID并保存待处理请求
|
|
const requestKey = `${ip}${IPC_EVENT.SENSORS_GET}`
|
|
|
|
// 设置超时
|
|
const timeout = setTimeout(() => {
|
|
pendingRequests.delete(requestKey)
|
|
resolve({ success: false, error: '请求超时' })
|
|
}, 10000) // 10秒超时
|
|
|
|
// 保存请求信息
|
|
pendingRequests.set(requestKey, {
|
|
resolve: (result) => {
|
|
clearTimeout(timeout)
|
|
resolve(result)
|
|
},
|
|
reject: (error) => {
|
|
clearTimeout(timeout)
|
|
reject(error)
|
|
},
|
|
timestamp: Date.now()
|
|
})
|
|
|
|
// 发送传感器加载命令
|
|
const command = { command: 'sensors', type: 'get', values: '' }
|
|
const message = JSON.stringify(command) + '\n\n'
|
|
|
|
connectionInfo.client.write(message, (err) => {
|
|
if (err) {
|
|
pendingRequests.delete(requestKey)
|
|
clearTimeout(timeout)
|
|
resolve({ success: false, error: err.message })
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
// 清理指定IP的所有待处理请求
|
|
const clearPendingRequestsByIp = (ip) => {
|
|
const keysToDelete = []
|
|
for (const [key, request] of pendingRequests) {
|
|
if (key.startsWith(`${ip}_`)) {
|
|
request.resolve({ success: false, error: '连接已断开' })
|
|
keysToDelete.push(key)
|
|
}
|
|
}
|
|
keysToDelete.forEach((key) => pendingRequests.delete(key))
|
|
}
|
|
|