Browse Source

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

master
cles 6 days 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", "name": "FlexometerSetup",
"version": "1.0.7", "version": "1.0.8",
"description": "An Electron application with React", "description": "An Electron application with React",
"main": "./out/main/index.js", "main": "./out/main/index.js",
"author": "cles", "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.IMAGE_SEND_ENABLED, imageSendEnabledSet)
ipcMain.handle(IPC_EVENT.IS_CLEAR_ZERO, isClearZeroSet) ipcMain.handle(IPC_EVENT.IS_CLEAR_ZERO, isClearZeroSet)
// 存储目录相关处理 // 存储目录相关处理
ipcMain.handle('open-directory', openDirectory) ipcMain.handle(IPC_EVENT.OPEN_DIRECTORY, openDirectory)
ipcMain.handle('select-directory', selectDirectory) ipcMain.handle(IPC_EVENT.SELECT_DIRECTORY, selectDirectory)
// 文件操作相关处理 // 文件操作相关处理
ipcMain.handle('ensure-directory', ensureDirectory) ipcMain.handle(IPC_EVENT.ENSURE_DIRECTORY, ensureDirectory)
ipcMain.handle('append-to-file', appendToFile) ipcMain.handle(IPC_EVENT.APPEND_TO_FILE, appendToFile)
ipcMain.handle('check-file-exists', checkFileExists) ipcMain.handle(IPC_EVENT.CHECK_FILE_EXISTS, checkFileExists)
ipcMain.handle('write-csv-header', writeCSVHeader) ipcMain.handle(IPC_EVENT.WRITE_CSV_HEADER, writeCSVHeader)
} }
// 搜索设备 // 搜索设备
const searchDevice = (event) => { const searchDevice = (event) => {

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

@ -37,6 +37,14 @@ export const IPC_EVENT = {
RECONNECT_CONFIG: 'reconnect:config', RECONNECT_CONFIG: 'reconnect:config',
RECONNECT_STATUS: 'reconnect:status', 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事件名 // 自动更新相关的value不要乱改,定义在这里是为了方便,这些都是标准的api事件名
UPDATE_AVAILABLE: 'update-available', UPDATE_AVAILABLE: 'update-available',
UPDATE_NOT_AVAILABLE: 'update-not-available', UPDATE_NOT_AVAILABLE: 'update-not-available',

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

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

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

@ -40,7 +40,7 @@ function DeflectionCollection() {
// //
useEffect(() => { useEffect(() => {
if (connectedDevice) { if (connectedDevice) {
console.log('设备连接,清空历史数据:', connectedDevice) // console.log(':', connectedDevice)
setSensorDataHistory([]) setSensorDataHistory([])
} }
}, [connectedDevice]) }, [connectedDevice])
@ -48,7 +48,7 @@ function DeflectionCollection() {
useEffect(() => { useEffect(() => {
if (window?.electron?.ipcRenderer?.on && IPC_EVENT?.RESULT_REPLY) { if (window?.electron?.ipcRenderer?.on && IPC_EVENT?.RESULT_REPLY) {
const handler = (event, data) => { 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)) { 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 // store
useEffect(() => { useEffect(() => {
console.log(rectangleData, 'rectangleData') // console.log(rectangleData, 'rectangleData')
if (rectangleData && image && imageScale) { if (rectangleData && image && imageScale) {
// //
const displayRect = { const displayRect = {
@ -109,16 +109,16 @@ function ImagePreview() {
height: relativeRect.height / imageScale height: relativeRect.height / imageScale
} }
console.log('矩形绘制完成:') // console.log(':')
console.log(' Stage坐标:', rectData) // console.log(' Stage:', rectData)
console.log(' 相对于显示图片的坐标:', relativeRect) // console.log(' :', relativeRect)
console.log(' 相对于原始图片的坐标:', originalImageRect) // console.log(' :', originalImageRect)
console.log( // console.log(
`最终坐标: x=${Math.round(originalImageRect.x)}, y=${Math.round(originalImageRect.y)}` // `: x=${Math.round(originalImageRect.x)}, y=${Math.round(originalImageRect.y)}`
) // )
console.log( // console.log(
`最终尺寸: width=${Math.round(originalImageRect.width)}, height=${Math.round(originalImageRect.height)}` // `: width=${Math.round(originalImageRect.width)}, height=${Math.round(originalImageRect.height)}`
) // )
// Zustand store // Zustand store
const rectInfo = { const rectInfo = {

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

@ -233,10 +233,10 @@ function MeasurementPointSetting() {
// //
setSensorList((prev) => prev.filter((sensor) => sensor.key !== selectedSensorKey)) setSensorList((prev) => prev.filter((sensor) => sensor.key !== selectedSensorKey))
setSelectedSensorKey(null) setSelectedSensorKey(null)
console.log('删除传感器:', selectedSensorKey) // console.log(':', selectedSensorKey)
}, },
onCancel: () => { onCancel: () => {
console.log('取消删除') // console.log('')
} }
}) })
} }
@ -262,10 +262,10 @@ function MeasurementPointSetting() {
// //
setSensorList([]) setSensorList([])
setSelectedSensorKey(null) setSelectedSensorKey(null)
console.log('清空传感器列表') // console.log('')
}, },
onCancel: () => { onCancel: () => {
console.log('取消清空') // console.log('')
} }
}) })
} }
@ -315,7 +315,7 @@ function MeasurementPointSetting() {
}) })
if (result.success) { if (result.success) {
console.log('传感器数据加载成功:', result.data) // console.log(':', result.data)
// //
if (result.data && result.data.values && Array.isArray(result.data.values)) { 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 = () => { const handleConnectOrDisconnect = () => {
console.log('点击连接,当前selectedDevice:', selectedDevice, '端口:', devicePort) // console.log('selectedDevice:', selectedDevice, ':', devicePort)
if (connected) { if (connected) {
// //
@ -171,7 +171,7 @@ function SiderHeader({ showSystemSettings = true }) {
return return
} }
console.log('发送连接请求:', { ip: selectedDevice, port: devicePort }) // console.log(':', { ip: selectedDevice, port: devicePort })
setConnecting(true) // loading setConnecting(true) // loading
window.electron.ipcRenderer.send(IPC_EVENT.DEVICE_CONNECT, { window.electron.ipcRenderer.send(IPC_EVENT.DEVICE_CONNECT, {
ip: selectedDevice, ip: selectedDevice,

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

@ -43,6 +43,9 @@ function SystemSettings() {
const realtimeDataEnabledRef = useRef(realtimeDataEnabled) const realtimeDataEnabledRef = useRef(realtimeDataEnabled)
const connectedDeviceRef = useRef(connectedDevice) const connectedDeviceRef = useRef(connectedDevice)
const storagePathRef = useRef(storagePath) const storagePathRef = useRef(storagePath)
const alarmDataEnabledRef = useRef(alarmDataEnabled)
const alarmEnabledRef = useRef(alarmEnabled)
const alarmLimitsRef = useRef(alarmLimits)
// ref // ref
useEffect(() => { useEffect(() => {
@ -57,11 +60,23 @@ function SystemSettings() {
storagePathRef.current = storagePath storagePathRef.current = storagePath
}, [storagePath]) }, [storagePath])
useEffect(() => {
alarmDataEnabledRef.current = alarmDataEnabled
}, [alarmDataEnabled])
useEffect(() => {
alarmEnabledRef.current = alarmEnabled
}, [alarmEnabled])
useEffect(() => {
alarmLimitsRef.current = alarmLimits
}, [alarmLimits])
// //
useEffect(() => { useEffect(() => {
const reconnectStatusHandler = (event, result) => { const reconnectStatusHandler = (event, result) => {
const { status } = result const { status } = result
console.log('Received reconnect status:', result) // console.log('Received reconnect status:', result)
switch (status) { switch (status) {
case 'reconnecting': case 'reconnecting':
@ -125,7 +140,7 @@ function SystemSettings() {
// //
const dirResult = await window.electron.ipcRenderer.invoke( const dirResult = await window.electron.ipcRenderer.invoke(
'ensure-directory', IPC_EVENT.ENSURE_DIRECTORY,
realtimeDataDir realtimeDataDir
) )
if (!dirResult.success) { if (!dirResult.success) {
@ -135,19 +150,19 @@ function SystemSettings() {
// CSV // CSV
const fileExistsResult = await window.electron.ipcRenderer.invoke( const fileExistsResult = await window.electron.ipcRenderer.invoke(
'check-file-exists', IPC_EVENT.CHECK_FILE_EXISTS,
csvFilePath csvFilePath
) )
if (fileExistsResult.success && !fileExistsResult.exists) { if (fileExistsResult.success && !fileExistsResult.exists) {
const sensorCount = data.values.sensors ? data.values.sensors.length : 0 const sensorCount = data.values.sensors ? data.values.sensors.length : 0
const header = createCSVHeader(sensorCount) 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 dataRow = sensorDataToCSVRow(data)
const appendResult = await window.electron.ipcRenderer.invoke( const appendResult = await window.electron.ipcRenderer.invoke(
'append-to-file', IPC_EVENT.APPEND_TO_FILE,
csvFilePath, csvFilePath,
dataRow dataRow
) )
@ -162,16 +177,97 @@ function SystemSettings() {
[] // 使ref访 [] // 使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 // CSV
useEffect(() => { useEffect(() => {
if (window?.electron?.ipcRenderer?.on && IPC_EVENT?.RESULT_REPLY) { if (window?.electron?.ipcRenderer?.on && IPC_EVENT?.RESULT_REPLY) {
const handler = (event, data) => { 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)) { if (data && data.values && data.values.sensors && Array.isArray(data.values.sensors)) {
// //
writeRealtimeDataToCSV(data) 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) window.electron.ipcRenderer.on(IPC_EVENT.RESULT_REPLY, handler)
@ -179,9 +275,7 @@ function SystemSettings() {
window.electron.ipcRenderer.removeListener(IPC_EVENT.RESULT_REPLY, handler) window.electron.ipcRenderer.removeListener(IPC_EVENT.RESULT_REPLY, handler)
} }
} }
}, [writeRealtimeDataToCSV]) }, [writeRealtimeDataToCSV, writeAlarmDataToCSV]) // 移除其他依赖项,因为现在使用ref // 打开存储目录
//
const handleOpenStoragePath = async () => { const handleOpenStoragePath = async () => {
if (!storagePath) { if (!storagePath) {
message.warning('存储路径未设置') message.warning('存储路径未设置')
@ -190,7 +284,7 @@ function SystemSettings() {
try { try {
// 使 IPC // 使 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) { if (!result.success) {
message.error(`打开目录失败:${result.error}`) message.error(`打开目录失败:${result.error}`)
} }
@ -204,7 +298,7 @@ function SystemSettings() {
const handleSelectStoragePath = async () => { const handleSelectStoragePath = async () => {
try { try {
// 使 IPC // 使 IPC
const result = await window.electron.ipcRenderer.invoke('select-directory', { const result = await window.electron.ipcRenderer.invoke(IPC_EVENT.SELECT_DIRECTORY, {
title: '选择存储目录', title: '选择存储目录',
defaultPath: storagePath defaultPath: storagePath
}) })
@ -236,6 +330,22 @@ function SystemSettings() {
return headers.join(',') + '\n' return headers.join(',') + '\n'
} }
// CSV
const createAlarmCSVHeader = () => {
const headers = [
'报警记录时间',
'测点位置',
'基准标靶',
'计算系数',
'xReal坐标',
'yReal坐标',
'报警类型',
'阈值范围',
'超出值'
]
return headers.join(',') + '\n'
}
// CSV // CSV
const sensorDataToCSVRow = (data) => { const sensorDataToCSVRow = (data) => {
const timestamp = new Date().toLocaleString() const timestamp = new Date().toLocaleString()
@ -262,6 +372,81 @@ function SystemSettings() {
return row.join(',') + '\n' 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 () => { const handleReadParam = async () => {
if (!selectedParam) { if (!selectedParam) {
@ -310,7 +495,7 @@ function SystemSettings() {
if (result.success) { if (result.success) {
setParamValue(result.data.values || '') setParamValue(result.data.values || '')
console.log(result.data, eventName) // console.log(result.data, eventName)
message.success(`${selectedParam} 读取成功!`) message.success(`${selectedParam} 读取成功!`)
} else { } else {
message.error(`读取失败:${result.error}`) message.error(`读取失败:${result.error}`)

Loading…
Cancel
Save