第一次提交

This commit is contained in:
wyw
2024-08-08 00:31:26 +08:00
commit c202e2b63d
1819 changed files with 221890 additions and 0 deletions

View File

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>fastbee-gateway</artifactId>
<groupId>com.fastbee</groupId>
<version>3.8.5</version>
</parent>
<artifactId>fastbee-mq</artifactId>
<dependencies>
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>com.fastbee</groupId>
<artifactId>fastbee-protocol-base</artifactId>
</dependency>
<dependency>
<groupId>com.fastbee</groupId>
<artifactId>fastbee-protocol-collect</artifactId>
</dependency>
<dependency>
<groupId>com.fastbee</groupId>
<artifactId>sip-server</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
<dependency>
<groupId>com.yomahub</groupId>
<artifactId>liteflow-core</artifactId>
<version>${liteflow.version}</version>
</dependency>
<dependency>
<groupId>com.yomahub</groupId>
<artifactId>liteflow-spring</artifactId>
<version>${liteflow.version}</version>
</dependency>
<dependency>
<groupId>com.fastbee</groupId>
<artifactId>fastbee-notify-core</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,36 @@
package com.fastbee.mq.config;
import com.fastbee.common.constant.FastBeeConstant;
import com.fastbee.mq.redischannel.service.RedisPublishServiceImpl;
import com.fastbee.mq.rocketmq.service.RocketMqPublishServiceImpl;
import com.fastbee.mq.service.IMessagePublishService;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* mq集群配置
* @author gsb
* @date 2022/10/10 8:27
*/
@Configuration
//是否开启集群,默认不开启
@ConditionalOnExpression("${cluster.enable:false}")
public class MqConfig {
@Bean
@ConditionalOnProperty(prefix ="cluster", name = "type" ,havingValue = FastBeeConstant.MQTT.REDIS_CHANNEL,matchIfMissing = true)
public IMessagePublishService redisChannelPublish(){
return new RedisPublishServiceImpl();
}
//@Bean
@ConditionalOnProperty(prefix ="cluster", name = "type",havingValue = FastBeeConstant.MQTT.ROCKET_MQ)
public IMessagePublishService rocketMqPublish() {
return new RocketMqPublishServiceImpl();
}
}

View File

@ -0,0 +1,45 @@
package com.fastbee.mq.model;
import com.fastbee.common.core.thingsModel.ThingsModelSimpleItem;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
/**
* 上报数据模型bo
* @author bill
*/
@Data
@Accessors(chain = true)
public class ReportDataBo {
/**产品id*/
private Long productId;
/**设备编号*/
private String serialNumber;
/**上报消息*/
private String message;
/**上报的数据*/
private List<ThingsModelSimpleItem> dataList;
/**设备影子*/
private boolean isShadow;
/**
* 物模型类型
* 1=属性2=功能3=事件4=设备升级5=设备上线6=设备下线
*/
private int type;
/**是否执行规则引擎*/
private boolean isRuleEngine;
/**从机编号*/
private Integer slaveId;
/**
* 上报原数据包
*/
private Object sources;
private Long userId;
private String userName;
private String deviceName;
}

View File

@ -0,0 +1,62 @@
package com.fastbee.mq.redischannel.config;
import com.fastbee.common.constant.FastBeeConstant;
import com.fastbee.mq.redischannel.consumer.RedisChannelConsume;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
/**
* redisChannel配置
* @author gsb
* @date 2022/10/10 8:57
*/
@Configuration
@EnableCaching
@Slf4j
public class RedisConsumeConfig {
@Bean
@ConditionalOnProperty(prefix ="cluster", name = "type" ,havingValue = FastBeeConstant.MQTT.REDIS_CHANNEL,matchIfMissing = true)
RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory,
MessageListenerAdapter listenerAdapter) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.addMessageListener(listenerAdapter, new PatternTopic(FastBeeConstant.CHANNEL.PROP_READ));
container.addMessageListener(listenerAdapter, new PatternTopic(FastBeeConstant.CHANNEL.FUNCTION_INVOKE));
/*推送消息不需要关联ClientId只需要处理推送数据 即使本地服务不存在该客户端*/
//container.addMessageListener(listenerAdapter, new PatternTopic(FastBeeConstant.CHANNEL.PUBLISH));
container.addMessageListener(listenerAdapter,new PatternTopic(FastBeeConstant.CHANNEL.UPGRADE));
//container.addMessageListener(listenerAdapter,new PatternTopic(FastBeeConstant.CHANNEL.OTHER));
//container.addMessageListener(listenerAdapter, new PatternTopic(FastBeeConstant.CHANNEL.PUBLISH_ACK));
//container.addMessageListener(listenerAdapter, new PatternTopic(FastBeeConstant.CHANNEL.PUB_REC));
//container.addMessageListener(listenerAdapter, new PatternTopic(FastBeeConstant.CHANNEL.PUB_REL));
//container.addMessageListener(listenerAdapter, new PatternTopic(FastBeeConstant.CHANNEL.PUB_COMP));
return container;
}
/**配置消息监听类 默认监听方法onMessage*/
@Bean
@ConditionalOnProperty(prefix ="cluster", name = "type" ,havingValue = FastBeeConstant.MQTT.REDIS_CHANNEL,matchIfMissing = true)
MessageListenerAdapter listenerAdapter(RedisChannelConsume consume){
return new MessageListenerAdapter(consume,"onMessage");
}
@Bean
@ConditionalOnProperty(prefix ="cluster", name = "type" ,havingValue = FastBeeConstant.MQTT.REDIS_CHANNEL,matchIfMissing = true)
StringRedisTemplate template(RedisConnectionFactory connectionFactory){
return new StringRedisTemplate(connectionFactory);
}
}

View File

@ -0,0 +1,34 @@
package com.fastbee.mq.redischannel.consumer;
import com.fastbee.common.constant.FastBeeConstant;
import com.fastbee.common.core.mq.DeviceReportBo;
import com.fastbee.mq.service.impl.DeviceOtherMsgHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* @author gsb
* @date 2023/2/27 14:33
*/
@Slf4j
@Component
public class DeviceOtherMsgConsumer {
@Resource
private DeviceOtherMsgHandler otherMsgHandler;
@Async(FastBeeConstant.TASK.DEVICE_OTHER_TASK)
public void consume(DeviceReportBo bo){
try {
//处理emq订阅的非 property/post 属性上报的消息 ,因为其他消息量小,放在一起处理
otherMsgHandler.messageHandler(bo);
}catch (Exception e){
log.error("=>设备其他消息处理出错",e);
}
}
}

View File

@ -0,0 +1,106 @@
package com.fastbee.mq.redischannel.consumer;
import com.fastbee.common.constant.FastBeeConstant;
import com.fastbee.common.core.mq.message.DeviceDownMessage;
import com.fastbee.common.core.mq.message.ModbusPollMsg;
import com.fastbee.common.core.mq.message.PropRead;
import com.fastbee.common.core.protocol.Message;
import com.fastbee.common.core.redis.RedisCache;
import com.fastbee.common.core.redis.RedisKeyBuilder;
import com.fastbee.common.enums.DeviceStatus;
import com.fastbee.common.enums.ServerType;
import com.fastbee.common.enums.TopicType;
import com.fastbee.common.utils.DateUtils;
import com.fastbee.common.utils.gateway.mq.TopicsUtils;
import com.fastbee.common.utils.gateway.protocol.ByteUtils;
import com.fastbee.common.utils.modbus.ModbusUtils;
import com.fastbee.iot.model.DeviceStatusVO;
import com.fastbee.iot.service.IDeviceService;
import com.fastbee.iot.service.IFunctionLogService;
import com.fastbee.mq.service.impl.MessageManager;
import com.fastbee.mqttclient.PubMqttClient;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 平台定时批量获取设备属性(或单个获取)
*
* @author bill
*/
@Slf4j
@Component
public class DevicePropFetchConsumer {
@Autowired
private PubMqttClient pubMqttClient;
@Autowired
private RedisCache redisCache;
@Resource
private MessageManager messageManager;
@Resource
private TopicsUtils topicsUtils;
@Resource
private IFunctionLogService functionLogService;
//锁集合
private final ConcurrentHashMap<String, Lock> taskLocks = new ConcurrentHashMap<>();
@Async(FastBeeConstant.TASK.DEVICE_FETCH_PROP_TASK)
public void consume(ModbusPollMsg task) {
String serialNumber = task.getSerialNumber();
//获取一个线程锁
Lock lock = taskLocks.computeIfAbsent(serialNumber, k -> new ReentrantLock());
//阻塞直到获取到锁
lock.lock();
try {
Long productId = task.getProductId();
List<String> commandList = task.getCommandList();
ServerType serverType = ServerType.explain(task.getTransport());
String topic = topicsUtils.buildTopic(productId, serialNumber, TopicType.FUNCTION_GET);
for (String command : commandList) {
String cacheKey = RedisKeyBuilder.buildModbusRuntimeCacheKey(serialNumber);
redisCache.zSetAdd(cacheKey, command, DateUtils.getTimestampSeconds());
switch (serverType) {
//通过mqtt内部客户端 下发指令
case MQTT:
publish(topic, ByteBufUtil.decodeHexDump(command));
log.info("=>MQTT-线程=[{}],轮询指令:[{}],主题:[{}]", Thread.currentThread().getName(), command, topic);
break;
// 下发TCP客户端
case TCP:
Message msg = new Message();
msg.setClientId(serialNumber);
msg.setPayload(Unpooled.wrappedBuffer(ByteBufUtil.decodeHexDump(command)));
messageManager.requestR(serialNumber, msg, Message.class);
log.info("=>TCP-线程=[{}],轮询指令:[{}]", Thread.currentThread().getName(), command);
break;
}
//指令间隔时间
Thread.sleep(1000);
}
} catch (Exception e) {
log.error("线程错误e", e);
} finally {
lock.unlock();
}
}
public void publish(String topic, byte[] pushMessage) {
redisCache.incr2(FastBeeConstant.REDIS.MESSAGE_SEND_TOTAL, -1L);
redisCache.incr2(FastBeeConstant.REDIS.MESSAGE_SEND_TODAY, 60 * 60 * 24);
pubMqttClient.publish(pushMessage, topic, false, 0);
}
}

View File

@ -0,0 +1,44 @@
package com.fastbee.mq.redischannel.consumer;
import com.fastbee.common.constant.FastBeeConstant;
import com.fastbee.common.core.mq.DeviceReportBo;
import com.fastbee.mq.service.IDeviceReportMessageService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* 设备消息回复
*
* @author gsb
* @date 2022/10/10 11:12
*/
@Component
@Slf4j
public class DeviceReplyMsgConsumer {
@Resource
private IDeviceReportMessageService deviceReportMessageService;
/*设备回调消息统一用上报model*/
@Async(FastBeeConstant.TASK.DEVICE_REPLY_MESSAGE_TASK)
public void consume(DeviceReportBo bo) {
try {
String topicName = bo.getTopicName();
if (topicName.endsWith(FastBeeConstant.TOPIC.MSG_REPLY)) {
//普通设备回复消息
deviceReportMessageService.parseReplyMsg(bo);
} else if (topicName.endsWith(FastBeeConstant.TOPIC.UPGRADE_REPLY)) {
//OTA升级的回复消息
deviceReportMessageService.parseOTAUpdateReply(bo);
}
} catch (Exception e) {
log.error("=>设备回复消息消费异常", e);
}
}
}

View File

@ -0,0 +1,36 @@
package com.fastbee.mq.redischannel.consumer;
import com.fastbee.common.constant.FastBeeConstant;
import com.fastbee.common.core.mq.DeviceReportBo;
import com.fastbee.common.core.redis.RedisCache;
import com.fastbee.mq.service.IDeviceReportMessageService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
/**
* 设备上报消息处理
*
* @author bill
*/
@Slf4j
@Component
public class DeviceReportMsgConsumer {
@Autowired
private IDeviceReportMessageService reportMessageService;
@Async(FastBeeConstant.TASK.DEVICE_UP_MESSAGE_TASK)
public void consume(DeviceReportBo bo) {
try {
//处理数据解析
reportMessageService.parseReportMsg(bo);
} catch (Exception e) {
log.error("设备主动上报队列监听异常", e);
}
}
}

