作者 钟来

初始提交

正在显示 35 个修改的文件 包含 1842 行增加11 行删除
@@ -2,6 +2,7 @@ @@ -2,6 +2,7 @@
2 <project version="4"> 2 <project version="4">
3 <component name="CompilerConfiguration"> 3 <component name="CompilerConfiguration">
4 <annotationProcessing> 4 <annotationProcessing>
  5 + <profile default="true" name="Default" enabled="true" />
5 <profile name="Maven default annotation processors profile" enabled="true"> 6 <profile name="Maven default annotation processors profile" enabled="true">
6 <sourceOutputDir name="target/generated-sources/annotations" /> 7 <sourceOutputDir name="target/generated-sources/annotations" />
7 <sourceTestOutputDir name="target/generated-test-sources/test-annotations" /> 8 <sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
此 diff 太大无法显示。
@@ -12,23 +12,71 @@ @@ -12,23 +12,71 @@
12 <artifactId>lh-mqtt-service</artifactId> 12 <artifactId>lh-mqtt-service</artifactId>
13 13
14 <dependencies> 14 <dependencies>
  15 + <!-- 通用工具-->
15 <dependency> 16 <dependency>
16 - <groupId>com.luhui.lyl.device.service</groupId>  
17 - <artifactId>ly-device-mqtt-comm</artifactId>  
18 - <version>${project.parent.version}</version> 17 + <groupId>com.zhonglai.luhui</groupId>
  18 + <artifactId>ruoyi-common</artifactId>
  19 + </dependency>
  20 +
  21 + <!-- 文档 -->
  22 + <dependency>
  23 + <groupId>io.springfox</groupId>
  24 + <artifactId>springfox-swagger2</artifactId>
  25 + <version>${swagger.version}</version>
19 <exclusions> 26 <exclusions>
20 <exclusion> 27 <exclusion>
21 - <groupId>org.springframework.boot</groupId>  
22 - <artifactId>**</artifactId> 28 + <groupId>io.swagger</groupId>
  29 + <artifactId>swagger-models</artifactId>
  30 + </exclusion>
  31 + <exclusion>
  32 + <groupId>com.google.guava</groupId>
  33 + <artifactId>guava</artifactId>
23 </exclusion> 34 </exclusion>
24 </exclusions> 35 </exclusions>
25 </dependency> 36 </dependency>
  37 + <!--https://mvnrepository.com/artifact/io.swagger/swagger-models-->
  38 + <dependency>
  39 + <groupId>io.swagger</groupId>
  40 + <artifactId>swagger-models</artifactId>
  41 + <version>${swagger-models.version}</version>
  42 + </dependency>
  43 + <dependency>
  44 + <groupId>io.springfox</groupId>
  45 + <artifactId>springfox-swagger-ui</artifactId>
  46 + <version>${swagger.version}</version>
  47 + </dependency>
  48 + <!--&lt;!&ndash; https://mvnrepository.com/artifact/com.github.xiaoymin/swagger-bootstrap-ui &ndash;&gt;-->
  49 + <dependency>
  50 + <groupId>com.github.xiaoymin</groupId>
  51 + <artifactId>swagger-bootstrap-ui</artifactId>
  52 + <version>${swagger-ui.version}</version>
  53 + </dependency>
  54 +
  55 + <dependency>
  56 + <groupId>tk.mybatis</groupId>
  57 + <artifactId>mapper</artifactId>
  58 + <!-- 建议使用最新版本,最新版本请从项目首页查找 -->
  59 + </dependency>
  60 + <dependency>
  61 + <groupId>tk.mybatis</groupId>
  62 + <artifactId>mapper-spring-boot-starter</artifactId>
  63 + </dependency>
  64 +
  65 + <!-- mqtt -->
  66 + <dependency>
  67 + <groupId>org.eclipse.paho</groupId>
  68 + <artifactId>org.eclipse.paho.client.mqttv3</artifactId>
  69 + </dependency>
26 70
27 <dependency> 71 <dependency>
28 <groupId>org.springframework.boot</groupId> 72 <groupId>org.springframework.boot</groupId>
29 <artifactId>spring-boot-starter-tomcat</artifactId> 73 <artifactId>spring-boot-starter-tomcat</artifactId>
30 <scope>provided</scope> 74 <scope>provided</scope>
31 </dependency> 75 </dependency>
  76 + <dependency>
  77 + <groupId>net.jodah</groupId>
  78 + <artifactId>expiringmap</artifactId>
  79 + </dependency>
