Commit 2af9f9ee0e3034b5a72cc315645eca25b895f9a8

Authored by guzijian
1 parent 5598699e

feat: 设备指令下发

Showing 74 changed files with 3596 additions and 2 deletions
Bsth-admin/pom.xml
... ... @@ -74,6 +74,12 @@
74 74 <artifactId>mysql-connector-j</artifactId>
75 75 <version>8.0.33</version>
76 76 </dependency>
  77 + <!-- https://mvnrepository.com/artifact/io.netty/netty-all -->
  78 + <dependency>
  79 + <groupId>io.netty</groupId>
  80 + <artifactId>netty-all</artifactId>
  81 + <version>4.1.101.Final</version>
  82 + </dependency>
77 83  
78 84 <!-- 核心模块-->
79 85 <dependency>
... ...
Bsth-admin/src/main/java/com/ruoyi/common/global/IDeviceException.java 0 → 100644
  1 +package com.ruoyi.common.global;
  2 +
  3 +import lombok.Data;
  4 +import lombok.NoArgsConstructor;
  5 +
  6 +/**
  7 + * 设备异常
  8 + * @author 20412
  9 + */
  10 +@Data
  11 +@NoArgsConstructor
  12 +public class IDeviceException extends RuntimeException{
  13 + private String msg;
  14 + private ResultCode code;
  15 +
  16 + public IDeviceException(ResultCode code, String msg) {
  17 + this.msg = msg;
  18 + this.code = code;
  19 + }
  20 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/client/ClientHandler.java 0 → 100644
  1 +package com.ruoyi.system.client;
  2 +
  3 +import io.netty.buffer.ByteBuf;
  4 +import io.netty.buffer.Unpooled;
  5 +import io.netty.channel.ChannelHandlerContext;
  6 +import io.netty.channel.ChannelInboundHandlerAdapter;
  7 +import io.netty.util.CharsetUtil;
  8 +
  9 +import java.util.Scanner;
  10 +
  11 +/**
  12 + * 测试类 模拟手表设备
  13 + */
  14 +public class ClientHandler extends ChannelInboundHandlerAdapter {
  15 +// @Override
  16 +// public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
  17 +// ctx.writeAndFlush(Unpooled.copiedBuffer("IWAP00353456789012345#", CharsetUtil.UTF_8));
  18 +//// super.channelRegistered(ctx);
  19 +// }
  20 +
  21 + @Override
  22 + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
  23 + ByteBuf byteBuf = (ByteBuf) msg;
  24 + String str = byteBuf.toString(CharsetUtil.UTF_8);
  25 + System.out.println("收到服务端" + ctx.channel().remoteAddress() + "的消息:" + byteBuf.toString(CharsetUtil.UTF_8));
  26 +
  27 + // 判断是否需要发送
  28 + if (str.startsWith("IWBP12")) {
  29 + String replace = str.replace(",353456789012345","").replace("IWBP12", "IWAP12");
  30 + System.out.println("客户端响应" + ctx.channel().remoteAddress() + "的消息:" + byteBuf.toString(CharsetUtil.UTF_8));
  31 + ctx.channel().writeAndFlush(Unpooled.copiedBuffer(replace, CharsetUtil.UTF_8));
  32 + }
  33 + if (str.startsWith("IWBP15")) {
  34 + String replace = str.replace(",353456789012345","").replace("IWBP15", "IWAP15");
  35 + System.out.println("客户端响应" + ctx.channel().remoteAddress() + "的消息:" + byteBuf.toString(CharsetUtil.UTF_8));
  36 + ctx.channel().writeAndFlush(Unpooled.copiedBuffer(replace, CharsetUtil.UTF_8));
  37 + }
  38 +// else {
  39 +// Scanner sc = new Scanner(System.in);
  40 +// String line = sc.nextLine();
  41 +// ctx.channel().writeAndFlush(Unpooled.copiedBuffer(line, CharsetUtil.UTF_8));
  42 +// }
  43 + }
  44 +
  45 +
  46 +// @Override
  47 +// public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
  48 +// ctx.writeAndFlush(Unpooled.copiedBuffer("歪比巴卜~茉莉~Are you good~马来西亚~", CharsetUtil.UTF_8));
  49 +// }
  50 +
  51 +
  52 + @Override
  53 + public void channelActive(ChannelHandlerContext ctx) throws Exception {
  54 + ctx.writeAndFlush(Unpooled.copiedBuffer("IWAP00353456789012345#", CharsetUtil.UTF_8));
  55 + }
  56 +
  57 + @Override
  58 + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
  59 + System.out.println(cause.getMessage());
  60 + }
  61 +
  62 +
  63 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/client/NettyClientDemoMain.java 0 → 100644
  1 +package com.ruoyi.system.client;
  2 +
  3 +import io.netty.bootstrap.Bootstrap;
  4 +import io.netty.channel.ChannelFuture;
  5 +import io.netty.channel.ChannelInitializer;
  6 +import io.netty.channel.nio.NioEventLoopGroup;
  7 +import io.netty.channel.socket.SocketChannel;
  8 +import io.netty.channel.socket.nio.NioSocketChannel;
  9 +
  10 +
  11 +public class NettyClientDemoMain {
  12 + public static void main(String[] args) throws Exception {
  13 + NioEventLoopGroup eventExecutors = new NioEventLoopGroup();
  14 + ChannelFuture channelFuture = null;
  15 + try {
  16 + //创建bootstrap对象,配置参数
  17 + Bootstrap bootstrap = new Bootstrap();
  18 + //设置线程组
  19 + bootstrap.group(eventExecutors)
  20 + //设置客户端的通道实现类型
  21 + .channel(NioSocketChannel.class)
  22 + //使用匿名内部类初始化通道
  23 + .handler(new ChannelInitializer<SocketChannel>() {
  24 + @Override
  25 + protected void initChannel(SocketChannel ch) throws Exception {
  26 + //添加客户端通道的处理器
  27 + ch.pipeline().addLast(new ClientHandler());
  28 + }
  29 + });
  30 + System.out.println("客户端准备就绪,随时可以起飞~");
  31 + //连接服务端
  32 + channelFuture = bootstrap.connect("127.0.0.1", 8989).sync();
  33 + channelFuture.channel().closeFuture().sync();
  34 +
  35 + } finally {
  36 + //关闭线程组
  37 + eventExecutors.shutdownGracefully();
  38 + }
  39 + }
  40 +}
  41 +
