From 3db26a408ff80a8dd45c2809c3b6b5d5032c8c5e Mon Sep 17 00:00:00 2001
From: cles <208023732@qq.com>
Date: Tue, 14 Oct 2025 16:28:06 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20=E6=9B=B4=E6=96=B0=E7=89=88=E6=9C=AC?=
=?UTF-8?q?=E8=87=B3=201.1.0=EF=BC=8C=E6=B7=BB=E5=8A=A0=E9=85=8D=E7=BD=AE?=
=?UTF-8?q?=E4=BF=9D=E5=AD=98=E4=B8=8E=E5=8A=A0=E8=BD=BD=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
package.json | 2 +-
src/main/ipcRouter.js | 80 ++++++++-
src/renderer/src/common/ipcEvents.js | 6 +-
.../MeasurementPointSetting.jsx | 156 +++++++++++++++++-
4 files changed, 238 insertions(+), 6 deletions(-)
diff --git a/package.json b/package.json
index 308d304..8b0b9b6 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "FlexometerSetup",
- "version": "1.0.9",
+ "version": "1.1.0",
"description": "An Electron application with React",
"main": "./out/main/index.js",
"author": "cles",
diff --git a/src/main/ipcRouter.js b/src/main/ipcRouter.js
index 9045ee4..f2c5aab 100644
--- a/src/main/ipcRouter.js
+++ b/src/main/ipcRouter.js
@@ -1,5 +1,5 @@
import { ipcMain } from 'electron'
-import { dialog, shell } from 'electron'
+import { dialog, shell, app } from 'electron'
import dgram from 'dgram'
import net from 'net'
import { appendFileSync, mkdirSync, existsSync } from 'fs'
@@ -7,7 +7,9 @@ import { dirname } from 'path'
import { IPC_EVENT } from '../renderer/src/common/ipcEvents.js'
import log from 'electron-log'
import ReconnectManager from './reconnectManager.js'
-import {getParametersFromDevice,setFirstParameter,disconnect} from './lib/adbClient.js'
+import { getParametersFromDevice, setFirstParameter, disconnect } from './lib/adbClient.js'
+import fs from 'fs/promises'
+import path from 'path'
const TIMEOUT = 10000 // 10秒超时
const END_SEQUENCE = '\n\n' // 消息结束标志
// 全局保存所有TCP连接和相关信息
@@ -202,6 +204,80 @@ export function registerIpRouter() {
// 曝光参数相关操作
ipcMain.handle(IPC_EVENT.EXPOSURE_GET, exposureGet)
ipcMain.handle(IPC_EVENT.EXPOSURE_SET, exposureSet)
+ // 配置保存与加载
+ ipcMain.handle(IPC_EVENT.SAVE_CONFIG, saveConfig)
+ ipcMain.handle(IPC_EVENT.LOAD_CONFIG, loadConfig)
+}
+// 导入配置文件
+const loadConfig = async (event) => {
+ try {
+ // 打开文件选择对话框
+ const result = await dialog.showOpenDialog({
+ title: '导入传感器配置',
+ defaultPath: app.getPath('documents'),
+ filters: [
+ { name: 'JSON 配置文件', extensions: ['json'] },
+ { name: '所有文件', extensions: ['*'] }
+ ],
+ properties: ['openFile']
+ })
+
+ if (result.canceled) {
+ return { success: false, canceled: true }
+ }
+
+ // 读取文件内容
+ const filePath = result.filePaths[0]
+ const fileContent = await fs.readFile(filePath, 'utf-8')
+ const configData = JSON.parse(fileContent)
+
+ // 验证配置文件格式
+ if (!configData.sensors || !Array.isArray(configData.sensors)) {
+ return {
+ success: false,
+ error: '配置文件格式不正确:缺少 sensors 数组'
+ }
+ }
+
+ return {
+ success: true,
+ data: configData,
+ filePath
+ }
+ } catch (error) {
+ console.error('导入配置文件失败:', error)
+ return {
+ success: false,
+ error: error.message || '文件读取或解析失败'
+ }
+ }
+}
+//配置保存
+const saveConfig = async (event, { data, ip }) => {
+ try {
+ // 打开保存对话框
+ const result = await dialog.showSaveDialog({
+ title: '保存传感器配置',
+ defaultPath: path.join(app.getPath('documents'), `${ip}-测点列表.json`),
+ filters: [
+ { name: 'JSON 配置文件', extensions: ['json'] },
+ { name: '所有文件', extensions: ['*'] }
+ ],
+ properties: ['createDirectory', 'showOverwriteConfirmation']
+ })
+
+ if (result.canceled) {
+ return { success: false, canceled: true }
+ }
+
+ // 写入文件
+ await fs.writeFile(result.filePath, JSON.stringify(data, null, 2), 'utf-8')
+
+ return { success: true, filePath: result.filePath }
+ } catch (error) {
+ console.error('保存配置文件失败:', error)
+ return { success: false, error: error.message }
+ }
}
// 获取设备的曝光参数
const exposureGet = async (event, { ip }) => {
diff --git a/src/renderer/src/common/ipcEvents.js b/src/renderer/src/common/ipcEvents.js
index 0b62b41..f9033e0 100644
--- a/src/renderer/src/common/ipcEvents.js
+++ b/src/renderer/src/common/ipcEvents.js
@@ -56,5 +56,9 @@ export const IPC_EVENT = {
// 曝光参数相关操作
EXPOSURE_GET: 'exposure:get',
- EXPOSURE_SET: 'exposure:set'
+ EXPOSURE_SET: 'exposure:set',
+
+ // 配置保存与加载
+ SAVE_CONFIG: 'save-config',
+ LOAD_CONFIG: 'load-config'
}
diff --git a/src/renderer/src/components/MeasurementPointSetting/MeasurementPointSetting.jsx b/src/renderer/src/components/MeasurementPointSetting/MeasurementPointSetting.jsx
index f07ffb0..fd7a200 100644
--- a/src/renderer/src/components/MeasurementPointSetting/MeasurementPointSetting.jsx
+++ b/src/renderer/src/components/MeasurementPointSetting/MeasurementPointSetting.jsx
@@ -6,6 +6,8 @@ import {
DeleteOutlined,
ReloadOutlined,
SendOutlined,
+ ExportOutlined,
+ ImportOutlined,
BoxPlotFilled
} from '@ant-design/icons'
import useRectangleStore from '../../stores/rectangleStore'
@@ -143,7 +145,7 @@ function MeasurementPointSetting() {
// 只更新当前选中的传感器的坐标
if (sensorKey === selectedSensorKey) {
- const coordMap = { '1': 'x', '2': 'y', '3': 'width', '4': 'height' }
+ const coordMap = { 1: 'x', 2: 'y', 3: 'width', 4: 'height' }
const coordName = coordMap[coordIndex]
if (coordName && rectangleData) {
@@ -282,7 +284,7 @@ function MeasurementPointSetting() {
return (
onInputFocus(record)}
+ onFocus={() => onInputFocus(record)}
value={value}
onChange={(e) => handleCellValueChange(record.key, e.target.value)}
style={{ fontSize }}
@@ -501,6 +503,138 @@ function MeasurementPointSetting() {
setRectangleData(null)
}
}
+ const handleSaveConfig = async () => {
+ try {
+ // 检查是否有传感器数据
+ if (!sensorList || sensorList.length === 0) {
+ message.warning('没有可保存的传感器数据!')
+ return
+ }
+
+ // 收集传感器数据并转换为指定格式(与handleSet相同的格式)
+ const configData = sensorList.map((sensor) => {
+ 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 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),
+ y: String(yItem?.value)
+ }
+ })
+
+ // 准备保存的完整配置数据
+ const fullConfig = {
+ timestamp: new Date().toISOString(),
+ sensors: configData,
+ metadata: {
+ lensDistance,
+ measureDistance,
+ calculatedCoefficient
+ }
+ }
+
+ // 调用主进程保存文件
+ const result = await window.electron.ipcRenderer.invoke(IPC_EVENT.SAVE_CONFIG, {
+ data: fullConfig,
+ ip: connectedDevice?.ip || ''
+ })
+
+ if (result.success) {
+ message.success(`配置已保存到:${result.filePath}`)
+ } else if (result.canceled) {
+ message.info('已取消保存')
+ } else {
+ message.error(`保存失败:${result.error}`)
+ }
+ } catch (error) {
+ console.error('保存配置失败:', error)
+ message.error(`保存配置失败:${error.message}`)
+ }
+ }
+ const handleImportConfig = async () => {
+ try {
+ // 如果当前有传感器数据,先确认是否覆盖
+ if (sensorList.length > 0) {
+ const confirmed = await new Promise((resolve) => {
+ Modal.confirm({
+ title: '导入确认',
+ content: '导入配置将覆盖当前的传感器列表,是否继续?',
+ okText: '继续导入',
+ cancelText: '取消',
+ centered: true,
+ onOk: () => resolve(true),
+ onCancel: () => resolve(false)
+ })
+ })
+
+ if (!confirmed) {
+ return
+ }
+ }
+
+ // 调用主进程打开文件选择对话框
+ const result = await window.electron.ipcRenderer.invoke(IPC_EVENT.LOAD_CONFIG)
+
+ if (result.canceled) {
+ message.info('已取消导入')
+ return
+ }
+
+ if (!result.success) {
+ message.error(`导入失败:${result.error}`)
+ return
+ }
+
+ const configData = result.data
+
+ // 验证配置数据
+ if (!configData.sensors || !Array.isArray(configData.sensors)) {
+ message.error('配置文件格式不正确')
+ return
+ }
+
+ // 转换导入的数据为内部格式
+ const convertedData = convertSensorDataToInternalFormat(configData.sensors)
+
+ // 更新传感器列表
+ setSensorList(convertedData)
+
+ // 清除选中状态
+ setSelectedSensorKey(null)
+ setRectangleData(null)
+
+ // 如果配置中包含元数据,恢复镜头焦距和测点距离
+ if (configData.metadata) {
+ if (configData.metadata.lensDistance !== undefined) {
+ setLensDistance(configData.metadata.lensDistance)
+ }
+ if (configData.metadata.measureDistance !== undefined) {
+ setMeasureDistance(configData.metadata.measureDistance)
+ }
+ }
+
+ message.success(`配置已成功导入!共 ${convertedData.length} 个传感器`)
+ } catch (error) {
+ console.error('导入配置失败:', error)
+ message.error(`导入配置失败:${error.message}`)
+ }
+ }
return (
@@ -744,6 +878,24 @@ function MeasurementPointSetting() {
disabled={!connectedDevice || loadingSensors || settingSensors}
/>
+
+ }
+ shape="circle"
+ onClick={handleSaveConfig}
+ loading={settingSensors}
+ disabled={!connectedDevice || loadingSensors || settingSensors}
+ />
+
+
+ }
+ shape="circle"
+ onClick={handleImportConfig}
+ loading={settingSensors}
+ disabled={!connectedDevice || loadingSensors || settingSensors}
+ />
+