32 </dependencies> 80 </dependencies>
33 81
34 <build> 82 <build>
  1 +package com.zhonglai.luhui.mqtt.comm.agreement;
  2 +
  3 +import com.zhonglai.luhui.mqtt.comm.factory.BusinessAgreement;
  4 +import com.zhonglai.luhui.mqtt.comm.factory.BusinessAgreementFactory;
  5 +import org.springframework.beans.factory.annotation.Autowired;
  6 +import org.springframework.stereotype.Component;
  7 +
  8 +import java.util.Map;
  9 +
  10 +/**
  11 + * 业务协议工厂实现
  12 + */
  13 +@Component
  14 +public class BusinessAgreementFactoryImpl implements BusinessAgreementFactory {
  15 + @Autowired
  16 + private Map<String, BusinessAgreement> businessAgreementMap;
  17 +
  18 + @Override
  19 + public BusinessAgreement createBusinessAgreement(String topicType) {
  20 + BusinessAgreement businessAgreement = businessAgreementMap.get(topicType.toUpperCase());
  21 + if(null == businessAgreement) //没有找到就用默认的
  22 + {
  23 + businessAgreementMap.get("DEFAULT");
  24 + }
  25 + return businessAgreement;
  26 + }
  27 +}
  1 +package com.zhonglai.luhui.mqtt.comm.agreement;
  2 +
  3 +import com.zhonglai.luhui.mqtt.comm.dto.ServerDto;
  4 +import com.zhonglai.luhui.mqtt.comm.factory.BusinessAgreement;
  5 +import com.zhonglai.luhui.mqtt.comm.factory.Topic;
  6 +import org.slf4j.Logger;
  7 +import org.slf4j.LoggerFactory;
  8 +import org.springframework.stereotype.Service;
  9 +
  10 +import java.nio.charset.Charset;
  11 +
  12 +@Service("DEFAULT")
  13 +public class DefaultAgreement implements BusinessAgreement<byte[]> {
  14 + private static final Logger log = LoggerFactory.getLogger(DefaultAgreement.class);
  15 +
  16 + @Override
  17 + public ServerDto analysis(Topic topic, byte[] data) throws Exception {
  18 + log.info("未知的topic{} payload{}",topic, new String(data, Charset.forName("UTF-8")));
  19 + return null;
  20 + }
  21 +
  22 + @Override
  23 + public byte[] toData(byte[] data) {
  24 + return data;
  25 + }
  26 +}
  1 +package com.zhonglai.luhui.mqtt.comm.clien;
  2 +
  3 +
  4 +import com.ruoyi.common.core.domain.Message;
  5 +import com.zhonglai.luhui.mqtt.comm.dto.ServerAgreementContent;
  6 +
  7 +/**
  8 + * 客户端链接
  9 + */
  10 +public interface ClienConnection {
  11 + public void close();
  12 +
  13 + /**
  14 + * 回复
  15 + * @param agreementContent
  16 + */
  17 + public void reply(ServerAgreementContent agreementContent);
  18 +
  19 + /**
  20 + * 回复
  21 + */
  22 + public Message getReplyMessage();
  23 +}
  1 +package com.zhonglai.luhui.mqtt.comm.clien.impl;
  2 +
  3 +
  4 +import com.ruoyi.common.core.domain.Message;
  5 +import com.ruoyi.common.core.domain.MessageCode;
  6 +import com.ruoyi.common.core.domain.MessageCodeType;
  7 +import com.zhonglai.luhui.mqtt.comm.clien.ClienConnection;
  8 +import com.zhonglai.luhui.mqtt.comm.dto.ServerAgreementContent;
  9 +
  10 +public class ClienConnectionImpl implements ClienConnection {
  11 + private Message message = new Message();
  12 +
  13 + @Override
  14 + public void close() {
  15 + this.message.setCode(MessageCode.DEFAULT_FAIL_CODE);
  16 + this.message.setMessage("链接超时关闭");
  17 + this.notify();
  18 + }
  19 +
  20 + @Override
  21 + public void reply(ServerAgreementContent agreementContent) {
  22 + agreementContent.setReplyMessage(message);
  23 + this.notify();
  24 + }
  25 +
  26 + @Override
  27 + public Message getReplyMessage() {
  28 + return message;
  29 + }
  30 +
  31 + public ClienConnectionImpl setCode(MessageCodeType code)
  32 + {
  33 + this.message.setCode(code);
  34 + return this;
  35 + }
  36 + public ClienConnectionImpl setData(Object data)
  37 + {
  38 + this.message.setData(data);
  39 + return this;
  40 + }
  41 +
  42 + public ClienConnectionImpl setMessage(String message)
  43 + {
  44 + this.message.setMessage(message);
  45 + return this;
  46 + }
  47 +}
  1 +package com.zhonglai.luhui.mqtt.comm.config;
  2 +
  3 +import org.aspectj.lang.JoinPoint;
  4 +import org.aspectj.lang.annotation.AfterReturning;
  5 +import org.aspectj.lang.annotation.Aspect;
  6 +import org.aspectj.lang.annotation.Before;
  7 +import org.aspectj.lang.annotation.Pointcut;
  8 +import org.slf4j.Logger;
  9 +import org.slf4j.LoggerFactory;
  10 +import org.springframework.stereotype.Component;
  11 +import org.springframework.web.context.request.RequestContextHolder;
  12 +import org.springframework.web.context.request.ServletRequestAttributes;
  13 +
  14 +import javax.servlet.http.HttpServletRequest;
  15 +import java.util.Enumeration;
  16 +import java.util.HashMap;
  17 +import java.util.Map;
  18 +
  19 +@Aspect
  20 +@Component
  21 +public class ControllerLogAspect {
  22 + private Logger logger = LoggerFactory.getLogger(this.getClass());
  23 +
  24 + @Pointcut("execution(public * com.zhonglai.luhui.mqtt.*.controller..*.*(..))")
  25 + public void controllerLog() {
  26 +
  27 + }
  28 +
  29 + @Before("controllerLog()")
  30 + public void doBefore(JoinPoint joinPoint)
  31 + {
  32 + // 接收到请求,记录请求内容
  33 + ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
  34 + HttpServletRequest request = attributes.getRequest();
  35 + Enumeration<String> hs = request.getHeaderNames();
  36 + Map<String,Object> map = new HashMap<>();
  37 + while (hs.hasMoreElements())
  38 + {
  39 + String key= hs.nextElement();
  40 + map.put(key,request.getHeader(key));
  41 + }
  42 + logger.info("请求url:{},请求head:{},请求IP:{},请求方法:{},请求参数:{}",request.getRequestURL(),map,request.getRemoteAddr(),joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName(),joinPoint.getArgs());
  43 + }
  44 +
  45 + @AfterReturning(returning = "ret", pointcut = "controllerLog()")
  46 + public void doAfterReturning(Object ret) {
  47 + // 处理完请求,返回内容
  48 + logger.info("返回 {} " , GsonConstructor.get().toJson(ret));
  49 + }
  50 +}
  1 +package com.zhonglai.luhui.mqtt.comm.config;
  2 +
  3 +import org.springframework.context.annotation.Bean;
  4 +import org.springframework.context.annotation.Configuration;
  5 +import org.springframework.web.cors.CorsConfiguration;
  6 +import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
  7 +import org.springframework.web.filter.CorsFilter;
  8 +
  9 +/**
  10 + * 跨域配置,需要和WebMvcConfigurerAdapter的addCorsMappings配合使用
  11 + */
  12 +@Configuration
  13 +public class CorsConfig {
  14 + private CorsConfiguration buildConfig() {
  15 + CorsConfiguration corsConfiguration = new CorsConfiguration();
  16 + corsConfiguration.addAllowedOrigin("*"); // 1允许任何域名使用
  17 + corsConfiguration.addAllowedHeader("*"); // 2允许任何头
  18 + corsConfiguration.addAllowedMethod("*"); // 3允许任何方法(post、get等)
  19 + return corsConfiguration;
  20 + }
  21 +
  22 + @Bean
  23 + public CorsFilter corsFilter() {
  24 + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
  25 + source.registerCorsConfiguration("/**", buildConfig()); // 4
  26 + return new CorsFilter(source);
  27 + }
  28 +}
  1 +package com.zhonglai.luhui.mqtt.comm.config;
  2 +
  3 +import com.fasterxml.jackson.annotation.JsonAutoDetect;
  4 +import com.fasterxml.jackson.annotation.PropertyAccessor;
  5 +import com.fasterxml.jackson.databind.ObjectMapper;
  6 +import org.springframework.beans.factory.annotation.Value;
  7 +import org.springframework.context.annotation.Bean;
  8 +import org.springframework.context.annotation.Configuration;
  9 +import org.springframework.data.redis.connection.RedisConnectionFactory;
  10 +import org.springframework.data.redis.core.RedisTemplate;
  11 +import org.springframework.data.redis.listener.PatternTopic;
  12 +import org.springframework.data.redis.listener.RedisMessageListenerContainer;
  13 +import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
  14 +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
  15 +import org.springframework.data.redis.serializer.StringRedisSerializer;
  16 +
  17 +@Configuration
  18 +public class RedisConfig {
  19 + @Value("${sys.redis.field:#{'luhui:mqttservice:device:'}")
  20 + public static String FIELD ; //域
  21 +
  22 + @Bean
  23 + public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
  24 + RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();
  25 + //设置工厂链接
  26 + redisTemplate.setConnectionFactory(redisConnectionFactory);
  27 + //设置自定义序列化方式
  28 + setSerializeConfig(redisTemplate, redisConnectionFactory);
  29 + return redisTemplate;
  30 + }
  31 +
  32 + private void setSerializeConfig(RedisTemplate<String, Object> redisTemplate, RedisConnectionFactory redisConnectionFactory) {
  33 + //对字符串采取普通的序列化方式 适用于key 因为我们一般采取简单字符串作为key
  34 + StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
  35 + //普通的string类型的key采用 普通序列化方式
  36 + redisTemplate.setKeySerializer(stringRedisSerializer);
  37 + //普通hash类型的key也使用 普通序列化方式
  38 + redisTemplate.setHashKeySerializer(stringRedisSerializer);
  39 + //解决查询缓存转换异常的问题 大家不能理解就直接用就可以了 这是springboot自带的jackson序列化类,但是会有一定问题
  40 + Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
  41 + ObjectMapper om = new ObjectMapper();
  42 + om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
  43 + om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
  44 + jackson2JsonRedisSerializer.setObjectMapper(om);
  45 + //普通的值采用jackson方式自动序列化
  46 + redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
  47 + //hash类型的值也采用jackson方式序列化
  48 + redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
  49 + //属性设置完成afterPropertiesSet就会被调用,可以对设置不成功的做一些默认处理
  50 + redisTemplate.afterPropertiesSet();
  51 + }
  52 +
  53 +
  54 + /**
  55 + * redis消息监听
  56 + * @param connectionFactory
  57 + * @return
  58 + */
  59 + @Bean
  60 + public RedisMessageListenerContainer messageListenerContainer(RedisConnectionFactory connectionFactory) {
  61 + RedisMessageListenerContainer container = new RedisMessageListenerContainer();
  62 + container.setConnectionFactory(connectionFactory);
  63 + container.addMessageListener(new MessageListenerAdapter(), new PatternTopic(getRedisKeyPath()+"**"));
  64 +
  65 + return container;
  66 + }
  67 +
  68 + public static String getRedisKeyPath()
  69 + {
  70 + return FIELD+ RedisKeyMqttUser.ROLEID+":"+ RedisKeyMqttUser.USERNAME+":";
  71 + }
  72 +}
  1 +package com.zhonglai.luhui.mqtt.comm.config;
  2 +
  3 +import org.springframework.beans.factory.annotation.Value;
  4 +import org.springframework.stereotype.Component;
  5 +
  6 +@Component
  7 +public class RedisKeyMqttUser {
  8 + public static String ROLEID;
  9 + public static String USERNAME;
  10 +
  11 + @Value("${mqtt.redis.key.roleid}")
  12 + public void setROLEID(String roleid) {
  13 + ROLEID = roleid;
  14 + }
  15 +
  16 + @Value("${mqtt.redis.key.username}")
  17 + public void setUSERNAME(String username) {
  18 + USERNAME = username;
  19 + }
  20 +}
  1 +package com.zhonglai.luhui.mqtt.comm.config;
  2 +
  3 +import com.alibaba.fastjson.JSONObject;
  4 +import com.ruoyi.common.utils.http.HttpUtils;
  5 +import org.slf4j.Logger;
  6 +import org.slf4j.LoggerFactory;
  7 +import org.springframework.beans.factory.annotation.Value;
  8 +import org.springframework.context.annotation.Configuration;
  9 +
  10 +import javax.annotation.PostConstruct;
  11 +
  12 +@Configuration
  13 +public class SysParameter {
  14 + private static Logger log = LoggerFactory.getLogger(SysParameter.class);
  15 +
  16 + public static String service_ip = ""; //服务所在地址
  17 +
  18 + @Value("${mqtt.topicconfig:/{{roleid}}/{{username}}/{{clientid}}/{{topicType}}/{{messageid}}}")
  19 + public String tempTopicconfig ; //topic 配置
  20 +
  21 + public static String topicconfig ; //topic 配置
  22 +
  23 + @PostConstruct
  24 + public static void init() {
  25 + String service_ip_url = "http://ly.userlogin.yu2le.com/ip";
  26 + JSONObject jsonObject = JSONObject.parseObject(HttpUtils.sendGet(service_ip_url));
  27 + service_ip = jsonObject.getString("data");
  28 + log.info("服务器地址:{}",service_ip);
  29 + }
  30 +
  31 + public void inittopicconfig()
  32 + {
  33 + topicconfig = tempTopicconfig;
  34 + }
  35 +}
  1 +package com.zhonglai.luhui.mqtt.comm.dto;
  2 +
  3 +import com.zhonglai.luhui.mqtt.comm.config.SysParameter;
  4 +import lombok.Data;
  5 +
  6 +@Data
  7 +public class DeviceInfoDto {
  8 + private String id; // varchar(50) NOT NULL COMMENT '设备编号,如:设备imei号_传感器编号',
  9 + private String deviceId; // varchar(50) NOT NULL COMMENT '设备IMEI号',
  10 + private String deviceName; // varchar(50) DEFAULT NULL COMMENT '设备名称',
  11 + private String sensorNumber; // varchar(10) NOT NULL DEFAULT '01' COMMENT '传感器编号,默认01',
  12 + private String deviceModel; // varchar(10) NOT NULL COMMENT '设备型号,(3,5,6)',
  13 + private String deviceType; // varchar(10) DEFAULT NULL COMMENT '设备类型(0传感器,1开关控制器)',
  14 + private String online; // varchar(10) DEFAULT NULL COMMENT '设备在线状态(00-不在线,01-在线)',
  15 + private Integer createTime; // int(11) DEFAULT NULL COMMENT '添加时间',
  16 + private String alarmCode; // varchar(10) DEFAULT NULL COMMENT '设备告警状态',
  17 + private Integer dataUpdateTime; // int(11) DEFAULT NULL COMMENT '数据更新时间',
  18 + private String dataState; // text COMMENT '数据状态(json串)',
  19 + private Integer alarmLastCreateTime; // int(11) DEFAULT NULL COMMENT '告警最后一次产生的时间',
  20 + private String deviceServiceIp; //设备链接服务器地址
  21 + private Integer alarmSqlApply; //告警sql规则申请(0-未申请,1-已申请)
  22 + private String deviceConfig; //设备配置
  23 + private String sensorType; //设备配置
  24 + private Boolean isadd; //是否添加
  25 +
  26 + public static DeviceInfoDto newDefaultDeviceInfoDto(String imei, String sensor_number, String device_model, String device_type)
  27 + {
  28 + DeviceInfoDto deviceInfoDto = new DeviceInfoDto();
  29 + deviceInfoDto.setId(imei+"_"+sensor_number);
  30 + deviceInfoDto.setDeviceId(imei);
  31 + deviceInfoDto.setSensorNumber(sensor_number);
  32 + deviceInfoDto.setDeviceModel(device_model);
  33 + deviceInfoDto.setDeviceType(device_type);
  34 + deviceInfoDto.setAlarmCode("00");
  35 + deviceInfoDto.setOnline("01");
  36 + deviceInfoDto.setDeviceServiceIp(SysParameter.service_ip);
  37 + deviceInfoDto.setIsadd(true);
  38 + return deviceInfoDto;
  39 + }
  40 +}
  1 +package com.zhonglai.luhui.mqtt.comm.dto;
  2 +
  3 +import lombok.Data;
  4 +
  5 +@Data
  6 +public class MyException extends RuntimeException{
  7 + private static final long serialVersionUID = 8827598182853467258L;
  8 + private Message errmge;
  9 +
  10 + public MyException(Message myMessage) {
  11 + super(myMessage.getMessage());
  12 + this.errmge = myMessage;
  13 + }
  14 +
  15 + public MyException(String message, Throwable cause) {
  16 + super(message, cause);
  17 + }
  18 + public MyException(String message) {
  19 + super(message);
  20 + }
  21 +
  22 +}
  1 +package com.zhonglai.luhui.mqtt.comm.dto;
  2 +
  3 +
  4 +import com.zhonglai.luhui.mqtt.comm.factory.Topic;
  5 +
  6 +public interface ServerAgreementContent {
  7 + String getClienConnectionId();
  8 + byte[] getCommd();
  9 + String getReplyCommdTopic(Topic topic);
  10 + void setReplyMessage(Message message);
  11 +}
  1 +package com.zhonglai.luhui.mqtt.comm.dto;
  2 +
  3 +
  4 +public interface ServerDto {
  5 + ServerAgreementContent getServerAgreementContent();
  6 + boolean isReplyMessage();
  7 +}
  1 +package com.zhonglai.luhui.mqtt.comm.factory;
  2 +
  3 +
  4 +import com.zhonglai.luhui.mqtt.comm.dto.ServerDto;
  5 +
  6 +/**
  7 + * mqtt业务协议
  8 + */
  9 +public interface BusinessAgreement<T> {
  10 + ServerDto analysis(Topic topic, T data) throws Exception; //解析协议
  11 + T toData(byte[] data);
  12 +}
  1 +package com.zhonglai.luhui.mqtt.comm.factory;
  2 +
  3 +/**
  4 + * mqtt业务协议工厂
  5 + */
  6 +public interface BusinessAgreementFactory {
  7 + BusinessAgreement createBusinessAgreement(String topicType);
  8 +}
  1 +package com.zhonglai.luhui.mqtt.comm.factory;
  2 +
  3 +import org.eclipse.paho.client.mqttv3.MqttClientPersistence;
  4 +import org.eclipse.paho.client.mqttv3.MqttPersistable;
  5 +import org.eclipse.paho.client.mqttv3.MqttPersistenceException;
  6 +
  7 +import java.util.Enumeration;
  8 +
  9 +public class MqttClientPersistenceImpl implements MqttClientPersistence {
  10 + @Override
  11 + public void open(String clientId, String serverURI) throws MqttPersistenceException {
  12 +
  13 + }
  14 +
  15 + @Override
  16 + public void close() throws MqttPersistenceException {
  17 +
  18 + }
  19 +
  20 + @Override
  21 + public void put(String key, MqttPersistable persistable) throws MqttPersistenceException {
  22 +
  23 + }
  24 +
  25 + @Override
  26 + public MqttPersistable get(String key) throws MqttPersistenceException {
  27 + return null;
  28 + }
  29 +
  30 + @Override
  31 + public void remove(String key) throws MqttPersistenceException {
  32 +
  33 + }
  34 +
  35 + @Override
  36 + public Enumeration keys() throws MqttPersistenceException {
  37 + return null;
  38 + }
  39 +
  40 + @Override
  41 + public void clear() throws MqttPersistenceException {
  42 +
  43 + }
  44 +
  45 + @Override
  46 + public boolean containsKey(String key) throws MqttPersistenceException {
  47 + return false;
  48 + }
  49 +}
  1 +package com.zhonglai.luhui.mqtt.comm.factory;
  2 +
  3 +import com.zhonglai.luhui.mqtt.comm.config.RedisConfig;
  4 +import com.zhonglai.luhui.mqtt.comm.config.SysParameter;
  5 +import com.zhonglai.luhui.mqtt.comm.dto.MyException;
  6 +import lombok.Data;
  7 +
  8 +import java.util.Optional;
  9 +
  10 +@Data
  11 +public class Topic {
  12 + private String roleid;
  13 + private String username;
  14 + private String clientid;
  15 + private String topicType;
  16 + private String redicKey;
  17 + private String messageid;
  18 + public Topic(String topic)
  19 + {
  20 + topic = Optional.ofNullable(topic).orElseThrow(()->new MyException("topic为空"));
  21 + String[] sts = topic.split("/");
  22 + String[] config = SysParameter.topicconfig.split("/");
  23 + for(int i=0;i<config.length;i++)
  24 + {
  25 + String cf = config[i].replace("{{","").replace("}}","");
  26 + switch (cf)
  27 + {
  28 + case "roleid":
  29 + roleid = sts[i];
  30 + break;
  31 + case "username":
  32 + username = sts[i];
  33 + break;
  34 + case "clientid":
  35 + clientid = sts[i];
  36 + break;
  37 + case "topicType":
  38 + topicType = sts[i];
  39 + break;
  40 + case "messageid":
  41 + messageid = sts[i];
  42 + break;
  43 + }
  44 + }
  45 + }
  46 + public String getRedicKey()
  47 + {
  48 + if(null == redicKey)
  49 + {
  50 + return generateRedicKey();
  51 + }
  52 + return redicKey;
  53 + }
  54 + private String generateRedicKey()
  55 + {
  56 + return RedisConfig.FIELD+roleid+":"+username+":"+clientid;
  57 + }
  58 +
  59 +}
  1 +package com.zhonglai.luhui.mqtt.comm.service;
  2 +
  3 +
  4 +import com.zhonglai.luhui.mqtt.comm.dto.ServerDto;
  5 +import com.zhonglai.luhui.mqtt.comm.factory.Topic;
  6 +
  7 +/**
  8 + * 缓存业务
  9 + */
  10 +public interface CacheService {
  11 + boolean updateCache(Topic topic, ServerDto dto); //返回是否需要持久化
  12 +}
  1 +package com.zhonglai.luhui.mqtt.comm.service;
  2 +
  3 +import com.ruoyi.common.core.domain.Message;
  4 +import com.ruoyi.common.utils.ByteUtil;
  5 +import com.zhonglai.luhui.mqtt.comm.clien.ClienConnection;
  6 +import com.zhonglai.luhui.mqtt.comm.clien.impl.ClienConnectionImpl;
  7 +import com.zhonglai.luhui.mqtt.comm.dto.ServerDto;
  8 +import com.zhonglai.luhui.mqtt.comm.factory.Topic;
  9 +import net.jodah.expiringmap.ExpirationListener;
  10 +import net.jodah.expiringmap.ExpirationPolicy;
  11 +import net.jodah.expiringmap.ExpiringMap;
  12 +import org.eclipse.paho.client.mqttv3.MqttException;
  13 +import org.eclipse.paho.client.mqttv3.MqttMessage;
  14 +import org.slf4j.Logger;
  15 +import org.slf4j.LoggerFactory;
  16 +import org.springframework.beans.factory.annotation.Autowired;
  17 +import org.springframework.beans.factory.annotation.Value;
  18 +import org.springframework.stereotype.Service;
  19 +
  20 +import javax.annotation.PostConstruct;
  21 +import java.util.concurrent.TimeUnit;
  22 +
  23 +/**
  24 + * 客户端通知服务
  25 + */
  26 +@Service
  27 +public class ClienNoticeService {
  28 + private static final Logger log = LoggerFactory.getLogger(ClienNoticeService.class);
  29 +
  30 + @Autowired
  31 + private TerminalService terminalService;
  32 +
  33 + @Autowired
  34 + private TopicsService topicsService;
  35 +
  36 + private ExpiringMap<String, ClienConnection> clienConnectionMap;
  37 +
  38 + @Value("${mqtt.client.operationTime}")
  39 + private long operationTime; //客户端操作时间
  40 +
  41 + @PostConstruct
  42 + public void init()
  43 + {
  44 + // maxSize: 设置最大值,添加第11个entry时,会导致第1个立马过期(即使没到过期时间)
  45 + // expiration:设置每个key有效时间10s, 如果key不设置过期时间,key永久有效。
  46 + // variableExpiration: 允许更新过期时间值,如果不设置variableExpiration,不允许后面更改过期时间,一旦执行更改过期时间操作会抛异常UnsupportedOperationException
  47 + // policy:
  48 + // CREATED: 只在put和replace方法清零过期时间
  49 + // ACCESSED: 在CREATED策略基础上增加, 在还没过期时get方法清零过期时间。
  50 + // 清零过期时间也就是重置过期时间,重新计算过期时间.
  51 + clienConnectionMap = ExpiringMap.builder().maxSize(2000).expiration(operationTime, TimeUnit.SECONDS)
  52 + .asyncExpirationListener((ExpirationListener<String, ClienConnection>) (s, clienConnection) -> clienConnection.close())
  53 + .expirationPolicy(ExpirationPolicy.CREATED).build();
  54 + }
  55 +
  56 + public Message sendMessage(String imei, MqttMessage mqttMessage, String messageid) throws MqttException, InterruptedException {
  57 + //设置通知渠道
  58 + ClienConnection clienConnection = new ClienConnectionImpl();
  59 + clienConnectionMap.put(topicsService.getClienConnectionMapKey(imei,messageid),clienConnection);
  60 +
  61 + sendMessage(imei,messageid,mqttMessage);
  62 + synchronized(clienConnection)
  63 + {
  64 + log.info("{}等待通知",imei);
  65 + clienConnection.wait(operationTime*1000+3000l);
  66 + }
  67 + log.info("{}收到通知{}",imei,clienConnection.getReplyMessage().getMessage());
  68 + Message message = clienConnection.getReplyMessage();
  69 + log.info("{}返回通知{}",imei,message);
  70 +
  71 + return message;
  72 + }
  73 +
  74 + /**
  75 + * 发送消息
  76 + * @param imei
  77 + * @param mqttMessage
  78 + * @throws MqttException
  79 + * @throws InterruptedException
  80 + */
  81 + public void sendMessage(String imei,String messageid, MqttMessage mqttMessage) throws MqttException, InterruptedException {
  82 + //发生指令,等待通知
  83 + String topic = topicsService.getTopicFromImei(imei,messageid);
  84 + System.out.println("发送的消息内容"+ ByteUtil.hexStringToSpace(ByteUtil.toHexString(mqttMessage.getPayload()).toUpperCase()));
  85 + terminalService.publish(topic,mqttMessage);
  86 + }
  87 +
  88 + public ClienConnection getClienConnection(String imei, String messageid)
  89 + {
  90 + return clienConnectionMap.get(topicsService.getClienConnectionMapKey(imei,messageid));
  91 + }
  92 +
  93 + public void replySendMessage(Topic topic, ServerDto dto)
  94 + {
  95 + log.info("开始通知{},数据:{}",topic,dto);
  96 + //判断有没有需要回复的客户端,如果有就回复
  97 + if(dto.isReplyMessage())
  98 + {
  99 + ClienConnection clienConnection = getClienConnection(topic.getClientid(),dto.getServerAgreementContent().getClienConnectionId());
  100 + if(null != clienConnection)
  101 + {
  102 + synchronized(clienConnection)
  103 + {
  104 + log.info("正在通知{},通知结果{}",topic,dto);
  105 + clienConnection.reply(dto.getServerAgreementContent());
  106 + }
  107 + }
  108 + }
  109 + log.info("结束通知{}",topic);
  110 + }
  111 +
  112 + public void replyTerminalMessage(Topic topic, ServerDto dto) throws MqttException {
  113 + if(dto.isReplyMessage() && null != dto.getServerAgreementContent().getReplyCommdTopic(topic))
  114 + {
  115 + String tc = dto.getServerAgreementContent().getReplyCommdTopic(topic);
  116 + MqttMessage mqttMessage = new MqttMessage();
  117 + mqttMessage.setPayload(dto.getServerAgreementContent().getCommd());
  118 + log.info("回复终端{}的消息{}",tc,new String(mqttMessage.getPayload()));
  119 + terminalService.publish(dto.getServerAgreementContent().getReplyCommdTopic(topic),mqttMessage);
  120 + }
  121 +
  122 + }
  123 +}
  1 +package com.zhonglai.luhui.mqtt.comm.service;
  2 +
  3 +import org.apache.commons.lang3.StringUtils;
  4 +
  5 +import java.util.ArrayList;
  6 +import java.util.List;
  7 +
  8 +/**
  9 + * 数据持久化服务
  10 + */
  11 +public abstract class DataPersistenceService {
  12 + protected BaseDao baseDao = new BaseDao();
  13 +
  14 + public abstract void persistence(Topic topic, ServerDto serverDto);
  15 + public abstract void addDeviceSensorData(Topic topic, ServerDto serverDto);
  16 +
  17 + /**
  18 + * 记录操作日志
  19 + * @param deviceOperationTypeEnum
  20 + * @param deviceNewState
  21 + */
  22 + public void logDeviceOperation(String deviceInfoId, DeviceOperationTypeEnum deviceOperationTypeEnum, String deviceNewState)
  23 + {
  24 + List<Object> operateHisList = new ArrayList<>();
  25 + //如果老的和新的不一致,记录日志
  26 + LogDeviceOperation operateHis = new LogDeviceOperation();
  27 + operateHis.setDeviceInfoId(deviceInfoId);
  28 + deviceOperationTypeEnum.setDeviceOperationLog(operateHis);
  29 + if(StringUtils.isNoneBlank(deviceNewState))
  30 + {
  31 + operateHis.setDeviceNewState(deviceNewState);
  32 + }
  33 + operateHis.setIsStateChange(1);
  34 + operateHis.setDeviceOperationTime(CommonUtil.getNowTimeMilly());
  35 +
  36 + operateHisList.add(operateHis);
  37 + baseDao.insertList(operateHisList, TableGenerateSqlEnum.LogDeviceOperation.getNowTableName());
  38 +
  39 + }
  40 +
  41 + /**
  42 + * 离线处理
  43 + * @param imei
  44 + */
  45 + public void offLine(String imei) {
  46 + addAlarm(upDeviceInfoOffLine(imei));
  47 + }
  48 +
  49 + /**
  50 + * 更新设备表离线信息
  51 + * @param imei
  52 + * @return
  53 + */
  54 + public abstract String[] upDeviceInfoOffLine(String imei);
  55 +
  56 +
  57 + /**
  58 + * 离线告警记录添加(实时离线)
  59 + * @param deviceInfoIds
  60 + */
  61 + private void addAlarm(String... deviceInfoIds)
  62 + {
  63 + List<Object> deviceAlarmInfoList = new ArrayList<>();
  64 +
  65 + for(String deviceInfoId:deviceInfoIds)
  66 + {
  67 + DeviceAlarmInfo deviceAlarmInfo = new DeviceAlarmInfo();
  68 + deviceAlarmInfo.setDeviceInfoId(deviceInfoId);
  69 + deviceAlarmInfo.setAlarmTime(CommonUtil.getNowTimeMilly());
  70 + deviceAlarmInfo.setAlarmState(1);
  71 + deviceAlarmInfo.setAlarmCode("09");
  72 + deviceAlarmInfo.setIsSendNumber(0);
  73 + deviceAlarmInfoList.add(deviceAlarmInfo);
  74 + }
  75 +
  76 + if(null != deviceAlarmInfoList && deviceAlarmInfoList.size() !=0 )
  77 + {
  78 + String tableName = TableUtil.getNowTableName( "ly_device_alarm_info", "device_alarm_info",3);
  79 + baseDao.insertList(deviceAlarmInfoList,tableName);
  80 + }
  81 + }
  82 +}
