package cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.handler.upstream; import cn.hutool.core.lang.Assert; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.BooleanUtil; import cn.hutool.core.util.ObjUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.exception.ServiceException; import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi; import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO; import cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusDeviceConfigRespDTO; import cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusPointRespDTO; import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; import cn.iocoder.yudao.module.iot.core.enums.modbus.IotModbusFrameFormatEnum; import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity; import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; 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 cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.codec.IotModbusFrameEncoder; import cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.manager.IotModbusTcpServerConfigCacheService; import cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.manager.IotModbusTcpServerConnectionManager; import cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.manager.IotModbusTcpServerConnectionManager.ConnectionInfo; import cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.manager.IotModbusTcpServerPendingRequestManager; import cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.manager.IotModbusTcpServerPendingRequestManager.PendingRequest; import cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.manager.IotModbusTcpServerPollScheduler; import cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceService; import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService; import io.vertx.core.net.NetSocket; import lombok.extern.slf4j.Slf4j; import java.util.Map; import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.invalidParamException; /** * IoT Modbus TCP Server 上行数据处理器 *
* 处理: * 1. 自定义 FC 认证 * 2. 轮询响应 → 点位翻译 → thing.property.post * * @author 芋道源码 */ @Slf4j public class IotModbusTcpServerUpstreamHandler { private static final String METHOD_AUTH = "auth"; private final IotDeviceCommonApi deviceApi; private final IotDeviceMessageService messageService; private final IotModbusFrameEncoder frameEncoder; private final IotModbusTcpServerConnectionManager connectionManager; private final IotModbusTcpServerConfigCacheService configCacheService; private final IotModbusTcpServerPendingRequestManager pendingRequestManager; private final IotModbusTcpServerPollScheduler pollScheduler; private final IotDeviceService deviceService; private final String serverId; public IotModbusTcpServerUpstreamHandler(IotDeviceCommonApi deviceApi, IotDeviceMessageService messageService, IotModbusFrameEncoder frameEncoder, IotModbusTcpServerConnectionManager connectionManager, IotModbusTcpServerConfigCacheService configCacheService, IotModbusTcpServerPendingRequestManager pendingRequestManager, IotModbusTcpServerPollScheduler pollScheduler, IotDeviceService deviceService, String serverId) { this.deviceApi = deviceApi; this.messageService = messageService; this.frameEncoder = frameEncoder; this.connectionManager = connectionManager; this.configCacheService = configCacheService; this.pendingRequestManager = pendingRequestManager; this.pollScheduler = pollScheduler; this.deviceService = deviceService; this.serverId = serverId; } // ========== 帧处理入口 ========== /** * 处理帧 */ public void handleFrame(NetSocket socket, IotModbusFrame frame, IotModbusFrameFormatEnum frameFormat) { if (frame == null) { return; } // 1. 异常响应 if (frame.isException()) { log.warn("[handleFrame][设备异常响应, slaveId={}, FC={}, exceptionCode={}]", frame.getSlaveId(), frame.getFunctionCode(), frame.getExceptionCode()); return; } // 2. 情况一:自定义功能码(认证等扩展) if (StrUtil.isNotEmpty(frame.getCustomData())) { handleCustomFrame(socket, frame, frameFormat); return; } // 3. 情况二:标准 Modbus 响应 → 轮询响应处理 handlePollingResponse(socket, frame, frameFormat); } // ========== 自定义 FC 处理(认证等) ========== /** * 处理自定义功能码帧 *
* 异常分层翻译,参考 {@link cn.iocoder.yudao.module.iot.gateway.protocol.http.handler.upstream.IotHttpAbstractHandler}
*/
private void handleCustomFrame(NetSocket socket, IotModbusFrame frame, IotModbusFrameFormatEnum frameFormat) {
String method = null;
try {
IotDeviceMessage message = JsonUtils.parseObject(frame.getCustomData(), IotDeviceMessage.class);
if (message == null) {
throw invalidParamException("自定义 FC 数据解析失败");
}
method = message.getMethod();
if (METHOD_AUTH.equals(method)) {
handleAuth(socket, frame, frameFormat, message.getParams());
return;
}
log.warn("[handleCustomFrame][未知 method: {}, frame: slaveId={}, FC={}, customData={}]",
method, frame.getSlaveId(), frame.getFunctionCode(), frame.getCustomData());
} catch (ServiceException e) {
// 已知业务异常,返回对应的错误码和错误信息
sendCustomResponse(socket, frame, frameFormat, method, e.getCode(), e.getMessage());
} catch (IllegalArgumentException e) {
// 参数校验异常,返回 400 错误
sendCustomResponse(socket, frame, frameFormat, method, BAD_REQUEST.getCode(), e.getMessage());
} catch (Exception e) {
// 其他未知异常,返回 500 错误
log.error("[handleCustomFrame][解析自定义 FC 数据失败, frame: slaveId={}, FC={}, customData={}]",
frame.getSlaveId(), frame.getFunctionCode(), frame.getCustomData(), e);
sendCustomResponse(socket, frame, frameFormat, method,
INTERNAL_SERVER_ERROR.getCode(), INTERNAL_SERVER_ERROR.getMsg());
}
}
/**
* 处理认证请求
*/
@SuppressWarnings("DataFlowIssue")
private void handleAuth(NetSocket socket, IotModbusFrame frame, IotModbusFrameFormatEnum frameFormat, Object params) {
// 1. 解析认证参数
IotDeviceAuthReqDTO request = JsonUtils.convertObject(params, IotDeviceAuthReqDTO.class);
Assert.notNull(request, "认证参数不能为空");
Assert.notBlank(request.getUsername(), "username 不能为空");
Assert.notBlank(request.getPassword(), "password 不能为空");
// 特殊:考虑到 modbus 消息体积较小,默认 clientId 传递空串
if (StrUtil.isBlank(request.getClientId())) {
request.setClientId(IotDeviceAuthUtils.buildClientIdFromUsername(request.getUsername()));
}
Assert.notBlank(request.getClientId(), "clientId 不能为空");
// 2.1 调用认证 API
CommonResult