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();
}
}