From d2d6e406a6098f7dd8ccf535e20e8d78d2ae6a13 Mon Sep 17 00:00:00 2001 From: lxg Date: Sun, 10 Nov 2024 20:29:28 +0800 Subject: [PATCH] =?UTF-8?q?=E9=A1=B9=E7=9B=AE=E4=B8=8A=E4=BC=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 103 +++++++++++++++++++++ src/main/java/com/whhj/Application.java | 18 ++++ .../com/whhj/config/ChannelInitializerImpl.java | 25 +++++ .../java/com/whhj/constant/CommonConstant.java | 10 ++ .../java/com/whhj/dao/YlSmartMattressMapper.java | 13 +++ src/main/java/com/whhj/entity/DeviceInfo.java | 41 ++++++++ src/main/java/com/whhj/entity/YlSmartMattress.java | 35 +++++++ .../java/com/whhj/handler/NettyServerHandler.java | 60 ++++++++++++ src/main/java/com/whhj/handler/QuantumDecoder.java | 67 ++++++++++++++ src/main/java/com/whhj/server/IotServer.java | 52 +++++++++++ .../com/whhj/service/YlSmartMattressService.java | 15 +++ .../java/com/whhj/util/ApplicationContextUtil.java | 25 +++++ src/main/java/com/whhj/util/HexUtil.java | 20 ++++ src/main/resources/application-test.yml | 26 ++++++ src/main/resources/application.yml | 3 + src/main/resources/logback-spring.xml | 47 ++++++++++ .../resources/mapper/YlSmartMattressMapper.xml | 5 + src/test/java/ApplicationTest.java | 30 ++++++ 18 files changed, 595 insertions(+) create mode 100644 pom.xml create mode 100644 src/main/java/com/whhj/Application.java create mode 100644 src/main/java/com/whhj/config/ChannelInitializerImpl.java create mode 100644 src/main/java/com/whhj/constant/CommonConstant.java create mode 100644 src/main/java/com/whhj/dao/YlSmartMattressMapper.java create mode 100644 src/main/java/com/whhj/entity/DeviceInfo.java create mode 100644 src/main/java/com/whhj/entity/YlSmartMattress.java create mode 100644 src/main/java/com/whhj/handler/NettyServerHandler.java create mode 100644 src/main/java/com/whhj/handler/QuantumDecoder.java create mode 100644 src/main/java/com/whhj/server/IotServer.java create mode 100644 src/main/java/com/whhj/service/YlSmartMattressService.java create mode 100644 src/main/java/com/whhj/util/ApplicationContextUtil.java create mode 100644 src/main/java/com/whhj/util/HexUtil.java create mode 100644 src/main/resources/application-test.yml create mode 100644 src/main/resources/application.yml create mode 100644 src/main/resources/logback-spring.xml create mode 100644 src/main/resources/mapper/YlSmartMattressMapper.xml create mode 100644 src/test/java/ApplicationTest.java diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..0164721 --- /dev/null +++ b/pom.xml @@ -0,0 +1,103 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.7.18 + + + com.whhz + quantum-iot + 1.0-SNAPSHOT + + + 8 + 8 + UTF-8 + 4.1.42.Final + + + + + org.springframework.boot + spring-boot-starter + + + io.netty + netty-all + ${netty.version} + + + org.projectlombok + lombok + 1.18.32 + + + com.alibaba + fastjson + 1.2.83 + + + mysql + mysql-connector-java + 5.1.40 + + + com.baomidou + mybatis-plus-boot-starter + 3.4.3 + + + cn.hutool + hutool-core + 5.7.16 + + + + + org.springframework.boot + spring-boot-starter-test + test + + + junit + junit + 4.13.2 + test + + + + + org.springframework.boot + spring-boot-starter-logging + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + com.whhj.Application + + + + org.apache.maven.plugins + maven-jar-plugin + + + + com.whhj.Application + + + + + + + + \ No newline at end of file diff --git a/src/main/java/com/whhj/Application.java b/src/main/java/com/whhj/Application.java new file mode 100644 index 0000000..b7223d9 --- /dev/null +++ b/src/main/java/com/whhj/Application.java @@ -0,0 +1,18 @@ +package com.whhj; + +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * @author lxg + * @date 2024/10/20 17:35 + **/ +@MapperScan("com.whhj.dao") +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} diff --git a/src/main/java/com/whhj/config/ChannelInitializerImpl.java b/src/main/java/com/whhj/config/ChannelInitializerImpl.java new file mode 100644 index 0000000..5f5396f --- /dev/null +++ b/src/main/java/com/whhj/config/ChannelInitializerImpl.java @@ -0,0 +1,25 @@ +package com.whhj.config; + +import com.whhj.handler.NettyServerHandler; +import com.whhj.handler.QuantumDecoder; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.socket.SocketChannel; +import io.netty.handler.codec.string.StringEncoder; + +/** + * @author lxg + * @date 2024/10/20 19:33 + **/ + + +public class ChannelInitializerImpl extends ChannelInitializer { + + @Override + protected void initChannel(SocketChannel ch) { + ChannelPipeline pipeline = ch.pipeline(); + pipeline.addLast( new QuantumDecoder()); + pipeline.addLast(new StringEncoder()); + pipeline.addLast(new NettyServerHandler()); // 自定义处理器 + } +} \ No newline at end of file diff --git a/src/main/java/com/whhj/constant/CommonConstant.java b/src/main/java/com/whhj/constant/CommonConstant.java new file mode 100644 index 0000000..64fcbef --- /dev/null +++ b/src/main/java/com/whhj/constant/CommonConstant.java @@ -0,0 +1,10 @@ +package com.whhj.constant; + +/** + * @author lxg + * @date 2024/10/30 23:26 + **/ +public interface CommonConstant { + + String DEVICE_BRAND_CODE = "4"; +} diff --git a/src/main/java/com/whhj/dao/YlSmartMattressMapper.java b/src/main/java/com/whhj/dao/YlSmartMattressMapper.java new file mode 100644 index 0000000..7ad1c81 --- /dev/null +++ b/src/main/java/com/whhj/dao/YlSmartMattressMapper.java @@ -0,0 +1,13 @@ +package com.whhj.dao; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.whhj.entity.YlSmartMattress; +import org.apache.ibatis.annotations.Mapper; + +/** + * @author lxg + * @date 2024/10/22 23:28 + **/ +@Mapper +public interface YlSmartMattressMapper extends BaseMapper { +} diff --git a/src/main/java/com/whhj/entity/DeviceInfo.java b/src/main/java/com/whhj/entity/DeviceInfo.java new file mode 100644 index 0000000..e611e75 --- /dev/null +++ b/src/main/java/com/whhj/entity/DeviceInfo.java @@ -0,0 +1,41 @@ +package com.whhj.entity; + +import lombok.Getter; +import lombok.Setter; + +/** + * @author lxg + * @date 2024/10/20 22:08 + **/ +@Getter +@Setter +public class DeviceInfo{ + /** + * 命令类型 + */ + private Integer commandType; + /** + * 设备sn + */ + private String sn; + /** + * 上报时间 + */ + private Integer reportTime; + /** + * 心率 + */ + private Integer heartRate; + /** + * 呼吸值 + */ + private Integer breath; + /** + * 状态 + */ + private Integer status; + /** + * 电量 + */ + private Integer electricity; +} diff --git a/src/main/java/com/whhj/entity/YlSmartMattress.java b/src/main/java/com/whhj/entity/YlSmartMattress.java new file mode 100644 index 0000000..308d3f1 --- /dev/null +++ b/src/main/java/com/whhj/entity/YlSmartMattress.java @@ -0,0 +1,35 @@ +package com.whhj.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.util.Date; + +@Data +@TableName("yl_smart_mattress") +public class YlSmartMattress { + + @TableId(type = IdType.AUTO) + private Long id; + + private String deviceId; + + private Integer heart; + + private Integer breathe; + + private String leaveBed; + + private Boolean move; + + private Boolean wifi; + + private Boolean m4g; + + private Date createTime; + + private String brand; + +} \ No newline at end of file diff --git a/src/main/java/com/whhj/handler/NettyServerHandler.java b/src/main/java/com/whhj/handler/NettyServerHandler.java new file mode 100644 index 0000000..40ef46d --- /dev/null +++ b/src/main/java/com/whhj/handler/NettyServerHandler.java @@ -0,0 +1,60 @@ +package com.whhj.handler; + +import cn.hutool.core.date.DateUtil; +import com.alibaba.fastjson.JSON; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.whhj.constant.CommonConstant; +import com.whhj.entity.DeviceInfo; +import com.whhj.entity.YlSmartMattress; +import com.whhj.service.YlSmartMattressService; +import com.whhj.util.ApplicationContextUtil; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import lombok.extern.slf4j.Slf4j; + +/** + * @author lxg + * @date 2024/10/20 19:34 + **/ + +@Slf4j +@ChannelHandler.Sharable +public class NettyServerHandler extends ChannelInboundHandlerAdapter { + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) { + if (msg instanceof DeviceInfo) { + DeviceInfo reportInfo = (DeviceInfo) msg; + log.info("Received info: {}", JSON.toJSONString(reportInfo)); + YlSmartMattressService service = ApplicationContextUtil.getBean(YlSmartMattressService.class); + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("device_id", reportInfo.getSn()); + queryWrapper.eq("brand", CommonConstant.DEVICE_BRAND_CODE); + YlSmartMattress originInfo = service.getOne(queryWrapper); + YlSmartMattress ylSmartMattress = new YlSmartMattress(); + if (originInfo != null) { + ylSmartMattress.setId(originInfo.getId()); + } + ylSmartMattress.setBrand(CommonConstant.DEVICE_BRAND_CODE); + ylSmartMattress.setDeviceId(reportInfo.getSn()); + ylSmartMattress.setHeart(reportInfo.getHeartRate()); + ylSmartMattress.setBreathe(reportInfo.getBreath()); + ylSmartMattress.setLeaveBed(reportInfo.getStatus() == 4 ? "1" : "0"); + ylSmartMattress.setCreateTime(DateUtil.date(Long.valueOf(reportInfo.getReportTime()) * 1000)); + service.saveOrUpdate(ylSmartMattress); + ctx.writeAndFlush("received data"); + } + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + cause.printStackTrace(); + ctx.close(); + } + + @Override + public void channelActive(ChannelHandlerContext ctx) { + log.info("Client:{} connected",ctx.channel().remoteAddress()); + } +} diff --git a/src/main/java/com/whhj/handler/QuantumDecoder.java b/src/main/java/com/whhj/handler/QuantumDecoder.java new file mode 100644 index 0000000..4364741 --- /dev/null +++ b/src/main/java/com/whhj/handler/QuantumDecoder.java @@ -0,0 +1,67 @@ +package com.whhj.handler; + +import cn.hutool.core.util.StrUtil; +import com.whhj.entity.DeviceInfo; +import com.whhj.util.HexUtil; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.ByteToMessageDecoder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.nio.ByteOrder; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.List; + +/** + * @author lxg + * @date 2024/10/20 22:04 + **/ +public class QuantumDecoder extends ByteToMessageDecoder { + + private static final Logger log = LoggerFactory.getLogger(QuantumDecoder.class); + + private static final int FRAME_LENGTH = 16; + private static final String START_MARKER = "cd"; + @Override + protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf in, List list) { + // 检查是否有足够的字节来读取一个完整的消息 + if (in.readableBytes() < FRAME_LENGTH) { + return; + } + in.markReaderIndex(); + String frameHeader = Integer.toHexString(0xFF & in.readByte()); + + // 检查起始标识是否匹配 + if (!StrUtil.equals(frameHeader, START_MARKER)) { + in.resetReaderIndex(); + byte[] data = new byte[in.readableBytes()]; + in.readBytes(data); + try { + log.info("invalid data:{}",HexUtil.bytesToHexStr(data)); + }catch (Exception e){ + e.printStackTrace(); + } + return; + } + DeviceInfo deviceInfo = new DeviceInfo(); + int cmdType = in.readByte(); + deviceInfo.setCommandType(cmdType); + byte[] sn = new byte[6]; + in.readBytes(sn); + deviceInfo.setSn(HexUtil.bytesToHexStr(sn)); + byte[] reportTime = new byte[4]; + in.readBytes(reportTime); + deviceInfo.setReportTime(ByteBuffer.wrap(reportTime).order(ByteOrder.BIG_ENDIAN).getInt()); + int heartRate = in.readByte(); + deviceInfo.setHeartRate(heartRate); + int breath = in.readByte(); + deviceInfo.setBreath(breath); + int status = in.readByte(); + deviceInfo.setStatus(status); + int electricity = in.readByte(); + deviceInfo.setElectricity(electricity); + list.add(deviceInfo); + } +} diff --git a/src/main/java/com/whhj/server/IotServer.java b/src/main/java/com/whhj/server/IotServer.java new file mode 100644 index 0000000..6309ce9 --- /dev/null +++ b/src/main/java/com/whhj/server/IotServer.java @@ -0,0 +1,52 @@ +package com.whhj.server; + +import com.whhj.config.ChannelInitializerImpl; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelOption; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.*; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.ApplicationListener; +import org.springframework.context.event.ContextClosedEvent; +import org.springframework.stereotype.Component; + +/** + * @author lxg + * @date 2024/10/20 17:36 + **/ +@Slf4j +@Component +public class IotServer implements ApplicationListener { + + private final EventLoopGroup bossGroup = new NioEventLoopGroup(); + private final EventLoopGroup workerGroup = new NioEventLoopGroup(); + private final ServerBootstrap bootstrap = new ServerBootstrap(); + + @Override + public void onApplicationEvent(ContextClosedEvent event) { + bootstrap.group(bossGroup, workerGroup) + .channel(NioServerSocketChannel.class) + .childHandler(new ChannelInitializerImpl()) + .option(ChannelOption.SO_BACKLOG, 128) + .childOption(ChannelOption.SO_KEEPALIVE, true); + try { + + ChannelFuture channelFuture = bootstrap.bind(8080).sync(); + log.info("服务启动:{}", channelFuture.channel().localAddress()); + channelFuture.channel().closeFuture().sync(); + log.info("服务关闭"); + } catch (InterruptedException e) { + e.printStackTrace(); + } finally { + try { + bossGroup.shutdownGracefully().sync(); + workerGroup.shutdownGracefully().sync(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + log.info("资源已释放"); + } + } +} diff --git a/src/main/java/com/whhj/service/YlSmartMattressService.java b/src/main/java/com/whhj/service/YlSmartMattressService.java new file mode 100644 index 0000000..141562e --- /dev/null +++ b/src/main/java/com/whhj/service/YlSmartMattressService.java @@ -0,0 +1,15 @@ +package com.whhj.service; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.whhj.dao.YlSmartMattressMapper; +import com.whhj.entity.YlSmartMattress; +import org.springframework.stereotype.Service; + +/** + * @author lxg + * @date 2024/10/22 23:32 + **/ +@Service +public class YlSmartMattressService extends ServiceImpl { + +} diff --git a/src/main/java/com/whhj/util/ApplicationContextUtil.java b/src/main/java/com/whhj/util/ApplicationContextUtil.java new file mode 100644 index 0000000..61342e6 --- /dev/null +++ b/src/main/java/com/whhj/util/ApplicationContextUtil.java @@ -0,0 +1,25 @@ +package com.whhj.util; + +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; + +/** + * @author lxg + * @date 2024/10/23 22:09 + **/ +@Component +public class ApplicationContextUtil implements ApplicationContextAware { + + private static ApplicationContext context; + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + context = applicationContext; + } + + public static T getBean(Class clazz) { + return context.getBean(clazz); + } +} diff --git a/src/main/java/com/whhj/util/HexUtil.java b/src/main/java/com/whhj/util/HexUtil.java new file mode 100644 index 0000000..e1e5666 --- /dev/null +++ b/src/main/java/com/whhj/util/HexUtil.java @@ -0,0 +1,20 @@ +package com.whhj.util; + +/** + * @author lxg + * @date 2024/10/21 22:43 + **/ +public class HexUtil { + + public static String bytesToHexStr(byte[] bytes) { + StringBuilder hexString = new StringBuilder(); + for (byte b : bytes) { + String hex = Integer.toHexString(0xFF & b); + if (hex.length() == 1) { + hexString.append('0'); + } + hexString.append(hex); + } + return hexString.toString(); + } +} diff --git a/src/main/resources/application-test.yml b/src/main/resources/application-test.yml new file mode 100644 index 0000000..aa3240f --- /dev/null +++ b/src/main/resources/application-test.yml @@ -0,0 +1,26 @@ +# 服务端口配置 +server: + port: 8088 + +spring: + datasource: + url: jdbc:mysql://119.23.39.166:3306/jjyl_test?useUnicode=true&&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitlsBoolean=true + username: root + password: rootWHJ2018 + driver-class-name: com.mysql.jdbc.Driver + type: com.zaxxer.hikari.HikariDataSource + hikari: + connection-timeout: 30000 # 连接超时时间(毫秒) + maximum-pool-size: 10 # 最大连接池大小 + minimum-idle: 5 # 最小空闲连接数 + idle-timeout: 600000 # 空闲连接超时时间(毫秒) + max-lifetime: 1800000 # 连接的最大生命周期(毫秒) + pool-name: HikariCPDemoPool # 连接池名称 + auto-commit: true # 是否自动提交 + connection-test-query: SELECT 1 # 连接测试查询 + validation-timeout: 5000 # 连接验证超时时间(毫秒) + +mybatis-plus: + configuration: + log-impl: org.apache.ibatis.logging.stdout.StdOutImpl + mapper-locations: classpath:mapper/*.xml diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..027b4e3 --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,3 @@ +spring: + profiles: + active: test \ No newline at end of file diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..f9071a4 --- /dev/null +++ b/src/main/resources/logback-spring.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + %d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n + + + + + + ${LOG_FILE} + + + ${LOG_PATH}/app.%d{yyyy-MM-dd}.%i.log + + ${LOG_FILE_MAX_HISTORY} + + + ${LOG_FILE_MAX_SIZE} + + + + %d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/mapper/YlSmartMattressMapper.xml b/src/main/resources/mapper/YlSmartMattressMapper.xml new file mode 100644 index 0000000..1c853a0 --- /dev/null +++ b/src/main/resources/mapper/YlSmartMattressMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/src/test/java/ApplicationTest.java b/src/test/java/ApplicationTest.java new file mode 100644 index 0000000..ccb1940 --- /dev/null +++ b/src/test/java/ApplicationTest.java @@ -0,0 +1,30 @@ +import com.whhj.Application; +import com.whhj.service.YlSmartMattressService; +import com.whhj.util.ApplicationContextUtil; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +/** + * @author lxg + * @date 2024/10/23 21:34 + **/ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = Application.class) +public class ApplicationTest { + + @Autowired + private YlSmartMattressService ylSmartMattressService; + + @Autowired + private ApplicationContextUtil applicationContextUtil; + + @Test + public void test_01() { + Integer cnt = ylSmartMattressService.count(); + System.out.println(cnt); + } + +}