diff --git a/CHANGELOGS/V1.2.2.md b/CHANGELOGS/V1.2.2.md new file mode 100644 index 0000000..3fcace4 --- /dev/null +++ b/CHANGELOGS/V1.2.2.md @@ -0,0 +1,64 @@ +# V1.2.2 更新日志 + +- **更新日期**: 2026年06月09日 +- **版本号**: 1.2.2 + +## 新增功能 + +### 1. 数据修正系数配置 +- **新增数据修正系数功能**:在系统设置中新增 X/Y 双方向修正系数配置,支持分别设置 `xK`、`xB`、`yK`、`yB` +- **支持线性修正公式**:页面显示值按 `newValue = K * originalValue + B` 计算 +- **本地持久化保存**:修正系数自动保存到本地,上位机重启后可继续沿用 +- **页面更新提示**:修正系数修改成功后,页面会给出即时提示,方便确认操作结果 + +### 2. 修正值统一应用 +- **曲线显示统一使用修正值**:X/Y 方向曲线改为显示修正后的数据 +- **实时数据显示统一使用修正值**:右侧实时数据显示与曲线口径保持一致 +- **报警判断统一使用修正值**:报警逻辑基于修正后的 X/Y 数据进行判断 +- **CSV 导出统一使用修正值**:实时数据与报警数据导出均使用修正后的值 + +## 技术细节 + +### 修正系数 +- 修正系数项: + - `xK` + - `xB` + - `yK` + - `yB` +- 默认值: + - `xK = 1` + - `xB = 0` + - `yK = 1` + - `yB = 0` + +### 数据处理策略 +- 页面接收到实时传感器数据后,先计算并冻结: + - `correctedX` + - `correctedY` +- 冻结结果用于: + - 历史曲线显示 + - 实时数据显示 + - 报警判断 + - 实时 CSV 导出 + - 报警 CSV 导出 + +## 影响范围 +- 挠度采集页 X/Y 曲线显示 +- 挠度采集页实时数据显示 +- 系统设置中的数据修正系数配置 +- 报警判断逻辑 +- 实时数据 CSV 导出 +- 报警数据 CSV 导出 + +## 依赖更新 +- 无依赖包更新 + +## 注意事项 +1. 修正系数仅作用于当前上位机本地显示与导出,不会下发到设备端。 +2. 设备侧“测点设置”中的“计算系数”仍为独立参数,与本次新增的上位机修正系数互不替代。 +3. 修改修正系数后,仅后续新采集数据使用新系数,已有历史数据保持采集当时的结果。 +4. 如需验证导出结果,建议同时比对页面曲线、实时数据显示与 CSV 记录的一致性。 + +--- + +**完整更新内容请查看项目 Git 提交记录** diff --git a/package.json b/package.json index a623ee0..67296c5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "FlexometerSetup", - "version": "1.2.1", + "version": "1.2.2", "description": "An Electron application with React", "main": "./out/main/index.js", "author": "cles", diff --git a/src/renderer/src/components/DeflectionCollection/DeflectionCollection.jsx b/src/renderer/src/components/DeflectionCollection/DeflectionCollection.jsx index 3b9270c..e693fb2 100644 --- a/src/renderer/src/components/DeflectionCollection/DeflectionCollection.jsx +++ b/src/renderer/src/components/DeflectionCollection/DeflectionCollection.jsx @@ -1,6 +1,7 @@ -import { useEffect, useState } from 'react' +import { useEffect, useRef, useState } from 'react' import { IPC_EVENT } from '../../common/ipcEvents' import useDeviceStore from '../../stores/deviceStore' +import { applyCorrectionToSensors } from '../../utils/deflectionCorrection' import { Line } from 'react-chartjs-2' import { Chart as ChartJS, @@ -24,6 +25,12 @@ function DeflectionCollection() { const connectedDevice = useDeviceStore((state) => state.connectedDevice) const alarmEnabled = useDeviceStore((state) => state.alarmEnabled) const alarmLimits = useDeviceStore((state) => state.alarmLimits) + const correctionFactors = useDeviceStore((state) => state.correctionFactors) + const correctionFactorsRef = useRef(correctionFactors) + + useEffect(() => { + correctionFactorsRef.current = correctionFactors + }, [correctionFactors]) // 为每个传感器生成颜色 const targetColors = [ @@ -54,7 +61,7 @@ function DeflectionCollection() { 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 + sensors: applyCorrectionToSensors(data.values.sensors, correctionFactorsRef.current) } setSensorDataHistory((prev) => { @@ -104,7 +111,7 @@ function DeflectionCollection() { label: `测点${sensor.pos}`, data: sensorDataHistory.map((dataPoint) => { const sensorData = dataPoint.sensors.find((s) => s.pos === sensor.pos) - return sensorData ? Number(sensorData.yReal) : null + return sensorData ? sensorData.correctedY : null }), borderColor: sensor.color, backgroundColor: sensor.color.replace('1)', '0.2)'), @@ -150,7 +157,7 @@ function DeflectionCollection() { label: `测点${sensor.pos}`, data: sensorDataHistory.map((dataPoint) => { const sensorData = dataPoint.sensors.find((s) => s.pos === sensor.pos) - return sensorData ? Number(sensorData.xReal) : null + return sensorData ? sensorData.correctedX : null }), borderColor: sensor.color, backgroundColor: sensor.color.replace('1)', '0.2)'), @@ -219,7 +226,9 @@ function DeflectionCollection() { // 获取最新数据用于右侧实时显示 const latestData = - sensorDataHistory.length > 0 ? sensorDataHistory[sensorDataHistory.length - 1] : null + sensorDataHistory.length > 0 + ? sensorDataHistory[sensorDataHistory.length - 1] + : null return (
- X → {Number(sensor.xReal).toFixed(3)} + X →{' '} + {sensor.correctedX !== null && sensor.correctedX !== undefined + ? Number(sensor.correctedX).toFixed(3) + : '无数据'}
@@ -422,7 +434,10 @@ function DeflectionCollection() { marginLeft: '5px' }} > - Y → {Number(sensor.yReal).toFixed(3)} + Y →{' '} + {sensor.correctedY !== null && sensor.correctedY !== undefined + ? Number(sensor.correctedY).toFixed(3) + : '无数据'} diff --git a/src/renderer/src/components/SystemSettings/SystemSettings.jsx b/src/renderer/src/components/SystemSettings/SystemSettings.jsx index 3f61001..02088b4 100644 --- a/src/renderer/src/components/SystemSettings/SystemSettings.jsx +++ b/src/renderer/src/components/SystemSettings/SystemSettings.jsx @@ -12,6 +12,12 @@ import { useState, useEffect, useCallback, useRef } from 'react' import { IPC_EVENT } from '../../common/ipcEvents' import useDeviceStore from '../../stores/deviceStore' import AlgorithmSettings from '../AlgorithmSettings/AlgorithmSettings' +import { + applyCorrectionToSensor, + DEFAULT_CORRECTION_FACTORS, + DEFLECTION_CORRECTION_STORAGE_KEY, + sanitizeCorrectionFactors +} from '../../utils/deflectionCorrection' function SystemSettings() { // 状态管理 @@ -37,11 +43,13 @@ function SystemSettings() { const isReconnecting = useDeviceStore((state) => state.isReconnecting) const alarmEnabled = useDeviceStore((state) => state.alarmEnabled) const alarmLimits = useDeviceStore((state) => state.alarmLimits) + const correctionFactors = useDeviceStore((state) => state.correctionFactors) const setReconnectEnabled = useDeviceStore((state) => state.setReconnectEnabled) const setReconnectInterval = useDeviceStore((state) => state.setReconnectInterval) const setReconnecting = useDeviceStore((state) => state.setReconnecting) const setAlarmEnabled = useDeviceStore((state) => state.setAlarmEnabled) const setAlarmLimits = useDeviceStore((state) => state.setAlarmLimits) + const setCorrectionFactors = useDeviceStore((state) => state.setCorrectionFactors) // 使用ref来存储最新的状态值,避免闭包问题 const realtimeDataEnabledRef = useRef(realtimeDataEnabled) @@ -50,6 +58,7 @@ function SystemSettings() { const alarmDataEnabledRef = useRef(alarmDataEnabled) const alarmEnabledRef = useRef(alarmEnabled) const alarmLimitsRef = useRef(alarmLimits) + const correctionFactorsRef = useRef(correctionFactors) // 更新ref值 useEffect(() => { @@ -76,6 +85,10 @@ function SystemSettings() { alarmLimitsRef.current = alarmLimits }, [alarmLimits]) + useEffect(() => { + correctionFactorsRef.current = correctionFactors + }, [correctionFactors]) + // 监听重连状态更新 useEffect(() => { const reconnectStatusHandler = (event, result) => { @@ -119,6 +132,23 @@ function SystemSettings() { } }, []) + useEffect(() => { + const savedFactors = localStorage.getItem(DEFLECTION_CORRECTION_STORAGE_KEY) + + if (!savedFactors) { + setCorrectionFactors(DEFAULT_CORRECTION_FACTORS) + return + } + + try { + const parsedFactors = JSON.parse(savedFactors) + setCorrectionFactors(sanitizeCorrectionFactors(parsedFactors)) + } catch (error) { + console.error('读取数据修正系数失败:', error) + setCorrectionFactors(DEFAULT_CORRECTION_FACTORS) + } + }, [setCorrectionFactors]) + // 新增:写入实时数据到CSV文件 const writeRealtimeDataToCSV = useCallback( async (data) => { @@ -255,8 +285,19 @@ function SystemSettings() { // 检查数据格式 if (data && data.values && data.values.sensors && Array.isArray(data.values.sensors)) { + const frozenSensors = data.values.sensors.map((sensor) => + applyCorrectionToSensor(sensor, correctionFactorsRef.current) + ) + const frozenData = { + ...data, + values: { + ...data.values, + sensors: frozenSensors + } + } + // 写入实时数据(如果启用) - writeRealtimeDataToCSV(data) + writeRealtimeDataToCSV(frozenData) // 检查报警条件并写入报警数据(如果启用) // 使用ref获取最新状态值 @@ -266,10 +307,10 @@ function SystemSettings() { connectedDeviceRef.current?.ip && storagePathRef.current ) { - const alarmSensors = checkAlarmConditions(data.values.sensors, alarmLimitsRef.current) + const alarmSensors = checkAlarmConditions(frozenSensors, alarmLimitsRef.current) if (alarmSensors.length > 0) { // console.log('检测到报警数据:', alarmSensors) - writeAlarmDataToCSV(data, alarmSensors) + writeAlarmDataToCSV(frozenData, alarmSensors) } } } @@ -327,8 +368,8 @@ function SystemSettings() { headers.push( `测点 ${i} 基准标靶`, `测点 ${i} 计算系数`, - `测点 ${i}xReal 坐标`, - `测点 ${i}yReal 坐标` + `测点 ${i} X修正值`, + `测点 ${i} Y修正值` ) } return headers.join(',') + '\n' @@ -341,8 +382,8 @@ function SystemSettings() { '测点位置', '基准标靶', '计算系数', - 'xReal坐标', - 'yReal坐标', + 'X修正值', + 'Y修正值', '报警类型', '阈值范围', '超出值' @@ -364,8 +405,12 @@ function SystemSettings() { row.push( sensor.tar || '', sensor.arg || '', - sensor.xReal !== null && sensor.xReal !== undefined ? sensor.xReal : '无数据', - sensor.yReal !== null && sensor.yReal !== undefined ? sensor.yReal : '无数据' + sensor.correctedX !== null && sensor.correctedX !== undefined + ? sensor.correctedX + : '无数据', + sensor.correctedY !== null && sensor.correctedY !== undefined + ? sensor.correctedY + : '无数据' ) } else { // 如果没有对应测点数据,填入空值 @@ -386,8 +431,8 @@ function SystemSettings() { sensor.pos || '', sensor.tar || '', sensor.arg || '', - sensor.xReal !== null && sensor.xReal !== undefined ? sensor.xReal : '无数据', - sensor.yReal !== null && sensor.yReal !== undefined ? sensor.yReal : '无数据', + sensor.correctedX !== null && sensor.correctedX !== undefined ? sensor.correctedX : '无数据', + sensor.correctedY !== null && sensor.correctedY !== undefined ? sensor.correctedY : '无数据', alarmType, threshold, exceedValue @@ -407,42 +452,42 @@ function SystemSettings() { sensors.forEach((sensor) => { if (!sensor) return - const { xReal, yReal } = sensor + const { correctedX, correctedY } = sensor // 检查X轴是否超出阈值 - if (xReal !== null && xReal !== undefined) { - if (xReal > limits.xUpper) { + if (correctedX !== null && correctedX !== undefined) { + if (correctedX > limits.xUpper) { alarmSensors.push({ sensor, alarmType: 'X轴上限超出', threshold: `±${limits.xUpper}`, - exceedValue: (xReal - limits.xUpper).toFixed(2) + exceedValue: (correctedX - limits.xUpper).toFixed(2) }) - } else if (xReal < limits.xLower) { + } else if (correctedX < limits.xLower) { alarmSensors.push({ sensor, alarmType: 'X轴下限超出', threshold: `±${Math.abs(limits.xLower)}`, - exceedValue: (limits.xLower - xReal).toFixed(2) + exceedValue: (limits.xLower - correctedX).toFixed(2) }) } } // 检查Y轴是否超出阈值 - if (yReal !== null && yReal !== undefined) { - if (yReal > limits.yUpper) { + if (correctedY !== null && correctedY !== undefined) { + if (correctedY > limits.yUpper) { alarmSensors.push({ sensor, alarmType: 'Y轴上限超出', threshold: `±${limits.yUpper}`, - exceedValue: (yReal - limits.yUpper).toFixed(2) + exceedValue: (correctedY - limits.yUpper).toFixed(2) }) - } else if (yReal < limits.yLower) { + } else if (correctedY < limits.yLower) { alarmSensors.push({ sensor, alarmType: 'Y轴下限超出', threshold: `±${Math.abs(limits.yLower)}`, - exceedValue: (limits.yLower - yReal).toFixed(2) + exceedValue: (limits.yLower - correctedY).toFixed(2) }) } } @@ -632,6 +677,21 @@ function SystemSettings() { } } + const handleCorrectionFactorChange = (field, value) => { + if (value === null || value === undefined || !Number.isFinite(Number(value))) { + return + } + + const nextFactors = { + ...correctionFactors, + [field]: Number(value) + } + + setCorrectionFactors(nextFactors) + localStorage.setItem(DEFLECTION_CORRECTION_STORAGE_KEY, JSON.stringify(nextFactors)) + message.success(`修正系数 ${field} 已更新`) + } + return (
@@ -820,6 +880,43 @@ function SystemSettings() { />
+ +
+
数据修正系数
+ + handleCorrectionFactorChange('xK', value)} + /> + + handleCorrectionFactorChange('xB', value)} + /> + + handleCorrectionFactorChange('yK', value)} + /> + + handleCorrectionFactorChange('yB', value)} + /> + +
{/* 存储设置 */} diff --git a/src/renderer/src/components/SystemSettings/SystemSettings.module.css b/src/renderer/src/components/SystemSettings/SystemSettings.module.css index 7f18d00..f5e6ec8 100644 --- a/src/renderer/src/components/SystemSettings/SystemSettings.module.css +++ b/src/renderer/src/components/SystemSettings/SystemSettings.module.css @@ -116,6 +116,10 @@ margin-bottom: 6px; } +.correctionInputs { + margin-bottom: 6px; +} + .storageSection { border: 1px solid #eee; border-radius: 6px; diff --git a/src/renderer/src/stores/deviceStore.js b/src/renderer/src/stores/deviceStore.js index 43af77a..8501636 100644 --- a/src/renderer/src/stores/deviceStore.js +++ b/src/renderer/src/stores/deviceStore.js @@ -26,6 +26,14 @@ const useDeviceStore = create((set) => ({ yLower: -15 }, + // 数据修正系数 + correctionFactors: { + xK: 1, + xB: 0, + yK: 1, + yB: 0 + }, + // 设置连接的设备 setConnectedDevice: (device) => set({ connectedDevice: device }), @@ -49,6 +57,15 @@ const useDeviceStore = create((set) => ({ setAlarmEnabled: (enabled) => set({ alarmEnabled: enabled }), setAlarmLimits: (limits) => set({ alarmLimits: limits }), + // 数据修正系数管理 + setCorrectionFactors: (factors) => + set((state) => ({ + correctionFactors: { + ...state.correctionFactors, + ...factors + } + })), + // 获取当前连接的IP getConnectedIp: () => { const state = useDeviceStore.getState() diff --git a/src/renderer/src/utils/deflectionCorrection.js b/src/renderer/src/utils/deflectionCorrection.js new file mode 100644 index 0000000..8b5ef19 --- /dev/null +++ b/src/renderer/src/utils/deflectionCorrection.js @@ -0,0 +1,60 @@ +export const DEFLECTION_CORRECTION_STORAGE_KEY = 'deflectionCorrectionFactors' + +export const DEFAULT_CORRECTION_FACTORS = { + xK: 1, + xB: 0, + yK: 1, + yB: 0 +} + +const toFiniteNumber = (value) => { + const num = Number(value) + return Number.isFinite(num) ? num : null +} + +export const sanitizeCorrectionFactors = (value) => { + const next = value && typeof value === 'object' ? value : {} + + return { + xK: toFiniteNumber(next.xK) ?? DEFAULT_CORRECTION_FACTORS.xK, + xB: toFiniteNumber(next.xB) ?? DEFAULT_CORRECTION_FACTORS.xB, + yK: toFiniteNumber(next.yK) ?? DEFAULT_CORRECTION_FACTORS.yK, + yB: toFiniteNumber(next.yB) ?? DEFAULT_CORRECTION_FACTORS.yB + } +} + +export const correctAxisValue = (originalValue, k, b) => { + if (originalValue === null || originalValue === undefined || originalValue === '') { + return null + } + + const numericOriginal = Number(originalValue) + if (!Number.isFinite(numericOriginal)) { + return null + } + + return k * numericOriginal + b +} + +export const applyCorrectionToSensor = (sensor, correctionFactors) => { + if (!sensor || typeof sensor !== 'object') { + return sensor + } + + const normalizedFactors = sanitizeCorrectionFactors(correctionFactors) + + return { + ...sensor, + appliedCorrectionFactors: normalizedFactors, + correctedX: correctAxisValue(sensor.xReal, normalizedFactors.xK, normalizedFactors.xB), + correctedY: correctAxisValue(sensor.yReal, normalizedFactors.yK, normalizedFactors.yB) + } +} + +export const applyCorrectionToSensors = (sensors, correctionFactors) => { + if (!Array.isArray(sensors)) { + return [] + } + + return sensors.map((sensor) => applyCorrectionToSensor(sensor, correctionFactors)) +}