From d703d3b58ad40c6d0f513947ea7b226f414b9972 Mon Sep 17 00:00:00 2001 From: cles <208023732@qq.com> Date: Thu, 4 Sep 2025 13:56:40 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=BC=BA=20IPC=20=E4=BA=8B?= =?UTF-8?q?=E4=BB=B6=E5=B9=B6=E5=AE=9E=E7=8E=B0=E4=BC=A0=E6=84=9F=E5=99=A8?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 ipcEvents.js 中添加了用于获取和设置各种传感器参数的新 IPC 事件。 - 更新了 DeflectionCollection 组件以处理传入的传感器数据,保留最近 10 个数据点的历史记录并在图表中显示。 - 改进了 MeasurementPointSetting 组件,支持加载和设置传感器数据,并添加了加载状态以提升用户体验。 - 重构了 SystemSettings 组件,通过适当的 IPC 事件读取和设置参数,包括加载状态和验证功能。 - 调整了 MeasurementPointSetting.module.css 中的样式,以获得更好的 UI 一致性。 --- src/main/ipcRouter.js | 836 +++++++++++++++++- src/renderer/src/common/ipcEvents.js | 21 +- .../DeflectionCollection.jsx | 339 ++++--- .../MeasurementPointSetting.jsx | 164 +++- .../MeasurementPointSetting.module.css | 2 +- .../SystemSettings/SystemSettings.jsx | 166 +++- 6 files changed, 1330 insertions(+), 198 deletions(-) diff --git a/src/main/ipcRouter.js b/src/main/ipcRouter.js index cc73f9d..ccc965e 100644 --- a/src/main/ipcRouter.js +++ b/src/main/ipcRouter.js @@ -3,7 +3,8 @@ import dgram from 'dgram' import net from 'net' import { IPC_EVENT } from '../renderer/src/common/ipcEvents.js' import fs from 'fs' - +const TIMEOUT = 10000 // 10秒超时 +const END_SEQUENCE = '\n\n' // 消息结束标志 // 全局保存所有TCP连接和相关信息 const tcpClients = new Map() // 保存待处理的请求,用于关联响应 @@ -13,7 +14,24 @@ 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,支持异步返回 + 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) } const searchDevice = (event) => { @@ -143,7 +161,7 @@ const disconnectDevice = (event, { ip }) => { } // 处理TCP响应 -const handleTcpResponse = (ip, msg) => { +const handleTcpResponse = async (ip, msg) => { const connectionInfo = tcpClients.get(ip) if (!connectionInfo) return @@ -159,11 +177,182 @@ const handleTcpResponse = (ip, msg) => { 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() + console.log(msg) + 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() + console.log(msg) + 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() + console.log(msg) + 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() + console.log(msg) + 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() + console.log(msg) + 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() + console.log(msg) + 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() + console.log(msg) + 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() + console.log(msg) + 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() + console.log(msg) + 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() + console.log(msg) + 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() + console.log(msg) + 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() + console.log(msg) + 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() + console.log(msg) + 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() + console.log(msg) + paramRequest.resolve({ success: true, data: msg }) + pendingRequests.delete(`${ip}${IPC_EVENT.INVALID_DATA_COUNT_SET}`) + } + } + break case IPC_EVENT.HEARTBEAT_REPLY: // 心跳处理 break @@ -173,7 +362,7 @@ const handleTcpResponse = (ip, msg) => { } } -// 传感器加载 - 改为异步处理 +// 传感器加载 const sensorLoad = async (event, { ip }) => { return new Promise((resolve, reject) => { const connectionInfo = tcpClients.get(ip) @@ -189,7 +378,7 @@ const sensorLoad = async (event, { ip }) => { const timeout = setTimeout(() => { pendingRequests.delete(requestKey) resolve({ success: false, error: '请求超时' }) - }, 10000) // 10秒超时 + }, TIMEOUT) // 10秒超时 // 保存请求信息 pendingRequests.set(requestKey, { @@ -206,7 +395,7 @@ const sensorLoad = async (event, { ip }) => { // 发送传感器加载命令 const command = { command: 'sensors', type: 'get', values: '' } - const message = JSON.stringify(command) + '\n\n' + const message = JSON.stringify(command) + END_SEQUENCE connectionInfo.client.write(message, (err) => { if (err) { @@ -217,15 +406,632 @@ const sensorLoad = async (event, { ip }) => { }) }) } +// 传感器设置 +const sensorSet = async (event, { ip, sensors }) => { + return new Promise((resolve, reject) => { + const connectionInfo = tcpClients.get(ip) + if (!connectionInfo) { + resolve({ success: false, error: '设备未连接' }) + return + } -// 清理指定IP的所有待处理请求 -const clearPendingRequestsByIp = (ip) => { - const keysToDelete = [] - for (const [key, request] of pendingRequests) { - if (key.startsWith(`${ip}_`)) { - request.resolve({ success: false, error: '连接已断开' }) - keysToDelete.push(key) + // 生成请求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 + + 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 } - } - keysToDelete.forEach((key) => pendingRequests.delete(key)) + + // 生成请求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 + + 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 + + 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 + + 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 + + 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 + + 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 + + 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 + + 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 + console.log('Sending command:', message) + 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 + + 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 + + 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 + + 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 + + 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 + + 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 + + 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)) } diff --git a/src/renderer/src/common/ipcEvents.js b/src/renderer/src/common/ipcEvents.js index 2e51b1a..dc7d460 100644 --- a/src/renderer/src/common/ipcEvents.js +++ b/src/renderer/src/common/ipcEvents.js @@ -10,5 +10,24 @@ export const IPC_EVENT = { RESULT_REPLY: 'result:reply', // 传感器数据 IMAGE_REPLY: 'image:reply', // 图像数据 HEARTBEAT_REPLY: 'heartbeat:reply', // 心跳包 - SENSORS_GET: 'sensors:get' // 加载传感器配置 + SENSORS_GET: 'sensors:get', // 加载传感器配置 + SENSORS_SET: 'sensors:set', // 加载传感器配置 + + // 基本参数相关,GET + IMAGE_SEND_TIME_GET: 'imageSendTime:get', + ZERO_COUNT_GET: 'zeroCount:get', + RESULT_COUNT_GET: 'resultCount:get', + DATA_FPS_GET: 'dataFps:get', + VIDEO_FPS_GET: 'videoFps:get', + THRESHOLD_GET: 'threshold:get', + INVALID_DATA_COUNT_GET: 'invalidDataCount:get', + + // 基本参数相关,SET + IMAGE_SEND_TIME_SET: 'imageSendTime:set', + ZERO_COUNT_SET: 'zeroCount:set', + RESULT_COUNT_SET: 'resultCount:set', + DATA_FPS_SET: 'dataFps:set', + VIDEO_FPS_SET: 'videoFps:set', + THRESHOLD_SET: 'threshold:set', + INVALID_DATA_COUNT_SET: 'invalidDataCount:set' } diff --git a/src/renderer/src/components/DeflectionCollection/DeflectionCollection.jsx b/src/renderer/src/components/DeflectionCollection/DeflectionCollection.jsx index 036b0fa..17caeb2 100644 --- a/src/renderer/src/components/DeflectionCollection/DeflectionCollection.jsx +++ b/src/renderer/src/components/DeflectionCollection/DeflectionCollection.jsx @@ -1,3 +1,6 @@ +import { useEffect, useState } from 'react' +import { IPC_EVENT } from '../../common/ipcEvents' +import useDeviceStore from '../../stores/deviceStore' import { Line } from 'react-chartjs-2' import { Chart as ChartJS, @@ -13,65 +16,111 @@ import { FileTextOutlined, ClockCircleOutlined, EyeOutlined } from '@ant-design/ ChartJS.register(CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend) -function randomAround(base, delta) { - return base + Math.round((Math.random() * 2 - 1) * delta) -} - -function getTimeLabels(count) { - const now = new Date() - return Array.from({ length: count }).map((_, i) => { - const d = new Date(now.getTime() - (count - 1 - i) * 2 * 1000) - return d.toTimeString().slice(0, 8) - }) -} - function DeflectionCollection() { - const count = 10 - const targetCount = 5 // 支持多个target + // 传感器数据状态 - 最多保存10个时间点的数据 + const [sensorDataHistory, setSensorDataHistory] = useState([]) + + // 获取设备连接状态 + const connectedDevice = useDeviceStore((state) => state.connectedDevice) - // 为每个target生成颜色 + // 为每个传感器生成颜色 const targetColors = [ 'rgba(75,192,192,1)', // 青色 'rgba(255,99,132,1)', // 红色 'rgba(54,162,235,1)', // 蓝色 'rgba(255,206,86,1)', // 黄色 - 'rgba(153,102,255,1)' // 紫色 + 'rgba(153,102,255,1)', // 紫色 + 'rgba(255,159,64,1)', // 橙色 + 'rgba(199,199,199,1)', // 灰色 + 'rgba(83,102,255,1)' // 靛色 ] - // 生成多个target的数据 - const targetsData = Array.from({ length: targetCount }).map((_, targetIndex) => ({ - name: `Target ${targetIndex + 1}`, - color: targetColors[targetIndex % targetColors.length], - data: Array.from({ length: count }).map((_, i) => ({ - x: 350 + targetIndex * 50 + Math.sin(i * 0.5 + targetIndex) * 20 + randomAround(0, 3), - y: 50 + targetIndex * 20 + Math.cos(i * 0.3 + targetIndex) * 15 + randomAround(0, 2), - w: randomAround(125, 5), - h: randomAround(115, 5) - })) - })) + // 监听设备连接状态变化,设备连接时清空历史数据 + useEffect(() => { + if (connectedDevice) { + console.log('设备连接,清空历史数据:', connectedDevice) + setSensorDataHistory([]) + } + }, [connectedDevice]) + + useEffect(() => { + if (window?.electron?.ipcRenderer?.on && IPC_EVENT?.RESULT_REPLY) { + const handler = (event, data) => { + console.log('收到主进程RESULT_REPLY:', data) + + // 检查数据格式 + if (data && data.values && data.values.sensors && Array.isArray(data.values.sensors)) { + const newDataPoint = { + timestamp: data.values.timestamp || new Date().toISOString(), + sensors: data.values.sensors + } + + setSensorDataHistory((prev) => { + const newHistory = [...prev, newDataPoint] + // 保持最多10个数据点 + return newHistory.slice(-10) + }) + } + } + window.electron.ipcRenderer.on(IPC_EVENT.RESULT_REPLY, handler) + return () => { + window.electron.ipcRenderer.removeListener(IPC_EVENT.RESULT_REPLY, handler) + } + } + }, []) - const timeLabels = getTimeLabels(count) + // 生成时间标签 - 包含毫秒级精度 + const timeLabels = sensorDataHistory.map((dataPoint) => { + const timestamp = new Date(dataPoint.timestamp) + // 格式化为 HH:MM:SS.mmm 显示毫秒 + const hours = timestamp.getHours().toString().padStart(2, '0') + const minutes = timestamp.getMinutes().toString().padStart(2, '0') + const seconds = timestamp.getSeconds().toString().padStart(2, '0') + const milliseconds = timestamp.getMilliseconds().toString().padStart(3, '0') + return `${hours}:${minutes}:${seconds}.${milliseconds}` + }) + // 获取所有唯一的传感器位置 + const allSensors = sensorDataHistory.reduce((acc, dataPoint) => { + dataPoint.sensors.forEach((sensor) => { + if (!acc.some((s) => s.pos === sensor.pos)) { + acc.push({ + pos: sensor.pos, + des: sensor.des, + color: targetColors[acc.length % targetColors.length] + }) + } + }) + return acc + }, []) + // 生成Y方向数据 const dataY = { labels: timeLabels, - datasets: targetsData.map((target) => ({ - label: `${target.name}`, - data: target.data.map((d) => d.y), - borderColor: target.color, - backgroundColor: target.color.replace('1)', '0.2)'), + datasets: allSensors.map((sensor) => ({ + label: `测点${sensor.pos}`, + data: sensorDataHistory.map((dataPoint) => { + const sensorData = dataPoint.sensors.find((s) => s.pos === sensor.pos) + return sensorData ? Number(sensorData.yReal) : null + }), + borderColor: sensor.color, + backgroundColor: sensor.color.replace('1)', '0.2)'), tension: 0.3, pointRadius: 3, pointHoverRadius: 5 })) } + // 生成X方向数据 const dataX = { labels: timeLabels, - datasets: targetsData.map((target) => ({ - label: `${target.name}`, - data: target.data.map((d) => d.x), - borderColor: target.color, - backgroundColor: target.color.replace('1)', '0.2)'), + datasets: allSensors.map((sensor) => ({ + label: `测点${sensor.pos}`, + data: sensorDataHistory.map((dataPoint) => { + const sensorData = dataPoint.sensors.find((s) => s.pos === sensor.pos) + return sensorData ? Number(sensorData.xReal) : null + }), + borderColor: sensor.color, + backgroundColor: sensor.color.replace('1)', '0.2)'), tension: 0.3, pointRadius: 3, pointHoverRadius: 5 @@ -108,6 +157,10 @@ function DeflectionCollection() { } } + // 获取最新数据用于右侧实时显示 + const latestData = + sensorDataHistory.length > 0 ? sensorDataHistory[sensorDataHistory.length - 1] : null + return (
- {targetsData.map((target, index) => { - const latestData = target.data[target.data.length - 1] - const latestTime = timeLabels[timeLabels.length - 1] + {latestData ? ( + latestData.sensors.map((sensor, index) => { + const sensorConfig = allSensors.find((s) => s.pos === sensor.pos) || { + color: targetColors[index % targetColors.length] + } + const latestTime = + timeLabels[timeLabels.length - 1] || + (() => { + const now = new Date() + const hours = now.getHours().toString().padStart(2, '0') + const minutes = now.getMinutes().toString().padStart(2, '0') + const seconds = now.getSeconds().toString().padStart(2, '0') + const milliseconds = now.getMilliseconds().toString().padStart(3, '0') + return `${hours}:${minutes}:${seconds}.${milliseconds}` + })() - return ( -
+ return (
- - 测点位置:{index + 1} - -
+
+ + 测点位置:{sensor.pos} + +
-
- - - 测点描述: - -
+ + + 测点描述:{sensor.des || '无'} + +
-
- - - 采集时间:{new Date().toLocaleDateString()} {latestTime} - -
+ + + 采集时间:{new Date(latestData.timestamp).toLocaleDateString()} {latestTime} + + -
- - - X → {latestData.x.toFixed(3)} - -
+ + + X → {Number(sensor.xReal).toFixed(3)} + + -
- - - Y → {latestData.y.toFixed(3)} - + + + Y → {Number(sensor.yReal).toFixed(3)} + +
- - ) - })} + ) + }) + ) : ( +
+ 等待数据... +
+ )} ) diff --git a/src/renderer/src/components/MeasurementPointSetting/MeasurementPointSetting.jsx b/src/renderer/src/components/MeasurementPointSetting/MeasurementPointSetting.jsx index ec8dfc3..3d9600d 100644 --- a/src/renderer/src/components/MeasurementPointSetting/MeasurementPointSetting.jsx +++ b/src/renderer/src/components/MeasurementPointSetting/MeasurementPointSetting.jsx @@ -35,6 +35,10 @@ function MeasurementPointSetting() { // 选中的传感器状态 const [selectedSensorKey, setSelectedSensorKey] = useState(null) + // loading状态 + const [loadingSensors, setLoadingSensors] = useState(false) + const [settingSensors, setSettingSensors] = useState(false) + // 根据选中的传感器更新矩形数据 const updateRectangleFromSensor = (sensorKey) => { if (!sensorKey) { @@ -243,7 +247,36 @@ function MeasurementPointSetting() { }) } + // 将传感器数据转换为组件内部格式 + const convertSensorDataToInternalFormat = (sensorValues) => { + return sensorValues.map((sensor, index) => { + const sensorKey = String(index + 1) + return { + key: sensorKey, + name: '传感器', + value: '', + children: [ + { key: `${sensorKey}-1`, name: '测点位置', value: Number(sensor.pos) || 0 }, + { key: `${sensorKey}-2`, name: '测点描述', value: sensor.des || '' }, + { key: `${sensorKey}-3`, name: '计算系数', value: Number(sensor.arg) || 0 }, + { + key: `${sensorKey}-4`, + name: '基准标靶', + value: sensor.tar || 'n', + children: [ + { key: `${sensorKey}-4-1`, name: 'x', value: Number(sensor.x) || 0 }, + { key: `${sensorKey}-4-2`, name: 'y', value: Number(sensor.y) || 0 }, + { key: `${sensorKey}-4-3`, name: 'w', value: Number(sensor.w) || 0 }, + { key: `${sensorKey}-4-4`, name: 'h', value: Number(sensor.h) || 0 } + ] + } + ] + } + }) + } + const handleLoad = async () => { + setLoadingSensors(true) try { // 从store中获取当前连接的设备IP if (!connectedDevice || !connectedDevice.ip) { @@ -260,7 +293,21 @@ function MeasurementPointSetting() { if (result.success) { console.log('传感器数据加载成功:', result.data) - message.success('传感器数据已成功加载!') + + // 检查返回的数据格式 + if (result.data && result.data.values && Array.isArray(result.data.values)) { + // 将传感器数据转换为内部格式并覆盖更新列表 + const convertedData = convertSensorDataToInternalFormat(result.data.values) + setSensorList(convertedData) + + // 清除当前选中的传感器 + setSelectedSensorKey(null) + setRectangleData(null) + + message.success(`传感器数据已成功加载!`) + } else { + message.warning('传感器数据格式不正确') + } } else { console.error('传感器数据加载失败:', result.error) message.error(`传感器数据加载失败:${result.error}`) @@ -268,54 +315,67 @@ function MeasurementPointSetting() { } catch (error) { console.error('调用传感器加载失败:', error) message.error(`调用传感器加载失败:${error.message}`) + } finally { + setLoadingSensors(false) } } - const handleSet = () => { - // 检查是否有传感器数据 - if (!sensorList || sensorList.length === 0) { - message.warning('没有传感器数据,请先添加传感器!') - return - } - - // 检查是否连接设备 - if (!connectedDevice || !connectedDevice.ip) { - message.warning('请先连接设备后再设置传感器!') - return - } - - // 收集传感器数据并转换为指定格式 - const collectedData = sensorList.map((sensor) => { - // 从传感器的children中提取数据 - const children = sensor.children || [] - - // 查找各个字段 - const positionItem = children.find((item) => item.name === '测点位置') - const descriptionItem = children.find((item) => item.name === '测点描述') - const coefficientItem = children.find((item) => item.name === '计算系数') - const targetItem = children.find((item) => item.name === '基准标靶') + const handleSet = async () => { + setSettingSensors(true) + try { + // 检查是否连接设备 + if (!connectedDevice || !connectedDevice.ip) { + message.warning('请先连接设备后再设置传感器!') + return + } - // 查找坐标信息(在基准标靶的children中) - const targetChildren = targetItem?.children || [] - const xItem = targetChildren.find((item) => item.name === 'x') - const yItem = targetChildren.find((item) => item.name === 'y') - const wItem = targetChildren.find((item) => item.name === 'w') - const hItem = targetChildren.find((item) => item.name === 'h') + // 收集传感器数据并转换为指定格式 + const collectedData = sensorList.map((sensor) => { + // 从传感器的children中提取数据 + const children = sensor.children || [] + + // 查找各个字段 + const positionItem = children.find((item) => item.name === '测点位置') + const descriptionItem = children.find((item) => item.name === '测点描述') + const coefficientItem = children.find((item) => item.name === '计算系数') + const targetItem = children.find((item) => item.name === '基准标靶') + + // 查找坐标信息(在基准标靶的children中) + const targetChildren = targetItem?.children || [] + const xItem = targetChildren.find((item) => item.name === 'x') + const yItem = targetChildren.find((item) => item.name === 'y') + const wItem = targetChildren.find((item) => item.name === 'w') + const hItem = targetChildren.find((item) => item.name === 'h') + + return { + arg: String(coefficientItem?.value), // 计算系数 + des: String(descriptionItem?.value), // 测点描述 + pos: String(positionItem?.value), // 测点位置 + tar: String(targetItem?.value), // 基准标靶 + w: String(wItem?.value), // 宽 + h: String(hItem?.value), // 高 + x: String(xItem?.value), // x坐标 + y: String(yItem?.value) // y坐标 + } + }) - return { - arg: String(coefficientItem?.value), // 计算系数 - des: String(descriptionItem?.value), // 测点描述 - pos: String(positionItem?.value), // 测点位置 - tar: String(targetItem?.value), // 基准标靶 - w: String(wItem?.value), // 宽 - h: String(hItem?.value), // 高 - x: String(xItem?.value), // x坐标 - y: String(yItem?.value) // y坐标 + // 发送到主进程进行设置 + const result = await window.electron.ipcRenderer.invoke(IPC_EVENT.SENSORS_SET, { + ip: connectedDevice.ip, + sensors: collectedData + }) + if (result.success) { + message.success('传感器数据已成功设置!') + } else { + console.error('传感器数据设置失败:', result.error) + message.error(`传感器数据设置失败:${result.error}`) } - }) - - console.log('收集到的传感器数据:', collectedData) - console.log('格式化后的JSON:', JSON.stringify(collectedData, null, 2)) + } catch (error) { + console.error('调用传感器设置失败:', error) + message.error(`调用传感器设置失败:${error.message}`) + } finally { + setSettingSensors(false) + } } return ( @@ -462,7 +522,7 @@ function MeasurementPointSetting() { childrenColumnName: 'children', indentSize: '2em' }} - scroll={{ y: 220 }} + scroll={{ y: 200 }} rowSelection={{ type: 'radio', selectedRowKeys: selectedSensorKey ? [selectedSensorKey] : [], @@ -512,10 +572,22 @@ function MeasurementPointSetting() { -