|
|
@@ -1,12 +1,18 @@
|
|
|
package com.yunfeiyun.agmp.iots.core.manager;
|
|
|
|
|
|
import cn.hutool.core.util.ArrayUtil;
|
|
|
+import com.alibaba.fastjson2.JSON;
|
|
|
+import com.alibaba.fastjson2.JSONArray;
|
|
|
import com.alibaba.fastjson2.JSONObject;
|
|
|
import com.yunfeiyun.agmp.common.constant.ErrorCode;
|
|
|
-import com.yunfeiyun.agmp.common.utils.DateUtils;
|
|
|
import com.yunfeiyun.agmp.iot.common.constant.IotErrorCode;
|
|
|
+import com.yunfeiyun.agmp.iot.common.constant.devicetype.IotDeviceDictEnum;
|
|
|
+import com.yunfeiyun.agmp.iot.common.constant.devicetype.IotDeviceTypeLv1Enum;
|
|
|
import com.yunfeiyun.agmp.iot.common.domain.IotDevice;
|
|
|
+import com.yunfeiyun.agmp.iot.common.domain.IotDeviceconn;
|
|
|
+import com.yunfeiyun.agmp.iot.common.enums.IotDeviceconnTypeEnum;
|
|
|
import com.yunfeiyun.agmp.iot.common.exception.IotBizException;
|
|
|
+import com.yunfeiyun.agmp.iot.common.service.DeviceconnCacheService;
|
|
|
import com.yunfeiyun.agmp.iots.common.modal.IotDeviceconnResVo;
|
|
|
import com.yunfeiyun.agmp.iots.core.mqtt.DeviceTopicService;
|
|
|
import com.yunfeiyun.agmp.iots.core.mqtt.modal.MqttTopicValue;
|
|
|
@@ -23,6 +29,7 @@ import org.springframework.stereotype.Component;
|
|
|
|
|
|
import javax.annotation.Resource;
|
|
|
import java.util.*;
|
|
|
+import java.util.concurrent.ConcurrentHashMap;
|
|
|
|
|
|
|
|
|
/**
|
|
|
@@ -41,12 +48,14 @@ public class MqttManager {
|
|
|
|
|
|
@Autowired
|
|
|
IIotDeviceService iIotDeviceService;
|
|
|
+ @Autowired
|
|
|
+ private DeviceconnCacheService deviceconnCacheService;
|
|
|
|
|
|
|
|
|
/**
|
|
|
* 实现类名称-->mqtt
|
|
|
*/
|
|
|
- private Map<String, MqttCore> serviceMqttMap = new HashMap<>();
|
|
|
+ private Map<String, MqttCore> mqttCoreMap = new HashMap<>();
|
|
|
|
|
|
/**
|
|
|
* spring 自动注入
|
|
|
@@ -76,67 +85,56 @@ public class MqttManager {
|
|
|
*/
|
|
|
public void buildMqttConnection(IotDeviceconnResVo iotDeviceconnResVo, JSONObject jsonConfig){
|
|
|
try {
|
|
|
- log.info("【开始构建MQTT连接】 devconnId:{} ,devconnName: {}, tosDeviceTypeName:{}, jsonConfig: {}", iotDeviceconnResVo.getDevconnBid(), iotDeviceconnResVo.getDevconnName(), iotDeviceconnResVo.getDevtypeBid(), jsonConfig);
|
|
|
-
|
|
|
// 构建配置
|
|
|
MqttConfig cfgYf = buildMqttConfig(iotDeviceconnResVo, jsonConfig);
|
|
|
- //服务类Bean名称
|
|
|
- String serviceName = cfgYf.getServiceName();
|
|
|
- // 之前干嘛的待定,
|
|
|
- String type = cfgYf.getType();
|
|
|
- //厂家名称
|
|
|
- String firmName = cfgYf.getFirmName();
|
|
|
- log.info("【初始化厂商配置】 {} {} 开始", firmName, type);
|
|
|
- // 创建MqttCore实例
|
|
|
- MqttCore mqttCore = new MqttCore();
|
|
|
-
|
|
|
- // 查询topics【需要实现:重新更改获取该型号下的设备】
|
|
|
- String connectionId = iotDeviceconnResVo.getDevconnBid();
|
|
|
- mqttCore.setConnectionId(connectionId);
|
|
|
+ // 生成Mqtt连接标识并写入缓存
|
|
|
+ String connectionId = generateMqttConnectionId(iotDeviceconnResVo,jsonConfig);
|
|
|
+ log.info("【开始构建MQTT连接】 devconnId:{} ,devconnName: {}, tosDeviceTypeName:{}, jsonConfig: {}", iotDeviceconnResVo.getDevconnBid(), iotDeviceconnResVo.getDevconnName(), iotDeviceconnResVo.getDevtypeBid(), jsonConfig);
|
|
|
// 构建topic
|
|
|
- List<IotDevice> devices = deviceTopicService.getDevicesByConectionId(connectionId);
|
|
|
- log.info("【创建MqttCore实例】 mqttCore: {} connectionId:{} devSize:{}", mqttCore, connectionId, devices.size());
|
|
|
+ List<IotDevice> devices = deviceTopicService.getDevicesByDevConnBid(iotDeviceconnResVo.getDevconnBid());
|
|
|
List<MqttTopicValue> mqttTopicValues = new ArrayList<>();
|
|
|
for (IotDevice iotDevice : devices) {
|
|
|
// 根据设备code获取topics
|
|
|
- String[] topics = deviceTopicService.getTopic(serviceName, iotDevice.getDevCode());
|
|
|
+ String[] topics = deviceTopicService.getTopic(cfgYf.getServiceName(), iotDevice.getDevCode());
|
|
|
if (topics != null) {
|
|
|
for (String s : topics) {
|
|
|
MqttTopicValue mqttTopicValue = new MqttTopicValue();
|
|
|
mqttTopicValue.setDevCode(iotDevice.getDevCode());
|
|
|
+ mqttTopicValue.setServiceName(IotDeviceDictEnum.findServiceNameByDevTypeBid(iotDevice.getDevtypeBid()));
|
|
|
mqttTopicValue.setDevId(iotDevice.getDevBid());
|
|
|
mqttTopicValue.setTopic(s);
|
|
|
mqttTopicValues.add(mqttTopicValue);
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
}
|
|
|
-
|
|
|
String[] topics = new String[mqttTopicValues.size()];
|
|
|
- if (mqttTopicValues != null && !mqttTopicValues.isEmpty()) {
|
|
|
+ if (!mqttTopicValues.isEmpty()) {
|
|
|
for (int i = 0; i < mqttTopicValues.size(); i++) {
|
|
|
topics[i] = mqttTopicValues.get(i).getTopic();
|
|
|
}
|
|
|
+ }
|
|
|
+ MqttCore mqttCore = mqttCoreMap.get(connectionId);
|
|
|
+ if(null != mqttCore){
|
|
|
+ // MqttCore已经存在
|
|
|
+ mqttCore.subscribe(topics,cfgYf);
|
|
|
+ mqttCore.bindTopicToDeviceId(mqttTopicValues);
|
|
|
+ }else {
|
|
|
+ // 创建新的mqttCore
|
|
|
+ mqttCore = new MqttCore();
|
|
|
+ mqttCore.setConnectionId(connectionId);
|
|
|
+ mqttCore.bindTopicToDeviceId(mqttTopicValues);
|
|
|
cfgYf.setSubTopic(topics);
|
|
|
+ // 构建MqttCore
|
|
|
+ mqttCore.buildMqttCore(cfgYf);
|
|
|
+ addConnectionMap(connectionId, mqttCore);
|
|
|
}
|
|
|
- mqttCore.bindTopicToDeviceId(mqttTopicValues);
|
|
|
- cfgYf.setServiceName(serviceName);
|
|
|
- log.info("【初始化厂商加载配置】 {} {} {}", firmName, type, Arrays.toString(topics));
|
|
|
-
|
|
|
- // 处理连接ID的逻辑 IP+port+name
|
|
|
- log.info("【添加公共连接】 connectionId: {}", connectionId);
|
|
|
- addConnectionMap(connectionId, mqttCore);
|
|
|
-
|
|
|
- // 构建MqttCore
|
|
|
- mqttCore.buildMqttCore(cfgYf);
|
|
|
- log.info("【成功构建MqttCore】 mqttCore: {}", mqttCore);
|
|
|
- log.info("【完成构建MQTT连接】");
|
|
|
} catch (Exception e) {
|
|
|
log.error("【构建MqttCore失败】 异常信息: {} ,{}", e.getMessage(), e);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
+
|
|
|
/**
|
|
|
* 【链接管理-删除】
|
|
|
* 删除mqtt链接,底层统一调这个
|
|
|
@@ -144,11 +142,20 @@ public class MqttManager {
|
|
|
* @param connectionId
|
|
|
* @throws MqttException
|
|
|
*/
|
|
|
- public void deleteMqttConnection(String connectionId) throws MqttException {
|
|
|
- log.info("【开始删除MQTT连接】 connectionId: {}", connectionId);
|
|
|
-
|
|
|
+ public void deleteMqttConnection(IotDeviceconnResVo iotDeviceconnResVo) throws MqttException {
|
|
|
+ // 首先获取连接标识
|
|
|
+ String connectionId = deviceconnCacheService.getMqttConnectIdByDeviceConnBid(iotDeviceconnResVo.getDevconnBid());
|
|
|
+ // 删除相关缓存
|
|
|
+ deviceconnCacheService.deleteMqttConnectionId(iotDeviceconnResVo.getDevconnBid());
|
|
|
+ log.info("【删除MQTT连接信息】 获取连接标识 connectionId: {}", connectionId);
|
|
|
+ // 运行到这一步的时候,设备的连接信息都已解绑,相关订阅都已取消
|
|
|
+ if(deviceconnCacheService.mqttConnectionIdHasLink(connectionId)){
|
|
|
+ // 连接标识下仍有其他连接再使用该MqttCore
|
|
|
+ log.info("【删除MQTT连接信息】 连接仍在使用,停止释放连接");
|
|
|
+ return;
|
|
|
+ }
|
|
|
// 从map中获取MqttCore
|
|
|
- MqttCore mqttCore = serviceMqttMap.get(connectionId);
|
|
|
+ MqttCore mqttCore = mqttCoreMap.get(connectionId);
|
|
|
if (mqttCore != null) {
|
|
|
try {
|
|
|
// 尝试关闭MqttCore
|
|
|
@@ -165,7 +172,7 @@ public class MqttManager {
|
|
|
throw e;
|
|
|
} finally {
|
|
|
// 从map中移除该MqttCore
|
|
|
- serviceMqttMap.remove(connectionId);
|
|
|
+ mqttCoreMap.remove(connectionId);
|
|
|
log.info("【从映射中移除MQTT连接】 connectionId: {}", connectionId);
|
|
|
}
|
|
|
} else {
|
|
|
@@ -173,7 +180,6 @@ public class MqttManager {
|
|
|
log.warn("【尝试关闭不存在的MQTT连接】 connectionId: {}", connectionId);
|
|
|
throw new IotBizException(IotErrorCode.FAILURE.getCode(), "关闭失败:" + connectionId + "暂无对应对象");
|
|
|
}
|
|
|
-
|
|
|
log.info("【完成删除MQTT连接】 connectionId: {}", connectionId);
|
|
|
}
|
|
|
|
|
|
@@ -228,16 +234,16 @@ public class MqttManager {
|
|
|
private void putConnection(String connectionId, MqttCore mqttCore) {
|
|
|
log.info("【开始维护连接】 connectionId: {}, mqttCore: {}", connectionId, mqttCore);
|
|
|
|
|
|
- if (!serviceMqttMap.containsKey(connectionId)) {
|
|
|
+ if (!mqttCoreMap.containsKey(connectionId)) {
|
|
|
// 如果 connectionId 不存在于 map 中,则添加新的连接
|
|
|
- serviceMqttMap.put(connectionId, mqttCore);
|
|
|
+ mqttCoreMap.put(connectionId, mqttCore);
|
|
|
log.info("【新增连接】 connectionId: {}, mqttCore: {}", connectionId, mqttCore);
|
|
|
} else {
|
|
|
// 如果 connectionId 已存在于 map 中,则记录重复配置的日志
|
|
|
log.info("【重复配置】 connectionId: {}, mqttCore: {}", connectionId, mqttCore);
|
|
|
}
|
|
|
|
|
|
- log.info("【完成维护连接】 connectionId: {}, 当前连接数: {}", connectionId, serviceMqttMap.size());
|
|
|
+ log.info("【完成维护连接】 connectionId: {}, 当前连接数: {}", connectionId, mqttCoreMap.size());
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -249,9 +255,9 @@ public class MqttManager {
|
|
|
* @return
|
|
|
*/
|
|
|
@Deprecated
|
|
|
- public MqttPublisher getPublisherByService(String connectionId) {
|
|
|
+ public MqttPublisher getPublisherByDevConnBid(String connectionId) {
|
|
|
throwDeprecatedMethod("getPublisherByService废弃,发布消息直接通过提供connection 调用 publishMsg");
|
|
|
- MqttCore mqttCore = serviceMqttMap.get(connectionId);
|
|
|
+ MqttCore mqttCore = mqttCoreMap.get(connectionId);
|
|
|
if (mqttCore == null) {
|
|
|
throw new IotBizException(IotErrorCode.FAILURE.getCode(), connectionId + "暂无对应处理器");
|
|
|
}
|
|
|
@@ -262,20 +268,19 @@ public class MqttManager {
|
|
|
* 【操作-发布消息】
|
|
|
* 发布消息
|
|
|
*
|
|
|
- * @param connectionId 链接id
|
|
|
+ * @param devConnBid 设备链接id
|
|
|
* @param topic
|
|
|
* @param message
|
|
|
* @throws MqttException
|
|
|
*/
|
|
|
- public void publishMsg(String connectionId, String topic, String message) throws MqttException {
|
|
|
+ public void publishMsg(String devConnBid, String topic, String message) throws MqttException {
|
|
|
+ String connectionId = deviceconnCacheService.getMqttConnectIdByDeviceConnBid(devConnBid);
|
|
|
log.info("【开始发布消息】 connectionId: {}, topic: {}, message: {}", connectionId, topic, message);
|
|
|
-
|
|
|
- MqttCore mqttCore = serviceMqttMap.get(connectionId);
|
|
|
+ MqttCore mqttCore = mqttCoreMap.get(connectionId);
|
|
|
if (mqttCore == null) {
|
|
|
log.error("【发布消息失败】 connectionId: {} 暂无对应处理器", connectionId);
|
|
|
throw new IotBizException(IotErrorCode.FAILURE.getCode(), connectionId + "暂无对应处理器");
|
|
|
}
|
|
|
-
|
|
|
try {
|
|
|
mqttCore.publish(topic, message);
|
|
|
log.info("【消息发布成功】 connectionId: {}, topic: {}, message: {}", connectionId, topic, message);
|
|
|
@@ -348,7 +353,7 @@ public class MqttManager {
|
|
|
throw new IotBizException(IotErrorCode.FAILURE.getCode(), "【获取MQTT对象失败】:" + connectionId + "对应不存在");
|
|
|
}
|
|
|
|
|
|
- log.info("【获取MQTT核心类成功】 connectionId: {}, mqttCore: {}", connectionId, mqttCore);
|
|
|
+ log.info("【获取MQTT核心类成功】 connectionId: {}, mqttClientId: {}", connectionId, mqttCore.getClient().getClientId());
|
|
|
return mqttCore;
|
|
|
}
|
|
|
|
|
|
@@ -360,14 +365,14 @@ public class MqttManager {
|
|
|
* @return
|
|
|
*/
|
|
|
private MqttCore getMqttCore(String connectionId) {
|
|
|
- return serviceMqttMap.get(connectionId);
|
|
|
+ return mqttCoreMap.get(connectionId);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 打印已经加载的配置信息
|
|
|
*/
|
|
|
public void showConfig() {
|
|
|
- for (MqttCore mqttCore : serviceMqttMap.values()) {
|
|
|
+ for (MqttCore mqttCore : mqttCoreMap.values()) {
|
|
|
log.info("【MQTT】已经记载的配置 FirmBizName:{} ,FirmBizId-devType:{}, ServiceType:{}, ServiceName:{} ,SubTopic:{}", mqttCore.getFirmName(), mqttCore.getServiceName(), mqttCore.getServiceType(), mqttCore.getSubTopic());
|
|
|
}
|
|
|
}
|
|
|
@@ -390,19 +395,17 @@ public class MqttManager {
|
|
|
*/
|
|
|
public void topicBatchSubscribeDevices(String connectionId, String serviceName, List<MqttTopicValue> mqttTopicValues) throws MqttException {
|
|
|
log.info("【开始批量订阅】 connectionId: {}, serviceName: {}, deviceIds: {}", connectionId, serviceName, mqttTopicValues);
|
|
|
-
|
|
|
- // 获取批量订阅的主题
|
|
|
- String[] topics = new String[mqttTopicValues.size()];
|
|
|
- for (int i = 0; i < topics.length; i++) {
|
|
|
- topics[i] = mqttTopicValues.get(i).getTopic();
|
|
|
- }
|
|
|
- log.info("【获取批量订阅主题】 topics: {}", Arrays.toString(topics));
|
|
|
// 获取MqttCore实例
|
|
|
MqttCore mqttCore = getMqttCoreByConnectionId(connectionId);
|
|
|
- log.info("【获取MqttCore实例】 mqttCore: {}", mqttCore);
|
|
|
+ log.info("【获取MqttCore实例】 mqttClientId: {}", mqttCore.getClient().getClientId());
|
|
|
+ // 获取批量订阅的主题
|
|
|
+ String[] topics = new String[mqttTopicValues.size()];
|
|
|
// 执行订阅
|
|
|
try {
|
|
|
- mqttCore.subscribe(topics);
|
|
|
+ for (int i = 0; i < topics.length; i++) {
|
|
|
+ topics[i] = mqttTopicValues.get(i).getTopic();
|
|
|
+ mqttCore.getClient().subscribe(topics[i],0);
|
|
|
+ }
|
|
|
mqttCore.bindTopicToDeviceId(mqttTopicValues);
|
|
|
log.info("【批量订阅成功】 connectionId: {}, serviceName: {}, topics: {}, serviceType: {}", connectionId, serviceName, Arrays.toString(topics), mqttCore.getServiceType());
|
|
|
} catch (MqttException e) {
|
|
|
@@ -496,4 +499,55 @@ public class MqttManager {
|
|
|
public String getDevIdByTopic(String connectionId, String topic) {
|
|
|
return getMqttCore(connectionId).getDevIdByTopic(topic);
|
|
|
}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取连接信息,如果缓存中不存在的话,会根据jsonConfig进行解析
|
|
|
+ * @param iotDeviceconn 连接信息
|
|
|
+ * @param jsonConfig 连接信息配置
|
|
|
+ **/
|
|
|
+ public String getMqttConnectionId(IotDeviceconn iotDeviceconn,JSONObject jsonConfig){
|
|
|
+ if(null == iotDeviceconn){
|
|
|
+ throw new IotBizException(ErrorCode.INVALID_PARAMETER.getCode(),"连接信息为空");
|
|
|
+ }
|
|
|
+ return deviceconnCacheService.getMqttConnectIdByDeviceConnBid(iotDeviceconn.getDevconnBid());
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ *
|
|
|
+ * */
|
|
|
+ public String generateMqttConnectionId(IotDeviceconn iotDeviceconn,JSONObject jsonConfig){
|
|
|
+ // 使用mqtt连接的ip+port+user
|
|
|
+ String ip = jsonConfig.getString("ip");
|
|
|
+ if(null != ip){
|
|
|
+ String[] ipItem = ip.split("\\.");
|
|
|
+ StringBuilder ipFormat = new StringBuilder();
|
|
|
+ for(String str : ipItem){
|
|
|
+ ipFormat.append(String.format("%03d",Long.parseLong(str)));
|
|
|
+ }
|
|
|
+ ip = ipFormat.toString();
|
|
|
+ }
|
|
|
+ String connectionId = ip+jsonConfig.getString("port")+jsonConfig.getString("username");
|
|
|
+ if(!IotDeviceconnTypeEnum.COMMON.getCode().equals(iotDeviceconn.getDevconnType())){
|
|
|
+ // 非通用连接
|
|
|
+ // 非通用连接信息会拼装租户标识
|
|
|
+ connectionId = connectionId + iotDeviceconn.getTid();
|
|
|
+ }
|
|
|
+ deviceconnCacheService.setMqttConnectionIdByConnBid(iotDeviceconn.getDevconnBid(),connectionId);
|
|
|
+ return connectionId;
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 获取连接信息,如果缓存中不存在的话,会根据jsonConfig进行解析
|
|
|
+ * @param iotDeviceconn 连接信息
|
|
|
+ **/
|
|
|
+ public String getMqttConnectionId(IotDeviceconn iotDeviceconn){
|
|
|
+ JSONArray jsonArray = JSONArray.parseArray(iotDeviceconn.getDevconnConfig());
|
|
|
+ for(int i = 0;i<jsonArray.size();i++){
|
|
|
+ JSONObject configObject = jsonArray.getJSONObject(i);
|
|
|
+ if("mqtt".equals(configObject.getString("type"))){
|
|
|
+ return getMqttConnectionId(iotDeviceconn, configObject);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
}
|