View File

@ -0,0 +1,128 @@
package com.fastbee.mq.redischannel.consumer;
import com.alibaba.fastjson2.JSONObject;
import com.fastbee.base.service.ISessionStore;
import com.fastbee.common.constant.FastBeeConstant;
import com.fastbee.common.core.device.DeviceAndProtocol;
import com.fastbee.common.core.mq.DeviceStatusBo;
import com.fastbee.common.core.mq.MQSendMessageBo;
import com.fastbee.common.core.thingsModel.ThingsModelSimpleItem;
import com.fastbee.common.enums.DeviceStatus;
import com.fastbee.common.enums.ThingsModelType;
import com.fastbee.iot.domain.Device;
import com.fastbee.iot.domain.Product;
import com.fastbee.iot.model.ThingsModels.ThingsModelShadow;
import com.fastbee.iot.service.IDeviceService;
import com.fastbee.iot.service.IProductService;
import com.fastbee.iot.cache.IDeviceCache;
import com.fastbee.iot.util.SnowflakeIdWorker;
import com.fastbee.mq.model.ReportDataBo;
import com.fastbee.mq.redischannel.producer.MessageProducer;
import com.fastbee.mq.service.IMqttMessagePublish;
import com.fastbee.mq.service.IRuleEngine;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.util.List;
/**
* 设备状态消息处理类
* @author gsb
* @date 2022/10/10 11:02
*/
@Slf4j
@Component
public class DeviceStatusConsumer {
@Autowired
private IDeviceCache deviceCache;
@Resource
private IRuleEngine ruleEngine;
@Resource
private IDeviceService deviceService;
@Resource
private IProductService productService;
@Resource
private ISessionStore sessionStore;
@Resource
private IMqttMessagePublish mqttMessagePublish;
@Value("${server.broker.enabled}")
private Boolean enabled;
private SnowflakeIdWorker snowflakeIdWorker = new SnowflakeIdWorker(2);
public synchronized void consume(DeviceStatusBo bo) {
try {
Device device = deviceService.selectDeviceBySerialNumber(bo.getSerialNumber());
Product product = productService.selectProductByProductId(device.getProductId());
if (enabled && product.getDeviceType() != 3) { //如果使用Netty版本 监控设备不走这里
boolean containsKey = sessionStore.containsKey(bo.getSerialNumber());
boolean isOnline = device.getStatus() == 3;
log.info("=>session{},数据库:{},更新状态:{}", containsKey, isOnline, bo.getStatus().getCode());
if (containsKey && !isOnline) {
//如果session存在但数据库状态不在线则以session为准
bo.setStatus(DeviceStatus.ONLINE);
}
if (!containsKey && isOnline) {
bo.setStatus(DeviceStatus.OFFLINE);
}
}
/*更新设备状态*/
deviceCache.updateDeviceStatusCache(bo, device);
//处理影子模式值
this.handlerShadow(device, bo.getStatus());
//设备上下线执行规则引擎
ReportDataBo dataBo = new ReportDataBo();
dataBo.setRuleEngine(true);
dataBo.setProductId(device.getProductId());
dataBo.setType(bo.getStatus().equals(DeviceStatus.ONLINE) ? 5 : 6);
dataBo.setSerialNumber(bo.getSerialNumber());
ruleEngine.ruleMatch(dataBo);
} catch (Exception e) {
log.error("=>设备状态处理异常", e);
}
}
private void handlerShadow(Device device,DeviceStatus status){
//获取设备协议编码
DeviceAndProtocol dp = deviceService.selectProtocolBySerialNumber(device.getSerialNumber());
String protocolCode = dp.getProtocolCode();
/* 设备上线 处理影子值*/
if (status.equals(DeviceStatus.ONLINE) && device.getIsShadow() ==1){
ThingsModelShadow shadow = deviceService.getDeviceShadowThingsModel(device);
List<ThingsModelSimpleItem> properties = shadow.getProperties();
List<ThingsModelSimpleItem> functions = shadow.getFunctions();
//JsonArray组合发送
if (FastBeeConstant.PROTOCOL.JsonArray.equals(protocolCode)) {
if (!CollectionUtils.isEmpty(properties)) {
mqttMessagePublish.publishProperty(device.getProductId(), device.getSerialNumber(), properties, 3);
}
if (!CollectionUtils.isEmpty(functions)) {
mqttMessagePublish.publishFunction(device.getProductId(), device.getSerialNumber(), functions, 3);
}
} else { //其他协议单个发送
functions.addAll(properties);
if (!CollectionUtils.isEmpty(functions)) {
for (ThingsModelSimpleItem function : functions) {
MQSendMessageBo bo = new MQSendMessageBo();
bo.setDp(dp);
bo.setShadow(false);
bo.setIdentifier(function.getId());
bo.setSerialNumber(device.getSerialNumber());
JSONObject jsonObject = new JSONObject();
jsonObject.put(function.getId(),function.getValue());
bo.setParams(jsonObject);
bo.setValue(function.getValue());
long id = snowflakeIdWorker.nextId();
bo.setMessageId(id +"");
//发送到MQ处理
MessageProducer.sendFunctionInvoke(bo);
}
}
}
}
}
}

View File

@ -0,0 +1,31 @@
package com.fastbee.mq.redischannel.consumer;
import com.fastbee.common.constant.FastBeeConstant;
import com.fastbee.common.core.mq.DeviceTestReportBo;
import com.fastbee.mq.service.IDeviceTestService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* @author bill
*/
@Slf4j
@Component
public class DeviceTestConsumer {
@Resource
private IDeviceTestService testHandler;
@Async(FastBeeConstant.TASK.DEVICE_TEST_TASK)
public void consume(DeviceTestReportBo bo){
try {
//处理emq订阅的非 property/post 属性上报的消息 ,因为其他消息量小,放在一起处理
testHandler.messageHandler(bo);
}catch (Exception e){
log.error("=>设备其他消息处理出错",e);
}
}
}

View File

@ -0,0 +1,38 @@
package com.fastbee.mq.redischannel.consumer;
import com.fastbee.common.constant.FastBeeConstant;
import com.fastbee.common.core.device.DeviceAndProtocol;
import com.fastbee.common.core.mq.MQSendMessageBo;
import com.fastbee.common.exception.ServiceException;
import com.fastbee.iot.service.IDeviceService;
import com.fastbee.mq.service.IMqttMessagePublish;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import java.util.Optional;
/**
* 指令(服务)下发处理类
*
* @author gsb
* @date 2022/10/11 8:17
*/
@Slf4j
@Component
public class FunctionInvokeConsumer {
@Autowired
private IMqttMessagePublish functionSendService;
@Async(FastBeeConstant.TASK.FUNCTION_INVOKE_TASK)
public void handler(MQSendMessageBo bo) {
try {
functionSendService.funcSend(bo);
} catch (Exception e) {
log.error("=>服务下发异常", e);
}
}
}

View File

@ -0,0 +1,61 @@
package com.fastbee.mq.redischannel.consumer;
import com.alibaba.fastjson2.JSONObject;
import com.fastbee.common.constant.FastBeeConstant;
import com.fastbee.common.core.mq.DeviceStatusBo;
import com.fastbee.common.core.mq.MQSendMessageBo;
import com.fastbee.common.core.mq.message.DeviceDownMessage;
import com.fastbee.common.core.mq.message.ModbusPollMsg;
import com.fastbee.common.core.mq.ota.OtaUpgradeBo;
import com.fastbee.mq.redischannel.queue.DevicePropFetchQueue;
import com.fastbee.mq.redischannel.queue.DeviceStatusQueue;
import com.fastbee.mq.redischannel.queue.FunctionInvokeQueue;
import com.fastbee.mq.redischannel.queue.OtaUpgradeQueue;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.stereotype.Component;
/**
* redisChannel消息监听
*
* @author gsb
* @date 2022/10/10 9:17
*/
@Component
@Slf4j
public class RedisChannelConsume implements MessageListener {
/**
* 监听推送消息
*/
@Override
public void onMessage(Message message, byte[] pattern) {
try {
/*获取channel*/
String channel = new String(message.getChannel());
/*获取消息*/
String body = new String(message.getBody());
switch (channel) {
case FastBeeConstant.CHANNEL.PROP_READ:
ModbusPollMsg downMessage = JSONObject.parseObject(body, ModbusPollMsg.class);
DevicePropFetchQueue.offer(downMessage);
break;
case FastBeeConstant.CHANNEL.FUNCTION_INVOKE:
MQSendMessageBo sendBo = JSONObject.parseObject(body, MQSendMessageBo.class);
FunctionInvokeQueue.offer(sendBo);
break;
case FastBeeConstant.CHANNEL.UPGRADE:
OtaUpgradeBo upgradeBo = JSONObject.parseObject(body, OtaUpgradeBo.class);
OtaUpgradeQueue.offer(upgradeBo);
break;
default:
log.error("=>未知消息类型,channel:[{}]", channel);
break;
}
} catch (Exception e) {
log.error("=>redisChannel处理消息异常,e", e);
}
}
}

View File

@ -0,0 +1,35 @@
package com.fastbee.mq.redischannel.listen;
import com.fastbee.common.constant.FastBeeConstant;
import com.fastbee.common.core.mq.DeviceReportBo;
import com.fastbee.mq.redischannel.consumer.DeviceOtherMsgConsumer;
import com.fastbee.mq.redischannel.queue.DeviceOtherQueue;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* @author gsb
* @date 2023/2/28 10:02
*/
@Slf4j
@Component
public class DeviceOtherListen {
@Resource
private DeviceOtherMsgConsumer otherMsgConsumer;
@Async(FastBeeConstant.TASK.DEVICE_OTHER_TASK)
public void listen(){
while (true){
try {
DeviceReportBo reportBo = DeviceOtherQueue.take();
otherMsgConsumer.consume(reportBo);
}catch (Exception e){
log.error("=>emq数据转发异常");
}
}
}
}

View File

@ -0,0 +1,37 @@
package com.fastbee.mq.redischannel.listen;
import com.fastbee.common.constant.FastBeeConstant;
import com.fastbee.common.core.mq.message.DeviceDownMessage;
import com.fastbee.common.core.mq.message.ModbusPollMsg;
import com.fastbee.mq.redischannel.consumer.DevicePropFetchConsumer;
import com.fastbee.mq.redischannel.queue.DevicePropFetchQueue;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
/**
* 设备属性获取(定时获取)监听
*
* @author gsb
* @date 2022/10/11 8:26
*/
@Slf4j
@Component
public class DevicePropFetchListen {
@Autowired
private DevicePropFetchConsumer devicePropFetchConsumer;
@Async(FastBeeConstant.TASK.MESSAGE_CONSUME_TASK_FETCH)
public void listen() {
while (true) {
try {
ModbusPollMsg take = DevicePropFetchQueue.take();
devicePropFetchConsumer.consume(take);
} catch (Exception e) {
log.error("=>设备属性获取异常", e);
}
}
}
}

View File

@ -0,0 +1,39 @@
package com.fastbee.mq.redischannel.listen;
import com.fastbee.common.constant.FastBeeConstant;
import com.fastbee.common.core.mq.DeviceReportBo;
import com.fastbee.mq.redischannel.consumer.DeviceReplyMsgConsumer;
import com.fastbee.mq.redischannel.queue.DeviceReplyQueue;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
/**
* 设备回调消息监听
*
* @author bill
*/
@Slf4j
@Component
public class DeviceReplyListen {
@Autowired
private DeviceReplyMsgConsumer deviceReplyMsgHandler;
@Async(FastBeeConstant.TASK.MESSAGE_CONSUME_TASK_PUB)
public void listen() {
while (true) {
try {
/*读队列消息*/
DeviceReportBo reportBo = DeviceReplyQueue.take();
/*处理消息*/
deviceReplyMsgHandler.consume(reportBo);
} catch (Exception e) {
log.error("=>设备回调消息监听异常", e);
}
}
}
}

