Browse Source

feat: 上位机数据修正功能

master
liujiangyong 2 weeks ago
parent
commit
fadb36b284
  1. 64
      CHANGELOGS/V1.2.2.md
  2. 2
      package.json
  3. 29
      src/renderer/src/components/DeflectionCollection/DeflectionCollection.jsx
  4. 141
      src/renderer/src/components/SystemSettings/SystemSettings.jsx
  5. 4
      src/renderer/src/components/SystemSettings/SystemSettings.module.css
  6. 17
      src/renderer/src/stores/deviceStore.js
  7. 60
      src/renderer/src/utils/deflectionCorrection.js

64
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 提交记录**

2
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",

29
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 (
<div
@ -404,7 +413,10 @@ function DeflectionCollection() {
marginLeft: '5px'
}}
>
X {Number(sensor.xReal).toFixed(3)}
X {' '}
{sensor.correctedX !== null && sensor.correctedX !== undefined
? Number(sensor.correctedX).toFixed(3)
: '无数据'}
</span>
</div>
@ -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)
: '无数据'}
</span>
</div>
</div>

141
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 (
<Flex vertical gap={4} className={styles.container}>
<div className={styles.header}>
@ -820,6 +880,43 @@ function SystemSettings() {
/>
</Flex>
</div>
<div className={styles.subSection}>
<div className={styles.subSectionTitle}>数据修正系数</div>
<Flex gap="middle" className={styles.correctionInputs} vertical>
<InputNumber
precision={6}
addonBefore={'X轴 K'}
className={styles.inputNumber}
value={correctionFactors.xK}
onChange={(value) => handleCorrectionFactorChange('xK', value)}
/>
<InputNumber
precision={6}
addonBefore={'X轴 B'}
className={styles.inputNumber}
value={correctionFactors.xB}
onChange={(value) => handleCorrectionFactorChange('xB', value)}
/>
<InputNumber
precision={6}
addonBefore={'Y轴 K'}
className={styles.inputNumber}
value={correctionFactors.yK}
onChange={(value) => handleCorrectionFactorChange('yK', value)}
/>
<InputNumber
precision={6}
addonBefore={'Y轴 B'}
className={styles.inputNumberLast}
value={correctionFactors.yB}
onChange={(value) => handleCorrectionFactorChange('yB', value)}
/>
</Flex>
</div>
</div>
{/* 存储设置 */}

4
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;

17
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()

60
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))
}
Loading…
Cancel
Save