QAUP_Management/.kiro/specs/traffic-light-ip-address-enhancement/design.md

12 KiB
Raw Blame History

设计文档

概述

红绿灯IP地址增强功能旨在改进现有的红绿灯信号处理系统使其能够正确解析包含IP地址和端口信息的红绿灯消息格式并相应地调整数据库结构以支持更灵活的设备管理。

根据实际的红绿灯消息格式:('36.113.38.178', 56930) - {"DI-01":0,"DI-02":0,"DI-11":1,...}系统需要修改信号解析器以提取网络地址信息并更新数据库表结构使设备ID字段变为可选。

架构

整体架构图

graph TB
    A[红绿灯硬件] -->|TCP消息<br/>格式: ('IP', port) - {DI数据}| B[TrafficLightTcpServer]
    B --> C[TrafficLightDataCollector]
    C --> D[DataProcessingService]
    D --> E[TrafficLightSignalParser - 增强版]
    E --> F[TrafficLightStatus - 包含IP/端口]
    F --> G[TrafficLightService - 支持IP查找]
    G --> H[数据库 - 更新表结构]
    F --> I[WebSocketMessageBroadcaster]
    I -->|WebSocket| J[前端客户端]
    
    subgraph "修改的组件"
        E
        F
        G
        H
    end
    
    subgraph "现有组件(无需修改)"
        B
        C
        D
        I
    end

数据流程

  1. 消息接收: TCP服务器接收格式为('IP', port) - {DI数据}的红绿灯消息
  2. 消息解析: 增强的信号解析器提取IP地址、端口号和DI信号数据
  3. 设备识别: 系统使用IP地址和端口信息识别或创建设备记录
  4. 数据处理: 处理DI信号并更新设备状态
  5. 状态广播: 通过WebSocket广播红绿灯状态更新

组件和接口

1. TrafficLightSignalParser (增强版)

职责: 解析包含IP地址和端口信息的红绿灯消息格式

接口设计:

@Component
public class TrafficLightSignalParser {
    // 解析包含IP和端口信息的原始消息
    public TrafficLightStatus parseSignalWithAddress(String rawMessage);
    
    // 提取IP地址信息
    private String extractIpAddress(String rawMessage);
    
    // 提取端口信息
    private Integer extractPort(String rawMessage);
    
    // 提取DI信号数据
    private String extractDiData(String rawMessage);
    
    // 验证消息格式
    public boolean isValidMessageFormat(String rawMessage);
}

消息格式解析逻辑:

// 输入格式: ('36.113.38.178', 56930) - {"DI-01":0,"DI-02":0,"DI-11":1,...}
// 解析步骤:
// 1. 使用正则表达式匹配 ('IP', port) 部分
// 2. 提取IP地址字符串
// 3. 提取端口号整数
// 4. 提取 - 后面的JSON数据部分
// 5. 解析DI信号数据

2. TrafficLightStatus (增强版)

职责: 包含IP地址和端口信息的红绿灯状态数据模型

数据模型:

public class TrafficLightStatus {
    private String ipAddress;         // 设备IP地址
    private Integer port;             // 设备端口号
    private String deviceId;          // 设备ID可选
    private String intersectionId;    // 路口ID
    private SignalState nsStatus;     // 南北方向状态
    private SignalState ewStatus;     // 东西方向状态
    private long timestamp;           // 信号时间戳
    private String rawSignal;         // 原始信号数据
    
    // 生成设备唯一标识符当deviceId为空时使用
    public String generateDeviceIdentifier() {
        return ipAddress + ":" + port;
    }
}

3. TrafficLight实体类 (修改版)

职责: 支持IP地址和端口信息存储的设备实体

实体设计:

@Entity
@Table(name = "traffic_lights")
public class TrafficLight {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;                              // 主键ID
    
    @Column(name = "device_id")                   // 设备ID改为可选
    private String deviceId;
    
    @Column(name = "ip_address", nullable = false) // 新增IP地址字段
    private String ipAddress;
    
    @Column(name = "port")                        // 新增:端口字段
    private Integer port;
    