View File

@ -0,0 +1,40 @@
package com.fastbee.mq.redischannel.listen;
import com.fastbee.common.constant.FastBeeConstant;
import com.fastbee.common.core.mq.DeviceReportBo;
import com.fastbee.mq.redischannel.consumer.DeviceReportMsgConsumer;
import com.fastbee.mq.redischannel.queue.DeviceReplyQueue;
import com.fastbee.mq.redischannel.queue.DeviceReportQueue;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
/**
* 设备主动上报消息监听
*
* @author bill
*/
@Slf4j
@Component
public class DeviceReportListen {
@Autowired
private DeviceReportMsgConsumer reportMsgConsumer;
@Async(FastBeeConstant.TASK.MESSAGE_CONSUME_TASK_PUB)
public void listen() {
while (true) {
try {
/*取出数据*/
DeviceReportBo reportBo = DeviceReportQueue.take();
/*处理数据*/
reportMsgConsumer.consume(reportBo);
} catch (Exception e) {
log.error("=>设备上报数据监听异常", e);
}
}
}
}

View File

@ -0,0 +1,35 @@
package com.fastbee.mq.redischannel.listen;
import com.fastbee.common.constant.FastBeeConstant;
import com.fastbee.common.core.mq.DeviceStatusBo;
import com.fastbee.mq.redischannel.consumer.DeviceStatusConsumer;
import com.fastbee.mq.redischannel.queue.DeviceStatusQueue;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
/**
* 设备状态监听
* @author bill
*/
@Slf4j
@Component
public class DeviceStatusListen {
@Autowired
private DeviceStatusConsumer deviceStatusConsumer;
@Async(FastBeeConstant.TASK.MESSAGE_CONSUME_TASK)
public void listen() {
try {
while (true) {
DeviceStatusBo status = DeviceStatusQueue.take();
deviceStatusConsumer.consume(status);
}
} catch (Exception e) {
log.error("设备状态监听错误", e);
}
}
}

View File

@ -0,0 +1,35 @@
package com.fastbee.mq.redischannel.listen;
import com.fastbee.common.constant.FastBeeConstant;
import com.fastbee.common.core.mq.DeviceTestReportBo;
import com.fastbee.mq.redischannel.consumer.DeviceTestConsumer;
import com.fastbee.mq.redischannel.queue.DeviceTestQueue;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* @author bill
*/
@Component
@Slf4j
public class DeviceTestListen {
@Resource
private DeviceTestConsumer deviceTestConsumer;
@Async(FastBeeConstant.TASK.DEVICE_TEST_TASK)
public void listen(){
while (true){
try {
DeviceTestReportBo take = DeviceTestQueue.take();
deviceTestConsumer.consume(take);
}catch (Exception e){
log.error("=>emq数据转发异常");
}
}
}
}

View File

@ -0,0 +1,35 @@
package com.fastbee.mq.redischannel.listen;
import com.fastbee.common.constant.FastBeeConstant;
import com.fastbee.common.core.mq.MQSendMessageBo;
import com.fastbee.mq.redischannel.consumer.FunctionInvokeConsumer;
import com.fastbee.mq.redischannel.queue.FunctionInvokeQueue;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
/**
* 设备服务下发监听
*
* @author bill
*/
@Slf4j
@Component
public class FunctionInvokeListen {
@Autowired
private FunctionInvokeConsumer functionInvokeConsumer;
@Async(FastBeeConstant.TASK.MESSAGE_CONSUME_TASK)
public void listen() {
while (true) {
try {
MQSendMessageBo sendBo = FunctionInvokeQueue.take();
functionInvokeConsumer.handler(sendBo);
} catch (Exception e) {
log.error("=>下发服务消费异常", e);
}
}
}
}

View File

@ -0,0 +1,38 @@
package com.fastbee.mq.redischannel.listen;
import com.fastbee.common.constant.FastBeeConstant;
import com.fastbee.common.core.mq.ota.OtaUpgradeBo;
import com.fastbee.mq.redischannel.queue.OtaUpgradeQueue;
import com.fastbee.mq.service.IMqttMessagePublish;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
/**
* OTA升级消息监听
*
* @author gsb
* @date 2022/10/11 8:36
*/
@Slf4j
@Service
public class UpgradeListen {
@Autowired
private IMqttMessagePublish functionSendService;
@Async(FastBeeConstant.TASK.MESSAGE_CONSUME_TASK)
public void listen() {
while (true) {
try {
/*获取队列中的OTA升级消息*/
OtaUpgradeBo upgradeBo = OtaUpgradeQueue.take();
// OTA升级处理
functionSendService.upGradeOTA(upgradeBo);
} catch (Exception e) {
log.error("->OTA消息监听异常", e);
}
}
}
}

View File

@ -0,0 +1,22 @@
package com.fastbee.mq.redischannel.producer;
import com.fastbee.common.core.mq.DeviceReportBo;
import com.fastbee.mqttclient.IEmqxMessageProducer;
import org.springframework.stereotype.Component;
/**
* @author bill
*/
@Component
public class EmqxMessageProducer implements IEmqxMessageProducer {
@Override
public void sendEmqxMessage(String topicName, DeviceReportBo deviceReportBo) {
if (topicName.contains("property/post")){
MessageProducer.sendPublishMsg(deviceReportBo);
} else {
MessageProducer.sendOtherMsg(deviceReportBo);
}
}
}

View File

@ -0,0 +1,46 @@
package com.fastbee.mq.redischannel.producer;
import com.fastbee.common.core.mq.DeviceReportBo;
import com.fastbee.common.core.mq.DeviceStatusBo;
import com.fastbee.common.core.mq.DeviceTestReportBo;
import com.fastbee.common.core.mq.MQSendMessageBo;
import com.fastbee.common.core.mq.message.ModbusPollMsg;
import com.fastbee.common.core.mq.ota.OtaUpgradeBo;
import com.fastbee.common.core.mq.message.DeviceDownMessage;
import com.fastbee.mq.redischannel.queue.*;
/**
*设备消息生产者 ,设备的消息发送通道
* @author bill
*/
public class MessageProducer {
/*发送设备获取属性消息到队列*/
public static void sendPropFetch(ModbusPollMsg bo){
DevicePropFetchQueue.offer(bo);
}
/*发送设备服务下发消息到队列*/
public static void sendFunctionInvoke(MQSendMessageBo bo){
FunctionInvokeQueue.offer(bo);
}
/*发送设备上报消息到队列*/
public static void sendPublishMsg(DeviceReportBo bo){
DeviceReportQueue.offer(bo);
}
public static void sendOtherMsg(DeviceReportBo bo){
DeviceOtherQueue.offer(bo);
}
public static void sendStatusMsg(DeviceStatusBo bo){
DeviceStatusQueue.offer(bo);
}
/**
* 设备调试通道
* @param bo
*/
public static void sendDeviceTestMsg(DeviceTestReportBo bo){
DeviceTestQueue.offer(bo);
}
}

View File

@ -0,0 +1,25 @@
package com.fastbee.mq.redischannel.queue;
import com.fastbee.common.core.mq.DeviceReportBo;
import lombok.SneakyThrows;
import java.util.concurrent.LinkedBlockingQueue;
/**
* @author gsb
* @date 2022/10/10 10:13
*/
public class DeviceOtherQueue {
private static final LinkedBlockingQueue<DeviceReportBo> queue = new LinkedBlockingQueue<>();
/*元素加入队列,最后*/
public static void offer(DeviceReportBo dto){
queue.offer(dto);
}
/*取出队列元素 先进先出*/
@SneakyThrows
public static DeviceReportBo take(){
return queue.take();
}
}

View File

@ -0,0 +1,27 @@
package com.fastbee.mq.redischannel.queue;
import com.fastbee.common.core.mq.DeviceReportBo;
import com.fastbee.common.core.mq.message.DeviceDownMessage;
import com.fastbee.common.core.mq.message.ModbusPollMsg;
import lombok.SneakyThrows;
import java.util.concurrent.LinkedBlockingQueue;
/**
* 设备属性获取存储列队
* @author gsb
* @date 2022/10/11 8:29
*/
public class DevicePropFetchQueue {
private static final LinkedBlockingQueue<ModbusPollMsg> queue = new LinkedBlockingQueue<>();
/*元素加入队列,最后*/
public static void offer(ModbusPollMsg dto){
queue.offer(dto);
}
/*取出队列元素 先进先出*/
@SneakyThrows
public static ModbusPollMsg take(){
return queue.take();
}
}

View File

@ -0,0 +1,25 @@
package com.fastbee.mq.redischannel.queue;
import com.fastbee.common.core.mq.DeviceReportBo;
import lombok.SneakyThrows;
import java.util.concurrent.LinkedBlockingQueue;
/**
* 设备消息回调队列 {@link DeviceReportBo}
* @author gsb
* @date 2022/10/10 10:15
*/
public class DeviceReplyQueue {
private static final LinkedBlockingQueue<DeviceReportBo> queue = new LinkedBlockingQueue<>();
/*元素加入队列,最后*/
public static void offer(DeviceReportBo dto){
queue.offer(dto);
}
/*取出队列元素 先进先出*/
@SneakyThrows
public static DeviceReportBo take(){
return queue.take();
}
}

View File

@ -0,0 +1,25 @@
package com.fastbee.mq.redischannel.queue;
import com.fastbee.common.core.mq.DeviceReportBo;
import lombok.SneakyThrows;
import java.util.concurrent.LinkedBlockingQueue;
/**
* @author gsb
* @date 2022/10/10 10:13
*/
public class DeviceReportQueue {
private static final LinkedBlockingQueue<DeviceReportBo> queue = new LinkedBlockingQueue<>();
/*元素加入队列,最后*/
public static void offer(DeviceReportBo dto){
queue.offer(dto);
}
/*取出队列元素 先进先出*/
@SneakyThrows
public static DeviceReportBo take(){
return queue.take();
}
}

View File

@ -0,0 +1,25 @@
package com.fastbee.mq.redischannel.queue;
import com.fastbee.common.core.mq.DeviceStatusBo;
import lombok.SneakyThrows;
import java.util.concurrent.LinkedBlockingQueue;
/**
* 设备消息缓存队列 添加{@link DeviceStatusBo} 消息
* @author gsb
* @date 2022/10/10 9:59
*/
public class DeviceStatusQueue {
private static final LinkedBlockingQueue<DeviceStatusBo> queue = new LinkedBlockingQueue<>();
/*元素加入队列,最后*/
public static void offer(DeviceStatusBo dto){
queue.offer(dto);
}
/*取出队列元素 先进先出*/
@SneakyThrows
public static DeviceStatusBo take(){
return queue.take();
}
}

View File

@ -0,0 +1,25 @@
package com.fastbee.mq.redischannel.queue;
import com.fastbee.common.core.mq.DeviceTestReportBo;
import lombok.SneakyThrows;
import java.util.concurrent.LinkedBlockingQueue;
/**
* @author bill
*/
public class DeviceTestQueue {
private static final LinkedBlockingQueue<DeviceTestReportBo> queue = new LinkedBlockingQueue<>();
/*元素加入队列,最后*/
public static void offer(DeviceTestReportBo dto){
queue.offer(dto);
}
/*取出队列元素 先进先出*/
@SneakyThrows
public static DeviceTestReportBo take(){
return queue.take();
}
}

View File

@ -0,0 +1,25 @@
package com.fastbee.mq.redischannel.queue;
import com.fastbee.common.core.mq.MQSendMessageBo;
import lombok.SneakyThrows;
import java.util.concurrent.LinkedBlockingQueue;
/**
* 服务下发队列 处理{@link MQSendMessageBo}
* @author gsb
* @date 2022/10/10 10:11
*/
public class FunctionInvokeQueue {
private static final LinkedBlockingQueue<MQSendMessageBo> queue = new LinkedBlockingQueue<>();
/*元素加入队列,最后*/
public static void offer(MQSendMessageBo dto){
queue.offer(dto);
}
/*取出队列元素 先进先出*/
@SneakyThrows
public static MQSendMessageBo take(){
return queue.take();
}
}

