修改了红绿灯消息解析,使用原始格式

This commit is contained in:
Tian jianyong 2025-08-06 16:46:52 +08:00
parent 51ac84eb4f
commit a3d8daa9d1
5 changed files with 313 additions and 290 deletions

View File

@ -1,20 +1,33 @@
- 红绿灯状态上报消息格式:
## 红绿灯数据格式说明
```
('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}{
"rw_prot": {
"dir": "up",
"Ver": "",
"err": "1"
}
### 真实的红绿灯设备上报格式
红绿灯设备通过TCP连接发送的原始数据格式为纯JSON
```json
{"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}
```
- 其中DI-11~13 表示南北向灯的状态DI14~16 表示东西向灯
### 数据字段说明
DI-11":北红,"DI-12":北黄,"DI-13":北绿,"DI-14":东红,"DI-15":东黄,"DI-16":东绿
- **DI-11~13**: 南北向灯的状态
- `DI-11`: 北红灯 (1=亮, 0=灭)
- `DI-12`: 北黄灯 (1=亮, 0=灭)
- `DI-13`: 北绿灯 (1=亮, 0=灭)
- 只有包含 DI 的这一段有用,其他的都是 0
- **DI-14~16**: 东西向灯的状态
- `DI-14`: 东红灯 (1=亮, 0=灭)
- `DI-15`: 东黄灯 (1=亮, 0=灭)
- `DI-16`: 东绿灯 (1=亮, 0=灭)
- 红绿灯状态上报频率:每 1 秒上报一次
- **其他DI字段**: 通常为0可忽略
- 红绿灯状态上报端口8082
### 网络连接信息
- **IP地址和端口**: 从TCP socket连接中获取不包含在JSON数据中
- **上报频率**: 每1秒上报一次
- **监听端口**: 8082
### 注意事项
之前文档中的格式 `('36.113.38.178', 56930) - {...}` 实际上是服务器日志输出格式不是设备的原始数据格式。真实的设备只发送JSON数据IP地址和端口信息需要从socket连接中提取。

View File

@ -12,8 +12,14 @@ import java.util.concurrent.atomic.AtomicLong;
/**
* 红绿灯信号解析器
*
* 负责解析DI格式的红绿灯信号JSON数据转换为内部的TrafficLightStatus对象
* 支持多种异常情况的处理确保系统稳定性
* 负责解析红绿灯设备发送的纯JSON格式DI信号数据结合从socket连接中获取的IP地址和端口信息
* 转换为内部的TrafficLightStatus对象支持多种异常情况的处理确保系统稳定性
*
* 数据格式说明
* - 红绿灯设备发送纯JSON: {"DI-01":0,"DI-11":1,"DI-12":0,...}
* - IP地址和端口从TCP socket连接中获取不包含在JSON数据中
* - DI-11~13: 南北向灯状态 (//绿)
* - DI-14~16: 东西向灯状态 (//绿)
*/
@Slf4j
@Component
@ -32,37 +38,44 @@ public class TrafficLightSignalParser {
}
/**
* 解析包含IP地址和端口信息的原始信号为TrafficLightStatus对象
* 支持格式: ('IP地址', 端口) - {"DI-01":0,"DI-02":0,"DI-11":1,...}
* 解析红绿灯JSON信号并结合连接信息创建TrafficLightStatus对象
*
* @param rawMessage 原始信号字符串
* @param jsonSignal 纯JSON信号字符串: {"DI-01":0,"DI-11":1,"DI-12":0,...}
* @param ipAddress 从socket连接中获取的IP地址
* @param port 从socket连接中获取的端口号
* @return 解析后的红绿灯状态解析失败时返回安全默认状态
*/
public TrafficLightStatus parseSignalWithAddress(String rawMessage) {
public TrafficLightStatus parseSignalWithAddress(String jsonSignal, String ipAddress, Integer port) {
totalParsed.incrementAndGet();
if (!isValidMessageFormat(rawMessage)) {
log.warn("🚦 无效的红绿灯消息格式: {}", rawMessage);
if (!isValidSignal(jsonSignal)) {
log.warn("🚦 无效的红绿灯JSON信号格式: {}", jsonSignal);
failedParsed.incrementAndGet();
return TrafficLightStatus.createSafeDefault(generateDeviceIdentifier(ipAddress, port));
}
if (ipAddress == null || ipAddress.trim().isEmpty()) {
log.warn("🚦 IP地址不能为空");
failedParsed.incrementAndGet();
return TrafficLightStatus.createSafeDefault("UNKNOWN_DEVICE");
}
// 验证IP地址格式
if (!isValidIpAddressFormat(ipAddress.trim())) {
log.warn("🚦 IP地址格式无效: {}", ipAddress);
failedParsed.incrementAndGet();
return TrafficLightStatus.createSafeDefault(generateDeviceIdentifier(ipAddress, port));
}
if (port == null || port <= 0 || port > 65535) {
log.warn("🚦 端口号无效: {}", port);
failedParsed.incrementAndGet();
return TrafficLightStatus.createSafeDefault(generateDeviceIdentifier(ipAddress, port));
}
try {
// 提取IP地址和端口信息
String ipAddress = extractIpAddress(rawMessage);
Integer port = extractPort(rawMessage);
// 提取DI信号数据
String diData = extractDiData(rawMessage);
if (ipAddress == null || port == null || diData == null) {
log.warn("🚦 无法提取完整的地址信息或DI数据: {}", rawMessage);
failedParsed.incrementAndGet();
return TrafficLightStatus.createSafeDefault("UNKNOWN_DEVICE");
}
// 解析DI信号数据
JsonNode rootNode = objectMapper.readTree(diData);
// 解析JSON数据
JsonNode rootNode = objectMapper.readTree(jsonSignal);
// 解析南北方向信号状态 (DI-11: 北红, DI-12: 北黄, DI-13: 北绿)
SignalState nsStatus = parseDirectionSignal(rootNode, "DI-11", "DI-12", "DI-13");
@ -71,17 +84,17 @@ public class TrafficLightSignalParser {
SignalState ewStatus = parseDirectionSignal(rootNode, "DI-14", "DI-15", "DI-16");
// 生成设备标识符使用IP:端口组合
String deviceIdentifier = ipAddress + ":" + port;
String deviceIdentifier = generateDeviceIdentifier(ipAddress, port);
// 创建状态对象
TrafficLightStatus status = TrafficLightStatus.builder()
.deviceId(null) // 设备ID可能为空使用IP:端口作为标识
.ipAddress(ipAddress)
.ipAddress(ipAddress.trim())
.port(port)
.nsStatus(nsStatus)
.ewStatus(ewStatus)
.timestamp(System.currentTimeMillis() * 1000) // 微秒级时间戳
.rawSignal(rawMessage)
.rawSignal(jsonSignal)
.build();
// 验证解析结果
@ -107,9 +120,10 @@ public class TrafficLightSignalParser {
return status;
} catch (Exception e) {
log.error("🚦 解析红绿灯信号异常: {}", rawMessage, e);
log.error("🚦 解析红绿灯信号异常: jsonSignal={}, ipAddress={}, port={}",
jsonSignal, ipAddress, port, e);
failedParsed.incrementAndGet();
return TrafficLightStatus.createSafeDefault("UNKNOWN_DEVICE");
return TrafficLightStatus.createSafeDefault(generateDeviceIdentifier(ipAddress, port));
}
}
@ -183,122 +197,49 @@ public class TrafficLightSignalParser {
}
/**
* 验证新消息格式是否有效
* 格式: ('IP地址', 端口) - {"DI-01":0,"DI-02":0,"DI-11":1,...}
* 验证IP地址格式是否有效
*
* @param rawMessage 原始消息字符串
* @param ipAddress IP地址字符串
* @return true如果格式有效false否则
*/
public boolean isValidMessageFormat(String rawMessage) {
if (rawMessage == null || rawMessage.trim().isEmpty()) {
private boolean isValidIpAddressFormat(String ipAddress) {
if (ipAddress == null || ipAddress.trim().isEmpty()) {
return false;
}
try {
// 检查是否包含IP地址和端口的格式
if (!rawMessage.contains("(") || !rawMessage.contains(")") || !rawMessage.contains("-")) {
String[] parts = ipAddress.split("\\.");
if (parts.length != 4) {
return false;
}
// 尝试提取各部分
String ipAddress = extractIpAddress(rawMessage);
Integer port = extractPort(rawMessage);
String diData = extractDiData(rawMessage);
if (ipAddress == null || port == null || diData == null) {
return false;
for (String part : parts) {
int num = Integer.parseInt(part);
if (num < 0 || num > 255) {
return false;
}
}
// 验证DI数据是否为有效JSON
JsonNode rootNode = objectMapper.readTree(diData);
if (!rootNode.isObject()) {
return false;
}
// 检查是否包含必要的DI字段
boolean hasRequiredFields = rootNode.has("DI-11") || rootNode.has("DI-12") || rootNode.has("DI-13") ||
rootNode.has("DI-14") || rootNode.has("DI-15") || rootNode.has("DI-16");
return hasRequiredFields;
} catch (Exception e) {
log.debug("消息格式验证失败: {}", rawMessage, e);
return true;
} catch (NumberFormatException e) {
return false;
}
}
/**
* 从原始消息中提取IP地址
* 生成设备标识符
*
* @param rawMessage 原始消息字符串
* @return IP地址提取失败时返回null
* @param ipAddress IP地址
* @param port 端口号
* @return 设备标识符格式为 "IP:端口"
*/
private String extractIpAddress(String rawMessage) {
try {
// 使用正则表达式匹配 ('IP地址', 端口) 格式
java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("\\('([^']+)',\\s*(\\d+)\\)");
java.util.regex.Matcher matcher = pattern.matcher(rawMessage);
if (matcher.find()) {
return matcher.group(1);
}
return null;
} catch (Exception e) {
log.debug("提取IP地址失败: {}", rawMessage, e);
return null;
private String generateDeviceIdentifier(String ipAddress, Integer port) {
if (ipAddress == null || ipAddress.trim().isEmpty()) {
return "UNKNOWN_DEVICE";
}
}
/**
* 从原始消息中提取端口号
*
* @param rawMessage 原始消息字符串
* @return 端口号提取失败时返回null
*/
private Integer extractPort(String rawMessage) {
try {
// 使用正则表达式匹配 ('IP地址', 端口) 格式
java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("\\('([^']+)',\\s*(\\d+)\\)");
java.util.regex.Matcher matcher = pattern.matcher(rawMessage);
if (matcher.find()) {
return Integer.parseInt(matcher.group(2));
}
return null;
} catch (Exception e) {
log.debug("提取端口号失败: {}", rawMessage, e);
return null;
}
}
/**
* 从原始消息中提取DI信号数据
*
* @param rawMessage 原始消息字符串
* @return DI信号JSON字符串提取失败时返回null
*/
private String extractDiData(String rawMessage) {
try {
// 查找 " - " 分隔符后的JSON数据
int separatorIndex = rawMessage.indexOf(" - ");
if (separatorIndex == -1) {
return null;
}
String jsonPart = rawMessage.substring(separatorIndex + 3).trim();
// 验证是否为有效的JSON格式
if (jsonPart.startsWith("{") && jsonPart.endsWith("}")) {
return jsonPart;
}
return null;
} catch (Exception e) {
log.debug("提取DI数据失败: {}", rawMessage, e);
return null;
if (port == null) {
return ipAddress.trim();
}
return ipAddress.trim() + ":" + port;
}
/**

View File

@ -483,6 +483,82 @@ public class DataProcessingService {
@Autowired
private IntersectionService intersectionService;
/**
* 处理红绿灯信号数据新格式分离的JSON数据和网络连接信息
*
* @param jsonSignal 纯JSON信号数据
* @param ipAddress 从socket连接中获取的IP地址
* @param port 从socket连接中获取的端口号
*/
public void processTrafficLightSignalWithAddress(String jsonSignal, String ipAddress, Integer port) {
if (jsonSignal == null || jsonSignal.trim().isEmpty()) {
log.warn("🚦 接收到空的红绿灯JSON信号");
return;
}
if (ipAddress == null || ipAddress.trim().isEmpty()) {
log.warn("🚦 IP地址不能为空");
return;
}
if (port == null || port <= 0 || port > 65535) {
log.warn("🚦 端口号无效: {}", port);
return;
}
try {
// 使用新的解析器处理分离的JSON数据和网络信息
com.qaup.collision.dataprocessing.model.TrafficLightStatus status =
trafficLightSignalParser.parseSignalWithAddress(jsonSignal, ipAddress, port);
log.debug("🚦 使用分离格式解析器处理信号: ipAddress={}, port={}, json={}",
ipAddress, port, jsonSignal.substring(0, Math.min(100, jsonSignal.length())));
if (!status.isValid()) {
log.warn("🚦 解析的红绿灯状态无效: {}", status);
return;
}
// 获取或创建设备
com.qaup.collision.common.model.spatial.TrafficLight device = getOrCreateDevice(status);
if (device == null) {
log.warn("🚦 无法获取或创建红绿灯设备");
return;
}
// 更新设备心跳和在线状态
updateDeviceStatusByAddress(status, device);
// 设置路口ID
status.setIntersectionId(device.getIntersectionId());
// 获取路口信息
Optional<com.qaup.collision.common.model.spatial.Intersection> intersectionOpt =
intersectionService.getActiveIntersectionById(device.getIntersectionId());
if (intersectionOpt.isEmpty()) {
log.warn("🚦 无法获取路口信息: intersectionId={}", device.getIntersectionId());
return;
}
com.qaup.collision.common.model.spatial.Intersection intersection = intersectionOpt.get();
// 创建WebSocket消息载荷
com.qaup.collision.websocket.message.TrafficLightStatusPayload payload =
createTrafficLightPayload(status, device, intersection);
// 发布红绿灯状态变更事件
publishTrafficLightStatusEvent(payload);
log.debug("🚦 成功处理红绿灯信号: deviceIdentifier={}, intersectionId={}, 状态={}",
status.generateDeviceIdentifier(), status.getIntersectionId(), status.getStatusDescription());
} catch (Exception e) {
log.error("🚦 处理红绿灯信号异常: ipAddress={}, port={}, json={}",
ipAddress, port, jsonSignal.substring(0, Math.min(100, jsonSignal.length())), e);
}
}
/**
* 处理红绿灯信号数据支持包含IP地址和端口信息的新格式
*
@ -497,13 +573,9 @@ public class DataProcessingService {
try {
com.qaup.collision.dataprocessing.model.TrafficLightStatus status;
// 检查消息格式优先使用新的IP地址格式解析器
if (trafficLightSignalParser.isValidMessageFormat(rawMessage)) {
// 使用新的解析器处理包含IP地址和端口信息的消息
status = trafficLightSignalParser.parseSignalWithAddress(rawMessage);
log.debug("🚦 使用IP地址格式解析器处理信号: {}", rawMessage.substring(0, Math.min(100, rawMessage.length())));
} else if (trafficLightSignalParser.isValidSignal(rawMessage)) {
// 使用旧的解析器处理传统JSON格式消息向后兼容
// 只支持传统JSON格式向后兼容
if (trafficLightSignalParser.isValidSignal(rawMessage)) {
// 使用传统JSON格式解析器处理信号向后兼容
status = trafficLightSignalParser.parseSignal(rawMessage);
log.debug("🚦 使用传统JSON格式解析器处理信号");
} else {

View File

@ -9,7 +9,8 @@ import static org.junit.jupiter.api.Assertions.*;
/**
* TrafficLightSignalParser增强功能测试类
*
* 测试新增的IP地址和端口解析功能
* 测试基于纯JSON格式和socket连接信息的解析功能
* 新的数据格式红绿灯设备发送纯JSONIP地址和端口从socket连接中获取
*/
class TrafficLightSignalParserEnhancedTest {
@ -22,10 +23,12 @@ class TrafficLightSignalParserEnhancedTest {
@Test
void testParseSignalWithAddress_ValidMessage() {
// 测试有效的消息格式
String rawMessage = "('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}";
// 测试有效的纯JSON格式和连接信息
String jsonSignal = "{\"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}";
String ipAddress = "36.113.38.178";
Integer port = 56930;
TrafficLightStatus status = parser.parseSignalWithAddress(rawMessage);
TrafficLightStatus status = parser.parseSignalWithAddress(jsonSignal, ipAddress, port);
assertNotNull(status);
assertTrue(status.isValid());
@ -33,15 +36,16 @@ class TrafficLightSignalParserEnhancedTest {
assertEquals(Integer.valueOf(56930), status.getPort());
assertEquals(SignalState.RED, status.getNsStatus()); // DI-11=1 表示北红
assertEquals(SignalState.GREEN, status.getEwStatus()); // DI-16=1 表示东绿
assertEquals("36.113.38.178:56930", status.generateDeviceIdentifier());
}
@Test
void testParseSignalWithAddress_DifferentStates() {
// 测试不同的信号状态组合
String rawMessage = "('192.168.1.100', 8082) - {\"DI-11\":0,\"DI-12\":1,\"DI-13\":0,\"DI-14\":1,\"DI-15\":0,\"DI-16\":0}";
String jsonSignal = "{\"DI-11\":0,\"DI-12\":1,\"DI-13\":0,\"DI-14\":1,\"DI-15\":0,\"DI-16\":0}";
String ipAddress = "192.168.1.100";
Integer port = 8082;
TrafficLightStatus status = parser.parseSignalWithAddress(rawMessage);
TrafficLightStatus status = parser.parseSignalWithAddress(jsonSignal, ipAddress, port);
assertNotNull(status);
assertTrue(status.isValid());
@ -54,30 +58,35 @@ class TrafficLightSignalParserEnhancedTest {
@Test
void testParseSignalWithAddress_AllGreenConflict() {
// 测试冲突状态两个方向都是绿灯
String rawMessage = "('10.0.0.1', 9090) - {\"DI-11\":0,\"DI-12\":0,\"DI-13\":1,\"DI-14\":0,\"DI-15\":0,\"DI-16\":1}";
String jsonSignal = "{\"DI-11\":0,\"DI-12\":0,\"DI-13\":1,\"DI-14\":0,\"DI-15\":0,\"DI-16\":1}";
String ipAddress = "10.0.0.1";
Integer port = 9090;
TrafficLightStatus status = parser.parseSignalWithAddress(rawMessage);
TrafficLightStatus status = parser.parseSignalWithAddress(jsonSignal, ipAddress, port);
assertNotNull(status);
// 冲突时应该返回安全状态红灯
assertEquals(SignalState.RED, status.getNsStatus());
assertEquals(SignalState.RED, status.getEwStatus());
assertEquals("10.0.0.1:9090", status.generateDeviceIdentifier());
assertEquals("10.0.0.1", status.getIpAddress());
assertEquals(Integer.valueOf(9090), status.getPort());
}
@Test
void testParseSignalWithAddress_InvalidFormat() {
// 测试无效的消息格式
String[] invalidMessages = {
"invalid message",
"('192.168.1.1') - {\"DI-11\":1}", // 缺少端口
"192.168.1.1, 8082 - {\"DI-11\":1}", // 格式错误
"('192.168.1.1', 8082)", // 缺少DI数据
"('192.168.1.1', 8082) - invalid_json"
// 测试无效的JSON格式和连接信息
String[] invalidJsons = {
"invalid json",
"{\"invalid\":\"data\"}",
"not json at all",
"{\"DI-11\":\"invalid_value\"}"
};
for (String invalidMessage : invalidMessages) {
TrafficLightStatus status = parser.parseSignalWithAddress(invalidMessage);
String validIp = "192.168.1.1";
Integer validPort = 8082;
for (String invalidJson : invalidJsons) {
TrafficLightStatus status = parser.parseSignalWithAddress(invalidJson, validIp, validPort);
assertNotNull(status);
// 无效格式应该返回安全默认状态
assertEquals(SignalState.RED, status.getNsStatus());
@ -86,50 +95,53 @@ class TrafficLightSignalParserEnhancedTest {
}
@Test
void testExtractIpAddress() {
// 通过反射测试私有方法或者通过公共方法间接测试
String rawMessage = "('172.16.0.1', 8083) - {\"DI-11\":1}";
void testParseSignalWithAddress_InvalidIpAddress() {
// 测试无效的IP地址
String validJson = "{\"DI-11\":1,\"DI-12\":0,\"DI-13\":0,\"DI-14\":0,\"DI-15\":0,\"DI-16\":1}";
TrafficLightStatus status = parser.parseSignalWithAddress(rawMessage);
String[] invalidIps = {null, "", " ", "invalid.ip"};
Integer validPort = 8082;
assertEquals("172.16.0.1", status.getIpAddress());
for (String invalidIp : invalidIps) {
TrafficLightStatus status = parser.parseSignalWithAddress(validJson, invalidIp, validPort);
assertNotNull(status);
assertEquals(SignalState.RED, status.getNsStatus());
assertEquals(SignalState.RED, status.getEwStatus());
}
}
@Test
void testExtractPort() {
// 测试端口提取
String rawMessage = "('192.168.1.1', 9999) - {\"DI-11\":1}";
void testParseSignalWithAddress_InvalidPort() {
// 测试无效的端口号
String validJson = "{\"DI-11\":1,\"DI-12\":0,\"DI-13\":0,\"DI-14\":0,\"DI-15\":0,\"DI-16\":1}";
String validIp = "192.168.1.1";
TrafficLightStatus status = parser.parseSignalWithAddress(rawMessage);
Integer[] invalidPorts = {null, -1, 0, 65536, 99999};
assertEquals(Integer.valueOf(9999), status.getPort());
for (Integer invalidPort : invalidPorts) {
TrafficLightStatus status = parser.parseSignalWithAddress(validJson, validIp, invalidPort);
assertNotNull(status);
assertEquals(SignalState.RED, status.getNsStatus());
assertEquals(SignalState.RED, status.getEwStatus());
}
}
@Test
void testExtractDiData() {
// 测试DI数据提取
String rawMessage = "('192.168.1.1', 8082) - {\"DI-11\":1,\"DI-12\":0,\"DI-13\":0,\"DI-14\":0,\"DI-15\":1,\"DI-16\":0}";
void testParseSignalWithAddress_ComplexDiData() {
// 测试复杂的DI数据解析
String jsonSignal = "{\"DI-11\":1,\"DI-12\":0,\"DI-13\":0,\"DI-14\":0,\"DI-15\":1,\"DI-16\":0}";
String ipAddress = "172.16.0.1";
Integer port = 8083;
TrafficLightStatus status = parser.parseSignalWithAddress(rawMessage);
TrafficLightStatus status = parser.parseSignalWithAddress(jsonSignal, ipAddress, port);
assertNotNull(status);
assertEquals("172.16.0.1", status.getIpAddress());
assertEquals(Integer.valueOf(8083), status.getPort());
assertEquals(SignalState.RED, status.getNsStatus()); // DI-11=1
assertEquals(SignalState.YELLOW, status.getEwStatus()); // DI-15=1
}
@Test
void testIsValidMessageFormat() {
// 测试消息格式验证
assertTrue(parser.isValidMessageFormat("('192.168.1.1', 8082) - {\"DI-11\":1}"));
assertTrue(parser.isValidMessageFormat("('36.113.38.178', 56930) - {\"DI-01\":0,\"DI-11\":1,\"DI-16\":1}"));
assertFalse(parser.isValidMessageFormat(null));
assertFalse(parser.isValidMessageFormat(""));
assertFalse(parser.isValidMessageFormat("invalid"));
assertFalse(parser.isValidMessageFormat("('192.168.1.1', 8082)")); // 缺少DI数据
assertFalse(parser.isValidMessageFormat("192.168.1.1 - {\"DI-11\":1}")); // 缺少括号格式
}
@Test
void testBackwardCompatibility() {
// 测试向后兼容性 - 旧的parseSignal方法仍然工作
@ -146,7 +158,7 @@ class TrafficLightSignalParserEnhancedTest {
@Test
void testTrafficLightStatusWithIpPort() {
// 测试TrafficLightStatus的功能
// 测试TrafficLightStatus的IP端口功能
TrafficLightStatus status = TrafficLightStatus.builder()
.ipAddress("192.168.1.100")
.port(8082)
@ -156,32 +168,17 @@ class TrafficLightSignalParserEnhancedTest {
.build();
assertTrue(status.isValid());
assertEquals("192.168.1.100:8082", status.generateDeviceIdentifier());
assertEquals("192.168.1.100", status.getIpAddress());
assertEquals(Integer.valueOf(8082), status.getPort());
assertNull(status.getDeviceId()); // 设备ID为空
}
@Test
void testCreateSafeDefaultWithIpPort() {
// 测试使用IP:端口格式创建安全默认状态
TrafficLightStatus status = TrafficLightStatus.createSafeDefault("10.0.0.1:8080");
assertNotNull(status);
assertEquals("10.0.0.1", status.getIpAddress());
assertEquals(Integer.valueOf(8080), status.getPort());
assertEquals(SignalState.RED, status.getNsStatus());
assertEquals(SignalState.RED, status.getEwStatus());
assertNull(status.getDeviceId());
}
@Test
void testCreateSafeDefaultWithDeviceId() {
// 测试使用设备ID创建安全默认状态
TrafficLightStatus status = TrafficLightStatus.createSafeDefault("TL_001");
assertNotNull(status);
assertEquals("TL_001", status.getDeviceId());
assertNull(status.getIpAddress());
assertNull(status.getPort());
assertEquals(SignalState.RED, status.getNsStatus());
assertEquals(SignalState.RED, status.getEwStatus());
}
@ -192,9 +189,9 @@ class TrafficLightSignalParserEnhancedTest {
parser.resetStatistics();
// 解析一些消息
parser.parseSignalWithAddress("('192.168.1.1', 8082) - {\"DI-11\":1}");
parser.parseSignalWithAddress("invalid message");
parser.parseSignalWithAddress("('192.168.1.2', 8083) - {\"DI-14\":1}");
parser.parseSignalWithAddress("{\"DI-11\":1}", "192.168.1.1", 8082);
parser.parseSignalWithAddress("invalid json", "192.168.1.1", 8082);
parser.parseSignalWithAddress("{\"DI-14\":1}", "192.168.1.2", 8083);
TrafficLightSignalParser.ParseStatistics stats = parser.getStatistics();

View File

@ -28,26 +28,26 @@ import static org.mockito.Mockito.*;
*/
@ExtendWith(MockitoExtension.class)
class DataProcessingServiceTrafficLightTest {
@Mock
private TrafficLightSignalParser trafficLightSignalParser;
@Mock
private TrafficLightService trafficLightService;
@Mock
private IntersectionService intersectionService;
@Mock
private ApplicationEventPublisher eventPublisher;
@InjectMocks
private DataProcessingService dataProcessingService;
private TrafficLight testDevice;
private Intersection testIntersection;
private TrafficLightStatus testStatus;
@BeforeEach
void setUp() {
testDevice = TrafficLight.builder()
@ -61,7 +61,7 @@ class DataProcessingServiceTrafficLightTest {
.isActive(true)
.isOnline(false)
.build();
testIntersection = Intersection.builder()
.id(1L)
.intersectionId("INTERSECTION_001")
@ -71,7 +71,7 @@ class DataProcessingServiceTrafficLightTest {
.areaCode("AREA_A")
.isActive(true)
.build();
testStatus = TrafficLightStatus.builder()
.deviceId("TL_001")
.ipAddress("192.168.1.100")
@ -82,46 +82,46 @@ class DataProcessingServiceTrafficLightTest {
.rawSignal("('192.168.1.100', 8082) - {\"DI-11\":1,\"DI-16\":1}")
.build();
}
@Test
void testProcessTrafficLightSignal_WithIpAddressFormat() {
// 测试处理包含IP地址和端口信息的新格式消息
String rawMessage = "('192.168.1.100', 8082) - {\"DI-11\":1,\"DI-12\":0,\"DI-13\":0,\"DI-14\":0,\"DI-15\":0,\"DI-16\":1}";
// 测试处理纯JSON格式和网络连接信息
String jsonSignal = "{\"DI-11\":1,\"DI-12\":0,\"DI-13\":0,\"DI-14\":0,\"DI-15\":0,\"DI-16\":1}";
String ipAddress = "192.168.1.100";
Integer port = 8082;
// Mock解析器行为
when(trafficLightSignalParser.isValidMessageFormat(rawMessage)).thenReturn(true);
when(trafficLightSignalParser.parseSignalWithAddress(rawMessage)).thenReturn(testStatus);
when(trafficLightSignalParser.parseSignalWithAddress(jsonSignal, ipAddress, port)).thenReturn(testStatus);
// Mock设备查找
when(trafficLightService.findDeviceByAddress("192.168.1.100", 8082))
.thenReturn(Optional.of(testDevice));
// Mock设备状态更新
when(trafficLightService.updateDeviceHeartbeatByAddress("192.168.1.100", 8082)).thenReturn(true);
when(trafficLightService.updateDeviceOnlineStatusByAddress("192.168.1.100", 8082, true)).thenReturn(true);
// Mock路口查找
when(intersectionService.getActiveIntersectionById("INTERSECTION_001"))
.thenReturn(Optional.of(testIntersection));
// 执行测试
dataProcessingService.processTrafficLightSignal(rawMessage);
// 执行测试 - 模拟从网络层接收到JSON数据和连接信息
dataProcessingService.processTrafficLightSignalWithAddress(jsonSignal, ipAddress, port);
// 验证调用
verify(trafficLightSignalParser).isValidMessageFormat(rawMessage);
verify(trafficLightSignalParser).parseSignalWithAddress(rawMessage);
verify(trafficLightSignalParser).parseSignalWithAddress(jsonSignal, ipAddress, port);
verify(trafficLightService).findDeviceByAddress("192.168.1.100", 8082);
verify(trafficLightService).updateDeviceHeartbeatByAddress("192.168.1.100", 8082);
verify(trafficLightService).updateDeviceOnlineStatusByAddress("192.168.1.100", 8082, true);
verify(intersectionService).getActiveIntersectionById("INTERSECTION_001");
verify(eventPublisher).publishEvent(any(TrafficLightStatusEvent.class));
}
@Test
void testProcessTrafficLightSignal_WithTraditionalFormat() {
// 测试处理传统JSON格式消息向后兼容
String rawMessage = "{\"device_id\":\"TL_001\",\"DI-11\":1,\"DI-16\":1}";
TrafficLightStatus traditionalStatus = TrafficLightStatus.builder()
.deviceId("TL_001")
.nsStatus(SignalState.RED)
@ -129,29 +129,27 @@ class DataProcessingServiceTrafficLightTest {
.timestamp(System.currentTimeMillis() * 1000)
.rawSignal(rawMessage)
.build();
// Mock解析器行为
when(trafficLightSignalParser.isValidMessageFormat(rawMessage)).thenReturn(false);
when(trafficLightSignalParser.isValidSignal(rawMessage)).thenReturn(true);
when(trafficLightSignalParser.parseSignal(rawMessage)).thenReturn(traditionalStatus);
// Mock设备查找通过设备ID
when(trafficLightService.getActiveTrafficLightByDeviceId("TL_001"))
.thenReturn(Optional.of(testDevice));
// Mock设备状态更新
when(trafficLightService.updateDeviceHeartbeat("TL_001")).thenReturn(true);
when(trafficLightService.updateDeviceOnlineStatus("TL_001", true)).thenReturn(true);
// Mock路口查找
when(intersectionService.getActiveIntersectionById("INTERSECTION_001"))
.thenReturn(Optional.of(testIntersection));
// 执行测试
dataProcessingService.processTrafficLightSignal(rawMessage);
// 验证调用
verify(trafficLightSignalParser).isValidMessageFormat(rawMessage);
verify(trafficLightSignalParser).isValidSignal(rawMessage);
verify(trafficLightSignalParser).parseSignal(rawMessage);
verify(trafficLightService).getActiveTrafficLightByDeviceId("TL_001");
@ -159,21 +157,23 @@ class DataProcessingServiceTrafficLightTest {
verify(trafficLightService).updateDeviceOnlineStatus("TL_001", true);
verify(eventPublisher).publishEvent(any(TrafficLightStatusEvent.class));
}
@Test
void testProcessTrafficLightSignal_AutoCreateDevice() {
// 测试自动创建设备功能
String rawMessage = "('10.0.0.1', 9090) - {\"DI-11\":1,\"DI-14\":1}";
String jsonSignal = "{\"DI-11\":1,\"DI-14\":1}";
String ipAddress = "10.0.0.1";
Integer port = 9090;
TrafficLightStatus newDeviceStatus = TrafficLightStatus.builder()
.ipAddress("10.0.0.1")
.port(9090)
.nsStatus(SignalState.RED)
.ewStatus(SignalState.RED)
.timestamp(System.currentTimeMillis() * 1000)
.rawSignal(rawMessage)
.rawSignal(jsonSignal)
.build();
TrafficLight newDevice = TrafficLight.builder()
.id(2L)
.deviceName("TrafficLight_10_0_0_1_9090")
@ -184,7 +184,7 @@ class DataProcessingServiceTrafficLightTest {
.isActive(true)
.isOnline(false)
.build();
Intersection defaultIntersection = Intersection.builder()
.id(2L)
.intersectionId("DEFAULT_INTERSECTION")
@ -194,28 +194,27 @@ class DataProcessingServiceTrafficLightTest {
.areaCode("DEFAULT")
.isActive(true)
.build();
// Mock解析器行为
when(trafficLightSignalParser.isValidMessageFormat(rawMessage)).thenReturn(true);
when(trafficLightSignalParser.parseSignalWithAddress(rawMessage)).thenReturn(newDeviceStatus);
when(trafficLightSignalParser.parseSignalWithAddress(jsonSignal, ipAddress, port)).thenReturn(newDeviceStatus);
// Mock设备查找不存在和创建
when(trafficLightService.findDeviceByAddress("10.0.0.1", 9090))
.thenReturn(Optional.empty());
when(trafficLightService.getOrCreateDeviceByAddress("10.0.0.1", 9090, "DEFAULT_INTERSECTION"))
.thenReturn(newDevice);
// Mock设备状态更新
when(trafficLightService.updateDeviceHeartbeatByAddress("10.0.0.1", 9090)).thenReturn(true);
when(trafficLightService.updateDeviceOnlineStatusByAddress("10.0.0.1", 9090, true)).thenReturn(true);
// Mock路口查找
when(intersectionService.getActiveIntersectionById("DEFAULT_INTERSECTION"))
.thenReturn(Optional.of(defaultIntersection));
// 执行测试
dataProcessingService.processTrafficLightSignal(rawMessage);
dataProcessingService.processTrafficLightSignalWithAddress(jsonSignal, ipAddress, port);
// 验证调用
verify(trafficLightService).findDeviceByAddress("10.0.0.1", 9090);
verify(trafficLightService).getOrCreateDeviceByAddress("10.0.0.1", 9090, "DEFAULT_INTERSECTION");
@ -223,92 +222,93 @@ class DataProcessingServiceTrafficLightTest {
verify(trafficLightService).updateDeviceOnlineStatusByAddress("10.0.0.1", 9090, true);
verify(eventPublisher).publishEvent(any(TrafficLightStatusEvent.class));
}
@Test
void testProcessTrafficLightSignal_InvalidFormat() {
// 测试无效格式消息
String invalidMessage = "invalid message format";
// Mock解析器行为
when(trafficLightSignalParser.isValidMessageFormat(invalidMessage)).thenReturn(false);
when(trafficLightSignalParser.isValidSignal(invalidMessage)).thenReturn(false);
// 执行测试
dataProcessingService.processTrafficLightSignal(invalidMessage);
// 验证只调用了格式检查没有进行后续处理
verify(trafficLightSignalParser).isValidMessageFormat(invalidMessage);
verify(trafficLightSignalParser).isValidSignal(invalidMessage);
verifyNoInteractions(trafficLightService);
verifyNoInteractions(intersectionService);
verifyNoInteractions(eventPublisher);
}
@Test
void testProcessTrafficLightSignal_InvalidStatus() {
// 测试解析出无效状态的情况
String rawMessage = "('192.168.1.100', 8082) - {\"DI-11\":0}";
String jsonSignal = "{\"DI-11\":0}";
String ipAddress = "192.168.1.100";
Integer port = 8082;
TrafficLightStatus invalidStatus = TrafficLightStatus.builder()
.timestamp(0) // 无效时间戳
.rawSignal(rawMessage)
.rawSignal(jsonSignal)
.build();
// Mock解析器行为
when(trafficLightSignalParser.isValidMessageFormat(rawMessage)).thenReturn(true);
when(trafficLightSignalParser.parseSignalWithAddress(rawMessage)).thenReturn(invalidStatus);
when(trafficLightSignalParser.parseSignalWithAddress(jsonSignal, ipAddress, port)).thenReturn(invalidStatus);
// 执行测试
dataProcessingService.processTrafficLightSignal(rawMessage);
dataProcessingService.processTrafficLightSignalWithAddress(jsonSignal, ipAddress, port);
// 验证只进行了解析没有后续处理
verify(trafficLightSignalParser).parseSignalWithAddress(rawMessage);
verify(trafficLightSignalParser).parseSignalWithAddress(jsonSignal, ipAddress, port);
verifyNoInteractions(trafficLightService);
verifyNoInteractions(intersectionService);
verifyNoInteractions(eventPublisher);
}
@Test
void testProcessTrafficLightSignal_EmptyMessage() {
// 测试空消息
dataProcessingService.processTrafficLightSignal(null);
dataProcessingService.processTrafficLightSignal("");
dataProcessingService.processTrafficLightSignal(" ");
// 验证没有任何处理
verifyNoInteractions(trafficLightSignalParser);
verifyNoInteractions(trafficLightService);
verifyNoInteractions(intersectionService);
verifyNoInteractions(eventPublisher);
}
@Test
void testProcessTrafficLightSignal_DeviceNotFound() {
// 测试设备不存在且无法创建的情况
String rawMessage = "('172.16.0.1', 8083) - {\"DI-11\":1}";
String jsonSignal = "{\"DI-11\":1}";
String ipAddress = "172.16.0.1";
Integer port = 8083;
TrafficLightStatus statusWithoutDevice = TrafficLightStatus.builder()
.ipAddress("172.16.0.1")
.port(8083)
.nsStatus(SignalState.RED)
.ewStatus(SignalState.RED)
.timestamp(System.currentTimeMillis() * 1000)
.rawSignal(rawMessage)
.rawSignal(jsonSignal)
.build();
// Mock解析器行为
when(trafficLightSignalParser.isValidMessageFormat(rawMessage)).thenReturn(true);
when(trafficLightSignalParser.parseSignalWithAddress(rawMessage)).thenReturn(statusWithoutDevice);
when(trafficLightSignalParser.parseSignalWithAddress(jsonSignal, ipAddress, port))
.thenReturn(statusWithoutDevice);
// Mock设备查找失败
when(trafficLightService.findDeviceByAddress("172.16.0.1", 8083))
.thenReturn(Optional.empty());
when(trafficLightService.getOrCreateDeviceByAddress("172.16.0.1", 8083, "DEFAULT_INTERSECTION"))
.thenThrow(new RuntimeException("创建设备失败"));
// 执行测试
dataProcessingService.processTrafficLightSignal(rawMessage);
dataProcessingService.processTrafficLightSignalWithAddress(jsonSignal, ipAddress, port);
// 验证尝试了设备查找和创建但没有后续处理
verify(trafficLightService).findDeviceByAddress("172.16.0.1", 8083);
verify(trafficLightService).getOrCreateDeviceByAddress("172.16.0.1", 8083, "DEFAULT_INTERSECTION");