diff --git a/CHANGELOGS/V1.2.4.md b/CHANGELOGS/V1.2.4.md
new file mode 100644
index 0000000..23045de
--- /dev/null
+++ b/CHANGELOGS/V1.2.4.md
@@ -0,0 +1,61 @@
+# V1.2.4 更新日志
+
+- **更新日期**: 2026年06月17日
+- **版本号**: 1.2.4
+
+## 新增功能
+
+### 1. 数据修正系数支持正负值分组配置
+- **新增正负值双组修正系数**:每个距离配置下,分别维护“正值配置”和“负值配置”两套 `xK/xB/yK/yB`
+- **按原始值符号自动应用**:当原始采集值大于等于 `0` 时使用正值配置,小于 `0` 时使用负值配置
+- **X/Y 独立判断**:`xReal` 和 `yReal` 分别按各自原始值符号选择对应修正系数,不共享判断结果
+- **保持历史冻结语义**:修正系数变更后仅影响后续新采集数据,已进入历史链路的数据保持采集当时的修正结果
+
+### 2. 修正系数配置界面升级
+- **卡片内新增正负值配置区**:每张距离配置卡片内新增“正值配置”和“负值配置”两个分组
+- **支持 8 项参数编辑**:每个距离可分别编辑正值组和负值组的 `xK/xB/yK/yB`
+- **保留多距离管理能力**:继续支持距离配置的新增、编辑、删除和当前应用项切换
+- **保留控制台调试输出**:修正计算时继续输出原始值、命中的正负组、K、B 和修正后值,便于排查数据口径
+
+## 技术细节
+
+### 本地存储结构
+```json
+{
+ "profiles": [
+ {
+ "distance": 10,
+ "positive": { "xK": 1, "xB": 0, "yK": 1, "yB": 0 },
+ "negative": { "xK": 1, "xB": 0, "yK": 1, "yB": 0 }
+ }
+ ],
+ "activeDistance": 10
+}
+```
+
+### 数据处理策略
+- `0` 归入正值配置
+- 正负值判断基于传感器原始值,而不是修正后的结果
+- 空值、缺失值或不可转数字的原始值不参与修正计算,保持“无数据”语义
+- 页面曲线、实时数据显示、报警判断、实时 CSV、报警 CSV 继续共享冻结后的 `correctedX/correctedY`
+
+## 影响范围
+- 系统设置中的数据修正系数配置区域
+- 多距离修正系数本地持久化结构
+- 挠度采集页 X/Y 曲线与右侧实时数据
+- 报警判断逻辑
+- 实时数据 CSV 导出
+- 报警数据 CSV 导出
+
+## 依赖更新
+- 无依赖包更新
+
+## 注意事项
+1. 本版本不兼容 V1.2.3 的单组修正系数本地结构,读取到旧结构或异常结构时会重建为新的默认 `10 米` 正负值双组配置。
+2. 距离仍仅作为上位机修正系数配置的管理维度,不会根据设备状态或测点距离自动切换。
+3. 当前应用项切换后,仅影响后续新采集数据;历史数据仍保持采集当时的修正结果。
+4. 设备侧“测点设置”中的“计算系数”仍为独立参数,与本地正负值数据修正系数不互相替代。
+
+---
+
+**完整更新内容请查看项目 Git 提交记录**
diff --git a/package.json b/package.json
index 31a314b..5398aee 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "FlexometerSetup",
- "version": "1.2.3",
+ "version": "1.2.4",
"description": "An Electron application with React",
"main": "./out/main/index.js",
"author": "cles",
diff --git a/src/renderer/src/components/SystemSettings/SystemSettings.jsx b/src/renderer/src/components/SystemSettings/SystemSettings.jsx
index 9c2546c..63229f7 100644
--- a/src/renderer/src/components/SystemSettings/SystemSettings.jsx
+++ b/src/renderer/src/components/SystemSettings/SystemSettings.jsx
@@ -798,8 +798,21 @@ function SystemSettings() {
return
}
+ const [groupKey, factorKey] = field.split('.')
+ if (!groupKey || !factorKey) {
+ return
+ }
+
const nextProfiles = correctionProfiles.map((profile) =>
- profile.distance === distance ? { ...profile, [field]: Number(value) } : profile
+ profile.distance === distance
+ ? {
+ ...profile,
+ [groupKey]: {
+ ...profile[groupKey],
+ [factorKey]: Number(value)
+ }
+ }
+ : profile
)
const nextConfig = persistCorrectionConfig(nextProfiles, activeCorrectionDistance)
@@ -1198,49 +1211,105 @@ function SystemSettings() {
-
- xK
-
- handleCorrectionProfileChange(profile.distance, 'xK', nextValue)
- }
- className={styles.correctionFieldInput}
- />
-
-
- xB
-
- handleCorrectionProfileChange(profile.distance, 'xB', nextValue)
- }
- className={styles.correctionFieldInput}
- />
-
-
-
yK
-
- handleCorrectionProfileChange(profile.distance, 'yK', nextValue)
- }
- className={styles.correctionFieldInput}
- />
+
+
正值配置
+
原始值大于等于 0 时使用
+
+
+ xK
+
+ handleCorrectionProfileChange(profile.distance, 'positive.xK', nextValue)
+ }
+ className={styles.correctionFieldInput}
+ />
+
+
+ xB
+
+ handleCorrectionProfileChange(profile.distance, 'positive.xB', nextValue)
+ }
+ className={styles.correctionFieldInput}
+ />
+
+
+ yK
+
+ handleCorrectionProfileChange(profile.distance, 'positive.yK', nextValue)
+ }
+ className={styles.correctionFieldInput}
+ />
+
+
+ yB
+
+ handleCorrectionProfileChange(profile.distance, 'positive.yB', nextValue)
+ }
+ className={styles.correctionFieldInput}
+ />
+
+
-
-
yB
-
- handleCorrectionProfileChange(profile.distance, 'yB', nextValue)
- }
- className={styles.correctionFieldInput}
- />
+
+
负值配置
+
原始值小于 0 时使用
+
+
+ xK
+
+ handleCorrectionProfileChange(profile.distance, 'negative.xK', nextValue)
+ }
+ className={styles.correctionFieldInput}
+ />
+
+
+ xB
+
+ handleCorrectionProfileChange(profile.distance, 'negative.xB', nextValue)
+ }
+ className={styles.correctionFieldInput}
+ />
+
+
+ yK
+
+ handleCorrectionProfileChange(profile.distance, 'negative.yK', nextValue)
+ }
+ className={styles.correctionFieldInput}
+ />
+
+
+ yB
+
+ handleCorrectionProfileChange(profile.distance, 'negative.yB', nextValue)
+ }
+ className={styles.correctionFieldInput}
+ />
+
+
diff --git a/src/renderer/src/components/SystemSettings/SystemSettings.module.css b/src/renderer/src/components/SystemSettings/SystemSettings.module.css
index aeea9c8..b0ead8c 100644
--- a/src/renderer/src/components/SystemSettings/SystemSettings.module.css
+++ b/src/renderer/src/components/SystemSettings/SystemSettings.module.css
@@ -198,6 +198,32 @@
}
.correctionFactorGrid {
+ display: grid;
+ grid-template-columns: 1fr;
+ gap: 10px;
+}
+
+.correctionFactorGroup {
+ border: 1px solid #e5e7eb;
+ border-radius: 6px;
+ padding: 10px;
+ background: #fff;
+}
+
+.correctionFactorGroupTitle {
+ font-size: 13px;
+ font-weight: 600;
+ color: #222;
+}
+
+.correctionFactorGroupHint {
+ margin-top: 2px;
+ margin-bottom: 8px;
+ font-size: 12px;
+ color: #666;
+}
+
+.correctionFactorSubGrid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 10px 8px;
diff --git a/src/renderer/src/stores/deviceStore.js b/src/renderer/src/stores/deviceStore.js
index 5124983..a20dcdb 100644
--- a/src/renderer/src/stores/deviceStore.js
+++ b/src/renderer/src/stores/deviceStore.js
@@ -1,6 +1,8 @@
import {
createDefaultCorrectionConfig,
+ createDefaultDualCorrectionFactors,
getActiveCorrectionFactors,
+ sanitizeDualCorrectionFactors,
sanitizeCorrectionProfiles
} from '../utils/deflectionCorrection'
@@ -64,12 +66,11 @@ const useDeviceStore = create((set) => ({
// 数据修正系数管理
setCorrectionFactors: (factors) =>
- set((state) => ({
- correctionFactors: {
- ...state.correctionFactors,
- ...factors
- }
- })),
+ set({
+ correctionFactors: factors
+ ? sanitizeDualCorrectionFactors(factors) ?? createDefaultDualCorrectionFactors()
+ : createDefaultDualCorrectionFactors()
+ }),
setCorrectionProfiles: (profiles) =>
set({
correctionProfiles: sanitizeCorrectionProfiles(profiles)
@@ -89,10 +90,8 @@ const useDeviceStore = create((set) => ({
return {
activeCorrectionDistance: distance,
correctionFactors: {
- xK: activeProfile.xK,
- xB: activeProfile.xB,
- yK: activeProfile.yK,
- yB: activeProfile.yB
+ positive: { ...activeProfile.positive },
+ negative: { ...activeProfile.negative }
}
}
}),
diff --git a/src/renderer/src/utils/deflectionCorrection.js b/src/renderer/src/utils/deflectionCorrection.js
index 4b0bcd3..b8d0da6 100644
--- a/src/renderer/src/utils/deflectionCorrection.js
+++ b/src/renderer/src/utils/deflectionCorrection.js
@@ -9,9 +9,18 @@ export const DEFAULT_CORRECTION_FACTORS = {
export const DEFAULT_CORRECTION_DISTANCE = 10
+export const createDefaultFactorGroup = () => ({
+ ...DEFAULT_CORRECTION_FACTORS
+})
+
+export const createDefaultDualCorrectionFactors = () => ({
+ positive: createDefaultFactorGroup(),
+ negative: createDefaultFactorGroup()
+})
+
export const createDefaultCorrectionProfile = (distance = DEFAULT_CORRECTION_DISTANCE) => ({
distance,
- ...DEFAULT_CORRECTION_FACTORS
+ ...createDefaultDualCorrectionFactors()
})
export const createDefaultCorrectionConfig = () => ({
@@ -44,6 +53,21 @@ export const sanitizeCorrectionFactors = (value) => {
}
}
+export const sanitizeDualCorrectionFactors = (value) => {
+ const next = value && typeof value === 'object' ? value : {}
+ const hasPositiveGroup = next.positive && typeof next.positive === 'object'
+ const hasNegativeGroup = next.negative && typeof next.negative === 'object'
+
+ if (!hasPositiveGroup || !hasNegativeGroup) {
+ return null
+ }
+
+ return {
+ positive: sanitizeCorrectionFactors(next.positive),
+ negative: sanitizeCorrectionFactors(next.negative)
+ }
+}
+
export const sanitizeCorrectionProfile = (value) => {
const next = value && typeof value === 'object' ? value : {}
const distance = toPositiveInteger(next.distance)
@@ -52,9 +76,14 @@ export const sanitizeCorrectionProfile = (value) => {
return null
}
+ const normalizedGroups = sanitizeDualCorrectionFactors(next)
+ if (!normalizedGroups) {
+ return null
+ }
+
return {
distance,
- ...sanitizeCorrectionFactors(next)
+ ...normalizedGroups
}
}
@@ -110,24 +139,8 @@ export const getActiveCorrectionFactors = (config) => {
)
return activeProfile
- ? sanitizeCorrectionFactors(activeProfile)
- : { ...DEFAULT_CORRECTION_FACTORS }
-}
-
-export const ensureDefaultDistanceProfile = (profiles) => {
- const normalizedProfiles = sanitizeCorrectionProfiles(profiles)
- const hasDefaultDistance = normalizedProfiles.some(
- (profile) => profile.distance === DEFAULT_CORRECTION_DISTANCE
- )
-
- if (hasDefaultDistance) {
- return normalizedProfiles
- }
-
- return [
- ...normalizedProfiles,
- createDefaultCorrectionProfile(DEFAULT_CORRECTION_DISTANCE)
- ]
+ ? sanitizeDualCorrectionFactors(activeProfile) ?? createDefaultDualCorrectionFactors()
+ : createDefaultDualCorrectionFactors()
}
export const removeCorrectionProfileWithFallback = (profiles, activeDistance, distanceToDelete) => {
@@ -162,7 +175,7 @@ export const removeCorrectionProfileWithFallback = (profiles, activeDistance, di
}
}
-export const correctAxisValue = (originalValue, k, b) => {
+const getFactorGroupNameByValue = (originalValue) => {
if (originalValue === null || originalValue === undefined || originalValue === '') {
return null
}
@@ -172,7 +185,72 @@ export const correctAxisValue = (originalValue, k, b) => {
return null
}
- return k * numericOriginal + b
+ return numericOriginal < 0 ? 'negative' : 'positive'
+}
+
+export const correctAxisValue = (originalValue, factors) => {
+ if (originalValue === null || originalValue === undefined || originalValue === '') {
+ return { correctedValue: null, appliedGroup: null, appliedFactors: null }
+ }
+
+ const numericOriginal = Number(originalValue)
+ if (!Number.isFinite(numericOriginal)) {
+ return { correctedValue: null, appliedGroup: null, appliedFactors: null }
+ }
+
+ const normalizedGroups = sanitizeDualCorrectionFactors(factors)
+ if (!normalizedGroups) {
+ return { correctedValue: null, appliedGroup: null, appliedFactors: null }
+ }
+ const appliedGroup = getFactorGroupNameByValue(numericOriginal)
+ const appliedFactors = appliedGroup ? normalizedGroups[appliedGroup] : null
+
+ if (!appliedFactors) {
+ return { correctedValue: null, appliedGroup: null, appliedFactors: null }
+ }
+
+ return {
+ correctedValue: appliedFactors.xK * numericOriginal + appliedFactors.xB,
+ appliedGroup,
+ appliedFactors: {
+ xK: appliedFactors.xK,
+ xB: appliedFactors.xB
+ }
+ }
+}
+
+const correctAxisWithKeys = (originalValue, factors, axis) => {
+ if (originalValue === null || originalValue === undefined || originalValue === '') {
+ return { correctedValue: null, appliedGroup: null, appliedFactors: null }
+ }
+
+ const numericOriginal = Number(originalValue)
+ if (!Number.isFinite(numericOriginal)) {
+ return { correctedValue: null, appliedGroup: null, appliedFactors: null }
+ }
+
+ const normalizedGroups = sanitizeDualCorrectionFactors(factors)
+ if (!normalizedGroups) {
+ return { correctedValue: null, appliedGroup: null, appliedFactors: null }
+ }
+ const appliedGroup = getFactorGroupNameByValue(numericOriginal)
+ const appliedFactors = appliedGroup ? normalizedGroups[appliedGroup] : null
+
+ if (!appliedFactors) {
+ return { correctedValue: null, appliedGroup: null, appliedFactors: null }
+ }
+
+ const kKey = `${axis}K`
+ const bKey = `${axis}B`
+
+ return {
+ correctedValue: appliedFactors[kKey] * numericOriginal + appliedFactors[bKey],
+ appliedGroup,
+ appliedFactors: {
+ [kKey]: appliedFactors[kKey],
+ [bKey]: appliedFactors[bKey]
+ }
+ }
}
export const applyCorrectionToSensor = (sensor, correctionFactors) => {
@@ -180,32 +258,44 @@ export const applyCorrectionToSensor = (sensor, correctionFactors) => {
return sensor
}
- const normalizedFactors = sanitizeCorrectionFactors(correctionFactors)
- const correctedX = correctAxisValue(sensor.xReal, normalizedFactors.xK, normalizedFactors.xB)
- const correctedY = correctAxisValue(sensor.yReal, normalizedFactors.yK, normalizedFactors.yB)
+ const normalizedFactors =
+ sanitizeDualCorrectionFactors(correctionFactors) ?? createDefaultDualCorrectionFactors()
+ const xResult = correctAxisWithKeys(sensor.xReal, normalizedFactors, 'x')
+ const yResult = correctAxisWithKeys(sensor.yReal, normalizedFactors, 'y')
console.log('[DeflectionCorrection]', {
pos: sensor.pos ?? null,
des: sensor.des ?? '',
x: {
originalValue: sensor.xReal ?? null,
- k: normalizedFactors.xK,
- b: normalizedFactors.xB,
- correctedValue: correctedX
+ appliedGroup: xResult.appliedGroup,
+ k: xResult.appliedFactors?.xK ?? null,
+ b: xResult.appliedFactors?.xB ?? null,
+ correctedValue: xResult.correctedValue
},
y: {
originalValue: sensor.yReal ?? null,
- k: normalizedFactors.yK,
- b: normalizedFactors.yB,
- correctedValue: correctedY
+ appliedGroup: yResult.appliedGroup,
+ k: yResult.appliedFactors?.yK ?? null,
+ b: yResult.appliedFactors?.yB ?? null,
+ correctedValue: yResult.correctedValue
}
})
return {
...sensor,
- appliedCorrectionFactors: normalizedFactors,
- correctedX,
- correctedY
+ appliedCorrectionFactors: {
+ x: {
+ group: xResult.appliedGroup,
+ factors: xResult.appliedFactors
+ },
+ y: {
+ group: yResult.appliedGroup,
+ factors: yResult.appliedFactors
+ }
+ },
+ correctedX: xResult.correctedValue,
+ correctedY: yResult.correctedValue
}
}