View File

@ -0,0 +1,25 @@
package com.fastbee.mq.redischannel.queue;
import com.fastbee.common.core.mq.ota.OtaUpgradeBo;
import lombok.SneakyThrows;
import java.util.concurrent.LinkedBlockingQueue;
/**
* OTA升级列队 {@link OtaUpgradeBo}
* @author gsb
* @date 2022/10/10 10:30
*/
public class OtaUpgradeQueue {
private static final LinkedBlockingQueue<OtaUpgradeBo> queue = new LinkedBlockingQueue<>();
/*元素加入队列,最后*/
public static void offer(OtaUpgradeBo dto){
queue.offer(dto);
}
/*取出队列元素 先进先出*/
@SneakyThrows
public static OtaUpgradeBo take(){
return queue.take();
}
}

View File

@ -0,0 +1,27 @@
package com.fastbee.mq.redischannel.service;
import com.fastbee.common.core.redis.RedisCache;
import com.fastbee.mq.service.IMessagePublishService;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
/**
* 设备消息推送至RedisChannel
* @author bill
*/
@NoArgsConstructor
public class RedisPublishServiceImpl implements IMessagePublishService {
@Autowired
private RedisCache redisCache;
/**
* 消息推送到redisChannel
* @param message 设备消息
* @param channel 推送channel
*/
@Override
public void publish(Object message,String channel) {
redisCache.publish(message,channel);
}
}

View File

@ -0,0 +1,27 @@
package com.fastbee.mq.rocketmq.consumer;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @author bill
*/
@Component
@ConfigurationProperties(prefix = "rocketmq.producer")
@Data
public class ConsumerTopicConstant {
/**网关默认主题*/
private String mqTopic;
/*设备状态topic*/
private String deviceStatusTopic;
/*设备主动上报topic*/
private String deviceUpTopic;
/*设备服务下发topic*/
private String functionInvokeTopic;
/*设备消息回调topic*/
private String deviceReplyTopic;
/*平台获取属性topic*/
private String fetchPropTopic;
}

View File

@ -0,0 +1,26 @@
package com.fastbee.mq.rocketmq.listener;
import com.fastbee.common.constant.FastBeeConstant;
import com.fastbee.common.core.mq.DeviceStatusBo;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
/**
* RocketMQ监听设备状态消息
* @author gsb
* @date 2022/10/11 9:37
*/
@Slf4j
@Component
@RocketMQMessageListener(consumerGroup = FastBeeConstant.CHANNEL.DEVICE_STATUS_GROUP , topic = FastBeeConstant.CHANNEL.DEVICE_STATUS)
@ConditionalOnProperty(prefix ="cluster", name = "type",havingValue = FastBeeConstant.MQTT.ROCKET_MQ)
public class RocketDeviceStatusListener implements RocketMQListener<DeviceStatusBo> {
@Override
public void onMessage(DeviceStatusBo deviceStatusBo) {
log.debug("=>收到设备状态消息,message=[{}]",deviceStatusBo);
}
}

View File

@ -0,0 +1,27 @@
package com.fastbee.mq.rocketmq.listener;
import com.fastbee.common.constant.FastBeeConstant;
import com.fastbee.common.core.mq.MQSendMessageBo;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
/**
* MQ监听服务下发消息
* @author gsb
* @date 2022/10/11 9:53
*/
@Slf4j
@Component
@RocketMQMessageListener(consumerGroup = FastBeeConstant.CHANNEL.FUNCTION_INVOKE_GROUP , topic = FastBeeConstant.CHANNEL.FUNCTION_INVOKE)
@ConditionalOnProperty(prefix ="cluster", name = "type",havingValue = FastBeeConstant.MQTT.ROCKET_MQ)
public class RocketFunctionInvokeListener implements RocketMQListener<MQSendMessageBo> {
@Override
public void onMessage(MQSendMessageBo bo) {
}
}

View File

@ -0,0 +1,25 @@
package com.fastbee.mq.rocketmq.listener;
import com.fastbee.common.constant.FastBeeConstant;
import com.fastbee.common.core.mq.message.DeviceDownMessage;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
/**
* @author gsb
* @date 2022/10/11 16:49
*/
@Slf4j
@Component
@RocketMQMessageListener(consumerGroup = FastBeeConstant.CHANNEL.PROP_READ_GROUP , topic = FastBeeConstant.CHANNEL.PROP_READ)
@ConditionalOnProperty(prefix ="cluster", name = "type",havingValue = FastBeeConstant.MQTT.ROCKET_MQ)
public class RocketPropReadListener implements RocketMQListener<DeviceDownMessage> {
@Override
public void onMessage(DeviceDownMessage deviceDownMessage) {
}
}

View File

@ -0,0 +1,26 @@
package com.fastbee.mq.rocketmq.listener;
import com.fastbee.common.constant.FastBeeConstant;
import com.fastbee.common.core.mq.DeviceReportBo;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
/**
* MQ监听设备推送消息(上报消息和回调消息)
* @author gsb
* @date 2022/10/11 9:51
*/
@Slf4j
@Component
@RocketMQMessageListener(consumerGroup = FastBeeConstant.CHANNEL.PUBLISH_GROUP , topic = FastBeeConstant.CHANNEL.PUBLISH)
@ConditionalOnProperty(prefix ="cluster", name = "type",havingValue = FastBeeConstant.MQTT.ROCKET_MQ)
public class RocketPublishMsgListener implements RocketMQListener<DeviceReportBo> {
@Override
public void onMessage(DeviceReportBo bo) {
log.debug("=>收到设备推送消息,message=[{}]",bo);
}
}

View File

@ -0,0 +1,21 @@
package com.fastbee.mq.rocketmq.model;
import lombok.Data;
import java.io.Serializable;
/**
* 网关通用模型
* @author bill
*/
@Data
public class MQSendMessage implements Serializable {
private static final long serialVersionUID = 1L;
/**topic*/
private String topicName;
/**消息-json格式*/
private String message;
}

View File

@ -0,0 +1,261 @@
package com.fastbee.mq.rocketmq.producer;
import com.alibaba.fastjson2.JSON;
import com.fastbee.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.producer.*;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.apache.rocketmq.spring.support.RocketMQHeaders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
//@Component
@Slf4j
public class RocketMqProducer {
/**
* rocketmq模板注入
*/
@Autowired
private RocketMQTemplate rocketMQTemplate;
/**
* 普通发送
* @param topic 消息主题
* @param msg 消息体
* @param <T> 消息泛型
*/
public <T> void send(String topic, T msg) {
rocketMQTemplate.convertAndSend(topic, msg);
//rocketMQTemplate.send(topic + ":tag1", MessageBuilder.withPayload(msg).build()); // 等价于上面一行
}
/**
* 发送带tag的消息直接在topic后面加上":tag"
*
* @param topic 消息主题
* @param tag 消息tag
* @param msg 消息体
* @param <T> 消息泛型
* @return
*/
public <T> SendResult sendTagMsg(String topic, String tag, T msg) {
topic = topic + ":" + tag;
return rocketMQTemplate.syncSend(topic, MessageBuilder.withPayload(msg).build());
}
/**
* 发送同步消息阻塞当前线程等待broker响应发送结果这样不太容易丢失消息
* sendResult为返回的发送结果
*/
public <T> SendResult sendMsg(String topic, T msg) {
Message<T> message = MessageBuilder.withPayload(msg).build();
SendResult sendResult = rocketMQTemplate.syncSend(topic, message);
log.info("【sendMsg】sendResult={}", JSON.toJSONString(sendResult));
return sendResult;
}
/**
* 发送异步消息
* 发送异步消息通过线程池执行发送到broker的消息任务执行完后回调在SendCallback中可处理相关成功失败时的逻辑
* (适合对响应时间敏感的业务场景)
* @param topic 消息Topic
* @param msg 消息实体
*
*/
public <T> void asyncSend(String topic, T msg) {
Message<T> message = MessageBuilder.withPayload(msg).build();
asyncSend(topic, message, new SendCallback() {
@Override
public void onSuccess(SendResult sendResult) {
log.info("topic:{}消息---发送MQ成功---", topic);
}
@Override
public void onException(Throwable throwable) {
log.error("topic:{}消息---发送MQ失败 ex:{}---", topic, throwable.getMessage());
}
});
}
/**
* 发送异步消息
* 发送异步消息通过线程池执行发送到broker的消息任务执行完后回调在SendCallback中可处理相关成功失败时的逻辑
* (适合对响应时间敏感的业务场景)
* @param topic 消息Topic
* @param message 消息实体
* @param sendCallback 回调函数
*/
public void asyncSend(String topic, Message<?> message, SendCallback sendCallback) {
rocketMQTemplate.asyncSend(topic, message, sendCallback);
}
/**
* 发送异步消息
*
* @param topic 消息Topic
* @param message 消息实体
* @param sendCallback 回调函数
* @param timeout 超时时间
*/
public void asyncSend(String topic, Message<?> message, SendCallback sendCallback, long timeout) {
rocketMQTemplate.asyncSend(topic, message, sendCallback, timeout);
}
/**
* 同步延迟消息
* rocketMQ的延迟消息发送其实是已发送就已经到broker端了然后消费端会延迟收到消息。
* RocketMQ 目前只支持固定精度的定时消息。
* 固定等级1到18分别对应1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h
* 延迟的底层方法是用定时任务实现的。
* 发送延时消息delayLevel的值就为0因为不延时
*
* @param topic 消息主题
* @param msg 消息体
* @param timeout 发送超时时间
* @param delayLevel 延迟级别 1到18
* @param <T> 消息泛型
*/
public <T> void sendDelay(String topic, T msg, long timeout, int delayLevel) {
Message<T> message = MessageBuilder.withPayload(msg).build();
rocketMQTemplate.syncSend(topic, message, timeout, delayLevel);
}
/**
* 发送异步延迟消息
*
* @param topic 消息Topic
* @param message 消息实体
* @param sendCallback 回调函数
* @param timeout 超时时间
* @param delayLevel 延迟消息的级别
*/
public void asyncSendDelay(String topic, Message<?> message, SendCallback sendCallback, long timeout, int delayLevel) {
rocketMQTemplate.asyncSend(topic, message, sendCallback, timeout, delayLevel);
}
/**
* 发送异步延迟消息
*
* @param topic 消息Topic
* @param message 消息实体
* @param timeout 超时时间
* @param delayLevel 延迟消息的级别
*/
public void asyncSendDelay(String topic, Message<?> message, long timeout, int delayLevel) {
rocketMQTemplate.asyncSend(topic, message, new SendCallback() {
@Override
public void onSuccess(SendResult sendResult) {
log.info("topic:{}消息---发送MQ成功---", topic);
}
@Override
public void onException(Throwable throwable) {
log.error("topic:{}消息---发送MQ失败 ex:{}---", topic, throwable.getMessage());
}
}, timeout, delayLevel);
}
/**
* 单向消息
* 特点为只负责发送消息,不等待服务器回应且没有回调函数触发,即只发送请求不等待应答
* 此方式发送消息的过程耗时非常短,一般在微秒级别
* 应用场景:适用于某些耗时非常短,但对可靠性要求并不高的场景,例如日志收集
* @param topic 消息主题
* @param msg 消息体
* @param <T> 消息泛型
*/
public <T> void sendOneWayMsg(String topic, T msg) {
Message<T> message = MessageBuilder.withPayload(msg).build();
rocketMQTemplate.sendOneWay(topic, message);
}
/**
* 发送顺序消息
*
* @param topic 消息主题
* @param msg 消息体
* @param hashKey 确定消息发送到哪个队列中
* @param <T> 消息泛型
*/
public <T> void syncSendOrderly(String topic, T msg, String hashKey) {
Message<T> message = MessageBuilder.withPayload(msg).build();
log.info("发送顺序消息topic:{}, hashKey:{}", topic, hashKey);
rocketMQTemplate.syncSendOrderly(topic, message, hashKey);
}
/**
* 发送顺序消息
*
* @param topic 消息主题
* @param msg 消息体
* @param hashKey 确定消息发送到哪个队列中
* @param timeout 超时时间
*/
public <T> void syncSendOrderly(String topic, T msg, String hashKey, long timeout) {
Message<T> message = MessageBuilder.withPayload(msg).build();
log.info("发送顺序消息topic:{}, hashKey:{}, timeout:{}", topic, hashKey, timeout);
rocketMQTemplate.syncSendOrderly(topic, message, hashKey, timeout);
}
/**
* 发送批量消息
*
* @param topic 消息主题
* @param msgList 消息体集合
* @param <T> 消息泛型
* @return
*/
public <T> SendResult asyncSendBatch(String topic, List<T> msgList) {
List<Message<T>> messageList = msgList.stream()
.map(msg -> MessageBuilder.withPayload(msg).build()).collect(Collectors.toList());
return rocketMQTemplate.syncSend(topic, messageList);
}
/**
* 发送事务消息
*
* @param txProducerGroup 事务消息的生产者组名称
* @param topic 事务消息主题
* @param tag 事务消息tag
* @param msg 事务消息体
* @param arg 事务消息监听器回查参数
* @param <T> 事务消息泛型
*/
public <T> void sendTransaction(String txProducerGroup, String topic, String tag, T msg, T arg){
if(StringUtils.isNotEmpty(tag)){
topic = topic + ":" + tag;
}
String transactionId = UUID.randomUUID().toString();
Message<T> message = MessageBuilder.withPayload(msg)
//header也有大用处
.setHeader(RocketMQHeaders.TRANSACTION_ID, transactionId)
.setHeader("share_id", "TEST")
.build();
TransactionSendResult result = rocketMQTemplate.sendMessageInTransaction(topic, message, arg);
if(result.getLocalTransactionState().equals(LocalTransactionState.COMMIT_MESSAGE)
&& result.getSendStatus().equals(SendStatus.SEND_OK)){
log.info("事物消息发送成功");
}
log.info("事物消息发送结果:{}", result);
}
}