1 -package com.zhonglai.luhui.mqtt.service; 1 +package com.zhonglai.luhui.mqtt.comm.service;
2 2
3 import com.luhui.ly.device.mqtt.comm.dto.ServerDto; 3 import com.luhui.ly.device.mqtt.comm.dto.ServerDto;
4 import com.luhui.ly.device.mqtt.comm.factory.BusinessAgreement; 4 import com.luhui.ly.device.mqtt.comm.factory.BusinessAgreement;
  1 +package com.zhonglai.luhui.mqtt.comm.service;
  2 +
  3 +import com.luhui.ly.device.mqtt.comm.config.RedisConfig;
  4 +import com.luhui.ly.device.mqtt.comm.service.DataPersistenceService;
  5 +import org.springframework.beans.factory.annotation.Autowired;
  6 +import org.springframework.data.redis.connection.Message;
  7 +import org.springframework.data.redis.listener.KeyExpirationEventMessageListener;
  8 +import org.springframework.data.redis.listener.RedisMessageListenerContainer;
  9 +import org.springframework.stereotype.Component;
  10 +
  11 +/**
  12 + * 不指定redis数据库(全局监听)
  13 + */
  14 +@Component
  15 +public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener {
  16 + @Autowired
  17 + private DataPersistenceService dtaPersistenceService;
  18 +
  19 + public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer) {
  20 + super(listenerContainer);
  21 + }
  22 +
  23 + @Override
  24 + public void onMessage(Message message, byte[] pattern) {
  25 + String expiredKey = message.toString();
  26 + if(expiredKey.startsWith(RedisConfig.getRedisKeyPath())) //指定设备离线
  27 + {
  28 + String imei = expiredKey.replace(RedisConfig.getRedisKeyPath(),"");
  29 + dtaPersistenceService.offLine(imei);
  30 + }
  31 + }
  32 +}
  1 +package com.zhonglai.luhui.mqtt.comm.service;
  2 +
  3 +import com.luhui.ly.comm.util.GsonConstructor;
  4 +import org.slf4j.Logger;
  5 +import org.slf4j.LoggerFactory;
  6 +import org.springframework.beans.factory.annotation.Value;
  7 +import org.springframework.data.redis.core.RedisTemplate;
  8 +import org.springframework.stereotype.Service;
  9 +import org.springframework.util.CollectionUtils;
  10 +
  11 +import javax.annotation.Resource;
  12 +import java.util.*;
  13 +import java.util.concurrent.TimeUnit;
  14 +import java.util.stream.Collectors;
  15 +
  16 +/**
  17 + * 缓存数据服务
  18 + */
  19 +@Service
  20 +public class RedisService {
  21 + private static final Logger log = LoggerFactory.getLogger(com.luhui.ly.device.mqtt.comm.service.RedisService.class);
  22 +
  23 + @Resource
  24 + private RedisTemplate<String,Object> redisTemplate;
  25 +
  26 + @Value("${mqtt.client.device_life}")
  27 + private long device_life; //设备生命周期
  28 +
  29 + /**
  30 + * @param key
  31 + * @return 获得值
  32 + * redis有五种数据类型 opsForValue表示是操作字符串类型
  33 + */
  34 + public Object get(String key){
  35 + return key == null ? null : redisTemplate.opsForValue().get(key);
  36 + }
  37 + //本来只可以放入string类型,但是我们配置了自动序列化所以这儿可以传入object
  38 + public boolean set(String key,Object value){
  39 + try{
  40 + redisTemplate.opsForValue().set(key,value);
  41 + return true;
  42 + }catch (Exception e){
  43 + log.error("redis set value exception:{}",e);
  44 + return false;
  45 + }
  46 + }
  47 +
  48 + /**
  49 + * 原子操作
  50 + * @param key
  51 + * @param value
  52 + * @return
  53 + */
  54 + public boolean setexDevice(String key,Object value){
  55 + try{//TimeUnit.SECONDS指定类型为秒
  56 + redisTemplate.opsForValue().set(key,value,device_life, TimeUnit.SECONDS);
  57 + return true;
  58 + }catch (Exception e){
  59 + log.error("redis set value and expire exception:{}",e);
  60 + return false;
  61 + }
  62 + }
  63 +
  64 + /**
  65 + * 原子操作
  66 + * @param key
  67 + * @param value
  68 + * @param expire 过期时间 秒
  69 + * @return
  70 + */
  71 + public boolean setex(String key,Object value,long expire){
  72 + try{//TimeUnit.SECONDS指定类型为秒
  73 + redisTemplate.opsForValue().set(key,value,expire, TimeUnit.SECONDS);
  74 + return true;
  75 + }catch (Exception e){
  76 + log.error("redis set value and expire exception:{}",e);
  77 + return false;
  78 + }
  79 + }
  80 +
  81 + /**
  82 + * 非原子操作
  83 + * @param key
  84 + * @param expire
  85 + * @return
  86 + */
  87 + public boolean expire(String key,long expire){
  88 + try{//这儿没有ops什么的是因为每种数据类型都能设置过期时间
  89 + redisTemplate.expire(key,expire,TimeUnit.SECONDS);
  90 + return true;
  91 + }catch (Exception e){
  92 + log.error("redis set key expire exception:{}",e);
  93 + return false;
  94 + }
  95 + }
  96 +
  97 + /**
  98 + *
  99 + * @param key
  100 + * @return 获取key的过期时间
  101 + */
  102 + public long ttl(String key){
  103 + return redisTemplate.getExpire(key);
  104 + }
  105 +
  106 + /**
  107 + *
  108 + * @param keys 删除key 可变参数
  109 + */
  110 + public void del(String ...keys){
  111 + if(keys!=null&&keys.length>0) {
  112 + redisTemplate.delete((Collection<String>) CollectionUtils.arrayToList(keys));
  113 + }
  114 + }
  115 +
  116 + /**
  117 + *
  118 + * @param key
  119 + * @param step 传入正数 就是加多少 传入负数就是减多少
  120 + * @return
  121 + */
  122 + public long incrBy(String key,long step){
  123 + return redisTemplate.opsForValue().increment(key,step);
  124 + }
  125 +
  126 + /**
  127 + *
  128 + * @param key
  129 + * @param value
  130 + * @return 如果该key存在就返回false 设置不成功 key不存在就返回ture设置成功
  131 + */
  132 + public boolean setnx(String key,Object value){
  133 + return redisTemplate.opsForValue().setIfAbsent(key,value);
  134 + }
  135 +
  136 +// /**
  137 +// * 原子操作
  138 +// * @param key
  139 +// * @param value
  140 +// * @param expire 在上面方法加上过期时间设置
  141 +// * @return
  142 +// */
  143 +// public boolean setnxAndExpire(String key,Object value,long expire){
  144 +// return redisTemplate.opsForValue().setIfAbsent(key,value,expire,TimeUnit.SECONDS);
  145 +// }
  146 +
  147 + /**
  148 + *
  149 + * @param key
  150 + * @param value
  151 + * @return 如果该key存在就返回之前的value 不存在就返回null
  152 + */
  153 + public Object getAndSet(String key,Object value){
  154 + return redisTemplate.opsForValue().getAndSet(key,value);
  155 + }
  156 +
  157 + /**
  158 + *
  159 + * @param key
  160 + * @return 判断key是否存在
  161 + */
  162 + public boolean hasKey(String key){
  163 + return redisTemplate.hasKey(key);
  164 + }
  165 +
  166 + /***list的长度**/
  167 + public long llen(String key){
  168 + return redisTemplate.opsForList().size(key);
  169 + }
  170 +
  171 + /**
  172 + * 获取key中index位置的值,负数就反过来数,-1为最后一个
  173 + * @param key
  174 + * @param index
  175 + * @return
  176 + */
  177 + public Object lgetByIndex(String key,long index){
  178 + try {
  179 + return redisTemplate.opsForList().index(key, index);
  180 + } catch (Exception e) {
  181 + log.error("redis lgetByIndex error,key:{},index:{}exception:{}",key,index,e);
  182 + return null;
  183 + }
  184 + }
  185 + /**
  186 + * 将list放入缓存
  187 + * @param key 键
  188 + * @param value 值
  189 + * @return
  190 + */
  191 + public boolean lrpush(String key, Object value) {
  192 + try {
  193 + redisTemplate.opsForList().rightPush(key, value);
  194 + return true;
  195 + } catch (Exception e) {
  196 + log.error("redis lrpush error,key:{},value:{}exception:{}",key,value,e);
  197 + return false;
  198 + }
  199 + }
  200 + /**
  201 + * 将list放入缓存
  202 + * @param key 键
  203 + * @param value 值
  204 + * @param time 时间(秒)
  205 + * @return
  206 + */
  207 + public boolean lrpush(String key, Object value, long time) {
  208 + try {
  209 + redisTemplate.opsForList().rightPush(key, value);
  210 + if (time > 0)
  211 + expire(key, time);
  212 + return true;
  213 + } catch (Exception e) {
  214 + log.error("redis lrpush error,key:{},value:{},timeL{},exception:{}",key,value,time,e);
  215 + return false;
  216 + }
  217 + }
  218 + /**
  219 + * 将list放入缓存
  220 + * @param key 键
  221 + * @param value 值
  222 + * @return
  223 + */
  224 + public boolean lrpush(String key, List<Object> value) {
  225 + try {
  226 + redisTemplate.opsForList().rightPushAll(key, value);
  227 + return true;
  228 + } catch (Exception e) {
  229 + log.error("redis lrpush error,key:{},value:{},exception:{}",key,value,e);
  230 + return false;
  231 + }
  232 + }
  233 + /**
  234 + * 将list放入缓存
  235 + *
  236 + * @param key 键
  237 + * @param value 值
  238 + * @param time 时间(秒)
  239 + * @return
  240 + */
  241 + public boolean lrpush(String key, List<Object> value, long time) {
  242 + try {
  243 + redisTemplate.opsForList().rightPushAll(key, value);
  244 + if (time > 0)
  245 + expire(key, time);
  246 + return true;
  247 + } catch (Exception e) {
  248 + log.error("redis lrpush error,key:{},value:{},time:{},exception:{}",key,value,time,e);
  249 + return false;
  250 + }
  251 + }
  252 + /**
  253 + * 根据索引修改list中的某条数据
  254 + * @param key 键
  255 + * @param index 索引
  256 + * @param value 值
  257 + * @return
  258 + */
  259 + public boolean lUpdateByIndex(String key, long index, Object value) {
  260 + try {
  261 + redisTemplate.opsForList().set(key, index, value);
  262 + return true;
  263 + } catch (Exception e) {
  264 + log.error("redis lUpdateByIndex error,key:{},index:{},value:{},exception:{}",key,index,value,e);
  265 + return false;
  266 + }
  267 + }
  268 + /**
  269 + * 移除N个值为value
  270 + * @param key 键
  271 + * @param count 移除多少个
  272 + * @param value 值
  273 + * @return 移除的个数
  274 + */
  275 + public long lrem(String key, long count, Object value) {
  276 + try {
  277 + Long remove = redisTemplate.opsForList().remove(key, count, value);
  278 + return remove;
  279 + } catch (Exception e) {
  280 + log.error("redis lrem error,key:{},count:{},value:{},exception:{}",key,count,value,e);
  281 + return 0;
  282 + }
  283 + }
  284 + /*****hash数据类型方法 opsForHash表示是操作字符串类型*****/
  285 +
  286 + /**
  287 + * @param key 健
  288 + * @param field 属性
  289 + * @param value 值
  290 + * @return
  291 + */
  292 + public boolean hset(String key, String field, Object value) {
  293 + try {
  294 + redisTemplate.opsForHash().put(key, field, value);
  295 + return true;
  296 + }catch (Exception e){
  297 + log.error("redis hset eror,key:{},field:{},value:{}",key,field,value);
  298 + return false;
  299 + }
  300 + }
  301 +
  302 + /**
  303 + *
  304 + * @param key
  305 + * @param field
  306 + * @param value
  307 + * @param seconds(秒) 过期时间
  308 + * @return
  309 + */
  310 + public boolean hset(String key, String field, Object value,long seconds) {
  311 + try {
  312 + redisTemplate.opsForHash().put(key, field, value);
  313 + expire(key,seconds);//调用通用方法设置过期时间
  314 + return true;
  315 + }catch (Exception e){
  316 + log.error("redis hset and expire eror,key:{},field:{},value:{},exception:{}",key,field,value,e);
  317 + return false;
  318 + }
  319 + }
  320 +
  321 + /**
  322 + * 获取key中field属性的值
  323 + * @param key
  324 + * @param field
  325 + * @return
  326 + */
  327 + public Object hget(String key,String field){
  328 + return redisTemplate.opsForHash().get(key,field);
  329 + }
  330 +
  331 + /**
  332 + * 获取key中多个属性的键值对,这儿使用map来接收
  333 + * @param key
  334 + * @param fields
  335 + * @return
  336 + */
  337 + public Map<String,Object> hmget(String key, String...fields){
  338 + Map<String,Object> map = new HashMap<>();
  339 + for (String field :fields){
  340 + map.put(field,hget(key,field));
  341 + }
  342 + return map;
  343 + }
  344 +
  345 + /**
  346 + * @param key 获得该key下的所有键值对
  347 + * @return
  348 + */
  349 + public Map<Object, Object> hmget(String key){
  350 + return redisTemplate.opsForHash().entries(key);
  351 + }
  352 +
  353 + /**
  354 + * @param key 获得该key下的所有键值对
  355 + * @return
  356 + */
  357 + //map----json字符串---->对象
  358 + public <T>T hmgetObject(String key,Class<T> tClass){
  359 + Map<Object, Object> hmget = hmget(key);
  360 + if(CollectionUtils.isEmpty(hmget)) return null;
  361 + //查询到了 先把数据转成json字符串
  362 + String s = GsonConstructor.get().toJson(hmget);
  363 + //再把json字符串转回对象
  364 + return GsonConstructor.get().fromJson(s,tClass);
  365 + }
  366 +
  367 + /**
  368 + * @param key 键
  369 + * @param map 对应多个键值
  370 + * @return
  371 + */
  372 + public boolean hmset(String key,Map<Object,Object> map){
  373 + try {
  374 + redisTemplate.opsForHash().putAll(key, map);
  375 + return true;
  376 + }catch (Exception e){
  377 + log.error("redis hmset eror,key:{},value:{},exception:{}",key,map,e);
  378 + return false;
  379 + }
  380 + }
  381 + public boolean hmset(String key,Object object){
  382 + try {
  383 + String s = GsonConstructor.get().toJson(object);
  384 + Map<String, String> map = GsonConstructor.get().fromJson(s,HashMap.class);
  385 + redisTemplate.opsForHash().putAll(key, map);
  386 + return true;
  387 + }catch (Exception e){
  388 + log.error("redis hmset eror,key:{},object:{},exception:{}",key,object,e);
  389 + return false;
  390 + }
  391 + }
  392 + /**
  393 + * @param key 键
  394 + * @param map 对应多个键值
  395 + * @param seconds 过期时间(秒)
  396 + * @return
  397 + */
  398 + public boolean hmset(String key,Map<String,Object> map,long seconds){
  399 + try {
  400 + redisTemplate.opsForHash().putAll(key, map);
  401 + expire(key,seconds);
  402 + return true;
  403 + }catch (Exception e){
  404 + log.error("redis hmset eror,key:{},value:{},expireTime,exception:{}",key,map,seconds,e);
  405 + return false;
  406 + }
  407 + }
  408 +
  409 + /**
  410 + *删除key中的属性
  411 + * @param key
  412 + * @param fields
  413 + */
  414 + public void hdel(String key,Object...fields){
  415 + redisTemplate.opsForHash().delete(key,fields);
  416 + }
  417 +
  418 + /**
  419 + * 判断key中是否存在某属性
  420 + * @param key
  421 + * @param field
  422 + * @return
  423 + */
  424 + public boolean hHashKey(String key,String field){
  425 + return redisTemplate.opsForHash().hasKey(key,field);
  426 + }
  427 +
  428 + /**
  429 + * 对key中filed的value增加多少 如果是减少就传入负数
  430 + * @param key
  431 + * @param field
  432 + * @param step 正数增加,负数减少
  433 + * @return
  434 + */
  435 + public double hincr(String key,String field,double step){
  436 + return redisTemplate.opsForHash().increment(key,field,step);
  437 + }
  438 +
  439 + /**
  440 + * key中多少个
  441 + * @param key
  442 + * @return
  443 + */
  444 + public long hlen(String key){
  445 + return redisTemplate.opsForHash().size(key);
  446 + }
  447 + /******其他方法用到在增加********/
  448 +
  449 + /***set集合***/
  450 + /**
  451 + * 获取key中所有元素
  452 + * @param key
  453 + * @return
  454 + */
  455 + public Set<Object> sgetAll(String key){
  456 + try {
  457 + return redisTemplate.opsForSet().members(key);
  458 + }catch (Exception e){
  459 + log.error("redis sgetAll error,key:{},exception:{}",key,e);
  460 + return null;
  461 + }
  462 + }
  463 +
  464 + /**
  465 + * 判断value是否在key中
  466 + * @param key
  467 + * @param value
  468 + * @return
  469 + */
  470 + public boolean sexists(String key,Object value){
  471 + try {
  472 + return redisTemplate.opsForSet().isMember(key,value);
  473 + }catch (Exception e){
  474 + log.error("redis sexists error,key:{},value:{},exception:{}",key,value,e);
  475 + return false;
  476 + }
  477 + }
  478 +
  479 + /**
  480 + * 插入多个元素
  481 + * @param key
  482 + * @param values
  483 + * @return 成功的个数
  484 + */
  485 + public long sset(String key ,Object...values){
  486 + try {
  487 + return redisTemplate.opsForSet().add(key,values);
  488 + }catch (Exception e){
  489 + log.error("redis sset error,key:{},value:{},values:{},exception:{}",key,values,e);
  490 + return 0;
  491 + }
  492 + }
  493 +
  494 + /**
  495 + * 添加元素并设置过期时间 (非原子操作)
  496 + * @param key
  497 + * @param time
  498 + * @param values
  499 + * @return
  500 + */
  501 + public long sset(String key ,long time,Object...values){
  502 + try {
  503 + long count = redisTemplate.opsForSet().add(key,values);
  504 + expire(key,time);
  505 + return count;
  506 + }catch (Exception e){
  507 + log.error("redis sset error,key:{},value:{},values:{},exception:{}",key,values,e);
  508 + return 0;
  509 + }
  510 + }
  511 +
  512 + /**
  513 + * 获取set的长度
  514 + * @param key
  515 + * @return
  516 + */
  517 + public long sgetSize(String key){
  518 + try {
  519 + return redisTemplate.opsForSet().size(key);
  520 + }catch (Exception e){
  521 + log.error("redis sgetSize error,key:{},exception:{}",key,e);
  522 + return 0;
  523 + }
  524 + }
  525 + /**
  526 + * 移除值为value的
  527 + * @param key 键
  528 + * @param values 值 可以是多个
  529 + * @return 移除的个数
  530 + */
  531 + public long sRemove(String key, Object... values) {
  532 + try {
  533 + Long count = redisTemplate.opsForSet().remove(key, values);
  534 + return count;
  535 + } catch (Exception e) {
  536 + log.error("redis sRemove error,key:{},values:{},exception:{}",key,values,e);
  537 + return 0;
  538 + }
  539 + }
  540 +
  541 + /**
  542 + * 随机取count个元素 count为正数就取不重复的 负数就有可能重复
  543 + * @param key
  544 + * @param count
  545 + * @return
  546 + */
  547 + public List<Object> sRandom(String key,long count) {
  548 + try {
  549 + return redisTemplate.opsForSet().randomMembers(key,count);
  550 + } catch (Exception e) {
  551 + log.error("redis sRandom error,key:{},count:{},exception:{}",key,count,e);
  552 + return null;
  553 + }
  554 + }
  555 + /****zset工具类***/
  556 + /**
  557 + * 添加元素
  558 + * @param key
  559 + * @param member
  560 + * @param score
  561 + * @return
  562 + */
  563 + public boolean zadd(String key,Object member,double score){
  564 + try {
  565 + return redisTemplate.opsForZSet().add(key,member,score);
  566 + } catch (Exception e) {
  567 + log.error("redis zadd error,key:{},value:{},score:{},exception:{}",key,member,score,e);
  568 + return false;
  569 + }
  570 + }
  571 + public Set<String> zrange(String key,int start,int end){
  572 +
  573 + try {
  574 + Set<Object> range = redisTemplate.opsForZSet().
  575 + range(key, start, end);
  576 + if(range==null||range.size()==0) return null;
  577 + return range.stream().
  578 + map(o->(String)o).collect(Collectors.toSet());
  579 + } catch (Exception e) {
  580 + log.error("redis zrange error,key:{},start:{},end:{},exception:{}",
  581 + key,start,end,e);
  582 + return null;
  583 + }
  584 + }
  585 +
  586 + /**
  587 + * 模糊匹配key
  588 + * @param match
  589 + * @return
  590 + */
  591 + public Set<String> keys(String match){
  592 + Set<String> ks = redisTemplate.keys(match);
  593 + return ks;
  594 + }
  595 +/***这个有点多,就不一一写了 大家用到什么去补全剩下的API
  596 + * Set< V > range(K key, long start, long end); 下标范围取
  597 + * Long remove(K key, Object… values); 删除元素
  598 + *Double incrementScore(K key, V value, double delta); 增加分数
  599 + * Long rank(K key, Object o); 当前元素的位置
  600 + *Long reverseRank(K key, Object o); 反过来的位置
  601 + *Set< TypedTuple< V >> rangeWithScores(K key, long start, long end); 下标取出来带分数
  602 + * Set< V > rangeByScore(K key, double min, double max); 根据分数取
  603 + * Set< TypedTuple< V >> rangeByScoreWithScores(K key, double min, double max); 根据分数取 并携带分数
  604 + * Set< TypedTuple< V >> rangeByScoreWithScores(K key, double min, double max, long offset, long count); 翻页
  605 + *Long count(K key, double min, double max); 统计分数段的个数
  606 + *Long size(K key); 个数 底层就是card
  607 + * Long zCard(K key); 个数
  608 + * Double score(K key, Object o); 查看某个元素的分数
  609 + * Long removeRange(K key, long start, long end); 根据下标删除某个范围
  610 + * Long removeRangeByScore(K key, double min, double max); 删除某个分数段
  611 + * 等等
  612 + * **/
  613 +}
  1 +package com.zhonglai.luhui.mqtt.comm.service;
  2 +
  3 +import com.luhui.ly.device.mqtt.comm.config.SysParameter;
  4 +import com.luhui.ly.device.mqtt.comm.service.MqttCallback;
  5 +import org.eclipse.paho.client.mqttv3.MqttClient;
  6 +import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
  7 +import org.eclipse.paho.client.mqttv3.MqttException;
  8 +import org.eclipse.paho.client.mqttv3.MqttMessage;
  9 +import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
  10 +import org.slf4j.Logger;
  11 +import org.slf4j.LoggerFactory;
  12 +import org.springframework.beans.factory.annotation.Autowired;
  13 +import org.springframework.beans.factory.annotation.Value;
  14 +import org.springframework.stereotype.Service;
  15 +
  16 +import javax.annotation.PostConstruct;
  17 +import java.nio.ByteBuffer;
  18 +import java.nio.charset.Charset;
  19 +import java.util.concurrent.ExecutorService;
  20 +import java.util.concurrent.LinkedBlockingQueue;
  21 +import java.util.concurrent.ThreadPoolExecutor;
  22 +import java.util.concurrent.TimeUnit;
  23 +
  24 +/**
  25 + * 终端服务
  26 + */
  27 +@Service
  28 +public class TerminalService {
  29 + private static final Logger log = LoggerFactory.getLogger(com.luhui.ly.device.mqtt.comm.service.TerminalService.class);
  30 + @Autowired
  31 + private MqttCallback mqttCallback;
  32 +
  33 + @Autowired
  34 + private SysParameter sysParameter;
  35 +
  36 + //业务处理异步线程池,线程池参数可以根据您的业务特点调整,或者您也可以用其他异步方式处理接收到的消息。
  37 + private final static ExecutorService executorService = new ThreadPoolExecutor(
  38 + Runtime.getRuntime().availableProcessors(),
  39 + Runtime.getRuntime().availableProcessors() * 2, 60, TimeUnit.SECONDS,
  40 + new LinkedBlockingQueue<>(50000));
  41 +
  42 + @Value("${mqtt.broker}")
  43 + private String broker;
  44 + @Value("${mqtt.clientId}")
  45 + private String clientId;
  46 + @Value("${mqtt.topics}")
  47 + private String topics;
  48 + @Value("${mqtt.username}")
  49 + private String username;
  50 + @Value("${mqtt.password}")
  51 + private String password;
  52 +
  53 + private MqttClient mqttclient;
  54 +
  55 + private MqttConnectOptions options;
  56 +
  57 + private void init() throws MqttException {
  58 + if(null == mqttclient)
  59 + {
  60 + mqttclient = new MqttClient(broker, clientId, new MemoryPersistence());
  61 + }
  62 + options = new MqttConnectOptions();
  63 + options.setCleanSession(true);
  64 + options.setConnectionTimeout(15);
  65 + //设置断开后重新连接
  66 + options.setAutomaticReconnect(true);
  67 + mqttclient.setCallback(mqttCallback);
  68 + }
  69 +
  70 + private void connect() throws MqttException {
  71 + options.setUserName(username);
  72 + options.setPassword(password.toCharArray());
  73 + mqttclient.connect(options);
  74 + }
  75 +
  76 + public void subscribe() throws MqttException {
  77 + mqttclient.subscribe(topics.split(","));
  78 + }
  79 +
  80 + @PostConstruct
  81 + public void startMqttListenerService() throws MqttException {
  82 + log.info("-----------开始启动mqtt监听服务--------------------");
  83 + init();
  84 + log.info("-----------启动参数{}--------------------",options);
  85 + connect();
  86 + subscribe();
  87 + sysParameter.inittopicconfig();
  88 + log.info("-----------mqtt监听服务启动成功--------------------");
  89 + }
  90 +
  91 + public void subscribe(String[] topicFilters) throws MqttException {
  92 + mqttclient.subscribe(topicFilters);
  93 + }
  94 +
  95 + public void publish(String topic, MqttMessage message) throws MqttException {
  96 + mqttclient.publish(topic,message);
  97 + }
  98 +
  99 + public void publish(String topic, String messageStr) throws MqttException {
  100 + MqttMessage message = new MqttMessage();
  101 + message.setPayload(messageStr.getBytes());
  102 + mqttclient.publish(topic,message);
  103 + }
  104 +
  105 + public void closeClient (String clientId,String code,String messageStr) throws MqttException {
  106 + String topic = "SYSOPERATION/CLOSE";
  107 + MqttMessage message = new MqttMessage();
  108 + Charset charset = Charset.forName("utf-8");
  109 + ByteBuffer payload = charset.encode(clientId+","+code+","+messageStr);
  110 + message.setPayload(payload.array());
  111 + mqttclient.publish(topic,message);
  112 + }
  113 +
  114 +}
  1 +package com.zhonglai.luhui.mqtt.comm.service;
  2 +
  3 +public interface TopicsService {
  4 + String getClienConnectionMapKey(String imei, String messageid);
  5 + String getTopicFromImei(String imei, String messageid);
  6 +}
  1 +package com.zhonglai.luhui.mqtt.comm.util;
  2 +
  3 +import com.google.gson.JsonObject;
  4 +import org.apache.commons.lang3.StringUtils;
  5 +
  6 +public class ChangUtil {
  7 + public static boolean changString(JsonObject oldData, String strName, String strValue)
  8 + {
  9 + if(StringUtils.isNoneBlank(strValue))
  10 + {
  11 + if(!oldData.has(strName) || !strValue.equals(oldData.get(strName).getAsString()))
  12 + {
  13 + oldData.addProperty(strName,strValue);
  14 + return true;
  15 + }
  16 + }
  17 + return false;
  18 + }
  19 +
  20 + public static boolean changInt(JsonObject oldData, String intName, Integer intValue)
  21 + {
  22 + if(null != intValue)
  23 + {
  24 + if(!oldData.has(intName) || intValue -oldData.get(intName).getAsInt() != 0 )
  25 + {
  26 + oldData.addProperty(intName,intValue);
  27 + return true;
  28 + }
  29 + }
  30 + return false;
  31 + }
  32 +
  33 + public static boolean changFloat(JsonObject oldData, String intName, Float intValue)
  34 + {
  35 + if(null != intValue)
  36 + {
  37 + if(!oldData.has(intName) || intValue -oldData.get(intName).getAsFloat() != 0 )
  38 + {
  39 + oldData.addProperty(intName,intValue);
  40 + return true;
  41 + }
  42 + }
  43 + return false;
  44 + }
  45 +}
  46 +
