fix:优化物联设备mqtt订阅
parent
0e5d780de8
commit
566acc12eb
@ -0,0 +1,30 @@
|
|||||||
|
package cn.iocoder.yudao.module.iot.framework.mqtt.config;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.eclipse.paho.client.mqttv3.MqttClient;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.annotation.PreDestroy;
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class MqttClientShutdown {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private MqttClient mqttClient;
|
||||||
|
|
||||||
|
@PreDestroy
|
||||||
|
public void shutdown() {
|
||||||
|
try {
|
||||||
|
if (mqttClient.isConnected()) {
|
||||||
|
mqttClient.disconnectForcibly(1000, 1000);
|
||||||
|
}
|
||||||
|
mqttClient.close();
|
||||||
|
log.info("MQTT客户端已关闭");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("MQTT客户端关闭失败: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,164 @@
|
|||||||
|
package cn.iocoder.yudao.module.iot.framework.mqtt.consumer;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.module.iot.controller.admin.devicemodelrules.vo.PointRulesRespVO;
|
||||||
|
import cn.iocoder.yudao.module.iot.dal.dataobject.device.DeviceDO;
|
||||||
|
import cn.iocoder.yudao.module.iot.dal.dataobject.devicecontactmodel.DeviceContactModelDO;
|
||||||
|
import cn.iocoder.yudao.module.iot.dal.dataobject.devicepointrules.DevicePointRulesDO;
|
||||||
|
import cn.iocoder.yudao.module.iot.dal.mysql.device.DeviceMapper;
|
||||||
|
import cn.iocoder.yudao.module.iot.dal.mysql.devicecontactmodel.DeviceContactModelMapper;
|
||||||
|
import cn.iocoder.yudao.module.iot.dal.mysql.devicepointrules.DevicePointRulesMapper;
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class IotMqttRuntimeCache {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
@Lazy
|
||||||
|
private DeviceMapper deviceMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private DeviceContactModelMapper deviceContactModelMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private DevicePointRulesMapper devicePointRulesMapper;
|
||||||
|
|
||||||
|
private final ConcurrentMap<String, Optional<DeviceDO>> deviceByTopic = new ConcurrentHashMap<>();
|
||||||
|
private final ConcurrentMap<Long, List<DeviceContactModelDO>> pointsByDeviceId = new ConcurrentHashMap<>();
|
||||||
|
private final ConcurrentMap<Long, Map<String, List<CachedPointRule>>> pointRulesByDeviceId = new ConcurrentHashMap<>();
|
||||||
|
private final ConcurrentMap<Long, Optional<DevicePointRulesDO>> countRuleByDeviceId = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
public DeviceDO getDeviceByTopic(String topic) {
|
||||||
|
if (StringUtils.isBlank(topic)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return deviceByTopic.computeIfAbsent(topic, this::loadDeviceByTopic).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<DeviceContactModelDO> getDevicePoints(Long deviceId) {
|
||||||
|
if (deviceId == null) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
return pointsByDeviceId.computeIfAbsent(deviceId, this::loadDevicePoints);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<CachedPointRule> getPointRules(Long deviceId, String attributeCode) {
|
||||||
|
if (deviceId == null || StringUtils.isBlank(attributeCode)) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
return pointRulesByDeviceId.computeIfAbsent(deviceId, this::loadPointRulesByAttributeCode)
|
||||||
|
.getOrDefault(attributeCode, Collections.emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public DevicePointRulesDO getLatestCountRule(Long deviceId) {
|
||||||
|
if (deviceId == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return countRuleByDeviceId.computeIfAbsent(deviceId, this::loadLatestCountRule).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void refreshDevice(Long deviceId) {
|
||||||
|
if (deviceId == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pointsByDeviceId.remove(deviceId);
|
||||||
|
pointRulesByDeviceId.remove(deviceId);
|
||||||
|
countRuleByDeviceId.remove(deviceId);
|
||||||
|
deviceByTopic.entrySet().removeIf(entry -> entry.getValue().map(device -> deviceId.equals(device.getId())).orElse(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void refreshTopic(String topic) {
|
||||||
|
if (StringUtils.isNotBlank(topic)) {
|
||||||
|
deviceByTopic.remove(topic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearAll() {
|
||||||
|
deviceByTopic.clear();
|
||||||
|
pointsByDeviceId.clear();
|
||||||
|
pointRulesByDeviceId.clear();
|
||||||
|
countRuleByDeviceId.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<DeviceDO> loadDeviceByTopic(String topic) {
|
||||||
|
return Optional.ofNullable(deviceMapper.selectOne(Wrappers.<DeviceDO>lambdaQuery()
|
||||||
|
.eq(DeviceDO::getTopic, topic)
|
||||||
|
.last("LIMIT 1")));
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<DeviceContactModelDO> loadDevicePoints(Long deviceId) {
|
||||||
|
List<DeviceContactModelDO> points = deviceContactModelMapper.selectList(Wrappers.<DeviceContactModelDO>lambdaQuery()
|
||||||
|
.eq(DeviceContactModelDO::getDeviceId, deviceId));
|
||||||
|
if (CollectionUtils.isEmpty(points)) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableList(new ArrayList<>(points));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, List<CachedPointRule>> loadPointRulesByAttributeCode(Long deviceId) {
|
||||||
|
List<DevicePointRulesDO> rules = devicePointRulesMapper.selectList(Wrappers.<DevicePointRulesDO>lambdaQuery()
|
||||||
|
.eq(DevicePointRulesDO::getDeviceId, deviceId)
|
||||||
|
.orderByDesc(DevicePointRulesDO::getCreateTime));
|
||||||
|
if (CollectionUtils.isEmpty(rules)) {
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
List<CachedPointRule> parsedRules = new ArrayList<>();
|
||||||
|
for (DevicePointRulesDO rule : rules) {
|
||||||
|
if (StringUtils.isBlank(rule.getFieldRule())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
List<PointRulesRespVO> pointRules = JSON.parseArray(rule.getFieldRule(), PointRulesRespVO.class);
|
||||||
|
if (CollectionUtils.isEmpty(pointRules)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (PointRulesRespVO pointRule : pointRules) {
|
||||||
|
if (StringUtils.isBlank(pointRule.getCode())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
parsedRules.add(new CachedPointRule(rule, pointRule));
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("MQTT规则解析失败 deviceId={}, ruleId={}, reason={}", deviceId, rule.getId(), e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (parsedRules.isEmpty()) {
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
return parsedRules.stream().collect(Collectors.groupingBy(rule -> rule.getPointRule().getCode()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<DevicePointRulesDO> loadLatestCountRule(Long deviceId) {
|
||||||
|
return Optional.ofNullable(devicePointRulesMapper.selectOne(Wrappers.<DevicePointRulesDO>lambdaQuery()
|
||||||
|
.eq(DevicePointRulesDO::getDeviceId, deviceId)
|
||||||
|
.eq(DevicePointRulesDO::getIdentifier, "COUNT")
|
||||||
|
.orderByDesc(DevicePointRulesDO::getUpdateTime)
|
||||||
|
.last("LIMIT 1")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public static class CachedPointRule {
|
||||||
|
private final DevicePointRulesDO rule;
|
||||||
|
private final PointRulesRespVO pointRule;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue