Browse Source

feat: 更新文件操作的IPC事件名称,并在系统设置中添加警报数据处理

pull/1/head
cles 1 year ago
parent
commit
fcdf1627fb
  1. 2
      package.json
  2. 12
      src/main/ipcRouter.js
  3. 8
      src/renderer/src/common/ipcEvents.js
  4. 6
      src/renderer/src/components/AutoUpdater/AutoUpdater.jsx
  5. 4
      src/renderer/src/components/DeflectionCollection/DeflectionCollection.jsx
  6. 22
      src/renderer/src/components/ImageCollection/ImageCollection.jsx
  7. 10
      src/renderer/src/components/MeasurementPointSetting/MeasurementPointSetting.jsx
  8. 4
      src/renderer/src/components/SiderHeader/SiderHeader.jsx
  9. 211
      src/renderer/src/components/SystemSettings/SystemSettings.jsx

2
package.json

@ -1,6 +1,6 @@
{
"name": "FlexometerSetup",
"version": "1.0.7",
"version": "1.0.8",
"description": "An Electron application with React",
"main": "./out/main/index.js",
"author": "cles",

12
src/main/ipcRouter.js

@ -191,13 +191,13 @@ export function registerIpRouter() {
ipcMain.handle(IPC_EVENT.IMAGE_SEND_ENABLED, imageSendEnabledSet)
ipcMain.handle(IPC_EVENT.IS_CLEAR_ZERO, isClearZeroSet)
// 存储目录相关处理
ipcMain.handle('open-directory', openDirectory)
ipcMain.handle('select-directory', selectDirectory)
ipcMain.handle(IPC_EVENT.OPEN_DIRECTORY, openDirectory)
ipcMain.handle(IPC_EVENT.SELECT_DIRECTORY, selectDirectory)
// 文件操作相关处理
ipcMain.handle('ensure-directory', ensureDirectory)
ipcMain.handle('append-to-file', appendToFile)
ipcMain.handle('check-file-exists', checkFileExists)
ipcMain.handle('write-csv-header', writeCSVHeader)
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) => {

8
src/renderer/src/common/ipcEvents.js

@ -37,6 +37,14 @@ export const IPC_EVENT = {
RECONNECT_CONFIG: 'reconnect:config',
RECONNECT_STATUS: 'reconnect:status',
// 文件操作相关
OPEN_DIRECTORY: 'file:open-directory',
SELECT_DIRECTORY: 'file:select-directory',
ENSURE_DIRECTORY: 'file:ensure-directory',
APPEND_TO_FILE: 'file:append-to-file',
CHECK_FILE_EXISTS: 'file:check-file-exists',
WRITE_CSV_HEADER: 'file:write-csv-header',
// 自动更新相关的value不要乱改,定义在这里是为了方便,这些都是标准的api事件名
UPDATE_AVAILABLE: 'update-available',
UPDATE_NOT_AVAILABLE: 'update-not-available',

6
src/renderer/src/components/AutoUpdater/AutoUpdater.jsx

@ -17,7 +17,7 @@ function AutoUpdater() {
useEffect(() => {
// 监听发现新版本
const handleUpdateAvailable = (event, info) => {
console.log('收到update-available事件:', info)
// console.log('收到update-available事件:', info)
setUpdateInfo(info)
setUpdateStatus('发现新版本')
setIsModalVisible(true)
@ -28,14 +28,14 @@ function AutoUpdater() {
// 监听下载进度
const handleDownloadProgress = (event, progressObj) => {
console.log('下载进度:', progressObj)
// console.log('下载进度:', progressObj)
setDownloadProgress(Math.round(progressObj.percent))
setUpdateStatus(`正在下载新版本... ${Math.round(progressObj.percent)}%`)
}
// 监听下载完成
const handleUpdateDownloaded = () => {
console.log('下载完成')
// console.log('下载完成')
setIsDownloading(false)
setIsDownloaded(true)
setUpdateStatus('新版本下载完成,准备安装')

4
src/renderer/src/components/DeflectionCollection/DeflectionCollection.jsx

@ -40,7 +40,7 @@ function DeflectionCollection() {
// 监听设备连接状态变化,设备连接时清空历史数据
useEffect(() => {
if (connectedDevice) {
console.log('设备连接,清空历史数据:', connectedDevice)
// console.log('设备连接,清空历史数据:', connectedDevice)
setSensorDataHistory([])
}
}, [connectedDevice])
@ -48,7 +48,7 @@ function DeflectionCollection() {
useEffect(() => {
if (window?.electron?.ipcRenderer?.on && IPC_EVENT?.RESULT_REPLY) {
const handler = (event, data) => {
console.log('收到主进程RESULT_REPLY:', data)
// console.log('收到主进程RESULT_REPLY:', data)
// 检查数据格式
if (data && data.values && data.values.sensors && Array.isArray(data.values.sensors)) {

22
src/renderer/src/components/ImageCollection/ImageCollection.jsx

@ -22,7 +22,7 @@ function ImagePreview() {
// 监听store中的矩形数据变化,转换为显示坐标
useEffect(() => {
console.log(rectangleData, 'rectangleData')
// console.log(rectangleData, 'rectangleData')
if (rectangleData && image && imageScale) {
// 将原始图片坐标转换为显示坐标
const displayRect = {
@ -109,16 +109,16 @@ function ImagePreview() {
height: relativeRect.height / imageScale
}
console.log('矩形绘制完成:')
console.log(' Stage坐标:', rectData)
console.log(' 相对于显示图片的坐标:', relativeRect)
console.log(' 相对于原始图片的坐标:', originalImageRect)
console.log(
`最终坐标: x=${Math.round(originalImageRect.x)}, y=${Math.round(originalImageRect.y)}`
)
console.log(
`最终尺寸: width=${Math.round(originalImageRect.width)}, height=${Math.round(originalImageRect.height)}`
)
// console.log('矩形绘制完成:')
// console.log(' Stage坐标:', rectData)
// console.log(' 相对于显示图片的坐标:', relativeRect)
// console.log(' 相对于原始图片的坐标:', originalImageRect)
// console.log(
// `最终坐标: x=${Math.round(originalImageRect.x)}, y=${Math.round(originalImageRect.y)}`
// )
// console.log(
// `最终尺寸: width=${Math.round(originalImageRect.width)}, height=${Math.round(originalImageRect.height)}`
// )
// 将矩形数据保存到Zustand store
const rectInfo = {

10
src/renderer/src/components/MeasurementPointSetting/MeasurementPointSetting.jsx

@ -233,10 +233,10 @@ function MeasurementPointSetting() {
// 删除选中的传感器
setSensorList((prev) => prev.filter((sensor) => sensor.key !== selectedSensorKey))
setSelectedSensorKey(null)
console.log('删除传感器:', selectedSensorKey)
// console.log('删除传感器:', selectedSensorKey)
},
onCancel: () => {
console.log('取消删除')
// console.log('取消删除')
}
})
}
@ -262,10 +262,10 @@ function MeasurementPointSetting() {
// 清空传感器列表
setSensorList([])
setSelectedSensorKey(null)
console.log('清空传感器列表')
// console.log('清空传感器列表')
},
onCancel: () => {
console.log('取消清空')
// console.log('取消清空')
}
})
}
@ -315,7 +315,7 @@ function MeasurementPointSetting() {
})
if (result.success) {
console.log('传感器数据加载成功:', result.data)
// console.log('传感器数据加载成功:', result.data)
// 检查返回的数据格式
if (result.data && result.data.values && Array.isArray(result.data.values)) {

4
src/renderer/src/components/SiderHeader/SiderHeader.jsx

@ -150,7 +150,7 @@ function SiderHeader({ showSystemSettings = true }) {
// 连接/断开设备
const handleConnectOrDisconnect = () => {
console.log('点击连接,当前selectedDevice:', selectedDevice, '端口:', devicePort)
// console.log('点击连接,当前selectedDevice:', selectedDevice, '端口:', devicePort)
if (connected) {
// 已连接,断开
@ -171,7 +171,7 @@ function SiderHeader({ showSystemSettings = true }) {
return
}
console.log('发送连接请求:', { ip: selectedDevice, port: devicePort })
// console.log('发送连接请求:', { ip: selectedDevice, port: devicePort })
setConnecting(true) // 开始 loading
window.electron.ipcRenderer.send(IPC_EVENT.DEVICE_CONNECT, {
ip: selectedDevice,

211
src/renderer/src/components/SystemSettings/SystemSettings.jsx

@ -43,6 +43,9 @@ function SystemSettings() {
const realtimeDataEnabledRef = useRef(realtimeDataEnabled)
const connectedDeviceRef = useRef(connectedDevice)
const storagePathRef = useRef(storagePath)
const alarmDataEnabledRef = useRef(alarmDataEnabled)
const alarmEnabledRef = useRef(alarmEnabled)
const alarmLimitsRef = useRef(alarmLimits)
// 更新ref值
useEffect(() => {
@ -57,11 +60,23 @@ function SystemSettings() {
storagePathRef.current = storagePath
}, [storagePath])
useEffect(() => {
alarmDataEnabledRef.current = alarmDataEnabled
}, [alarmDataEnabled])
useEffect(() => {
alarmEnabledRef.current = alarmEnabled
}, [alarmEnabled])
useEffect(() => {
alarmLimitsRef.current = alarmLimits
}, [alarmLimits])
// 监听重连状态更新
useEffect(() => {
const reconnectStatusHandler = (event, result) => {
const { status } = result
console.log('Received reconnect status:', result)
// console.log('Received reconnect status:', result)
switch (status) {
case 'reconnecting':
@ -125,7 +140,7 @@ function SystemSettings() {
// 确保目录存在
const dirResult = await window.electron.ipcRenderer.invoke(
'ensure-directory',
IPC_EVENT.ENSURE_DIRECTORY,
realtimeDataDir
)
if (!dirResult.success) {
@ -135,19 +150,19 @@ function SystemSettings() {
// 检查CSV文件是否存在,如果不存在则先写入表头
const fileExistsResult = await window.electron.ipcRenderer.invoke(
'check-file-exists',
IPC_EVENT.CHECK_FILE_EXISTS,
csvFilePath
)
if (fileExistsResult.success && !fileExistsResult.exists) {
const sensorCount = data.values.sensors ? data.values.sensors.length : 0
const header = createCSVHeader(sensorCount)
await window.electron.ipcRenderer.invoke('write-csv-header', csvFilePath, header)
await window.electron.ipcRenderer.invoke(IPC_EVENT.WRITE_CSV_HEADER, csvFilePath, header)
}
// 追加数据行
const dataRow = sensorDataToCSVRow(data)
const appendResult = await window.electron.ipcRenderer.invoke(
'append-to-file',
IPC_EVENT.APPEND_TO_FILE,
csvFilePath,
dataRow
)
@ -162,16 +177,97 @@ function SystemSettings() {
[] // 空依赖数组,因为我们使用ref来访问最新的值
)
// 新增:写入报警数据到CSV文件
const writeAlarmDataToCSV = useCallback(
async (data, alarmSensors) => {
// 使用ref获取最新状态值,避免闭包问题
if (
!alarmDataEnabledRef.current ||
!alarmEnabledRef.current ||
!connectedDeviceRef.current?.ip ||
!storagePathRef.current ||
!alarmSensors.length
) {
return
}
try {
// 构建文件路径: 存储路径/IP地址/报警数据/YYYY-MM-DD.csv
const today = new Date()
const dateStr =
today.getFullYear() +
String(today.getMonth() + 1).padStart(2, '0') +
String(today.getDate()).padStart(2, '0')
const ipFolder = connectedDeviceRef.current.ip
const alarmDataDir = `${storagePathRef.current}/${ipFolder}/报警数据`
const csvFilePath = `${alarmDataDir}/${dateStr}.csv`
// 确保目录存在
const dirResult = await window.electron.ipcRenderer.invoke(
IPC_EVENT.ENSURE_DIRECTORY,
alarmDataDir
)
if (!dirResult.success) {
console.error('创建报警数据目录失败:', dirResult.error)
return
}
// 检查CSV文件是否存在,如果不存在则先写入表头
const fileExistsResult = await window.electron.ipcRenderer.invoke(
IPC_EVENT.CHECK_FILE_EXISTS,
csvFilePath
)
if (fileExistsResult.success && !fileExistsResult.exists) {
const header = createAlarmCSVHeader()
await window.electron.ipcRenderer.invoke(IPC_EVENT.WRITE_CSV_HEADER, csvFilePath, header)
}
// 为每个报警传感器写入一行数据
for (const sensor of alarmSensors) {
const dataRow = alarmSensorToCSVRow(data, sensor)
const appendResult = await window.electron.ipcRenderer.invoke(
IPC_EVENT.APPEND_TO_FILE,
csvFilePath,
dataRow
)
if (!appendResult.success) {
console.error('写入报警CSV文件失败:', appendResult.error)
}
}
} catch (error) {
console.error('写入报警数据失败:', error)
}
},
[] // 现在完全使用ref,不需要依赖项
)
// 监听实时数据并记录到CSV
useEffect(() => {
if (window?.electron?.ipcRenderer?.on && IPC_EVENT?.RESULT_REPLY) {
const handler = (event, data) => {
console.log('收到主进程RESULT_REPLY:', data)
// console.log('收到主进程RESULT_REPLY:', data)
// 检查数据格式
if (data && data.values && data.values.sensors && Array.isArray(data.values.sensors)) {
// 直接调用写入函数,内部会检查是否启用实时数据记录
// 写入实时数据(如果启用)
writeRealtimeDataToCSV(data)
// 检查报警条件并写入报警数据(如果启用)
// 使用ref获取最新状态值
if (
alarmDataEnabledRef.current &&
alarmEnabledRef.current &&
connectedDeviceRef.current?.ip &&
storagePathRef.current
) {
const alarmSensors = checkAlarmConditions(data.values.sensors, alarmLimitsRef.current)
if (alarmSensors.length > 0) {
// console.log('检测到报警数据:', alarmSensors)
writeAlarmDataToCSV(data, alarmSensors)
}
}
}
}
window.electron.ipcRenderer.on(IPC_EVENT.RESULT_REPLY, handler)
@ -179,9 +275,7 @@ function SystemSettings() {
window.electron.ipcRenderer.removeListener(IPC_EVENT.RESULT_REPLY, handler)
}
}
}, [writeRealtimeDataToCSV])
// 打开存储目录
}, [writeRealtimeDataToCSV, writeAlarmDataToCSV]) // 移除其他依赖项,因为现在使用ref // 打开存储目录
const handleOpenStoragePath = async () => {
if (!storagePath) {
message.warning('存储路径未设置')
@ -190,7 +284,7 @@ function SystemSettings() {
try {
// 使用 IPC 调用主进程打开目录
const result = await window.electron.ipcRenderer.invoke('open-directory', storagePath)
const result = await window.electron.ipcRenderer.invoke(IPC_EVENT.OPEN_DIRECTORY, storagePath)
if (!result.success) {
message.error(`打开目录失败:${result.error}`)
}
@ -204,7 +298,7 @@ function SystemSettings() {
const handleSelectStoragePath = async () => {
try {
// 使用 IPC 调用主进程选择目录
const result = await window.electron.ipcRenderer.invoke('select-directory', {
const result = await window.electron.ipcRenderer.invoke(IPC_EVENT.SELECT_DIRECTORY, {
title: '选择存储目录',
defaultPath: storagePath
})
@ -236,6 +330,22 @@ function SystemSettings() {
return headers.join(',') + '\n'
}
// 创建报警数据CSV表头
const createAlarmCSVHeader = () => {
const headers = [
'报警记录时间',
'测点位置',
'基准标靶',
'计算系数',
'xReal坐标',
'yReal坐标',
'报警类型',
'阈值范围',
'超出值'
]
return headers.join(',') + '\n'
}
// 将传感器数据转换为CSV行(动态处理所有测点)
const sensorDataToCSVRow = (data) => {
const timestamp = new Date().toLocaleString()
@ -262,6 +372,81 @@ function SystemSettings() {
return row.join(',') + '\n'
}
// 将报警传感器数据转换为CSV行
const alarmSensorToCSVRow = (data, alarmInfo) => {
const timestamp = new Date().toLocaleString()
const { sensor, alarmType, threshold, exceedValue } = alarmInfo
const row = [
timestamp,
sensor.pos || '',
sensor.tar || '',
sensor.arg || '',
sensor.xReal !== null && sensor.xReal !== undefined ? sensor.xReal : '无数据',
sensor.yReal !== null && sensor.yReal !== undefined ? sensor.yReal : '无数据',
alarmType,
threshold,
exceedValue
]
return row.join(',') + '\n'
}
// 检查传感器数据是否超出阈值
const checkAlarmConditions = (sensors, limits) => {
const alarmSensors = []
if (!sensors || !Array.isArray(sensors)) {
return alarmSensors
}
sensors.forEach((sensor) => {
if (!sensor) return
const { xReal, yReal } = sensor
// 检查X轴是否超出阈值
if (xReal !== null && xReal !== undefined) {
if (xReal > limits.xUpper) {
alarmSensors.push({
sensor,
alarmType: 'X轴上限超出',
threshold: `±${limits.xUpper}`,
exceedValue: (xReal - limits.xUpper).toFixed(2)
})
} else if (xReal < limits.xLower) {
alarmSensors.push({
sensor,
alarmType: 'X轴下限超出',
threshold: `±${Math.abs(limits.xLower)}`,
exceedValue: (limits.xLower - xReal).toFixed(2)
})
}
}
// 检查Y轴是否超出阈值
if (yReal !== null && yReal !== undefined) {
if (yReal > limits.yUpper) {
alarmSensors.push({
sensor,
alarmType: 'Y轴上限超出',
threshold: `±${limits.yUpper}`,
exceedValue: (yReal - limits.yUpper).toFixed(2)
})
} else if (yReal < limits.yLower) {
alarmSensors.push({
sensor,
alarmType: 'Y轴下限超出',
threshold: `±${Math.abs(limits.yLower)}`,
exceedValue: (limits.yLower - yReal).toFixed(2)
})
}
}
})
return alarmSensors
}
// 读取参数函数
const handleReadParam = async () => {
if (!selectedParam) {
@ -310,7 +495,7 @@ function SystemSettings() {
if (result.success) {
setParamValue(result.data.values || '')
console.log(result.data, eventName)
// console.log(result.data, eventName)
message.success(`${selectedParam} 读取成功!`)
} else {
message.error(`读取失败:${result.error}`)

Loading…
Cancel
Save