@@ -289,6 +289,24 @@ @@ -289,6 +289,24 @@
289 <artifactId>mapper-spring-boot-starter</artifactId> 289 <artifactId>mapper-spring-boot-starter</artifactId>
290 <version>${tkmapper.version}</version> 290 <version>${tkmapper.version}</version>
291 </dependency> 291 </dependency>
  292 +
  293 + <!-- mqtt -->
  294 + <dependency>
  295 + <groupId>org.eclipse.paho</groupId>
  296 + <artifactId>org.eclipse.paho.client.mqttv3</artifactId>
  297 + <version>1.0.2</version>
  298 + <exclusions>
  299 + <exclusion>
  300 + <groupId>org.slf4j</groupId>
  301 + <artifactId>slf4j-log4j12</artifactId>
  302 + </exclusion>
  303 + </exclusions>
  304 + </dependency>
  305 + <dependency>
  306 + <groupId>net.jodah</groupId>
  307 + <artifactId>expiringmap</artifactId>
  308 + <version>0.5.8</version>
  309 + </dependency>
292 </dependencies> 310 </dependencies>
293 311
294 312
1 -package com.ruoyi.system.dto; 1 +package com.ruoyi.common.core.domain;
2 2
3 import lombok.Data; 3 import lombok.Data;
4 4
@@ -37,4 +37,13 @@ public class Message { @@ -37,4 +37,13 @@ public class Message {
37 this.code = code.getCode(); 37 this.code = code.getCode();
38 this.message = code.getMessage(); 38 this.message = code.getMessage();
39 } 39 }
  40 +
  41 + public void setCode(MessageCode messageCode )
  42 + {
  43 + code = messageCode.code;
  44 + }
  45 +
  46 + public void setCode(MessageCodeType code) {
  47 + this.code = code.getCode();
  48 + }
