diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml index eca49295..d85d110a 100644 --- a/ruoyi-admin/src/main/resources/application.yml +++ b/ruoyi-admin/src/main/resources/application.yml @@ -140,6 +140,6 @@ mill: # L3 系统 IP 地址 remote-host: 127.0.0.1 # L3 系统 UDP 端口 - remote-port: 9000 + remote-port: 9001 # 接收缓冲区大小(字节) buffer-size: 4096 diff --git a/ruoyi-mill/src/main/java/com/ruoyi/mill/controller/UdpController.java b/ruoyi-mill/src/main/java/com/ruoyi/mill/controller/UdpController.java index 13083469..fd89732d 100644 --- a/ruoyi-mill/src/main/java/com/ruoyi/mill/controller/UdpController.java +++ b/ruoyi-mill/src/main/java/com/ruoyi/mill/controller/UdpController.java @@ -3,6 +3,10 @@ package com.ruoyi.mill.controller; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.mill.protocol.TelegramCodec; +import com.ruoyi.mill.protocol.TelegramSchema; +import com.ruoyi.mill.udp.TelegramRecord; +import com.ruoyi.mill.udp.TelegramStore; import com.ruoyi.mill.udp.UdpProperties; import com.ruoyi.mill.udp.UdpServer; import com.ruoyi.mill.udp.UdpSender; @@ -37,6 +41,9 @@ public class UdpController extends BaseController { @Autowired private UdpSender udpSender; + @Autowired + private TelegramStore telegramStore; + /** * 获取UDP配置 */ @@ -78,27 +85,28 @@ public class UdpController extends BaseController { try { String tcNo = (String) requestData.get("tcNo"); - Object payloadObj = requestData.get("payload"); + if (StringUtils.isBlank(tcNo)) { + return error("电文号不能为空"); + } byte[] payload; - if (payloadObj instanceof java.util.List) { - java.util.List list = (java.util.List) payloadObj; - payload = new byte[list.size()]; - for (int i = 0; i < list.size(); i++) { - payload[i] = ((Number) list.get(i)).byteValue(); + Object dataObj = requestData.get("data"); + + if (dataObj != null) { + @SuppressWarnings("unchecked") + Map dataMap = (Map) dataObj; + java.util.List schema = TelegramSchema.getSchema(tcNo); + if (schema == null) { + return error("未知电文号: " + tcNo + ",无法编码"); } - } else if (payloadObj instanceof int[]) { - int[] intArray = (int[]) payloadObj; - payload = new byte[intArray.length]; - for (int i = 0; i < intArray.length; i++) { - payload[i] = (byte) intArray[i]; - } - } else if (payloadObj instanceof byte[]) { - payload = (byte[]) payloadObj; - } else if (payloadObj instanceof String) { - payload = ((String) payloadObj).getBytes(StandardCharsets.UTF_8); + payload = TelegramCodec.encode(schema, dataMap); + log.info("[UDP-SEND] 使用Schema编码电文 {},data字段数={},编码后长度={} bytes", tcNo, dataMap.size(), payload.length); } else { - return error("无效的payload类型: " + (payloadObj == null ? "null" : payloadObj.getClass().getName())); + Object payloadObj = requestData.get("payload"); + if (payloadObj == null) { + return error("data 或 payload 必须提供一项"); + } + payload = convertPayloadToBytes(payloadObj); } boolean result = udpSender.send(tcNo, payload); @@ -117,6 +125,30 @@ public class UdpController extends BaseController { } } + @SuppressWarnings("unchecked") + private byte[] convertPayloadToBytes(Object payloadObj) { + if (payloadObj instanceof java.util.List) { + java.util.List list = (java.util.List) payloadObj; + byte[] payload = new byte[list.size()]; + for (int i = 0; i < list.size(); i++) { + payload[i] = ((Number) list.get(i)).byteValue(); + } + return payload; + } else if (payloadObj instanceof int[]) { + int[] intArray = (int[]) payloadObj; + byte[] payload = new byte[intArray.length]; + for (int i = 0; i < intArray.length; i++) { + payload[i] = (byte) intArray[i]; + } + return payload; + } else if (payloadObj instanceof byte[]) { + return (byte[]) payloadObj; + } else if (payloadObj instanceof String) { + return ((String) payloadObj).getBytes(StandardCharsets.UTF_8); + } + throw new IllegalArgumentException("无效的payload类型: " + (payloadObj == null ? "null" : payloadObj.getClass().getName())); + } + /** * 解析电文数据 */ @@ -169,20 +201,35 @@ public class UdpController extends BaseController { @RequestParam(defaultValue = "1") Integer pageNum, @RequestParam(defaultValue = "50") Integer pageSize) { - // TODO: 从数据库或缓存中获取历史记录 - // 这里返回模拟数据 - return success(getMockHistory(pageNum, pageSize)); + java.util.List rows = telegramStore.getHistory(pageNum, pageSize); + int total = telegramStore.getTotalCount(); + + java.util.Map result = new java.util.HashMap<>(); + result.put("rows", rows); + result.put("total", total); + return success(result); } - /** - * 获取电文统计信息 - */ @ApiOperation("获取电文统计信息") @GetMapping("/stats") public AjaxResult telegramStats() { - // TODO: 从数据库或缓存中获取统计数据 - // 这里返回模拟数据 - return success(getMockStats()); + int total = telegramStore.getTotalCount(); + long successCount = telegramStore.countSuccess(); + long todayInbound = telegramStore.countTodayInbound(); + + java.util.Map stats = new java.util.HashMap<>(); + stats.put("todayReceived", todayInbound); + stats.put("totalReceived", telegramStore.countInbound()); + stats.put("successRate", total > 0 ? Math.round((successCount * 100.0) / total) : 0); + stats.put("avgDelay", 0); + return success(stats); + } + + @ApiOperation("清空电文历史记录") + @DeleteMapping("/history") + public AjaxResult clearHistory() { + telegramStore.clear(); + return success(); } /** @@ -221,60 +268,4 @@ public class UdpController extends BaseController { return result.toString(); } - /** - * 生成模拟历史记录数据 - */ - private Object getMockHistory(int pageNum, int pageSize) { - // 这里应该从数据库查询真实数据 - // 返回模拟数据用于演示 - - java.util.List mockData = java.util.Arrays.asList( - createMockRecord(1L, "2FK101", "IN", "2024-04-30 10:30:15", 128), - createMockRecord(2L, "K12F03", "OUT", "2024-04-30 10:30:20", 96), - createMockRecord(3L, "2FK102", "IN", "2024-04-30 10:30:25", 64), - createMockRecord(4L, "K12F01", "OUT", "2024-04-30 10:30:30", 80) - ); - - int total = mockData.size(); - int fromIndex = (pageNum - 1) * pageSize; - int toIndex = Math.min(fromIndex + pageSize, total); - - if (fromIndex >= total) { - return java.util.Collections.emptyList(); - } - - java.util.Map result = new java.util.HashMap<>(); - result.put("rows", mockData.subList(fromIndex, toIndex)); - result.put("total", total); - return result; - } - - /** - * 创建模拟记录 - */ - private java.util.Map createMockRecord( - Long id, String tcNo, String direction, - String timestamp, Integer payloadLength) { - - java.util.Map record = new java.util.HashMap<>(); - record.put("id", id); - record.put("tcNo", tcNo); - record.put("direction", direction); - record.put("timestamp", timestamp); - record.put("payloadLength", payloadLength); - record.put("status", "成功"); - return record; - } - - /** - * 生成模拟统计数据 - */ - private java.util.Map getMockStats() { - java.util.Map stats = new java.util.HashMap<>(); - stats.put("todayReceived", 25); - stats.put("totalReceived", 1247); - stats.put("successRate", 98); - stats.put("avgDelay", 150); - return stats; - } } diff --git a/ruoyi-mill/src/main/java/com/ruoyi/mill/protocol/TelegramSchema.java b/ruoyi-mill/src/main/java/com/ruoyi/mill/protocol/TelegramSchema.java index 71888fe0..71e12795 100644 --- a/ruoyi-mill/src/main/java/com/ruoyi/mill/protocol/TelegramSchema.java +++ b/ruoyi-mill/src/main/java/com/ruoyi/mill/protocol/TelegramSchema.java @@ -28,6 +28,19 @@ public final class TelegramSchema { private static FieldDef i(String name) { return new FieldDef(name, INT, 4, 0); } private static FieldDef f(String name, int len, int precision) { return new FieldDef(name, FLOAT, len, precision); } + public static List getSchema(String tcNo) { + switch (tcNo) { + case ID_2FK101: return SCHEMA_2FK101; + case ID_2FK102: return SCHEMA_2FK102; + case ID_2FK103: return SCHEMA_2FK103; + case ID_2FK104: return SCHEMA_2FK104; + case ID_K12F01: return SCHEMA_K12F01; + case ID_K12F02: return SCHEMA_K12F02; + case ID_K12F03: return SCHEMA_K12F03; + default: return null; + } + } + // ───────────────────────────────────────────────────────────── // 2FK101 作业命令信息 L3→L2 // ───────────────────────────────────────────────────────────── diff --git a/ruoyi-mill/src/main/java/com/ruoyi/mill/service/impl/UdpServiceImpl.java b/ruoyi-mill/src/main/java/com/ruoyi/mill/service/impl/UdpServiceImpl.java index 7029664c..e397e8fa 100644 --- a/ruoyi-mill/src/main/java/com/ruoyi/mill/service/impl/UdpServiceImpl.java +++ b/ruoyi-mill/src/main/java/com/ruoyi/mill/service/impl/UdpServiceImpl.java @@ -1,6 +1,8 @@ package com.ruoyi.mill.service.impl; import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.mill.udp.TelegramRecord; +import com.ruoyi.mill.udp.TelegramStore; import com.ruoyi.mill.udp.UdpProperties; import com.ruoyi.mill.udp.UdpServer; import com.ruoyi.mill.udp.UdpSender; @@ -10,7 +12,6 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import java.text.SimpleDateFormat; import java.util.*; /** @@ -31,6 +32,9 @@ public class UdpServiceImpl implements IUdpService { @Autowired private UdpSender udpSender; + @Autowired + private TelegramStore telegramStore; + /** * 获取UDP配置 */ @@ -86,68 +90,34 @@ public class UdpServiceImpl implements IUdpService { */ @Override public List> getTelegramHistory(int pageNum, int pageSize) { - // TODO: 从数据库查询真实的历史记录 - // 这里返回模拟数据用于演示 - - List> mockData = new ArrayList<>(); - - // 生成一些模拟的历史记录 - for (int i = 1; i <= 20; i++) { - Map record = new HashMap<>(); - record.put("id", i); - record.put("tcNo", getRandomTcNo()); - record.put("direction", Math.random() > 0.5 ? "IN" : "OUT"); - record.put("timestamp", generateMockTimestamp(i)); - record.put("payloadLength", new Random().nextInt(200) + 32); - record.put("status", "成功"); - - mockData.add(record); + List records = telegramStore.getHistory(pageNum, pageSize); + List> result = new ArrayList<>(); + for (TelegramRecord r : records) { + Map map = new LinkedHashMap<>(); + map.put("id", r.getId()); + map.put("tcNo", r.getTcNo()); + map.put("direction", r.getDirection()); + map.put("timestamp", r.getTimestamp()); + map.put("payloadLength", r.getPayloadLength()); + map.put("status", r.getStatus()); + map.put("rawPayload", r.getRawPayload()); + map.put("parsedFields", r.getParsedFields()); + map.put("decodedData", r.getDecodedData()); + result.add(map); } - - // 简单的分页逻辑 - int fromIndex = (pageNum - 1) * pageSize; - int toIndex = Math.min(fromIndex + pageSize, mockData.size()); - - if (fromIndex >= mockData.size()) { - return Collections.emptyList(); - } - - return mockData.subList(fromIndex, toIndex); + return result; } - /** - * 获取电文统计数据 - */ @Override public Map getTelegramStats() { - // TODO: 从数据库查询真实的统计数据 - // 这里返回模拟数据用于演示 + int total = telegramStore.getTotalCount(); + long successCount = telegramStore.countSuccess(); Map stats = new HashMap<>(); - stats.put("todayReceived", 25 + new Random().nextInt(10)); - stats.put("totalReceived", 1247 + new Random().nextInt(100)); - stats.put("successRate", 95 + new Random().nextInt(5)); - stats.put("avgDelay", 120 + new Random().nextInt(60)); - + stats.put("todayReceived", telegramStore.countTodayInbound()); + stats.put("totalReceived", telegramStore.countInbound()); + stats.put("successRate", total > 0 ? Math.round((successCount * 100.0) / total) : 0); + stats.put("avgDelay", 0); return stats; } - - /** - * 获取随机电文号(用于模拟) - */ - private String getRandomTcNo() { - String[] tcNos = {"2FK101", "2FK102", "2FK103", "2FK104", "K12F01", "K12F02", "K12F03"}; - return tcNos[new Random().nextInt(tcNos.length)]; - } - - /** - * 生成模拟时间戳 - */ - private String generateMockTimestamp(int index) { - Calendar calendar = Calendar.getInstance(); - calendar.add(Calendar.MINUTE, -index * 5); // 每5分钟一条记录 - - SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - return sdf.format(calendar.getTime()); - } } diff --git a/ruoyi-mill/src/main/java/com/ruoyi/mill/udp/TelegramDispatcher.java b/ruoyi-mill/src/main/java/com/ruoyi/mill/udp/TelegramDispatcher.java index 8c327810..c5cd3bd8 100644 --- a/ruoyi-mill/src/main/java/com/ruoyi/mill/udp/TelegramDispatcher.java +++ b/ruoyi-mill/src/main/java/com/ruoyi/mill/udp/TelegramDispatcher.java @@ -17,24 +17,33 @@ public class TelegramDispatcher { private static final Logger log = LoggerFactory.getLogger(TelegramDispatcher.class); - public void dispatch(String telegramId, byte[] payload) { + @org.springframework.beans.factory.annotation.Autowired + private TelegramStore telegramStore; + + public void dispatch(String telegramId, byte[] rawFrame, byte[] payload) { log.info("[UDP-RECV] telegramId={} payloadLen={}", telegramId, payload.length); + Map decoded = null; switch (telegramId) { case TelegramSchema.ID_2FK101: - handle2FK101(TelegramCodec.decode(TelegramSchema.SCHEMA_2FK101, payload)); + decoded = TelegramCodec.decode(TelegramSchema.SCHEMA_2FK101, payload); + handle2FK101(decoded); break; case TelegramSchema.ID_2FK102: - handle2FK102(TelegramCodec.decode(TelegramSchema.SCHEMA_2FK102, payload)); + decoded = TelegramCodec.decode(TelegramSchema.SCHEMA_2FK102, payload); + handle2FK102(decoded); break; case TelegramSchema.ID_2FK103: - handle2FK103(TelegramCodec.decode(TelegramSchema.SCHEMA_2FK103, payload)); + decoded = TelegramCodec.decode(TelegramSchema.SCHEMA_2FK103, payload); + handle2FK103(decoded); break; case TelegramSchema.ID_2FK104: - handle2FK104(TelegramCodec.decode(TelegramSchema.SCHEMA_2FK104, payload)); + decoded = TelegramCodec.decode(TelegramSchema.SCHEMA_2FK104, payload); + handle2FK104(decoded); break; default: log.warn("[UDP-RECV] 未知电文号: {}", telegramId); } + telegramStore.addInbound(telegramId, rawFrame, decoded); } /** 2FK101 作业命令信息 — L3 下发生产计划 */ diff --git a/ruoyi-mill/src/main/java/com/ruoyi/mill/udp/TelegramRecord.java b/ruoyi-mill/src/main/java/com/ruoyi/mill/udp/TelegramRecord.java new file mode 100644 index 00000000..42b0c24b --- /dev/null +++ b/ruoyi-mill/src/main/java/com/ruoyi/mill/udp/TelegramRecord.java @@ -0,0 +1,95 @@ +package com.ruoyi.mill.udp; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class TelegramRecord { + + private static final DateTimeFormatter FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + + private long id; + private String tcNo; + private String direction; + private String timestamp; + private int payloadLength; + private String status; + private String rawPayload; + private List> parsedFields; + private Map decodedData; + + public static TelegramRecord inbound(long id, String tcNo, byte[] rawFrame, Map decodedData) { + TelegramRecord r = new TelegramRecord(); + r.id = id; + r.tcNo = tcNo; + r.direction = "IN"; + r.timestamp = LocalDateTime.now().format(FMT); + r.payloadLength = rawFrame.length; + r.status = "成功"; + r.rawPayload = toHexDump(rawFrame); + r.decodedData = decodedData; + r.parsedFields = buildParsedFields(decodedData); + return r; + } + + public static TelegramRecord outbound(long id, String tcNo, byte[] payload, boolean success, Map decodedData) { + TelegramRecord r = new TelegramRecord(); + r.id = id; + r.tcNo = tcNo; + r.direction = "OUT"; + r.timestamp = LocalDateTime.now().format(FMT); + r.payloadLength = payload.length; + r.status = success ? "成功" : "失败"; + r.rawPayload = toHexDump(payload); + r.decodedData = decodedData; + r.parsedFields = buildParsedFields(decodedData); + return r; + } + + private static String toHexDump(byte[] data) { + StringBuilder sb = new StringBuilder(); + int limit = Math.min(data.length, 256); + for (int i = 0; i < limit; i++) { + sb.append(String.format("%02X ", data[i])); + if ((i + 1) % 32 == 0) sb.append('\n'); + } + if (data.length > 256) sb.append("... (truncated)"); + return sb.toString().trim(); + } + + private static List> buildParsedFields(Map decodedData) { + List> fields = new ArrayList<>(); + if (decodedData == null) return fields; + for (Map.Entry entry : decodedData.entrySet()) { + Map field = new java.util.LinkedHashMap<>(); + field.put("name", entry.getKey()); + Object val = entry.getValue(); + field.put("value", val != null ? val.toString() : ""); + field.put("type", val instanceof Integer ? "INT" : val instanceof Float ? "FLOAT" : val instanceof Long ? "LONG" : "STRING"); + field.put("length", val != null ? val.toString().length() : 0); + fields.add(field); + } + return fields; + } + + public long getId() { return id; } + public void setId(long id) { this.id = id; } + public String getTcNo() { return tcNo; } + public void setTcNo(String tcNo) { this.tcNo = tcNo; } + public String getDirection() { return direction; } + public void setDirection(String direction) { this.direction = direction; } + public String getTimestamp() { return timestamp; } + public void setTimestamp(String timestamp) { this.timestamp = timestamp; } + public int getPayloadLength() { return payloadLength; } + public void setPayloadLength(int payloadLength) { this.payloadLength = payloadLength; } + public String getStatus() { return status; } + public void setStatus(String status) { this.status = status; } + public String getRawPayload() { return rawPayload; } + public void setRawPayload(String rawPayload) { this.rawPayload = rawPayload; } + public List> getParsedFields() { return parsedFields; } + public void setParsedFields(List> parsedFields) { this.parsedFields = parsedFields; } + public Map getDecodedData() { return decodedData; } + public void setDecodedData(Map decodedData) { this.decodedData = decodedData; } +} diff --git a/ruoyi-mill/src/main/java/com/ruoyi/mill/udp/TelegramStore.java b/ruoyi-mill/src/main/java/com/ruoyi/mill/udp/TelegramStore.java new file mode 100644 index 00000000..e20af84d --- /dev/null +++ b/ruoyi-mill/src/main/java/com/ruoyi/mill/udp/TelegramStore.java @@ -0,0 +1,69 @@ +package com.ruoyi.mill.udp; + +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.CopyOnWriteArrayList; + +@Component +public class TelegramStore { + + private static final int MAX_RECORDS = 1000; + + private final CopyOnWriteArrayList records = new CopyOnWriteArrayList<>(); + private final AtomicLong idGenerator = new AtomicLong(0); + + public TelegramRecord addInbound(String tcNo, byte[] rawFrame, java.util.Map decodedData) { + TelegramRecord record = TelegramRecord.inbound(idGenerator.incrementAndGet(), tcNo, rawFrame, decodedData); + records.add(0, record); + trimIfNeeded(); + return record; + } + + public TelegramRecord addOutbound(String tcNo, byte[] payload, boolean success, java.util.Map decodedData) { + TelegramRecord record = TelegramRecord.outbound(idGenerator.incrementAndGet(), tcNo, payload, success, decodedData); + records.add(0, record); + trimIfNeeded(); + return record; + } + + public List getHistory(int pageNum, int pageSize) { + int total = records.size(); + int fromIndex = (pageNum - 1) * pageSize; + if (fromIndex >= total) return Collections.emptyList(); + int toIndex = Math.min(fromIndex + pageSize, total); + return new ArrayList<>(records.subList(fromIndex, toIndex)); + } + + public int getTotalCount() { + return records.size(); + } + + public long countTodayInbound() { + String today = java.time.LocalDate.now().toString(); + return records.stream() + .filter(r -> r.getDirection().equals("IN") && r.getTimestamp().startsWith(today)) + .count(); + } + + public long countInbound() { + return records.stream().filter(r -> r.getDirection().equals("IN")).count(); + } + + public long countSuccess() { + return records.stream().filter(r -> "成功".equals(r.getStatus())).count(); + } + + public void clear() { + records.clear(); + } + + private void trimIfNeeded() { + while (records.size() > MAX_RECORDS) { + records.remove(records.size() - 1); + } + } +} diff --git a/ruoyi-mill/src/main/java/com/ruoyi/mill/udp/UdpSender.java b/ruoyi-mill/src/main/java/com/ruoyi/mill/udp/UdpSender.java index f390bdfe..4f95c369 100644 --- a/ruoyi-mill/src/main/java/com/ruoyi/mill/udp/UdpSender.java +++ b/ruoyi-mill/src/main/java/com/ruoyi/mill/udp/UdpSender.java @@ -26,6 +26,9 @@ public class UdpSender { @Autowired private UdpProperties props; + @Autowired + private TelegramStore telegramStore; + /** K12F01 计划信息应答 */ public void sendK12F01(Map data) { send(TelegramSchema.ID_K12F01, TelegramCodec.encode(TelegramSchema.SCHEMA_K12F01, data)); @@ -68,6 +71,7 @@ public class UdpSender { log.info("[UDP-SEND] tcNo={} -> {}:{} frameLen={}", tcNo, props.getRemoteHost(), props.getRemotePort(), frame.length); + telegramStore.addOutbound(tcNo, frame, true, decodePayload(tcNo, payload)); return true; } catch (Exception e) { @@ -81,10 +85,23 @@ public class UdpSender { } catch (Exception e) { log.error("[UDP-SEND] 发送失败 tcNo={}", tcNo, e); + telegramStore.addOutbound(tcNo, payload, false, decodePayload(tcNo, payload)); return false; } return false; } + private Map decodePayload(String tcNo, byte[] payload) { + try { + java.util.List schema = TelegramSchema.getSchema(tcNo); + if (schema != null) { + return TelegramCodec.decode(schema, payload); + } + } catch (Exception e) { + log.debug("[UDP-SEND] 解码payload失败 tcNo={}: {}", tcNo, e.getMessage()); + } + return null; + } + } diff --git a/ruoyi-mill/src/main/java/com/ruoyi/mill/udp/UdpServer.java b/ruoyi-mill/src/main/java/com/ruoyi/mill/udp/UdpServer.java index a241104a..bcf736f6 100644 --- a/ruoyi-mill/src/main/java/com/ruoyi/mill/udp/UdpServer.java +++ b/ruoyi-mill/src/main/java/com/ruoyi/mill/udp/UdpServer.java @@ -57,20 +57,43 @@ public class UdpServer { private void receiveLoop() { byte[] buf = new byte[props.getBufferSize()]; + log.info("[UDP-SERVER] 接收线程已启动,监听端口: {}", props.getLocalPort()); while (running) { try { DatagramPacket pkt = new DatagramPacket(buf, buf.length); socket.receive(pkt); + + // 打印接收到的原始数据信息 + String senderAddr = pkt.getAddress().getHostAddress(); + int senderPort = pkt.getPort(); byte[] data = Arrays.copyOf(pkt.getData(), pkt.getLength()); + + log.info("[UDP-RECV] <<<< 收到UDP数据包 - 来源: {}:{}, 长度: {} bytes", + senderAddr, senderPort, data.length); + if (data.length < TC_NO_LEN) { - log.warn("[UDP-SERVER] 收到过短数据包,长度={}", data.length); + log.warn("[UDP-RECV] 收到过短数据包,长度={}, 忽略", data.length); continue; } + String tcNo = new String(data, 0, TC_NO_LEN, StandardCharsets.US_ASCII).trim(); byte[] payload = Arrays.copyOfRange(data, TC_NO_LEN, data.length); - dispatcher.dispatch(tcNo, payload); + + log.info("[UDP-RECV] 电文号: '{}', Payload长度: {} bytes", tcNo, payload.length); + + // 打印前32字节的十六进制数据 + StringBuilder hexDump = new StringBuilder(); + for (int i = 0; i < Math.min(data.length, 32); i++) { + hexDump.append(String.format("%02X ", data[i])); + } + log.debug("[UDP-RECV] 数据预览: {}", hexDump.toString()); + + dispatcher.dispatch(tcNo, data, payload); + } catch (Exception e) { - if (running) log.error("[UDP-SERVER] 接收异常", e); + if (running) { + log.error("[UDP-SERVER] 接收异常", e); + } } } } diff --git a/ruoyi-ui/src/api/mill/udp.js b/ruoyi-ui/src/api/mill/udp.js index a1ce282c..d2718e55 100644 --- a/ruoyi-ui/src/api/mill/udp.js +++ b/ruoyi-ui/src/api/mill/udp.js @@ -1,6 +1,5 @@ import request from '@/utils/request' -// UDP 服务器配置 export function getUdpConfig() { return request({ url: '/mill/udp/config', method: 'get' }) } @@ -9,7 +8,6 @@ export function updateUdpConfig(data) { return request({ url: '/mill/udp/config', method: 'put', data }) } -// 发送 UDP 报文 export function sendTelegram(data) { return request({ url: '/mill/udp/send', @@ -18,7 +16,6 @@ export function sendTelegram(data) { }) } -// 获取电文历史记录 export function getTelegramHistory(query) { return request({ url: '/mill/udp/history', @@ -27,16 +24,18 @@ export function getTelegramHistory(query) { }) } -// 获取当前电文统计 export function getTelegramStats() { return request({ url: '/mill/udp/stats', method: 'get' }) } -// 解析电文数据 export function parseTelegram(tcNo, payload) { return request({ url: '/mill/udp/parse', method: 'post', data: { tcNo, payload } }) +} + +export function clearTelegramHistory() { + return request({ url: '/mill/udp/history', method: 'delete' }) } \ No newline at end of file diff --git a/ruoyi-ui/src/views/tool/udp-debug.vue b/ruoyi-ui/src/views/tool/udp-debug.vue index 27718d5d..655c8f85 100644 --- a/ruoyi-ui/src/views/tool/udp-debug.vue +++ b/ruoyi-ui/src/views/tool/udp-debug.vue @@ -20,12 +20,12 @@ - + - + @@ -184,7 +184,7 @@ height="400" style="margin-top: 10px;" > - +