package cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.manager; import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.module.iot.core.enums.modbus.IotModbusFrameFormatEnum; import cn.iocoder.yudao.module.iot.gateway.protocol.modbus.common.utils.IotModbusCommonUtils; import cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.codec.IotModbusFrame; import lombok.AllArgsConstructor; import lombok.Data; import lombok.extern.slf4j.Slf4j; import java.util.Deque; import java.util.Iterator; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedDeque; /** * IoT Modbus TCP Server 待响应请求管理器 *

* 管理轮询下发的请求,用于匹配设备响应: * - TCP 模式:按 transactionId 精确匹配 * - RTU 模式:按 slaveId + functionCode FIFO 匹配 * * @author 芋道源码 */ @Slf4j public class IotModbusTcpServerPendingRequestManager { /** * deviceId → 有序队列 */ private final Map> pendingRequests = new ConcurrentHashMap<>(); /** * 待响应请求信息 */ @Data @AllArgsConstructor public static class PendingRequest { private Long deviceId; private Long pointId; private String identifier; private int slaveId; private int functionCode; private int registerAddress; private int registerCount; private Integer transactionId; private long expireAt; } /** * 添加待响应请求 */ public void addRequest(PendingRequest request) { pendingRequests.computeIfAbsent(request.getDeviceId(), k -> new ConcurrentLinkedDeque<>()) .addLast(request); } /** * 匹配响应(TCP 模式按 transactionId,RTU 模式按 FIFO) * * @param deviceId 设备 ID * @param frame 收到的响应帧 * @param frameFormat 帧格式 * @return 匹配到的 PendingRequest,没有匹配返回 null */ public PendingRequest matchResponse(Long deviceId, IotModbusFrame frame, IotModbusFrameFormatEnum frameFormat) { Deque queue = pendingRequests.get(deviceId); if (CollUtil.isEmpty(queue)) { return null; } // TCP 模式:按 transactionId 精确匹配 if (frameFormat == IotModbusFrameFormatEnum.MODBUS_TCP && frame.getTransactionId() != null) { return matchByTransactionId(queue, frame.getTransactionId()); } // RTU 模式:FIFO,匹配 slaveId + functionCode + registerCount int responseRegisterCount = IotModbusCommonUtils.extractRegisterCountFromResponse(frame); return matchByFifo(queue, frame.getSlaveId(), frame.getFunctionCode(), responseRegisterCount); } /** * 按 transactionId 匹配 */ private PendingRequest matchByTransactionId(Deque queue, int transactionId) { Iterator it = queue.iterator(); while (it.hasNext()) { PendingRequest req = it.next(); if (req.getTransactionId() != null && req.getTransactionId() == transactionId) { it.remove(); return req; } } return null; } /** * 按 FIFO 匹配(slaveId + functionCode + registerCount) */ private PendingRequest matchByFifo(Deque queue, int slaveId, int functionCode, int responseRegisterCount) { Iterator it = queue.iterator(); while (it.hasNext()) { PendingRequest req = it.next(); if (req.getSlaveId() == slaveId && req.getFunctionCode() == functionCode && (responseRegisterCount <= 0 || req.getRegisterCount() == responseRegisterCount)) { it.remove(); return req; } } return null; } /** * 清理过期请求 */ public void cleanupExpired() { long now = System.currentTimeMillis(); for (Map.Entry> entry : pendingRequests.entrySet()) { Deque queue = entry.getValue(); int removed = 0; Iterator it = queue.iterator(); while (it.hasNext()) { PendingRequest req = it.next(); if (req.getExpireAt() < now) { it.remove(); removed++; } } if (removed > 0) { log.debug("[cleanupExpired][设备 {} 清理了 {} 个过期请求]", entry.getKey(), removed); } } } /** * 清理指定设备的所有待响应请求 */ public void removeDevice(Long deviceId) { pendingRequests.remove(deviceId); } /** * 清理所有待响应请求 */ public void clear() { pendingRequests.clear(); } }