40 } 49 }
1 -package com.ruoyi.system.dto; 1 +package com.ruoyi.common.core.domain;
2 2
3 public enum MessageCode implements MessageCodeType{ 3 public enum MessageCode implements MessageCodeType{
4 DEFAULT_FAIL_CODE(0, "请求失败"), 4 DEFAULT_FAIL_CODE(0, "请求失败"),
1 -package com.ruoyi.system.dto; 1 +package com.ruoyi.common.core.domain;
2 2
3 public interface MessageCodeType { 3 public interface MessageCodeType {
4 int getCode(); 4 int getCode();
  1 +package com.ruoyi.common.utils;
  2 +
  3 +import java.util.Arrays;
  4 +
  5 +public class ByteUtil {
  6 + /**
  7 + * byte数组中取int数值,本方法适用于(低位在前,高位在后)的顺序,和和intToBytes()配套使用
  8 + *
  9 + * @param src
  10 + * byte数组
  11 + * @param offset
  12 + * 从数组的第offset位开始
  13 + * @return int数值
  14 + */
  15 + public static long bytesToLongASC(byte[] src, int offset,int lenth) {
  16 + int value = 0;
  17 + for(int i=0;i<lenth;i++)
  18 + {
  19 + value = value | ((src[offset+i] & 0xFF)<<(8*i));
  20 + }
  21 + return value;
  22 + }
  23 +
  24 + /**
  25 + * 把16进制字符串转换成字节数组
  26 + *
  27 + * @param hex
  28 + * @return
  29 + */
  30 + public static byte[] hexStringToByte(String hex) {
  31 + int len = (hex.length() / 2);
  32 + byte[] result = new byte[len];
  33 + char[] achar = hex.toCharArray();
  34 + for (int i = 0; i < len; i++) {
  35 + int pos = i * 2;
  36 + result[i] = (byte) (toByte(achar[pos]) << 4 | toByte(achar[pos + 1]));
  37 + }
  38 + return result;
  39 + }
  40 + private static byte toByte(char c) {
  41 + byte b = (byte) "0123456789ABCDEF".indexOf(c);
  42 + return b;
  43 + }
  44 +
  45 + /**
  46 + * 把16进制字符串转换成字节数组
  47 + *
  48 + * @param hex
  49 + * @return
  50 + */
  51 + public static String hexStringToSpace(String hex) {
  52 + if (null == hex) {
  53 + return null;
  54 + } else {
  55 + StringBuilder sb = new StringBuilder(hex.length() << 1);
  56 +
  57 + for(int i = 0; i < hex.length(); i+=2) {
  58 + sb.append(hex.substring(i,i+2)).append(" ");
  59 + }
  60 + return sb.toString();
  61 + }
  62 + }
  63 +
  64 + /**
  65 + * 把原数组加点目标数组后面
  66 + * @param dest 目标数组
  67 + * @param src 原数组
  68 + * @return
  69 + */
  70 + public static byte[] addBytes(byte[] dest,byte[] src )
  71 + {
  72 + int dl = dest.length;
  73 + int sl = src.length;
  74 + dest = Arrays.copyOf(dest, dl+sl);//数组扩容
  75 + System.arraycopy(src,0,dest,dl,src.length);
  76 + return dest;
  77 + }
  78 +
  79 + /**
  80 + * 将int数值转换为占四个字节的byte数组,本方法适用于(高位在前,低位在后)的顺序。 和bytesToInt2()配套使用
  81 + */
  82 + public static byte[] intToBytesDESC(long value,int lenth)
  83 + {
  84 + byte[] src = new byte[lenth];
  85 + for(int i=0;i<lenth;i++)
  86 + {
  87 + src[i] = (byte) ((value>>(8*(lenth-i-1))) & 0xFF);
  88 + }
  89 + return src;
  90 + }
  91 +
  92 + /**
  93 + * 将int数值转换为占四个字节的byte数组,本方法适用于(低位在前,高位在后)的顺序。 和bytesToInt()配套使用
  94 + * @param value
  95 + * 要转换的int值
  96 + * @return byte数组
  97 + */
  98 + public static byte[] intToBytesASC( long value,int lenth)
  99 + {
  100 + byte[] src = new byte[lenth];
  101 + for(int i=lenth;i>0;i--)
  102 + {
  103 + src[i-1] = (byte) ((value>>(8*(i-1))) & 0xFF);
  104 + }
  105 + return src;
  106 + }
  107 +
  108 + public static void main(String[] args) {
  109 + System.out.println(ByteUtil.toHexString( ByteUtil.intToBytesASC(2011239256,4)));
  110 + }
  111 +
  112 + /**
  113 + * ip转化位4byte
  114 + * @param ip
  115 + * @return
  116 + */
  117 + public static byte[] ipTo4Byte(String ip)
  118 + {
  119 + String[] ips = ip.split(".");
  120 + return new byte[]{(byte) Integer.parseInt(ips[0]),(byte) Integer.parseInt(ips[1]),(byte) Integer.parseInt(ips[2]),(byte) Integer.parseInt(ips[3])};
  121 + }
  122 +
  123 + /**
  124 + * byte数组中取int数值,本方法适用于(低位在后,高位在前)的顺序。和intToBytes2()配套使用
  125 + */
  126 + public static long bytesToLongDESC(byte[] src, int offset,int lenth) {
  127 + long value = 0;
  128 + for(int i=lenth;i>0;i--)
  129 + {
  130 + value = value | ((src[offset+(lenth-i)] & 0xFF)<<(8*(i-1)));
  131 + }
  132 + return value;
  133 + }
  134 +
  135 + private static final char[] hex = "0123456789abcdef".toCharArray();
  136 + public static String toHexString(byte[] bytes) {
  137 + if (null == bytes) {
  138 + return null;
  139 + } else {
  140 + StringBuilder sb = new StringBuilder(bytes.length << 1);
  141 +
  142 + for(int i = 0; i < bytes.length; ++i) {
  143 + sb.append(hex[(bytes[i] & 240) >> 4]).append(hex[bytes[i] & 15]);
  144 + }
  145 +
  146 + return sb.toString();
  147 + }
  148 + }
  149 +
  150 + /**
  151 + * 计算CRC16/Modbus校验码 低位在前,高位在后
  152 + *
  153 + * @param str 十六进制字符串
  154 + * @return
  155 + */
  156 + public static String getCRC16(String str) {
  157 + byte[] bytes = hexStringToByte(str);
  158 + return getCRC16(bytes);
  159 + }
  160 +
  161 + /**
  162 + * 计算CRC16/Modbus校验码 低位在前,高位在后
  163 + *
  164 + * @return
  165 + */
  166 + public static String getCRC16( byte[] bytes) {
  167 + int CRC = 0x0000ffff;
  168 + int POLYNOMIAL = 0x0000a001;
  169 +
  170 + int i, j;
  171 + for (i = 0; i < bytes.length; i++) {
  172 + CRC ^= ((int) bytes[i] & 0x000000ff);
  173 + for (j = 0; j < 8; j++) {
  174 + if ((CRC & 0x00000001) != 0) {
  175 + CRC >>= 1;
  176 + CRC ^= POLYNOMIAL;
  177 + } else {
  178 + CRC >>= 1;
  179 + }
  180 + }
  181 + }
  182 + String crc = Integer.toHexString(CRC);
  183 + if (crc.length() == 2) {
  184 + crc = "00" + crc;
  185 + } else if (crc.length() == 3) {
  186 + crc = "0" + crc;
  187 + }
  188 + crc = crc.substring(2, 4) + crc.substring(0, 2);
  189 + return crc.toUpperCase();
  190 + }
  191 +}
@@ -47,8 +47,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" @@ -47,8 +47,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
47 update iot_role 47 update iot_role
48 <trim prefix="SET" suffixOverrides=","> 48 <trim prefix="SET" suffixOverrides=",">
49 <if test="create_time != null">create_time = #{create_time},</if> 49 <if test="create_time != null">create_time = #{create_time},</if>
50 - <if test="describe != null">describe = #{describe},</if>  
51 - <if test="name != null">name = #{name},</if> 50 + <if test="describe != null">`describe` = #{describe},</if>
  51 + <if test="name != null">`name` = #{name},</if>
52 <if test="used != null">used = #{used},</if> 52 <if test="used != null">used = #{used},</if>
53 </trim> 53 </trim>
54 where id = #{id} 54 where id = #{id}