View File

@ -0,0 +1,26 @@
package com.fastbee.mq.rocketmq.service;
import com.fastbee.mq.rocketmq.producer.RocketMqProducer;
import com.fastbee.mq.service.IMessagePublishService;
import org.springframework.beans.factory.annotation.Autowired;
/**
* 设备消息推送至RocketMq
* @author bill
*/
public class RocketMqPublishServiceImpl implements IMessagePublishService {
@Autowired
private RocketMqProducer rocketMqProducer;
/**
* rocket通用生产消息方法
* @param message 设备消息
* @param channel 推送topic
*/
@Override
public void publish(Object message,String channel)
{
rocketMqProducer.send(channel,message);
}
}

View File

@ -0,0 +1,689 @@
package com.fastbee.mq.ruleEngine;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.fastbee.common.core.mq.InvokeReqDto;
import com.fastbee.common.core.notify.AlertPushParams;
import com.fastbee.common.core.redis.RedisCache;
import com.fastbee.common.core.redis.RedisKeyBuilder;
import com.fastbee.common.core.thingsModel.SceneThingsModelItem;
import com.fastbee.common.core.thingsModel.ThingsModelSimpleItem;
import com.fastbee.common.exception.ServiceException;
import com.fastbee.common.utils.DateUtils;
import com.fastbee.common.utils.StringUtils;
import com.fastbee.common.utils.spring.SpringUtils;
import com.fastbee.iot.domain.*;
import com.fastbee.iot.model.AlertSceneSendVO;
import com.fastbee.iot.model.SceneTerminalUserVO;
import com.fastbee.iot.model.ScriptTemplate;
import com.fastbee.iot.model.ThingsModels.ValueItem;
import com.fastbee.iot.service.*;
import com.fastbee.mq.service.IFunctionInvoke;
import com.fastbee.mq.service.IRuleEngine;
import com.fastbee.notify.core.service.NotifySendService;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.*;
import java.util.stream.Collectors;
import static java.util.regex.Pattern.compile;
@Data
@Slf4j
public class SceneContext {
/**
* 上报信息的设备编号
*/
private String deviceNum;
/**
* 上报信息的设备所属产品ID
*/
private Long productId;
/**
* 上报信息的设备信息类型 1=属性, 2=功能3=事件4=设备升级5=设备上线6=设备下线
*/
private int type;
/**
* 上报的物模型集合
*/
private List<ThingsModelSimpleItem> thingsModelSimpleItems;
/**
* 触发成功的物模型集合,保留给告警记录
*/
private List<SceneThingsModelItem> sceneThingsModelItems;
private static IFunctionInvoke functionInvoke = SpringUtils.getBean(IFunctionInvoke.class);
private static IDeviceService deviceService = SpringUtils.getBean(IDeviceService.class);
private static IAlertService alertService = SpringUtils.getBean(IAlertService.class);
private static NotifySendService notifySendService = SpringUtils.getBean(NotifySendService.class);
private static RedisCache redisCache = SpringUtils.getBean(RedisCache.class);
private static IAlertLogService alertLogService = SpringUtils.getBean(IAlertLogService.class);
private static IDeviceUserService deviceUserService = SpringUtils.getBean(IDeviceUserService.class);
private static ISceneService sceneService = SpringUtils.getBean(ISceneService.class);
private static IRuleEngine ruleService = SpringUtils.getBean(IRuleEngine.class);
private static IDeviceAlertUserService deviceAlertUserService = SpringUtils.getBean(IDeviceAlertUserService.class);
public SceneContext(String deviceNum, Long productId, int type, List<ThingsModelSimpleItem> thingsModelSimpleItems) {
this.deviceNum = deviceNum;
this.productId = productId;
this.type = type;
this.thingsModelSimpleItems = thingsModelSimpleItems;
}
/**
* 处理规则脚本
*
* @return
*/
private boolean process(String json) throws InterruptedException {
log.info("------------------[规则引擎执行...]---------------------");
ScriptTemplate scriptTemplate = JSON.parseObject(json, ScriptTemplate.class);
log.info("规则引擎脚本配置:{},规则引擎上下文:{}", scriptTemplate, this);
//触发器
if (scriptTemplate.getPurpose() == 2) {
// 触发器,检查静默时间
if (!checkSilent(scriptTemplate.getSilent(), scriptTemplate.getSceneId(), this.deviceNum)) {
// 触发条件为不满足时返回true
if (scriptTemplate.getCond() == 3) {
return true;
}
return false;
}
if (scriptTemplate.getSource() == 1) {
// 设备触发
return deviceTrigger(scriptTemplate);
} else if (scriptTemplate.getSource() == 3) {
// 产品触发
return productTrigger(scriptTemplate);
}
//执行动作
} else if (scriptTemplate.getPurpose() == 3) {
if (scriptTemplate.getCheckdelay() != 0) {
// 告警恢复规则 设备上线触发 不加载
if (scriptTemplate.getSource() != 5 && type != 5) {
loadDelayTrigger(scriptTemplate);
}
} else {
// 执行动作,延迟执行,线程休眠 delay x 1000毫秒
Thread.sleep(scriptTemplate.getDelay() * 1000);
// 存在相同未处理告警只记录 不触发告警通知和动作
if (scriptTemplate.getSource() == 4) {
//存在相同未处理告警只记录 不触发告警通知和动作
if (checkDeviceAlerting(scriptTemplate.getSceneId(), this.deviceNum)) {
this.alert(scriptTemplate.getSceneId(),false);
} else {
this.alert(scriptTemplate.getSceneId(),true);
}
} else if (scriptTemplate.getSource() == 5) {
this.alertRecover(scriptTemplate);
} else if (scriptTemplate.getSource() == 1 || scriptTemplate.getSource() == 3) {
// 下发指令
this.send(scriptTemplate);
}
// 更新静默时间
this.updateSilent(scriptTemplate.getSilent(), scriptTemplate.getSceneId(), this.deviceNum);
}
}
return false;
}
/***
* 设备触发脚本处理
* @param scriptTemplate 解析后的Json脚本数据
* @return
*/
private boolean deviceTrigger(ScriptTemplate scriptTemplate) {
// 判断定制触发(执行一次)或设备上报
boolean isDeviceReport = StringUtils.isEmpty(deviceNum) ? false : true;
if (isDeviceReport) {
// 1. 匹配设备编号
boolean matchDeviceNum = Arrays.asList(scriptTemplate.getDeviceNums().split(",")).contains(deviceNum);
if (scriptTemplate.getType() < 4) {
// 2.匹配物模型标识符
ThingsModelSimpleItem matchItem = null;
if (thingsModelSimpleItems != null) {
for (ThingsModelSimpleItem item : thingsModelSimpleItems) {
if (item.getId().equals(scriptTemplate.getId())) {
matchItem = item;
break;
}
}
}
if (matchDeviceNum && matchItem != null) {
// 记录结果
if (sceneThingsModelItems == null) {
sceneThingsModelItems = new ArrayList<>();
}
SceneThingsModelItem sceneItem = new SceneThingsModelItem(scriptTemplate.getId(), matchItem.getValue(), type,
scriptTemplate.getScriptId(), scriptTemplate.getSceneId(), scriptTemplate.getProductId(), deviceNum);
sceneThingsModelItems.add(sceneItem);
// 3.设备上报值匹配
boolean isMatch = matchValue(scriptTemplate.getOperator(), scriptTemplate.getValue(), matchItem.getValue());
if (isMatch) {
return true;
}
}
} else {
//设备上线清理 离线延时匹配任务
if (type == 5 && scriptTemplate.getCheckdelay() != 0) {
String key = "CHECK_" + scriptTemplate.getSceneId() + deviceNum;
//设备上线删除告警任务
ruleService.removeCheckTask(key);
log.info("设备上线清理 离线延时匹配任务:{}", key);
}
// 上线,下线
if (matchDeviceNum && scriptTemplate.getType() == type) {
// 记录结果
if (sceneThingsModelItems == null) {
sceneThingsModelItems = new ArrayList<>();
}
SceneThingsModelItem sceneItem = new SceneThingsModelItem(type == 5 ? "online" : "offline", type == 5 ? "1" : "0", type,
scriptTemplate.getScriptId(), scriptTemplate.getSceneId(), scriptTemplate.getProductId(), deviceNum);
sceneThingsModelItems.add(sceneItem);
// 记录结果
return true;
}
}
} else {
// 定时触发/执行一次
int resultCount = 0;
// 3.查询设备最新上报值去匹配
for (String num : Arrays.asList(scriptTemplate.getDeviceNums().split(","))) {
// 数组类型key去除前缀值从逗号分隔的字符串获取
String id = "";
String value = "";
int index = 0;
if (scriptTemplate.getId().startsWith("array_")) {
id = scriptTemplate.getId().substring(9);
index = Integer.parseInt(scriptTemplate.getId().substring(6, 8));
} else {
id = scriptTemplate.getId();
}
String key = RedisKeyBuilder.buildTSLVCacheKey(scriptTemplate.getProductId(), num);
String cacheValue = redisCache.getCacheMapValue(key, id);
if (StringUtils.isEmpty(cacheValue)) {
continue;
}
ValueItem valueItem = JSON.parseObject(cacheValue, ValueItem.class);
if (scriptTemplate.getId().startsWith("array_")) {
String[] values = valueItem.getValue().split(",");
value = values[index];
} else {
value = valueItem.getValue();
}
boolean isMatch = matchValue(scriptTemplate.getOperator(), scriptTemplate.getValue(), value);
if (isMatch) {
// 记录结果
if (sceneThingsModelItems == null) {
sceneThingsModelItems = new ArrayList<>();
}
SceneThingsModelItem sceneItem = new SceneThingsModelItem(scriptTemplate.getId(), value, type,
scriptTemplate.getScriptId(), scriptTemplate.getSceneId(), scriptTemplate.getProductId(), num);
sceneThingsModelItems.add(sceneItem);
resultCount++;
}
}
// 任意设备匹配成功返回true
return resultCount > 0 ? true : false;
}
return false;
}
/***
* 产品触发脚本处理
* @param scriptTemplate
* @return
*/
private boolean productTrigger(ScriptTemplate scriptTemplate) {
// 判断定制触发(执行一次)或设备上报
boolean isDeviceReport = StringUtils.isEmpty(deviceNum) ? false : true;
if (isDeviceReport) {
// 匹配产品编号
boolean matchProductId = scriptTemplate.getProductId().equals(productId);
if (scriptTemplate.getType() < 4) {
// 匹配物模型标识符
ThingsModelSimpleItem matchItem = null;
if (thingsModelSimpleItems != null) {
for (ThingsModelSimpleItem item : thingsModelSimpleItems) {
if (item.getId().equals(scriptTemplate.getId())) {
matchItem = item;
break;
}
}
}
if (matchProductId && matchItem != null) {
// 记录结果
if (sceneThingsModelItems == null) {
sceneThingsModelItems = new ArrayList<>();
}
SceneThingsModelItem sceneItem = new SceneThingsModelItem(scriptTemplate.getId(), matchItem.getValue(), type,
scriptTemplate.getScriptId(), scriptTemplate.getSceneId(), scriptTemplate.getProductId(), deviceNum);
sceneThingsModelItems.add(sceneItem);
// 设备上报值匹配
boolean isMatch = matchValue(scriptTemplate.getOperator(), scriptTemplate.getValue(), matchItem.getValue());
if (isMatch) {
return true;
}
}
} else {
//设备上线清理 离线延时匹配任务
if (type == 5 && scriptTemplate.getCheckdelay() != 0) {
String key = "CHECK_" + scriptTemplate.getSceneId() + deviceNum;
//设备上线删除告警任务
ruleService.removeCheckTask(key);
log.info("设备上线清理 离线延时匹配任务:{}", key);
}
// 上线,下线
if (matchProductId && scriptTemplate.getType() == type) {
// 记录结果
if (sceneThingsModelItems == null) {
sceneThingsModelItems = new ArrayList<>();
}
SceneThingsModelItem sceneItem = new SceneThingsModelItem(type == 5 ? "online" : "offline", type == 5 ? "1" : "0", type,
scriptTemplate.getScriptId(), scriptTemplate.getSceneId(), scriptTemplate.getProductId(), deviceNum);
sceneThingsModelItems.add(sceneItem);
// 记录结果
return true;
}
}
} else {
// 定时触发/执行一次
int resultCount = 0;
// 查询设备最新上报值去匹配
String[] deviceNums = deviceService.getDeviceNumsByProductId(scriptTemplate.getProductId());
for (String num : deviceNums) {
// 数组类型key去除前缀值从逗号分隔的字符串获取
String id = "";
String value = "";
int index = 0;
if (scriptTemplate.getId().startsWith("array_")) {
id = scriptTemplate.getId().substring(9);
index = Integer.parseInt(scriptTemplate.getId().substring(6, 8));
} else {
id = scriptTemplate.getId();
}
String key = RedisKeyBuilder.buildTSLVCacheKey(scriptTemplate.getProductId(), num);
String cacheValue = redisCache.getCacheMapValue(key, id);
if (StringUtils.isEmpty(cacheValue)) {
continue;
}
ValueItem valueItem = JSON.parseObject(cacheValue, ValueItem.class);
if (scriptTemplate.getId().startsWith("array_")) {
String[] values = valueItem.getValue().split(",");
value = values[index];
} else {
value = valueItem.getValue();
}
boolean isMatch = matchValue(scriptTemplate.getOperator(), scriptTemplate.getValue(), value);
if (isMatch) {
// 记录结果
if (sceneThingsModelItems == null) {
sceneThingsModelItems = new ArrayList<>();
}
SceneThingsModelItem sceneItem = new SceneThingsModelItem(scriptTemplate.getId(), value, type,
scriptTemplate.getScriptId(), scriptTemplate.getSceneId(), scriptTemplate.getProductId(), num);
sceneThingsModelItems.add(sceneItem);
resultCount++;
}
}
// 任意设备匹配成功返回true
return resultCount > 0 ? true : false;
}
return false;
}
private void loadDelayTrigger(ScriptTemplate scriptTemplate) {
String key = "CHECK_" + scriptTemplate.getSceneId() + deviceNum;
//默认上线执行告警恢复 在延迟匹配周期内不告警
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
executor.schedule(() -> {
//设备未上线,则执行告警任务
if (scriptTemplate.getSource() == 4) {
// 告警
this.alert(scriptTemplate.getSceneId(),true);
} else if (scriptTemplate.getSource() == 1 || scriptTemplate.getSource() == 3) {
// 下发指令
this.send(scriptTemplate);
}
}, scriptTemplate.getCheckdelay(), TimeUnit.SECONDS);
log.info("延时执行告警任务:{}", key);
ruleService.addCheckTask(key, executor);
}
private boolean checkDeviceAlerting(Long sceneId, String deviceNum) {
AlertLog alertLog = new AlertLog();
alertLog.setSerialNumber(deviceNum);
alertLog.setStatus(2);
alertLog.setCreateBy(sceneId.toString());
Long count = alertLogService.selectAlertLogListCount(alertLog);
// 查询设备告警对应的场景是否有未处理告警
if (count > 0) {
return true;
} else {
return false;
}
}
/**
* 执行动作,下发指令
*
* @param scriptTemplate
*/
private void send(ScriptTemplate scriptTemplate) {
String[] deviceNumbers = null;
if (scriptTemplate.getSource() == 1) {
// 下发给指定设备
deviceNumbers = scriptTemplate.getDeviceNums().split(",");
} else if (scriptTemplate.getSource() == 3) {
// 下发给产品下所有设备
deviceNumbers = deviceService.getDeviceNumsByProductId(scriptTemplate.getProductId());
}
for (String deviceNum : deviceNumbers) {
InvokeReqDto reqDto = new InvokeReqDto();
reqDto.setProductId(scriptTemplate.getProductId());
reqDto.setSerialNumber(deviceNum);
reqDto.setModelName("");
reqDto.setType(1);
reqDto.setIdentifier(scriptTemplate.getId());
Map<String, Object> params = new HashMap<>();
params.put(scriptTemplate.getId(), scriptTemplate.getValue());
reqDto.setRemoteCommand(params);
reqDto.setParams(new JSONObject(reqDto.getRemoteCommand()));
functionInvoke.invokeNoReply(reqDto);
}
}
private void alertRecover(ScriptTemplate scriptTemplate) {
AlertLog alertLog = new AlertLog();
alertLog.setCreateBy(scriptTemplate.getRecoverId().toString());
alertLog.setSerialNumber(deviceNum);
alertLog.setStatus(2);
//自动设置告警处理状态
alertLogService.updateAlertLogStatus(alertLog);
//如果存在延时确认任务,则删除
String key = "CHECK_" + scriptTemplate.getRecoverId() + deviceNum;
//设备上线删除告警任务
ruleService.removeCheckTask(key);
}
/**
* 执行动作,告警处理
*
* @param sceneId 场景ID
*/
private void alert(Long sceneId, boolean isNodify) {
Set<Long> sceneIdSet = sceneThingsModelItems.stream().map(SceneThingsModelItem::getSceneId).collect(Collectors.toSet());
List<SceneTerminalUserVO> sceneTerminalUserVOList = sceneService.selectTerminalUserBySceneIds(sceneIdSet);
Map<Long, SceneTerminalUserVO> sceneTerminalUserMap = sceneTerminalUserVOList.stream().collect(Collectors.toMap(SceneTerminalUserVO::getSceneId, Function.identity()));
List<AlertLog> alertLogList = new ArrayList<>();
for (SceneThingsModelItem sceneThingsModelItem : sceneThingsModelItems) {
// 查询设备信息
Device device = deviceService.selectDeviceBySerialNumber(sceneThingsModelItem.getDeviceNumber());
Optional.ofNullable(device).orElseThrow(() -> new ServiceException("告警推送,设备不存在" + "[{" + sceneThingsModelItem.getDeviceNumber() + "}]"));
// 判断是否是终端用户的场景
SceneTerminalUserVO sceneTerminalUserVO = sceneTerminalUserMap.get(sceneId);
if (1 == sceneTerminalUserVO.getTerminalUser()) {
AlertLog alertLog = this.getTerminalUserAlertLog(sceneTerminalUserVO, device, sceneThingsModelItem);
alertLogList.add(alertLog);
continue;
}
// 获取场景相关的告警参数,告警必须要是启动状态
List<AlertSceneSendVO> sceneSendVOList = alertService.listByAlertIds(sceneId);
if (CollectionUtils.isEmpty(sceneSendVOList)) {
continue;
}
// 获取告警关联模版id
for (AlertSceneSendVO alertSceneSendVO : sceneSendVOList) {
// 开启通知发送
if (isNodify) {
AlertPushParams alertPushParams = buildAlertPushParams(device, sceneThingsModelItem);
List<AlertNotifyTemplate> alertNotifyTemplateList = alertService.listAlertNotifyTemplate(alertSceneSendVO.getAlertId());
alertPushParams.setAlertName(alertSceneSendVO.getAlertName());
for (AlertNotifyTemplate alertNotifyTemplate : alertNotifyTemplateList) {
alertPushParams.setNotifyTemplateId(alertNotifyTemplate.getNotifyTemplateId());
notifySendService.alertSend(alertPushParams);
}
}
AlertLog alertLog = getAlertLog(alertSceneSendVO, device, sceneThingsModelItem);
alertLogList.add(alertLog);
}
}
// 保存告警日志
if (CollectionUtils.isNotEmpty(alertLogList)) {
alertLogService.insertAlertLogBatch(alertLogList);
}
}
private AlertPushParams buildAlertPushParams(Device device, SceneThingsModelItem sceneThingsModelItem) {
// 获取告警推送参数
AlertPushParams alertPushParams = new AlertPushParams();
alertPushParams.setDeviceName(device.getDeviceName());
alertPushParams.setSerialNumber(sceneThingsModelItem.getDeviceNumber());
// List<DeviceUser> deviceUserList = deviceUserService.getDeviceUserAndShare(device.getDeviceId());
// 多租户改版查询自己配置的告警用户
DeviceAlertUser deviceAlertUser = new DeviceAlertUser();
deviceAlertUser.setDeviceId(device.getDeviceId());
List<DeviceAlertUser> deviceUserList = deviceAlertUserService.selectDeviceAlertUserList(deviceAlertUser);
if (CollectionUtils.isNotEmpty(deviceUserList)) {
alertPushParams.setUserPhoneSet(deviceUserList.stream().map(DeviceAlertUser::getPhoneNumber).filter(StringUtils::isNotEmpty).collect(Collectors.toSet()));
alertPushParams.setUserIdSet(deviceUserList.stream().map(DeviceAlertUser::getUserId).collect(Collectors.toSet()));
}
String address;
if (StringUtils.isNotEmpty(device.getNetworkAddress())) {
address = device.getNetworkAddress();
} else if (StringUtils.isNotEmpty(device.getNetworkIp())) {
address = device.getNetworkIp();
} else if (Objects.nonNull(device.getLongitude()) && Objects.nonNull(device.getLatitude())) {
address = device.getLongitude() + "," + device.getLatitude();
} else {
address = "未知地点";
}
alertPushParams.setAddress(address);
alertPushParams.setAlertTime(DateUtils.parseDateToStr(DateUtils.YY_MM_DD_HH_MM_SS, new Date()));
return alertPushParams;
}
private AlertLog getTerminalUserAlertLog(SceneTerminalUserVO sceneTerminalUserVO, Device device, SceneThingsModelItem sceneThingsModelItem) {
AlertLog alertLog = new AlertLog();
alertLog.setAlertName("设备告警");
alertLog.setAlertLevel(1L);
alertLog.setSerialNumber(sceneThingsModelItem.getDeviceNumber());
alertLog.setProductId(sceneThingsModelItem.getProductId());
alertLog.setDeviceName(device.getDeviceName());
alertLog.setUserId(sceneTerminalUserVO.getUserId());
alertLog.setCreateBy(sceneThingsModelItem.getSceneId().toString());
// 统一未处理
alertLog.setStatus(2);
JSONObject jsonObject = new JSONObject();
jsonObject.put("id", sceneThingsModelItem.getId());
jsonObject.put("value", sceneThingsModelItem.getValue());
jsonObject.put("remark", "");
alertLog.setDetail(jsonObject.toJSONString());
alertLog.setCreateTime(new Date());
alertLog.setCreateBy(sceneThingsModelItem.getSceneId().toString());
return alertLog;
}
/**
* 组装告警日志
*
* @param alertSceneSendVO
* @return com.fastbee.iot.domain.AlertLog
* @param: device
*/
private AlertLog getAlertLog(AlertSceneSendVO alertSceneSendVO, Device device, SceneThingsModelItem sceneThingsModelItem) {
AlertLog alertLog = new AlertLog();
alertLog.setAlertName(alertSceneSendVO.getAlertName());
alertLog.setAlertLevel(alertSceneSendVO.getAlertLevel());
alertLog.setSerialNumber(sceneThingsModelItem.getDeviceNumber());
alertLog.setProductId(sceneThingsModelItem.getProductId());
alertLog.setDeviceName(device.getDeviceName());
alertLog.setUserId(device.getTenantId());
alertLog.setCreateBy(sceneThingsModelItem.getSceneId().toString());
// 统一未处理
alertLog.setStatus(2);
JSONObject jsonObject = new JSONObject();
jsonObject.put("id", sceneThingsModelItem.getId());
jsonObject.put("value", sceneThingsModelItem.getValue());
jsonObject.put("remark", "");
alertLog.setDetail(jsonObject.toJSONString());
alertLog.setCreateTime(new Date());
alertLog.setCreateBy(sceneThingsModelItem.getSceneId().toString());
return alertLog;
}
/**
* 检查静默周期物模型值是否匹配
*
* @param operator 操作符
* @param triggerValue 触发值
* @param value 上报的值
* @return
*/
private boolean matchValue(String operator, String triggerValue, String value) {
boolean result = false;
// 操作符比较
switch (operator) {
case "=":
result = value.equals(triggerValue);
break;
case "!=":
result = !value.equals(triggerValue);
break;
case ">":
if (isNumeric(value) && isNumeric(triggerValue)) {
result = Double.parseDouble(value) > Double.parseDouble(triggerValue);
}
break;
case "<":
if (isNumeric(value) && isNumeric(triggerValue)) {
result = Double.parseDouble(value) < Double.parseDouble(triggerValue);
}
break;
case ">=":
if (isNumeric(value) && isNumeric(triggerValue)) {
result = Double.parseDouble(value) >= Double.parseDouble(triggerValue);
}
break;
case "<=":
if (isNumeric(value) && isNumeric(triggerValue)) {
result = Double.parseDouble(value) <= Double.parseDouble(triggerValue);
}
break;
case "between":
// 比较值用英文中划线分割 -
String[] triggerValues = triggerValue.split("-");
if (isNumeric(value) && isNumeric(triggerValues[0]) && isNumeric(triggerValues[1])) {
result = Double.parseDouble(value) >= Double.parseDouble(triggerValues[0]) && Double.parseDouble(value) <= Double.parseDouble(triggerValues[1]);
}
break;
case "notBetween":
// 比较值用英文中划线分割 -
String[] trigValues = triggerValue.split("-");
if (isNumeric(value) && isNumeric(trigValues[0]) && isNumeric(trigValues[1])) {
result = Double.parseDouble(value) <= Double.parseDouble(trigValues[0]) || Double.parseDouble(value) >= Double.parseDouble(trigValues[1]);
}
break;
case "contain":
result = value.contains(triggerValue);
break;
case "notContain":
result = !value.contains(triggerValue);
break;
default:
break;
}
return result;
}
/**
* 检查静默时间
*
* @param silent
* @param sceneId
* @return
*/
private boolean checkSilent(int silent, Long sceneId, String serialNumber) {
if (silent == 0 || sceneId == 0) {
return true;
}
// silent:scene_场景编号
String key = "silent:" + "scene_" + sceneId + ":" + serialNumber;
Calendar calendar = Calendar.getInstance();
// 查询静默截止时间
Long expireTime = redisCache.getCacheObject(key);
if (expireTime == null) {
// 添加场景静默时间
calendar.add(Calendar.MINUTE, silent);
redisCache.setCacheObject(key, calendar.getTimeInMillis());
return true;
} else {
Long NowTimestamp = Calendar.getInstance().getTimeInMillis();
if (NowTimestamp > expireTime) {
return true;
}
return false;
}
}
/**
* 更新静默时间
*
* @param sceneId
* @param silent
*/
private void updateSilent(int silent, Long sceneId, String serialNumber) {
if (silent == 0 || sceneId == 0) {
return;
}
// 更新场景静默时间
String key = "silent:" + "scene_" + sceneId + ":" + serialNumber;
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.MINUTE, silent);
redisCache.setCacheObject(key, calendar.getTimeInMillis());
}
/**
* 判断字符串是否为整数或小数
*/
private boolean isNumeric(String str) {
Pattern pattern = compile("[0-9]*\\.?[0-9]+");
Matcher isNum = pattern.matcher(str);
if (!isNum.matches()) {
return false;
}
return true;
}
}

