forked from qinjian/FlexometerSetup
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.
1554 lines
46 KiB
1554 lines
46 KiB
import { ipcMain } from 'electron'
|
|
import { dialog, shell } from 'electron'
|
|
import dgram from 'dgram'
|
|
import net from 'net'
|
|
import { appendFileSync, mkdirSync, existsSync } from 'fs'
|
|
import { dirname } from 'path'
|
|
import { IPC_EVENT } from '../renderer/src/common/ipcEvents.js'
|
|
import log from 'electron-log'
|
|
import ReconnectManager from './reconnectManager.js'
|
|
const TIMEOUT = 10000 // 10秒超时
|
|
const END_SEQUENCE = '\n\n' // 消息结束标志
|
|
// 全局保存所有TCP连接和相关信息
|
|
const tcpClients = new Map()
|
|
// 保存待处理的请求,用于关联响应
|
|
const pendingRequests = new Map()
|
|
|
|
// 创建重连管理器实例
|
|
const reconnectManager = new ReconnectManager()
|
|
// 处理重连尝试的回调函数
|
|
const handleAttemptReconnect = (ip, callback) => {
|
|
// 获取重连信息
|
|
const connectionData = reconnectManager.getReconnectStatus(ip)
|
|
if (!connectionData.config.enabled) {
|
|
callback(false, 'Reconnect disabled')
|
|
return
|
|
}
|
|
|
|
log.info(`Executing reconnect attempt for ${ip}`)
|
|
const client = new net.Socket()
|
|
|
|
// 设置连接超时为重连间隔的80%,确保在下次重连前完成
|
|
const reconnectInterval = reconnectManager.getConfig().interval * 1000
|
|
const connectionTimeout = Math.max(3000, Math.floor(reconnectInterval * 0.8))
|
|
client.setTimeout(connectionTimeout)
|
|
|
|
log.debug(
|
|
`Connection timeout set to ${connectionTimeout}ms (reconnect interval: ${reconnectInterval}ms)`
|
|
)
|
|
|
|
// 需要从某处获取端口和eventSender
|
|
const storedConnectionData = tcpClients.get(ip) || tcpConnectionData.get(ip)
|
|
if (!storedConnectionData) {
|
|
callback(false, 'Connection data not found')
|
|
return
|
|
}
|
|
|
|
const connectionInfo = {
|
|
client,
|
|
eventSender: storedConnectionData.eventSender,
|
|
ip: ip,
|
|
port: storedConnectionData.port
|
|
}
|
|
|
|
client.connect(Number(storedConnectionData.port), ip, () => {
|
|
log.info(`Reconnected successfully to ${ip}:${storedConnectionData.port}`)
|
|
|
|
// 连接成功,保存连接信息
|
|
tcpClients.set(ip, connectionInfo)
|
|
|
|
// 启动心跳检测
|
|
startHeartbeatCheck(ip)
|
|
|
|
// 通知渲染进程重连成功
|
|
storedConnectionData.eventSender.send(IPC_EVENT.DEVICE_CONNECT_REPLY, {
|
|
success: true,
|
|
ip: ip,
|
|
reconnected: true
|
|
})
|
|
|
|
callback(true)
|
|
})
|
|
|
|
// 设置数据处理
|
|
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)
|
|
} catch (e) {
|
|
log.error('TCP reconnect data parse error:', e)
|
|
continue
|
|
}
|
|
|
|
if (!msg || !msg.command) continue
|
|
handleTcpResponse(ip, msg)
|
|
}
|
|
})
|
|
|
|
client.on('error', (err) => {
|
|
log.warn(`Reconnect attempt failed for ${ip}:`, err.message)
|
|
client.destroy()
|
|
callback(false, err)
|
|
})
|
|
|
|
client.on('timeout', () => {
|
|
log.warn(`Reconnect attempt timeout for ${ip}`)
|
|
client.destroy()
|
|
callback(false, new Error('Connection timeout'))
|
|
})
|
|
|
|
client.on('close', () => {
|
|
log.debug(`Reconnect attempt connection closed for ${ip}`)
|
|
// 连接关闭由心跳检测处理
|
|
})
|
|
}
|
|
|
|
// 处理重连状态更新的回调函数
|
|
const handleReconnectStatus = (status) => {
|
|
// 通知所有渲染进程重连状态变化
|
|
const storedConnectionData = tcpConnectionData.get(status.ip)
|
|
if (storedConnectionData && storedConnectionData.eventSender) {
|
|
log.info(
|
|
`Sending reconnect status update for ${status.ip}: ${status.isReconnecting ? 'reconnecting' : 'idle'}`
|
|
)
|
|
storedConnectionData.eventSender.send(IPC_EVENT.RECONNECT_STATUS, {
|
|
ip: status.ip,
|
|
status: status.isReconnecting ? 'reconnecting' : 'idle',
|
|
attempts: status.attempts,
|
|
maxAttempts: status.maxAttempts,
|
|
lastError: status.lastError
|
|
})
|
|
} else {
|
|
log.warn(
|
|
`No stored connection data found for ${status.ip}, cannot send reconnect status update`
|
|
)
|
|
|
|
// 如果找不到存储的连接数据,尝试从活动连接中获取
|
|
const activeConnection = tcpClients.get(status.ip)
|
|
if (activeConnection && activeConnection.eventSender) {
|
|
log.info(`Using active connection data to send reconnect status for ${status.ip}`)
|
|
activeConnection.eventSender.send(IPC_EVENT.RECONNECT_STATUS, {
|
|
ip: status.ip,
|
|
status: status.isReconnecting ? 'reconnecting' : 'idle',
|
|
attempts: status.attempts,
|
|
maxAttempts: status.maxAttempts,
|
|
lastError: status.lastError
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
// 配置重连管理器事件监听
|
|
reconnectManager.on('attempt-reconnect', handleAttemptReconnect)
|
|
reconnectManager.on('reconnect-status', handleReconnectStatus)
|
|
|
|
// 存储连接数据以供重连使用
|
|
const tcpConnectionData = new Map() // 存储连接参数
|
|
|
|
// 心跳检测相关配置
|
|
const HEARTBEAT_INTERVAL = 5000 // 心跳检测间隔 5秒(设备每2秒发送,我们5秒检测一次)
|
|
const HEARTBEAT_TIMEOUT = 10000 // 心跳超时时间 10秒(设备每2秒发送,超过10秒没收到就认为断开)
|
|
const heartbeatTimers = new Map() // 保存每个IP的心跳检测定时器
|
|
const lastHeartbeatTime = new Map() // 保存每个IP的最后心跳时间
|
|
|
|
// 记录IPC命令的通用函数(事件类型自动拼接)
|
|
const logIPCCommand = (ip, command) => {
|
|
const eventType = `${command.command?.toUpperCase()}_${command.type?.toUpperCase()}`
|
|
log.info(`IPC Command from renderer - ${eventType} to ${ip}:`, command)
|
|
}
|
|
|
|
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.on(IPC_EVENT.RECONNECT_CONFIG, handleReconnectConfig)
|
|
ipcMain.handle(IPC_EVENT.SENSORS_GET, sensorLoad) // 获取传感器
|
|
ipcMain.handle(IPC_EVENT.SENSORS_SET, sensorSet) // 传感器设置处理
|
|
ipcMain.handle(IPC_EVENT.IMAGE_SEND_TIME_GET, imageSendTimeGet)
|
|
ipcMain.handle(IPC_EVENT.IMAGE_SEND_TIME_SET, imageSendTimeSet)
|
|
// 其他基本参数GET处理
|
|
ipcMain.handle(IPC_EVENT.ZERO_COUNT_GET, zeroCountGet)
|
|
ipcMain.handle(IPC_EVENT.RESULT_COUNT_GET, resultCountGet)
|
|
ipcMain.handle(IPC_EVENT.DATA_FPS_GET, dataFpsGet)
|
|
ipcMain.handle(IPC_EVENT.VIDEO_FPS_GET, videoFpsGet)
|
|
ipcMain.handle(IPC_EVENT.THRESHOLD_GET, thresholdGet)
|
|
ipcMain.handle(IPC_EVENT.INVALID_DATA_COUNT_GET, invalidDataCountGet)
|
|
// 其他基本参数SET处理
|
|
ipcMain.handle(IPC_EVENT.ZERO_COUNT_SET, zeroCountSet)
|
|
ipcMain.handle(IPC_EVENT.RESULT_COUNT_SET, resultCountSet)
|
|
ipcMain.handle(IPC_EVENT.DATA_FPS_SET, dataFpsSet)
|
|
ipcMain.handle(IPC_EVENT.VIDEO_FPS_SET, videoFpsSet)
|
|
ipcMain.handle(IPC_EVENT.THRESHOLD_SET, thresholdSet)
|
|
ipcMain.handle(IPC_EVENT.INVALID_DATA_COUNT_SET, invalidDataCountSet)
|
|
ipcMain.handle(IPC_EVENT.IMAGE_SEND_ENABLED, imageSendEnabledSet)
|
|
ipcMain.handle(IPC_EVENT.IS_CLEAR_ZERO, isClearZeroSet)
|
|
// 存储目录相关处理
|
|
ipcMain.handle(IPC_EVENT.OPEN_DIRECTORY, openDirectory)
|
|
ipcMain.handle(IPC_EVENT.SELECT_DIRECTORY, selectDirectory)
|
|
// 文件操作相关处理
|
|
ipcMain.handle(IPC_EVENT.ENSURE_DIRECTORY, ensureDirectory)
|
|
ipcMain.handle(IPC_EVENT.APPEND_TO_FILE, appendToFile)
|
|
ipcMain.handle(IPC_EVENT.CHECK_FILE_EXISTS, checkFileExists)
|
|
ipcMain.handle(IPC_EVENT.WRITE_CSV_HEADER, writeCSVHeader)
|
|
}
|
|
// 搜索设备
|
|
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')
|
|
|
|
const UDP_SEARCH_TIMEOUT = 5000 // 5秒总超时时间
|
|
const UDP_RESPONSE_DELAY = 1000 // 收到响应后等1秒收集更多设备
|
|
|
|
let timer = null
|
|
let globalTimer = null
|
|
const resultMap = new Map()
|
|
|
|
// 清理资源的函数
|
|
const cleanup = () => {
|
|
if (timer) clearTimeout(timer)
|
|
if (globalTimer) clearTimeout(globalTimer)
|
|
if (!udpClient.destroyed) {
|
|
udpClient.close()
|
|
}
|
|
}
|
|
|
|
// 发送结果并清理
|
|
const sendResultsAndCleanup = () => {
|
|
const results = Array.from(resultMap.values())
|
|
log.info(`UDP search completed, found ${results.length} devices`)
|
|
cleanup()
|
|
event.reply && event.reply(IPC_EVENT.DEVICE_SEARCH_REPLY, results)
|
|
}
|
|
|
|
// 设置全局超时 - 无论如何5秒后结束搜索
|
|
globalTimer = setTimeout(() => {
|
|
log.info('UDP search timeout - no devices found within timeout period')
|
|
sendResultsAndCleanup()
|
|
}, UDP_SEARCH_TIMEOUT)
|
|
|
|
udpClient.bind(() => {
|
|
udpClient.setBroadcast(true)
|
|
udpClient.send(message, 0, message.length, PORT, BROADCAST_ADDR, (err) => {
|
|
if (err) {
|
|
log.error('UDP send failed', err)
|
|
sendResultsAndCleanup()
|
|
} else {
|
|
log.info('UDP broadcast sent, waiting for responses...')
|
|
}
|
|
})
|
|
})
|
|
|
|
udpClient.on('message', (msg, rinfo) => {
|
|
try {
|
|
resultMap.set(rinfo.address, {
|
|
from: rinfo.address,
|
|
data: msg.toString()
|
|
})
|
|
log.info(`UDP response from ${rinfo.address}:`, msg.toString())
|
|
} catch (e) {
|
|
log.error('UDP message parse error', e)
|
|
}
|
|
|
|
// 每次收到消息后,重置响应延迟定时器
|
|
if (timer) clearTimeout(timer)
|
|
timer = setTimeout(() => {
|
|
log.info('UDP response collection timeout reached')
|
|
sendResultsAndCleanup()
|
|
}, UDP_RESPONSE_DELAY)
|
|
})
|
|
|
|
udpClient.on('error', (err) => {
|
|
log.error('UDP error:', err)
|
|
sendResultsAndCleanup()
|
|
})
|
|
}
|
|
|
|
// 连接设备
|
|
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)
|
|
|
|
// 启动心跳检测
|
|
startHeartbeatCheck(ip)
|
|
})
|
|
|
|
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)
|
|
} catch (e) {
|
|
log.error('TCP data parse error:', e)
|
|
continue
|
|
}
|
|
|
|
if (!msg || !msg.command) {
|
|
log.warn('invalid msg format:', msg)
|
|
continue
|
|
}
|
|
|
|
// 处理TCP响应
|
|
handleTcpResponse(ip, msg)
|
|
}
|
|
})
|
|
|
|
client.on('error', (err) => {
|
|
log.error('TCP connection 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', () => {
|
|
log.info(`TCP connection to ${ip} closed`)
|
|
const connectionInfo = tcpClients.get(ip)
|
|
if (connectionInfo) {
|
|
tcpClients.delete(ip)
|
|
clearPendingRequestsByIp(ip)
|
|
|
|
// 只有在非主动断开的情况下才启动重连
|
|
// 主动断开会先调用 disconnectDevice 函数
|
|
const reconnectConfig = reconnectManager.getConfig()
|
|
if (reconnectConfig.enabled) {
|
|
log.info(`Connection lost to ${ip}, starting reconnect...`)
|
|
// 保存连接数据供重连使用
|
|
tcpConnectionData.set(ip, {
|
|
port: port,
|
|
eventSender: connectionInfo.eventSender
|
|
})
|
|
reconnectManager.startReconnect(ip, 'Connection lost')
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
// 断开连接
|
|
const disconnectDevice = (event, { ip }) => {
|
|
const connectionInfo = tcpClients.get(ip)
|
|
if (connectionInfo) {
|
|
// 停止重连和心跳检测
|
|
reconnectManager.stopReconnect(ip)
|
|
stopHeartbeatCheck(ip)
|
|
|
|
connectionInfo.client.destroy()
|
|
tcpClients.delete(ip)
|
|
tcpConnectionData.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 = async (ip, msg) => {
|
|
const connectionInfo = tcpClients.get(ip)
|
|
if (!connectionInfo) return
|
|
|
|
// 记录TCP响应数据
|
|
const commandType = `${msg.command}:${msg.type}`
|
|
if (
|
|
commandType !== IPC_EVENT.RESULT_REPLY &&
|
|
commandType !== IPC_EVENT.IMAGE_REPLY &&
|
|
commandType !== IPC_EVENT.HEARTBEAT_REPLY
|
|
) {
|
|
log.info(`TCP Response from ${ip}:`, commandType, msg.values || msg)
|
|
}
|
|
|
|
switch (commandType) {
|
|
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) {
|
|
// 响应传感器加载请求
|
|
await delay()
|
|
sensorRequest.resolve({ success: true, data: msg })
|
|
pendingRequests.delete(`${ip}${IPC_EVENT.SENSORS_GET}`)
|
|
}
|
|
}
|
|
break
|
|
case IPC_EVENT.SENSORS_SET:
|
|
{
|
|
const sensorSetRequest = pendingRequests.get(`${ip}${IPC_EVENT.SENSORS_SET}`)
|
|
if (sensorSetRequest) {
|
|
// 响应传感器设置请求
|
|
await delay()
|
|
sensorSetRequest.resolve({ success: true, data: msg })
|
|
pendingRequests.delete(`${ip}${IPC_EVENT.SENSORS_SET}`)
|
|
}
|
|
}
|
|
break
|
|
case IPC_EVENT.IMAGE_SEND_TIME_GET:
|
|
{
|
|
const imageTimeRequest = pendingRequests.get(`${ip}${IPC_EVENT.IMAGE_SEND_TIME_GET}`)
|
|
if (imageTimeRequest) {
|
|
// 响应图像发送间隔获取请求
|
|
await delay()
|
|
imageTimeRequest.resolve({ success: true, data: msg })
|
|
|
|
pendingRequests.delete(`${ip}${IPC_EVENT.IMAGE_SEND_TIME_GET}`)
|
|
}
|
|
}
|
|
break
|
|
case IPC_EVENT.IMAGE_SEND_TIME_SET:
|
|
{
|
|
const imageTimeRequest = pendingRequests.get(`${ip}${IPC_EVENT.IMAGE_SEND_TIME_SET}`)
|
|
if (imageTimeRequest) {
|
|
// 响应图像发送间隔设置请求
|
|
await delay()
|
|
imageTimeRequest.resolve({ success: true, data: msg })
|
|
pendingRequests.delete(`${ip}${IPC_EVENT.IMAGE_SEND_TIME_SET}`)
|
|
}
|
|
}
|
|
break
|
|
// 其他基本参数GET响应处理
|
|
case IPC_EVENT.ZERO_COUNT_GET:
|
|
{
|
|
const paramRequest = pendingRequests.get(`${ip}${IPC_EVENT.ZERO_COUNT_GET}`)
|
|
if (paramRequest) {
|
|
await delay()
|
|
paramRequest.resolve({ success: true, data: msg })
|
|
pendingRequests.delete(`${ip}${IPC_EVENT.ZERO_COUNT_GET}`)
|
|
}
|
|
}
|
|
break
|
|
case IPC_EVENT.RESULT_COUNT_GET:
|
|
{
|
|
const paramRequest = pendingRequests.get(`${ip}${IPC_EVENT.RESULT_COUNT_GET}`)
|
|
if (paramRequest) {
|
|
await delay()
|
|
paramRequest.resolve({ success: true, data: msg })
|
|
pendingRequests.delete(`${ip}${IPC_EVENT.RESULT_COUNT_GET}`)
|
|
}
|
|
}
|
|
break
|
|
case IPC_EVENT.DATA_FPS_GET:
|
|
{
|
|
const paramRequest = pendingRequests.get(`${ip}${IPC_EVENT.DATA_FPS_GET}`)
|
|
if (paramRequest) {
|
|
await delay()
|
|
paramRequest.resolve({ success: true, data: msg })
|
|
pendingRequests.delete(`${ip}${IPC_EVENT.DATA_FPS_GET}`)
|
|
}
|
|
}
|
|
break
|
|
case IPC_EVENT.VIDEO_FPS_GET:
|
|
{
|
|
const paramRequest = pendingRequests.get(`${ip}${IPC_EVENT.VIDEO_FPS_GET}`)
|
|
if (paramRequest) {
|
|
await delay()
|
|
paramRequest.resolve({ success: true, data: msg })
|
|
pendingRequests.delete(`${ip}${IPC_EVENT.VIDEO_FPS_GET}`)
|
|
}
|
|
}
|
|
break
|
|
case IPC_EVENT.THRESHOLD_GET:
|
|
{
|
|
const paramRequest = pendingRequests.get(`${ip}${IPC_EVENT.THRESHOLD_GET}`)
|
|
if (paramRequest) {
|
|
await delay()
|
|
paramRequest.resolve({ success: true, data: msg })
|
|
pendingRequests.delete(`${ip}${IPC_EVENT.THRESHOLD_GET}`)
|
|
}
|
|
}
|
|
break
|
|
case IPC_EVENT.INVALID_DATA_COUNT_GET:
|
|
{
|
|
const paramRequest = pendingRequests.get(`${ip}${IPC_EVENT.INVALID_DATA_COUNT_GET}`)
|
|
if (paramRequest) {
|
|
await delay()
|
|
paramRequest.resolve({ success: true, data: msg })
|
|
pendingRequests.delete(`${ip}${IPC_EVENT.INVALID_DATA_COUNT_GET}`)
|
|
}
|
|
}
|
|
break
|
|
// 其他基本参数SET响应处理
|
|
case IPC_EVENT.ZERO_COUNT_SET:
|
|
{
|
|
const paramRequest = pendingRequests.get(`${ip}${IPC_EVENT.ZERO_COUNT_SET}`)
|
|
if (paramRequest) {
|
|
await delay()
|
|
paramRequest.resolve({ success: true, data: msg })
|
|
pendingRequests.delete(`${ip}${IPC_EVENT.ZERO_COUNT_SET}`)
|
|
}
|
|
}
|
|
break
|
|
case IPC_EVENT.RESULT_COUNT_SET:
|
|
{
|
|
const paramRequest = pendingRequests.get(`${ip}${IPC_EVENT.RESULT_COUNT_SET}`)
|
|
if (paramRequest) {
|
|
await delay()
|
|
paramRequest.resolve({ success: true, data: msg })
|
|
pendingRequests.delete(`${ip}${IPC_EVENT.RESULT_COUNT_SET}`)
|
|
}
|
|
}
|
|
break
|
|
case IPC_EVENT.DATA_FPS_SET:
|
|
{
|
|
const paramRequest = pendingRequests.get(`${ip}${IPC_EVENT.DATA_FPS_SET}`)
|
|
if (paramRequest) {
|
|
await delay()
|
|
paramRequest.resolve({ success: true, data: msg })
|
|
pendingRequests.delete(`${ip}${IPC_EVENT.DATA_FPS_SET}`)
|
|
}
|
|
}
|
|
break
|
|
case IPC_EVENT.VIDEO_FPS_SET:
|
|
{
|
|
const paramRequest = pendingRequests.get(`${ip}${IPC_EVENT.VIDEO_FPS_SET}`)
|
|
if (paramRequest) {
|
|
await delay()
|
|
paramRequest.resolve({ success: true, data: msg })
|
|
pendingRequests.delete(`${ip}${IPC_EVENT.VIDEO_FPS_SET}`)
|
|
}
|
|
}
|
|
break
|
|
case IPC_EVENT.THRESHOLD_SET:
|
|
{
|
|
const paramRequest = pendingRequests.get(`${ip}${IPC_EVENT.THRESHOLD_SET}`)
|
|
if (paramRequest) {
|
|
await delay()
|
|
paramRequest.resolve({ success: true, data: msg })
|
|
pendingRequests.delete(`${ip}${IPC_EVENT.THRESHOLD_SET}`)
|
|
}
|
|
}
|
|
break
|
|
case IPC_EVENT.INVALID_DATA_COUNT_SET:
|
|
{
|
|
const paramRequest = pendingRequests.get(`${ip}${IPC_EVENT.INVALID_DATA_COUNT_SET}`)
|
|
if (paramRequest) {
|
|
await delay()
|
|
paramRequest.resolve({ success: true, data: msg })
|
|
pendingRequests.delete(`${ip}${IPC_EVENT.INVALID_DATA_COUNT_SET}`)
|
|
}
|
|
}
|
|
break
|
|
case IPC_EVENT.IMAGE_SEND_ENABLED:
|
|
{
|
|
const imageSendRequest = pendingRequests.get(`${ip}${IPC_EVENT.IMAGE_SEND_ENABLED}`)
|
|
if (imageSendRequest) {
|
|
// 响应图像发送设置请求
|
|
await delay()
|
|
imageSendRequest.resolve({ success: true, data: msg })
|
|
pendingRequests.delete(`${ip}${IPC_EVENT.IMAGE_SEND_ENABLED}`)
|
|
}
|
|
}
|
|
break
|
|
case IPC_EVENT.IS_CLEAR_ZERO:
|
|
{
|
|
const clearZeroRequest = pendingRequests.get(`${ip}${IPC_EVENT.IS_CLEAR_ZERO}`)
|
|
if (clearZeroRequest) {
|
|
// 响应清零使能设置请求
|
|
await delay()
|
|
clearZeroRequest.resolve({ success: true, data: msg })
|
|
pendingRequests.delete(`${ip}${IPC_EVENT.IS_CLEAR_ZERO}`)
|
|
}
|
|
}
|
|
break
|
|
case IPC_EVENT.HEARTBEAT_REPLY:
|
|
// 更新心跳时间戳(设备每2秒发送心跳包)
|
|
lastHeartbeatTime.set(ip, Date.now())
|
|
// 心跳包不记录到日志,避免日志过多
|
|
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: '请求超时' })
|
|
}, TIMEOUT) // 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) + END_SEQUENCE
|
|
logIPCCommand(ip, command)
|
|
|
|
connectionInfo.client.write(message, (err) => {
|
|
if (err) {
|
|
pendingRequests.delete(requestKey)
|
|
clearTimeout(timeout)
|
|
resolve({ success: false, error: err.message })
|
|
}
|
|
})
|
|
})
|
|
}
|
|
// 传感器设置
|
|
const sensorSet = async (event, { ip, sensors }) => {
|
|
return new Promise((resolve, reject) => {
|
|
const connectionInfo = tcpClients.get(ip)
|
|
if (!connectionInfo) {
|
|
resolve({ success: false, error: '设备未连接' })
|
|
return
|
|
}
|
|
|
|
// 生成请求ID并保存待处理请求
|
|
const requestKey = `${ip}${IPC_EVENT.SENSORS_SET}`
|
|
|
|
// 设置超时
|
|
const timeout = setTimeout(() => {
|
|
pendingRequests.delete(requestKey)
|
|
resolve({ success: false, error: '请求超时' })
|
|
}, TIMEOUT) // 10秒超时
|
|
|
|
// 保存请求信息
|
|
pendingRequests.set(requestKey, {
|
|
resolve: (result) => {
|
|
clearTimeout(timeout)
|
|
resolve(result)
|
|
},
|
|
reject: (error) => {
|
|
clearTimeout(timeout)
|
|
reject(error)
|
|
},
|
|
timestamp: Date.now()
|
|
})
|
|
|
|
// 发送传感器设置命令
|
|
const command = { command: 'sensors', type: 'set', values: sensors }
|
|
const message = JSON.stringify(command) + END_SEQUENCE
|
|
logIPCCommand(ip, command)
|
|
|
|
connectionInfo.client.write(message, (err) => {
|
|
if (err) {
|
|
pendingRequests.delete(requestKey)
|
|
clearTimeout(timeout)
|
|
resolve({ success: false, error: err.message })
|
|
}
|
|
})
|
|
})
|
|
}
|
|
// 图像发送间隔获取
|
|
const imageSendTimeGet = 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.IMAGE_SEND_TIME_GET}`
|
|
|
|
// 设置超时
|
|
const timeout = setTimeout(() => {
|
|
pendingRequests.delete(requestKey)
|
|
resolve({ success: false, error: '请求超时' })
|
|
}, TIMEOUT) // 10秒超时
|
|
|
|
// 保存请求信息
|
|
pendingRequests.set(requestKey, {
|
|
resolve: (result) => {
|
|
clearTimeout(timeout)
|
|
resolve(result)
|
|
},
|
|
reject: (error) => {
|
|
clearTimeout(timeout)
|
|
reject(error)
|
|
},
|
|
timestamp: Date.now()
|
|
})
|
|
|
|
// 发送图像发送间隔获取命令
|
|
const command = { command: 'imageSendTime', type: 'get', values: '' }
|
|
const message = JSON.stringify(command) + END_SEQUENCE
|
|
logIPCCommand(ip, command)
|
|
|
|
connectionInfo.client.write(message, (err) => {
|
|
if (err) {
|
|
pendingRequests.delete(requestKey)
|
|
clearTimeout(timeout)
|
|
resolve({ success: false, error: err.message })
|
|
}
|
|
})
|
|
})
|
|
}
|
|
// 图像发送间隔设置
|
|
const imageSendTimeSet = async (event, { ip, value }) => {
|
|
return new Promise((resolve, reject) => {
|
|
const connectionInfo = tcpClients.get(ip)
|
|
if (!connectionInfo) {
|
|
resolve({ success: false, error: '设备未连接' })
|
|
return
|
|
}
|
|
|
|
// 生成请求ID并保存待处理请求
|
|
const requestKey = `${ip}${IPC_EVENT.IMAGE_SEND_TIME_SET}`
|
|
|
|
// 设置超时
|
|
const timeout = setTimeout(() => {
|
|
pendingRequests.delete(requestKey)
|
|
resolve({ success: false, error: '请求超时' })
|
|
}, TIMEOUT) // 10秒超时
|
|
|
|
// 保存请求信息
|
|
pendingRequests.set(requestKey, {
|
|
resolve: (result) => {
|
|
clearTimeout(timeout)
|
|
resolve(result)
|
|
},
|
|
reject: (error) => {
|
|
clearTimeout(timeout)
|
|
reject(error)
|
|
},
|
|
timestamp: Date.now()
|
|
})
|
|
|
|
// 发送图像发送间隔设置命令
|
|
const command = { command: 'imageSendTime', type: 'set', values: value }
|
|
const message = JSON.stringify(command) + END_SEQUENCE
|
|
logIPCCommand(ip, command)
|
|
|
|
connectionInfo.client.write(message, (err) => {
|
|
if (err) {
|
|
pendingRequests.delete(requestKey)
|
|
clearTimeout(timeout)
|
|
resolve({ success: false, error: err.message })
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
// zeroCount获取
|
|
const zeroCountGet = async (event, { ip }) => {
|
|
return new Promise((resolve, reject) => {
|
|
const connectionInfo = tcpClients.get(ip)
|
|
if (!connectionInfo) {
|
|
resolve({ success: false, error: '设备未连接' })
|
|
return
|
|
}
|
|
|
|
const requestKey = `${ip}${IPC_EVENT.ZERO_COUNT_GET}`
|
|
const timeout = setTimeout(() => {
|
|
pendingRequests.delete(requestKey)
|
|
resolve({ success: false, error: '请求超时' })
|
|
}, TIMEOUT)
|
|
|
|
pendingRequests.set(requestKey, {
|
|
resolve: (result) => {
|
|
clearTimeout(timeout)
|
|
resolve(result)
|
|
},
|
|
reject: (error) => {
|
|
clearTimeout(timeout)
|
|
reject(error)
|
|
},
|
|
timestamp: Date.now()
|
|
})
|
|
|
|
const command = { command: 'zeroCount', type: 'get', values: '' }
|
|
const message = JSON.stringify(command) + END_SEQUENCE
|
|
logIPCCommand(ip, command)
|
|
|
|
connectionInfo.client.write(message, (err) => {
|
|
if (err) {
|
|
pendingRequests.delete(requestKey)
|
|
clearTimeout(timeout)
|
|
resolve({ success: false, error: err.message })
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
// zeroCount设置
|
|
const zeroCountSet = async (event, { ip, value }) => {
|
|
return new Promise((resolve, reject) => {
|
|
const connectionInfo = tcpClients.get(ip)
|
|
if (!connectionInfo) {
|
|
resolve({ success: false, error: '设备未连接' })
|
|
return
|
|
}
|
|
|
|
const requestKey = `${ip}${IPC_EVENT.ZERO_COUNT_SET}`
|
|
const timeout = setTimeout(() => {
|
|
pendingRequests.delete(requestKey)
|
|
resolve({ success: false, error: '请求超时' })
|
|
}, TIMEOUT)
|
|
|
|
pendingRequests.set(requestKey, {
|
|
resolve: (result) => {
|
|
clearTimeout(timeout)
|
|
resolve(result)
|
|
},
|
|
reject: (error) => {
|
|
clearTimeout(timeout)
|
|
reject(error)
|
|
},
|
|
timestamp: Date.now()
|
|
})
|
|
|
|
const command = { command: 'zeroCount', type: 'set', values: value }
|
|
const message = JSON.stringify(command) + END_SEQUENCE
|
|
logIPCCommand(ip, command)
|
|
|
|
connectionInfo.client.write(message, (err) => {
|
|
if (err) {
|
|
pendingRequests.delete(requestKey)
|
|
clearTimeout(timeout)
|
|
resolve({ success: false, error: err.message })
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
// resultCount获取
|
|
const resultCountGet = async (event, { ip }) => {
|
|
return new Promise((resolve, reject) => {
|
|
const connectionInfo = tcpClients.get(ip)
|
|
if (!connectionInfo) {
|
|
resolve({ success: false, error: '设备未连接' })
|
|
return
|
|
}
|
|
|
|
const requestKey = `${ip}${IPC_EVENT.RESULT_COUNT_GET}`
|
|
const timeout = setTimeout(() => {
|
|
pendingRequests.delete(requestKey)
|
|
resolve({ success: false, error: '请求超时' })
|
|
}, TIMEOUT)
|
|
|
|
pendingRequests.set(requestKey, {
|
|
resolve: (result) => {
|
|
clearTimeout(timeout)
|
|
resolve(result)
|
|
},
|
|
reject: (error) => {
|
|
clearTimeout(timeout)
|
|
reject(error)
|
|
},
|
|
timestamp: Date.now()
|
|
})
|
|
|
|
const command = { command: 'resultCount', type: 'get', values: '' }
|
|
const message = JSON.stringify(command) + END_SEQUENCE
|
|
logIPCCommand('RESULT_COUNT_GET', ip, command)
|
|
|
|
connectionInfo.client.write(message, (err) => {
|
|
if (err) {
|
|
pendingRequests.delete(requestKey)
|
|
clearTimeout(timeout)
|
|
resolve({ success: false, error: err.message })
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
// resultCount设置
|
|
const resultCountSet = async (event, { ip, value }) => {
|
|
return new Promise((resolve, reject) => {
|
|
const connectionInfo = tcpClients.get(ip)
|
|
if (!connectionInfo) {
|
|
resolve({ success: false, error: '设备未连接' })
|
|
return
|
|
}
|
|
|
|
const requestKey = `${ip}${IPC_EVENT.RESULT_COUNT_SET}`
|
|
const timeout = setTimeout(() => {
|
|
pendingRequests.delete(requestKey)
|
|
resolve({ success: false, error: '请求超时' })
|
|
}, TIMEOUT)
|
|
|
|
pendingRequests.set(requestKey, {
|
|
resolve: (result) => {
|
|
clearTimeout(timeout)
|
|
resolve(result)
|
|
},
|
|
reject: (error) => {
|
|
clearTimeout(timeout)
|
|
reject(error)
|
|
},
|
|
timestamp: Date.now()
|
|
})
|
|
|
|
const command = { command: 'resultCount', type: 'set', values: value }
|
|
const message = JSON.stringify(command) + END_SEQUENCE
|
|
logIPCCommand(ip, command)
|
|
|
|
connectionInfo.client.write(message, (err) => {
|
|
if (err) {
|
|
pendingRequests.delete(requestKey)
|
|
clearTimeout(timeout)
|
|
resolve({ success: false, error: err.message })
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
// dataFps获取
|
|
const dataFpsGet = async (event, { ip }) => {
|
|
return new Promise((resolve, reject) => {
|
|
const connectionInfo = tcpClients.get(ip)
|
|
if (!connectionInfo) {
|
|
resolve({ success: false, error: '设备未连接' })
|
|
return
|
|
}
|
|
|
|
const requestKey = `${ip}${IPC_EVENT.DATA_FPS_GET}`
|
|
const timeout = setTimeout(() => {
|
|
pendingRequests.delete(requestKey)
|
|
resolve({ success: false, error: '请求超时' })
|
|
}, TIMEOUT)
|
|
|
|
pendingRequests.set(requestKey, {
|
|
resolve: (result) => {
|
|
clearTimeout(timeout)
|
|
resolve(result)
|
|
},
|
|
reject: (error) => {
|
|
clearTimeout(timeout)
|
|
reject(error)
|
|
},
|
|
timestamp: Date.now()
|
|
})
|
|
|
|
const command = { command: 'dataFps', type: 'get', values: '' }
|
|
const message = JSON.stringify(command) + END_SEQUENCE
|
|
logIPCCommand(ip, command)
|
|
connectionInfo.client.write(message, (err) => {
|
|
if (err) {
|
|
pendingRequests.delete(requestKey)
|
|
clearTimeout(timeout)
|
|
resolve({ success: false, error: err.message })
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
// dataFps设置
|
|
const dataFpsSet = async (event, { ip, value }) => {
|
|
return new Promise((resolve, reject) => {
|
|
const connectionInfo = tcpClients.get(ip)
|
|
if (!connectionInfo) {
|
|
resolve({ success: false, error: '设备未连接' })
|
|
return
|
|
}
|
|
|
|
const requestKey = `${ip}${IPC_EVENT.DATA_FPS_SET}`
|
|
const timeout = setTimeout(() => {
|
|
pendingRequests.delete(requestKey)
|
|
resolve({ success: false, error: '请求超时' })
|
|
}, TIMEOUT)
|
|
|
|
pendingRequests.set(requestKey, {
|
|
resolve: (result) => {
|
|
clearTimeout(timeout)
|
|
resolve(result)
|
|
},
|
|
reject: (error) => {
|
|
clearTimeout(timeout)
|
|
reject(error)
|
|
},
|
|
timestamp: Date.now()
|
|
})
|
|
|
|
const command = { command: 'dataFps', type: 'set', values: value }
|
|
const message = JSON.stringify(command) + END_SEQUENCE
|
|
logIPCCommand(ip, command)
|
|
connectionInfo.client.write(message, (err) => {
|
|
if (err) {
|
|
pendingRequests.delete(requestKey)
|
|
clearTimeout(timeout)
|
|
resolve({ success: false, error: err.message })
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
// videoFps获取
|
|
const videoFpsGet = async (event, { ip }) => {
|
|
return new Promise((resolve, reject) => {
|
|
const connectionInfo = tcpClients.get(ip)
|
|
if (!connectionInfo) {
|
|
resolve({ success: false, error: '设备未连接' })
|
|
return
|
|
}
|
|
|
|
const requestKey = `${ip}${IPC_EVENT.VIDEO_FPS_GET}`
|
|
const timeout = setTimeout(() => {
|
|
pendingRequests.delete(requestKey)
|
|
resolve({ success: false, error: '请求超时' })
|
|
}, TIMEOUT)
|
|
|
|
pendingRequests.set(requestKey, {
|
|
resolve: (result) => {
|
|
clearTimeout(timeout)
|
|
resolve(result)
|
|
},
|
|
reject: (error) => {
|
|
clearTimeout(timeout)
|
|
reject(error)
|
|
},
|
|
timestamp: Date.now()
|
|
})
|
|
|
|
const command = { command: 'videoFps', type: 'get', values: '' }
|
|
const message = JSON.stringify(command) + END_SEQUENCE
|
|
logIPCCommand(ip, command)
|
|
connectionInfo.client.write(message, (err) => {
|
|
if (err) {
|
|
pendingRequests.delete(requestKey)
|
|
clearTimeout(timeout)
|
|
resolve({ success: false, error: err.message })
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
// videoFps设置
|
|
const videoFpsSet = async (event, { ip, value }) => {
|
|
return new Promise((resolve, reject) => {
|
|
const connectionInfo = tcpClients.get(ip)
|
|
if (!connectionInfo) {
|
|
resolve({ success: false, error: '设备未连接' })
|
|
return
|
|
}
|
|
|
|
const requestKey = `${ip}${IPC_EVENT.VIDEO_FPS_SET}`
|
|
const timeout = setTimeout(() => {
|
|
pendingRequests.delete(requestKey)
|
|
resolve({ success: false, error: '请求超时' })
|
|
}, TIMEOUT)
|
|
|
|
pendingRequests.set(requestKey, {
|
|
resolve: (result) => {
|
|
clearTimeout(timeout)
|
|
resolve(result)
|
|
},
|
|
reject: (error) => {
|
|
clearTimeout(timeout)
|
|
reject(error)
|
|
},
|
|
timestamp: Date.now()
|
|
})
|
|
|
|
const command = { command: 'videoFps', type: 'set', values: value }
|
|
const message = JSON.stringify(command) + END_SEQUENCE
|
|
logIPCCommand(ip, command)
|
|
connectionInfo.client.write(message, (err) => {
|
|
if (err) {
|
|
pendingRequests.delete(requestKey)
|
|
clearTimeout(timeout)
|
|
resolve({ success: false, error: err.message })
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
// threshold获取
|
|
const thresholdGet = async (event, { ip }) => {
|
|
return new Promise((resolve, reject) => {
|
|
const connectionInfo = tcpClients.get(ip)
|
|
if (!connectionInfo) {
|
|
resolve({ success: false, error: '设备未连接' })
|
|
return
|
|
}
|
|
|
|
const requestKey = `${ip}${IPC_EVENT.THRESHOLD_GET}`
|
|
const timeout = setTimeout(() => {
|
|
pendingRequests.delete(requestKey)
|
|
resolve({ success: false, error: '请求超时' })
|
|
}, TIMEOUT)
|
|
|
|
pendingRequests.set(requestKey, {
|
|
resolve: (result) => {
|
|
clearTimeout(timeout)
|
|
resolve(result)
|
|
},
|
|
reject: (error) => {
|
|
clearTimeout(timeout)
|
|
reject(error)
|
|
},
|
|
timestamp: Date.now()
|
|
})
|
|
|
|
const command = { command: 'threshold', type: 'get', values: '' }
|
|
const message = JSON.stringify(command) + END_SEQUENCE
|
|
logIPCCommand(ip, command)
|
|
connectionInfo.client.write(message, (err) => {
|
|
if (err) {
|
|
pendingRequests.delete(requestKey)
|
|
clearTimeout(timeout)
|
|
resolve({ success: false, error: err.message })
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
// threshold设置
|
|
const thresholdSet = async (event, { ip, value }) => {
|
|
return new Promise((resolve, reject) => {
|
|
const connectionInfo = tcpClients.get(ip)
|
|
if (!connectionInfo) {
|
|
resolve({ success: false, error: '设备未连接' })
|
|
return
|
|
}
|
|
|
|
const requestKey = `${ip}${IPC_EVENT.THRESHOLD_SET}`
|
|
const timeout = setTimeout(() => {
|
|
pendingRequests.delete(requestKey)
|
|
resolve({ success: false, error: '请求超时' })
|
|
}, TIMEOUT)
|
|
|
|
pendingRequests.set(requestKey, {
|
|
resolve: (result) => {
|
|
clearTimeout(timeout)
|
|
resolve(result)
|
|
},
|
|
reject: (error) => {
|
|
clearTimeout(timeout)
|
|
reject(error)
|
|
},
|
|
timestamp: Date.now()
|
|
})
|
|
|
|
const command = { command: 'threshold', type: 'set', values: value }
|
|
const message = JSON.stringify(command) + END_SEQUENCE
|
|
logIPCCommand(ip, command)
|
|
connectionInfo.client.write(message, (err) => {
|
|
if (err) {
|
|
pendingRequests.delete(requestKey)
|
|
clearTimeout(timeout)
|
|
resolve({ success: false, error: err.message })
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
// invalidDataCount获取
|
|
const invalidDataCountGet = async (event, { ip }) => {
|
|
return new Promise((resolve, reject) => {
|
|
const connectionInfo = tcpClients.get(ip)
|
|
if (!connectionInfo) {
|
|
resolve({ success: false, error: '设备未连接' })
|
|
return
|
|
}
|
|
|
|
const requestKey = `${ip}${IPC_EVENT.INVALID_DATA_COUNT_GET}`
|
|
const timeout = setTimeout(() => {
|
|
pendingRequests.delete(requestKey)
|
|
resolve({ success: false, error: '请求超时' })
|
|
}, TIMEOUT)
|
|
|
|
pendingRequests.set(requestKey, {
|
|
resolve: (result) => {
|
|
clearTimeout(timeout)
|
|
resolve(result)
|
|
},
|
|
reject: (error) => {
|
|
clearTimeout(timeout)
|
|
reject(error)
|
|
},
|
|
timestamp: Date.now()
|
|
})
|
|
|
|
const command = { command: 'invalidDataCount', type: 'get', values: '' }
|
|
const message = JSON.stringify(command) + END_SEQUENCE
|
|
logIPCCommand(ip, command)
|
|
connectionInfo.client.write(message, (err) => {
|
|
if (err) {
|
|
pendingRequests.delete(requestKey)
|
|
clearTimeout(timeout)
|
|
resolve({ success: false, error: err.message })
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
// invalidDataCount设置
|
|
const invalidDataCountSet = async (event, { ip, value }) => {
|
|
return new Promise((resolve, reject) => {
|
|
const connectionInfo = tcpClients.get(ip)
|
|
if (!connectionInfo) {
|
|
resolve({ success: false, error: '设备未连接' })
|
|
return
|
|
}
|
|
|
|
const requestKey = `${ip}${IPC_EVENT.INVALID_DATA_COUNT_SET}`
|
|
const timeout = setTimeout(() => {
|
|
pendingRequests.delete(requestKey)
|
|
resolve({ success: false, error: '请求超时' })
|
|
}, TIMEOUT)
|
|
|
|
pendingRequests.set(requestKey, {
|
|
resolve: (result) => {
|
|
clearTimeout(timeout)
|
|
resolve(result)
|
|
},
|
|
reject: (error) => {
|
|
clearTimeout(timeout)
|
|
reject(error)
|
|
},
|
|
timestamp: Date.now()
|
|
})
|
|
|
|
const command = { command: 'invalidDataCount', type: 'set', values: value }
|
|
const message = JSON.stringify(command) + END_SEQUENCE
|
|
logIPCCommand(ip, command)
|
|
connectionInfo.client.write(message, (err) => {
|
|
if (err) {
|
|
pendingRequests.delete(requestKey)
|
|
clearTimeout(timeout)
|
|
resolve({ success: false, error: err.message })
|
|
}
|
|
})
|
|
})
|
|
}
|
|
// 图像发送使能设置
|
|
const imageSendEnabledSet = async (event, { ip, value }) => {
|
|
return new Promise((resolve, reject) => {
|
|
const connectionInfo = tcpClients.get(ip)
|
|
if (!connectionInfo) {
|
|
resolve({ success: false, error: '设备未连接' })
|
|
return
|
|
}
|
|
|
|
const requestKey = `${ip}${IPC_EVENT.IMAGE_SEND_ENABLED}`
|
|
const timeout = setTimeout(() => {
|
|
pendingRequests.delete(requestKey)
|
|
resolve({ success: false, error: '请求超时' })
|
|
}, TIMEOUT)
|
|
|
|
pendingRequests.set(requestKey, {
|
|
resolve: (result) => {
|
|
clearTimeout(timeout)
|
|
resolve(result)
|
|
},
|
|
reject: (error) => {
|
|
clearTimeout(timeout)
|
|
reject(error)
|
|
},
|
|
timestamp: Date.now()
|
|
})
|
|
|
|
const command = { command: 'imageSendEnabled', type: 'set', values: value }
|
|
const message = JSON.stringify(command) + END_SEQUENCE
|
|
logIPCCommand(ip, command)
|
|
|
|
connectionInfo.client.write(message, (err) => {
|
|
if (err) {
|
|
pendingRequests.delete(requestKey)
|
|
clearTimeout(timeout)
|
|
resolve({ success: false, error: err.message })
|
|
}
|
|
})
|
|
})
|
|
}
|
|
const isClearZeroSet = async (event, { ip, value }) => {
|
|
return new Promise((resolve, reject) => {
|
|
const connectionInfo = tcpClients.get(ip)
|
|
if (!connectionInfo) {
|
|
resolve({ success: false, error: '设备未连接' })
|
|
return
|
|
}
|
|
|
|
const requestKey = `${ip}${IPC_EVENT.IS_CLEAR_ZERO}`
|
|
const timeout = setTimeout(() => {
|
|
pendingRequests.delete(requestKey)
|
|
resolve({ success: false, error: '请求超时' })
|
|
}, TIMEOUT)
|
|
|
|
pendingRequests.set(requestKey, {
|
|
resolve: (result) => {
|
|
clearTimeout(timeout)
|
|
resolve(result)
|
|
},
|
|
reject: (error) => {
|
|
clearTimeout(timeout)
|
|
reject(error)
|
|
},
|
|
timestamp: Date.now()
|
|
})
|
|
|
|
const command = { command: 'isClearZero', type: 'set', values: value }
|
|
const message = JSON.stringify(command) + END_SEQUENCE
|
|
logIPCCommand(ip, command)
|
|
|
|
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))
|
|
}
|
|
|
|
//模拟延迟函数
|
|
const delay = () => {
|
|
return new Promise((resolve) => setTimeout(resolve, 1000))
|
|
}
|
|
|
|
// 处理重连配置
|
|
const handleReconnectConfig = (event, { enabled, interval }) => {
|
|
const validInterval = Math.max(4, interval) // 最小4秒
|
|
|
|
// 更新重连管理器配置
|
|
reconnectManager.updateConfig({
|
|
enabled,
|
|
interval: validInterval
|
|
})
|
|
|
|
log.info('Reconnect config updated:', { enabled, interval: validInterval })
|
|
}
|
|
|
|
// 启动心跳检测
|
|
const startHeartbeatCheck = (ip) => {
|
|
// 清除之前的心跳检测定时器
|
|
stopHeartbeatCheck(ip)
|
|
|
|
// 初始化心跳时间
|
|
lastHeartbeatTime.set(ip, Date.now())
|
|
|
|
// 设置心跳检测定时器
|
|
const timer = setInterval(() => {
|
|
checkHeartbeat(ip)
|
|
}, HEARTBEAT_INTERVAL)
|
|
|
|
heartbeatTimers.set(ip, timer)
|
|
log.info(`Started heartbeat check for ${ip}`)
|
|
}
|
|
|
|
// 检查心跳超时
|
|
const checkHeartbeat = (ip) => {
|
|
const lastTime = lastHeartbeatTime.get(ip)
|
|
const currentTime = Date.now()
|
|
|
|
if (!lastTime) {
|
|
log.warn(`No heartbeat recorded for ${ip}`)
|
|
return
|
|
}
|
|
|
|
const timeSinceLastHeartbeat = currentTime - lastTime
|
|
|
|
if (timeSinceLastHeartbeat > HEARTBEAT_TIMEOUT) {
|
|
log.warn(
|
|
`Heartbeat timeout for ${ip}: ${timeSinceLastHeartbeat}ms since last heartbeat (timeout: ${HEARTBEAT_TIMEOUT}ms)`
|
|
)
|
|
|
|
// 心跳超时,主动断开连接并启动重连
|
|
const connectionInfo = tcpClients.get(ip)
|
|
if (connectionInfo) {
|
|
log.info(`Force disconnecting ${ip} due to heartbeat timeout`)
|
|
|
|
// 停止心跳检测
|
|
stopHeartbeatCheck(ip)
|
|
|
|
// 销毁连接
|
|
connectionInfo.client.destroy()
|
|
tcpClients.delete(ip)
|
|
clearPendingRequestsByIp(ip)
|
|
|
|
// 通知渲染进程连接断开
|
|
connectionInfo.eventSender.send(IPC_EVENT.DEVICE_DISCONNECT_REPLY, {
|
|
success: true,
|
|
reason: 'heartbeat_timeout'
|
|
})
|
|
|
|
// 启动重连(如果启用)
|
|
const reconnectConfig = reconnectManager.getConfig()
|
|
if (reconnectConfig.enabled) {
|
|
log.info(`Connection lost to ${ip} due to heartbeat timeout, starting reconnect...`)
|
|
// 保存连接数据供重连使用
|
|
tcpConnectionData.set(ip, {
|
|
port: connectionInfo.port,
|
|
eventSender: connectionInfo.eventSender
|
|
})
|
|
reconnectManager.startReconnect(ip, 'Heartbeat timeout')
|
|
}
|
|
}
|
|
} else {
|
|
// 心跳正常,记录调试信息
|
|
// log.debug(`Heartbeat OK for ${ip}: ${timeSinceLastHeartbeat}ms since last heartbeat`)
|
|
}
|
|
}
|
|
|
|
// 停止心跳检测
|
|
const stopHeartbeatCheck = (ip) => {
|
|
const timer = heartbeatTimers.get(ip)
|
|
if (timer) {
|
|
clearInterval(timer)
|
|
heartbeatTimers.delete(ip)
|
|
}
|
|
lastHeartbeatTime.delete(ip)
|
|
log.info(`Stopped heartbeat check for ${ip}`)
|
|
}
|
|
|
|
// 存储目录相关处理函数
|
|
const openDirectory = async (event, path) => {
|
|
try {
|
|
await shell.openPath(path)
|
|
log.info(`Opened directory: ${path}`)
|
|
return { success: true }
|
|
} catch (error) {
|
|
log.error('Failed to open directory:', error)
|
|
return { success: false, error: error.message }
|
|
}
|
|
}
|
|
|
|
const selectDirectory = async (event, options) => {
|
|
try {
|
|
const result = await dialog.showOpenDialog({
|
|
title: options.title || '选择目录',
|
|
properties: ['openDirectory', 'createDirectory'],
|
|
defaultPath: options.defaultPath
|
|
})
|
|
|
|
if (result.canceled) {
|
|
return { success: false, canceled: true }
|
|
}
|
|
|
|
const selectedPath = result.filePaths[0]
|
|
log.info(`Directory selected: ${selectedPath}`)
|
|
return { success: true, path: selectedPath }
|
|
} catch (error) {
|
|
log.error('Failed to select directory:', error)
|
|
return { success: false, error: error.message }
|
|
}
|
|
}
|
|
|
|
// 确保目录存在
|
|
const ensureDirectory = async (event, dirPath) => {
|
|
try {
|
|
if (!existsSync(dirPath)) {
|
|
mkdirSync(dirPath, { recursive: true })
|
|
log.info(`Created directory: ${dirPath}`)
|
|
}
|
|
return { success: true }
|
|
} catch (error) {
|
|
log.error('Failed to create directory:', error)
|
|
return { success: false, error: error.message }
|
|
}
|
|
}
|
|
|
|
// 追加内容到文件
|
|
const appendToFile = async (event, filePath, content) => {
|
|
try {
|
|
// 确保父目录存在
|
|
const dir = dirname(filePath)
|
|
if (!existsSync(dir)) {
|
|
mkdirSync(dir, { recursive: true })
|
|
}
|
|
|
|
appendFileSync(filePath, content, 'utf8')
|
|
return { success: true }
|
|
} catch (error) {
|
|
log.error('Failed to append to file:', error)
|
|
return { success: false, error: error.message }
|
|
}
|
|
}
|
|
|
|
// 写入CSV文件头(包含BOM)
|
|
const writeCSVHeader = async (event, filePath, content) => {
|
|
try {
|
|
// 确保父目录存在
|
|
const dir = dirname(filePath)
|
|
if (!existsSync(dir)) {
|
|
mkdirSync(dir, { recursive: true })
|
|
}
|
|
|
|
// 添加UTF-8 BOM以确保Excel正确显示中文
|
|
const bom = '\uFEFF'
|
|
appendFileSync(filePath, bom + content, 'utf8')
|
|
log.info(`Created CSV file with header: ${filePath}`)
|
|
return { success: true }
|
|
} catch (error) {
|
|
log.error('Failed to write CSV header:', error)
|
|
return { success: false, error: error.message }
|
|
}
|
|
}
|
|
|
|
// 检查文件是否存在
|
|
const checkFileExists = async (event, filePath) => {
|
|
try {
|
|
const exists = existsSync(filePath)
|
|
return { success: true, exists }
|
|
} catch (error) {
|
|
log.error('Failed to check file existence:', error)
|
|
return { success: false, error: error.message }
|
|
}
|
|
}
|
|
|