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