... ...
Bsth-admin/src/main/java/com/ruoyi/system/controller/DeviceCommandController.java 0 → 100644
  1 +package com.ruoyi.system.controller;
  2 +
  3 +import com.ruoyi.common.global.Result;
  4 +import com.ruoyi.system.domain.active.Sos;
  5 +import com.ruoyi.system.domain.active.UploadGpsTime;
  6 +import com.ruoyi.system.domain.active.WhiteListPeople;
  7 +import com.ruoyi.system.service.IDeviceService;
  8 +import org.springframework.beans.factory.annotation.Autowired;
  9 +import org.springframework.web.bind.annotation.PostMapping;
  10 +import org.springframework.web.bind.annotation.RequestBody;
  11 +import org.springframework.web.bind.annotation.RequestMapping;
  12 +import org.springframework.web.bind.annotation.RestController;
  13 +
  14 +@RestController
  15 +@RequestMapping("/command/send")
  16 +public class DeviceCommandController {
  17 + @Autowired
  18 + private IDeviceService deviceService;
  19 +
  20 + @PostMapping("/settingSos")
  21 + public Result<?> settingSos(@RequestBody Sos sos) {
  22 + return deviceService.settingSos(sos);
  23 + }
  24 +
  25 +
  26 + @PostMapping("/settingWhiteList")
  27 + public Result<?> settingWhiteList(@RequestBody WhiteListPeople wlp) {
  28 + return deviceService.settingWhiteList(wlp);
  29 + }
  30 +
  31 + @PostMapping("/settingGpsUploadTime")
  32 + public Result<?> settingGpsUploadTime(@RequestBody UploadGpsTime ugt) {
  33 + return deviceService.settingGpsUploadTime(ugt);
  34 + }
  35 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/domain/active/ActiveBase.java 0 → 100644
  1 +package com.ruoyi.system.domain.active;
  2 +
  3 +public class ActiveBase {
  4 + private String imei;
  5 +
  6 + public String getImei() {
  7 + return imei;
  8 + }
  9 +
  10 + public void setImei(String imei) {
  11 + this.imei = imei;
  12 + }
  13 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/domain/active/Sos.java 0 → 100644
  1 +package com.ruoyi.system.domain.active;
  2 +
  3 +import lombok.Data;
  4 +
  5 +import java.util.List;
  6 +
  7 +/**
  8 + * sos信息设置
  9 + * @author 20412
  10 + */
  11 +@Data
  12 +public class Sos extends ActiveBase{
  13 + private List<String> calls;
  14 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/domain/active/UploadGpsTime.java 0 → 100644
  1 +package com.ruoyi.system.domain.active;
  2 +
  3 +import lombok.Data;
  4 +
  5 +/**
  6 + * @author 20412
  7 + */
  8 +@Data
  9 +public class UploadGpsTime extends ActiveBase{
  10 + private Integer seconds;
  11 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/domain/active/WhiteListPeople.java 0 → 100644
  1 +package com.ruoyi.system.domain.active;
  2 +
  3 +import lombok.Data;
  4 +
  5 +import java.util.List;
  6 +
  7 +/**
  8 + * 联系人白名单
  9 + * @author 20412
  10 + */
  11 +@Data
  12 +public class WhiteListPeople extends ActiveBase {
  13 + private List<Contact> contacts;
  14 + @Data
  15 + public static class Contact {
  16 + private String name;
  17 + private String tell;
  18 + }
  19 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/domain/command/Bluetooth.java 0 → 100644
  1 +package com.ruoyi.system.domain.command;
  2 +
  3 +import lombok.AllArgsConstructor;
  4 +import lombok.Data;
  5 +import lombok.NoArgsConstructor;
  6 +
  7 +@Data
  8 +@AllArgsConstructor
  9 +@NoArgsConstructor
  10 +public class Bluetooth {
  11 + /** 蓝牙名称 */
  12 + private String name;
  13 + /** mac 地址 */
  14 + private String mac;
  15 + /** 信号强度 */
  16 + private String signalStrong;
  17 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/domain/command/BluetoothInfo.java 0 → 100644
  1 +package com.ruoyi.system.domain.command;
  2 +
  3 +import lombok.Data;
  4 +
  5 +import java.util.List;
  6 +
  7 +@Data
  8 +public class BluetoothInfo {
  9 + private String imei;
  10 + private List<Bluetooth> bluetoothList;
  11 + private String mac;
  12 + private String timestamp;
  13 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/domain/command/DeviceAlarm.java 0 → 100644
  1 +package com.ruoyi.system.domain.command;
  2 +
  3 +import lombok.Data;
  4 +
  5 +import java.util.Date;
  6 +import java.util.List;
  7 +
  8 +/**
  9 + * 设备报警
  10 + * @author 20412
  11 + */
  12 +@Data
  13 +public class DeviceAlarm {
  14 + private String imei;
  15 + private Date createTime;
  16 + private String deviceDate;
  17 + private String gpsLabel;
  18 + /** 地址是否有效 A 还是V */
  19 + private String addressCheck;
  20 + /** ddmm.mmmm维度 */
  21 + private String gpsN;
  22 + /** dddmm.mmmm 经度 */
  23 + private String gpsE;
  24 + private String speed;
  25 + private String deviceTime;
  26 + private String direction;
  27 + private String gms;
  28 + /** 参与定位的卫星数 */
  29 + private String gpsSatelliteCount;
  30 + private String power;
  31 + private String placeholder;
  32 + /** 是否设防 01 设防 00 未设置 */
  33 + private String isSafety;
  34 + /** 是否为工作模式 02 工作模式 */
  35 + private String isWork;
  36 + private String mcc;
  37 + private String mnc;
  38 + private String lac;
  39 + private String cid;
  40 + /** 告警值
  41 + * 01 SOS 02 低电 03 摘除手表
  42 + * 04 预留 05 防拆表带打开 06 跌倒
  43 + * 07 无效的心率值 08 心率过高 09 心率过低
  44 + * 10 舒张压过高 11 舒张压过低 12 收缩压过高
  45 + * 13 收缩压过低 14 久坐 15 预留
  46 + * 16 防拆表带锁上 17 体温过高 18 体温过低
  47 + * 19 关机 20 离开电子围栏 21 进入电子围栏
  48 + * 22 消息已阅读 */
  49 + private String alarmValue;
  50 + /** 设备语言 */
  51 + private String deviceLanguage;
  52 + /** 设备标志位 */
  53 + private String alarmInfoLabel;
  54 + private List<WifiInfo> wifiInfo;
  55 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/domain/command/GpsInfo.java 0 → 100644
  1 +package com.ruoyi.system.domain.command;
  2 +
  3 +import lombok.Data;
  4 +
  5 +import java.util.Date;
  6 +import java.util.List;
  7 +
  8 +/**
  9 + * gps信息
  10 + * @author 20412
  11 + */
  12 +@Data
  13 +public class GpsInfo {
  14 + private String imei;
  15 + private String commandLabel;
  16 + /** 地址是否有效 A 还是V */
  17 + private String addressCheck;
  18 + private Date createTime;
  19 + private String deviceDate;
  20 + private String gpsLabel;
  21 + /** ddmm.mmmm维度 */
  22 + private String gpsN;
  23 + /** dddmm.mmmm 经度 */
  24 + private String gpsE;
  25 + /** 速度 */
  26 + private String speed;
  27 + private String deviceTime;
  28 + private String direction;
  29 + private String gms;
  30 + /** 参与定位的卫星数 */
  31 + private String gpsSatelliteCount;
  32 + private String power;
  33 + private String placeholder;
  34 + /** 是否设防 01 设防 00 未设置 */
  35 + private String isSafety;
  36 + /** 是否为工作模式 02 工作模式 */
  37 + private String isWork;
  38 + private String mcc;
  39 + private String mnc;
  40 + private String lac;
  41 + private String cid;
  42 + /** 网络识别码 */
  43 + private String sid;
  44 + /** 网络识别码 */
  45 + private String nid;
  46 + /** 小区唯一识别码 */
  47 + private String bid;
  48 + /** 信号强度 */
  49 + private Integer signal;
  50 + private List<WifiInfo> wifiInfoList;
  51 +
  52 + private String languageLabel;
  53 + private String replyLabel;
  54 + /** 基站组数 */
  55 + private Integer baseStationGroups;
  56 + /** 基站集合 */
  57 + private List<BaseStation> baseStations;
  58 + /** wifi组数 */
  59 + private Integer wifiGroups;
  60 +
  61 + private String remark;
  62 +
  63 + @Data
  64 + public static class BaseStation {
  65 + private String lac;
  66 + private String cid;
  67 + /** 信号强度 */
  68 + private String dbm;
  69 + }
  70 +
  71 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/domain/command/HealthInfo.java 0 → 100644
  1 +package com.ruoyi.system.domain.command;
  2 +
  3 +import lombok.Data;
  4 +
  5 +import java.util.Date;
  6 +
  7 +/**
  8 + * @author 20412
  9 + */
  10 +@Data
  11 +public class HealthInfo {
  12 + private String imei;
  13 + private String commandLabel;
  14 + private String power;
  15 + private String uploadTime;
  16 + /** 1:健康数据类型,取值1表示血压,取值2表示心率,取值3表示体温,4表示血氧 */
  17 + private String healthType;
  18 + // 691120:健康数值,根据类型不同数据内容有不同。691120表示,舒张压(低压)69,收缩压(高 压)120,中间的1为多个参数分隔符。
  19 + // 当健康数据类型为3时,后面的健康数值是体温,36.19是具体的温度值,单位为摄氏度
  20 + // 若只有一个参数则无需分隔符,例如心率:80,则包为:WAPJK,2021-05-29100:00,2,80#
  21 + private String heartRate;
  22 + /** 高压 */
  23 + private Integer highPressure;
  24 + /** 低压 */
  25 + private Integer lowPressure;
  26 + /** 体温 */
  27 + private Float temperature;
  28 + /** 血氧 */
  29 + private String bloodOxygen;
  30 + /** 血糖 */
  31 + private String bloodSugar;
  32 + /** 手腕温度 */
  33 + private String handTemperature;
  34 + private Date createTime;
  35 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/domain/command/ImageInfo.java 0 → 100644
  1 +package com.ruoyi.system.domain.command;
  2 +
  3 +import lombok.Data;
  4 +
  5 +import java.util.Date;
  6 +
  7 +/**
  8 + * @author 20412
  9 + */
  10 +@Data
  11 +public class ImageInfo {
  12 + private String imei;
  13 + private String imageUrl;
  14 + private String uploadTime;
  15 + private Date createTime;
  16 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/domain/command/KeepLiveInfo.java 0 → 100644
  1 +package com.ruoyi.system.domain.command;
  2 +
  3 +import lombok.Data;
  4 +
  5 +import java.util.Date;
  6 +
  7 +/**
  8 + * 心跳包
  9 + * @author 20412
  10 + */
  11 +@Data
  12 +public class KeepLiveInfo {
  13 + private String imei;
  14 + private String commandLabel;
  15 + private Date createTime;
  16 + private String gsm;
  17 + private String satelliteCount;
  18 + private String power;
  19 + private String placeholder;
  20 + private String isSafety;
  21 + private String isWork;
  22 + /** 计步数 */
  23 + private String step;
  24 + /** 翻滚次数 */
  25 + private String rollingCount;
  26 + /** 定位间隔 */
  27 + private String gpsInterval;
  28 + private String remark;
  29 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/domain/command/PowerAlarm.java 0 → 100644
  1 +package com.ruoyi.system.domain.command;
  2 +
  3 +import lombok.Data;
  4 +
  5 +import java.util.Date;
  6 +
  7 +/**
  8 + * 电源警告
  9 + * @author 20412
  10 + */
  11 +@Data
  12 +public class PowerAlarm {
  13 + private String power;
  14 + private Date createTime;
  15 + private String imei;
  16 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/domain/command/SpeechInfo.java 0 → 100644
  1 +package com.ruoyi.system.domain.command;
  2 +
  3 +import lombok.Data;
  4 +
  5 +import java.util.Date;
  6 +
  7 +/**
  8 + * 语音包
  9 + * @author 20412
  10 + */
  11 +@Data
  12 +public class SpeechInfo {
  13 + private String imei;
  14 + private String speechUrl;
  15 + private String uploadTime;
  16 + private Date createTime;
  17 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/domain/command/WearingState.java 0 → 100644
  1 +package com.ruoyi.system.domain.command;
  2 +
  3 +import lombok.Data;
  4 +
  5 +import java.util.Date;
  6 +
  7 +/**
  8 + * 佩戴状态
  9 + * @author 20412
  10 + */
  11 +@Data
  12 +public class WearingState {
  13 + private String imei;
  14 + private String state;
  15 + private Long timestamp;
  16 + private Date createTime;
  17 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/domain/command/WifiInfo.java 0 → 100644
  1 +package com.ruoyi.system.domain.command;
  2 +
  3 +import lombok.AllArgsConstructor;
  4 +import lombok.Data;
  5 +import lombok.NoArgsConstructor;
  6 +
  7 +@Data
  8 +@AllArgsConstructor
  9 +@NoArgsConstructor
  10 +public class WifiInfo {
  11 + private String ssid;
  12 + private String mac;
  13 + private String signal;
  14 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/domain/entity/Device.java 0 → 100644
  1 +package com.ruoyi.system.domain.entity;
  2 +
  3 +import lombok.Data;
  4 +
  5 +import java.util.Date;
  6 +
  7 +@Data
  8 +public class Device {
  9 + /**
  10 + * 设备号
  11 + */
  12 + private String imei;
  13 +
  14 + private String ip;
  15 +
  16 + private String version;
  17 +
  18 + /**
  19 + * MCC 标识移动通信网络所属国家或地区的代码
  20 + */
  21 + private String mcc;
  22 +
  23 + /**
  24 + * MNC 标识移动网络运营商的代码
  25 + */
  26 + private String mnc;
  27 +
  28 + /**
  29 + * APN 建立移动网络数据连接的配置信息
  30 + */
  31 + private String apn;
  32 +
  33 + //,通过将 IMSI 存储在 SIM 卡中,并将 ICCID 与 IMSI 关联,可以实现对移动网络用户和 SIM 卡的管理和追踪
  34 +
  35 + /**
  36 + * ICCID 用于唯一标识 SIM 卡 可以用于激活、注册、识别和管理 SIM 卡
  37 + */
  38 + private String iccId;
  39 +
  40 + /**
  41 + * IMSI 用于唯一标识移动网络用户
  42 + */
  43 + private String imsi;
  44 +
  45 +
  46 + private Date createTime;
  47 +
  48 + private Date updateTime;
  49 + private String id;
  50 +
  51 + private String remark;
  52 + private Integer state;
  53 +
  54 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/domain/response/BaseStationResponse.java 0 → 100644
  1 +package com.ruoyi.system.domain.response;
  2 +
  3 +import lombok.Data;
  4 +
  5 +import javax.xml.bind.annotation.XmlAccessType;
  6 +import javax.xml.bind.annotation.XmlAccessorType;
  7 +import javax.xml.bind.annotation.XmlElement;
  8 +import javax.xml.bind.annotation.XmlRootElement;
  9 +
  10 +@Data
  11 +@XmlRootElement(name = "response")
  12 +@XmlAccessorType(value = XmlAccessType.FIELD)
  13 +public class BaseStationResponse {
  14 + @XmlElement(name="errcode")
  15 + private String errcode;
  16 + @XmlElement(name="lat")
  17 + private String lat = "00.000000";
  18 + @XmlElement(name="lon")
  19 + private String lon = "000.000000";
  20 + @XmlElement(name="radius")
  21 + private String radius;
  22 + @XmlElement(name="address")
  23 + private String address;
  24 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/domain/response/WeatherResponse.java 0 → 100644
  1 +package com.ruoyi.system.domain.response;
  2 +
  3 +import lombok.Data;
  4 +
  5 +import java.util.List;
  6 +
  7 +@Data
  8 +public class WeatherResponse {
  9 + private String code;
  10 +
  11 + private String updateTime;
  12 +
  13 + private String fxLink;
  14 +
  15 + private List<Daily> daily;
  16 +
  17 + private Refer refer;
  18 + @Data
  19 + public static class Daily
  20 + {
  21 + private String fxDate;
  22 +
  23 + private String sunrise;
  24 +
  25 + private String sunset;
  26 +
  27 + private String moonrise;
  28 +
  29 + private String moonset;
  30 +
  31 + private String moonPhase;
  32 +
  33 + private String moonPhaseIcon;
  34 +
  35 + private String tempMax;
  36 +
  37 + private String tempMin;
  38 +
  39 + private String iconDay;
  40 +
  41 + private String textDay;
  42 +
  43 + private String iconNight;
  44 +
  45 + private String textNight;
  46 +
  47 + private String wind360Day;
  48 +
  49 + private String windDirDay;
  50 +
  51 + private String windScaleDay;
  52 +
  53 + private String windSpeedDay;
  54 +
  55 + private String wind360Night;
  56 +
  57 + private String windDirNight;
  58 +
  59 + private String windScaleNight;
  60 +
  61 + private String windSpeedNight;
  62 +
  63 + private String humidity;
  64 +
  65 + private String precip;
  66 +
  67 + private String pressure;
  68 +
  69 + private String vis;
  70 +
  71 + private String cloud;
  72 +
  73 + private String uvIndex;
  74 + }
  75 +
  76 + @Data
  77 + public static class Refer
  78 + {
  79 + private List<String> sources;
  80 +
  81 + private List<String> license;
  82 + }
  83 +
  84 +
  85 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/handler/DeviceExceptionHandler.java 0 → 100644
  1 +package com.ruoyi.system.handler;
  2 +
  3 +import com.ruoyi.common.global.IDeviceException;
  4 +import com.ruoyi.common.global.Result;
  5 +import org.slf4j.Logger;
  6 +import org.slf4j.LoggerFactory;
  7 +import org.springframework.web.bind.annotation.ExceptionHandler;
  8 +import org.springframework.web.bind.annotation.RestControllerAdvice;
  9 +
  10 +/**
  11 + * @author 20412
  12 + */
  13 +@RestControllerAdvice
  14 +public class DeviceExceptionHandler {
  15 + private static final Logger log = LoggerFactory.getLogger(DeviceExceptionHandler.class);
  16 +
  17 + @ExceptionHandler(IDeviceException.class)
  18 + public Result<?> handlerDeviceException(IDeviceException e) {
  19 + log.error(e.getMsg());
  20 + return Result.ERROR(e.getCode(), e.getMsg());
  21 + }
  22 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/handler/SocketServerHandler.java 0 → 100644
  1 +package com.ruoyi.system.handler;
  2 +
  3 +import com.ruoyi.system.protocol.ThinkraceUtil;
  4 +import com.ruoyi.system.strategy.CommandStrategy;
  5 +import io.netty.buffer.ByteBuf;
  6 +import io.netty.channel.ChannelHandlerContext;
  7 +import io.netty.channel.ChannelInboundHandlerAdapter;
  8 +import io.netty.util.AttributeKey;
  9 +import io.netty.util.CharsetUtil;
  10 +import org.slf4j.Logger;
  11 +import org.slf4j.LoggerFactory;
  12 +
  13 +public class SocketServerHandler extends ChannelInboundHandlerAdapter {
  14 + CommandStrategy strategy;
  15 + private static final Logger log = LoggerFactory.getLogger(SocketServerHandler.class);
  16 +
  17 + public SocketServerHandler() {
  18 + this.strategy = new CommandStrategy();
  19 + }
  20 +
  21 +
  22 + @Override
  23 + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
  24 + ByteBuf buf = (ByteBuf) msg;
  25 + // 处理指令
  26 + strategy.handler(buf.toString(CharsetUtil.UTF_8), ctx);
  27 + }
  28 +
  29 + @Override
  30 + public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
  31 +// System.out.println(ctx.channel().attr(AttributeKey.valueOf(ThinkraceUtil.IMEI)).get());
  32 + super.channelReadComplete(ctx);
  33 + }
  34 +
  35 +
  36 + @Override
  37 + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
  38 + String imei = AttributeKey.valueOf(ThinkraceUtil.IMEI).toString();
  39 + // 发生异常触发
  40 + ThinkraceUtil.removeDevice(imei);
  41 + log.info("发生未知错误,设备:{}将关闭连接,原因:{}", imei, cause.getMessage());
  42 + ctx.close();
  43 + }
  44 +
  45 +
  46 + @Override
  47 + public void channelActive(ChannelHandlerContext ctx) throws Exception {
  48 +// ctx.channel().close();
  49 + super.channelActive(ctx);
  50 + }
  51 +
  52 + @Override
  53 + public void channelInactive(ChannelHandlerContext ctx) throws Exception {
  54 + String imei = ctx.channel().attr(AttributeKey.valueOf(ThinkraceUtil.IMEI)).toString();
  55 + log.info("设备断开连接了,设备号为:{}", imei);
  56 + }
  57 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/mapper/DeviceMapper.java 0 → 100644
  1 +package com.ruoyi.system.mapper;
  2 +
  3 +import com.baomidou.mybatisplus.core.mapper.BaseMapper;
  4 +import com.ruoyi.system.domain.entity.Device;
  5 +
  6 +/**
  7 + * deviceMapper接口
  8 + *
  9 + * @author bsth
  10 + * @date 2024-04-17
  11 + */
  12 +public interface DeviceMapper extends BaseMapper<Device>
  13 +{
  14 +
  15 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/mapper/system/DeviceMapper.xml 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8" ?>
  2 +<!DOCTYPE mapper
  3 +PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  4 +"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  5 +<mapper namespace="com.ruoyi.system.mapper.DeviceMapper">
  6 +
  7 + <resultMap type="Device" id="DeviceResult">
  8 + <result property="id" column="id" />
  9 + <result property="imei" column="imei" />
  10 + <result property="ip" column="ip" />
  11 + <result property="version" column="version" />
  12 + <result property="mcc" column="mcc" />
  13 + <result property="mnc" column="mnc" />
  14 + <result property="apn" column="apn" />
  15 + <result property="iccId" column="iccId" />
  16 + <result property="imsi" column="imsi" />
  17 + <result property="createTime" column="create_time" />
  18 + <result property="updateTime" column="update_time" />
  19 + <result property="remark" column="remark" />
  20 + <result property="state" column="state" />
  21 + </resultMap>
  22 +
  23 +</mapper>
0 24 \ No newline at end of file
... ...
Bsth-admin/src/main/java/com/ruoyi/system/protocol/ThinkraceUtil.java 0 → 100644
  1 +package com.ruoyi.system.protocol;
  2 +
  3 +import com.ruoyi.common.core.redis.RedisCache;
  4 +import com.ruoyi.common.global.IDeviceException;
  5 +import com.ruoyi.common.global.ResultCode;
  6 +import com.ruoyi.system.protocol.adapter.ICommandAdapter;
  7 +import io.netty.buffer.Unpooled;
  8 +import io.netty.channel.Channel;
  9 +import io.netty.channel.ChannelHandlerContext;
  10 +import io.netty.util.AttributeKey;
  11 +import io.netty.util.CharsetUtil;
  12 +import org.slf4j.Logger;
  13 +import org.slf4j.LoggerFactory;
  14 +
  15 +import java.util.HashMap;
  16 +import java.util.Map;
  17 +import java.util.Objects;
  18 +import java.util.concurrent.ConcurrentHashMap;
  19 +import java.util.concurrent.TimeUnit;
  20 +
  21 +public class ThinkraceUtil {
  22 + // 命令过期时间
  23 + private static final Integer COMMAND_TTL = 300;
  24 +
  25 + private static final String COMMAND_SN = "command:sn";
  26 + private static final String COMMAND_SEND = "command:send:";
  27 + public static final Integer COMMAND_LABEL_SURE = 1;
  28 + public static final Integer COMMAND_LABEL_INIT = 0;
  29 + private static final Long SN_LENGTH = 1000000L;
  30 + private static RedisCache REDIS_CACHE;
  31 + /**
  32 + * 指令列表
  33 + */
  34 + private static final Map<String, ICommandAdapter> REGEX_MAP = new HashMap<>();
  35 +
  36 + /**
  37 + * 时区
  38 + */
  39 + public static String UTC_8 = "8";
  40 + /**
  41 + * 命令结束符
  42 + */
  43 + public static String END_SYMBOL = "#";
  44 + /**
  45 + * 设备号
  46 + */
  47 + public static String IMEI = "IMEI";
  48 +
  49 + public static String generatorSn() {
  50 + Long commandSn;
  51 + // 防止出现线程安全 重复覆盖值的情况
  52 + synchronized (ThinkraceUtil.class) {
  53 + // 指令流水号是6位的 不足补0
  54 + commandSn = REDIS_CACHE.increment(COMMAND_SN);
  55 + if (commandSn == null || commandSn > SN_LENGTH) {
  56 + commandSn = 0L;
  57 + REDIS_CACHE.setCacheObject(COMMAND_SN, commandSn.intValue());
  58 + }
  59 + }
  60 + String commandSnStr = commandSn.toString();
  61 + int snLen = (SN_LENGTH.toString().length() - 1) - commandSnStr.length();
  62 + StringBuilder sb = new StringBuilder();
  63 + //长度不足6位补零
  64 + for (int i = 0; i < snLen; i++) {
  65 + sb.append("0");
  66 + }
  67 + return sb + commandSnStr;
  68 + }
  69 +
  70 + public static String commandLabelInit(String imei, String sn) {
  71 + // 存入redis处理会有网络io的消耗 所以redis和服务器最好是在同一台服务器 原则是30s 增加容错
  72 + String key = getCommandKey(imei, sn);
  73 + REDIS_CACHE.expire(key, COMMAND_LABEL_INIT, COMMAND_TTL, TimeUnit.SECONDS);
  74 + return key;
  75 + }
  76 +
  77 + private static String getCommandKey(String imei, String sn) {
  78 + return COMMAND_SEND + imei + ":" + sn;
  79 + }
  80 +
  81 + public static Integer getCommandSn(String key) {
  82 + return REDIS_CACHE.getCacheObject(key);
  83 + }
  84 +
  85 +
  86 + public static class REQ {
  87 + /**
  88 + * 登陆包
  89 + */
  90 + public static final String LOGIN_REQ = "IWAP00";
  91 + public static final String GPS_REQ = "IWAP01";
  92 + public static final String MORE_GPS_REQ = "IWAP02";
  93 + public static final String KEEPLIVE_REQ = "IWAP03";
  94 + /**
  95 + * 电量报警
  96 + */
  97 + public static final String POWER_REQ = "IWAP04";
  98 + /**
  99 + * 这个可能在文档内是被划线了的可能没有启用过 语音传输没有在设备找到按钮
  100 + */
  101 + @Deprecated
  102 + public static final String SPEECH_REQ = "IWAP05";
  103 + /**
  104 + * 语音上行
  105 + */
  106 + public static final String SPEECH_UP_REQ = "IWAP07";
  107 + /**
  108 + * 设备报警与地址回复包
  109 + */
  110 + public static final String ALARM_REQ = "IWAP10";
  111 + public static final String IMAGE_REQ = "IWAP42";
  112 + public static final String HEART_REQ = "IWAP49";
  113 + public static final String TEMPERATURE_50_REQ = "IWAP50";
  114 + public static final String QUERY_BLUETOOTH_REQ = "IWAP51";
  115 + public static final String CDMA_GPS_REQ = "IWAP91";
  116 + public static final String CDMA_WIFI_GPS_REQ = "IWAP92";
  117 + public static final String BLUETOOTH_REQ = "IWAPBL";
  118 + public static final String HEART_BLOOD_REQ = "IWAPHT";
  119 + public static final String HEALTH_REQ = "IWAPJK";
  120 + public static final String TEMPERATURE_REQ = "IWAPTP";
  121 + public static final String VERSION_REQ = "IWAPVR";
  122 + public static final String WEARING_REQ = "IWAPWR";
  123 + /**
  124 + * 12 上传心率、血压、血氧、血糖
  125 + */
  126 + public static final String HEART_RATE_BLOOD_PRESSURE_OXYGEN_SUGAR_REQ = "IWAPHP";
  127 + public static final String BASE_STATION_TIMING_GPS_REQ = "IWAPTM";
  128 + /**
  129 + * 14 同步天气
  130 + */
  131 + public static final String WEATHER_REQ = "IWAPWT";
  132 + /**
  133 + * 确认已读文字
  134 + */
  135 + public static final String SURE_TEXT_REQ = "IWAP94";
  136 + public static final String ECG_DATA_REQ = "IWAPHD";
  137 + /**
  138 + * 设备响应
  139 + */
  140 + public static final String CONTACT_REQ = "IWAP14";
  141 + public static final String SOS_REQ = "IWAP12";
  142 +
  143 +
  144 + public static final String UPLOAD_GPS_TIME_REQ = "IWAP15";
  145 +
  146 + }
  147 +
  148 + public static class RES {
  149 + /**
  150 + * 登陆包
  151 + */
  152 + public static final String LOGIN_RES = "IWBP00";
  153 + /**
  154 + * 定位
  155 + */
  156 + public static final String GPS_RES = "IWBP01";
  157 + /**
  158 + * 多基站GPS定位
  159 + */
  160 + public static final String MORE_GPS_RES = "IWBP02";
  161 + /**
  162 + * 心跳
  163 + */
  164 + public static final String KEEPLIVE_RES = "IWBP03";
  165 + public static final String HEART_RES = "IWBP49";
  166 + public static final String TEMPERATURE_50_RES = "IWBP50";
  167 + public static final String QUERY_BLUETOOTH_RES = "IWBP51";
  168 + public static final String CDMA_GPS_RES = "IWBP91";
  169 +
  170 + public static final String CDMA_WIFI_GPS_RES = "IWBP92";
  171 + /**
  172 + * 电源报警
  173 + */
  174 + public static final String POWER_RES = "IWBP04";
  175 + /**
  176 + * 设备报警与地址回复包
  177 + */
  178 + public static final String ALARM_RES = "IWBP10";
  179 + public static final String IMAGE_RES = "IWBP42";
  180 + public static final String BLUETOOTH_RES = "IWBPBL";
  181 + public static final String HEART_BLOOD_RES = "IWBPHT";
  182 + public static final String HEALTH_RES = "IWBPJK";
  183 + public static final String TEMPERATURE_RES = "IWBPTP";
  184 + public static final String HEART_RATE_BLOOD_PRESSURE_OXYGEN_SUGAR_RES = "IWBPHP";
  185 + /**
  186 + * 基站校时并获取经纬度协议
  187 + */
  188 + public static final String BASE_STATION_TIMING_GPS_RES = "IWBPTM";
  189 + ;
  190 + /**
  191 + * 确认已读文字
  192 + */
  193 + public static final String SURE_TEXT_RES = "IWBP94";
  194 + public static final String ECG_DATA_RES = "IWBPHD";
  195 + @Deprecated
  196 + public static final String SPEECH_RES = "IWBP05";
  197 + public static final String SPEECH_UP_RES = "IWBP07";
  198 + public static final String WEATHER_RES = "IWBPWT";
  199 + }
  200 +
  201 + /**
  202 + * 下发指令集
  203 + */
  204 + public static class ACTIVE {
  205 + public static final String CONTACT_ACTIVE = "IWBP14";
  206 + public static final String UPLOAD_GPS_TIME_ACTIVE = "IWBP15";
  207 + public static final String SOS_ACTIVE = "IWBP12";
  208 + }
  209 +
  210 +
  211 + public static ICommandAdapter getAdapter(String key) {
  212 + return REGEX_MAP.get(key);
  213 + }
  214 +
  215 + public static void addAdapter(String key, ICommandAdapter adapter) {
  216 + REGEX_MAP.put(key, adapter);
  217 + }
  218 +
  219 + public static void removeDevice(String imei) {
  220 + DeviceManager.removeDevice(imei);
  221 + }
  222 +
  223 + public static boolean hasDevice(Channel channel) {
  224 + return DeviceManager.hasDevice(channel);
  225 + }
  226 +
  227 + public static void sendCommand(String imei, String command) {
  228 + DeviceManager.sendCommand(imei, command);
  229 + }
  230 +
  231 + public static boolean askCommand(String txt, String imei, String command, String commandSn, ICommandAdapter adapter) {
  232 + return DeviceManager.askCommand(txt, imei, command, commandSn, adapter);
  233 + }
  234 +
  235 + public static void setDevice(String imei, ChannelHandlerContext ctx) {
  236 + DeviceManager.onlineDevice(imei, ctx);
  237 + }
  238 +
  239 +
  240 + /**
  241 + * 管理设备
  242 + *
  243 + * @author 20412
  244 + */
  245 + protected static class DeviceManager {
  246 +
  247 + private static final Logger log = LoggerFactory.getLogger(DeviceManager.class);
  248 + private static final ConcurrentHashMap<String, Channel> DEVICE_MAP = new ConcurrentHashMap<>();
  249 +
  250 + /**
  251 + * 设备上线
  252 + *
  253 + * @param imei 设备号
  254 + * @param ctx 当前连接的处理器上下文
  255 + */
  256 + public static void onlineDevice(String imei, ChannelHandlerContext ctx) {
  257 + if (DEVICE_MAP.containsKey(imei)) {
  258 + // 关闭之前的连接
  259 + Channel channel = DEVICE_MAP.get(imei);
  260 + channel.close();
  261 + }
  262 + // 为当前连接设置id
  263 + Channel channel = ctx.channel();
  264 + channel.attr(AttributeKey.valueOf(ThinkraceUtil.IMEI)).set(imei);
  265 + // 继续传递事件给下一个 ChannelHandler
  266 + ctx.fireChannelActive();
  267 + init(imei, channel);
  268 + log.info("设备上线:设备号为:{}", imei);
  269 + }
  270 +
  271 + /**
  272 + * 移除设备 以及资源管理器
  273 + *
  274 + * @param imei 设备号
  275 + */
  276 + public static void removeDevice(String imei) {
  277 + if (DEVICE_MAP.containsKey(imei)) {
  278 + clean(imei);
  279 + log.info("设备离线,设备号为:{}", imei);
  280 + }
  281 + }
  282 +
  283 + /**
  284 + * 向设备发送指令
  285 + *
  286 + * @param imei 设备号
  287 + * @param command 指令
  288 + * @return obj 处理结果
  289 + */
  290 + public static void sendCommand(String imei, String command) {
  291 + log.info("服务器发起:{}", command);
  292 + Channel channel = DEVICE_MAP.get(imei);
  293 + if (Objects.isNull(channel)) {
  294 + throw new IDeviceException(ResultCode.CODE_207, "设备" + imei + "不在线");
  295 + }
  296 + channel.writeAndFlush(Unpooled.copiedBuffer(command, CharsetUtil.UTF_8));
  297 + }
  298 +
  299 + /**
  300 + * 判断信道内是否有设备注册
  301 + *
  302 + * @param channel 通道
  303 + * @return result 如果存在返回 --true 如果不存在返回 --false
  304 + */
  305 + public static boolean hasDevice(Channel channel) {
  306 + AttributeKey<String> key = AttributeKey.valueOf(ThinkraceUtil.IMEI);
  307 + //netty移除了这个map的remove方法,这里的判断谨慎一点
  308 + return (channel.hasAttr(key) || channel.attr(key).get() != null);
  309 + }
  310 +
  311 +
  312 + // 设备上线 创建资源管理器
  313 + private static void init(String imei, Channel channel) {
  314 + // 设备上线
  315 + DEVICE_MAP.put(imei, channel);
  316 + }
  317 +
  318 + // 离线删除创建的资源管理器
  319 + private static void clean(String imei) {
  320 + DEVICE_MAP.remove(imei);
  321 + }
  322 +
  323 + /**
  324 + * 指令响应
  325 + *
  326 + * @param imei 设备号 可以为 -- null
  327 + * @param command 指令
  328 + * @param commandSn 指令流水号
  329 + * @param adapter 适配器 -- 调用适配器的更新方法
  330 + * @return obj 处理结果
  331 + */
  332 + public static boolean askCommand(String txt, String imei, String command, String commandSn, ICommandAdapter adapter) {
  333 + log.info("{}:设备:{},命令流水号:{},响应命令:{}", txt, imei, commandSn, command);
  334 + // 通过事件通知 另外单独一个类通知
  335 + String key = getCommandKey(imei, commandSn);
  336 + REDIS_CACHE.expire(key, COMMAND_LABEL_SURE, COMMAND_TTL, TimeUnit.SECONDS);
  337 + return true;
  338 + }
  339 + }
  340 +
  341 +
  342 + public static void initRedisThinkraceCommandParam(RedisCache redisCache) {
  343 + REDIS_CACHE = redisCache;
  344 + REDIS_CACHE.setNx(COMMAND_SN, 0);
  345 + }
  346 +
  347 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/protocol/adapter/ICommandAdapter.java 0 → 100644
  1 +package com.ruoyi.system.protocol.adapter;
  2 +
  3 +import com.ruoyi.system.protocol.ThinkraceUtil;
  4 +import com.ruoyi.system.protocol.adapter.device.ILoginPacketAdapter;
  5 +import io.netty.channel.ChannelHandlerContext;
  6 +import org.slf4j.Logger;
  7 +import org.slf4j.LoggerFactory;
  8 +
  9 +/**
  10 + * 命令适配器基类
  11 + *
  12 + * @author 20412
  13 + */
  14 +public abstract class ICommandAdapter {
  15 + protected static final Logger log = LoggerFactory.getLogger(ICommandAdapter.class);
  16 +
  17 +
  18 + /**
  19 + * 指令解析
  20 + *
  21 + * @param command 指令
  22 + * @param ctx netty处理器上下文
  23 + * @return obj 处理结果
  24 + */
  25 + public abstract Object resolveCommand(String command, ChannelHandlerContext ctx);
  26 +
  27 + /**
  28 + * 指令下发
  29 + *
  30 + * @return obj 处理结果
  31 + */
  32 + public abstract CommandListen sendCommand(Object params);
  33 +
  34 +
  35 + /**
  36 + * 生成命令序列号
  37 + *
  38 + * @return commandSn
  39 + */
  40 + protected String generatorSn() {
  41 + return ThinkraceUtil.generatorSn();
  42 + }
  43 +
  44 + /**
  45 + * 设置命令
  46 + *
  47 + * @param sn 命令流水号
  48 + * @param imei 设备号
  49 + * @return
  50 + */
  51 + private CommandListen syncCommandSnAsk(String imei, String sn) {
  52 + String key = ThinkraceUtil.commandLabelInit(imei, sn);
  53 + return new CommandListenImpl(key);
  54 + }
  55 +
  56 + protected CommandListen syncSendCommand(String imei, String sn, String command) {
  57 + ThinkraceUtil.sendCommand(imei, command);
  58 + return syncCommandSnAsk(imei, sn);
  59 + }
  60 +
  61 +
  62 + public interface CommandListen {
  63 + /**
  64 + * 监听应答
  65 + *
  66 + * @return 应答结果
  67 + */
  68 + boolean listenAsk();
  69 + }
  70 +
  71 + protected class CommandListenImpl implements CommandListen {
  72 + private String key;
  73 +
  74 + protected CommandListenImpl(String key) {
  75 + this.key = key;
  76 + }
  77 +
  78 + @Override
  79 + public boolean listenAsk() {
  80 + // 循环读取
  81 + try {
  82 + Thread.sleep(100);
  83 + Integer val = ThinkraceUtil.getCommandSn(this.key);
  84 + while (val != null) {
  85 + if (!val.equals(ThinkraceUtil.COMMAND_LABEL_SURE)) {
  86 + return true;
  87 + }
  88 + Thread.sleep(100);
  89 + val = ThinkraceUtil.getCommandSn(this.key);
  90 + }
  91 + return false;
  92 + } catch (Exception e) {
  93 + System.out.println("等待命令响应出现异常:" + e.getMessage());
  94 + return false;
  95 + }
  96 + }
  97 + }
  98 +
  99 + protected void responseLog(String imei, String response) {
  100 + log.info("设备:{},命令解析完毕,返回响应:{}", imei, response);
  101 + }
  102 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/protocol/adapter/active/IActiveContactPeopleWhitePacketAdapter.java 0 → 100644
  1 +package com.ruoyi.system.protocol.adapter.active;
  2 +
  3 +import com.ruoyi.system.domain.active.WhiteListPeople;
  4 +import com.ruoyi.system.protocol.ThinkraceUtil;
  5 +import com.ruoyi.system.protocol.adapter.ICommandAdapter;
  6 +import com.ruoyi.utils.IUnicodeUtils;
  7 +import io.netty.channel.ChannelHandlerContext;
  8 +import org.springframework.stereotype.Service;
  9 +
  10 +/**
  11 + * @author 20412
  12 + * 白名单
  13 + */
  14 +@Service(ThinkraceUtil.ACTIVE.CONTACT_ACTIVE)
  15 +public class IActiveContactPeopleWhitePacketAdapter extends ICommandAdapter {
  16 +
  17 + @Override
  18 + public Object resolveCommand(String command, ChannelHandlerContext ctx) {
  19 + return null;
  20 + }
  21 +
  22 + @Override
  23 + public CommandListen sendCommand(Object params) {
  24 + // 设置10个联系人白名单
  25 + // IWBP14,IMEI,指令流水号,联系人1名称|联系人1电话,联系人2名称|联系人2电话,联系人3名称|联系人3电话,联系人4名称|联系人4电话,联系人5名称|联系人5电话,联系人6名称|联系人6电话,联系人7名称|联系人7电话,联系人8名称|联系人8电话,联系人9名称|联系人9电话,联系人10名称|联系人10电话#
  26 + WhiteListPeople wlp = (WhiteListPeople) params;
  27 + String commandSn = generatorSn();
  28 + StringBuilder command = new StringBuilder();
  29 + command.append(ThinkraceUtil.ACTIVE.CONTACT_ACTIVE).append(",");
  30 + command.append(wlp.getImei()).append(",");
  31 + command.append(commandSn).append(",");
  32 + for (WhiteListPeople.Contact contact : wlp.getContacts()) {
  33 + command.append(IUnicodeUtils.toUnicode(contact.getName())).append("|").append(contact.getTell()).append(",");
  34 + }
  35 + command.deleteCharAt(command.length() - 1).append(ThinkraceUtil.END_SYMBOL);
  36 + // 发送指令
  37 + return syncSendCommand(wlp.getImei(), commandSn, command.toString());
  38 + }
  39 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/protocol/adapter/active/IActiveSettingSosPacketAdapter.java 0 → 100644
  1 +package com.ruoyi.system.protocol.adapter.active;
  2 +
  3 +import com.ruoyi.system.domain.active.Sos;
  4 +import com.ruoyi.system.protocol.ThinkraceUtil;
  5 +import com.ruoyi.system.protocol.adapter.ICommandAdapter;
  6 +import io.netty.channel.ChannelHandlerContext;
  7 +import org.springframework.stereotype.Service;
  8 +
  9 +import java.util.ArrayList;
  10 +import java.util.List;
  11 +import java.util.stream.Collectors;
  12 +
  13 +
  14 +/**
  15 + * 下发指令 设置sos联系方式
  16 + *
  17 + * @author 20412
  18 + */
  19 +@Service(ThinkraceUtil.ACTIVE.SOS_ACTIVE)
  20 +public class IActiveSettingSosPacketAdapter extends ICommandAdapter {
  21 +
  22 +
  23 + @Override
  24 + public Object resolveCommand(String command, ChannelHandlerContext ctx) {
  25 + return null;
  26 + }
  27 +
  28 + @Override
  29 + public CommandListen sendCommand(Object params) {
  30 + // 格式:IWBP12,IMEI,指令流水号,SOS1,SOS2,SOS3#
  31 + // 示例:IWBP12,353456789012345,080835,1XXXXXXXX,2XXXXXXXX,3XXXXXXXX#
  32 + Sos sos = (Sos) params;
  33 + List<String> commands = new ArrayList<>();
  34 + commands.add(ThinkraceUtil.ACTIVE.SOS_ACTIVE);
  35 + commands.add(sos.getImei());
  36 + commands.add(generatorSn());
  37 + commands.addAll(sos.getCalls());
  38 + String command = commands.stream().collect(Collectors.joining(",")) + ThinkraceUtil.END_SYMBOL;
  39 + return syncSendCommand(sos.getImei(), commands.get(2), command);
  40 + }
  41 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/protocol/adapter/active/IActiveUploadGpsTimePacketAdapter.java 0 → 100644
  1 +package com.ruoyi.system.protocol.adapter.active;
  2 +
  3 +import com.ruoyi.system.domain.active.UploadGpsTime;
  4 +import com.ruoyi.system.protocol.ThinkraceUtil;
  5 +import com.ruoyi.system.protocol.adapter.ICommandAdapter;
  6 +import io.netty.channel.ChannelHandlerContext;
  7 +import org.springframework.stereotype.Service;
  8 +
  9 +
  10 +/**
  11 + * 下发指令 设置sos联系方式
  12 + *
  13 + * @author 20412
  14 + */
  15 +@Service(ThinkraceUtil.ACTIVE.UPLOAD_GPS_TIME_ACTIVE)
  16 +public class IActiveUploadGpsTimePacketAdapter extends ICommandAdapter {
  17 +
  18 +
  19 + @Override
  20 + public Object resolveCommand(String command, ChannelHandlerContext ctx) {
  21 + return null;
  22 + }
  23 +
  24 + @Override
  25 + public CommandListen sendCommand(Object params) {
  26 + // 格式:IWBP15,IMEI,指令流水号,定位间隔#
  27 + // 示例:IWBP15,353456789012345,080835,300#
  28 + UploadGpsTime ugt = (UploadGpsTime) params;
  29 + String sn = generatorSn();
  30 + StringBuilder command = new StringBuilder();
  31 + command.append(ThinkraceUtil.ACTIVE.UPLOAD_GPS_TIME_ACTIVE).append(",");
  32 + command.append(ugt.getImei()).append(",");
  33 + command.append(sn).append(",");
  34 + command.append(ugt.getSeconds());
  35 + command.append(ThinkraceUtil.END_SYMBOL);
  36 + return syncSendCommand(ugt.getImei(), sn, command.toString());
  37 + }
  38 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/protocol/adapter/callback/ICallbackContactPeopleWhitePacketAdapter.java 0 → 100644
  1 +package com.ruoyi.system.protocol.adapter.callback;
  2 +
  3 +import com.ruoyi.system.protocol.ThinkraceUtil;
  4 +import com.ruoyi.system.protocol.adapter.ICommandAdapter;
  5 +import io.netty.channel.ChannelHandlerContext;
  6 +import io.netty.util.AttributeKey;
  7 +import org.springframework.stereotype.Service;
  8 +
  9 +/**
  10 + * @author 20412
  11 + * 白名单
  12 + */
  13 +@Service(ThinkraceUtil.REQ.CONTACT_REQ)
  14 +public class ICallbackContactPeopleWhitePacketAdapter extends ICommandAdapter {
  15 +
  16 + @Override
  17 + public Object resolveCommand(String command, ChannelHandlerContext ctx) {
  18 + // IWAP14,080835, D3590D54|135xxxxxxxxxx,
  19 + String imei = ctx.channel().attr(AttributeKey.valueOf(ThinkraceUtil.IMEI)).toString();
  20 + String[] params = command.split(",");
  21 + return ThinkraceUtil.askCommand("设置联系人白名单", imei, command, params[1], this);
  22 + }
  23 +
  24 + @Override
  25 + public CommandListen sendCommand(Object params) {
  26 + return null;
  27 + }
  28 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/protocol/adapter/callback/ICallbackSettingSosPacketAdapter.java 0 → 100644
  1 +package com.ruoyi.system.protocol.adapter.callback;
  2 +
  3 +import com.ruoyi.system.protocol.ThinkraceUtil;
  4 +import com.ruoyi.system.protocol.adapter.ICommandAdapter;
  5 +import io.netty.channel.ChannelHandlerContext;
  6 +import io.netty.util.AttributeKey;
  7 +import org.springframework.stereotype.Service;
  8 +
  9 +/**
  10 + * @author 20412
  11 + */
  12 +@Service(ThinkraceUtil.REQ.SOS_REQ)
  13 +public class ICallbackSettingSosPacketAdapter extends ICommandAdapter {
  14 +
  15 + @Override
  16 + public Object resolveCommand(String command, ChannelHandlerContext ctx) {
  17 + // IWAP12,指令流水号,SOS1,SOS2, SOS3#
  18 + // IWAP12,080835, 1XXXXXXXX,2XXXXXXXX,3XXXXXXXX #
  19 + // 080835: 终端响应的时候带上响应流水号
  20 + // 1XXXXXXXX,2XXXXXXXX,3XXXXXXXX
  21 + String[] params = command.split(",");
  22 + String imei = ctx.channel().attr(AttributeKey.valueOf(ThinkraceUtil.IMEI)).toString();
  23 + // 命令确认应答
  24 + return ThinkraceUtil.askCommand("设置sos命令响应",imei, command, params[1], this);
  25 + }
  26 +
  27 + @Override
  28 + public CommandListen sendCommand(Object params) {
  29 + return null;
  30 + }
  31 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/protocol/adapter/callback/ICallbackUploadGpsTimePacketAdapter.java 0 → 100644
  1 +package com.ruoyi.system.protocol.adapter.callback;
  2 +
  3 +import com.ruoyi.system.protocol.ThinkraceUtil;
  4 +import com.ruoyi.system.protocol.adapter.ICommandAdapter;
  5 +import io.netty.channel.ChannelHandlerContext;
  6 +import io.netty.util.AttributeKey;
  7 +import org.springframework.stereotype.Service;
  8 +
  9 +/**
  10 + * @author 20412
  11 + */
  12 +@Service(ThinkraceUtil.REQ.UPLOAD_GPS_TIME_REQ)
  13 +public class ICallbackUploadGpsTimePacketAdapter extends ICommandAdapter {
  14 +
  15 + @Override
  16 + public Object resolveCommand(String command, ChannelHandlerContext ctx) {
  17 + //IWAP15,指令流水号,定位间隔#
  18 + String[] params = command.split(",");
  19 + String imei = ctx.channel().attr(AttributeKey.valueOf(ThinkraceUtil.IMEI)).toString();
  20 + // 命令确认应答
  21 + return ThinkraceUtil.askCommand("GPS定位数据上传时间间隔命令响应", imei, command, params[1], this);
  22 + }
  23 +
  24 + @Override
  25 + public CommandListen sendCommand(Object params) {
  26 + return null;
  27 + }
  28 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/protocol/adapter/device/IBaseStationTimeGpsPacketAdapter.java 0 → 100644
  1 +package com.ruoyi.system.protocol.adapter.device;
  2 +
  3 +import com.ruoyi.system.domain.response.BaseStationResponse;
  4 +import com.ruoyi.system.protocol.ThinkraceUtil;
  5 +import com.ruoyi.system.protocol.adapter.ICommandAdapter;
  6 +import com.ruoyi.utils.ConstDateUtil;
  7 +import io.netty.buffer.Unpooled;
  8 +import io.netty.channel.ChannelHandlerContext;
  9 +import io.netty.util.AttributeKey;
  10 +import io.netty.util.CharsetUtil;
  11 +import org.springframework.http.HttpMethod;
  12 +import org.springframework.http.ResponseEntity;
  13 +import org.springframework.stereotype.Service;
  14 +import org.springframework.web.client.RestTemplate;
  15 +import javax.xml.bind.JAXBContext;
  16 +import javax.xml.bind.Unmarshaller;
  17 +import java.io.StringReader;
  18 +import java.time.ZoneOffset;
  19 +
  20 +/**
  21 + * 13 通过基站校时并获取经纬度命令
  22 + *
  23 + * @author 20412
  24 + */
  25 +@Service(ThinkraceUtil.REQ.BASE_STATION_TIMING_GPS_REQ)
  26 +public class IBaseStationTimeGpsPacketAdapter extends ICommandAdapter {
  27 +
  28 + @Override
  29 + public Object resolveCommand(String command, ChannelHandlerContext ctx) {
  30 + // IWAPTM,460,0,9750,3613#
  31 + String[] params = command.substring(0, command.length() - 1).split(",");
  32 + String imei = ctx.channel().attr(AttributeKey.valueOf(ThinkraceUtil.IMEI)).toString();
  33 + log.info("设备:{},通过基站校时并获取经纬度命令:{}", imei, command);
  34 + // IWBPTM,20160225090909,22.123456,113.654321#
  35 + //返回时间,格式为:yyyyMMddHHmmss ,为UTC0 时间
  36 + String mcc = params[1];
  37 + String mnc = params[2];
  38 + String lac = params[3];
  39 + String cid = params[4];
  40 + BaseStationResponse obs = getLocation(mcc, mnc, lac, cid);
  41 + // 这个坐标解析
  42 + String response = ThinkraceUtil.RES.BASE_STATION_TIMING_GPS_RES + "," + ConstDateUtil.formatDate("yyyyMMddHHmmss", ZoneOffset.UTC)
  43 + + "," + obs.getLat() + "," + obs.getLon() + ThinkraceUtil.END_SYMBOL;
  44 + ctx.channel().writeAndFlush(Unpooled.copiedBuffer(response, CharsetUtil.UTF_8));
  45 + responseLog(imei, response);
  46 + return null;
  47 + }
  48 +
  49 + private BaseStationResponse getLocation(String mcc, String mnc, String lac, String cid) {
  50 + BaseStationResponse result = null;
  51 + try {
  52 + StringBuilder sb = new StringBuilder();
  53 + sb.append("http://api.cellocation.com:84/cell/?");
  54 + sb.append("mnc=").append(mnc);
  55 + sb.append("&mcc=").append(mcc);
  56 + sb.append("&lac=").append(lac);
  57 + sb.append("&ci=").append(cid);
  58 + sb.append("&output=xml");
  59 + RestTemplate restTemplate = new RestTemplate();
  60 + ResponseEntity<String> response = restTemplate.exchange(sb.toString(), HttpMethod.GET, null, String.class);
  61 + String xmlData = response.getBody();
  62 + // 创建JAXB上下文对象,并指定要绑定的根类
  63 + JAXBContext jaxbContext = JAXBContext.newInstance(BaseStationResponse.class);
  64 + // 创建Unmarshaller对象,用于将XML数据转换为Java对象
  65 + Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
  66 + // 调用unmarshal方法将XML字符串转换为Java对象
  67 + result = (BaseStationResponse) unmarshaller.unmarshal(new StringReader(xmlData));
  68 + } catch (Exception e) {
  69 + log.error("根据基站获取信息api调用异常:{}", e.getMessage());
  70 + //若没取到则返回 0 TODO
  71 + return new BaseStationResponse();
  72 + }
  73 + return result;
  74 +
  75 + }
  76 +
  77 + @Override
  78 + public CommandListen sendCommand(Object params) {
  79 + return null;
  80 + }
  81 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/protocol/adapter/device/ICdmaGpsPacketAdapter.java 0 → 100644
  1 +package com.ruoyi.system.protocol.adapter.device;
  2 +
  3 +import com.ruoyi.system.domain.command.GpsInfo;
  4 +import com.ruoyi.system.domain.command.WifiInfo;
  5 +import com.ruoyi.system.protocol.ThinkraceUtil;
  6 +import com.ruoyi.system.protocol.adapter.ICommandAdapter;
  7 +import com.ruoyi.system.protocol.property.DeviceProperty;
  8 +import io.netty.buffer.Unpooled;
  9 +import io.netty.channel.ChannelHandlerContext;
  10 +import io.netty.util.AttributeKey;
  11 +import io.netty.util.CharsetUtil;
  12 +import org.springframework.stereotype.Service;
  13 +import java.util.ArrayList;
  14 +import java.util.List;
  15 +import java.util.regex.Matcher;
  16 +import java.util.regex.Pattern;
  17 +
  18 +/**
  19 + * 3. CDMA GPS定位数据包
  20 + *
  21 + * @author 20412
  22 + */
  23 +@Service(ThinkraceUtil.REQ.CDMA_GPS_REQ)
  24 +public class ICdmaGpsPacketAdapter extends ICommandAdapter {
  25 +
  26 +
  27 + @Override
  28 + public Object resolveCommand(String command, ChannelHandlerContext ctx) {
  29 + String imei = ctx.channel().attr(AttributeKey.valueOf(ThinkraceUtil.IMEI)).toString();
  30 + log.info("CDMA GPS定位数据包:{}", command);
  31 + // 格式GPS+LBS+状态+基站+WIFI合并包
  32 + // 示例:IWAP91080524A2232.9806N11404.9355E000.1061830323.8700908000102,
  33 + // 13824,1,1838,-52,Home|74-DE-2B-44-88-8C|97& Home1|74-DE-2B-44-88-8C|97&Home2|74-DE-2B-44-88-8C|97& Home3|74-DE-2B-44-88-8C|97#
  34 + GpsInfo gpsCommand = resolveGpsCommand(command);
  35 + // 返回响应 IWBP91#
  36 + String response = ThinkraceUtil.RES.CDMA_GPS_RES + ThinkraceUtil.END_SYMBOL;
  37 + ctx.channel().writeAndFlush(Unpooled.copiedBuffer(response, CharsetUtil.UTF_8));
  38 + // 响应log
  39 + responseLog(imei, response);
  40 + return gpsCommand;
  41 + }
  42 +
  43 + @Override
  44 + public CommandListen sendCommand(Object params) {
  45 + return null;
  46 + }
  47 +
  48 + /**
  49 + * 格式GPS+LBS+状态+基站+WIFI合并包
  50 + * 示例:IWAP91080524A2232.9806N11404.9355E000.1061830323.8700908000102,
  51 + * 13824,1,1838,-52,Home|74-DE-2B-44-88-8C|97& Home1|74-DE-2B-44-88-8C|97&Home2|74-DE-2B-44-88-8C|97& Home3|74-DE-2B-44-88-8C|97#
  52 + *
  53 + * @param command 指令
  54 + */
  55 + private GpsInfo resolveGpsCommand(String command) {
  56 + String conditionRegex = ThinkraceUtil.REQ.CDMA_GPS_REQ + "([0-9]{6})([AV])";
  57 + // 数据分段
  58 + String[] sc = command.split(",");
  59 + String str1 = sc[0];
  60 + // TODO 判断定位是否有效 匹配 "IWAP01" 后的数字,直到遇到大写字母 "A" 而是V的话 就是不合法
  61 + Pattern pattern = Pattern.compile(conditionRegex);
  62 + Matcher matcher = pattern.matcher(str1);
  63 + GpsInfo gps = new GpsInfo();
  64 + if (matcher.find()) {
  65 + // 位置信息合法 todo
  66 + String addressAndOtherRegex = conditionRegex + "([0-9]{4}.[0-9]{4}N)([0-9]{5}.[0-9]{4}E)(\\d+.[0-9]{1})([0-9]{6})(\\d+.[0-9]{2})([0-9]{3})([0-9]{3})([0-9]{1})([0-9]{2})([0-9]{2})";
  67 + pattern = Pattern.compile(addressAndOtherRegex);
  68 + matcher = pattern.matcher(str1);
  69 + if (matcher.find()) {
  70 + // 设备时间
  71 + gps.setDeviceDate(matcher.group(1));
  72 + gps.setAddressCheck(matcher.group(2));
  73 + if (DeviceProperty.AddressProperty.INVALID.equals(gps.getAddressCheck())) {
  74 + log.info("gps数据V无效,命令为:{}", command);
  75 + }
  76 + // ddmm.mmmm维度
  77 + gps.setGpsN(matcher.group(3));
  78 + // dddmm.mmmm 经度
  79 + gps.setGpsE(matcher.group(4));
  80 + gps.setSpeed(matcher.group(5));
  81 + gps.setDeviceTime(matcher.group(6));
  82 + gps.setDirection(matcher.group(7));
  83 +// gps.setGms(matcher.group(7));
  84 + // 参与定位的卫星数
  85 + gps.setGpsSatelliteCount(matcher.group(8));
  86 + gps.setPower(matcher.group(9));
  87 + gps.setPlaceholder(matcher.group(10));
  88 + // 是否设防 01 设防 00 未设置
  89 + gps.setIsSafety(matcher.group(11));
  90 + // 是否为工作模式 02 工作模式
  91 + gps.setIsWork(matcher.group(12));
  92 + gps.setSid(sc[1]);
  93 + gps.setNid(sc[2]);
  94 + gps.setBid(sc[3]);
  95 + gps.setSignal(Integer.parseInt(sc[4]));
  96 + // WIFI 信息
  97 + List<WifiInfo> wifiGroups = new ArrayList<>();
  98 + gps.setWifiInfoList(wifiGroups);
  99 + if (sc.length > 5) {
  100 + String[] wifiSpilt = sc[5].replace(ThinkraceUtil.END_SYMBOL, "")
  101 + .replaceAll(" ", "")
  102 + .split("&");
  103 + for (String wifi : wifiSpilt) {
  104 + String[] wifiInfo = wifi.split("\\|");
  105 + wifiGroups.add(new WifiInfo(wifiInfo[0], wifiInfo[1], wifiInfo[2]));
  106 + }
  107 + }
  108 + }
  109 + }
  110 +
  111 + return gps;
  112 +
  113 + }
  114 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/protocol/adapter/device/ICdmaWifiPacketAdapter.java 0 → 100644
  1 +package com.ruoyi.system.protocol.adapter.device;
  2 +
  3 +import com.ruoyi.system.domain.command.GpsInfo;
  4 +import com.ruoyi.system.domain.command.WifiInfo;
  5 +import com.ruoyi.system.protocol.ThinkraceUtil;
  6 +import com.ruoyi.system.protocol.adapter.ICommandAdapter;
  7 +import io.netty.buffer.Unpooled;
  8 +import io.netty.channel.ChannelHandlerContext;
  9 +import io.netty.util.AttributeKey;
  10 +import io.netty.util.CharsetUtil;
  11 +import org.springframework.stereotype.Service;
  12 +
  13 +import java.util.ArrayList;
  14 +import java.util.List;
  15 +
  16 +/**
  17 + * CDMA基站wifi定位数据包
  18 + *
  19 + * @author 20412
  20 + */
  21 +@Service(ThinkraceUtil.REQ.CDMA_WIFI_GPS_REQ)
  22 +public class ICdmaWifiPacketAdapter extends ICommandAdapter {
  23 +
  24 + @Override
  25 + public Object resolveCommand(String command, ChannelHandlerContext ctx) {
  26 + // IWAP92,zh_cn,0, 13824,1,1838,-52,4,1|D8-24-BD-79-FA-1F|59&2|3C-46-D8-6D-CE-01|81&3|0C-4C-39-1A-7C-65|69&4|70-A8-E3-5D-D7-C0|65#
  27 + GpsInfo gi = new GpsInfo();
  28 + gi.setImei(ctx.channel().attr(AttributeKey.valueOf(ThinkraceUtil.IMEI)).toString());
  29 + log.info("设备:{},CDMA基站wifi定位数据包:{}", gi.getImei(), command);
  30 + String sub = command.substring(0, command.length() - 1);
  31 + String[] params = sub.split(",");
  32 + gi.setCommandLabel(params[0]);
  33 + gi.setLanguageLabel(params[1]);
  34 + gi.setReplyLabel(params[2]);
  35 + gi.setSid(params[3]);
  36 + gi.setNid(params[4]);
  37 + gi.setBid(params[5]);
  38 + gi.setSignal(Integer.parseInt(params[6]));
  39 + gi.setWifiGroups(Integer.parseInt(params[7]));
  40 + List<WifiInfo> wifiInfoList = new ArrayList<>();
  41 + gi.setWifiInfoList(wifiInfoList);
  42 + String[] wifiInfo = params[8].split("&");
  43 + for (String baseWifi : wifiInfo) {
  44 + String[] param = baseWifi.split("\\|");
  45 + WifiInfo wi = new WifiInfo(param[0], param[1], param[2]);
  46 + wifiInfoList.add(wi);
  47 + }
  48 +
  49 + // 响应 IWBP92#
  50 + String response = ThinkraceUtil.RES.CDMA_WIFI_GPS_RES + ThinkraceUtil.END_SYMBOL;
  51 + ctx.channel().writeAndFlush(Unpooled.copiedBuffer(response, CharsetUtil.UTF_8));
  52 + responseLog(gi.getImei(), response);
  53 + return gi;
  54 + }
  55 +
  56 + @Override
  57 + public CommandListen sendCommand(Object params) {
  58 + return null;
  59 + }
  60 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/protocol/adapter/device/IDeviceAlarmPacketAdapter.java 0 → 100644
  1 +package com.ruoyi.system.protocol.adapter.device;
  2 +
  3 +import com.ruoyi.system.domain.command.DeviceAlarm;
  4 +import com.ruoyi.system.domain.command.WifiInfo;
  5 +import com.ruoyi.system.protocol.ThinkraceUtil;
  6 +import com.ruoyi.system.protocol.adapter.ICommandAdapter;
  7 +import com.ruoyi.system.protocol.property.DeviceProperty;
  8 +import com.ruoyi.utils.IUnicodeUtils;
  9 +import io.netty.buffer.Unpooled;
  10 +import io.netty.channel.ChannelHandlerContext;
  11 +import io.netty.util.AttributeKey;
  12 +import io.netty.util.CharsetUtil;
  13 +import org.springframework.stereotype.Service;
  14 +import java.util.ArrayList;
  15 +import java.util.List;
  16 +import java.util.regex.Matcher;
  17 +import java.util.regex.Pattern;
  18 +
  19 +/**
  20 + * 报警与地址回复包
  21 + *
  22 + * @author 20412
  23 + */
  24 +@Service(ThinkraceUtil.REQ.ALARM_REQ)
  25 +public class IDeviceAlarmPacketAdapter extends ICommandAdapter {
  26 +
  27 + @Override
  28 + public Object resolveCommand(String command, ChannelHandlerContext ctx) {
  29 +
  30 + // IWAP10+时间+GPS位置有效性标志+纬度+经度+速度+定位数据产生时间+方向角+参数,基站数据,报警值,终端语言,标志位,Wi-Fi信息#
  31 + // 示例:IWAP10080524A2232.9806N11404.9355E000.1061830323.8706000908000502,460,0,9520,3671,00,zh-cn,00,
  32 + String regex = ThinkraceUtil.REQ.ALARM_REQ + "([0-9]{6})([AV])";
  33 + // 数据分段
  34 + String[] sc = command.split(",");
  35 + String str1 = sc[0];
  36 + // TODO 判断定位是否有效 匹配 "IWAP01" 后的数字,直到遇到大写字母 "A" 而是V的话 就是不合法
  37 + Pattern pattern = Pattern.compile(regex);
  38 + Matcher matcher = pattern.matcher(str1);
  39 + DeviceAlarm alarm = new DeviceAlarm();
  40 + alarm.setImei(ctx.channel().attr(AttributeKey.valueOf(ThinkraceUtil.IMEI)).toString());
  41 + log.info("设备:{},报警与地址回复:{}", alarm.getImei(), command);
  42 + // 匹配是否有效
  43 + if (matcher.find()) {
  44 + // 位置信息合法 todo
  45 + String addressAndOtherRegex = regex + "([0-9]{4}.[0-9]{4}N)([0-9]{5}.[0-9]{4}E)(\\d+.[0-9]{1})([0-9]{6})(\\d+.[0-9]{2})([0-9]{3})([0-9]{3})([0-9]{3})([0-9]{1})([0-9]{2})([0-9]{2})";
  46 + pattern = Pattern.compile(addressAndOtherRegex);
  47 + matcher = pattern.matcher(str1);
  48 + if (matcher.find()) {
  49 + // 设备时间
  50 + alarm.setDeviceDate(matcher.group(1));
  51 + alarm.setAddressCheck(matcher.group(2));
  52 + if (DeviceProperty.AddressProperty.INVALID.equals(alarm.getAddressCheck())) {
  53 + log.info("gps数据V无效,命令为:{}", command);
  54 + }
  55 + // ddmm.mmmm维度
  56 + alarm.setGpsN(matcher.group(3));
  57 + // dddmm.mmmm 经度
  58 + alarm.setGpsE(matcher.group(4));
  59 + alarm.setSpeed(matcher.group(5));
  60 + alarm.setDeviceTime(matcher.group(6));
  61 + alarm.setDirection(matcher.group(7));
  62 + alarm.setGms(matcher.group(8));
  63 + // 参与定位的卫星数
  64 + alarm.setGpsSatelliteCount(matcher.group(9));
  65 + alarm.setPower(matcher.group(10));
  66 + alarm.setPlaceholder(matcher.group(11));
  67 + // 是否设防 01 设防 00 未设置
  68 + alarm.setIsSafety(matcher.group(12));
  69 + // 是否为工作模式 02 工作模式
  70 + alarm.setIsWork(matcher.group(13));
  71 + alarm.setMcc(sc[1]);
  72 + alarm.setMnc(sc[2]);
  73 + alarm.setLac(sc[3]);
  74 + alarm.setCid(sc[4]);
  75 + // 01 SOS 02 低电 03 摘除手表
  76 + // 04 预留 05 防拆表带打开 06 跌倒
  77 + // 07 无效的心率值 08 心率过高 09 心率过低
  78 + // 10 舒张压过高 11 舒张压过低 12 收缩压过高
  79 + // 13 收缩压过低 14 久坐 15 预留
  80 + // 16 防拆表带锁上 17 体温过高 18 体温过低
  81 + // 19 关机 20 离开电子围栏 21 进入电子围栏
  82 + // 22 消息已阅读
  83 + alarm.setAlarmValue(sc[5]);
  84 + alarm.setDeviceLanguage(sc[6]);
  85 + // 第一个0:是否需要回复地址信息标志,取值为0表示不需要回复,取值为1表示需要回复.
  86 + // 第二个0:地址信息中是否包含手机超链接, 取值为0表示不包含,取值为1表示包含. TODO 什么手机超链接
  87 + alarm.setAlarmInfoLabel(sc[7]);
  88 + // WIFI 信息
  89 + List<WifiInfo> infoList = new ArrayList<>();
  90 + alarm.setWifiInfo(infoList);
  91 + String[] wifiSpilt = sc[sc.length - 1].replace(ThinkraceUtil.END_SYMBOL, "")
  92 + .replaceAll(" ", "")
  93 + .split("&");
  94 + for (String wifi : wifiSpilt) {
  95 + String[] wifiInfo = wifi.split("\\|");
  96 + infoList.add(new WifiInfo(wifiInfo[0], wifiInfo[1], wifiInfo[2]));
  97 + }
  98 + }
  99 + }
  100 + // 返回响应
  101 + backCommand(ctx, alarm);
  102 + return alarm;
  103 + }
  104 +
  105 + private void backCommand(ChannelHandlerContext ctx, DeviceAlarm alarm) {
  106 + char isBackAddress = alarm.getAlarmInfoLabel().charAt(0);
  107 + String response = "";
  108 + if (isBackAddress == '1') {
  109 + // TODO 返回地址 判断是否需要返回超链接 地址是解析好的设备传过来的地址还是服务器预留的地址以及连接。
  110 + String unicode = IUnicodeUtils.toUnicode("深圳市南山区南海大道1079号http://www.gps.com/map.aspx?lat=23.123&lng=113.123");
  111 + response = ThinkraceUtil.RES.ALARM_RES + unicode + ThinkraceUtil.END_SYMBOL;
  112 + } else {
  113 + response = ThinkraceUtil.RES.ALARM_RES + ThinkraceUtil.END_SYMBOL;
  114 + }
  115 + ctx.channel().writeAndFlush(Unpooled.copiedBuffer(response, CharsetUtil.UTF_8));
  116 + // 打印日志
  117 + responseLog(alarm.getImei(), response);
  118 + }
  119 +
  120 + @Override
  121 + public CommandListen sendCommand(Object params) {
  122 + return null;
  123 + }
  124 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/protocol/adapter/device/IEcgDatePacketAdapter.java 0 → 100644
  1 +package com.ruoyi.system.protocol.adapter.device;
  2 +
  3 +import com.ruoyi.system.protocol.ThinkraceUtil;
  4 +import com.ruoyi.system.protocol.adapter.ICommandAdapter;
  5 +import io.netty.buffer.Unpooled;
  6 +import io.netty.channel.ChannelHandlerContext;
  7 +import io.netty.util.AttributeKey;
  8 +import io.netty.util.CharsetUtil;
  9 +import org.springframework.stereotype.Service;
  10 +
  11 +/**
  12 + * ECG 数据
  13 + *
  14 + * @author 20412
  15 + */
  16 +@Service(ThinkraceUtil.REQ.ECG_DATA_REQ)
  17 +public class IEcgDatePacketAdapter extends ICommandAdapter {
  18 +
  19 + @Override
  20 + public Object resolveCommand(String command, ChannelHandlerContext ctx) {
  21 + // IWAPHD,20140818064408,6,1,1.3,20.2,1,1000,1024,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX#
  22 + String imei = ctx.channel().attr(AttributeKey.valueOf(ThinkraceUtil.IMEI)).toString();
  23 + log.info("设备:{},ECG数据命令:{}", imei, command);
  24 +
  25 + // 响应IWBPHD,0, 98, 59, 156, 296, 369,7aa660275fc35f8b4e0d9f50|623f98a4 #
  26 + String response = "IWBPHD,0, 98, 59, 156, 296, 369,7aa660275fc35f8b4e0d9f50|623f98a4 #";
  27 + ctx.channel().writeAndFlush(Unpooled.copiedBuffer(response, CharsetUtil.UTF_8));
  28 + responseLog(imei, response);
  29 + return null;
  30 + }
  31 +
  32 + @Override
  33 + public CommandListen sendCommand(Object params) {
  34 + return null;
  35 + }
  36 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/protocol/adapter/device/IGpsPacketAdapter.java 0 → 100644
  1 +package com.ruoyi.system.protocol.adapter.device;
  2 +
  3 +import com.ruoyi.system.domain.command.GpsInfo;
  4 +import com.ruoyi.system.domain.command.WifiInfo;
  5 +import com.ruoyi.system.protocol.ThinkraceUtil;
  6 +import com.ruoyi.system.protocol.adapter.ICommandAdapter;
  7 +import com.ruoyi.system.protocol.property.DeviceProperty;
  8 +import io.netty.buffer.Unpooled;
  9 +import io.netty.channel.ChannelHandlerContext;
  10 +import io.netty.util.AttributeKey;
  11 +import io.netty.util.CharsetUtil;
  12 +import org.springframework.stereotype.Service;
  13 +
  14 +import java.util.ArrayList;
  15 +import java.util.List;
  16 +import java.util.regex.Matcher;
  17 +import java.util.regex.Pattern;
  18 +
  19 +@Service(ThinkraceUtil.REQ.GPS_REQ)
  20 +public class IGpsPacketAdapter extends ICommandAdapter {
  21 +
  22 + @Override
  23 + public Object resolveCommand(String command, ChannelHandlerContext ctx) {
  24 + log.info("定位数据包:{}", command);
  25 + // 处理指令 获取gps信息
  26 + GpsInfo gpsInfo = resolveGpsCommand(command);
  27 + gpsInfo.setImei(ctx.channel().attr(AttributeKey.valueOf(ThinkraceUtil.IMEI)).toString());
  28 + // 返回响应
  29 + String response = ThinkraceUtil.RES.GPS_RES + ThinkraceUtil.END_SYMBOL;
  30 + ctx.channel().writeAndFlush(Unpooled.copiedBuffer(response, CharsetUtil.UTF_8));
  31 + // 打印日志
  32 + responseLog(gpsInfo.getImei(), response);
  33 + return gpsInfo;
  34 + }
  35 +
  36 + @Override
  37 + public CommandListen sendCommand(Object params) {
  38 + return null;
  39 + }
  40 +
  41 + private GpsInfo resolveGpsCommand(String command) {
  42 + String conditionRegex = ThinkraceUtil.REQ.GPS_REQ + "([0-9]{6})([AV])";
  43 + // 数据分段
  44 + String[] sc = command.split(",");
  45 + String str1 = sc[0];
  46 + // TODO 判断定位是否有效 匹配 "IWAP01" 后的数字,直到遇到大写字母 "A" 而是V的话 就是不合法
  47 + Pattern pattern = Pattern.compile(conditionRegex);
  48 + Matcher matcher = pattern.matcher(str1);
  49 + GpsInfo gps = new GpsInfo();
  50 + if (matcher.find()) {
  51 + // 位置信息合法 todo
  52 + String addressAndOtherRegex = conditionRegex + "([0-9]{4}.[0-9]{4}N)([0-9]{5}.[0-9]{4}E)(\\d+.[0-9]{1})([0-9]{6})(\\d+.[0-9]{2})([0-9]{3})([0-9]{3})([0-9]{3})([0-9]{1})([0-9]{2})([0-9]{2})";
  53 + pattern = Pattern.compile(addressAndOtherRegex);
  54 + matcher = pattern.matcher(str1);
  55 + if (matcher.find()) {
  56 + // 设备时间
  57 + gps.setDeviceDate(matcher.group(1));
  58 + gps.setAddressCheck(matcher.group(2));
  59 + if (DeviceProperty.AddressProperty.INVALID.equals(gps.getAddressCheck())) {
  60 + log.info("gps数据V无效,命令为:{}", command);
  61 + }
  62 + // ddmm.mmmm维度
  63 + gps.setGpsN(matcher.group(3));
  64 + // dddmm.mmmm 经度
  65 + gps.setGpsE(matcher.group(4));
  66 + gps.setSpeed(matcher.group(5));
  67 + gps.setDeviceTime(matcher.group(6));
  68 + gps.setDirection(matcher.group(7));
  69 + gps.setGms(matcher.group(8));
  70 + // 参与定位的卫星数
  71 + gps.setGpsSatelliteCount(matcher.group(9));
  72 + gps.setPower(matcher.group(10));
  73 + gps.setPlaceholder(matcher.group(11));
  74 + // 是否设防 01 设防 00 未设置
  75 + gps.setIsSafety(matcher.group(12));
  76 + // 是否为工作模式 02 工作模式
  77 + gps.setIsWork(matcher.group(13));
  78 + gps.setMcc(sc[1]);
  79 + gps.setMnc(sc[2]);
  80 + gps.setLac(sc[3]);
  81 + gps.setCid(sc[4]);
  82 + // WIFI 信息
  83 + List<WifiInfo> infoList = new ArrayList<>();
  84 + gps.setWifiInfoList(infoList);
  85 + if (sc.length > 5) {
  86 + String[] wifiSpilt = sc[5].replace(ThinkraceUtil.END_SYMBOL, "")
  87 + .replaceAll(" ", "")
  88 + .split("&");
  89 + for (String wifi : wifiSpilt) {
  90 + String[] wifiInfo = wifi.split("\\|");
  91 + infoList.add(new WifiInfo(wifiInfo[0], wifiInfo[1], wifiInfo[2]));
  92 + }
  93 + }
  94 + }
  95 + }
  96 + return gps;
  97 +
  98 + }
  99 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/protocol/adapter/device/IHealthPacketAdapter.java 0 → 100644
  1 +package com.ruoyi.system.protocol.adapter.device;
  2 +
  3 +import com.ruoyi.system.domain.command.HealthInfo;
  4 +import com.ruoyi.system.protocol.ThinkraceUtil;
  5 +import com.ruoyi.system.protocol.adapter.ICommandAdapter;
  6 +import io.netty.buffer.Unpooled;
  7 +import io.netty.channel.ChannelHandlerContext;
  8 +import io.netty.util.CharsetUtil;
  9 +import org.springframework.stereotype.Service;
  10 +
  11 +import java.util.Date;
  12 +
  13 +import static com.ruoyi.system.protocol.property.DeviceProperty.HealthProperty.*;
  14 +
  15 +/**
  16 + * @author 20412健康
  17 + */
  18 +@Service(ThinkraceUtil.REQ.HEALTH_REQ)
  19 +public class IHealthPacketAdapter extends ICommandAdapter {
  20 +
  21 + @Override
  22 + public Object resolveCommand(String command, ChannelHandlerContext ctx) {
  23 + // 格式:IWAPJK,产生健康数据的时间,健康数据类型,健康数值#
  24 + // 示例:IWAPJK,2021-05-29 13:00:00,1,691120# 或
  25 + // IWAPJK,2024-03-28 14:21:09,3,36.19#
  26 + String[] params = command.split(",");
  27 + HealthInfo info = new HealthInfo();
  28 + info.setCreateTime(new Date());
  29 + info.setCommandLabel(params[0]);
  30 + info.setUploadTime(params[1]);
  31 + info.setHealthType(params[2]);
  32 +// info.setHealthValue(params[3].replace(ThinkraceUtil.END_SYMBOL,""));
  33 + // 1:健康数据类型,取值1表示血压,取值2表示心率,取值3表示体温,4表示血氧。
  34 + //691120:健康数值,根据类型不同数据内容有不同。691120表示,舒张压(低压)69,收缩压(高 压)120,中间的1为多个参数分隔符。
  35 + //当健康数据类型为3时,后面的健康数值是体温,36.19是具体的温度值,单位为摄氏度。
  36 + //若只有一个参数则无需分隔符,例如心率:80,则包为:WAPJK,2021-05-29100:00,2,80#
  37 + switch (info.getHealthType()) {
  38 + case BLOOD_PRESSURE:
  39 + info.setLowPressure(Integer.parseInt(params[3].substring(0, 2)));
  40 + info.setHighPressure(Integer.parseInt(params[3].substring(3, 6)));
  41 + break;
  42 + case HEART_RATE:
  43 + info.setHeartRate(params[3]);
  44 + break;
  45 + case TEMPERATURE:
  46 + info.setTemperature(Float.parseFloat(params[3].replace(ThinkraceUtil.END_SYMBOL, "")));
  47 + break;
  48 + case BLOOD_OXYGEN:
  49 + info.setBloodOxygen(params[3]);
  50 + break;
  51 + }
  52 + String response = ThinkraceUtil.RES.HEALTH_RES + "," + info.getHealthType() + ThinkraceUtil.END_SYMBOL;
  53 + ctx.channel().writeAndFlush(Unpooled.copiedBuffer(response, CharsetUtil.UTF_8));
  54 + responseLog(info.getImei(), response);
  55 + return null;
  56 + }
  57 +
  58 + @Override
  59 + public CommandListen sendCommand(Object params) {
  60 + return null;
  61 + }
  62 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/protocol/adapter/device/IHeartRateBloodPressureOxygenSugarResPacketAdapter.java 0 → 100644
  1 +package com.ruoyi.system.protocol.adapter.device;
  2 +
  3 +import com.ruoyi.system.domain.command.HealthInfo;
  4 +import com.ruoyi.system.protocol.ThinkraceUtil;
  5 +import com.ruoyi.system.protocol.adapter.ICommandAdapter;
  6 +import io.netty.buffer.Unpooled;
  7 +import io.netty.channel.ChannelHandlerContext;
  8 +import io.netty.util.AttributeKey;
  9 +import io.netty.util.CharsetUtil;
  10 +import org.springframework.stereotype.Service;
  11 +
  12 +import java.util.ArrayList;
  13 +import java.util.List;
  14 +
  15 +/**
  16 + * 12 上传心率、血压、血氧、血糖
  17 + *
  18 + * @author 20412
  19 + */
  20 +@Service(ThinkraceUtil.REQ.HEART_RATE_BLOOD_PRESSURE_OXYGEN_SUGAR_REQ)
  21 +public class IHeartRateBloodPressureOxygenSugarResPacketAdapter extends ICommandAdapter {
  22 +
  23 + @Override
  24 + public Object resolveCommand(String command, ChannelHandlerContext ctx) {
  25 + // IWAPHP,60,130,85,95,90,36.5,,,,,,,#
  26 + // IWAPHP,83,119,80,,,,,,,,,,#
  27 + // IWAPHP,,,,98,,,,,,,,,#
  28 + //IW:标识符
  29 + //APHT : 命令字符
  30 + //60 : 表示心率
  31 + //130: 血压高压 [收缩压]
  32 + //85 : 血压低压 [舒张压]
  33 + //95:血氧
  34 + //90:血糖
  35 + //36.5: 体温
  36 + // ,分割
  37 + List<String> params = new ArrayList<>();
  38 + StringBuilder sb = new StringBuilder();
  39 + for (char ch : command.toCharArray()) {
  40 + if (',' == ch) {
  41 + params.add(sb.toString());
  42 + sb = new StringBuilder();
  43 + continue;
  44 + }
  45 + sb.append(ch);
  46 + }
  47 + HealthInfo health = new HealthInfo();
  48 + health.setCommandLabel(params.get(0));
  49 + health.setImei(ctx.channel().attr(AttributeKey.valueOf(ThinkraceUtil.IMEI)).toString());
  50 + log.info("设备:{},上传心率、血压、血氧、血糖命令:{}", health.getImei(), command);
  51 + health.setHeartRate(params.get(1));
  52 + health.setHighPressure(params.get(2).length() != 0 ? Integer.parseInt(params.get(2)) : null);
  53 + health.setLowPressure(params.get(3).length() != 0 ? Integer.parseInt(params.get(3)) : null);
  54 + health.setBloodOxygen(params.get(4));
  55 + health.setBloodSugar(params.get(5));
  56 + health.setTemperature(params.get(6).length() != 0 ? Float.parseFloat(params.get(6)) : null);
  57 + String response = ThinkraceUtil.RES.HEART_RATE_BLOOD_PRESSURE_OXYGEN_SUGAR_RES + ThinkraceUtil.END_SYMBOL;
  58 + ctx.channel().writeAndFlush(Unpooled.copiedBuffer(response, CharsetUtil.UTF_8));
  59 + responseLog(health.getImei(), response);
  60 + return null;
  61 + }
  62 +
  63 + @Override
  64 + public CommandListen sendCommand(Object params) {
  65 + return null;
  66 + }
  67 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/protocol/adapter/device/IHeartRatePacketAdapter.java 0 → 100644
  1 +package com.ruoyi.system.protocol.adapter.device;
  2 +
  3 +import com.ruoyi.system.domain.command.HealthInfo;
  4 +import com.ruoyi.system.protocol.ThinkraceUtil;
  5 +import com.ruoyi.system.protocol.adapter.ICommandAdapter;
  6 +import io.netty.buffer.Unpooled;
  7 +import io.netty.channel.ChannelHandlerContext;
  8 +import io.netty.util.AttributeKey;
  9 +import io.netty.util.CharsetUtil;
  10 +import org.springframework.stereotype.Service;
  11 +
  12 +import java.util.Date;
  13 +
  14 +@Service(ThinkraceUtil.REQ.HEART_REQ)
  15 +public class IHeartRatePacketAdapter extends ICommandAdapter {
  16 +
  17 + @Override
  18 + public Object resolveCommand(String command, ChannelHandlerContext ctx) {
  19 + // 格式:IWAP49,心率值#
  20 + // 示例:IWAP49,68#
  21 + HealthInfo rateInfo = new HealthInfo();
  22 + String[] params = command.substring(0, command.length() - 1).split(",");
  23 + rateInfo.setCommandLabel(params[0]);
  24 + rateInfo.setHeartRate(params[1]);
  25 + rateInfo.setCreateTime(new Date());
  26 + rateInfo.setImei(ctx.channel().attr(AttributeKey.valueOf(ThinkraceUtil.IMEI)).toString());
  27 + log.info("设备:{},心率数据上传命令:{}", rateInfo.getImei(), command);
  28 + // 返回响应
  29 + String response = ThinkraceUtil.RES.HEART_RES + ThinkraceUtil.END_SYMBOL;
  30 + ctx.channel().writeAndFlush(Unpooled.copiedBuffer(response, CharsetUtil.UTF_8));
  31 + // 打印日志
  32 + responseLog(rateInfo.getImei(), response);
  33 + return rateInfo;
  34 + }
  35 +
  36 + @Override
  37 + public CommandListen sendCommand(Object params) {
  38 + return null;
  39 + }
  40 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/protocol/adapter/device/IImagePacketAdapter.java 0 → 100644
  1 +package com.ruoyi.system.protocol.adapter.device;
  2 +
  3 +import com.ruoyi.system.domain.command.ImageInfo;
  4 +import com.ruoyi.system.protocol.ThinkraceUtil;
  5 +import com.ruoyi.system.protocol.adapter.ICommandAdapter;
  6 +import com.ruoyi.system.protocol.property.DeviceProperty;
  7 +import com.ruoyi.utils.PathToolUtil;
  8 +import io.netty.buffer.Unpooled;
  9 +import io.netty.channel.ChannelHandlerContext;
  10 +import io.netty.util.AttributeKey;
  11 +import io.netty.util.CharsetUtil;
  12 +import org.springframework.beans.factory.annotation.Value;
  13 +import org.springframework.stereotype.Service;
  14 +
  15 +/**
  16 + * 设备图片指令
  17 + *
  18 + * @author 20412
  19 + */
  20 +@Service(ThinkraceUtil.REQ.IMAGE_REQ)
  21 +public class IImagePacketAdapter extends ICommandAdapter {
  22 +
  23 + @Value("${netty.socket.catalogue.image}")
  24 + private String catalogue;
  25 +
  26 +
  27 + /**
  28 + * 图片上行指令
  29 + *
  30 + * @param command 指令
  31 + * @param ctx
  32 + * @return
  33 + */
  34 + @Override
  35 + public Object resolveCommand(String command, ChannelHandlerContext ctx) {
  36 + // TODO 记录表状态 给出耗时 给出当前上传进度 超时机制 重传机制
  37 + ImageInfo imageInfo = new ImageInfo();
  38 + imageInfo.setImei(ctx.channel().attr(AttributeKey.valueOf(ThinkraceUtil.IMEI)).toString());
  39 + String[] params = command.substring(0, command.length() - 1).split(",");
  40 + String count = params[2];
  41 + String current = params[3];
  42 + log.info("设备:{},图片上传命令:{},共:{}个包,当前第:{}个包,字节数据:{}", imageInfo.getImei(), command, count, current, params[5]);
  43 + boolean result = uploadImage(imageInfo.getImei(), params, ctx);
  44 + // 响应示例
  45 + // IWBP42, 终端产生图片的时间+图片包总数+当前收到的包号+接收成功标志#
  46 + // IWBP42,20140818064408,6,1,1#
  47 + String response = ThinkraceUtil.RES.IMAGE_RES + "," + params[1] + "," + params[2] + "," + params[3] + "," +
  48 + (result ? DeviceProperty.ImageProperty.UPLOAD_SUCCESS : DeviceProperty.ImageProperty.UPLOAD_FAIL)
  49 + + ThinkraceUtil.END_SYMBOL;
  50 + ctx.channel().writeAndFlush(Unpooled.copiedBuffer(response, CharsetUtil.UTF_8));
  51 + // TODO 更新写入日志 on key 可以使用mysql的key存在时更新操作把新的文件写入进去
  52 +// String count = params[2];
  53 + responseLog(imageInfo.getImei(), response);
  54 + return imageInfo;
  55 + }
  56 +
  57 + @Override
  58 + public CommandListen sendCommand(Object params) {
  59 + return null;
  60 + }
  61 +
  62 + private boolean uploadImage(String imei, String[] params, ChannelHandlerContext ctx) {
  63 + // IWAP42,终端产生图片的时间,图片包总数,当前包号,当前包长度,当前包图片数据#
  64 + // IWAP42,20140818064408,6,1,1024,XXXXXXXXXXXXXXXXXXXXXXXXX#
  65 + // 数据存入集合全部集齐后写入
  66 + String uploadTime = params[1];
  67 + // TODO 当前是否为第一个包 判断资源是否存在
  68 + String count = params[2];
  69 + String current = params[3];
  70 + String packetLength = params[4];
  71 + String data = params[5];
  72 + // TODO 写入数据库
  73 + // 追加数据包
  74 + boolean result = PathToolUtil.generateRelativeImagePath(catalogue, imei, uploadTime, data.getBytes());
  75 + return true;
  76 + }
  77 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/protocol/adapter/device/IKeepLivePacketAdapter.java 0 → 100644
  1 +package com.ruoyi.system.protocol.adapter.device;
  2 +
  3 +import com.ruoyi.system.domain.command.KeepLiveInfo;
  4 +import com.ruoyi.system.protocol.ThinkraceUtil;
  5 +import com.ruoyi.system.protocol.adapter.ICommandAdapter;
  6 +import io.netty.buffer.Unpooled;
  7 +import io.netty.channel.ChannelHandlerContext;
  8 +import io.netty.util.AttributeKey;
  9 +import io.netty.util.CharsetUtil;
  10 +import org.springframework.stereotype.Service;
  11 +
  12 +import java.util.regex.Matcher;
  13 +import java.util.regex.Pattern;
  14 +
  15 +/**
  16 + * 心跳包处理
  17 + *
  18 + * @author 20412
  19 + */
  20 +@Service(ThinkraceUtil.REQ.KEEPLIVE_REQ)
  21 +public class IKeepLivePacketAdapter extends ICommandAdapter {
  22 +
  23 + @Override
  24 + public Object resolveCommand(String command, ChannelHandlerContext ctx) {
  25 + KeepLiveInfo heart = handlerHeartPacket(command);
  26 + heart.setImei(ctx.channel().attr(AttributeKey.valueOf(ThinkraceUtil.IMEI)).toString());
  27 + log.info("设备:{},心跳包:{}", heart.getImei(), command);
  28 + // 返回响应
  29 + String response = ThinkraceUtil.RES.KEEPLIVE_RES + ThinkraceUtil.END_SYMBOL;
  30 + ctx.channel().writeAndFlush(Unpooled.copiedBuffer(response, CharsetUtil.UTF_8));
  31 + return heart;
  32 + }
  33 +
  34 + private KeepLiveInfo handlerHeartPacket(String command) {
  35 + String[] params = command.substring(0, command.length() - 1).split(",");
  36 + KeepLiveInfo heart = new KeepLiveInfo();
  37 + heart.setCommandLabel(params[0]);
  38 + // 处理指令
  39 + String regex = "([0-9]{3})([0-9]{3})([0-9]{3})([0-9]{1})([0-9]{2})([0-9]{2})";
  40 + Pattern pattern = Pattern.compile(regex);
  41 + Matcher matcher = pattern.matcher(command);
  42 + if (matcher.find()) {
  43 + heart.setGsm(matcher.group(1));
  44 + heart.setSatelliteCount(matcher.group(2));
  45 + heart.setPower(matcher.group(3));
  46 + heart.setPlaceholder(matcher.group(4));
  47 + heart.setIsSafety(matcher.group(5));
  48 + heart.setIsWork(matcher.group(6));
  49 + }
  50 + // TODO 判断是那种形式的心跳包
  51 + // IWAP03,06300706800008,0,00,8,600#
  52 + // IWAP03,GSM信号值+定位卫星数+电池电量+保留位+设防状态+工作模式,计步数值,翻滚次数,工作模式,定位间隔#
  53 + if (params.length == 6) {
  54 + heart.setStep(params[2]);
  55 + heart.setRollingCount(params[3]);
  56 + heart.setGpsInterval(params[5]);
  57 + }
  58 + // IWAP03,06000908000102,5555,30#
  59 + // IWAP03,GSM信号值+定位卫星数+电池电量+保留位+设防状态+工作模式,计步数,翻滚次数#
  60 + else {
  61 + heart.setStep(params[2]);
  62 + heart.setRollingCount(params[3]);
  63 + }
  64 + return heart;
  65 + }
  66 +
  67 + @Override
  68 + public CommandListen sendCommand(Object params) {
  69 + return null;
  70 + }
  71 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/protocol/adapter/device/ILoginPacketAdapter.java 0 → 100644
  1 +package com.ruoyi.system.protocol.adapter.device;
  2 +
  3 +import com.ruoyi.system.domain.entity.Device;
  4 +import com.ruoyi.system.protocol.ThinkraceUtil;
  5 +import com.ruoyi.system.protocol.adapter.ICommandAdapter;
  6 +import com.ruoyi.utils.ConstDateUtil;
  7 +import io.netty.buffer.Unpooled;
  8 +import io.netty.channel.ChannelHandlerContext;
  9 +import io.netty.util.CharsetUtil;
  10 +import org.springframework.stereotype.Service;
  11 +
  12 +import java.util.Date;
  13 +import java.util.regex.Matcher;
  14 +import java.util.regex.Pattern;
  15 +
  16 +/**
  17 + * @author 20412
  18 + */
  19 +
  20 +/**
  21 + * 处理登录包
  22 + */
  23 +@Service(ThinkraceUtil.REQ.LOGIN_REQ)
  24 +public class ILoginPacketAdapter extends ICommandAdapter {
  25 +
  26 +
  27 + @Override
  28 + public Object resolveCommand(String command, ChannelHandlerContext ctx) {
  29 + Device device = getDeviceInfo(command);
  30 + ThinkraceUtil.setDevice(device.getImei(), ctx);
  31 + String response = ThinkraceUtil.RES.LOGIN_RES + "," + ConstDateUtil.formatDate("yyyyMMddHHmmss") + ThinkraceUtil.UTC_8 + ThinkraceUtil.END_SYMBOL;
  32 + // 返回响应
  33 + ctx.channel().writeAndFlush(Unpooled.copiedBuffer(response, CharsetUtil.UTF_8));
  34 + responseLog(device.getImei(), response);
  35 + return device;
  36 + }
  37 +
  38 + /**
  39 + * 返回响应
  40 + *
  41 + * @return command 指令
  42 + */
  43 + @Override
  44 + public CommandListen sendCommand(Object params) {
  45 + return null;
  46 + }
  47 +
  48 +
  49 + Device getDeviceInfo(String command) {
  50 + String[] params = command.split(",");
  51 + Device device = new Device();
  52 + device.setCreateTime(new Date());
  53 + String pattern = ThinkraceUtil.REQ.LOGIN_REQ + "([0-9]{15})";
  54 + Pattern regex = Pattern.compile(pattern);
  55 + //格式IWAP00+IMEI# 或者 IWAP00+IMEI,MCC|MNC|APN名称# 或者 IWAP00+IMEI,ICCID,IMSI#
  56 + if (params.length == 1) {
  57 + Matcher matcher = regex.matcher(command);
  58 + if (matcher.find()) {
  59 + String imei = matcher.group(1);
  60 + device.setImei(imei);
  61 + }
  62 + return device;
  63 + }
  64 + // 格式 IWAP00+IMEI,MCC|MNC|APN名称#
  65 + else if (params.length == 2) {
  66 + Matcher matcher = regex.matcher(params[0]);
  67 + if (matcher.find()) {
  68 + String imei = matcher.group(1);
  69 + device.setImei(imei);
  70 + }
  71 + String[] mma = params[1].split("\\|");
  72 + device.setMcc(mma[0]);
  73 + device.setMnc(mma[1]);
  74 + device.setApn(mma[2].replace(ThinkraceUtil.END_SYMBOL, ""));
  75 + return device;
  76 + }
  77 + // 格式 IWAP00+IMEI,ICCID,IMSI#
  78 + else {
  79 + Matcher matcher = regex.matcher(params[0]);
  80 + if (matcher.find()) {
  81 + String imei = matcher.group(1);
  82 + device.setImei(imei);
  83 + }
  84 + device.setIccId(params[1]);
  85 + device.setImsi(params[2].replace(ThinkraceUtil.END_SYMBOL, ""));
  86 + return device;
  87 + }
  88 + }
  89 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/protocol/adapter/device/IMoreBaseGpsPacketAdapter.java 0 → 100644
  1 +package com.ruoyi.system.protocol.adapter.device;
  2 +
  3 +import com.ruoyi.system.domain.command.GpsInfo;
  4 +import com.ruoyi.system.domain.command.WifiInfo;
  5 +import com.ruoyi.system.protocol.ThinkraceUtil;
  6 +import com.ruoyi.system.protocol.adapter.ICommandAdapter;
  7 +import io.netty.buffer.Unpooled;
  8 +import io.netty.channel.ChannelHandlerContext;
  9 +import io.netty.util.AttributeKey;
  10 +import io.netty.util.CharsetUtil;
  11 +import org.springframework.stereotype.Service;
  12 +
  13 +import java.util.ArrayList;
  14 +import java.util.List;
  15 +
  16 +/**
  17 + * 4. 多基站定位数据包
  18 + *
  19 + * @author 20412
  20 + */
  21 +@Service(ThinkraceUtil.REQ.MORE_GPS_REQ)
  22 +public class IMoreBaseGpsPacketAdapter extends ICommandAdapter {
  23 +
  24 + @Override
  25 + public Object resolveCommand(String command, ChannelHandlerContext ctx) {
  26 + GpsInfo mgi = new GpsInfo();
  27 + mgi.setImei(ctx.channel().attr(AttributeKey.valueOf(ThinkraceUtil.IMEI)).toString());
  28 + log.info("设备:{},基站定位数据包:{}", mgi.getImei(), command);
  29 + // IWAP02,zh_cn,0,7,460,0,9520|3671|13,9520|3672|12,9520|3673|11,9520|3674|10,9520|3675|9,9520|3676|8,9520|3677|7,4,1|D8-24-BD-79-FA-1F|59&2|3C-46-D8-6D-CE-01|81&3|0C-4C-39-1A-7C-65|69&4|70-A8-E3-5D-D7-C0|65#
  30 + String sub = command.substring(0, command.length() - 1);
  31 + String[] params = sub.split(",");
  32 + mgi.setLanguageLabel(params[1]);
  33 + // 回复标识:回复标识,为0时不回复地址,为 1 时,服务器回复地址信息(目前一定是0)
  34 + mgi.setReplyLabel(params[2]);
  35 + mgi.setBaseStationGroups(Integer.parseInt(params[3]));
  36 + mgi.setMcc(params[4]);
  37 + mgi.setMnc(params[5]);
  38 + List<GpsInfo.BaseStation> bsList = new ArrayList<>(mgi.getBaseStationGroups());
  39 + mgi.setBaseStations(bsList);
  40 + // 基站数据
  41 + for (int i = 0; i < mgi.getBaseStationGroups(); i++) {
  42 + GpsInfo.BaseStation bs = new GpsInfo.BaseStation();
  43 + String baseParam = params[6 + i];
  44 + String[] param = baseParam.split("\\|");
  45 + bs.setLac(param[0]);
  46 + bs.setCid(param[1]);
  47 + bs.setDbm(param[2]);
  48 + bsList.add(bs);
  49 + }
  50 + mgi.setWifiGroups(Integer.parseInt(params[6 + mgi.getBaseStationGroups()]));
  51 + List<WifiInfo> wifiInfoList = new ArrayList<>(mgi.getWifiGroups());
  52 + mgi.setWifiInfoList(wifiInfoList);
  53 + // WiFi数据
  54 + String baseParam = params[7 + mgi.getBaseStationGroups()];
  55 + String[] param = baseParam.split("&");
  56 + for (int i = 0; i < param.length; i++) {
  57 + String[] baseWifi = param[i].split("\\|");
  58 + WifiInfo wi = new WifiInfo();
  59 + wi.setSsid(baseWifi[0]);
  60 + wi.setMac(baseWifi[1]);
  61 + wi.setSignal(baseWifi[2]);
  62 + wifiInfoList.add(wi);
  63 + }
  64 + String response = ThinkraceUtil.RES.MORE_GPS_RES + ThinkraceUtil.END_SYMBOL;
  65 + ctx.channel().writeAndFlush(Unpooled.copiedBuffer(response, CharsetUtil.UTF_8));
  66 + // 响应日志
  67 + responseLog(mgi.getImei(), response);
  68 + return mgi;
  69 + }
  70 +
  71 + @Override
  72 + public CommandListen sendCommand(Object params) {
  73 + return null;
  74 + }
  75 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/protocol/adapter/device/IPowerAlarmPacketAdapter.java 0 → 100644
  1 +package com.ruoyi.system.protocol.adapter.device;
  2 +
  3 +import com.ruoyi.system.domain.command.PowerAlarm;
  4 +import com.ruoyi.system.protocol.ThinkraceUtil;
  5 +import com.ruoyi.system.protocol.adapter.ICommandAdapter;
  6 +import io.netty.buffer.Unpooled;
  7 +import io.netty.channel.ChannelHandlerContext;
  8 +import io.netty.util.AttributeKey;
  9 +import io.netty.util.CharsetUtil;
  10 +import org.springframework.stereotype.Service;
  11 +
  12 +import java.util.Date;
  13 +import java.util.regex.Matcher;
  14 +import java.util.regex.Pattern;
  15 +
  16 +/**
  17 + * 低电量报警上报
  18 + *
  19 + * @author guzijian
  20 + */
  21 +@Service(ThinkraceUtil.REQ.POWER_REQ)
  22 +public class IPowerAlarmPacketAdapter extends ICommandAdapter {
  23 +
  24 + @Override
  25 + public Object resolveCommand(String command, ChannelHandlerContext ctx) {
  26 + // IWAP04+电池电量#
  27 + // IWAP04075#
  28 + PowerAlarm power = handlerPower(command, ctx);
  29 + // 返回响应
  30 + String response = ThinkraceUtil.RES.POWER_RES + ThinkraceUtil.END_SYMBOL;
  31 + ctx.channel().writeAndFlush(Unpooled.copiedBuffer(response, CharsetUtil.UTF_8));
  32 + return power;
  33 + }
  34 +
  35 + private PowerAlarm handlerPower(String command, ChannelHandlerContext ctx) {
  36 + String regex = ThinkraceUtil.REQ.POWER_REQ + "(\\d+)";
  37 + Pattern pattern = Pattern.compile(regex);
  38 + Matcher matcher = pattern.matcher(command);
  39 + if (matcher.find()) {
  40 + PowerAlarm pa = new PowerAlarm();
  41 + pa.setPower(matcher.group(1));
  42 + pa.setCreateTime(new Date());
  43 + pa.setImei(ctx.channel().attr(AttributeKey.valueOf(ThinkraceUtil.IMEI)).get().toString());
  44 + return pa;
  45 + }
  46 + return null;
  47 + }
  48 +
  49 + @Override
  50 + public CommandListen sendCommand(Object params) {
  51 + return null;
  52 + }
  53 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/protocol/adapter/device/IQueryBluetoothPacketAdapter.java 0 → 100644
  1 +package com.ruoyi.system.protocol.adapter.device;
  2 +
  3 +import com.ruoyi.system.protocol.ThinkraceUtil;
  4 +import com.ruoyi.system.protocol.adapter.ICommandAdapter;
  5 +import io.netty.buffer.Unpooled;
  6 +import io.netty.channel.ChannelHandlerContext;
  7 +import io.netty.util.AttributeKey;
  8 +import io.netty.util.CharsetUtil;
  9 +import org.springframework.stereotype.Service;
  10 +
  11 +/**
  12 + * 10 查询手表对应的蓝牙温度计SN
  13 + *
  14 + * @author 20412
  15 + */
  16 +@Service(ThinkraceUtil.REQ.QUERY_BLUETOOTH_REQ)
  17 +public class IQueryBluetoothPacketAdapter extends ICommandAdapter {
  18 +
  19 + @Override
  20 + public Object resolveCommand(String command, ChannelHandlerContext ctx) {
  21 + String imei = ctx.channel().attr(AttributeKey.valueOf(ThinkraceUtil.IMEI)).toString();
  22 + log.info("设备:{},查询手表对应的蓝牙温度计SN命令:{}", imei, command);
  23 + // IWBP51,123456#
  24 + String response = ThinkraceUtil.RES.QUERY_BLUETOOTH_RES + "," + 123456 + ThinkraceUtil.END_SYMBOL;
  25 + ctx.channel().writeAndFlush(Unpooled.copiedBuffer(response, CharsetUtil.UTF_8));
  26 + responseLog(imei, response);
  27 + return null;
  28 + }
  29 +
  30 + @Override
  31 + public CommandListen sendCommand(Object params) {
  32 + return null;
  33 + }
  34 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/protocol/adapter/device/ISpeechPacketAdapter.java 0 → 100644
  1 +package com.ruoyi.system.protocol.adapter.device;
  2 +
  3 +import com.ruoyi.system.protocol.ThinkraceUtil;
  4 +import com.ruoyi.system.protocol.adapter.ICommandAdapter;
  5 +import io.netty.channel.ChannelHandlerContext;
  6 +import io.netty.util.AttributeKey;
  7 +import org.springframework.stereotype.Service;
  8 +
  9 +/**
  10 + * @author 20412
  11 + */
  12 +@Service(ThinkraceUtil.REQ.SPEECH_REQ)
  13 +@Deprecated
  14 +public class ISpeechPacketAdapter extends ICommandAdapter {
  15 +
  16 + @Override
  17 + public Object resolveCommand(String command, ChannelHandlerContext ctx) {
  18 + String imei = ctx.channel().attr(AttributeKey.valueOf(ThinkraceUtil.IMEI)).toString();
  19 + log.info("设备:{},语音查询接收协议命令:{},未作处理。", imei, command);
  20 + // TODO
  21 + // 响应 IWBP05,6#
  22 + String response = ThinkraceUtil.RES.SPEECH_RES + "," + 6 + ThinkraceUtil.END_SYMBOL;
  23 + responseLog(imei, response);
  24 + return null;
  25 + }
  26 +
  27 + @Override
  28 + public CommandListen sendCommand(Object params) {
  29 + return null;
  30 + }
  31 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/protocol/adapter/device/ISpeechUpPacketAdapter.java 0 → 100644
  1 +package com.ruoyi.system.protocol.adapter.device;
  2 +
  3 +import com.ruoyi.system.protocol.ThinkraceUtil;
  4 +import com.ruoyi.system.protocol.adapter.ICommandAdapter;
  5 +import com.ruoyi.system.protocol.property.DeviceProperty;
  6 +import com.ruoyi.utils.PathToolUtil;
  7 +import io.netty.buffer.Unpooled;
  8 +import io.netty.channel.ChannelHandlerContext;
  9 +import io.netty.util.AttributeKey;
  10 +import io.netty.util.CharsetUtil;
  11 +import org.springframework.beans.factory.annotation.Value;
  12 +import org.springframework.stereotype.Service;
  13 +
  14 +/**
  15 + * 语音上行
  16 + *
  17 + * @author 20412
  18 + */
  19 +@Service(ThinkraceUtil.REQ.SPEECH_UP_REQ)
  20 +public class ISpeechUpPacketAdapter extends ICommandAdapter {
  21 +
  22 + @Value("${netty.socket.catalogue.speech}")
  23 + private String catalogue;
  24 +
  25 +
  26 + @Override
  27 + public Object resolveCommand(String command, ChannelHandlerContext ctx) {
  28 + String imei = ctx.channel().attr(AttributeKey.valueOf(ThinkraceUtil.IMEI)).toString();
  29 + // 传输的是字节流 需要处理字节流的数据
  30 + String[] params = command.substring(0, command.length() - 1).split(",");
  31 + String count = params[2];
  32 + String current = params[3];
  33 + log.info("设备:{},图片上传命令:{},共:{}个包,当前第:{}个包,字节数据:{}", imei, command, count, current, params[5]);
  34 + boolean result = uploadSpeech(imei, params, ctx);
  35 +
  36 + // 返回响应 IWBP07,20140818064408,6,1,1#
  37 + String response = ThinkraceUtil.RES.SPEECH_UP_RES + "," + params[1] + "," + count + "," + current + "," +
  38 + (result ? DeviceProperty.SpeechProperty.UPLOAD_SUCCESS : DeviceProperty.SpeechProperty.UPLOAD_FAIL) + ThinkraceUtil.END_SYMBOL;
  39 + ctx.channel().writeAndFlush(Unpooled.copiedBuffer(response, CharsetUtil.UTF_8));
  40 + responseLog(imei, response);
  41 + return null;
  42 + }
  43 +
  44 + @Override
  45 + public CommandListen sendCommand(Object params) {
  46 + return null;
  47 + }
  48 +
  49 + private boolean uploadSpeech(String imei, String[] params, ChannelHandlerContext ctx) {
  50 + // IWAP42,终端产生语音的时间,图片包总数,当前包号,当前包长度,当前包语音数据#
  51 + // IWAP42,20140818064408,6,1,1024,XXXXXXXXXXXXXXXXXXXXXXXXX#
  52 + // 数据存入集合全部集齐后写入
  53 + String uploadTime = params[1];
  54 + // TODO 当前是否为第一个包 判断资源是否存在
  55 + String count = params[2];
  56 + String current = params[3];
  57 + String packetLength = params[4];
  58 + String data = params[5];
  59 + // TODO 写入数据库
  60 + // 追加数据包
  61 + PathToolUtil.generateRelativeSpeechPath(catalogue, imei, uploadTime, data.getBytes());
  62 + return true;
  63 + }
  64 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/protocol/adapter/device/ISureReadTextPacketAdapter.java 0 → 100644
  1 +package com.ruoyi.system.protocol.adapter.device;
  2 +
  3 +import com.ruoyi.system.protocol.ThinkraceUtil;
  4 +import com.ruoyi.system.protocol.adapter.ICommandAdapter;
  5 +import io.netty.buffer.Unpooled;
  6 +import io.netty.channel.ChannelHandlerContext;
  7 +import io.netty.util.AttributeKey;
  8 +import io.netty.util.CharsetUtil;
  9 +import org.springframework.stereotype.Service;
  10 +
  11 +/**
  12 + * 确认已读文字
  13 + *
  14 + * @author 20412
  15 + */
  16 +@Service(ThinkraceUtil.REQ.SURE_TEXT_REQ)
  17 +public class ISureReadTextPacketAdapter extends ICommandAdapter {
  18 +
  19 + @Override
  20 + public Object resolveCommand(String command, ChannelHandlerContext ctx) {
  21 + //TODO
  22 + // IWAP94,123456,1#
  23 + // IWAP94:标识符
  24 + // APHCI: 命令字符
  25 + // 123456: 文字来源系统流水号
  26 + // 1:确认阅读、0:取消阅读
  27 + String imei = ctx.channel().attr(AttributeKey.valueOf(ThinkraceUtil.IMEI)).toString();
  28 + log.info("设备:{},确认已读文字命令:{},未作处理。", imei, command);
  29 + String response = ThinkraceUtil.RES.SURE_TEXT_RES + ThinkraceUtil.END_SYMBOL;
  30 + ctx.channel().writeAndFlush(Unpooled.copiedBuffer(response, CharsetUtil.UTF_8));
  31 + responseLog(imei, response);
  32 + return null;
  33 + }
  34 +
  35 + @Override
  36 + public CommandListen sendCommand(Object params) {
  37 + return null;
  38 + }
  39 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/protocol/adapter/device/ISyncWeatherPacketAdapter.java 0 → 100644
  1 +package com.ruoyi.system.protocol.adapter.device;
  2 +
  3 +import com.alibaba.fastjson2.JSONObject;
  4 +import com.ruoyi.system.domain.response.WeatherResponse;
  5 +import com.ruoyi.system.protocol.ThinkraceUtil;
  6 +import com.ruoyi.system.protocol.adapter.ICommandAdapter;
  7 +import com.ruoyi.utils.ConstDateUtil;
  8 +import com.ruoyi.utils.IUnicodeUtils;
  9 +import io.netty.buffer.Unpooled;
  10 +import io.netty.channel.ChannelHandlerContext;
  11 +import io.netty.util.AttributeKey;
  12 +import io.netty.util.CharsetUtil;
  13 +import org.springframework.stereotype.Service;
  14 +
  15 +import java.io.*;
  16 +import java.net.HttpURLConnection;
  17 +import java.net.URL;
  18 +import java.util.Comparator;
  19 +import java.util.List;
  20 +import java.util.zip.GZIPInputStream;
  21 +
  22 +/**
  23 + * 同步天气
  24 + *
  25 + * @author 20412
  26 + */
  27 +@Service(ThinkraceUtil.REQ.WEATHER_REQ)
  28 +public class ISyncWeatherPacketAdapter extends ICommandAdapter {
  29 +
  30 + @Override
  31 + public Object resolveCommand(String command, ChannelHandlerContext ctx) {
  32 + String imei = ctx.channel().attr(AttributeKey.valueOf(ThinkraceUtil.IMEI)).toString();
  33 + log.info("设备:{},同步天气指令:{}", imei, command);
  34 + // 三天api https://devapi.qweather.com/v7/weather/3d?{查询参数}
  35 + // 天气api请求示例 'https://devapi.qweather.com/v7/weather/3d?location=116.41,39.92&key=9ee551aeac884cca8c68d70086708b44'
  36 + // TODO 后续完善 没有获取设备位置
  37 + // 先从表中获取当前设备位置 获取指定位置的天气 响应示例 unicode
  38 + String location = "116.41,39.92";
  39 + WeatherResponse weather = getWeather(location);
  40 + // IWBPWT, 深圳, 2018-12-24 16:45:5, 2018-12-25,1, 3,1,晴, 2018-12-26, -1,4,2, 阴, 2018-12-27,3,7, 3,小雨#
  41 + // IWBPWT, 6df15733, 2018-12-24 16:45:5, 2018-12-25,1, 3, 1, 6674, 2018-12-26, -1,4, 2,9634, 2018-12-27,3,7, 3, 5c0f96e8#
  42 + String weatherStr = transString("深圳", weather);
  43 + String response = ThinkraceUtil.RES.WEATHER_RES + weatherStr;
  44 + ctx.channel().writeAndFlush(Unpooled.copiedBuffer(response, CharsetUtil.UTF_8));
  45 + responseLog(imei, response);
  46 + return null;
  47 + }
  48 +
  49 + private String transString(String localName, WeatherResponse weather) {
  50 + StringBuilder sb = new StringBuilder();
  51 + List<WeatherResponse.Daily> daily = weather.getDaily();
  52 + sb.append(",").append(IUnicodeUtils.toUnicode(localName));
  53 + sb.append(",").append(ConstDateUtil.formatDate("yyyy-MM-dd HH:mm:ss"));
  54 + sb.append(",").append(daily.get(0).getFxDate());
  55 + sb.append(",").append(daily.get(0).getTempMin());
  56 + sb.append(",").append(daily.get(0).getTempMax());
  57 + sb.append(",").append(enumTrans(daily.get(0).getTextDay()));
  58 + sb.append(",").append(IUnicodeUtils.toUnicode(daily.get(0).getTextDay()));
  59 +
  60 + sb.append(",").append(daily.get(1).getFxDate());
  61 + sb.append(",").append(daily.get(1).getTempMin());
  62 + sb.append(",").append(daily.get(1).getTempMax());
  63 + sb.append(",").append(enumTrans(daily.get(1).getTextDay()));
  64 + sb.append(",").append(IUnicodeUtils.toUnicode(daily.get(1).getTextDay()));
  65 +
  66 + sb.append(",").append(daily.get(2).getFxDate());
  67 + sb.append(",").append(daily.get(2).getTempMin());
  68 + sb.append(",").append(daily.get(2).getTempMax());
  69 + sb.append(",").append(enumTrans(daily.get(2).getTextDay()));
  70 + sb.append(",").append(IUnicodeUtils.toUnicode(daily.get(2).getTextDay()));
  71 +
  72 + sb.append(ThinkraceUtil.END_SYMBOL);
  73 + return sb.toString();
  74 + }
  75 +
  76 + private String enumTrans(String str) {
  77 + switch (str) {
  78 + case "晴":
  79 + return "1";
  80 + case "多云":
  81 + case "阴":
  82 + return "2";
  83 + case "雨":
  84 + return "3";
  85 + case "雪":
  86 + return "4";
  87 + default:
  88 + return "5";
  89 + }
  90 + }
  91 +
  92 + private WeatherResponse getWeather(String location) {
  93 + WeatherResponse weather = null;
  94 + try {
  95 + String url = "https://devapi.qweather.com/v7/weather/3d?location=" + location + "&key=9ee551aeac884cca8c68d70086708b44";
  96 + weather = JSONObject.parseObject(sendGetRequest(url), WeatherResponse.class);
  97 + weather.getDaily().sort(Comparator.comparing(WeatherResponse.Daily::getFxDate));
  98 + } catch (Exception e) {
  99 + log.info(e.getMessage());
  100 + }
  101 + return weather;
  102 + }
  103 +
  104 + // 发送GET请求并获取响应数据(包括Gzip解压缩)
  105 + private static String sendGetRequest(String url) throws IOException {
  106 + HttpURLConnection connection = null;
  107 + try {
  108 + URL requestUrl = new URL(url);
  109 + connection = (HttpURLConnection) requestUrl.openConnection();
  110 + connection.setRequestMethod("GET");
  111 + // 请求Gzip压缩
  112 + connection.setRequestProperty("Accept-Encoding", "gzip");
  113 +
  114 + int responseCode = connection.getResponseCode();
  115 + if (responseCode == HttpURLConnection.HTTP_OK) {
  116 + InputStream inputStream = connection.getInputStream();
  117 + String contentEncoding = connection.getContentEncoding();
  118 +
  119 + if (contentEncoding != null && contentEncoding.equalsIgnoreCase("gzip")) {
  120 + inputStream = new GZIPInputStream(inputStream);
  121 + }
  122 +
  123 + BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
  124 + StringBuilder response = new StringBuilder();
  125 + String line;
  126 + while ((line = reader.readLine()) != null) {
  127 + response.append(line);
  128 + }
  129 +
  130 + reader.close();
  131 + return response.toString();
  132 + } else {
  133 + throw new IOException("GET request failed with response code: " + responseCode);
  134 + }
  135 + } finally {
  136 + if (connection != null) {
  137 + connection.disconnect();
  138 + }
  139 + }
  140 + }
  141 +
  142 + // 解压缩Gzip数据
  143 + private static byte[] decompressGzip(byte[] compressedData) throws IOException {
  144 + ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(compressedData);
  145 + GZIPInputStream gzipInputStream = new GZIPInputStream(byteArrayInputStream);
  146 + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
  147 +
  148 + byte[] buffer = new byte[1024];
  149 + int len;
  150 + while ((len = gzipInputStream.read(buffer)) > 0) {
  151 + byteArrayOutputStream.write(buffer, 0, len);
  152 + }
  153 +
  154 + gzipInputStream.close();
  155 + byteArrayOutputStream.close();
  156 +
  157 + return byteArrayOutputStream.toByteArray();
  158 + }
  159 +
  160 + @Override
  161 + public CommandListen sendCommand(Object params) {
  162 + return null;
  163 + }
  164 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/protocol/adapter/device/ITemperature50PacketAdapter.java 0 → 100644
  1 +package com.ruoyi.system.protocol.adapter.device;
  2 +
  3 +import com.ruoyi.system.domain.command.HealthInfo;
  4 +import com.ruoyi.system.protocol.ThinkraceUtil;
  5 +import com.ruoyi.system.protocol.adapter.ICommandAdapter;
  6 +import io.netty.buffer.Unpooled;
  7 +import io.netty.channel.ChannelHandlerContext;
  8 +import io.netty.util.AttributeKey;
  9 +import io.netty.util.CharsetUtil;
  10 +import org.springframework.stereotype.Service;
  11 +
  12 +/**
  13 + * 9 温度数据上传
  14 + *
  15 + * @author 20412
  16 + */
  17 +@Service(ThinkraceUtil.REQ.TEMPERATURE_50_REQ)
  18 +public class ITemperature50PacketAdapter extends ICommandAdapter {
  19 +
  20 + @Override
  21 + public Object resolveCommand(String command, ChannelHandlerContext ctx) {
  22 + // IWAP50,36.7,90#
  23 + String sub = command.substring(0, command.length() - 1);
  24 + String[] params = sub.split(",");
  25 + HealthInfo hi = new HealthInfo();
  26 + hi.setImei(ctx.channel().attr(AttributeKey.valueOf(ThinkraceUtil.IMEI)).toString());
  27 + log.info("设备:{},温度数据上传命令:{}", hi.getImei(), command);
  28 + hi.setTemperature(Float.parseFloat(params[1]));
  29 + hi.setPower(params[2]);
  30 + // IWBP50#
  31 + String response = ThinkraceUtil.RES.TEMPERATURE_50_RES + ThinkraceUtil.END_SYMBOL;
  32 + ctx.channel().writeAndFlush(Unpooled.copiedBuffer(response, CharsetUtil.UTF_8));
  33 + responseLog(hi.getImei(), response);
  34 + return hi;
  35 + }
  36 +
  37 + @Override
  38 + public CommandListen sendCommand(Object params) {
  39 + return null;
  40 + }
  41 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/protocol/adapter/device/ITemperaturePacketAdapter.java 0 → 100644
  1 +package com.ruoyi.system.protocol.adapter.device;
  2 +
  3 +import com.ruoyi.system.domain.command.HealthInfo;
  4 +import com.ruoyi.system.protocol.ThinkraceUtil;
  5 +import com.ruoyi.system.protocol.adapter.ICommandAdapter;
  6 +import com.ruoyi.system.protocol.property.DeviceProperty;
  7 +import io.netty.buffer.Unpooled;
  8 +import io.netty.channel.ChannelHandlerContext;
  9 +import io.netty.util.AttributeKey;
  10 +import io.netty.util.CharsetUtil;
  11 +import org.springframework.stereotype.Service;
  12 +
  13 +/**
  14 + * 上报体温
  15 + *
  16 + * @author 20412
  17 + */
  18 +@Service(ThinkraceUtil.REQ.TEMPERATURE_REQ)
  19 +public class ITemperaturePacketAdapter extends ICommandAdapter {
  20 +
  21 + @Override
  22 + public Object resolveCommand(String command, ChannelHandlerContext ctx) {
  23 + // 格式:IWAPTP,体温,腕温#
  24 + // 示例:IWAPTP,36.0,33,0#
  25 + String[] params = command.split(",");
  26 + HealthInfo health = new HealthInfo();
  27 + health.setImei(ctx.channel().attr(AttributeKey.valueOf(ThinkraceUtil.IMEI)).toString());
  28 + log.info("设备:{},上报体温命令:{}", health.getImei(), command);
  29 + health.setHealthType(DeviceProperty.HealthProperty.TEMPERATURE);
  30 + health.setCommandLabel(params[0]);
  31 + health.setTemperature(Float.parseFloat(params[1]));
  32 + health.setHandTemperature(params[2].replace(ThinkraceUtil.END_SYMBOL, ""));
  33 + // 返回响应
  34 + String response = ThinkraceUtil.RES.TEMPERATURE_RES + ThinkraceUtil.END_SYMBOL;
  35 + ctx.channel().writeAndFlush(Unpooled.copiedBuffer(response, CharsetUtil.UTF_8));
  36 + responseLog(health.getImei(), response);
  37 + return health;
  38 + }
  39 +
  40 + @Override
  41 + public CommandListen sendCommand(Object params) {
  42 + return null;
  43 + }
  44 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/protocol/adapter/device/IUploadBluetoothAdapter.java 0 → 100644
  1 +package com.ruoyi.system.protocol.adapter.device;
  2 +
  3 +import com.ruoyi.system.domain.command.Bluetooth;
  4 +import com.ruoyi.system.domain.command.BluetoothInfo;
  5 +import com.ruoyi.system.protocol.ThinkraceUtil;
  6 +import com.ruoyi.system.protocol.adapter.ICommandAdapter;
  7 +import io.netty.buffer.Unpooled;
  8 +import io.netty.channel.ChannelHandlerContext;
  9 +import io.netty.util.CharsetUtil;
  10 +import org.springframework.stereotype.Service;
  11 +
  12 +import java.util.ArrayList;
  13 +import java.util.List;
  14 +
  15 +/**
  16 + * @author 20412
  17 + */
  18 +@Service(ThinkraceUtil.REQ.BLUETOOTH_REQ)
  19 +public class IUploadBluetoothAdapter extends ICommandAdapter {
  20 +
  21 +
  22 + @Override
  23 + public Object resolveCommand(String command, ChannelHandlerContext ctx) {
  24 + // 格式:IWAPBL,IMEI,周围的蓝牙参数,终端自身MAC地址,时间戳#
  25 + // 示例:IWAPBL,355932600029362,TraxBean064|BF:0C:B8:3F:2F:37|-30&VG05|F0:49:32:83:F9:9E|-52,28:05:31:16:02:04,1711512576683#
  26 + String[] params = command.split(",");
  27 + BluetoothInfo bluetooth = new BluetoothInfo();
  28 + bluetooth.setImei(params[1]);
  29 + bluetooth.setMac(params[3]);
  30 + bluetooth.setTimestamp(params[4].replace(ThinkraceUtil.END_SYMBOL, ""));
  31 + String[] btInfo = params[2].split("&");
  32 + List<Bluetooth> bluetoothList = new ArrayList<>(btInfo.length);
  33 + for (String bti : btInfo) {
  34 + String[] bts = bti.split("\\|");
  35 + Bluetooth bt = new Bluetooth(bts[0], bts[1], bts[2]);
  36 + bluetoothList.add(bt);
  37 + }
  38 + bluetooth.setBluetoothList(bluetoothList);
  39 + // TODO 信息入表
  40 + // 返回响应
  41 + // IWBPBL#
  42 + String response = ThinkraceUtil.RES.BLUETOOTH_RES + ThinkraceUtil.END_SYMBOL;
  43 + ctx.channel().writeAndFlush(Unpooled.copiedBuffer(response, CharsetUtil.UTF_8));
  44 + responseLog(bluetooth.getImei(), response);
  45 + return null;
  46 + }
  47 +
  48 + @Override
  49 + public CommandListen sendCommand(Object params) {
  50 + return null;
  51 + }
  52 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/protocol/adapter/device/IUploadDeviceVersionPacketAdapter.java 0 → 100644
  1 +package com.ruoyi.system.protocol.adapter.device;
  2 +
  3 +import com.ruoyi.system.domain.entity.Device;
  4 +import com.ruoyi.system.protocol.ThinkraceUtil;
  5 +import com.ruoyi.system.protocol.adapter.ICommandAdapter;
  6 +import io.netty.channel.ChannelHandlerContext;
  7 +import org.springframework.stereotype.Service;
  8 +
  9 +/**
  10 + * 设备上传固件版本
  11 + *
  12 + * @author 20412
  13 + */
  14 +@Service(ThinkraceUtil.REQ.VERSION_REQ)
  15 +public class IUploadDeviceVersionPacketAdapter extends ICommandAdapter {
  16 +
  17 +
  18 + @Override
  19 + public Object resolveCommand(String command, ChannelHandlerContext ctx) {
  20 + // 格式:IWAPVR,IMEI,固件版本号#
  21 + // 示例:IWAPVR,357653050858997,C42F-l005l005-EU-P1-V0.3.38.20210906.182655#
  22 + String[] params = command.split(",");
  23 + Device device = new Device();
  24 + device.setImei(params[1]);
  25 + device.setVersion(params[2].replace(ThinkraceUtil.END_SYMBOL, ""));
  26 + // 无需响应
  27 + return null;
  28 + }
  29 +
  30 + @Override
  31 + public CommandListen sendCommand(Object params) {
  32 + return null;
  33 + }
  34 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/protocol/adapter/device/IUploadHeartRateBloodOxygenPacketAdapter.java 0 → 100644
  1 +package com.ruoyi.system.protocol.adapter.device;
  2 +
  3 +import com.ruoyi.system.domain.command.HealthInfo;
  4 +import com.ruoyi.system.protocol.ThinkraceUtil;
  5 +import com.ruoyi.system.protocol.adapter.ICommandAdapter;
  6 +import io.netty.buffer.Unpooled;
  7 +import io.netty.channel.ChannelHandlerContext;
  8 +import io.netty.util.AttributeKey;
  9 +import io.netty.util.CharsetUtil;
  10 +import org.springframework.stereotype.Service;
  11 +
  12 +import java.util.Date;
  13 +
  14 +/**
  15 + * 处理血样血压
  16 + *
  17 + * @author 20412
  18 + */
  19 +@Service(ThinkraceUtil.REQ.HEART_BLOOD_REQ)
  20 +public class IUploadHeartRateBloodOxygenPacketAdapter extends ICommandAdapter {
  21 +
  22 +
  23 + @Override
  24 + public Object resolveCommand(String command, ChannelHandlerContext ctx) {
  25 + // 格式:IWAPHT,心率值,收缩压值,舒张压值#
  26 + // 示例:IWAPHT,60,130,85#
  27 + String[] params = command.substring(0, command.length() - 1).split(",");
  28 + HealthInfo info = new HealthInfo();
  29 + info.setImei(ctx.channel().attr(AttributeKey.valueOf(ThinkraceUtil.IMEI)).toString());
  30 + info.setCommandLabel(params[0]);
  31 + info.setHeartRate(params[1]);
  32 + info.setHighPressure(Integer.parseInt(params[2]));
  33 + // 去掉结束符
  34 + info.setLowPressure(Integer.parseInt(params[3]));
  35 + info.setCreateTime(new Date());
  36 + // TODO 记录表 info
  37 + // 返回响应
  38 + // IWBPHT#
  39 + String response = ThinkraceUtil.RES.HEART_BLOOD_RES + ThinkraceUtil.END_SYMBOL;
  40 + ctx.channel().writeAndFlush(Unpooled.copiedBuffer(response, CharsetUtil.UTF_8));
  41 + responseLog(info.getImei(), response);
  42 + return info;
  43 + }
  44 +
  45 + @Override
  46 + public CommandListen sendCommand(Object params) {
  47 + return null;
  48 + }
  49 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/protocol/adapter/device/IWearingStatePacketAdapter.java 0 → 100644
  1 +package com.ruoyi.system.protocol.adapter.device;
  2 +
  3 +import com.ruoyi.system.domain.command.WearingState;
  4 +import com.ruoyi.system.protocol.ThinkraceUtil;
  5 +import com.ruoyi.system.protocol.adapter.ICommandAdapter;
  6 +import io.netty.channel.ChannelHandlerContext;
  7 +import org.springframework.stereotype.Service;
  8 +import java.util.Date;
  9 +
  10 +/**
  11 + * 佩戴状态
  12 + *
  13 + * @author 20412
  14 + */
  15 +@Service(ThinkraceUtil.REQ.WEARING_REQ)
  16 +public class IWearingStatePacketAdapter extends ICommandAdapter {
  17 +
  18 + @Override
  19 + public Object resolveCommand(String command, ChannelHandlerContext ctx) {
  20 + // 格式:IWAPWR, IMEI,佩戴标志位,时间戳#
  21 + // 示例:IWAPVR,357653050858997,1,xxxxxx#
  22 + String[] params = command.split(",");
  23 + WearingState ws = new WearingState();
  24 + ws.setImei(params[1]);
  25 + ws.setState(params[2]);
  26 + ws.setTimestamp(Long.parseLong(params[3].replace(ThinkraceUtil.END_SYMBOL, "")));
  27 + ws.setCreateTime(new Date());
  28 + // TODO 入表
  29 + // 无需响应
  30 + return null;
  31 + }
  32 +
  33 + @Override
  34 + public CommandListen sendCommand(Object params) {
  35 + return null;
  36 + }
  37 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/protocol/property/DeviceProperty.java 0 → 100644
  1 +package com.ruoyi.system.protocol.property;
  2 +
  3 +/**
  4 + * 设备配置
  5 + * @author 20412
  6 + */
  7 +public class DeviceProperty {
  8 + public static class RedisProperty {
  9 + public static String UPLOAD_IMAGE_PRE = "device:image:upload:";
  10 + }
  11 +
  12 + public static class ImageProperty {
  13 + public static final String UPLOAD_SUCCESS = "1";
  14 + public static final String UPLOAD_FAIL = "0";
  15 + }
  16 +
  17 + public static class SpeechProperty {
  18 + public static final String UPLOAD_SUCCESS = "1";
  19 + public static final String UPLOAD_FAIL = "0";
  20 + }
  21 +
  22 + public static class AddressProperty {
  23 + public static final String VALID = "A";
  24 + public static final String INVALID = "V";
  25 + }
  26 +
  27 +
  28 +
  29 + public static class HealthProperty {
  30 + /** 血压 */
  31 + public static final String BLOOD_PRESSURE = "1";
  32 + /** 心率*/
  33 + public static final String HEART_RATE = "2";
  34 + /** 体温 */
  35 + public static final String TEMPERATURE = "3";
  36 + /** 血氧 */
  37 + public static final String BLOOD_OXYGEN = "4";
  38 + }
  39 +
  40 + public static class WearingState {
  41 + /** 血压 */
  42 + public static final String HAND = "1";
  43 + /** 血压 */
  44 + public static final String NOT = "0";
  45 + }
  46 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/server/SocketServer.java 0 → 100644
  1 +package com.ruoyi.system.server;
  2 +
  3 +import com.ruoyi.common.core.redis.RedisCache;
  4 +import com.ruoyi.system.handler.SocketServerHandler;
  5 +import com.ruoyi.system.protocol.ThinkraceUtil;
  6 +import com.ruoyi.system.protocol.adapter.ICommandAdapter;
  7 +import io.netty.bootstrap.ServerBootstrap;
  8 +import io.netty.channel.ChannelFuture;
  9 +import io.netty.channel.ChannelInitializer;
  10 +import io.netty.channel.ChannelOption;
  11 +import io.netty.channel.EventLoopGroup;
  12 +import io.netty.channel.nio.NioEventLoopGroup;
  13 +import io.netty.channel.socket.SocketChannel;
  14 +import io.netty.channel.socket.nio.NioServerSocketChannel;
  15 +import org.slf4j.Logger;
  16 +import org.slf4j.LoggerFactory;
  17 +import org.springframework.beans.factory.annotation.Autowired;
  18 +import org.springframework.beans.factory.annotation.Value;
  19 +import org.springframework.boot.CommandLineRunner;
  20 +import org.springframework.context.ApplicationContext;
  21 +import org.springframework.stereotype.Component;
  22 +
  23 +import javax.annotation.PreDestroy;
  24 +import java.util.Map;
  25 +
  26 +/**
  27 + * @author 20412
  28 + */
  29 +@Component
  30 +public class SocketServer implements CommandLineRunner {
  31 + @Autowired
  32 + private ApplicationContext applicationContext;
  33 +
  34 + @Autowired
  35 + private RedisCache redisCache;
  36 +
  37 + private static final Logger log = LoggerFactory.getLogger(SocketServer.class);
  38 + EventLoopGroup bossGroup = null;
  39 + EventLoopGroup workerGroup = null;
  40 + @Value("${netty.socket.port:8989}")
  41 + private Integer port;
  42 +
  43 + @Value("${netty.enabled:false}")
  44 + private Boolean enabled;
  45 +
  46 + @Override
  47 + public void run(String... args) throws Exception {
  48 + if (enabled) {
  49 + // 创建一个线程启动netty 这个CommandLineRunner 接口在主服务基本启动完后会调用使用的是主线程 netty启动会占用线程一直监听端口
  50 + Thread thread = new Thread(new NettyServer());
  51 + log.info("netty server active...");
  52 + thread.start();
  53 + // 初始化命令适配器
  54 + log.info("command adapter init...");
  55 + initCommandAdapter();
  56 + }
  57 + }
  58 +
  59 + private void initCommandAdapter() {
  60 + // 把所有适配器注入到工具类中管理
  61 + Map<String, ICommandAdapter> implementations = applicationContext.getBeansOfType(ICommandAdapter.class);
  62 + for (Map.Entry<String, ICommandAdapter> entry : implementations.entrySet()) {
  63 + ThinkraceUtil.addAdapter(entry.getKey(), entry.getValue());
  64 + }
  65 + ThinkraceUtil.initRedisThinkraceCommandParam(redisCache);
  66 + }
  67 +
  68 + /**
  69 + * 销毁方法
  70 + *
  71 + * @throws Exception
  72 + */
  73 + @PreDestroy
  74 + public void destroy() throws Exception {
  75 + if (bossGroup != null) {
  76 + bossGroup.shutdownGracefully();
  77 + }
  78 + if (workerGroup != null) {
  79 + workerGroup.shutdownGracefully();
  80 + }
  81 + log.info("netty server destroy...");
  82 + }
  83 +
  84 + class NettyServer implements Runnable {
  85 + @Override
  86 + public void run() {
  87 + try {
  88 + bossGroup = new NioEventLoopGroup();
  89 + workerGroup = new NioEventLoopGroup();
  90 + //创建服务端的启动对象,设置参数
  91 + ServerBootstrap bootstrap = new ServerBootstrap();
  92 + //设置两个线程组boosGroup和workerGroup
  93 + bootstrap.group(bossGroup, workerGroup)
  94 + //设置服务端通道实现类型
  95 + .channel(NioServerSocketChannel.class)
  96 + //设置线程队列得到连接个数
  97 + .option(ChannelOption.SO_BACKLOG, 128)
  98 + //设置保持活动连接状态
  99 + .childOption(ChannelOption.SO_KEEPALIVE, true)
  100 + //使用匿名内部类的形式初始化通道对象
  101 + .childHandler(new ChannelInitializer<SocketChannel>() {
  102 + @Override
  103 + protected void initChannel(SocketChannel socketChannel) throws Exception {
  104 + //给pipeline管道设置处理器
  105 + socketChannel.pipeline().addLast(new SocketServerHandler());
  106 + }
  107 + });//给workerGroup的EventLoop对应的管道设置处理器
  108 + //绑定端口号,启动服务端
  109 + ChannelFuture channelFuture = bootstrap.bind(port).sync();
  110 + //对关闭通道进行监听
  111 + channelFuture.channel().closeFuture().sync();
  112 + } catch (InterruptedException e) {
  113 + log.error("SocketServer at line 71:" + e.getMessage());
  114 + }
  115 + }
  116 + }
  117 +
  118 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/service/IDeviceService.java 0 → 100644
  1 +package com.ruoyi.system.service;
  2 +
  3 +import com.baomidou.mybatisplus.extension.service.IService;
  4 +import com.ruoyi.common.global.Result;
  5 +import com.ruoyi.system.domain.active.Sos;
  6 +import com.ruoyi.system.domain.active.UploadGpsTime;
  7 +import com.ruoyi.system.domain.active.WhiteListPeople;
  8 +import com.ruoyi.system.domain.entity.Device;
  9 +
  10 +/**
  11 + * deviceService接口
  12 + *
  13 + * @author bsth
  14 + * @date 2024-04-17
  15 + */
  16 +public interface IDeviceService extends IService<Device>
  17 +{
  18 +
  19 + Result<?> settingSos(Sos sos);
  20 +
  21 + Result<?> settingWhiteList(WhiteListPeople wlp);
  22 +
  23 + Result<?> settingGpsUploadTime(UploadGpsTime ugt);
  24 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/service/ITimedTaskService.java 0 → 100644
  1 +package com.ruoyi.system.service;
  2 +
  3 +/**
  4 + * @author 20412
  5 + */
  6 +public interface ITimedTaskService {
  7 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/service/impl/DeviceServiceImpl.java 0 → 100644
  1 +package com.ruoyi.system.service.impl;
  2 +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
  3 +
  4 +import com.ruoyi.common.global.Result;
  5 +import com.ruoyi.system.domain.active.Sos;
  6 +import com.ruoyi.system.domain.active.UploadGpsTime;
  7 +import com.ruoyi.system.domain.active.WhiteListPeople;
  8 +import com.ruoyi.system.domain.entity.Device;
  9 +import com.ruoyi.system.mapper.DeviceMapper;
  10 +import com.ruoyi.system.protocol.ThinkraceUtil;
  11 +import com.ruoyi.system.service.IDeviceService;
  12 +import org.springframework.stereotype.Service;
  13 +
  14 +/**
  15 + * deviceService业务层处理
  16 + *
  17 + * @author bsth
  18 + * @date 2024-04-17
  19 + */
  20 +@Service
  21 +public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> implements IDeviceService
  22 +{
  23 +
  24 +
  25 + @Override
  26 + public Result<?> settingSos(Sos sos) {
  27 + boolean result = ThinkraceUtil
  28 + .getAdapter(ThinkraceUtil.ACTIVE.SOS_ACTIVE)
  29 + .sendCommand(sos)
  30 + .listenAsk();
  31 + return Result.OK((result ? "设备成功响应" :"设备无响应"));
  32 + }
  33 +
  34 + @Override
  35 + public Result<?> settingWhiteList(WhiteListPeople wlp) {
  36 + boolean result = ThinkraceUtil
  37 + .getAdapter(ThinkraceUtil.ACTIVE.CONTACT_ACTIVE)
  38 + .sendCommand(wlp)
  39 + .listenAsk();
  40 + return Result.OK((result ? "设备成功响应" :"设备无响应"));
  41 + }
  42 +
  43 + @Override
  44 + public Result<?> settingGpsUploadTime(UploadGpsTime ugt) {
  45 + boolean result = ThinkraceUtil
  46 + .getAdapter(ThinkraceUtil.ACTIVE.UPLOAD_GPS_TIME_ACTIVE)
  47 + .sendCommand(ugt)
  48 + .listenAsk();
  49 + return Result.OK((result ? "设备成功响应" :"设备无响应"));
  50 + }
  51 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/service/impl/ITimedTaskServiceImpl.java 0 → 100644
  1 +package com.ruoyi.system.service.impl;
  2 +
  3 +import com.ruoyi.system.service.ITimedTaskService;
  4 +import lombok.extern.slf4j.Slf4j;
  5 +import org.springframework.stereotype.Service;
  6 +
  7 +/**
  8 + * @author 20412
  9 + */
  10 +@Service
  11 +@Slf4j
  12 +public class ITimedTaskServiceImpl implements ITimedTaskService {
  13 +
  14 +
  15 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/system/strategy/CommandStrategy.java 0 → 100644
  1 +package com.ruoyi.system.strategy;
  2 +
  3 +import com.ruoyi.common.utils.StringUtils;
  4 +import com.ruoyi.system.protocol.ThinkraceUtil;
  5 +import com.ruoyi.system.protocol.adapter.ICommandAdapter;
  6 +import io.netty.channel.ChannelHandlerContext;
  7 +import org.slf4j.Logger;
  8 +import org.slf4j.LoggerFactory;
  9 +
  10 +import static com.ruoyi.system.protocol.ThinkraceUtil.REQ.LOGIN_REQ;
  11 +
  12 +/**
  13 + * 命令观察者模式
  14 + *
  15 + * @author 20412
  16 + */
  17 +public class CommandStrategy {
  18 + private static final Logger log = LoggerFactory.getLogger(CommandStrategy.class);
  19 +
  20 + public CommandStrategy() {
  21 + }
  22 +
  23 + public void handler(String command, ChannelHandlerContext ctx) {
  24 + processor(command, ctx);
  25 + }
  26 +
  27 + private void processor(String command, ChannelHandlerContext ctx) {
  28 + if (StringUtils.isNotEmpty(command) && command.length() > 6) {
  29 + String key = command.substring(0, 6);
  30 + // 未上传登录包无法使用对应指令
  31 + ICommandAdapter adapter = ThinkraceUtil.getAdapter(key);
  32 + if (filterCommand(key, ctx) && adapter != null) {
  33 + adapter.resolveCommand(command, ctx);
  34 + return;
  35 + }
  36 + log.error("设备传来未知指令:{}", command);
  37 + }
  38 + }
  39 +
  40 + private boolean filterCommand(String key, ChannelHandlerContext ctx) {
  41 + return LOGIN_REQ.equals(key) || ThinkraceUtil.hasDevice(ctx.channel());
  42 + }
  43 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/utils/ConstDateUtil.java
... ... @@ -46,6 +46,11 @@ public class ConstDateUtil {
46 46 SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);
47 47 return simpleDateFormat.format(new Date());
48 48 }
  49 + public static String formatDate(String pattern,ZoneOffset utc){
  50 + LocalDateTime currentTime = LocalDateTime.now(utc);
  51 + DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
  52 + return formatter.format(currentTime);
  53 + }
49 54  
50 55 public static Date parseDate(String date){
51 56 SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
... ...
Bsth-admin/src/main/java/com/ruoyi/utils/IUnicodeUtils.java 0 → 100644
  1 +package com.ruoyi.utils;
  2 +
  3 +import org.apache.commons.lang3.StringUtils;
  4 +
  5 +/**
  6 + * @author 20412
  7 + */
  8 +public class IUnicodeUtils {
  9 +
  10 + /**
  11 + * @param str content
  12 + * @return unicode
  13 + */
  14 + public static String toUnicode(String str) {
  15 + StringBuilder outStr = new StringBuilder();
  16 + if (StringUtils.isNotEmpty(str)) {
  17 + for (int i = 0; i < str.length(); i++) {
  18 + String outStr2 = Integer.toHexString(str.charAt(i));
  19 + outStr.append(StringUtils.leftPad(outStr2, 4, '0'));
  20 + }
  21 + }
  22 + return outStr.toString();
  23 + }
  24 +}
... ...
Bsth-admin/src/main/java/com/ruoyi/utils/PathToolUtil.java 0 → 100644
  1 +package com.ruoyi.utils;
  2 +
  3 +import com.ruoyi.common.utils.StringUtils;
  4 +import org.slf4j.Logger;
  5 +import org.slf4j.LoggerFactory;
  6 +
  7 +import java.io.File;
  8 +import java.io.FileInputStream;
  9 +import java.io.RandomAccessFile;
  10 +
  11 +public class PathToolUtil {
  12 +
  13 + private static final Logger log = LoggerFactory.getLogger(PathToolUtil.class);
  14 +
  15 + // 维护写入队列 不停的遍历队列内的任务来完成写入 提高指令的响应速度
  16 +
  17 +// private static LinkedList<Runnable> runnableList = new LinkedList<>();
  18 +
  19 + public static boolean generateRelativeImagePath(String catalogue, String imei, String uploadTime, byte[] bits) {
  20 +// runnableList.push();
  21 + String resourceDirectory = "Bsth-admin/src/main/resources";
  22 + String path = StringUtils.format("{}/{}/{}/{}.{}", resourceDirectory,
  23 + catalogue, imei, uploadTime, "jpg");
  24 + File file = new File(path);
  25 + RandomAccessFile raf = null;
  26 + if (!file.exists()) {
  27 + if (!file.getParentFile().exists()) {
  28 + file.getParentFile().mkdirs();
  29 + }
  30 + }
  31 + try {
  32 + // 创建RandomAccessFile对象,并以读写模式打开文件
  33 + raf = new RandomAccessFile(file, "rw");
  34 +
  35 + // 将文件指针移动到文件末尾
  36 + raf.seek(file.length());
  37 +
  38 + // 写入数据
  39 + raf.write(bits);
  40 +
  41 + } catch (Exception e) {
  42 + log.error("图片写入出错:{}", e.getMessage());
  43 + // TODO 第三次返回true
  44 + } finally {
  45 + try {
  46 + if (raf != null) {
  47 + raf.close();
  48 + }
  49 + } catch (Exception e) {
  50 + log.info(e.getMessage());
  51 + }
  52 + return true;
  53 + }
  54 + }
  55 +
  56 +
  57 + public static boolean generateRelativeSpeechPath(String catalogue, String imei, String uploadTime, byte[] bits) {
  58 +// runnableList.push();
  59 + String resourceDirectory = "Bsth-admin/src/main/resources";
  60 + String path = StringUtils.format("{}/{}/{}/{}.{}", resourceDirectory,
  61 + catalogue, imei, uploadTime, "mp3");
  62 + File file = new File(path);
  63 + RandomAccessFile raf = null;
  64 + if (!file.exists()) {
  65 + if (!file.getParentFile().exists()) {
  66 + file.getParentFile().mkdirs();
  67 + }
  68 + }
  69 + try {
  70 + // 创建RandomAccessFile对象,并以读写模式打开文件
  71 + raf = new RandomAccessFile(file, "rw");
  72 +
  73 + // 将文件指针移动到文件末尾
  74 + raf.seek(file.length());
  75 +
  76 + // 写入数据
  77 + raf.write(bits);
  78 +
  79 + } catch (Exception e) {
  80 + log.error("图片写入出错:{}", e.getMessage());
  81 + // TODO 第三次返回true
  82 + } finally {
  83 + try {
  84 + if (raf != null) {
  85 + raf.close();
  86 + }
  87 + } catch (Exception e) {
  88 + log.info(e.getMessage());
  89 + }
  90 + return true;
  91 + }
  92 + }
  93 +
  94 +
  95 +
  96 + public static void main(String[] args) {
  97 +
  98 + try {
  99 + int count = 0;
  100 + FileInputStream fis = new FileInputStream("C:\\Users\\20412\\Desktop\\Bsth\\岗前检查\\Bsth\\Bsth-admin\\src\\main\\resources\\564.jpg");
  101 + byte[] bits = new byte[1024];
  102 + int len;
  103 + while ((len = fis.read(bits)) != -1) {
  104 + count++;
  105 + for (int i = 0; i < len; i++) {
  106 + System.out.print(bits[i]);
  107 + }
  108 + System.out.println("分片");
  109 + }
  110 + System.out.println("共" + count + "片");
  111 + } catch (Exception e) {
  112 + throw new RuntimeException(e);
  113 + }
  114 +
  115 + }
  116 +
  117 +
  118 +}
... ...
Bsth-admin/src/main/resources/application-druid-dev.yml
... ... @@ -97,7 +97,7 @@ spring:
97 97 redis:
98 98 # 地址
99 99 # host: 121.41.83.61
100   - host: 192.168.168.124
  100 + host: localhost
101 101 database: 14
102 102 # password: "guzijian"
103 103 port: 6379
... ... @@ -182,3 +182,12 @@ log:
182 182 path: D:/ruoyi/logs
183 183 server:
184 184 port: 8100
  185 +netty:
  186 + # 是否开启netty服务
  187 + enabled: false
  188 + socket:
  189 + # 相对路径 classpath
  190 + catalogue:
  191 + image: device/image
  192 + speech: device/speech
  193 + port: 8989
185 194 \ No newline at end of file
... ...
Bsth-admin/src/main/resources/application-druid-prd.yml
... ... @@ -206,3 +206,13 @@ api:
206 206 people:
207 207 url: https://api.dingtalk.com/v1.0/yida/forms/instances/search
208 208  
  209 +
  210 +netty:
  211 + # 是否开启netty服务
  212 + enabled: false
  213 + socket:
  214 + # 相对路径 classpath
  215 + catalogue:
  216 + image: device/image
  217 + speech: device/speech
  218 + port: 8989
209 219 \ No newline at end of file
... ...
Bsth-admin/src/main/resources/application-druid-uat.yml
... ... @@ -198,4 +198,13 @@ api:
198 198 people:
199 199 url: https://api.dingtalk.com/v1.0/yida/forms/instances/search
200 200 log:
201   - path: /home/sign/logs
202 201 \ No newline at end of file
  202 + path: /home/sign/logs
  203 +netty:
  204 + # 是否开启netty服务
  205 + enabled: false
  206 + socket:
  207 + # 相对路径 classpath
  208 + catalogue:
  209 + image: device/image
  210 + speech: device/speech
  211 + port: 8989
203 212 \ No newline at end of file
... ...
Bsth-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java
... ... @@ -69,6 +69,18 @@ public class RedisCache {
69 69 * @param unit 时间单位
70 70 * @return true=设置成功;false=设置失败
71 71 */
  72 + public void expire(final String key,Object val ,final long timeout, final TimeUnit unit) {
  73 + redisTemplate.opsForValue().set(key, val,timeout, unit);
  74 + }
  75 +
  76 + /**
  77 + * 设置有效时间
  78 + *
  79 + * @param key Redis键
  80 + * @param timeout 超时时间
  81 + * @param unit 时间单位
  82 + * @return true=设置成功;false=设置失败
  83 + */
72 84 public boolean expire(final String key, final long timeout, final TimeUnit unit) {
73 85 return redisTemplate.expire(key, timeout, unit);
74 86 }
... ... @@ -283,4 +295,14 @@ public class RedisCache {
283 295 public <T> Set<T> getHashKeys(String key){
284 296 return redisTemplate.opsForHash().keys(key);
285 297 }
  298 +
  299 + public boolean setNx(String key, Object val) {
  300 + Boolean result = redisTemplate.opsForValue().setIfAbsent(key, val);
  301 + return result != null && result;
  302 + }
  303 +
  304 + public Long increment(String key) {
  305 + Long increment = redisTemplate.opsForValue().increment(key);
  306 + return increment;
  307 + }
286 308 }
... ...