|
@ -3,12 +3,17 @@ import dgram from 'dgram' |
|
|
import net from 'net' |
|
|
import net from 'net' |
|
|
import { IPC_EVENT } from '../renderer/src/common/ipcEvents.js' |
|
|
import { IPC_EVENT } from '../renderer/src/common/ipcEvents.js' |
|
|
import fs from 'fs' |
|
|
import fs from 'fs' |
|
|
// 全局保存所有TCP连接
|
|
|
|
|
|
|
|
|
// 全局保存所有TCP连接和相关信息
|
|
|
const tcpClients = new Map() |
|
|
const tcpClients = new Map() |
|
|
|
|
|
// 保存待处理的请求,用于关联响应
|
|
|
|
|
|
const pendingRequests = new Map() |
|
|
|
|
|
|
|
|
export function registerIpRouter() { |
|
|
export function registerIpRouter() { |
|
|
ipcMain.on(IPC_EVENT.DEVICE_SEARCH, searchDevice) // 设备搜索
|
|
|
ipcMain.on(IPC_EVENT.DEVICE_SEARCH, searchDevice) |
|
|
ipcMain.on(IPC_EVENT.DEVICE_CONNECT, connectDevice) // 设备连接
|
|
|
ipcMain.on(IPC_EVENT.DEVICE_CONNECT, connectDevice) |
|
|
ipcMain.on(IPC_EVENT.DEVICE_DISCONNECT, disconnectDevice) // 设备断开
|
|
|
ipcMain.on(IPC_EVENT.DEVICE_DISCONNECT, disconnectDevice) |
|
|
|
|
|
ipcMain.handle(IPC_EVENT.SENSORS_GET, sensorLoad) // 改为handle,支持异步返回
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const searchDevice = (event) => { |
|
|
const searchDevice = (event) => { |
|
@ -34,7 +39,6 @@ const searchDevice = (event) => { |
|
|
|
|
|
|
|
|
udpClient.on('message', (msg, rinfo) => { |
|
|
udpClient.on('message', (msg, rinfo) => { |
|
|
try { |
|
|
try { |
|
|
// 以 IP 为 key,自动去重
|
|
|
|
|
|
resultMap.set(rinfo.address, { |
|
|
resultMap.set(rinfo.address, { |
|
|
from: rinfo.address, |
|
|
from: rinfo.address, |
|
|
data: msg.toString() |
|
|
data: msg.toString() |
|
@ -42,20 +46,17 @@ const searchDevice = (event) => { |
|
|
} catch (e) { |
|
|
} catch (e) { |
|
|
console.error('parse UDP message failed:', e) |
|
|
console.error('parse UDP message failed:', e) |
|
|
} |
|
|
} |
|
|
// 每收到一条消息就重置定时器
|
|
|
|
|
|
if (timer) clearTimeout(timer) |
|
|
if (timer) clearTimeout(timer) |
|
|
timer = setTimeout(() => { |
|
|
timer = setTimeout(() => { |
|
|
udpClient.close() |
|
|
udpClient.close() |
|
|
console.log('UDP socket closed after timeout') |
|
|
console.log('UDP socket closed after timeout') |
|
|
// 关闭后统一回复所有结果(已去重)
|
|
|
|
|
|
event.reply && event.reply(IPC_EVENT.DEVICE_SEARCH_REPLY, Array.from(resultMap.values())) |
|
|
event.reply && event.reply(IPC_EVENT.DEVICE_SEARCH_REPLY, Array.from(resultMap.values())) |
|
|
}, 1000) // 1秒内没有新消息就关闭
|
|
|
}, 1000) |
|
|
}) |
|
|
}) |
|
|
|
|
|
|
|
|
udpClient.on('error', (err) => { |
|
|
udpClient.on('error', (err) => { |
|
|
console.error('UDP error:', err) |
|
|
console.error('UDP error:', err) |
|
|
udpClient.close() |
|
|
udpClient.close() |
|
|
// 出错时也可以回复已收到的内容
|
|
|
|
|
|
event.reply && event.reply(IPC_EVENT.DEVICE_SEARCH_REPLY, Array.from(resultMap.values())) |
|
|
event.reply && event.reply(IPC_EVENT.DEVICE_SEARCH_REPLY, Array.from(resultMap.values())) |
|
|
}) |
|
|
}) |
|
|
} |
|
|
} |
|
@ -69,10 +70,20 @@ const connectDevice = (event, { ip, port }) => { |
|
|
event.reply(IPC_EVENT.DEVICE_CONNECT_REPLY, { success: false, error: '已连接' }) |
|
|
event.reply(IPC_EVENT.DEVICE_CONNECT_REPLY, { success: false, error: '已连接' }) |
|
|
return |
|
|
return |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const client = new net.Socket() |
|
|
const client = new net.Socket() |
|
|
|
|
|
|
|
|
|
|
|
// 保存连接信息,包括event sender用于后续通信
|
|
|
|
|
|
const connectionInfo = { |
|
|
|
|
|
client, |
|
|
|
|
|
eventSender: event.sender, |
|
|
|
|
|
ip, |
|
|
|
|
|
port |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
client.connect(Number(port), ip, () => { |
|
|
client.connect(Number(port), ip, () => { |
|
|
event.reply(IPC_EVENT.DEVICE_CONNECT_REPLY, { success: true, ip }) |
|
|
event.reply(IPC_EVENT.DEVICE_CONNECT_REPLY, { success: true, ip }) |
|
|
tcpClients.set(ip, client) |
|
|
tcpClients.set(ip, connectionInfo) |
|
|
}) |
|
|
}) |
|
|
|
|
|
|
|
|
let buffer = '' |
|
|
let buffer = '' |
|
@ -84,50 +95,137 @@ const connectDevice = (event, { ip, port }) => { |
|
|
const line = buffer.slice(0, index) |
|
|
const line = buffer.slice(0, index) |
|
|
buffer = buffer.slice(index + 1) |
|
|
buffer = buffer.slice(index + 1) |
|
|
if (!line.trim()) continue |
|
|
if (!line.trim()) continue |
|
|
|
|
|
|
|
|
let msg |
|
|
let msg |
|
|
try { |
|
|
try { |
|
|
msg = JSON.parse(line) |
|
|
msg = JSON.parse(line) |
|
|
console.log(msg.command) |
|
|
console.log('Received command:', `${msg.command}:${msg.type}`) |
|
|
} catch (e) { |
|
|
} catch (e) { |
|
|
console.error('TCP data parse error:', e) |
|
|
console.error('TCP data parse error:', e) |
|
|
fs.appendFileSync('error_log.txt', line + '\n') |
|
|
fs.appendFileSync('error_log.txt', line + '\n') |
|
|
continue |
|
|
continue |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (!msg || !msg.command) { |
|
|
if (!msg || !msg.command) { |
|
|
console.log('invalid msg format:', msg) |
|
|
console.log('invalid msg format:', msg) |
|
|
continue |
|
|
continue |
|
|
} |
|
|
} |
|
|
switch (msg.command) { |
|
|
|
|
|
case 'result': |
|
|
// 处理TCP响应
|
|
|
event.sender.send(IPC_EVENT.SENSOR_DATA, { ip, ...msg }) |
|
|
handleTcpResponse(ip, msg) |
|
|
break |
|
|
|
|
|
case 'image': |
|
|
|
|
|
event.sender.send(IPC_EVENT.IMAGE_DATA, { ip, ...msg }) |
|
|
|
|
|
break |
|
|
|
|
|
case 'heartbeat': |
|
|
|
|
|
break |
|
|
|
|
|
default: |
|
|
|
|
|
console.warn('unknow command type:', msg.command) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
} |
|
|
}) |
|
|
}) |
|
|
|
|
|
|
|
|
client.on('error', (err) => { |
|
|
client.on('error', (err) => { |
|
|
event.reply(IPC_EVENT.DEVICE_CONNECT_REPLY, { success: false, error: err.message }) |
|
|
event.reply(IPC_EVENT.DEVICE_CONNECT_REPLY, { success: false, error: err.message }) |
|
|
client.destroy() |
|
|
client.destroy() |
|
|
tcpClients.delete(ip) |
|
|
tcpClients.delete(ip) |
|
|
|
|
|
// 清理该IP的所有待处理请求
|
|
|
|
|
|
clearPendingRequestsByIp(ip) |
|
|
}) |
|
|
}) |
|
|
|
|
|
|
|
|
client.on('close', () => { |
|
|
client.on('close', () => { |
|
|
tcpClients.delete(ip) |
|
|
tcpClients.delete(ip) |
|
|
|
|
|
clearPendingRequestsByIp(ip) |
|
|
}) |
|
|
}) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const disconnectDevice = (event, { ip }) => { |
|
|
const disconnectDevice = (event, { ip }) => { |
|
|
const client = tcpClients.get(ip) |
|
|
const connectionInfo = tcpClients.get(ip) |
|
|
if (client) { |
|
|
if (connectionInfo) { |
|
|
client.destroy() |
|
|
connectionInfo.client.destroy() |
|
|
tcpClients.delete(ip) |
|
|
tcpClients.delete(ip) |
|
|
|
|
|
clearPendingRequestsByIp(ip) |
|
|
event.reply(IPC_EVENT.DEVICE_DISCONNECT_REPLY, { success: true }) |
|
|
event.reply(IPC_EVENT.DEVICE_DISCONNECT_REPLY, { success: true }) |
|
|
} else { |
|
|
} else { |
|
|
event.reply(IPC_EVENT.DEVICE_DISCONNECT_REPLY, { success: false, error: '未连接' }) |
|
|
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)) |
|
|
|
|
|
} |
|
|