    @Column(name = "device_name", nullable = false)
    private String deviceName;
    
    @Column(name = "intersection_id", nullable = false)
    private String intersectionId;
    
    @Column(name = "device_type")
    private String deviceType = "STANDARD";
    
    @Column(name = "is_online")
    private Boolean isOnline = false;
    
    @Column(name = "last_heartbeat")
    private LocalDateTime lastHeartbeat;
    
    @Column(name = "is_active")
    private Boolean isActive = true;
    
    @Column(name = "created_time")
    private LocalDateTime createdTime;
    
    @Column(name = "updated_time")
    private LocalDateTime updatedTime;
    
    // 添加唯一约束IP地址和端口组合必须唯一
    // 在数据库层面通过复合唯一索引实现
}

4. TrafficLightRepository (增强版)

职责: 支持基于IP地址和端口查询的数据访问层

接口设计:

@Repository
public interface TrafficLightRepository extends JpaRepository<TrafficLight, Long> {
    // 现有方法...
    
    // 根据IP地址和端口查找设备
    Optional<TrafficLight> findByIpAddressAndPort(String ipAddress, Integer port);
    
    // 根据IP地址查找设备列表
    List<TrafficLight> findByIpAddress(String ipAddress);
    
    // 根据设备ID查找保持兼容性
    Optional<TrafficLight> findByDeviceId(String deviceId);
    
    // 检查IP地址和端口组合是否已存在
    boolean existsByIpAddressAndPort(String ipAddress, Integer port);
}

5. TrafficLightService (增强版)

职责: 支持基于IP地址的设备管理服务

接口设计:

@Service
public class TrafficLightService {
    // 现有方法...
    
    // 根据IP地址和端口获取或创建设备
    public TrafficLight getOrCreateDeviceByAddress(String ipAddress, Integer port, String intersectionId);
    
    // 根据IP地址和端口查找设备
    public Optional<TrafficLight> findDeviceByAddress(String ipAddress, Integer port);
    
    // 更新设备心跳基于IP地址和端口
    public void updateDeviceHeartbeatByAddress(String ipAddress, Integer port);
    
    // 生成默认设备名称
    private String generateDefaultDeviceName(String ipAddress, Integer port) {
        return "TrafficLight_" + ipAddress.replace(".", "_") + "_" + port;
    }
}

6. DataProcessingService (修改版)

职责: 处理包含IP地址信息的红绿灯信号

接口修改:

@Service
public class DataProcessingService {
    // 现有方法...
    
    // 修改处理包含IP地址信息的红绿灯信号
    public void processTrafficLightSignal(String rawMessage) {
        try {
            // 使用增强的解析器解析消息
            TrafficLightStatus status = trafficLightSignalParser.parseSignalWithAddress(rawMessage);
            
            // 根据IP地址和端口获取或创建设备
            TrafficLight device = trafficLightService.getOrCreateDeviceByAddress(
                status.getIpAddress(), 
                status.getPort(), 
                status.getIntersectionId()
            );
            
            // 更新设备心跳
            trafficLightService.updateDeviceHeartbeatByAddress(
                status.getIpAddress(), 
                status.getPort()
            );
            
            // 创建WebSocket消息载荷
            TrafficLightStatusPayload payload = createTrafficLightPayload(status, device);
            
            // 发布状态变更事件
            publishTrafficLightStatusEvent(payload);
            
        } catch (Exception e) {
            log.error("处理红绿灯信号失败: {}", rawMessage, e);
        }
    }
}

数据模型

原始消息格式

输入: ('36.113.38.178', 56930) - {"DI-01":0,"DI-02":0,"DI-11":1,"DI-12":0,"DI-13":0,"DI-14":0,"DI-15":0,"DI-16":1,"DI-17":0,"DI-18":0}

解析结果:
- IP地址: "36.113.38.178"
- 端口: 56930
- DI数据: {"DI-01":0,"DI-02":0,"DI-11":1,"DI-12":0,"DI-13":0,"DI-14":0,"DI-15":0,"DI-16":1,"DI-17":0,"DI-18":0}