View File

@ -0,0 +1,46 @@
package com.fastbee.mq.service;
import com.fastbee.common.core.mq.InvokeReqDto;
import com.fastbee.mq.model.ReportDataBo;
/**
* 客户端上报数据处理方法集合
* @author bill
*/
public interface IDataHandler {
/**
* 上报属性或功能处理
*
* @param bo 上报数据模型
*/
public void reportData(ReportDataBo bo);
/**
* 上报事件
*
* @param bo 上报数据模型
*/
public void reportEvent(ReportDataBo bo);
/**
* 上报设备信息
* @param bo 上报数据模型
*/
public void reportDevice(ReportDataBo bo);
/**
* 计算场景变量的值
* @param id 主键id
* @return java.lang.String
*/
String calculateSceneModelTagValue(Long id);
/**
* 场景变量指令下发
* @param reqDto 下发类
* @return void
*/
void invokeSceneModelTagValue(InvokeReqDto reqDto, String messageId);
}

View File

@ -0,0 +1,47 @@
package com.fastbee.mq.service;
import com.fastbee.common.core.mq.DeviceReport;
import com.fastbee.common.core.mq.DeviceReportBo;
import com.fastbee.iot.domain.Device;
import com.fastbee.iot.model.DeviceStatusVO;
import com.fastbee.protocol.base.protocol.IProtocol;
/**
* 处理设备上报数据解析
* @author gsb
* @date 2022/10/10 13:48
*/
public interface IDeviceReportMessageService {
/**
* 处理设备主动上报数据
* @param bo
*/
public void parseReportMsg(DeviceReportBo bo);
/**
* 处理设备普通消息回调
* @param bo
*/
public void parseReplyMsg(DeviceReportBo bo);
/**
* 处理设备OTA升级
* @param bo
*/
public void parseOTAUpdateReply(DeviceReportBo bo);
/**
* 构建消息
* @param bo
*/
public DeviceStatusVO buildReport(DeviceReportBo bo);
/**
* 根据产品id获取协议处理器
*/
IProtocol selectedProtocol(Long productId);
}

