feat(processor): 添加循环名称自动生成的配置开关
新增环境变量 ENABLE_LOOP_NAME_AUTO_GENERATION 用于控制当缓存未命中时是否自动生成循环名称。当设置为 false 时,系统将不再生成 [类型-地址-回路] 格式的备用名称,而是直接返回 null。更新了配置文件、处理器逻辑并添加了相应的单元测试。
This commit is contained in:
@@ -34,3 +34,5 @@ REDIS_DB=15
|
|||||||
REDIS_CONNECT_TIMEOUT_MS=5000
|
REDIS_CONNECT_TIMEOUT_MS=5000
|
||||||
|
|
||||||
ACTION_TYPE_DEV_TYPE_RULES='[{"dev_type":0,"name":"无效设备(也可以被认为是场景)","action_type":"无效"},{"dev_type":1,"name":"强电继电器(输出状态)","action_type":"设备回路状态"},{"dev_type":2,"name":"弱电输入(输入状态)","action_type":"用户操作"},{"dev_type":3,"name":"弱电输出(输出状态)","action_type":"设备回路状态"},{"dev_type":4,"name":"服务信息","action_type":"设备回路状态"},{"dev_type":5,"name":"干节点窗帘","action_type":"设备回路状态"},{"dev_type":6,"name":"开关","action_type":"用户操作"},{"dev_type":7,"name":"空调","action_type":"用户操作"},{"dev_type":8,"name":"红外感应","action_type":"用户操作"},{"dev_type":9,"name":"空气质量检测设备","action_type":"设备回路状态"},{"dev_type":10,"name":"插卡取电","action_type":"用户操作"},{"dev_type":11,"name":"地暖","action_type":"用户操作"},{"dev_type":12,"name":"RCU 设备网络 - 没使用","action_type":""},{"dev_type":13,"name":"窗帘","action_type":"设备回路状态"},{"dev_type":14,"name":"继电器","action_type":"设备回路状态"},{"dev_type":15,"name":"红外发送","action_type":"设备回路状态"},{"dev_type":16,"name":"调光驱动","action_type":"设备回路状态"},{"dev_type":17,"name":"可控硅调光(可控硅状态)","action_type":"设备回路状态"},{"dev_type":18,"name":"灯带(灯带状态) --2025-11-24 取消","action_type":"无效"},{"dev_type":19,"name":"中控","action_type":"无效"},{"dev_type":20,"name":"微信锁 (福 瑞狗的蓝牙锁 默认 0 地址)","action_type":"无效"},{"dev_type":21,"name":"背景音乐(背景音乐状态)","action_type":"设备回路状态"},{"dev_type":22,"name":"房态下发","action_type":"无效"},{"dev_type":23,"name":"主机本地 调光","action_type":"无效"},{"dev_type":24,"name":"485PWM 调光( PWM 调光状态)","action_type":"无效"},{"dev_type":25,"name":"总线调光( PBLED 调光状态) - 没使用 -","action_type":"无效"},{"dev_type":26,"name":"RCU 电源","action_type":"无效"},{"dev_type":27,"name":"A9IO 开关","action_type":"用户操作"},{"dev_type":28,"name":"A9IO 扩展","action_type":"设备回路状态"},{"dev_type":29,"name":"A9IO 电源","action_type":"设备回路状态"},{"dev_type":30,"name":"无线网关轮询(用于轮询控制轮询设备;给无线网关下发配置和询问网关状态)","action_type":"无效"},{"dev_type":31,"name":"无线网关主动(用于主动控制主动设备)","action_type":"无效"},{"dev_type":32,"name":"无线门磁","action_type":"用户操作"},{"dev_type":33,"name":"空气参数显示设备","action_type":"设备回路状态"},{"dev_type":34,"name":"无线继电器红外","action_type":"设备回路状态"},{"dev_type":35,"name":"时间同步","action_type":"设备回路状态"},{"dev_type":36,"name":"监控控制","action_type":"无效"},{"dev_type":37,"name":"旋钮开关控制","action_type":"用户操作"},{"dev_type":38,"name":"CSIO - 类型","action_type":"设备回路状态"},{"dev_type":39,"name":"插卡状态虚拟设备","action_type":"设备回路状态"},{"dev_type":40,"name":"485 新风设备","action_type":"用户操作"},{"dev_type":41,"name":"485 人脸机","action_type":"用户操作"},{"dev_type":42,"name":"中控","action_type":"无效"},{"dev_type":43,"name":"域控","action_type":"无效"},{"dev_type":44,"name":"LCD","action_type":"设备回路状态"},{"dev_type":45,"name":"无卡断电 --2025-11-24 取消","action_type":"无效"},{"dev_type":46,"name":"无卡取电 2","action_type":"用户操作"},{"dev_type":47,"name":"虚拟时间设备","action_type":"设备回路状态"},{"dev_type":48,"name":"PLC 总控","action_type":"设备回路状态"},{"dev_type":49,"name":"PLC 设备 - 恒流调光设备","action_type":"设备回路状态"},{"dev_type":50,"name":"PLC 设备 - 恒压调光设备","action_type":"设备回路状态"},{"dev_type":51,"name":"PLC 设备 - 继电器设备","action_type":"设备回路状态"},{"dev_type":52,"name":"色温调节功能","action_type":"设备回路状态"},{"dev_type":53,"name":"蓝牙音频","action_type":"设备回路状态"},{"dev_type":54,"name":"碳达人","action_type":"用户操作"},{"dev_type":55,"name":"场景还原","action_type":"用户操作"},{"dev_type":56,"name":"全局设置","action_type":"设备回路状态"},{"dev_type":57,"name":"能耗检测","action_type":"设备回路状态"},{"dev_type":241,"name":"CSIO - 类型","action_type":"设备回路状态"}]'
|
ACTION_TYPE_DEV_TYPE_RULES='[{"dev_type":0,"name":"无效设备(也可以被认为是场景)","action_type":"无效"},{"dev_type":1,"name":"强电继电器(输出状态)","action_type":"设备回路状态"},{"dev_type":2,"name":"弱电输入(输入状态)","action_type":"用户操作"},{"dev_type":3,"name":"弱电输出(输出状态)","action_type":"设备回路状态"},{"dev_type":4,"name":"服务信息","action_type":"设备回路状态"},{"dev_type":5,"name":"干节点窗帘","action_type":"设备回路状态"},{"dev_type":6,"name":"开关","action_type":"用户操作"},{"dev_type":7,"name":"空调","action_type":"用户操作"},{"dev_type":8,"name":"红外感应","action_type":"用户操作"},{"dev_type":9,"name":"空气质量检测设备","action_type":"设备回路状态"},{"dev_type":10,"name":"插卡取电","action_type":"用户操作"},{"dev_type":11,"name":"地暖","action_type":"用户操作"},{"dev_type":12,"name":"RCU 设备网络 - 没使用","action_type":""},{"dev_type":13,"name":"窗帘","action_type":"设备回路状态"},{"dev_type":14,"name":"继电器","action_type":"设备回路状态"},{"dev_type":15,"name":"红外发送","action_type":"设备回路状态"},{"dev_type":16,"name":"调光驱动","action_type":"设备回路状态"},{"dev_type":17,"name":"可控硅调光(可控硅状态)","action_type":"设备回路状态"},{"dev_type":18,"name":"灯带(灯带状态) --2025-11-24 取消","action_type":"无效"},{"dev_type":19,"name":"中控","action_type":"无效"},{"dev_type":20,"name":"微信锁 (福 瑞狗的蓝牙锁 默认 0 地址)","action_type":"无效"},{"dev_type":21,"name":"背景音乐(背景音乐状态)","action_type":"设备回路状态"},{"dev_type":22,"name":"房态下发","action_type":"无效"},{"dev_type":23,"name":"主机本地 调光","action_type":"无效"},{"dev_type":24,"name":"485PWM 调光( PWM 调光状态)","action_type":"无效"},{"dev_type":25,"name":"总线调光( PBLED 调光状态) - 没使用 -","action_type":"无效"},{"dev_type":26,"name":"RCU 电源","action_type":"无效"},{"dev_type":27,"name":"A9IO 开关","action_type":"用户操作"},{"dev_type":28,"name":"A9IO 扩展","action_type":"设备回路状态"},{"dev_type":29,"name":"A9IO 电源","action_type":"设备回路状态"},{"dev_type":30,"name":"无线网关轮询(用于轮询控制轮询设备;给无线网关下发配置和询问网关状态)","action_type":"无效"},{"dev_type":31,"name":"无线网关主动(用于主动控制主动设备)","action_type":"无效"},{"dev_type":32,"name":"无线门磁","action_type":"用户操作"},{"dev_type":33,"name":"空气参数显示设备","action_type":"设备回路状态"},{"dev_type":34,"name":"无线继电器红外","action_type":"设备回路状态"},{"dev_type":35,"name":"时间同步","action_type":"设备回路状态"},{"dev_type":36,"name":"监控控制","action_type":"无效"},{"dev_type":37,"name":"旋钮开关控制","action_type":"用户操作"},{"dev_type":38,"name":"CSIO - 类型","action_type":"设备回路状态"},{"dev_type":39,"name":"插卡状态虚拟设备","action_type":"设备回路状态"},{"dev_type":40,"name":"485 新风设备","action_type":"用户操作"},{"dev_type":41,"name":"485 人脸机","action_type":"用户操作"},{"dev_type":42,"name":"中控","action_type":"无效"},{"dev_type":43,"name":"域控","action_type":"无效"},{"dev_type":44,"name":"LCD","action_type":"设备回路状态"},{"dev_type":45,"name":"无卡断电 --2025-11-24 取消","action_type":"无效"},{"dev_type":46,"name":"无卡取电 2","action_type":"用户操作"},{"dev_type":47,"name":"虚拟时间设备","action_type":"设备回路状态"},{"dev_type":48,"name":"PLC 总控","action_type":"设备回路状态"},{"dev_type":49,"name":"PLC 设备 - 恒流调光设备","action_type":"设备回路状态"},{"dev_type":50,"name":"PLC 设备 - 恒压调光设备","action_type":"设备回路状态"},{"dev_type":51,"name":"PLC 设备 - 继电器设备","action_type":"设备回路状态"},{"dev_type":52,"name":"色温调节功能","action_type":"设备回路状态"},{"dev_type":53,"name":"蓝牙音频","action_type":"设备回路状态"},{"dev_type":54,"name":"碳达人","action_type":"用户操作"},{"dev_type":55,"name":"场景还原","action_type":"用户操作"},{"dev_type":56,"name":"全局设置","action_type":"设备回路状态"},{"dev_type":57,"name":"能耗检测","action_type":"设备回路状态"},{"dev_type":241,"name":"CSIO - 类型","action_type":"设备回路状态"}]'
|
||||||
|
|
||||||
|
ENABLE_LOOP_NAME_AUTO_GENERATION=false
|
||||||
|
|||||||
@@ -27,3 +27,5 @@ REDIS_PASSWORD=
|
|||||||
REDIS_DB=0
|
REDIS_DB=0
|
||||||
REDIS_PROJECT_NAME=my-project
|
REDIS_PROJECT_NAME=my-project
|
||||||
REDIS_API_BASE_URL=http://localhost:3000
|
REDIS_API_BASE_URL=http://localhost:3000
|
||||||
|
|
||||||
|
ENABLE_LOOP_NAME_AUTO_GENERATION=true
|
||||||
|
|||||||
@@ -52,5 +52,6 @@ export const config = {
|
|||||||
db: parseNumber(process.env.REDIS_DB, 0),
|
db: parseNumber(process.env.REDIS_DB, 0),
|
||||||
projectName: process.env.REDIS_PROJECT_NAME || 'bls-rcu-action',
|
projectName: process.env.REDIS_PROJECT_NAME || 'bls-rcu-action',
|
||||||
apiBaseUrl: process.env.REDIS_API_BASE_URL || `http://localhost:${parseNumber(process.env.PORT, 3000)}`
|
apiBaseUrl: process.env.REDIS_API_BASE_URL || `http://localhost:${parseNumber(process.env.PORT, 3000)}`
|
||||||
}
|
},
|
||||||
|
enableLoopNameAutoGeneration: process.env.ENABLE_LOOP_NAME_AUTO_GENERATION === 'true'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { createGuid } from '../utils/uuid.js';
|
import { createGuid } from '../utils/uuid.js';
|
||||||
import { kafkaPayloadSchema } from '../schema/kafkaPayload.js';
|
import { kafkaPayloadSchema } from '../schema/kafkaPayload.js';
|
||||||
import projectMetadata from '../cache/projectMetadata.js';
|
import projectMetadata from '../cache/projectMetadata.js';
|
||||||
|
import { config } from '../config/config.js';
|
||||||
|
|
||||||
const normalizeDirection = (value) => {
|
const normalizeDirection = (value) => {
|
||||||
if (!value) return null;
|
if (!value) return null;
|
||||||
@@ -241,7 +242,12 @@ export const buildRowsFromPayload = (rawPayload) => {
|
|||||||
const cachedName = projectMetadata.getLoopName(deviceId, devType, devAddr, devLoop);
|
const cachedName = projectMetadata.getLoopName(deviceId, devType, devAddr, devLoop);
|
||||||
if (cachedName) return cachedName;
|
if (cachedName) return cachedName;
|
||||||
|
|
||||||
// 2. Fallback: [TypeName-Addr-Loop]
|
// 2. Check config for auto-generation
|
||||||
|
if (!config.enableLoopNameAutoGeneration) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Fallback: [TypeName-Addr-Loop]
|
||||||
const typeName = getDevTypeName(devType);
|
const typeName = getDevTypeName(devType);
|
||||||
if (!typeName) return null; // Should have a name if devType is valid
|
if (!typeName) return null; // Should have a name if devType is valid
|
||||||
|
|
||||||
|
|||||||
47
bls-rcu-action-backend/tests/feature_flag_loop_name.test.js
Normal file
47
bls-rcu-action-backend/tests/feature_flag_loop_name.test.js
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||||
|
import { buildRowsFromPayload } from '../src/processor/index.js';
|
||||||
|
import { config } from '../src/config/config.js';
|
||||||
|
|
||||||
|
describe('Feature Toggle: Loop Name Auto Generation', () => {
|
||||||
|
const basePayload = {
|
||||||
|
ts_ms: 1700000000000,
|
||||||
|
hotel_id: 1001,
|
||||||
|
room_id: '8001',
|
||||||
|
device_id: 'dev_001',
|
||||||
|
direction: '上报',
|
||||||
|
cmd_word: '0x36',
|
||||||
|
frame_id: 1,
|
||||||
|
udp_raw: '00',
|
||||||
|
sys_lock_status: 0,
|
||||||
|
report_count: 0,
|
||||||
|
fault_count: 0,
|
||||||
|
device_list: [
|
||||||
|
{ dev_type: 1, dev_addr: 10, dev_loop: 1, dev_data: 100 }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
let originalConfigValue;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
originalConfigValue = config.enableLoopNameAutoGeneration;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
config.enableLoopNameAutoGeneration = originalConfigValue;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate loop_name when flag is true', () => {
|
||||||
|
config.enableLoopNameAutoGeneration = true;
|
||||||
|
const rows = buildRowsFromPayload(basePayload);
|
||||||
|
// Expect format: [1强电继电器(输出状态)-10-1]
|
||||||
|
// The exact name depends on the map, but it should contain brackets and numbers
|
||||||
|
expect(rows[0].loop_name).toBeDefined();
|
||||||
|
expect(rows[0].loop_name).toMatch(/^\[1.*-10-1\]$/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should NOT generate loop_name when flag is false', () => {
|
||||||
|
config.enableLoopNameAutoGeneration = false;
|
||||||
|
const rows = buildRowsFromPayload(basePayload);
|
||||||
|
expect(rows[0].loop_name).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -223,7 +223,7 @@ describe('Processor Logic', () => {
|
|||||||
|
|
||||||
const rows = buildRowsFromPayload(payload);
|
const rows = buildRowsFromPayload(payload);
|
||||||
expect(rows[0].loop_name).toBe('Main Chandelier');
|
expect(rows[0].loop_name).toBe('Main Chandelier');
|
||||||
// dev_type 1 is 'Dev_Host_HVout'
|
// dev_type 1 is '强电继电器(输出状态)'
|
||||||
expect(rows[1].loop_name).toBe('[1Dev_Host_HVout-10-2]');
|
expect(rows[1].loop_name).toBe('[1强电继电器(输出状态)-10-2]');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Feature: Loop Name Enrichment
|
# Feature: Loop Name Enrichment
|
||||||
|
|
||||||
**Status**: Proposed
|
**Status**: Implemented
|
||||||
**Date**: 2026-02-02
|
**Date**: 2026-02-02
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# Feature: Feature Toggle Loop Name Generation
|
||||||
|
|
||||||
|
**Status**: Implemented
|
||||||
|
**Date**: 2026-02-03
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
Add a configuration switch to control the fallback behavior for `loop_name` generation. Currently, when cache lookup fails, the system auto-generates a name using the format `[Type-Addr-Loop]`. This change allows users to enable or disable this fallback behavior via an environment variable.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
1. **Configuration**:
|
||||||
|
- Add `ENABLE_LOOP_NAME_AUTO_GENERATION` to environment variables.
|
||||||
|
- Default behavior should match existing logic (enable generation) if not specified, but user requested explicit control.
|
||||||
|
- If `true`: Perform concatenation `[dev_type名称+'-'+dev_addr+'-'+dev_loop]`.
|
||||||
|
- If `false`: Do not generate name, leave `loop_name` as null (or whatever default is appropriate, likely null).
|
||||||
|
|
||||||
|
2. **Processor Logic**:
|
||||||
|
- In `getLoopNameWithFallback`, check the configuration flag before applying the fallback generation logic.
|
||||||
|
|
||||||
|
## Implementation Plan
|
||||||
|
1. **Config**: Update `src/config/config.js` to parse `ENABLE_LOOP_NAME_AUTO_GENERATION`.
|
||||||
|
2. **Env**: Update `.env` and `.env.example`.
|
||||||
|
3. **Processor**: Update `src/processor/index.js` to respect the flag.
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# Summary of Changes: Loop Name Features
|
||||||
|
|
||||||
|
**Date**: 2026-02-03
|
||||||
|
**Status**: Archived
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
This archive contains the specifications and proposals for the Loop Name Enrichment and Auto-Generation features. These features enhance the RCU Action Server by enriching event data with descriptive loop names derived from project metadata or fallback generation logic.
|
||||||
|
|
||||||
|
## Included Changes
|
||||||
|
|
||||||
|
### 1. [Feature: Loop Name Enrichment](./feature-loop-name-enrichment.md)
|
||||||
|
- **Goal**: Enrich `rcu_action_events` with `loop_name` by looking up cached metadata from `temporary_project` tables.
|
||||||
|
- **Key Components**:
|
||||||
|
- `ProjectMetadataCache`: Loads rooms and loops data daily.
|
||||||
|
- `loop_name` column added to `rcu_action_events` table.
|
||||||
|
- Processor logic updated to perform lookup.
|
||||||
|
|
||||||
|
### 2. [Feature: Feature Toggle Loop Name Generation](./feature-toggle-loop-name-generation.md)
|
||||||
|
- **Goal**: Provide a configuration switch to control the fallback behavior when cache lookup fails.
|
||||||
|
- **Key Components**:
|
||||||
|
- `ENABLE_LOOP_NAME_AUTO_GENERATION` env var.
|
||||||
|
- Logic to generate `[Type-Addr-Loop]` format only if enabled.
|
||||||
|
- Default behavior is `true` (enabled).
|
||||||
|
|
||||||
|
## Implementation Status
|
||||||
|
- All proposed features have been implemented and verified via unit tests.
|
||||||
|
- Configuration variables are documented in `.env.example`.
|
||||||
Reference in New Issue
Block a user