12 KiB
12 KiB
设计文档
概述
红绿灯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
数据流程
- 消息接收: TCP服务器接收格式为
('IP', port) - {DI数据}的红绿灯消息 - 消息解析: 增强的信号解析器提取IP地址、端口号和DI信号数据
- 设备识别: 系统使用IP地址和端口信息识别或创建设备记录
- 数据处理: 处理DI信号并更新设备状态
- 状态广播: 通过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);