View File

@ -0,0 +1,11 @@
package com.fastbee.mq.service;
import com.fastbee.common.core.mq.DeviceTestReportBo;
/**
* @author bill
*/
public interface IDeviceTestService {
public void messageHandler(DeviceTestReportBo testReportBo);
}

View File

@ -0,0 +1,28 @@
package com.fastbee.mq.service;
import com.fastbee.common.core.domain.AjaxResult;
import com.fastbee.common.core.mq.InvokeReqDto;
import java.util.Map;
/**
* 设备指令下发接口
* @author gsb
* @date 2022/12/5 11:03
*/
public interface IFunctionInvoke {
/**
* 服务调用,等待设备响应
* @param reqDto 服务下发对象
* @return 数据结果
*/
public AjaxResult invokeReply(InvokeReqDto reqDto);
/**
* 服务调用,设备不响应
* @param reqDto 服务下发对象
* @return 消息id messageId
*/
public AjaxResult invokeNoReply(InvokeReqDto reqDto);
}

View File

@ -0,0 +1,17 @@
package com.fastbee.mq.service;
/**
* 设备消息推送mq
* @author bill
*/
public interface IMessagePublishService {
/**
* 发布消息到mq
* @param message 设备消息
* @param channel 推送channel
*/
public void publish(Object message,String channel);
}

View File

@ -0,0 +1,77 @@
package com.fastbee.mq.service;
import com.fastbee.common.core.mq.DeviceReportBo;
import com.fastbee.common.core.mq.MQSendMessageBo;
import com.fastbee.common.core.mq.message.DeviceDownMessage;
import com.fastbee.common.core.mq.message.FunctionCallBackBo;
import com.fastbee.common.core.mq.ota.OtaUpgradeBo;
import com.fastbee.common.core.thingsModel.ThingsModelSimpleItem;
import com.fastbee.common.enums.TopicType;
import com.fastbee.iot.domain.Device;
import com.fastbee.mq.model.ReportDataBo;
import org.checkerframework.checker.units.qual.C;
import java.util.List;
public interface IMqttMessagePublish {
/**
* 服务(指令)下发
*/
public void funcSend(MQSendMessageBo bo);
/**
* OTA升级下发
*/
public void upGradeOTA(OtaUpgradeBo bo);
/**
* 下发数据编码
*/
FunctionCallBackBo buildMessage(MQSendMessageBo bo);
/**
* 1.发布设备状态
*/
public void publishStatus(Long productId, String deviceNum, int deviceStatus, int isShadow, int rssi);
/**
* 2.发布设备信息
*/
public void publishInfo(Long productId, String deviceNum);
/**
* 3.发布时钟同步信息
*
* @param bo 数据模型
*/
public void publishNtp(ReportDataBo bo);
/**
* 4.发布属性
* delay 延时,秒为单位
*/
public void publishProperty(Long productId, String deviceNum, List<ThingsModelSimpleItem> thingsList, int delay);
/**
* 5.发布功能
* delay 延时,秒为单位
*/
public void publishFunction(Long productId, String deviceNum, List<ThingsModelSimpleItem> thingsList, int delay);
/**
* 设备数据同步
*
* @param deviceNumber 设备编号
* @return 设备
*/
public Device deviceSynchronization(String deviceNumber);
}

View File

@ -0,0 +1,22 @@
package com.fastbee.mq.service;
import com.fastbee.mq.model.ReportDataBo;
import java.util.concurrent.ScheduledExecutorService;
/**
* 规则引擎处理数据方法集合
* @author bill
*/
public interface IRuleEngine {
/**
* 规则匹配(告警和场景联动)
*
* @param bo 上报数据模型
*/
public void ruleMatch(ReportDataBo bo);
public void addCheckTask(String key, ScheduledExecutorService task);
public void removeCheckTask(String key);
}

View File

@ -0,0 +1,86 @@
package com.fastbee.mq.service.impl;
import com.fastbee.common.core.mq.DeviceReportBo;
import com.fastbee.common.enums.TopicType;
import com.fastbee.common.utils.StringUtils;
import com.fastbee.common.utils.gateway.mq.TopicsUtils;
import com.fastbee.mq.model.ReportDataBo;
import com.fastbee.mq.service.IDataHandler;
import com.fastbee.mq.service.IMqttMessagePublish;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* @author gsb
* @date 2023/2/27 14:42
*/
@Component
@Slf4j
public class DeviceOtherMsgHandler {
@Resource
private TopicsUtils topicsUtils;
@Resource
private IDataHandler dataHandler;
@Resource
private IMqttMessagePublish messagePublish;
/**
* true: 使用netty搭建的mqttBroker false: 使用emq
*/
@Value("${server.broker.enabled}")
private Boolean enabled;
/**
* 非属性消息消息处理入口
*
* @param bo
*/
public void messageHandler(DeviceReportBo bo) {
String type = "";
String name = topicsUtils.parseTopicName(bo.getTopicName());
if (StringUtils.isEmpty(name) || name.endsWith(TopicType.FUNCTION_GET.getTopicSuffix())) return;
ReportDataBo data = this.buildReportData(bo);
TopicType topicType = TopicType.getType(name);
switch (topicType) {
case INFO_POST:
dataHandler.reportDevice(data);
break;
case NTP_POST:
messagePublish.publishNtp(data);
break;
case FUNCTION_POST:
data.setShadow(false);
data.setType(2);
data.setRuleEngine(true);
dataHandler.reportData(data);
break;
case EVENT_POST:
data.setType(3);
data.setRuleEngine(true);
dataHandler.reportEvent(data);
break;
}
}
/**
* 组装数据
*/
private ReportDataBo buildReportData(DeviceReportBo bo) {
String message = new String(bo.getData());
log.info("收到设备信息[{}]", message);
Long productId = topicsUtils.parseProductId(bo.getTopicName());
String serialNumber = topicsUtils.parseSerialNumber(bo.getTopicName());
ReportDataBo dataBo = new ReportDataBo();
dataBo.setMessage(message);
dataBo.setProductId(productId);
dataBo.setSerialNumber(serialNumber);
dataBo.setRuleEngine(false);
return dataBo;
}
}