数据库表结构变更

修改traffic_lights表

-- 添加新字段
ALTER TABLE traffic_lights 
ADD COLUMN ip_address VARCHAR(45) NOT NULL DEFAULT '0.0.0.0',
ADD COLUMN port INTEGER;

-- 修改device_id字段为可选
ALTER TABLE traffic_lights 
ALTER COLUMN device_id DROP NOT NULL;

-- 添加唯一约束IP地址和端口组合必须唯一
CREATE UNIQUE INDEX idx_traffic_light_ip_port 
ON traffic_lights(ip_address, port);

-- 添加IP地址索引
CREATE INDEX idx_traffic_light_ip 
ON traffic_lights(ip_address);

WebSocket消息格式 (保持不变)

{
  "type": "intersection_traffic_light_status",
  "timestamp": 1704067200000000,
  "payload": {
    "intersection_id": "INTERSECTION_001",
    "device_id": "36.113.38.178:56930",  // 当设备ID为空时使用IP:端口
    "ip_address": "36.113.38.178",       // 新增字段
    "port": 56930,                       // 新增字段
    "position": {
      "latitude": 39.9042,
      "longitude": 116.4074
    },
    "ns_status": "red",
    "ew_status": "green",
    "timestamp": 1704067200000000
  }
}

错误处理

1. 消息格式解析错误

  • 格式不匹配: 当消息不符合('IP', port) - {JSON}格式时,记录错误并跳过
  • IP地址无效: 验证IP地址格式无效时使用默认值并记录警告
  • 端口号无效: 验证端口号范围,无效时使用默认值
  • JSON解析失败: 记录原始数据,跳过当前消息

2. 数据库操作错误

  • IP端口重复: 当IP地址和端口组合已存在时更新现有记录而不是创建新记录
  • 约束违反: 处理数据库约束违反,提供清晰的错误信息
  • 连接失败: 数据库连接失败时,缓存数据并重试

3. 设备管理错误

  • 设备创建失败: 记录错误详情,使用临时标识符继续处理
  • 心跳更新失败: 记录警告,不影响信号处理流程

测试策略

1. 单元测试

  • 消息解析测试: 测试各种消息格式的解析结果
  • IP地址提取测试: 验证IP地址和端口的正确提取
  • 设备查找测试: 测试基于IP地址和端口的设备查找功能

2. 集成测试

  • 数据库迁移测试: 验证表结构变更的正确性
  • 端到端测试: 从消息接收到WebSocket广播的完整流程测试
  • 错误处理测试: 测试各种异常情况的处理

3. 兼容性测试

  • 现有数据兼容性: 确保现有设备记录在升级后仍能正常工作
  • API兼容性: 验证现有API调用不受影响

配置管理

应用配置文件 (application.yml)

traffic:
  light:
    parsing:
      # 消息格式配置
      message-format-regex: "\\('([^']+)',\\s*(\\d+)\\)\\s*-\\s*(\\{.*\\})"
      default-ip: "0.0.0.0"
      default-port: 8082
      
    device:
      # 设备管理配置
      auto-create-device: true
      default-intersection-id: "DEFAULT_INTERSECTION"
      device-name-prefix: "TrafficLight_"

数据库迁移脚本

-- V1.1__add_ip_port_to_traffic_lights.sql
-- 添加IP地址和端口字段
ALTER TABLE traffic_lights 
ADD COLUMN ip_address VARCHAR(45) NOT NULL DEFAULT '0.0.0.0',
ADD COLUMN port INTEGER;

-- 修改device_id为可选
ALTER TABLE traffic_lights 
ALTER COLUMN device_id DROP NOT NULL;

-- 为现有记录设置默认IP地址
UPDATE traffic_lights 
SET ip_address = '0.0.0.0', port = 8082 
WHERE ip_address IS NULL;

-- 添加唯一约束和索引
CREATE UNIQUE INDEX idx_traffic_light_ip_port 
ON traffic_lights(ip_address, port);

CREATE INDEX idx_traffic_light_ip 
ON traffic_lights(ip_address);