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} /> + +