2026-06-26 20b96473f2520590a0dca6b775b81e3ea06a77a0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
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 待响应请求管理器
 * <p>
 * 管理轮询下发的请求,用于匹配设备响应:
 * - TCP 模式:按 transactionId 精确匹配
 * - RTU 模式:按 slaveId + functionCode FIFO 匹配
 *
 * @author 芋道源码
 */
@Slf4j
public class IotModbusTcpServerPendingRequestManager {
 
    /**
     * deviceId → 有序队列
     */
    private final Map<Long, Deque<PendingRequest>> 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<PendingRequest> 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<PendingRequest> queue, int transactionId) {
        Iterator<PendingRequest> 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<PendingRequest> queue, int slaveId, int functionCode,
                                        int responseRegisterCount) {
        Iterator<PendingRequest> 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<Long, Deque<PendingRequest>> entry : pendingRequests.entrySet()) {
            Deque<PendingRequest> queue = entry.getValue();
            int removed = 0;
            Iterator<PendingRequest> 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();
    }
 
}