View File

@ -0,0 +1,143 @@
package com.fastbee.mq.service.impl;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.fastbee.common.core.domain.AjaxResult;
import com.fastbee.common.core.domain.model.LoginUser;
import com.fastbee.common.core.mq.DeviceReplyBo;
import com.fastbee.common.core.mq.InvokeReqDto;
import com.fastbee.common.core.mq.MQSendMessageBo;
import com.fastbee.common.core.mq.MessageReplyBo;
import com.fastbee.common.core.protocol.modbus.ModbusCode;
import com.fastbee.common.core.redis.RedisCache;
import com.fastbee.common.core.redis.RedisKeyBuilder;
import com.fastbee.common.enums.FunctionReplyStatus;
import com.fastbee.common.enums.ResultCode;
import com.fastbee.common.enums.ThingsModelType;
import com.fastbee.common.enums.scenemodel.SceneModelVariableTypeEnum;
import com.fastbee.common.exception.ServiceException;
import com.fastbee.common.utils.DateUtils;
import com.fastbee.common.utils.MessageUtils;
import com.fastbee.common.utils.SecurityUtils;
import com.fastbee.common.utils.StringUtils;
import com.fastbee.common.utils.bean.BeanUtils;
import com.fastbee.iot.cache.ITSLCache;
import com.fastbee.iot.domain.Device;
import com.fastbee.iot.domain.FunctionLog;
import com.fastbee.iot.model.ThingsModels.ThingsModelValueItem;
import com.fastbee.iot.service.IDeviceService;
import com.fastbee.iot.service.IFunctionLogService;
import com.fastbee.iot.service.IOrderControlService;
import com.fastbee.iot.util.SnowflakeIdWorker;
import com.fastbee.mq.redischannel.producer.MessageProducer;
import com.fastbee.mq.service.IDataHandler;
import com.fastbee.mq.service.IFunctionInvoke;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.weaver.loadtime.Aj;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
/**
* @author gsb
* @date 2022/12/5 11:34
*/
@Slf4j
@Service
public class FunctionInvokeImpl implements IFunctionInvoke {
@Resource
private IDataHandler dataHandler;
@Resource
private IFunctionLogService functionLogService;
private SnowflakeIdWorker snowflakeIdWorker = new SnowflakeIdWorker(2);
/**
* 服务调用,等待设备响应
* @param reqDto 服务下发对象
* @return 数据结果
*/
@Override
public AjaxResult invokeReply(InvokeReqDto reqDto){
AjaxResult ajaxResult = invokeNoReply(reqDto);
String messageId = ajaxResult.get("msg")+"";
int status = this.queryData(messageId);
ajaxResult.put("code", status);
if (status == FunctionReplyStatus.UNKNOWN.getCode()) {
ajaxResult.put("msg", FunctionReplyStatus.UNKNOWN.getMessage());
this.updateFunctionLog(messageId);
}else {
ajaxResult.put("msg",FunctionReplyStatus.SUCCESS.getMessage());
}
return ajaxResult;
}
/**
* 服务调用,设备不响应
* @param reqDto 服务下发对象
* @return 消息id messageId
*/
@Override
public AjaxResult invokeNoReply(InvokeReqDto reqDto){
log.debug("=>下发指令请求:[{}]",reqDto);
if (null != reqDto.getSceneModelId() && null != reqDto.getVariableType() && !SceneModelVariableTypeEnum.THINGS_MODEL.getType().equals(reqDto.getVariableType())) {
String messageId = snowflakeIdWorker.nextId() + "";
dataHandler.invokeSceneModelTagValue(reqDto, messageId);
return AjaxResult.success(messageId);
}
if (StringUtils.isEmpty(reqDto.getSerialNumber())) {
return AjaxResult.error("设备编号不能为空");
}
MQSendMessageBo bo = new MQSendMessageBo();
BeanUtils.copyBeanProp(bo,reqDto);
long id = snowflakeIdWorker.nextId();
String messageId = id+"";
bo.setMessageId(messageId+"");
bo.setUserId(SecurityUtils.getUserId());
MessageProducer.sendFunctionInvoke(bo);
return AjaxResult.success(messageId);
}
/**
* 轮询5S拿返回值
* @param messageId 消息id
* @return 回调对象
*/
private int queryData(String messageId) {
FunctionLog functionLog = null;
int status = 204;
long startTime = DateUtils.getTimestamp();
/**5秒轮询拿值*/
while ((DateUtils.getTimestamp() - startTime) < 5 * 1000) {
try {
functionLog = functionLogService.selectLogByMessageId(messageId);
if (null != functionLog && functionLog.getResultCode() == FunctionReplyStatus.SUCCESS.getCode()) {
status = FunctionReplyStatus.SUCCESS.getCode();
break;
}
Thread.sleep(1000);
} catch (Exception e) {
log.error("=>读取设备下发指定回执异常", e);
}
}
return status;
}
/**
* 更新设备下发指令,当设备超时未回复情况
* @param messageId
*/
private void updateFunctionLog(String messageId){
FunctionLog functionLog = new FunctionLog();
functionLog.setResultCode(FunctionReplyStatus.UNKNOWN.getCode());
functionLog.setResultMsg(FunctionReplyStatus.UNKNOWN.getMessage());
functionLog.setReplyTime(DateUtils.getNowDate());
functionLog.setMessageId(messageId);
functionLogService.updateByMessageId(functionLog);
}
}

View File

@ -0,0 +1,77 @@
package com.fastbee.mq.service.impl;
import com.fastbee.base.session.Session;
import com.fastbee.base.session.SessionManager;
import com.fastbee.common.core.domain.AjaxResult;
import com.fastbee.common.core.protocol.Message;
import com.fastbee.common.exception.ServiceException;
import com.fastbee.modbus.model.ModbusRtu;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
import java.time.Duration;
/**
* @author gsb
* @date 2022/11/22 10:30
*/
@Component
@Slf4j
public class MessageManager {
private static final Mono<Void> NEVER = Mono.never();
private static final Mono OFFLINE_EXCEPTION = Mono.error( new ServiceException("离线的客户端",4000));
private static final Mono OFFLINE_RESULT = Mono.just(new AjaxResult(4000, "离线的客户端"));
private static final Mono SEND_FAIL_RESULT = Mono.just(new AjaxResult(4001, "消息发送失败"));
private SessionManager sessionManager;
public MessageManager(SessionManager sessionManager){
this.sessionManager = sessionManager;
}
public Mono<Void> notifyR(String sessionId, ModbusRtu request){
Session session = sessionManager.getSession(sessionId);
if (session == null){
return OFFLINE_EXCEPTION;
}
return session.notify(request);
}
public <T> Mono<AjaxResult> requestR(String sessionId, Message request, Class<T> responseClass){
Session session = sessionManager.getSession(sessionId);
if (session == null){
return OFFLINE_RESULT;
}
return session.request(request,responseClass)
.map(message -> AjaxResult.success(message))
.onErrorResume(e ->{
log.warn("消息发送失败:{}",e);
return SEND_FAIL_RESULT;
});
}
/**
* 下发指令等待回复
* @param sessionId
* @return
*/
public <T> Mono<T> request(String sessionId, ModbusRtu request, Class<T> responseClass, long timeout){
return request(sessionId,request,responseClass).timeout(Duration.ofMillis(timeout));
}
/**
* 下发指令,不等待回复
* @param sessionId
* @return
*/
public <T> Mono<T> request(String sessionId, ModbusRtu request, Class<T> responseClass){
Session session = sessionManager.getSession(sessionId);
if (session == null){
return OFFLINE_EXCEPTION;
}
return session.request(request,responseClass);
}
}

View File

@ -0,0 +1,108 @@
package com.fastbee.mq.service.impl;
import com.alibaba.fastjson2.JSON;
import com.fastbee.common.core.thingsModel.ThingsModelSimpleItem;
import com.fastbee.iot.domain.*;
import com.fastbee.iot.mapper.*;
import com.fastbee.iot.model.ScriptCondition;
import com.fastbee.iot.service.IScriptService;
import com.fastbee.mq.model.ReportDataBo;
import com.fastbee.mq.service.IRuleEngine;
import com.fastbee.mq.ruleEngine.SceneContext;
import com.fastbee.ruleEngine.core.FlowLogExecutor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ScheduledExecutorService;
import static java.util.regex.Pattern.compile;
/**
* 规则引擎处理数据方法
*
* @author bill
*/
@Component
@Slf4j
public class RuleEngineHandler implements IRuleEngine {
@Resource
private SceneDeviceMapper sceneDeviceMapper;
@Resource
private IScriptService scriptService;
@Autowired
private FlowLogExecutor flowExecutor;
private static final Map<String, ScheduledExecutorService> CEHCK_CACHE = new ConcurrentHashMap<>();
/**
* 规则匹配(告警和场景联动)
*
* @param bo 上报数据模型bo
* @see ReportDataBo
*/
public void ruleMatch(ReportDataBo bo) {
try {
// 场景联动处理
this.sceneProcess(bo);
} catch (Exception e) {
log.error("接收数据,解析数据时异常 message={}", e, e.getMessage());
}
}
/**
* 场景规则处理
*/
public void sceneProcess(ReportDataBo bo) throws ExecutionException, InterruptedException {
// 查询设备关联的场景
SceneDevice sceneDeviceParam = new SceneDevice();
sceneDeviceParam.setProductId(bo.getProductId());
sceneDeviceParam.setSerialNumber(bo.getSerialNumber());
List<Scene> sceneList = sceneDeviceMapper.selectTriggerDeviceRelateScenes(sceneDeviceParam);
int type = bo.getType();
// 获取上报的物模型
List<ThingsModelSimpleItem> thingsModelSimpleItems = bo.getDataList();
if (CollectionUtils.isEmpty(bo.getDataList())) {
thingsModelSimpleItems = JSON.parseArray(bo.getMessage(), ThingsModelSimpleItem.class);
}
// 执行场景规则,异步非阻塞
for (Scene scene : sceneList) {
SceneContext context = new SceneContext(bo.getSerialNumber(), bo.getProductId(), type, thingsModelSimpleItems);
ScriptCondition scriptCondition = ScriptCondition.builder().sceneId(scene.getSceneId()).build();
String requestId = scene.getChainName();
List<Script> list = scriptService.selectExecRuleScriptList(scriptCondition);
for(Script script : list) {
requestId = requestId + "/" + script.getScriptId();
}
flowExecutor.execute2FutureWithRid(String.valueOf(scene.getChainName()), null, requestId, context);
}
}
public void addCheckTask(String key, ScheduledExecutorService task) {
ScheduledExecutorService taskold = CEHCK_CACHE.get(key);
if (taskold != null) {
taskold.shutdown();
}
CEHCK_CACHE.put(key, task);
}
public void removeCheckTask(String key) {
ScheduledExecutorService taskold = CEHCK_CACHE.get(key);
if (taskold != null) {
taskold.shutdown();
CEHCK_CACHE.remove(key);
}
}
}